18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * core.c - ChipIdea USB IP core family device controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (C) 2020 NXP 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: David Lopo 98c2ecf20Sopenharmony_ci * Peter Chen <peter.chen@nxp.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Main Features: 128c2ecf20Sopenharmony_ci * - Four transfers are supported, usbtest is passed 138c2ecf20Sopenharmony_ci * - USB Certification for gadget: CH9 and Mass Storage are passed 148c2ecf20Sopenharmony_ci * - Low power mode 158c2ecf20Sopenharmony_ci * - USB wakeup 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/extcon.h> 218c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/idr.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <linux/io.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 308c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 318c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 328c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 338c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 348c2ecf20Sopenharmony_ci#include <linux/usb/chipidea.h> 358c2ecf20Sopenharmony_ci#include <linux/usb/of.h> 368c2ecf20Sopenharmony_ci#include <linux/of.h> 378c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 388c2ecf20Sopenharmony_ci#include <linux/usb/ehci_def.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "ci.h" 418c2ecf20Sopenharmony_ci#include "udc.h" 428c2ecf20Sopenharmony_ci#include "bits.h" 438c2ecf20Sopenharmony_ci#include "host.h" 448c2ecf20Sopenharmony_ci#include "otg.h" 458c2ecf20Sopenharmony_ci#include "otg_fsm.h" 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Controller register map */ 488c2ecf20Sopenharmony_cistatic const u8 ci_regs_nolpm[] = { 498c2ecf20Sopenharmony_ci [CAP_CAPLENGTH] = 0x00U, 508c2ecf20Sopenharmony_ci [CAP_HCCPARAMS] = 0x08U, 518c2ecf20Sopenharmony_ci [CAP_DCCPARAMS] = 0x24U, 528c2ecf20Sopenharmony_ci [CAP_TESTMODE] = 0x38U, 538c2ecf20Sopenharmony_ci [OP_USBCMD] = 0x00U, 548c2ecf20Sopenharmony_ci [OP_USBSTS] = 0x04U, 558c2ecf20Sopenharmony_ci [OP_USBINTR] = 0x08U, 568c2ecf20Sopenharmony_ci [OP_DEVICEADDR] = 0x14U, 578c2ecf20Sopenharmony_ci [OP_ENDPTLISTADDR] = 0x18U, 588c2ecf20Sopenharmony_ci [OP_TTCTRL] = 0x1CU, 598c2ecf20Sopenharmony_ci [OP_BURSTSIZE] = 0x20U, 608c2ecf20Sopenharmony_ci [OP_ULPI_VIEWPORT] = 0x30U, 618c2ecf20Sopenharmony_ci [OP_PORTSC] = 0x44U, 628c2ecf20Sopenharmony_ci [OP_DEVLC] = 0x84U, 638c2ecf20Sopenharmony_ci [OP_OTGSC] = 0x64U, 648c2ecf20Sopenharmony_ci [OP_USBMODE] = 0x68U, 658c2ecf20Sopenharmony_ci [OP_ENDPTSETUPSTAT] = 0x6CU, 668c2ecf20Sopenharmony_ci [OP_ENDPTPRIME] = 0x70U, 678c2ecf20Sopenharmony_ci [OP_ENDPTFLUSH] = 0x74U, 688c2ecf20Sopenharmony_ci [OP_ENDPTSTAT] = 0x78U, 698c2ecf20Sopenharmony_ci [OP_ENDPTCOMPLETE] = 0x7CU, 708c2ecf20Sopenharmony_ci [OP_ENDPTCTRL] = 0x80U, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const u8 ci_regs_lpm[] = { 748c2ecf20Sopenharmony_ci [CAP_CAPLENGTH] = 0x00U, 758c2ecf20Sopenharmony_ci [CAP_HCCPARAMS] = 0x08U, 768c2ecf20Sopenharmony_ci [CAP_DCCPARAMS] = 0x24U, 778c2ecf20Sopenharmony_ci [CAP_TESTMODE] = 0xFCU, 788c2ecf20Sopenharmony_ci [OP_USBCMD] = 0x00U, 798c2ecf20Sopenharmony_ci [OP_USBSTS] = 0x04U, 808c2ecf20Sopenharmony_ci [OP_USBINTR] = 0x08U, 818c2ecf20Sopenharmony_ci [OP_DEVICEADDR] = 0x14U, 828c2ecf20Sopenharmony_ci [OP_ENDPTLISTADDR] = 0x18U, 838c2ecf20Sopenharmony_ci [OP_TTCTRL] = 0x1CU, 848c2ecf20Sopenharmony_ci [OP_BURSTSIZE] = 0x20U, 858c2ecf20Sopenharmony_ci [OP_ULPI_VIEWPORT] = 0x30U, 868c2ecf20Sopenharmony_ci [OP_PORTSC] = 0x44U, 878c2ecf20Sopenharmony_ci [OP_DEVLC] = 0x84U, 888c2ecf20Sopenharmony_ci [OP_OTGSC] = 0xC4U, 898c2ecf20Sopenharmony_ci [OP_USBMODE] = 0xC8U, 908c2ecf20Sopenharmony_ci [OP_ENDPTSETUPSTAT] = 0xD8U, 918c2ecf20Sopenharmony_ci [OP_ENDPTPRIME] = 0xDCU, 928c2ecf20Sopenharmony_ci [OP_ENDPTFLUSH] = 0xE0U, 938c2ecf20Sopenharmony_ci [OP_ENDPTSTAT] = 0xE4U, 948c2ecf20Sopenharmony_ci [OP_ENDPTCOMPLETE] = 0xE8U, 958c2ecf20Sopenharmony_ci [OP_ENDPTCTRL] = 0xECU, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int i; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (i = 0; i < OP_ENDPTCTRL; i++) 1038c2ecf20Sopenharmony_ci ci->hw_bank.regmap[i] = 1048c2ecf20Sopenharmony_ci (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + 1058c2ecf20Sopenharmony_ci (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci for (; i <= OP_LAST; i++) 1088c2ecf20Sopenharmony_ci ci->hw_bank.regmap[i] = ci->hw_bank.op + 1098c2ecf20Sopenharmony_ci 4 * (i - OP_ENDPTCTRL) + 1108c2ecf20Sopenharmony_ci (is_lpm 1118c2ecf20Sopenharmony_ci ? ci_regs_lpm[OP_ENDPTCTRL] 1128c2ecf20Sopenharmony_ci : ci_regs_nolpm[OP_ENDPTCTRL]); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic enum ci_revision ci_get_revision(struct ci_hdrc *ci) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION); 1198c2ecf20Sopenharmony_ci enum ci_revision rev = CI_REVISION_UNKNOWN; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (ver == 0x2) { 1228c2ecf20Sopenharmony_ci rev = hw_read_id_reg(ci, ID_ID, REVISION) 1238c2ecf20Sopenharmony_ci >> __ffs(REVISION); 1248c2ecf20Sopenharmony_ci rev += CI_REVISION_20; 1258c2ecf20Sopenharmony_ci } else if (ver == 0x0) { 1268c2ecf20Sopenharmony_ci rev = CI_REVISION_1X; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return rev; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/** 1338c2ecf20Sopenharmony_ci * hw_read_intr_enable: returns interrupt enable register 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * @ci: the controller 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * This function returns register data 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ciu32 hw_read_intr_enable(struct ci_hdrc *ci) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return hw_read(ci, OP_USBINTR, ~0); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/** 1458c2ecf20Sopenharmony_ci * hw_read_intr_status: returns interrupt status register 1468c2ecf20Sopenharmony_ci * 1478c2ecf20Sopenharmony_ci * @ci: the controller 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * This function returns register data 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ciu32 hw_read_intr_status(struct ci_hdrc *ci) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return hw_read(ci, OP_USBSTS, ~0); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * hw_port_test_set: writes port test mode (execute without interruption) 1588c2ecf20Sopenharmony_ci * @ci: the controller 1598c2ecf20Sopenharmony_ci * @mode: new value 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * This function returns an error code 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ciint hw_port_test_set(struct ci_hdrc *ci, u8 mode) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci const u8 TEST_MODE_MAX = 7; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (mode > TEST_MODE_MAX) 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/** 1758c2ecf20Sopenharmony_ci * hw_port_test_get: reads port test mode value 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * @ci: the controller 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * This function returns port test mode value 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ciu8 hw_port_test_get(struct ci_hdrc *ci) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void hw_wait_phy_stable(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * The phy needs some delay to output the stable status from low 1908c2ecf20Sopenharmony_ci * power mode. And for OTGSC, the status inputs are debounced 1918c2ecf20Sopenharmony_ci * using a 1 ms time constant, so, delay 2ms for controller to get 1928c2ecf20Sopenharmony_ci * the stable status, like vbus and id when the phy leaves low power. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci usleep_range(2000, 2500); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* The PHY enters/leaves low power mode */ 1988c2ecf20Sopenharmony_cistatic void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; 2018c2ecf20Sopenharmony_ci bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (enable && !lpm) 2048c2ecf20Sopenharmony_ci hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 2058c2ecf20Sopenharmony_ci PORTSC_PHCD(ci->hw_bank.lpm)); 2068c2ecf20Sopenharmony_ci else if (!enable && lpm) 2078c2ecf20Sopenharmony_ci hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 2088c2ecf20Sopenharmony_ci 0); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int hw_device_init(struct ci_hdrc *ci, void __iomem *base) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci u32 reg; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* bank is a module variable */ 2168c2ecf20Sopenharmony_ci ci->hw_bank.abs = base; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ci->hw_bank.cap = ci->hw_bank.abs; 2198c2ecf20Sopenharmony_ci ci->hw_bank.cap += ci->platdata->capoffset; 2208c2ecf20Sopenharmony_ci ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hw_alloc_regmap(ci, false); 2238c2ecf20Sopenharmony_ci reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> 2248c2ecf20Sopenharmony_ci __ffs(HCCPARAMS_LEN); 2258c2ecf20Sopenharmony_ci ci->hw_bank.lpm = reg; 2268c2ecf20Sopenharmony_ci if (reg) 2278c2ecf20Sopenharmony_ci hw_alloc_regmap(ci, !!reg); 2288c2ecf20Sopenharmony_ci ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; 2298c2ecf20Sopenharmony_ci ci->hw_bank.size += OP_LAST; 2308c2ecf20Sopenharmony_ci ci->hw_bank.size /= sizeof(u32); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> 2338c2ecf20Sopenharmony_ci __ffs(DCCPARAMS_DEN); 2348c2ecf20Sopenharmony_ci ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (ci->hw_ep_max > ENDPT_MAX) 2378c2ecf20Sopenharmony_ci return -ENODEV; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ci_hdrc_enter_lpm(ci, false); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Disable all interrupts bits */ 2428c2ecf20Sopenharmony_ci hw_write(ci, OP_USBINTR, 0xffffffff, 0); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Clear all interrupts status bits*/ 2458c2ecf20Sopenharmony_ci hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ci->rev = ci_get_revision(ci); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci dev_dbg(ci->dev, 2508c2ecf20Sopenharmony_ci "revision: %d, lpm: %d; cap: %px op: %px\n", 2518c2ecf20Sopenharmony_ci ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* setup lock mode ? */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* ENDPTSETUPSTAT is '0' by default */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_civoid hw_phymode_configure(struct ci_hdrc *ci) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci u32 portsc, lpm, sts = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci switch (ci->platdata->phy_mode) { 2678c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_UTMI: 2688c2ecf20Sopenharmony_ci portsc = PORTSC_PTS(PTS_UTMI); 2698c2ecf20Sopenharmony_ci lpm = DEVLC_PTS(PTS_UTMI); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_UTMIW: 2728c2ecf20Sopenharmony_ci portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW; 2738c2ecf20Sopenharmony_ci lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_ULPI: 2768c2ecf20Sopenharmony_ci portsc = PORTSC_PTS(PTS_ULPI); 2778c2ecf20Sopenharmony_ci lpm = DEVLC_PTS(PTS_ULPI); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_SERIAL: 2808c2ecf20Sopenharmony_ci portsc = PORTSC_PTS(PTS_SERIAL); 2818c2ecf20Sopenharmony_ci lpm = DEVLC_PTS(PTS_SERIAL); 2828c2ecf20Sopenharmony_ci sts = 1; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_HSIC: 2858c2ecf20Sopenharmony_ci portsc = PORTSC_PTS(PTS_HSIC); 2868c2ecf20Sopenharmony_ci lpm = DEVLC_PTS(PTS_HSIC); 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci default: 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (ci->hw_bank.lpm) { 2938c2ecf20Sopenharmony_ci hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); 2948c2ecf20Sopenharmony_ci if (sts) 2958c2ecf20Sopenharmony_ci hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); 2988c2ecf20Sopenharmony_ci if (sts) 2998c2ecf20Sopenharmony_ci hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hw_phymode_configure); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy 3068c2ecf20Sopenharmony_ci * interfaces 3078c2ecf20Sopenharmony_ci * @ci: the controller 3088c2ecf20Sopenharmony_ci * 3098c2ecf20Sopenharmony_ci * This function returns an error code if the phy failed to init 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic int _ci_usb_phy_init(struct ci_hdrc *ci) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci int ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (ci->phy) { 3168c2ecf20Sopenharmony_ci ret = phy_init(ci->phy); 3178c2ecf20Sopenharmony_ci if (ret) 3188c2ecf20Sopenharmony_ci return ret; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = phy_power_on(ci->phy); 3218c2ecf20Sopenharmony_ci if (ret) { 3228c2ecf20Sopenharmony_ci phy_exit(ci->phy); 3238c2ecf20Sopenharmony_ci return ret; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci } else { 3268c2ecf20Sopenharmony_ci ret = usb_phy_init(ci->usb_phy); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/** 3338c2ecf20Sopenharmony_ci * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy 3348c2ecf20Sopenharmony_ci * interfaces 3358c2ecf20Sopenharmony_ci * @ci: the controller 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_cistatic void ci_usb_phy_exit(struct ci_hdrc *ci) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL) 3408c2ecf20Sopenharmony_ci return; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (ci->phy) { 3438c2ecf20Sopenharmony_ci phy_power_off(ci->phy); 3448c2ecf20Sopenharmony_ci phy_exit(ci->phy); 3458c2ecf20Sopenharmony_ci } else { 3468c2ecf20Sopenharmony_ci usb_phy_shutdown(ci->usb_phy); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/** 3518c2ecf20Sopenharmony_ci * ci_usb_phy_init: initialize phy according to different phy type 3528c2ecf20Sopenharmony_ci * @ci: the controller 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci * This function returns an error code if usb_phy_init has failed 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_cistatic int ci_usb_phy_init(struct ci_hdrc *ci) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci int ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL) 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci switch (ci->platdata->phy_mode) { 3648c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_UTMI: 3658c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_UTMIW: 3668c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_HSIC: 3678c2ecf20Sopenharmony_ci ret = _ci_usb_phy_init(ci); 3688c2ecf20Sopenharmony_ci if (!ret) 3698c2ecf20Sopenharmony_ci hw_wait_phy_stable(); 3708c2ecf20Sopenharmony_ci else 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci hw_phymode_configure(ci); 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_ULPI: 3758c2ecf20Sopenharmony_ci case USBPHY_INTERFACE_MODE_SERIAL: 3768c2ecf20Sopenharmony_ci hw_phymode_configure(ci); 3778c2ecf20Sopenharmony_ci ret = _ci_usb_phy_init(ci); 3788c2ecf20Sopenharmony_ci if (ret) 3798c2ecf20Sopenharmony_ci return ret; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci default: 3828c2ecf20Sopenharmony_ci ret = _ci_usb_phy_init(ci); 3838c2ecf20Sopenharmony_ci if (!ret) 3848c2ecf20Sopenharmony_ci hw_wait_phy_stable(); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/** 3928c2ecf20Sopenharmony_ci * ci_platform_configure: do controller configure 3938c2ecf20Sopenharmony_ci * @ci: the controller 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_civoid ci_platform_configure(struct ci_hdrc *ci) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci bool is_device_mode, is_host_mode; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC; 4018c2ecf20Sopenharmony_ci is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (is_device_mode) { 4048c2ecf20Sopenharmony_ci phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING) 4078c2ecf20Sopenharmony_ci hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, 4088c2ecf20Sopenharmony_ci USBMODE_CI_SDIS); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (is_host_mode) { 4128c2ecf20Sopenharmony_ci phy_set_mode(ci->phy, PHY_MODE_USB_HOST); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING) 4158c2ecf20Sopenharmony_ci hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, 4168c2ecf20Sopenharmony_ci USBMODE_CI_SDIS); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { 4208c2ecf20Sopenharmony_ci if (ci->hw_bank.lpm) 4218c2ecf20Sopenharmony_ci hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); 4228c2ecf20Sopenharmony_ci else 4238c2ecf20Sopenharmony_ci hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA) 4278c2ecf20Sopenharmony_ci hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST) 4328c2ecf20Sopenharmony_ci hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK, 4338c2ecf20Sopenharmony_ci ci->platdata->ahb_burst_config); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* override burst size, take effect only when ahb_burst_config is 0 */ 4368c2ecf20Sopenharmony_ci if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) { 4378c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST) 4388c2ecf20Sopenharmony_ci hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK, 4398c2ecf20Sopenharmony_ci ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK)); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST) 4428c2ecf20Sopenharmony_ci hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK, 4438c2ecf20Sopenharmony_ci ci->platdata->rx_burst_size); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/** 4488c2ecf20Sopenharmony_ci * hw_controller_reset: do controller reset 4498c2ecf20Sopenharmony_ci * @ci: the controller 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * This function returns an error code 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_cistatic int hw_controller_reset(struct ci_hdrc *ci) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci int count = 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); 4588c2ecf20Sopenharmony_ci while (hw_read(ci, OP_USBCMD, USBCMD_RST)) { 4598c2ecf20Sopenharmony_ci udelay(10); 4608c2ecf20Sopenharmony_ci if (count++ > 1000) 4618c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/** 4688c2ecf20Sopenharmony_ci * hw_device_reset: resets chip (execute without interruption) 4698c2ecf20Sopenharmony_ci * @ci: the controller 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * This function returns an error code 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ciint hw_device_reset(struct ci_hdrc *ci) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci int ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* should flush & stop before reset */ 4788c2ecf20Sopenharmony_ci hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); 4798c2ecf20Sopenharmony_ci hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci ret = hw_controller_reset(ci); 4828c2ecf20Sopenharmony_ci if (ret) { 4838c2ecf20Sopenharmony_ci dev_err(ci->dev, "error resetting controller, ret=%d\n", ret); 4848c2ecf20Sopenharmony_ci return ret; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (ci->platdata->notify_event) { 4888c2ecf20Sopenharmony_ci ret = ci->platdata->notify_event(ci, 4898c2ecf20Sopenharmony_ci CI_HDRC_CONTROLLER_RESET_EVENT); 4908c2ecf20Sopenharmony_ci if (ret) 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* USBMODE should be configured step by step */ 4958c2ecf20Sopenharmony_ci hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); 4968c2ecf20Sopenharmony_ci hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); 4978c2ecf20Sopenharmony_ci /* HW >= 2.3 */ 4988c2ecf20Sopenharmony_ci hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { 5018c2ecf20Sopenharmony_ci dev_err(ci->dev, "cannot enter in %s device mode\n", 5028c2ecf20Sopenharmony_ci ci_role(ci)->name); 5038c2ecf20Sopenharmony_ci dev_err(ci->dev, "lpm = %i\n", ci->hw_bank.lpm); 5048c2ecf20Sopenharmony_ci return -ENODEV; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ci_platform_configure(ci); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic irqreturn_t ci_irq_handler(int irq, void *data) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct ci_hdrc *ci = data; 5158c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 5168c2ecf20Sopenharmony_ci u32 otgsc = 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (ci->in_lpm) { 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * If we already have a wakeup irq pending there, 5218c2ecf20Sopenharmony_ci * let's just return to wait resume finished firstly. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci if (ci->wakeup_int) 5248c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 5278c2ecf20Sopenharmony_ci ci->wakeup_int = true; 5288c2ecf20Sopenharmony_ci pm_runtime_get(ci->dev); 5298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (ci->is_otg) { 5338c2ecf20Sopenharmony_ci otgsc = hw_read_otgsc(ci, ~0); 5348c2ecf20Sopenharmony_ci if (ci_otg_is_fsm_mode(ci)) { 5358c2ecf20Sopenharmony_ci ret = ci_otg_fsm_irq(ci); 5368c2ecf20Sopenharmony_ci if (ret == IRQ_HANDLED) 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * Handle id change interrupt, it indicates device/host function 5438c2ecf20Sopenharmony_ci * switch. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { 5468c2ecf20Sopenharmony_ci ci->id_event = true; 5478c2ecf20Sopenharmony_ci /* Clear ID change irq status */ 5488c2ecf20Sopenharmony_ci hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); 5498c2ecf20Sopenharmony_ci ci_otg_queue_work(ci); 5508c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* 5548c2ecf20Sopenharmony_ci * Handle vbus change interrupt, it indicates device connection 5558c2ecf20Sopenharmony_ci * and disconnection events. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { 5588c2ecf20Sopenharmony_ci ci->b_sess_valid_event = true; 5598c2ecf20Sopenharmony_ci /* Clear BSV irq */ 5608c2ecf20Sopenharmony_ci hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); 5618c2ecf20Sopenharmony_ci ci_otg_queue_work(ci); 5628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Handle device/host interrupt */ 5668c2ecf20Sopenharmony_ci if (ci->role != CI_ROLE_END) 5678c2ecf20Sopenharmony_ci ret = ci_role(ci)->irq(ci); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return ret; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic void ci_irq(struct ci_hdrc *ci) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci unsigned long flags; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci local_irq_save(flags); 5778c2ecf20Sopenharmony_ci ci_irq_handler(ci->irq, ci); 5788c2ecf20Sopenharmony_ci local_irq_restore(flags); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int ci_cable_notifier(struct notifier_block *nb, unsigned long event, 5828c2ecf20Sopenharmony_ci void *ptr) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb); 5858c2ecf20Sopenharmony_ci struct ci_hdrc *ci = cbl->ci; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci cbl->connected = event; 5888c2ecf20Sopenharmony_ci cbl->changed = true; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci ci_irq(ci); 5918c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic enum usb_role ci_usb_role_switch_get(struct usb_role_switch *sw) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); 5978c2ecf20Sopenharmony_ci enum usb_role role; 5988c2ecf20Sopenharmony_ci unsigned long flags; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci spin_lock_irqsave(&ci->lock, flags); 6018c2ecf20Sopenharmony_ci role = ci_role_to_usb_role(ci); 6028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ci->lock, flags); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return role; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic int ci_usb_role_switch_set(struct usb_role_switch *sw, 6088c2ecf20Sopenharmony_ci enum usb_role role) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); 6118c2ecf20Sopenharmony_ci struct ci_hdrc_cable *cable = NULL; 6128c2ecf20Sopenharmony_ci enum usb_role current_role = ci_role_to_usb_role(ci); 6138c2ecf20Sopenharmony_ci enum ci_role ci_role = usb_role_to_ci_role(role); 6148c2ecf20Sopenharmony_ci unsigned long flags; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if ((ci_role != CI_ROLE_END && !ci->roles[ci_role]) || 6178c2ecf20Sopenharmony_ci (current_role == role)) 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci pm_runtime_get_sync(ci->dev); 6218c2ecf20Sopenharmony_ci /* Stop current role */ 6228c2ecf20Sopenharmony_ci spin_lock_irqsave(&ci->lock, flags); 6238c2ecf20Sopenharmony_ci if (current_role == USB_ROLE_DEVICE) 6248c2ecf20Sopenharmony_ci cable = &ci->platdata->vbus_extcon; 6258c2ecf20Sopenharmony_ci else if (current_role == USB_ROLE_HOST) 6268c2ecf20Sopenharmony_ci cable = &ci->platdata->id_extcon; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (cable) { 6298c2ecf20Sopenharmony_ci cable->changed = true; 6308c2ecf20Sopenharmony_ci cable->connected = false; 6318c2ecf20Sopenharmony_ci ci_irq(ci); 6328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ci->lock, flags); 6338c2ecf20Sopenharmony_ci if (ci->wq && role != USB_ROLE_NONE) 6348c2ecf20Sopenharmony_ci flush_workqueue(ci->wq); 6358c2ecf20Sopenharmony_ci spin_lock_irqsave(&ci->lock, flags); 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci cable = NULL; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Start target role */ 6418c2ecf20Sopenharmony_ci if (role == USB_ROLE_DEVICE) 6428c2ecf20Sopenharmony_ci cable = &ci->platdata->vbus_extcon; 6438c2ecf20Sopenharmony_ci else if (role == USB_ROLE_HOST) 6448c2ecf20Sopenharmony_ci cable = &ci->platdata->id_extcon; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (cable) { 6478c2ecf20Sopenharmony_ci cable->changed = true; 6488c2ecf20Sopenharmony_ci cable->connected = true; 6498c2ecf20Sopenharmony_ci ci_irq(ci); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ci->lock, flags); 6528c2ecf20Sopenharmony_ci pm_runtime_put_sync(ci->dev); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic struct usb_role_switch_desc ci_role_switch = { 6588c2ecf20Sopenharmony_ci .set = ci_usb_role_switch_set, 6598c2ecf20Sopenharmony_ci .get = ci_usb_role_switch_get, 6608c2ecf20Sopenharmony_ci .allow_userspace_control = true, 6618c2ecf20Sopenharmony_ci}; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int ci_get_platdata(struct device *dev, 6648c2ecf20Sopenharmony_ci struct ci_hdrc_platform_data *platdata) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct extcon_dev *ext_vbus, *ext_id; 6678c2ecf20Sopenharmony_ci struct ci_hdrc_cable *cable; 6688c2ecf20Sopenharmony_ci int ret; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (!platdata->phy_mode) 6718c2ecf20Sopenharmony_ci platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (!platdata->dr_mode) 6748c2ecf20Sopenharmony_ci platdata->dr_mode = usb_get_dr_mode(dev); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) 6778c2ecf20Sopenharmony_ci platdata->dr_mode = USB_DR_MODE_OTG; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { 6808c2ecf20Sopenharmony_ci /* Get the vbus regulator */ 6818c2ecf20Sopenharmony_ci platdata->reg_vbus = devm_regulator_get_optional(dev, "vbus"); 6828c2ecf20Sopenharmony_ci if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { 6838c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 6848c2ecf20Sopenharmony_ci } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { 6858c2ecf20Sopenharmony_ci /* no vbus regulator is needed */ 6868c2ecf20Sopenharmony_ci platdata->reg_vbus = NULL; 6878c2ecf20Sopenharmony_ci } else if (IS_ERR(platdata->reg_vbus)) { 6888c2ecf20Sopenharmony_ci dev_err(dev, "Getting regulator error: %ld\n", 6898c2ecf20Sopenharmony_ci PTR_ERR(platdata->reg_vbus)); 6908c2ecf20Sopenharmony_ci return PTR_ERR(platdata->reg_vbus); 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci /* Get TPL support */ 6938c2ecf20Sopenharmony_ci if (!platdata->tpl_support) 6948c2ecf20Sopenharmony_ci platdata->tpl_support = 6958c2ecf20Sopenharmony_ci of_usb_host_tpl_support(dev->of_node); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (platdata->dr_mode == USB_DR_MODE_OTG) { 6998c2ecf20Sopenharmony_ci /* We can support HNP and SRP of OTG 2.0 */ 7008c2ecf20Sopenharmony_ci platdata->ci_otg_caps.otg_rev = 0x0200; 7018c2ecf20Sopenharmony_ci platdata->ci_otg_caps.hnp_support = true; 7028c2ecf20Sopenharmony_ci platdata->ci_otg_caps.srp_support = true; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* Update otg capabilities by DT properties */ 7058c2ecf20Sopenharmony_ci ret = of_usb_update_otg_caps(dev->of_node, 7068c2ecf20Sopenharmony_ci &platdata->ci_otg_caps); 7078c2ecf20Sopenharmony_ci if (ret) 7088c2ecf20Sopenharmony_ci return ret; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) 7128c2ecf20Sopenharmony_ci platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci of_property_read_u32(dev->of_node, "phy-clkgate-delay-us", 7158c2ecf20Sopenharmony_ci &platdata->phy_clkgate_delay_us); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci platdata->itc_setting = 1; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci of_property_read_u32(dev->of_node, "itc-setting", 7208c2ecf20Sopenharmony_ci &platdata->itc_setting); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "ahb-burst-config", 7238c2ecf20Sopenharmony_ci &platdata->ahb_burst_config); 7248c2ecf20Sopenharmony_ci if (!ret) { 7258c2ecf20Sopenharmony_ci platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST; 7268c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 7278c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ahb-burst-config\n"); 7288c2ecf20Sopenharmony_ci return ret; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword", 7328c2ecf20Sopenharmony_ci &platdata->tx_burst_size); 7338c2ecf20Sopenharmony_ci if (!ret) { 7348c2ecf20Sopenharmony_ci platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST; 7358c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 7368c2ecf20Sopenharmony_ci dev_err(dev, "failed to get tx-burst-size-dword\n"); 7378c2ecf20Sopenharmony_ci return ret; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword", 7418c2ecf20Sopenharmony_ci &platdata->rx_burst_size); 7428c2ecf20Sopenharmony_ci if (!ret) { 7438c2ecf20Sopenharmony_ci platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 7448c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 7458c2ecf20Sopenharmony_ci dev_err(dev, "failed to get rx-burst-size-dword\n"); 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL)) 7508c2ecf20Sopenharmony_ci platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci ext_id = ERR_PTR(-ENODEV); 7538c2ecf20Sopenharmony_ci ext_vbus = ERR_PTR(-ENODEV); 7548c2ecf20Sopenharmony_ci if (of_property_read_bool(dev->of_node, "extcon")) { 7558c2ecf20Sopenharmony_ci /* Each one of them is not mandatory */ 7568c2ecf20Sopenharmony_ci ext_vbus = extcon_get_edev_by_phandle(dev, 0); 7578c2ecf20Sopenharmony_ci if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) 7588c2ecf20Sopenharmony_ci return PTR_ERR(ext_vbus); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci ext_id = extcon_get_edev_by_phandle(dev, 1); 7618c2ecf20Sopenharmony_ci if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) 7628c2ecf20Sopenharmony_ci return PTR_ERR(ext_id); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci cable = &platdata->vbus_extcon; 7668c2ecf20Sopenharmony_ci cable->nb.notifier_call = ci_cable_notifier; 7678c2ecf20Sopenharmony_ci cable->edev = ext_vbus; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!IS_ERR(ext_vbus)) { 7708c2ecf20Sopenharmony_ci ret = extcon_get_state(cable->edev, EXTCON_USB); 7718c2ecf20Sopenharmony_ci if (ret) 7728c2ecf20Sopenharmony_ci cable->connected = true; 7738c2ecf20Sopenharmony_ci else 7748c2ecf20Sopenharmony_ci cable->connected = false; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci cable = &platdata->id_extcon; 7788c2ecf20Sopenharmony_ci cable->nb.notifier_call = ci_cable_notifier; 7798c2ecf20Sopenharmony_ci cable->edev = ext_id; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (!IS_ERR(ext_id)) { 7828c2ecf20Sopenharmony_ci ret = extcon_get_state(cable->edev, EXTCON_USB_HOST); 7838c2ecf20Sopenharmony_ci if (ret) 7848c2ecf20Sopenharmony_ci cable->connected = true; 7858c2ecf20Sopenharmony_ci else 7868c2ecf20Sopenharmony_ci cable->connected = false; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (device_property_read_bool(dev, "usb-role-switch")) 7908c2ecf20Sopenharmony_ci ci_role_switch.fwnode = dev->fwnode; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci platdata->pctl = devm_pinctrl_get(dev); 7938c2ecf20Sopenharmony_ci if (!IS_ERR(platdata->pctl)) { 7948c2ecf20Sopenharmony_ci struct pinctrl_state *p; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci p = pinctrl_lookup_state(platdata->pctl, "default"); 7978c2ecf20Sopenharmony_ci if (!IS_ERR(p)) 7988c2ecf20Sopenharmony_ci platdata->pins_default = p; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci p = pinctrl_lookup_state(platdata->pctl, "host"); 8018c2ecf20Sopenharmony_ci if (!IS_ERR(p)) 8028c2ecf20Sopenharmony_ci platdata->pins_host = p; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci p = pinctrl_lookup_state(platdata->pctl, "device"); 8058c2ecf20Sopenharmony_ci if (!IS_ERR(p)) 8068c2ecf20Sopenharmony_ci platdata->pins_device = p; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int ci_extcon_register(struct ci_hdrc *ci) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct ci_hdrc_cable *id, *vbus; 8158c2ecf20Sopenharmony_ci int ret; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci id = &ci->platdata->id_extcon; 8188c2ecf20Sopenharmony_ci id->ci = ci; 8198c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(id->edev)) { 8208c2ecf20Sopenharmony_ci ret = devm_extcon_register_notifier(ci->dev, id->edev, 8218c2ecf20Sopenharmony_ci EXTCON_USB_HOST, &id->nb); 8228c2ecf20Sopenharmony_ci if (ret < 0) { 8238c2ecf20Sopenharmony_ci dev_err(ci->dev, "register ID failed\n"); 8248c2ecf20Sopenharmony_ci return ret; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci vbus = &ci->platdata->vbus_extcon; 8298c2ecf20Sopenharmony_ci vbus->ci = ci; 8308c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(vbus->edev)) { 8318c2ecf20Sopenharmony_ci ret = devm_extcon_register_notifier(ci->dev, vbus->edev, 8328c2ecf20Sopenharmony_ci EXTCON_USB, &vbus->nb); 8338c2ecf20Sopenharmony_ci if (ret < 0) { 8348c2ecf20Sopenharmony_ci dev_err(ci->dev, "register VBUS failed\n"); 8358c2ecf20Sopenharmony_ci return ret; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return 0; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic DEFINE_IDA(ci_ida); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistruct platform_device *ci_hdrc_add_device(struct device *dev, 8458c2ecf20Sopenharmony_ci struct resource *res, int nres, 8468c2ecf20Sopenharmony_ci struct ci_hdrc_platform_data *platdata) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci struct platform_device *pdev; 8498c2ecf20Sopenharmony_ci int id, ret; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = ci_get_platdata(dev, platdata); 8528c2ecf20Sopenharmony_ci if (ret) 8538c2ecf20Sopenharmony_ci return ERR_PTR(ret); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); 8568c2ecf20Sopenharmony_ci if (id < 0) 8578c2ecf20Sopenharmony_ci return ERR_PTR(id); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci pdev = platform_device_alloc("ci_hdrc", id); 8608c2ecf20Sopenharmony_ci if (!pdev) { 8618c2ecf20Sopenharmony_ci ret = -ENOMEM; 8628c2ecf20Sopenharmony_ci goto put_id; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci pdev->dev.parent = dev; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ret = platform_device_add_resources(pdev, res, nres); 8688c2ecf20Sopenharmony_ci if (ret) 8698c2ecf20Sopenharmony_ci goto err; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); 8728c2ecf20Sopenharmony_ci if (ret) 8738c2ecf20Sopenharmony_ci goto err; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci ret = platform_device_add(pdev); 8768c2ecf20Sopenharmony_ci if (ret) 8778c2ecf20Sopenharmony_ci goto err; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return pdev; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cierr: 8828c2ecf20Sopenharmony_ci platform_device_put(pdev); 8838c2ecf20Sopenharmony_ciput_id: 8848c2ecf20Sopenharmony_ci ida_simple_remove(&ci_ida, id); 8858c2ecf20Sopenharmony_ci return ERR_PTR(ret); 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ci_hdrc_add_device); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_civoid ci_hdrc_remove_device(struct platform_device *pdev) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci int id = pdev->id; 8928c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 8938c2ecf20Sopenharmony_ci ida_simple_remove(&ci_ida, id); 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ci_hdrc_remove_device); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci/** 8988c2ecf20Sopenharmony_ci * ci_hdrc_query_available_role: get runtime available operation mode 8998c2ecf20Sopenharmony_ci * 9008c2ecf20Sopenharmony_ci * The glue layer can get current operation mode (host/peripheral/otg) 9018c2ecf20Sopenharmony_ci * This function should be called after ci core device has created. 9028c2ecf20Sopenharmony_ci * 9038c2ecf20Sopenharmony_ci * @pdev: the platform device of ci core. 9048c2ecf20Sopenharmony_ci * 9058c2ecf20Sopenharmony_ci * Return runtime usb_dr_mode. 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_cienum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct ci_hdrc *ci = platform_get_drvdata(pdev); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (!ci) 9128c2ecf20Sopenharmony_ci return USB_DR_MODE_UNKNOWN; 9138c2ecf20Sopenharmony_ci if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) 9148c2ecf20Sopenharmony_ci return USB_DR_MODE_OTG; 9158c2ecf20Sopenharmony_ci else if (ci->roles[CI_ROLE_HOST]) 9168c2ecf20Sopenharmony_ci return USB_DR_MODE_HOST; 9178c2ecf20Sopenharmony_ci else if (ci->roles[CI_ROLE_GADGET]) 9188c2ecf20Sopenharmony_ci return USB_DR_MODE_PERIPHERAL; 9198c2ecf20Sopenharmony_ci else 9208c2ecf20Sopenharmony_ci return USB_DR_MODE_UNKNOWN; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ci_hdrc_query_available_role); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic inline void ci_role_destroy(struct ci_hdrc *ci) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci ci_hdrc_gadget_destroy(ci); 9278c2ecf20Sopenharmony_ci ci_hdrc_host_destroy(ci); 9288c2ecf20Sopenharmony_ci if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) 9298c2ecf20Sopenharmony_ci ci_hdrc_otg_destroy(ci); 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void ci_get_otg_capable(struct ci_hdrc *ci) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) 9358c2ecf20Sopenharmony_ci ci->is_otg = false; 9368c2ecf20Sopenharmony_ci else 9378c2ecf20Sopenharmony_ci ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, 9388c2ecf20Sopenharmony_ci DCCPARAMS_DC | DCCPARAMS_HC) 9398c2ecf20Sopenharmony_ci == (DCCPARAMS_DC | DCCPARAMS_HC)); 9408c2ecf20Sopenharmony_ci if (ci->is_otg) { 9418c2ecf20Sopenharmony_ci dev_dbg(ci->dev, "It is OTG capable controller\n"); 9428c2ecf20Sopenharmony_ci /* Disable and clear all OTG irq */ 9438c2ecf20Sopenharmony_ci hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 9448c2ecf20Sopenharmony_ci OTGSC_INT_STATUS_BITS); 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic ssize_t role_show(struct device *dev, struct device_attribute *attr, 9498c2ecf20Sopenharmony_ci char *buf) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct ci_hdrc *ci = dev_get_drvdata(dev); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (ci->role != CI_ROLE_END) 9548c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", ci_role(ci)->name); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic ssize_t role_store(struct device *dev, 9608c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t n) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct ci_hdrc *ci = dev_get_drvdata(dev); 9638c2ecf20Sopenharmony_ci enum ci_role role; 9648c2ecf20Sopenharmony_ci int ret; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) { 9678c2ecf20Sopenharmony_ci dev_warn(dev, "Current configuration is not dual-role, quit\n"); 9688c2ecf20Sopenharmony_ci return -EPERM; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++) 9728c2ecf20Sopenharmony_ci if (!strncmp(buf, ci->roles[role]->name, 9738c2ecf20Sopenharmony_ci strlen(ci->roles[role]->name))) 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (role == CI_ROLE_END) 9778c2ecf20Sopenharmony_ci return -EINVAL; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci mutex_lock(&ci->mutex); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (role == ci->role) { 9828c2ecf20Sopenharmony_ci mutex_unlock(&ci->mutex); 9838c2ecf20Sopenharmony_ci return n; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 9878c2ecf20Sopenharmony_ci disable_irq(ci->irq); 9888c2ecf20Sopenharmony_ci ci_role_stop(ci); 9898c2ecf20Sopenharmony_ci ret = ci_role_start(ci, role); 9908c2ecf20Sopenharmony_ci if (!ret && ci->role == CI_ROLE_GADGET) 9918c2ecf20Sopenharmony_ci ci_handle_vbus_change(ci); 9928c2ecf20Sopenharmony_ci enable_irq(ci->irq); 9938c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 9948c2ecf20Sopenharmony_ci mutex_unlock(&ci->mutex); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci return (ret == 0) ? n : ret; 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(role); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic struct attribute *ci_attrs[] = { 10018c2ecf20Sopenharmony_ci &dev_attr_role.attr, 10028c2ecf20Sopenharmony_ci NULL, 10038c2ecf20Sopenharmony_ci}; 10048c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ci); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int ci_hdrc_probe(struct platform_device *pdev) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 10098c2ecf20Sopenharmony_ci struct ci_hdrc *ci; 10108c2ecf20Sopenharmony_ci struct resource *res; 10118c2ecf20Sopenharmony_ci void __iomem *base; 10128c2ecf20Sopenharmony_ci int ret; 10138c2ecf20Sopenharmony_ci enum usb_dr_mode dr_mode; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (!dev_get_platdata(dev)) { 10168c2ecf20Sopenharmony_ci dev_err(dev, "platform data missing\n"); 10178c2ecf20Sopenharmony_ci return -ENODEV; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10218c2ecf20Sopenharmony_ci base = devm_ioremap_resource(dev, res); 10228c2ecf20Sopenharmony_ci if (IS_ERR(base)) 10238c2ecf20Sopenharmony_ci return PTR_ERR(base); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); 10268c2ecf20Sopenharmony_ci if (!ci) 10278c2ecf20Sopenharmony_ci return -ENOMEM; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci spin_lock_init(&ci->lock); 10308c2ecf20Sopenharmony_ci mutex_init(&ci->mutex); 10318c2ecf20Sopenharmony_ci ci->dev = dev; 10328c2ecf20Sopenharmony_ci ci->platdata = dev_get_platdata(dev); 10338c2ecf20Sopenharmony_ci ci->imx28_write_fix = !!(ci->platdata->flags & 10348c2ecf20Sopenharmony_ci CI_HDRC_IMX28_WRITE_FIX); 10358c2ecf20Sopenharmony_ci ci->supports_runtime_pm = !!(ci->platdata->flags & 10368c2ecf20Sopenharmony_ci CI_HDRC_SUPPORTS_RUNTIME_PM); 10378c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ci); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci ret = hw_device_init(ci, base); 10408c2ecf20Sopenharmony_ci if (ret < 0) { 10418c2ecf20Sopenharmony_ci dev_err(dev, "can't initialize hardware\n"); 10428c2ecf20Sopenharmony_ci return -ENODEV; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci ret = ci_ulpi_init(ci); 10468c2ecf20Sopenharmony_ci if (ret) 10478c2ecf20Sopenharmony_ci return ret; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (ci->platdata->phy) { 10508c2ecf20Sopenharmony_ci ci->phy = ci->platdata->phy; 10518c2ecf20Sopenharmony_ci } else if (ci->platdata->usb_phy) { 10528c2ecf20Sopenharmony_ci ci->usb_phy = ci->platdata->usb_phy; 10538c2ecf20Sopenharmony_ci } else { 10548c2ecf20Sopenharmony_ci /* Look for a generic PHY first */ 10558c2ecf20Sopenharmony_ci ci->phy = devm_phy_get(dev->parent, "usb-phy"); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (PTR_ERR(ci->phy) == -EPROBE_DEFER) { 10588c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 10598c2ecf20Sopenharmony_ci goto ulpi_exit; 10608c2ecf20Sopenharmony_ci } else if (IS_ERR(ci->phy)) { 10618c2ecf20Sopenharmony_ci ci->phy = NULL; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Look for a legacy USB PHY from device-tree next */ 10658c2ecf20Sopenharmony_ci if (!ci->phy) { 10668c2ecf20Sopenharmony_ci ci->usb_phy = devm_usb_get_phy_by_phandle(dev->parent, 10678c2ecf20Sopenharmony_ci "phys", 0); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) { 10708c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 10718c2ecf20Sopenharmony_ci goto ulpi_exit; 10728c2ecf20Sopenharmony_ci } else if (IS_ERR(ci->usb_phy)) { 10738c2ecf20Sopenharmony_ci ci->usb_phy = NULL; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* Look for any registered legacy USB PHY as last resort */ 10788c2ecf20Sopenharmony_ci if (!ci->phy && !ci->usb_phy) { 10798c2ecf20Sopenharmony_ci ci->usb_phy = devm_usb_get_phy(dev->parent, 10808c2ecf20Sopenharmony_ci USB_PHY_TYPE_USB2); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) { 10838c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 10848c2ecf20Sopenharmony_ci goto ulpi_exit; 10858c2ecf20Sopenharmony_ci } else if (IS_ERR(ci->usb_phy)) { 10868c2ecf20Sopenharmony_ci ci->usb_phy = NULL; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* No USB PHY was found in the end */ 10918c2ecf20Sopenharmony_ci if (!ci->phy && !ci->usb_phy) { 10928c2ecf20Sopenharmony_ci ret = -ENXIO; 10938c2ecf20Sopenharmony_ci goto ulpi_exit; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci ret = ci_usb_phy_init(ci); 10988c2ecf20Sopenharmony_ci if (ret) { 10998c2ecf20Sopenharmony_ci dev_err(dev, "unable to init phy: %d\n", ret); 11008c2ecf20Sopenharmony_ci goto ulpi_exit; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci ci->hw_bank.phys = res->start; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci ci->irq = platform_get_irq(pdev, 0); 11068c2ecf20Sopenharmony_ci if (ci->irq < 0) { 11078c2ecf20Sopenharmony_ci ret = ci->irq; 11088c2ecf20Sopenharmony_ci goto deinit_phy; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci ci_get_otg_capable(ci); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci dr_mode = ci->platdata->dr_mode; 11148c2ecf20Sopenharmony_ci /* initialize role(s) before the interrupt is requested */ 11158c2ecf20Sopenharmony_ci if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 11168c2ecf20Sopenharmony_ci ret = ci_hdrc_host_init(ci); 11178c2ecf20Sopenharmony_ci if (ret) { 11188c2ecf20Sopenharmony_ci if (ret == -ENXIO) 11198c2ecf20Sopenharmony_ci dev_info(dev, "doesn't support host\n"); 11208c2ecf20Sopenharmony_ci else 11218c2ecf20Sopenharmony_ci goto deinit_phy; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 11268c2ecf20Sopenharmony_ci ret = ci_hdrc_gadget_init(ci); 11278c2ecf20Sopenharmony_ci if (ret) { 11288c2ecf20Sopenharmony_ci if (ret == -ENXIO) 11298c2ecf20Sopenharmony_ci dev_info(dev, "doesn't support gadget\n"); 11308c2ecf20Sopenharmony_ci else 11318c2ecf20Sopenharmony_ci goto deinit_host; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { 11368c2ecf20Sopenharmony_ci dev_err(dev, "no supported roles\n"); 11378c2ecf20Sopenharmony_ci ret = -ENODEV; 11388c2ecf20Sopenharmony_ci goto deinit_gadget; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { 11428c2ecf20Sopenharmony_ci ret = ci_hdrc_otg_init(ci); 11438c2ecf20Sopenharmony_ci if (ret) { 11448c2ecf20Sopenharmony_ci dev_err(dev, "init otg fails, ret = %d\n", ret); 11458c2ecf20Sopenharmony_ci goto deinit_gadget; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (ci_role_switch.fwnode) { 11508c2ecf20Sopenharmony_ci ci_role_switch.driver_data = ci; 11518c2ecf20Sopenharmony_ci ci->role_switch = usb_role_switch_register(dev, 11528c2ecf20Sopenharmony_ci &ci_role_switch); 11538c2ecf20Sopenharmony_ci if (IS_ERR(ci->role_switch)) { 11548c2ecf20Sopenharmony_ci ret = PTR_ERR(ci->role_switch); 11558c2ecf20Sopenharmony_ci goto deinit_otg; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { 11608c2ecf20Sopenharmony_ci if (ci->is_otg) { 11618c2ecf20Sopenharmony_ci ci->role = ci_otg_role(ci); 11628c2ecf20Sopenharmony_ci /* Enable ID change irq */ 11638c2ecf20Sopenharmony_ci hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); 11648c2ecf20Sopenharmony_ci } else { 11658c2ecf20Sopenharmony_ci /* 11668c2ecf20Sopenharmony_ci * If the controller is not OTG capable, but support 11678c2ecf20Sopenharmony_ci * role switch, the defalt role is gadget, and the 11688c2ecf20Sopenharmony_ci * user can switch it through debugfs. 11698c2ecf20Sopenharmony_ci */ 11708c2ecf20Sopenharmony_ci ci->role = CI_ROLE_GADGET; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci } else { 11738c2ecf20Sopenharmony_ci ci->role = ci->roles[CI_ROLE_HOST] 11748c2ecf20Sopenharmony_ci ? CI_ROLE_HOST 11758c2ecf20Sopenharmony_ci : CI_ROLE_GADGET; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (!ci_otg_is_fsm_mode(ci)) { 11798c2ecf20Sopenharmony_ci /* only update vbus status for peripheral */ 11808c2ecf20Sopenharmony_ci if (ci->role == CI_ROLE_GADGET) { 11818c2ecf20Sopenharmony_ci /* Pull down DP for possible charger detection */ 11828c2ecf20Sopenharmony_ci hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 11838c2ecf20Sopenharmony_ci ci_handle_vbus_change(ci); 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci ret = ci_role_start(ci, ci->role); 11878c2ecf20Sopenharmony_ci if (ret) { 11888c2ecf20Sopenharmony_ci dev_err(dev, "can't start %s role\n", 11898c2ecf20Sopenharmony_ci ci_role(ci)->name); 11908c2ecf20Sopenharmony_ci goto stop; 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED, 11958c2ecf20Sopenharmony_ci ci->platdata->name, ci); 11968c2ecf20Sopenharmony_ci if (ret) 11978c2ecf20Sopenharmony_ci goto stop; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci ret = ci_extcon_register(ci); 12008c2ecf20Sopenharmony_ci if (ret) 12018c2ecf20Sopenharmony_ci goto stop; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (ci->supports_runtime_pm) { 12048c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 12058c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 12068c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 12078c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(ci->dev); 12088c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (ci_otg_is_fsm_mode(ci)) 12128c2ecf20Sopenharmony_ci ci_hdrc_otg_fsm_start(ci); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci device_set_wakeup_capable(&pdev->dev, true); 12158c2ecf20Sopenharmony_ci dbg_create_files(ci); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci return 0; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistop: 12208c2ecf20Sopenharmony_ci if (ci->role_switch) 12218c2ecf20Sopenharmony_ci usb_role_switch_unregister(ci->role_switch); 12228c2ecf20Sopenharmony_cideinit_otg: 12238c2ecf20Sopenharmony_ci if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) 12248c2ecf20Sopenharmony_ci ci_hdrc_otg_destroy(ci); 12258c2ecf20Sopenharmony_cideinit_gadget: 12268c2ecf20Sopenharmony_ci ci_hdrc_gadget_destroy(ci); 12278c2ecf20Sopenharmony_cideinit_host: 12288c2ecf20Sopenharmony_ci ci_hdrc_host_destroy(ci); 12298c2ecf20Sopenharmony_cideinit_phy: 12308c2ecf20Sopenharmony_ci ci_usb_phy_exit(ci); 12318c2ecf20Sopenharmony_ciulpi_exit: 12328c2ecf20Sopenharmony_ci ci_ulpi_exit(ci); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return ret; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int ci_hdrc_remove(struct platform_device *pdev) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct ci_hdrc *ci = platform_get_drvdata(pdev); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (ci->role_switch) 12428c2ecf20Sopenharmony_ci usb_role_switch_unregister(ci->role_switch); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci if (ci->supports_runtime_pm) { 12458c2ecf20Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 12468c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 12478c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci dbg_remove_files(ci); 12518c2ecf20Sopenharmony_ci ci_role_destroy(ci); 12528c2ecf20Sopenharmony_ci ci_hdrc_enter_lpm(ci, true); 12538c2ecf20Sopenharmony_ci ci_usb_phy_exit(ci); 12548c2ecf20Sopenharmony_ci ci_ulpi_exit(ci); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci return 0; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12608c2ecf20Sopenharmony_ci/* Prepare wakeup by SRP before suspend */ 12618c2ecf20Sopenharmony_cistatic void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 12648c2ecf20Sopenharmony_ci !hw_read_otgsc(ci, OTGSC_ID)) { 12658c2ecf20Sopenharmony_ci hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 12668c2ecf20Sopenharmony_ci PORTSC_PP); 12678c2ecf20Sopenharmony_ci hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, 12688c2ecf20Sopenharmony_ci PORTSC_WKCN); 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci/* Handle SRP when wakeup by data pulse */ 12738c2ecf20Sopenharmony_cistatic void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 12768c2ecf20Sopenharmony_ci (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { 12778c2ecf20Sopenharmony_ci if (!hw_read_otgsc(ci, OTGSC_ID)) { 12788c2ecf20Sopenharmony_ci ci->fsm.a_srp_det = 1; 12798c2ecf20Sopenharmony_ci ci->fsm.a_bus_drop = 0; 12808c2ecf20Sopenharmony_ci } else { 12818c2ecf20Sopenharmony_ci ci->fsm.id = 1; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci ci_otg_queue_work(ci); 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void ci_controller_suspend(struct ci_hdrc *ci) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci disable_irq(ci->irq); 12908c2ecf20Sopenharmony_ci ci_hdrc_enter_lpm(ci, true); 12918c2ecf20Sopenharmony_ci if (ci->platdata->phy_clkgate_delay_us) 12928c2ecf20Sopenharmony_ci usleep_range(ci->platdata->phy_clkgate_delay_us, 12938c2ecf20Sopenharmony_ci ci->platdata->phy_clkgate_delay_us + 50); 12948c2ecf20Sopenharmony_ci usb_phy_set_suspend(ci->usb_phy, 1); 12958c2ecf20Sopenharmony_ci ci->in_lpm = true; 12968c2ecf20Sopenharmony_ci enable_irq(ci->irq); 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci/* 13008c2ecf20Sopenharmony_ci * Handle the wakeup interrupt triggered by extcon connector 13018c2ecf20Sopenharmony_ci * We need to call ci_irq again for extcon since the first 13028c2ecf20Sopenharmony_ci * interrupt (wakeup int) only let the controller be out of 13038c2ecf20Sopenharmony_ci * low power mode, but not handle any interrupts. 13048c2ecf20Sopenharmony_ci */ 13058c2ecf20Sopenharmony_cistatic void ci_extcon_wakeup_int(struct ci_hdrc *ci) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci struct ci_hdrc_cable *cable_id, *cable_vbus; 13088c2ecf20Sopenharmony_ci u32 otgsc = hw_read_otgsc(ci, ~0); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci cable_id = &ci->platdata->id_extcon; 13118c2ecf20Sopenharmony_ci cable_vbus = &ci->platdata->vbus_extcon; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if (!IS_ERR(cable_id->edev) && ci->is_otg && 13148c2ecf20Sopenharmony_ci (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) 13158c2ecf20Sopenharmony_ci ci_irq(ci); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (!IS_ERR(cable_vbus->edev) && ci->is_otg && 13188c2ecf20Sopenharmony_ci (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) 13198c2ecf20Sopenharmony_ci ci_irq(ci); 13208c2ecf20Sopenharmony_ci} 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic int ci_controller_resume(struct device *dev) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci struct ci_hdrc *ci = dev_get_drvdata(dev); 13258c2ecf20Sopenharmony_ci int ret; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci dev_dbg(dev, "at %s\n", __func__); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci if (!ci->in_lpm) { 13308c2ecf20Sopenharmony_ci WARN_ON(1); 13318c2ecf20Sopenharmony_ci return 0; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci ci_hdrc_enter_lpm(ci, false); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci ret = ci_ulpi_resume(ci); 13378c2ecf20Sopenharmony_ci if (ret) 13388c2ecf20Sopenharmony_ci return ret; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci if (ci->usb_phy) { 13418c2ecf20Sopenharmony_ci usb_phy_set_suspend(ci->usb_phy, 0); 13428c2ecf20Sopenharmony_ci usb_phy_set_wakeup(ci->usb_phy, false); 13438c2ecf20Sopenharmony_ci hw_wait_phy_stable(); 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci ci->in_lpm = false; 13478c2ecf20Sopenharmony_ci if (ci->wakeup_int) { 13488c2ecf20Sopenharmony_ci ci->wakeup_int = false; 13498c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(ci->dev); 13508c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(ci->dev); 13518c2ecf20Sopenharmony_ci enable_irq(ci->irq); 13528c2ecf20Sopenharmony_ci if (ci_otg_is_fsm_mode(ci)) 13538c2ecf20Sopenharmony_ci ci_otg_fsm_wakeup_by_srp(ci); 13548c2ecf20Sopenharmony_ci ci_extcon_wakeup_int(ci); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 13618c2ecf20Sopenharmony_cistatic int ci_suspend(struct device *dev) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct ci_hdrc *ci = dev_get_drvdata(dev); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (ci->wq) 13668c2ecf20Sopenharmony_ci flush_workqueue(ci->wq); 13678c2ecf20Sopenharmony_ci /* 13688c2ecf20Sopenharmony_ci * Controller needs to be active during suspend, otherwise the core 13698c2ecf20Sopenharmony_ci * may run resume when the parent is at suspend if other driver's 13708c2ecf20Sopenharmony_ci * suspend fails, it occurs before parent's suspend has not started, 13718c2ecf20Sopenharmony_ci * but the core suspend has finished. 13728c2ecf20Sopenharmony_ci */ 13738c2ecf20Sopenharmony_ci if (ci->in_lpm) 13748c2ecf20Sopenharmony_ci pm_runtime_resume(dev); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci if (ci->in_lpm) { 13778c2ecf20Sopenharmony_ci WARN_ON(1); 13788c2ecf20Sopenharmony_ci return 0; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 13828c2ecf20Sopenharmony_ci if (ci_otg_is_fsm_mode(ci)) 13838c2ecf20Sopenharmony_ci ci_otg_fsm_suspend_for_srp(ci); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci usb_phy_set_wakeup(ci->usb_phy, true); 13868c2ecf20Sopenharmony_ci enable_irq_wake(ci->irq); 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci ci_controller_suspend(ci); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci return 0; 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic int ci_resume(struct device *dev) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci struct ci_hdrc *ci = dev_get_drvdata(dev); 13978c2ecf20Sopenharmony_ci int ret; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 14008c2ecf20Sopenharmony_ci disable_irq_wake(ci->irq); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci ret = ci_controller_resume(dev); 14038c2ecf20Sopenharmony_ci if (ret) 14048c2ecf20Sopenharmony_ci return ret; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci if (ci->supports_runtime_pm) { 14078c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 14088c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 14098c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci return ret; 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_cistatic int ci_runtime_suspend(struct device *dev) 14178c2ecf20Sopenharmony_ci{ 14188c2ecf20Sopenharmony_ci struct ci_hdrc *ci = dev_get_drvdata(dev); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci dev_dbg(dev, "at %s\n", __func__); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (ci->in_lpm) { 14238c2ecf20Sopenharmony_ci WARN_ON(1); 14248c2ecf20Sopenharmony_ci return 0; 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (ci_otg_is_fsm_mode(ci)) 14288c2ecf20Sopenharmony_ci ci_otg_fsm_suspend_for_srp(ci); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci usb_phy_set_wakeup(ci->usb_phy, true); 14318c2ecf20Sopenharmony_ci ci_controller_suspend(ci); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci return 0; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic int ci_runtime_resume(struct device *dev) 14378c2ecf20Sopenharmony_ci{ 14388c2ecf20Sopenharmony_ci return ci_controller_resume(dev); 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 14428c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ci_pm_ops = { 14438c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 14448c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL) 14458c2ecf20Sopenharmony_ci}; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic struct platform_driver ci_hdrc_driver = { 14488c2ecf20Sopenharmony_ci .probe = ci_hdrc_probe, 14498c2ecf20Sopenharmony_ci .remove = ci_hdrc_remove, 14508c2ecf20Sopenharmony_ci .driver = { 14518c2ecf20Sopenharmony_ci .name = "ci_hdrc", 14528c2ecf20Sopenharmony_ci .pm = &ci_pm_ops, 14538c2ecf20Sopenharmony_ci .dev_groups = ci_groups, 14548c2ecf20Sopenharmony_ci }, 14558c2ecf20Sopenharmony_ci}; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int __init ci_hdrc_platform_register(void) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci ci_hdrc_host_driver_init(); 14608c2ecf20Sopenharmony_ci return platform_driver_register(&ci_hdrc_driver); 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_cimodule_init(ci_hdrc_platform_register); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_cistatic void __exit ci_hdrc_platform_unregister(void) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci platform_driver_unregister(&ci_hdrc_driver); 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_cimodule_exit(ci_hdrc_platform_unregister); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ci_hdrc"); 14718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 14728c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); 14738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ChipIdea HDRC Driver"); 1474