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