18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bdc_udc.c - BRCM BDC USB3.0 device controller gagdet ops 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Broadcom Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Ashwini Pahuja 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on drivers under drivers/usb/gadget/udc/ 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/ioport.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/timer.h> 228c2ecf20Sopenharmony_ci#include <linux/list.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 258c2ecf20Sopenharmony_ci#include <linux/device.h> 268c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 278c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 288c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 298c2ecf20Sopenharmony_ci#include <linux/pm.h> 308c2ecf20Sopenharmony_ci#include <linux/io.h> 318c2ecf20Sopenharmony_ci#include <linux/irq.h> 328c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 338c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include "bdc.h" 368c2ecf20Sopenharmony_ci#include "bdc_ep.h" 378c2ecf20Sopenharmony_ci#include "bdc_cmd.h" 388c2ecf20Sopenharmony_ci#include "bdc_dbg.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops bdc_gadget_ops; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const char * const conn_speed_str[] = { 438c2ecf20Sopenharmony_ci "Not connected", 448c2ecf20Sopenharmony_ci "Full Speed", 458c2ecf20Sopenharmony_ci "Low Speed", 468c2ecf20Sopenharmony_ci "High Speed", 478c2ecf20Sopenharmony_ci "Super Speed", 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* EP0 initial descripror */ 518c2ecf20Sopenharmony_cistatic struct usb_endpoint_descriptor bdc_gadget_ep0_desc = { 528c2ecf20Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 538c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 548c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_CONTROL, 558c2ecf20Sopenharmony_ci .bEndpointAddress = 0, 568c2ecf20Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(EP0_MAX_PKT_SIZE), 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Advance the srr dqp maintained by SW */ 608c2ecf20Sopenharmony_cistatic void srr_dqp_index_advc(struct bdc *bdc, u32 srr_num) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct srr *srr; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci srr = &bdc->srr; 658c2ecf20Sopenharmony_ci dev_dbg_ratelimited(bdc->dev, "srr->dqp_index:%d\n", srr->dqp_index); 668c2ecf20Sopenharmony_ci srr->dqp_index++; 678c2ecf20Sopenharmony_ci /* rollback to 0 if we are past the last */ 688c2ecf20Sopenharmony_ci if (srr->dqp_index == NUM_SR_ENTRIES) 698c2ecf20Sopenharmony_ci srr->dqp_index = 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* connect sr */ 738c2ecf20Sopenharmony_cistatic void bdc_uspc_connected(struct bdc *bdc) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u32 speed, temp; 768c2ecf20Sopenharmony_ci u32 usppms; 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci temp = bdc_readl(bdc->regs, BDC_USPC); 808c2ecf20Sopenharmony_ci speed = BDC_PSP(temp); 818c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s speed=%x\n", __func__, speed); 828c2ecf20Sopenharmony_ci switch (speed) { 838c2ecf20Sopenharmony_ci case BDC_SPEED_SS: 848c2ecf20Sopenharmony_ci bdc_gadget_ep0_desc.wMaxPacketSize = 858c2ecf20Sopenharmony_ci cpu_to_le16(EP0_MAX_PKT_SIZE); 868c2ecf20Sopenharmony_ci bdc->gadget.ep0->maxpacket = EP0_MAX_PKT_SIZE; 878c2ecf20Sopenharmony_ci bdc->gadget.speed = USB_SPEED_SUPER; 888c2ecf20Sopenharmony_ci /* Enable U1T in SS mode */ 898c2ecf20Sopenharmony_ci usppms = bdc_readl(bdc->regs, BDC_USPPMS); 908c2ecf20Sopenharmony_ci usppms &= ~BDC_U1T(0xff); 918c2ecf20Sopenharmony_ci usppms |= BDC_U1T(U1_TIMEOUT); 928c2ecf20Sopenharmony_ci usppms |= BDC_PORT_W1S; 938c2ecf20Sopenharmony_ci bdc_writel(bdc->regs, BDC_USPPMS, usppms); 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci case BDC_SPEED_HS: 978c2ecf20Sopenharmony_ci bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); 988c2ecf20Sopenharmony_ci bdc->gadget.ep0->maxpacket = 64; 998c2ecf20Sopenharmony_ci bdc->gadget.speed = USB_SPEED_HIGH; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci case BDC_SPEED_FS: 1038c2ecf20Sopenharmony_ci bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); 1048c2ecf20Sopenharmony_ci bdc->gadget.ep0->maxpacket = 64; 1058c2ecf20Sopenharmony_ci bdc->gadget.speed = USB_SPEED_FULL; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case BDC_SPEED_LS: 1098c2ecf20Sopenharmony_ci bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); 1108c2ecf20Sopenharmony_ci bdc->gadget.ep0->maxpacket = 8; 1118c2ecf20Sopenharmony_ci bdc->gadget.speed = USB_SPEED_LOW; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci default: 1148c2ecf20Sopenharmony_ci dev_err(bdc->dev, "UNDEFINED SPEED\n"); 1158c2ecf20Sopenharmony_ci return; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "connected at %s\n", conn_speed_str[speed]); 1188c2ecf20Sopenharmony_ci /* Now we know the speed, configure ep0 */ 1198c2ecf20Sopenharmony_ci bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc; 1208c2ecf20Sopenharmony_ci ret = bdc_config_ep(bdc, bdc->bdc_ep_array[1]); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci dev_err(bdc->dev, "EP0 config failed\n"); 1238c2ecf20Sopenharmony_ci bdc->bdc_ep_array[1]->usb_ep.desc = &bdc_gadget_ep0_desc; 1248c2ecf20Sopenharmony_ci bdc->bdc_ep_array[1]->flags |= BDC_EP_ENABLED; 1258c2ecf20Sopenharmony_ci usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* device got disconnected */ 1298c2ecf20Sopenharmony_cistatic void bdc_uspc_disconnected(struct bdc *bdc, bool reinit) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct bdc_ep *ep; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s\n", __func__); 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Only stop ep0 from here, rest of the endpoints will be disabled 1368c2ecf20Sopenharmony_ci * from gadget_disconnect 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci ep = bdc->bdc_ep_array[1]; 1398c2ecf20Sopenharmony_ci if (ep && (ep->flags & BDC_EP_ENABLED)) 1408c2ecf20Sopenharmony_ci /* if enabled then stop and remove requests */ 1418c2ecf20Sopenharmony_ci bdc_ep_disable(ep); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (bdc->gadget_driver && bdc->gadget_driver->disconnect) { 1448c2ecf20Sopenharmony_ci spin_unlock(&bdc->lock); 1458c2ecf20Sopenharmony_ci bdc->gadget_driver->disconnect(&bdc->gadget); 1468c2ecf20Sopenharmony_ci spin_lock(&bdc->lock); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci /* Set Unknown speed */ 1498c2ecf20Sopenharmony_ci bdc->gadget.speed = USB_SPEED_UNKNOWN; 1508c2ecf20Sopenharmony_ci bdc->devstatus &= DEVSTATUS_CLEAR; 1518c2ecf20Sopenharmony_ci bdc->delayed_status = false; 1528c2ecf20Sopenharmony_ci bdc->reinit = reinit; 1538c2ecf20Sopenharmony_ci bdc->test_mode = false; 1548c2ecf20Sopenharmony_ci usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* TNotify wkaeup timer */ 1588c2ecf20Sopenharmony_cistatic void bdc_func_wake_timer(struct work_struct *work) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct bdc *bdc = container_of(work, struct bdc, func_wake_notify.work); 1618c2ecf20Sopenharmony_ci unsigned long flags; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s\n", __func__); 1648c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * Check if host has started transferring on endpoints 1678c2ecf20Sopenharmony_ci * FUNC_WAKE_ISSUED is cleared when transfer has started after resume 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci if (bdc->devstatus & FUNC_WAKE_ISSUED) { 1708c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "FUNC_WAKE_ISSUED FLAG IS STILL SET\n"); 1718c2ecf20Sopenharmony_ci /* flag is still set, so again send func wake */ 1728c2ecf20Sopenharmony_ci bdc_function_wake_fh(bdc, 0); 1738c2ecf20Sopenharmony_ci schedule_delayed_work(&bdc->func_wake_notify, 1748c2ecf20Sopenharmony_ci msecs_to_jiffies(BDC_TNOTIFY)); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* handler for Link state change condition */ 1808c2ecf20Sopenharmony_cistatic void handle_link_state_change(struct bdc *bdc, u32 uspc) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci u32 link_state; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "Link state change"); 1858c2ecf20Sopenharmony_ci link_state = BDC_PST(uspc); 1868c2ecf20Sopenharmony_ci switch (link_state) { 1878c2ecf20Sopenharmony_ci case BDC_LINK_STATE_U3: 1888c2ecf20Sopenharmony_ci if ((bdc->gadget.speed != USB_SPEED_UNKNOWN) && 1898c2ecf20Sopenharmony_ci bdc->gadget_driver->suspend) { 1908c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "Entered Suspend mode\n"); 1918c2ecf20Sopenharmony_ci spin_unlock(&bdc->lock); 1928c2ecf20Sopenharmony_ci bdc->devstatus |= DEVICE_SUSPENDED; 1938c2ecf20Sopenharmony_ci bdc->gadget_driver->suspend(&bdc->gadget); 1948c2ecf20Sopenharmony_ci spin_lock(&bdc->lock); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case BDC_LINK_STATE_U0: 1988c2ecf20Sopenharmony_ci if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) { 1998c2ecf20Sopenharmony_ci bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; 2008c2ecf20Sopenharmony_ci if (bdc->gadget.speed == USB_SPEED_SUPER) { 2018c2ecf20Sopenharmony_ci bdc_function_wake_fh(bdc, 0); 2028c2ecf20Sopenharmony_ci bdc->devstatus |= FUNC_WAKE_ISSUED; 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Start a Notification timer and check if the 2058c2ecf20Sopenharmony_ci * Host transferred anything on any of the EPs, 2068c2ecf20Sopenharmony_ci * if not then send function wake again every 2078c2ecf20Sopenharmony_ci * TNotification secs until host initiates 2088c2ecf20Sopenharmony_ci * transfer to BDC, USB3 spec Table 8.13 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci schedule_delayed_work( 2118c2ecf20Sopenharmony_ci &bdc->func_wake_notify, 2128c2ecf20Sopenharmony_ci msecs_to_jiffies(BDC_TNOTIFY)); 2138c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "sched func_wake_notify\n"); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci case BDC_LINK_STATE_RESUME: 2198c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "Resumed from Suspend\n"); 2208c2ecf20Sopenharmony_ci if (bdc->devstatus & DEVICE_SUSPENDED) { 2218c2ecf20Sopenharmony_ci bdc->gadget_driver->resume(&bdc->gadget); 2228c2ecf20Sopenharmony_ci bdc->devstatus &= ~DEVICE_SUSPENDED; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci default: 2268c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "link state:%d\n", link_state); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* something changes on upstream port, handle it here */ 2318c2ecf20Sopenharmony_civoid bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci u32 clear_flags = 0; 2348c2ecf20Sopenharmony_ci u32 uspc; 2358c2ecf20Sopenharmony_ci bool connected = false; 2368c2ecf20Sopenharmony_ci bool disconn = false; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci uspc = bdc_readl(bdc->regs, BDC_USPC); 2398c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s uspc=0x%08x\n", __func__, uspc); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Port connect changed */ 2428c2ecf20Sopenharmony_ci if (uspc & BDC_PCC) { 2438c2ecf20Sopenharmony_ci /* Vbus not present, and not connected to Downstream port */ 2448c2ecf20Sopenharmony_ci if ((uspc & BDC_VBC) && !(uspc & BDC_VBS) && !(uspc & BDC_PCS)) 2458c2ecf20Sopenharmony_ci disconn = true; 2468c2ecf20Sopenharmony_ci else if ((uspc & BDC_PCS) && !BDC_PST(uspc)) 2478c2ecf20Sopenharmony_ci connected = true; 2488c2ecf20Sopenharmony_ci clear_flags |= BDC_PCC; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Change in VBus and VBus is present */ 2528c2ecf20Sopenharmony_ci if ((uspc & BDC_VBC) && (uspc & BDC_VBS)) { 2538c2ecf20Sopenharmony_ci if (bdc->pullup) { 2548c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "Do a softconnect\n"); 2558c2ecf20Sopenharmony_ci /* Attached state, do a softconnect */ 2568c2ecf20Sopenharmony_ci bdc_softconn(bdc); 2578c2ecf20Sopenharmony_ci usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci clear_flags |= BDC_VBC; 2608c2ecf20Sopenharmony_ci } else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) { 2618c2ecf20Sopenharmony_ci /* Hot reset, warm reset, 2.0 bus reset or disconn */ 2628c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "Port reset or disconn\n"); 2638c2ecf20Sopenharmony_ci bdc_uspc_disconnected(bdc, disconn); 2648c2ecf20Sopenharmony_ci clear_flags |= BDC_PRC; 2658c2ecf20Sopenharmony_ci } else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) { 2668c2ecf20Sopenharmony_ci /* Change in Link state */ 2678c2ecf20Sopenharmony_ci handle_link_state_change(bdc, uspc); 2688c2ecf20Sopenharmony_ci clear_flags |= BDC_PSC; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * In SS we might not have PRC bit set before connection, but in 2.0 2738c2ecf20Sopenharmony_ci * the PRC bit is set before connection, so moving this condition out 2748c2ecf20Sopenharmony_ci * of bus reset to handle both SS/2.0 speeds. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci if (connected) { 2778c2ecf20Sopenharmony_ci /* This is the connect event for U0/L0 */ 2788c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "Connected\n"); 2798c2ecf20Sopenharmony_ci bdc_uspc_connected(bdc); 2808c2ecf20Sopenharmony_ci bdc->devstatus &= ~(DEVICE_SUSPENDED); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci uspc = bdc_readl(bdc->regs, BDC_USPC); 2838c2ecf20Sopenharmony_ci uspc &= (~BDC_USPSC_RW); 2848c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "uspc=%x\n", uspc); 2858c2ecf20Sopenharmony_ci bdc_writel(bdc->regs, BDC_USPC, clear_flags); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* Main interrupt handler for bdc */ 2898c2ecf20Sopenharmony_cistatic irqreturn_t bdc_udc_interrupt(int irq, void *_bdc) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci u32 eqp_index, dqp_index, sr_type, srr_int; 2928c2ecf20Sopenharmony_ci struct bdc_sr *sreport; 2938c2ecf20Sopenharmony_ci struct bdc *bdc = _bdc; 2948c2ecf20Sopenharmony_ci u32 status; 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci spin_lock(&bdc->lock); 2988c2ecf20Sopenharmony_ci status = bdc_readl(bdc->regs, BDC_BDCSC); 2998c2ecf20Sopenharmony_ci if (!(status & BDC_GIP)) { 3008c2ecf20Sopenharmony_ci spin_unlock(&bdc->lock); 3018c2ecf20Sopenharmony_ci return IRQ_NONE; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0)); 3048c2ecf20Sopenharmony_ci /* Check if the SRR IP bit it set? */ 3058c2ecf20Sopenharmony_ci if (!(srr_int & BDC_SRR_IP)) { 3068c2ecf20Sopenharmony_ci dev_warn(bdc->dev, "Global irq pending but SRR IP is 0\n"); 3078c2ecf20Sopenharmony_ci spin_unlock(&bdc->lock); 3088c2ecf20Sopenharmony_ci return IRQ_NONE; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci eqp_index = BDC_SRR_EPI(srr_int); 3118c2ecf20Sopenharmony_ci dqp_index = BDC_SRR_DPI(srr_int); 3128c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, 3138c2ecf20Sopenharmony_ci "%s eqp_index=%d dqp_index=%d srr.dqp_index=%d\n\n", 3148c2ecf20Sopenharmony_ci __func__, eqp_index, dqp_index, bdc->srr.dqp_index); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* check for ring empty condition */ 3178c2ecf20Sopenharmony_ci if (eqp_index == dqp_index) { 3188c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "SRR empty?\n"); 3198c2ecf20Sopenharmony_ci spin_unlock(&bdc->lock); 3208c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci while (bdc->srr.dqp_index != eqp_index) { 3248c2ecf20Sopenharmony_ci sreport = &bdc->srr.sr_bds[bdc->srr.dqp_index]; 3258c2ecf20Sopenharmony_ci /* sreport is read before using it */ 3268c2ecf20Sopenharmony_ci rmb(); 3278c2ecf20Sopenharmony_ci sr_type = le32_to_cpu(sreport->offset[3]) & BD_TYPE_BITMASK; 3288c2ecf20Sopenharmony_ci dev_dbg_ratelimited(bdc->dev, "sr_type=%d\n", sr_type); 3298c2ecf20Sopenharmony_ci switch (sr_type) { 3308c2ecf20Sopenharmony_ci case SR_XSF: 3318c2ecf20Sopenharmony_ci bdc->sr_handler[0](bdc, sreport); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci case SR_USPC: 3358c2ecf20Sopenharmony_ci bdc->sr_handler[1](bdc, sreport); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci default: 3388c2ecf20Sopenharmony_ci dev_warn(bdc->dev, "SR:%d not handled\n", sr_type); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci /* Advance the srr dqp index */ 3418c2ecf20Sopenharmony_ci srr_dqp_index_advc(bdc, 0); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci /* update the hw dequeue pointer */ 3448c2ecf20Sopenharmony_ci srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0)); 3458c2ecf20Sopenharmony_ci srr_int &= ~BDC_SRR_DPI_MASK; 3468c2ecf20Sopenharmony_ci srr_int &= ~(BDC_SRR_RWS|BDC_SRR_RST|BDC_SRR_ISR); 3478c2ecf20Sopenharmony_ci srr_int |= ((bdc->srr.dqp_index) << 16); 3488c2ecf20Sopenharmony_ci srr_int |= BDC_SRR_IP; 3498c2ecf20Sopenharmony_ci bdc_writel(bdc->regs, BDC_SRRINT(0), srr_int); 3508c2ecf20Sopenharmony_ci srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0)); 3518c2ecf20Sopenharmony_ci if (bdc->reinit) { 3528c2ecf20Sopenharmony_ci ret = bdc_reinit(bdc); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci dev_err(bdc->dev, "err in bdc reinit\n"); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci spin_unlock(&bdc->lock); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* Gadget ops */ 3638c2ecf20Sopenharmony_cistatic int bdc_udc_start(struct usb_gadget *gadget, 3648c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct bdc *bdc = gadget_to_bdc(gadget); 3678c2ecf20Sopenharmony_ci unsigned long flags; 3688c2ecf20Sopenharmony_ci int ret = 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s()\n", __func__); 3718c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 3728c2ecf20Sopenharmony_ci if (bdc->gadget_driver) { 3738c2ecf20Sopenharmony_ci dev_err(bdc->dev, "%s is already bound to %s\n", 3748c2ecf20Sopenharmony_ci bdc->gadget.name, 3758c2ecf20Sopenharmony_ci bdc->gadget_driver->driver.name); 3768c2ecf20Sopenharmony_ci ret = -EBUSY; 3778c2ecf20Sopenharmony_ci goto err; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * Run the controller from here and when BDC is connected to 3818c2ecf20Sopenharmony_ci * Host then driver will receive a USPC SR with VBUS present 3828c2ecf20Sopenharmony_ci * and then driver will do a softconnect. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci ret = bdc_run(bdc); 3858c2ecf20Sopenharmony_ci if (ret) { 3868c2ecf20Sopenharmony_ci dev_err(bdc->dev, "%s bdc run fail\n", __func__); 3878c2ecf20Sopenharmony_ci goto err; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci bdc->gadget_driver = driver; 3908c2ecf20Sopenharmony_ci bdc->gadget.dev.driver = &driver->driver; 3918c2ecf20Sopenharmony_cierr: 3928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int bdc_udc_stop(struct usb_gadget *gadget) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct bdc *bdc = gadget_to_bdc(gadget); 4008c2ecf20Sopenharmony_ci unsigned long flags; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s()\n", __func__); 4038c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 4048c2ecf20Sopenharmony_ci bdc_stop(bdc); 4058c2ecf20Sopenharmony_ci bdc->gadget_driver = NULL; 4068c2ecf20Sopenharmony_ci bdc->gadget.dev.driver = NULL; 4078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int bdc_udc_pullup(struct usb_gadget *gadget, int is_on) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct bdc *bdc = gadget_to_bdc(gadget); 4158c2ecf20Sopenharmony_ci unsigned long flags; 4168c2ecf20Sopenharmony_ci u32 uspc; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s() is_on:%d\n", __func__, is_on); 4198c2ecf20Sopenharmony_ci if (!gadget) 4208c2ecf20Sopenharmony_ci return -EINVAL; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 4238c2ecf20Sopenharmony_ci if (!is_on) { 4248c2ecf20Sopenharmony_ci bdc_softdisconn(bdc); 4258c2ecf20Sopenharmony_ci bdc->pullup = false; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * For a self powered device, we need to wait till we receive 4298c2ecf20Sopenharmony_ci * a VBUS change and Vbus present event, then if pullup flag 4308c2ecf20Sopenharmony_ci * is set, then only we present the Termintation. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_ci bdc->pullup = true; 4338c2ecf20Sopenharmony_ci /* 4348c2ecf20Sopenharmony_ci * Check if BDC is already connected to Host i.e Vbus=1, 4358c2ecf20Sopenharmony_ci * if yes, then present TERM now, this is typical for bus 4368c2ecf20Sopenharmony_ci * powered devices. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci uspc = bdc_readl(bdc->regs, BDC_USPC); 4398c2ecf20Sopenharmony_ci if (uspc & BDC_VBS) 4408c2ecf20Sopenharmony_ci bdc_softconn(bdc); 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int bdc_udc_set_selfpowered(struct usb_gadget *gadget, 4488c2ecf20Sopenharmony_ci int is_self) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct bdc *bdc = gadget_to_bdc(gadget); 4518c2ecf20Sopenharmony_ci unsigned long flags; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s()\n", __func__); 4548c2ecf20Sopenharmony_ci gadget->is_selfpowered = (is_self != 0); 4558c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 4568c2ecf20Sopenharmony_ci if (!is_self) 4578c2ecf20Sopenharmony_ci bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci bdc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int bdc_udc_wakeup(struct usb_gadget *gadget) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct bdc *bdc = gadget_to_bdc(gadget); 4698c2ecf20Sopenharmony_ci unsigned long flags; 4708c2ecf20Sopenharmony_ci u8 link_state; 4718c2ecf20Sopenharmony_ci u32 uspc; 4728c2ecf20Sopenharmony_ci int ret = 0; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, 4758c2ecf20Sopenharmony_ci "%s() bdc->devstatus=%08x\n", 4768c2ecf20Sopenharmony_ci __func__, bdc->devstatus); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (!(bdc->devstatus & REMOTE_WAKE_ENABLE)) 4798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 4828c2ecf20Sopenharmony_ci uspc = bdc_readl(bdc->regs, BDC_USPC); 4838c2ecf20Sopenharmony_ci link_state = BDC_PST(uspc); 4848c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc); 4858c2ecf20Sopenharmony_ci if (link_state != BDC_LINK_STATE_U3) { 4868c2ecf20Sopenharmony_ci dev_warn(bdc->dev, 4878c2ecf20Sopenharmony_ci "can't wakeup from link state %d\n", 4888c2ecf20Sopenharmony_ci link_state); 4898c2ecf20Sopenharmony_ci ret = -EINVAL; 4908c2ecf20Sopenharmony_ci goto out; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci if (bdc->gadget.speed == USB_SPEED_SUPER) 4938c2ecf20Sopenharmony_ci bdc->devstatus |= REMOTE_WAKEUP_ISSUED; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci uspc &= ~BDC_PST_MASK; 4968c2ecf20Sopenharmony_ci uspc &= (~BDC_USPSC_RW); 4978c2ecf20Sopenharmony_ci uspc |= BDC_PST(BDC_LINK_STATE_U0); 4988c2ecf20Sopenharmony_ci uspc |= BDC_SWS; 4998c2ecf20Sopenharmony_ci bdc_writel(bdc->regs, BDC_USPC, uspc); 5008c2ecf20Sopenharmony_ci uspc = bdc_readl(bdc->regs, BDC_USPC); 5018c2ecf20Sopenharmony_ci link_state = BDC_PST(uspc); 5028c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc); 5038c2ecf20Sopenharmony_ciout: 5048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops bdc_gadget_ops = { 5108c2ecf20Sopenharmony_ci .wakeup = bdc_udc_wakeup, 5118c2ecf20Sopenharmony_ci .set_selfpowered = bdc_udc_set_selfpowered, 5128c2ecf20Sopenharmony_ci .pullup = bdc_udc_pullup, 5138c2ecf20Sopenharmony_ci .udc_start = bdc_udc_start, 5148c2ecf20Sopenharmony_ci .udc_stop = bdc_udc_stop, 5158c2ecf20Sopenharmony_ci}; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/* Init the gadget interface and register the udc */ 5188c2ecf20Sopenharmony_ciint bdc_udc_init(struct bdc *bdc) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci u32 temp; 5218c2ecf20Sopenharmony_ci int ret; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s()\n", __func__); 5248c2ecf20Sopenharmony_ci bdc->gadget.ops = &bdc_gadget_ops; 5258c2ecf20Sopenharmony_ci bdc->gadget.max_speed = USB_SPEED_SUPER; 5268c2ecf20Sopenharmony_ci bdc->gadget.speed = USB_SPEED_UNKNOWN; 5278c2ecf20Sopenharmony_ci bdc->gadget.dev.parent = bdc->dev; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci bdc->gadget.sg_supported = false; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci bdc->gadget.name = BRCM_BDC_NAME; 5338c2ecf20Sopenharmony_ci ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt, 5348c2ecf20Sopenharmony_ci IRQF_SHARED , BRCM_BDC_NAME, bdc); 5358c2ecf20Sopenharmony_ci if (ret) { 5368c2ecf20Sopenharmony_ci dev_err(bdc->dev, 5378c2ecf20Sopenharmony_ci "failed to request irq #%d %d\n", 5388c2ecf20Sopenharmony_ci bdc->irq, ret); 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ret = bdc_init_ep(bdc); 5438c2ecf20Sopenharmony_ci if (ret) { 5448c2ecf20Sopenharmony_ci dev_err(bdc->dev, "bdc init ep fail: %d\n", ret); 5458c2ecf20Sopenharmony_ci return ret; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget); 5498c2ecf20Sopenharmony_ci if (ret) { 5508c2ecf20Sopenharmony_ci dev_err(bdc->dev, "failed to register udc\n"); 5518c2ecf20Sopenharmony_ci goto err0; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED); 5548c2ecf20Sopenharmony_ci bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc; 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * Allocate bd list for ep0 only, ep0 will be enabled on connect 5578c2ecf20Sopenharmony_ci * status report when the speed is known 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci ret = bdc_ep_enable(bdc->bdc_ep_array[1]); 5608c2ecf20Sopenharmony_ci if (ret) { 5618c2ecf20Sopenharmony_ci dev_err(bdc->dev, "fail to enable %s\n", 5628c2ecf20Sopenharmony_ci bdc->bdc_ep_array[1]->name); 5638c2ecf20Sopenharmony_ci goto err1; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&bdc->func_wake_notify, bdc_func_wake_timer); 5668c2ecf20Sopenharmony_ci /* Enable Interrupts */ 5678c2ecf20Sopenharmony_ci temp = bdc_readl(bdc->regs, BDC_BDCSC); 5688c2ecf20Sopenharmony_ci temp |= BDC_GIE; 5698c2ecf20Sopenharmony_ci bdc_writel(bdc->regs, BDC_BDCSC, temp); 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_cierr1: 5728c2ecf20Sopenharmony_ci usb_del_gadget_udc(&bdc->gadget); 5738c2ecf20Sopenharmony_cierr0: 5748c2ecf20Sopenharmony_ci bdc_free_ep(bdc); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_civoid bdc_udc_exit(struct bdc *bdc) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci unsigned long flags; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci dev_dbg(bdc->dev, "%s()\n", __func__); 5848c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdc->lock, flags); 5858c2ecf20Sopenharmony_ci bdc_ep_disable(bdc->bdc_ep_array[1]); 5868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdc->lock, flags); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci usb_del_gadget_udc(&bdc->gadget); 5898c2ecf20Sopenharmony_ci bdc_free_ep(bdc); 5908c2ecf20Sopenharmony_ci} 591