18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* linux/drivers/usb/gadget/s3c-hsudc.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2010 Samsung Electronics Co., Ltd.
58c2ecf20Sopenharmony_ci *		http://www.samsung.com/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * S3C24XX USB 2.0 High-speed USB controller gadget driver
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
108c2ecf20Sopenharmony_ci * Each endpoint can be configured as either in or out endpoint. Endpoints
118c2ecf20Sopenharmony_ci * can be configured for Bulk or Interrupt transfer mode.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/clk.h>
248c2ecf20Sopenharmony_ci#include <linux/err.h>
258c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h>
268c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h>
278c2ecf20Sopenharmony_ci#include <linux/usb/otg.h>
288c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
298c2ecf20Sopenharmony_ci#include <linux/platform_data/s3c-hsudc.h>
308c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
318c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define S3C_HSUDC_REG(x)	(x)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Non-Indexed Registers */
368c2ecf20Sopenharmony_ci#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
378c2ecf20Sopenharmony_ci#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
388c2ecf20Sopenharmony_ci#define S3C_EIR_EP0			(1<<0)
398c2ecf20Sopenharmony_ci#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
408c2ecf20Sopenharmony_ci#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
418c2ecf20Sopenharmony_ci#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
428c2ecf20Sopenharmony_ci#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
438c2ecf20Sopenharmony_ci#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
448c2ecf20Sopenharmony_ci#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
458c2ecf20Sopenharmony_ci#define S3C_SSR_DTZIEN_EN		(0xff8f)
468c2ecf20Sopenharmony_ci#define S3C_SSR_ERR			(0xff80)
478c2ecf20Sopenharmony_ci#define S3C_SSR_VBUSON			(1 << 8)
488c2ecf20Sopenharmony_ci#define S3C_SSR_HSP			(1 << 4)
498c2ecf20Sopenharmony_ci#define S3C_SSR_SDE			(1 << 3)
508c2ecf20Sopenharmony_ci#define S3C_SSR_RESUME			(1 << 2)
518c2ecf20Sopenharmony_ci#define S3C_SSR_SUSPEND			(1 << 1)
528c2ecf20Sopenharmony_ci#define S3C_SSR_RESET			(1 << 0)
538c2ecf20Sopenharmony_ci#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
548c2ecf20Sopenharmony_ci#define S3C_SCR_DTZIEN_EN		(1 << 14)
558c2ecf20Sopenharmony_ci#define S3C_SCR_RRD_EN			(1 << 5)
568c2ecf20Sopenharmony_ci#define S3C_SCR_SUS_EN			(1 << 1)
578c2ecf20Sopenharmony_ci#define S3C_SCR_RST_EN			(1 << 0)
588c2ecf20Sopenharmony_ci#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
598c2ecf20Sopenharmony_ci#define S3C_EP0SR_EP0_LWO		(1 << 6)
608c2ecf20Sopenharmony_ci#define S3C_EP0SR_STALL			(1 << 4)
618c2ecf20Sopenharmony_ci#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
628c2ecf20Sopenharmony_ci#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
638c2ecf20Sopenharmony_ci#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
648c2ecf20Sopenharmony_ci#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* Indexed Registers */
678c2ecf20Sopenharmony_ci#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
688c2ecf20Sopenharmony_ci#define S3C_ESR_FLUSH			(1 << 6)
698c2ecf20Sopenharmony_ci#define S3C_ESR_STALL			(1 << 5)
708c2ecf20Sopenharmony_ci#define S3C_ESR_LWO			(1 << 4)
718c2ecf20Sopenharmony_ci#define S3C_ESR_PSIF_ONE		(1 << 2)
728c2ecf20Sopenharmony_ci#define S3C_ESR_PSIF_TWO		(2 << 2)
738c2ecf20Sopenharmony_ci#define S3C_ESR_TX_SUCCESS		(1 << 1)
748c2ecf20Sopenharmony_ci#define S3C_ESR_RX_SUCCESS		(1 << 0)
758c2ecf20Sopenharmony_ci#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
768c2ecf20Sopenharmony_ci#define S3C_ECR_DUEN			(1 << 7)
778c2ecf20Sopenharmony_ci#define S3C_ECR_FLUSH			(1 << 6)
788c2ecf20Sopenharmony_ci#define S3C_ECR_STALL			(1 << 1)
798c2ecf20Sopenharmony_ci#define S3C_ECR_IEMS			(1 << 0)
808c2ecf20Sopenharmony_ci#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
818c2ecf20Sopenharmony_ci#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
828c2ecf20Sopenharmony_ci#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define WAIT_FOR_SETUP			(0)
858c2ecf20Sopenharmony_ci#define DATA_STATE_XMIT			(1)
868c2ecf20Sopenharmony_ci#define DATA_STATE_RECV			(2)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic const char * const s3c_hsudc_supply_names[] = {
898c2ecf20Sopenharmony_ci	"vdda",		/* analog phy supply, 3.3V */
908c2ecf20Sopenharmony_ci	"vddi",		/* digital phy supply, 1.2V */
918c2ecf20Sopenharmony_ci	"vddosc",	/* oscillator supply, 1.8V - 3.3V */
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * struct s3c_hsudc_ep - Endpoint representation used by driver.
968c2ecf20Sopenharmony_ci * @ep: USB gadget layer representation of device endpoint.
978c2ecf20Sopenharmony_ci * @name: Endpoint name (as required by ep autoconfiguration).
988c2ecf20Sopenharmony_ci * @dev: Reference to the device controller to which this EP belongs.
998c2ecf20Sopenharmony_ci * @desc: Endpoint descriptor obtained from the gadget driver.
1008c2ecf20Sopenharmony_ci * @queue: Transfer request queue for the endpoint.
1018c2ecf20Sopenharmony_ci * @stopped: Maintains state of endpoint, set if EP is halted.
1028c2ecf20Sopenharmony_ci * @bEndpointAddress: EP address (including direction bit).
1038c2ecf20Sopenharmony_ci * @fifo: Base address of EP FIFO.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistruct s3c_hsudc_ep {
1068c2ecf20Sopenharmony_ci	struct usb_ep ep;
1078c2ecf20Sopenharmony_ci	char name[20];
1088c2ecf20Sopenharmony_ci	struct s3c_hsudc *dev;
1098c2ecf20Sopenharmony_ci	struct list_head queue;
1108c2ecf20Sopenharmony_ci	u8 stopped;
1118c2ecf20Sopenharmony_ci	u8 wedge;
1128c2ecf20Sopenharmony_ci	u8 bEndpointAddress;
1138c2ecf20Sopenharmony_ci	void __iomem *fifo;
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
1188c2ecf20Sopenharmony_ci * @req: Reference to USB gadget transfer request.
1198c2ecf20Sopenharmony_ci * @queue: Used for inserting this request to the endpoint request queue.
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_cistruct s3c_hsudc_req {
1228c2ecf20Sopenharmony_ci	struct usb_request req;
1238c2ecf20Sopenharmony_ci	struct list_head queue;
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * struct s3c_hsudc - Driver's abstraction of the device controller.
1288c2ecf20Sopenharmony_ci * @gadget: Instance of usb_gadget which is referenced by gadget driver.
1298c2ecf20Sopenharmony_ci * @driver: Reference to currenty active gadget driver.
1308c2ecf20Sopenharmony_ci * @dev: The device reference used by probe function.
1318c2ecf20Sopenharmony_ci * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
1328c2ecf20Sopenharmony_ci * @regs: Remapped base address of controller's register space.
1338c2ecf20Sopenharmony_ci * irq: IRQ number used by the controller.
1348c2ecf20Sopenharmony_ci * uclk: Reference to the controller clock.
1358c2ecf20Sopenharmony_ci * ep0state: Current state of EP0.
1368c2ecf20Sopenharmony_ci * ep: List of endpoints supported by the controller.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_cistruct s3c_hsudc {
1398c2ecf20Sopenharmony_ci	struct usb_gadget gadget;
1408c2ecf20Sopenharmony_ci	struct usb_gadget_driver *driver;
1418c2ecf20Sopenharmony_ci	struct device *dev;
1428c2ecf20Sopenharmony_ci	struct s3c24xx_hsudc_platdata *pd;
1438c2ecf20Sopenharmony_ci	struct usb_phy *transceiver;
1448c2ecf20Sopenharmony_ci	struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
1458c2ecf20Sopenharmony_ci	spinlock_t lock;
1468c2ecf20Sopenharmony_ci	void __iomem *regs;
1478c2ecf20Sopenharmony_ci	int irq;
1488c2ecf20Sopenharmony_ci	struct clk *uclk;
1498c2ecf20Sopenharmony_ci	int ep0state;
1508c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep ep[];
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
1548c2ecf20Sopenharmony_ci#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
1558c2ecf20Sopenharmony_ci#define ep_index(_ep)		((_ep)->bEndpointAddress & \
1568c2ecf20Sopenharmony_ci					USB_ENDPOINT_NUMBER_MASK)
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic const char driver_name[] = "s3c-udc";
1598c2ecf20Sopenharmony_cistatic const char ep0name[] = "ep0-control";
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic inline struct s3c_hsudc_req *our_req(struct usb_request *req)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	return container_of(req, struct s3c_hsudc_req, req);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return container_of(ep, struct s3c_hsudc_ep, ep);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	return container_of(gadget, struct s3c_hsudc, gadget);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
1798c2ecf20Sopenharmony_ci	writel(ep_addr, hsudc->regs + S3C_IR);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic inline void __orr32(void __iomem *ptr, u32 val)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	writel(readl(ptr) | val, ptr);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/**
1888c2ecf20Sopenharmony_ci * s3c_hsudc_complete_request - Complete a transfer request.
1898c2ecf20Sopenharmony_ci * @hsep: Endpoint to which the request belongs.
1908c2ecf20Sopenharmony_ci * @hsreq: Transfer request to be completed.
1918c2ecf20Sopenharmony_ci * @status: Transfer completion status for the transfer request.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_cistatic void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
1948c2ecf20Sopenharmony_ci				struct s3c_hsudc_req *hsreq, int status)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	unsigned int stopped = hsep->stopped;
1978c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = hsep->dev;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	list_del_init(&hsreq->queue);
2008c2ecf20Sopenharmony_ci	hsreq->req.status = status;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (!ep_index(hsep)) {
2038c2ecf20Sopenharmony_ci		hsudc->ep0state = WAIT_FOR_SETUP;
2048c2ecf20Sopenharmony_ci		hsep->bEndpointAddress &= ~USB_DIR_IN;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	hsep->stopped = 1;
2088c2ecf20Sopenharmony_ci	spin_unlock(&hsudc->lock);
2098c2ecf20Sopenharmony_ci	usb_gadget_giveback_request(&hsep->ep, &hsreq->req);
2108c2ecf20Sopenharmony_ci	spin_lock(&hsudc->lock);
2118c2ecf20Sopenharmony_ci	hsep->stopped = stopped;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/**
2158c2ecf20Sopenharmony_ci * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
2168c2ecf20Sopenharmony_ci * @hsep: Endpoint for which queued requests have to be terminated.
2178c2ecf20Sopenharmony_ci * @status: Transfer completion status for the transfer request.
2188c2ecf20Sopenharmony_ci */
2198c2ecf20Sopenharmony_cistatic void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	while (!list_empty(&hsep->queue)) {
2248c2ecf20Sopenharmony_ci		hsreq = list_entry(hsep->queue.next,
2258c2ecf20Sopenharmony_ci				struct s3c_hsudc_req, queue);
2268c2ecf20Sopenharmony_ci		s3c_hsudc_complete_request(hsep, hsreq, status);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**
2318c2ecf20Sopenharmony_ci * s3c_hsudc_stop_activity - Stop activity on all endpoints.
2328c2ecf20Sopenharmony_ci * @hsudc: Device controller for which EP activity is to be stopped.
2338c2ecf20Sopenharmony_ci *
2348c2ecf20Sopenharmony_ci * All the endpoints are stopped and any pending transfer requests if any on
2358c2ecf20Sopenharmony_ci * the endpoint are terminated.
2368c2ecf20Sopenharmony_ci */
2378c2ecf20Sopenharmony_cistatic void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep;
2408c2ecf20Sopenharmony_ci	int epnum;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
2458c2ecf20Sopenharmony_ci		hsep = &hsudc->ep[epnum];
2468c2ecf20Sopenharmony_ci		hsep->stopped = 1;
2478c2ecf20Sopenharmony_ci		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/**
2528c2ecf20Sopenharmony_ci * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
2538c2ecf20Sopenharmony_ci * @hsudc: Device controller from which setup packet is to be read.
2548c2ecf20Sopenharmony_ci * @buf: The buffer into which the setup packet is read.
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci * The setup packet received in the EP0 fifo is read and stored into a
2578c2ecf20Sopenharmony_ci * given buffer address.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	int count;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	count = readl(hsudc->regs + S3C_BRCR);
2658c2ecf20Sopenharmony_ci	while (count--)
2668c2ecf20Sopenharmony_ci		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/**
2728c2ecf20Sopenharmony_ci * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
2738c2ecf20Sopenharmony_ci * @hsep: Endpoint to which the data is to be written.
2748c2ecf20Sopenharmony_ci * @hsreq: Transfer request from which the next chunk of data is written.
2758c2ecf20Sopenharmony_ci *
2768c2ecf20Sopenharmony_ci * Write the next chunk of data from a transfer request to the endpoint FIFO.
2778c2ecf20Sopenharmony_ci * If the transfer request completes, 1 is returned, otherwise 0 is returned.
2788c2ecf20Sopenharmony_ci */
2798c2ecf20Sopenharmony_cistatic int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
2808c2ecf20Sopenharmony_ci				struct s3c_hsudc_req *hsreq)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	u16 *buf;
2838c2ecf20Sopenharmony_ci	u32 max = ep_maxpacket(hsep);
2848c2ecf20Sopenharmony_ci	u32 count, length;
2858c2ecf20Sopenharmony_ci	bool is_last;
2868c2ecf20Sopenharmony_ci	void __iomem *fifo = hsep->fifo;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	buf = hsreq->req.buf + hsreq->req.actual;
2898c2ecf20Sopenharmony_ci	prefetch(buf);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	length = hsreq->req.length - hsreq->req.actual;
2928c2ecf20Sopenharmony_ci	length = min(length, max);
2938c2ecf20Sopenharmony_ci	hsreq->req.actual += length;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	writel(length, hsep->dev->regs + S3C_BWCR);
2968c2ecf20Sopenharmony_ci	for (count = 0; count < length; count += 2)
2978c2ecf20Sopenharmony_ci		writel(*buf++, fifo);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (count != max) {
3008c2ecf20Sopenharmony_ci		is_last = true;
3018c2ecf20Sopenharmony_ci	} else {
3028c2ecf20Sopenharmony_ci		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
3038c2ecf20Sopenharmony_ci			is_last = false;
3048c2ecf20Sopenharmony_ci		else
3058c2ecf20Sopenharmony_ci			is_last = true;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (is_last) {
3098c2ecf20Sopenharmony_ci		s3c_hsudc_complete_request(hsep, hsreq, 0);
3108c2ecf20Sopenharmony_ci		return 1;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/**
3178c2ecf20Sopenharmony_ci * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
3188c2ecf20Sopenharmony_ci * @hsep: Endpoint from which the data is to be read.
3198c2ecf20Sopenharmony_ci * @hsreq: Transfer request to which the next chunk of data read is written.
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * Read the next chunk of data from the endpoint FIFO and a write it to the
3228c2ecf20Sopenharmony_ci * transfer request buffer. If the transfer request completes, 1 is returned,
3238c2ecf20Sopenharmony_ci * otherwise 0 is returned.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
3268c2ecf20Sopenharmony_ci				struct s3c_hsudc_req *hsreq)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = hsep->dev;
3298c2ecf20Sopenharmony_ci	u32 csr, offset;
3308c2ecf20Sopenharmony_ci	u16 *buf, word;
3318c2ecf20Sopenharmony_ci	u32 buflen, rcnt, rlen;
3328c2ecf20Sopenharmony_ci	void __iomem *fifo = hsep->fifo;
3338c2ecf20Sopenharmony_ci	u32 is_short = 0;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
3368c2ecf20Sopenharmony_ci	csr = readl(hsudc->regs + offset);
3378c2ecf20Sopenharmony_ci	if (!(csr & S3C_ESR_RX_SUCCESS))
3388c2ecf20Sopenharmony_ci		return -EINVAL;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	buf = hsreq->req.buf + hsreq->req.actual;
3418c2ecf20Sopenharmony_ci	prefetchw(buf);
3428c2ecf20Sopenharmony_ci	buflen = hsreq->req.length - hsreq->req.actual;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	rcnt = readl(hsudc->regs + S3C_BRCR);
3458c2ecf20Sopenharmony_ci	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	hsreq->req.actual += min(rlen, buflen);
3488c2ecf20Sopenharmony_ci	is_short = (rlen < hsep->ep.maxpacket);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	while (rcnt-- != 0) {
3518c2ecf20Sopenharmony_ci		word = (u16)readl(fifo);
3528c2ecf20Sopenharmony_ci		if (buflen) {
3538c2ecf20Sopenharmony_ci			*buf++ = word;
3548c2ecf20Sopenharmony_ci			buflen--;
3558c2ecf20Sopenharmony_ci		} else {
3568c2ecf20Sopenharmony_ci			hsreq->req.status = -EOVERFLOW;
3578c2ecf20Sopenharmony_ci		}
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (is_short || hsreq->req.actual == hsreq->req.length) {
3638c2ecf20Sopenharmony_ci		s3c_hsudc_complete_request(hsep, hsreq, 0);
3648c2ecf20Sopenharmony_ci		return 1;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/**
3718c2ecf20Sopenharmony_ci * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
3728c2ecf20Sopenharmony_ci * @hsudc - Device controller for which the interrupt is to be handled.
3738c2ecf20Sopenharmony_ci * @ep_idx - Endpoint number on which an interrupt is pending.
3748c2ecf20Sopenharmony_ci *
3758c2ecf20Sopenharmony_ci * Handles interrupt for a in-endpoint. The interrupts that are handled are
3768c2ecf20Sopenharmony_ci * stall and data transmit complete interrupt.
3778c2ecf20Sopenharmony_ci */
3788c2ecf20Sopenharmony_cistatic void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
3818c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
3828c2ecf20Sopenharmony_ci	u32 csr;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	csr = readl(hsudc->regs + S3C_ESR);
3858c2ecf20Sopenharmony_ci	if (csr & S3C_ESR_STALL) {
3868c2ecf20Sopenharmony_ci		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
3878c2ecf20Sopenharmony_ci		return;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (csr & S3C_ESR_TX_SUCCESS) {
3918c2ecf20Sopenharmony_ci		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
3928c2ecf20Sopenharmony_ci		if (list_empty(&hsep->queue))
3938c2ecf20Sopenharmony_ci			return;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		hsreq = list_entry(hsep->queue.next,
3968c2ecf20Sopenharmony_ci				struct s3c_hsudc_req, queue);
3978c2ecf20Sopenharmony_ci		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
3988c2ecf20Sopenharmony_ci				(csr & S3C_ESR_PSIF_TWO))
3998c2ecf20Sopenharmony_ci			s3c_hsudc_write_fifo(hsep, hsreq);
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci/**
4048c2ecf20Sopenharmony_ci * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
4058c2ecf20Sopenharmony_ci * @hsudc - Device controller for which the interrupt is to be handled.
4068c2ecf20Sopenharmony_ci * @ep_idx - Endpoint number on which an interrupt is pending.
4078c2ecf20Sopenharmony_ci *
4088c2ecf20Sopenharmony_ci * Handles interrupt for a out-endpoint. The interrupts that are handled are
4098c2ecf20Sopenharmony_ci * stall, flush and data ready interrupt.
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_cistatic void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
4148c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
4158c2ecf20Sopenharmony_ci	u32 csr;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	csr = readl(hsudc->regs + S3C_ESR);
4188c2ecf20Sopenharmony_ci	if (csr & S3C_ESR_STALL) {
4198c2ecf20Sopenharmony_ci		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
4208c2ecf20Sopenharmony_ci		return;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (csr & S3C_ESR_FLUSH) {
4248c2ecf20Sopenharmony_ci		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (csr & S3C_ESR_RX_SUCCESS) {
4298c2ecf20Sopenharmony_ci		if (list_empty(&hsep->queue))
4308c2ecf20Sopenharmony_ci			return;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		hsreq = list_entry(hsep->queue.next,
4338c2ecf20Sopenharmony_ci				struct s3c_hsudc_req, queue);
4348c2ecf20Sopenharmony_ci		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
4358c2ecf20Sopenharmony_ci				(csr & S3C_ESR_PSIF_TWO))
4368c2ecf20Sopenharmony_ci			s3c_hsudc_read_fifo(hsep, hsreq);
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
4418c2ecf20Sopenharmony_ci * @_ep: Endpoint on which halt has to be set or cleared.
4428c2ecf20Sopenharmony_ci * @value: 1 for setting halt on endpoint, 0 to clear halt.
4438c2ecf20Sopenharmony_ci *
4448c2ecf20Sopenharmony_ci * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
4458c2ecf20Sopenharmony_ci * If halt is cleared, for in-endpoints, if there are any pending
4468c2ecf20Sopenharmony_ci * transfer requests, transfers are started.
4478c2ecf20Sopenharmony_ci */
4488c2ecf20Sopenharmony_cistatic int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = our_ep(_ep);
4518c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = hsep->dev;
4528c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
4538c2ecf20Sopenharmony_ci	unsigned long irqflags;
4548c2ecf20Sopenharmony_ci	u32 ecr;
4558c2ecf20Sopenharmony_ci	u32 offset;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
4588c2ecf20Sopenharmony_ci		return -EAGAIN;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hsudc->lock, irqflags);
4618c2ecf20Sopenharmony_ci	set_index(hsudc, ep_index(hsep));
4628c2ecf20Sopenharmony_ci	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
4638c2ecf20Sopenharmony_ci	ecr = readl(hsudc->regs + offset);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (value) {
4668c2ecf20Sopenharmony_ci		ecr |= S3C_ECR_STALL;
4678c2ecf20Sopenharmony_ci		if (ep_index(hsep))
4688c2ecf20Sopenharmony_ci			ecr |= S3C_ECR_FLUSH;
4698c2ecf20Sopenharmony_ci		hsep->stopped = 1;
4708c2ecf20Sopenharmony_ci	} else {
4718c2ecf20Sopenharmony_ci		ecr &= ~S3C_ECR_STALL;
4728c2ecf20Sopenharmony_ci		hsep->stopped = hsep->wedge = 0;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	writel(ecr, hsudc->regs + offset);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
4778c2ecf20Sopenharmony_ci		hsreq = list_entry(hsep->queue.next,
4788c2ecf20Sopenharmony_ci			struct s3c_hsudc_req, queue);
4798c2ecf20Sopenharmony_ci		if (hsreq)
4808c2ecf20Sopenharmony_ci			s3c_hsudc_write_fifo(hsep, hsreq);
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hsudc->lock, irqflags);
4848c2ecf20Sopenharmony_ci	return 0;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored
4888c2ecf20Sopenharmony_ci * @_ep: Endpoint on which wedge has to be set.
4898c2ecf20Sopenharmony_ci *
4908c2ecf20Sopenharmony_ci * Sets the halt feature with the clear requests ignored.
4918c2ecf20Sopenharmony_ci */
4928c2ecf20Sopenharmony_cistatic int s3c_hsudc_set_wedge(struct usb_ep *_ep)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = our_ep(_ep);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (!hsep)
4978c2ecf20Sopenharmony_ci		return -EINVAL;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	hsep->wedge = 1;
5008c2ecf20Sopenharmony_ci	return usb_ep_set_halt(_ep);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
5048c2ecf20Sopenharmony_ci * @_ep: Device controller on which the set/clear feature needs to be handled.
5058c2ecf20Sopenharmony_ci * @ctrl: Control request as received on the endpoint 0.
5068c2ecf20Sopenharmony_ci *
5078c2ecf20Sopenharmony_ci * Handle set feature or clear feature control requests on the control endpoint.
5088c2ecf20Sopenharmony_ci */
5098c2ecf20Sopenharmony_cistatic int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
5108c2ecf20Sopenharmony_ci					struct usb_ctrlrequest *ctrl)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep;
5138c2ecf20Sopenharmony_ci	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
5148c2ecf20Sopenharmony_ci	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
5178c2ecf20Sopenharmony_ci		hsep = &hsudc->ep[ep_num];
5188c2ecf20Sopenharmony_ci		switch (le16_to_cpu(ctrl->wValue)) {
5198c2ecf20Sopenharmony_ci		case USB_ENDPOINT_HALT:
5208c2ecf20Sopenharmony_ci			if (set || !hsep->wedge)
5218c2ecf20Sopenharmony_ci				s3c_hsudc_set_halt(&hsep->ep, set);
5228c2ecf20Sopenharmony_ci			return 0;
5238c2ecf20Sopenharmony_ci		}
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return -ENOENT;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci/**
5308c2ecf20Sopenharmony_ci * s3c_hsudc_process_req_status - Handle get status control request.
5318c2ecf20Sopenharmony_ci * @hsudc: Device controller on which get status request has be handled.
5328c2ecf20Sopenharmony_ci * @ctrl: Control request as received on the endpoint 0.
5338c2ecf20Sopenharmony_ci *
5348c2ecf20Sopenharmony_ci * Handle get status control request received on control endpoint.
5358c2ecf20Sopenharmony_ci */
5368c2ecf20Sopenharmony_cistatic void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
5378c2ecf20Sopenharmony_ci					struct usb_ctrlrequest *ctrl)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
5408c2ecf20Sopenharmony_ci	struct s3c_hsudc_req hsreq;
5418c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep;
5428c2ecf20Sopenharmony_ci	__le16 reply;
5438c2ecf20Sopenharmony_ci	u8 epnum;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	switch (ctrl->bRequestType & USB_RECIP_MASK) {
5468c2ecf20Sopenharmony_ci	case USB_RECIP_DEVICE:
5478c2ecf20Sopenharmony_ci		reply = cpu_to_le16(0);
5488c2ecf20Sopenharmony_ci		break;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	case USB_RECIP_INTERFACE:
5518c2ecf20Sopenharmony_ci		reply = cpu_to_le16(0);
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	case USB_RECIP_ENDPOINT:
5558c2ecf20Sopenharmony_ci		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
5568c2ecf20Sopenharmony_ci		hsep = &hsudc->ep[epnum];
5578c2ecf20Sopenharmony_ci		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hsreq.queue);
5628c2ecf20Sopenharmony_ci	hsreq.req.length = 2;
5638c2ecf20Sopenharmony_ci	hsreq.req.buf = &reply;
5648c2ecf20Sopenharmony_ci	hsreq.req.actual = 0;
5658c2ecf20Sopenharmony_ci	hsreq.req.complete = NULL;
5668c2ecf20Sopenharmony_ci	s3c_hsudc_write_fifo(hsep0, &hsreq);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/**
5708c2ecf20Sopenharmony_ci * s3c_hsudc_process_setup - Process control request received on endpoint 0.
5718c2ecf20Sopenharmony_ci * @hsudc: Device controller on which control request has been received.
5728c2ecf20Sopenharmony_ci *
5738c2ecf20Sopenharmony_ci * Read the control request received on endpoint 0, decode it and handle
5748c2ecf20Sopenharmony_ci * the request.
5758c2ecf20Sopenharmony_ci */
5768c2ecf20Sopenharmony_cistatic void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
5798c2ecf20Sopenharmony_ci	struct usb_ctrlrequest ctrl = {0};
5808c2ecf20Sopenharmony_ci	int ret;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	s3c_hsudc_nuke_ep(hsep, -EPROTO);
5838c2ecf20Sopenharmony_ci	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (ctrl.bRequestType & USB_DIR_IN) {
5868c2ecf20Sopenharmony_ci		hsep->bEndpointAddress |= USB_DIR_IN;
5878c2ecf20Sopenharmony_ci		hsudc->ep0state = DATA_STATE_XMIT;
5888c2ecf20Sopenharmony_ci	} else {
5898c2ecf20Sopenharmony_ci		hsep->bEndpointAddress &= ~USB_DIR_IN;
5908c2ecf20Sopenharmony_ci		hsudc->ep0state = DATA_STATE_RECV;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	switch (ctrl.bRequest) {
5948c2ecf20Sopenharmony_ci	case USB_REQ_SET_ADDRESS:
5958c2ecf20Sopenharmony_ci		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
5968c2ecf20Sopenharmony_ci			break;
5978c2ecf20Sopenharmony_ci		hsudc->ep0state = WAIT_FOR_SETUP;
5988c2ecf20Sopenharmony_ci		return;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	case USB_REQ_GET_STATUS:
6018c2ecf20Sopenharmony_ci		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
6028c2ecf20Sopenharmony_ci			break;
6038c2ecf20Sopenharmony_ci		s3c_hsudc_process_req_status(hsudc, &ctrl);
6048c2ecf20Sopenharmony_ci		return;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	case USB_REQ_SET_FEATURE:
6078c2ecf20Sopenharmony_ci	case USB_REQ_CLEAR_FEATURE:
6088c2ecf20Sopenharmony_ci		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
6098c2ecf20Sopenharmony_ci			break;
6108c2ecf20Sopenharmony_ci		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
6118c2ecf20Sopenharmony_ci		hsudc->ep0state = WAIT_FOR_SETUP;
6128c2ecf20Sopenharmony_ci		return;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (hsudc->driver) {
6168c2ecf20Sopenharmony_ci		spin_unlock(&hsudc->lock);
6178c2ecf20Sopenharmony_ci		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
6188c2ecf20Sopenharmony_ci		spin_lock(&hsudc->lock);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
6218c2ecf20Sopenharmony_ci			hsep->bEndpointAddress &= ~USB_DIR_IN;
6228c2ecf20Sopenharmony_ci			hsudc->ep0state = WAIT_FOR_SETUP;
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		if (ret < 0) {
6268c2ecf20Sopenharmony_ci			dev_err(hsudc->dev, "setup failed, returned %d\n",
6278c2ecf20Sopenharmony_ci						ret);
6288c2ecf20Sopenharmony_ci			s3c_hsudc_set_halt(&hsep->ep, 1);
6298c2ecf20Sopenharmony_ci			hsudc->ep0state = WAIT_FOR_SETUP;
6308c2ecf20Sopenharmony_ci			hsep->bEndpointAddress &= ~USB_DIR_IN;
6318c2ecf20Sopenharmony_ci		}
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
6368c2ecf20Sopenharmony_ci * @hsudc: Device controller on which endpoint 0 interrupt has occured.
6378c2ecf20Sopenharmony_ci *
6388c2ecf20Sopenharmony_ci * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
6398c2ecf20Sopenharmony_ci * when a stall handshake is sent to host or data is sent/received on
6408c2ecf20Sopenharmony_ci * endpoint 0.
6418c2ecf20Sopenharmony_ci */
6428c2ecf20Sopenharmony_cistatic void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
6458c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
6468c2ecf20Sopenharmony_ci	u32 csr = readl(hsudc->regs + S3C_EP0SR);
6478c2ecf20Sopenharmony_ci	u32 ecr;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (csr & S3C_EP0SR_STALL) {
6508c2ecf20Sopenharmony_ci		ecr = readl(hsudc->regs + S3C_EP0CR);
6518c2ecf20Sopenharmony_ci		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
6528c2ecf20Sopenharmony_ci		writel(ecr, hsudc->regs + S3C_EP0CR);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
6558c2ecf20Sopenharmony_ci		hsep->stopped = 0;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
6588c2ecf20Sopenharmony_ci		hsudc->ep0state = WAIT_FOR_SETUP;
6598c2ecf20Sopenharmony_ci		hsep->bEndpointAddress &= ~USB_DIR_IN;
6608c2ecf20Sopenharmony_ci		return;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (csr & S3C_EP0SR_TX_SUCCESS) {
6648c2ecf20Sopenharmony_ci		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
6658c2ecf20Sopenharmony_ci		if (ep_is_in(hsep)) {
6668c2ecf20Sopenharmony_ci			if (list_empty(&hsep->queue))
6678c2ecf20Sopenharmony_ci				return;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci			hsreq = list_entry(hsep->queue.next,
6708c2ecf20Sopenharmony_ci					struct s3c_hsudc_req, queue);
6718c2ecf20Sopenharmony_ci			s3c_hsudc_write_fifo(hsep, hsreq);
6728c2ecf20Sopenharmony_ci		}
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (csr & S3C_EP0SR_RX_SUCCESS) {
6768c2ecf20Sopenharmony_ci		if (hsudc->ep0state == WAIT_FOR_SETUP)
6778c2ecf20Sopenharmony_ci			s3c_hsudc_process_setup(hsudc);
6788c2ecf20Sopenharmony_ci		else {
6798c2ecf20Sopenharmony_ci			if (!ep_is_in(hsep)) {
6808c2ecf20Sopenharmony_ci				if (list_empty(&hsep->queue))
6818c2ecf20Sopenharmony_ci					return;
6828c2ecf20Sopenharmony_ci				hsreq = list_entry(hsep->queue.next,
6838c2ecf20Sopenharmony_ci					struct s3c_hsudc_req, queue);
6848c2ecf20Sopenharmony_ci				s3c_hsudc_read_fifo(hsep, hsreq);
6858c2ecf20Sopenharmony_ci			}
6868c2ecf20Sopenharmony_ci		}
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci/**
6918c2ecf20Sopenharmony_ci * s3c_hsudc_ep_enable - Enable a endpoint.
6928c2ecf20Sopenharmony_ci * @_ep: The endpoint to be enabled.
6938c2ecf20Sopenharmony_ci * @desc: Endpoint descriptor.
6948c2ecf20Sopenharmony_ci *
6958c2ecf20Sopenharmony_ci * Enables a endpoint when called from the gadget driver. Endpoint stall if
6968c2ecf20Sopenharmony_ci * any is cleared, transfer type is configured and endpoint interrupt is
6978c2ecf20Sopenharmony_ci * enabled.
6988c2ecf20Sopenharmony_ci */
6998c2ecf20Sopenharmony_cistatic int s3c_hsudc_ep_enable(struct usb_ep *_ep,
7008c2ecf20Sopenharmony_ci				const struct usb_endpoint_descriptor *desc)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep;
7038c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc;
7048c2ecf20Sopenharmony_ci	unsigned long flags;
7058c2ecf20Sopenharmony_ci	u32 ecr = 0;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	hsep = our_ep(_ep);
7088c2ecf20Sopenharmony_ci	if (!_ep || !desc || _ep->name == ep0name
7098c2ecf20Sopenharmony_ci		|| desc->bDescriptorType != USB_DT_ENDPOINT
7108c2ecf20Sopenharmony_ci		|| hsep->bEndpointAddress != desc->bEndpointAddress
7118c2ecf20Sopenharmony_ci		|| ep_maxpacket(hsep) < usb_endpoint_maxp(desc))
7128c2ecf20Sopenharmony_ci		return -EINVAL;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
7158c2ecf20Sopenharmony_ci		&& usb_endpoint_maxp(desc) != ep_maxpacket(hsep))
7168c2ecf20Sopenharmony_ci		|| !desc->wMaxPacketSize)
7178c2ecf20Sopenharmony_ci		return -ERANGE;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	hsudc = hsep->dev;
7208c2ecf20Sopenharmony_ci	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
7218c2ecf20Sopenharmony_ci		return -ESHUTDOWN;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hsudc->lock, flags);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	set_index(hsudc, hsep->bEndpointAddress);
7268c2ecf20Sopenharmony_ci	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
7278c2ecf20Sopenharmony_ci	writel(ecr, hsudc->regs + S3C_ECR);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	hsep->stopped = hsep->wedge = 0;
7308c2ecf20Sopenharmony_ci	hsep->ep.desc = desc;
7318c2ecf20Sopenharmony_ci	hsep->ep.maxpacket = usb_endpoint_maxp(desc);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	s3c_hsudc_set_halt(_ep, 0);
7348c2ecf20Sopenharmony_ci	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hsudc->lock, flags);
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci/**
7418c2ecf20Sopenharmony_ci * s3c_hsudc_ep_disable - Disable a endpoint.
7428c2ecf20Sopenharmony_ci * @_ep: The endpoint to be disabled.
7438c2ecf20Sopenharmony_ci * @desc: Endpoint descriptor.
7448c2ecf20Sopenharmony_ci *
7458c2ecf20Sopenharmony_ci * Disables a endpoint when called from the gadget driver.
7468c2ecf20Sopenharmony_ci */
7478c2ecf20Sopenharmony_cistatic int s3c_hsudc_ep_disable(struct usb_ep *_ep)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = our_ep(_ep);
7508c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = hsep->dev;
7518c2ecf20Sopenharmony_ci	unsigned long flags;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if (!_ep || !hsep->ep.desc)
7548c2ecf20Sopenharmony_ci		return -EINVAL;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hsudc->lock, flags);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	set_index(hsudc, hsep->bEndpointAddress);
7598c2ecf20Sopenharmony_ci	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	hsep->ep.desc = NULL;
7648c2ecf20Sopenharmony_ci	hsep->stopped = 1;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hsudc->lock, flags);
7678c2ecf20Sopenharmony_ci	return 0;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/**
7718c2ecf20Sopenharmony_ci * s3c_hsudc_alloc_request - Allocate a new request.
7728c2ecf20Sopenharmony_ci * @_ep: Endpoint for which request is allocated (not used).
7738c2ecf20Sopenharmony_ci * @gfp_flags: Flags used for the allocation.
7748c2ecf20Sopenharmony_ci *
7758c2ecf20Sopenharmony_ci * Allocates a single transfer request structure when called from gadget driver.
7768c2ecf20Sopenharmony_ci */
7778c2ecf20Sopenharmony_cistatic struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
7788c2ecf20Sopenharmony_ci						gfp_t gfp_flags)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	hsreq = kzalloc(sizeof(*hsreq), gfp_flags);
7838c2ecf20Sopenharmony_ci	if (!hsreq)
7848c2ecf20Sopenharmony_ci		return NULL;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hsreq->queue);
7878c2ecf20Sopenharmony_ci	return &hsreq->req;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci/**
7918c2ecf20Sopenharmony_ci * s3c_hsudc_free_request - Deallocate a request.
7928c2ecf20Sopenharmony_ci * @ep: Endpoint for which request is deallocated (not used).
7938c2ecf20Sopenharmony_ci * @_req: Request to be deallocated.
7948c2ecf20Sopenharmony_ci *
7958c2ecf20Sopenharmony_ci * Allocates a single transfer request structure when called from gadget driver.
7968c2ecf20Sopenharmony_ci */
7978c2ecf20Sopenharmony_cistatic void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	hsreq = our_req(_req);
8028c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&hsreq->queue));
8038c2ecf20Sopenharmony_ci	kfree(hsreq);
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci/**
8078c2ecf20Sopenharmony_ci * s3c_hsudc_queue - Queue a transfer request for the endpoint.
8088c2ecf20Sopenharmony_ci * @_ep: Endpoint for which the request is queued.
8098c2ecf20Sopenharmony_ci * @_req: Request to be queued.
8108c2ecf20Sopenharmony_ci * @gfp_flags: Not used.
8118c2ecf20Sopenharmony_ci *
8128c2ecf20Sopenharmony_ci * Start or enqueue a request for a endpoint when called from gadget driver.
8138c2ecf20Sopenharmony_ci */
8148c2ecf20Sopenharmony_cistatic int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
8158c2ecf20Sopenharmony_ci			gfp_t gfp_flags)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
8188c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep;
8198c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc;
8208c2ecf20Sopenharmony_ci	unsigned long flags;
8218c2ecf20Sopenharmony_ci	u32 offset;
8228c2ecf20Sopenharmony_ci	u32 csr;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	hsreq = our_req(_req);
8258c2ecf20Sopenharmony_ci	if ((!_req || !_req->complete || !_req->buf ||
8268c2ecf20Sopenharmony_ci		!list_empty(&hsreq->queue)))
8278c2ecf20Sopenharmony_ci		return -EINVAL;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	hsep = our_ep(_ep);
8308c2ecf20Sopenharmony_ci	hsudc = hsep->dev;
8318c2ecf20Sopenharmony_ci	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
8328c2ecf20Sopenharmony_ci		return -ESHUTDOWN;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hsudc->lock, flags);
8358c2ecf20Sopenharmony_ci	set_index(hsudc, hsep->bEndpointAddress);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	_req->status = -EINPROGRESS;
8388c2ecf20Sopenharmony_ci	_req->actual = 0;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	if (!ep_index(hsep) && _req->length == 0) {
8418c2ecf20Sopenharmony_ci		hsudc->ep0state = WAIT_FOR_SETUP;
8428c2ecf20Sopenharmony_ci		s3c_hsudc_complete_request(hsep, hsreq, 0);
8438c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hsudc->lock, flags);
8448c2ecf20Sopenharmony_ci		return 0;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (list_empty(&hsep->queue) && !hsep->stopped) {
8488c2ecf20Sopenharmony_ci		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
8498c2ecf20Sopenharmony_ci		if (ep_is_in(hsep)) {
8508c2ecf20Sopenharmony_ci			csr = readl(hsudc->regs + offset);
8518c2ecf20Sopenharmony_ci			if (!(csr & S3C_ESR_TX_SUCCESS) &&
8528c2ecf20Sopenharmony_ci				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
8538c2ecf20Sopenharmony_ci				hsreq = NULL;
8548c2ecf20Sopenharmony_ci		} else {
8558c2ecf20Sopenharmony_ci			csr = readl(hsudc->regs + offset);
8568c2ecf20Sopenharmony_ci			if ((csr & S3C_ESR_RX_SUCCESS)
8578c2ecf20Sopenharmony_ci				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
8588c2ecf20Sopenharmony_ci				hsreq = NULL;
8598c2ecf20Sopenharmony_ci		}
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (hsreq)
8638c2ecf20Sopenharmony_ci		list_add_tail(&hsreq->queue, &hsep->queue);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hsudc->lock, flags);
8668c2ecf20Sopenharmony_ci	return 0;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci/**
8708c2ecf20Sopenharmony_ci * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
8718c2ecf20Sopenharmony_ci * @_ep: Endpoint from which the request is dequeued.
8728c2ecf20Sopenharmony_ci * @_req: Request to be dequeued.
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * Dequeue a request from a endpoint when called from gadget driver.
8758c2ecf20Sopenharmony_ci */
8768c2ecf20Sopenharmony_cistatic int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep = our_ep(_ep);
8798c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = hsep->dev;
8808c2ecf20Sopenharmony_ci	struct s3c_hsudc_req *hsreq;
8818c2ecf20Sopenharmony_ci	unsigned long flags;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	hsep = our_ep(_ep);
8848c2ecf20Sopenharmony_ci	if (!_ep || hsep->ep.name == ep0name)
8858c2ecf20Sopenharmony_ci		return -EINVAL;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hsudc->lock, flags);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	list_for_each_entry(hsreq, &hsep->queue, queue) {
8908c2ecf20Sopenharmony_ci		if (&hsreq->req == _req)
8918c2ecf20Sopenharmony_ci			break;
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	if (&hsreq->req != _req) {
8948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hsudc->lock, flags);
8958c2ecf20Sopenharmony_ci		return -EINVAL;
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	set_index(hsudc, hsep->bEndpointAddress);
8998c2ecf20Sopenharmony_ci	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hsudc->lock, flags);
9028c2ecf20Sopenharmony_ci	return 0;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic const struct usb_ep_ops s3c_hsudc_ep_ops = {
9068c2ecf20Sopenharmony_ci	.enable = s3c_hsudc_ep_enable,
9078c2ecf20Sopenharmony_ci	.disable = s3c_hsudc_ep_disable,
9088c2ecf20Sopenharmony_ci	.alloc_request = s3c_hsudc_alloc_request,
9098c2ecf20Sopenharmony_ci	.free_request = s3c_hsudc_free_request,
9108c2ecf20Sopenharmony_ci	.queue = s3c_hsudc_queue,
9118c2ecf20Sopenharmony_ci	.dequeue = s3c_hsudc_dequeue,
9128c2ecf20Sopenharmony_ci	.set_halt = s3c_hsudc_set_halt,
9138c2ecf20Sopenharmony_ci	.set_wedge = s3c_hsudc_set_wedge,
9148c2ecf20Sopenharmony_ci};
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci/**
9178c2ecf20Sopenharmony_ci * s3c_hsudc_initep - Initialize a endpoint to default state.
9188c2ecf20Sopenharmony_ci * @hsudc - Reference to the device controller.
9198c2ecf20Sopenharmony_ci * @hsep - Endpoint to be initialized.
9208c2ecf20Sopenharmony_ci * @epnum - Address to be assigned to the endpoint.
9218c2ecf20Sopenharmony_ci *
9228c2ecf20Sopenharmony_ci * Initialize a endpoint with default configuration.
9238c2ecf20Sopenharmony_ci */
9248c2ecf20Sopenharmony_cistatic void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
9258c2ecf20Sopenharmony_ci				struct s3c_hsudc_ep *hsep, int epnum)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	char *dir;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if ((epnum % 2) == 0) {
9308c2ecf20Sopenharmony_ci		dir = "out";
9318c2ecf20Sopenharmony_ci	} else {
9328c2ecf20Sopenharmony_ci		dir = "in";
9338c2ecf20Sopenharmony_ci		hsep->bEndpointAddress = USB_DIR_IN;
9348c2ecf20Sopenharmony_ci	}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	hsep->bEndpointAddress |= epnum;
9378c2ecf20Sopenharmony_ci	if (epnum)
9388c2ecf20Sopenharmony_ci		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
9398c2ecf20Sopenharmony_ci	else
9408c2ecf20Sopenharmony_ci		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hsep->queue);
9438c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hsep->ep.ep_list);
9448c2ecf20Sopenharmony_ci	if (epnum)
9458c2ecf20Sopenharmony_ci		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	hsep->dev = hsudc;
9488c2ecf20Sopenharmony_ci	hsep->ep.name = hsep->name;
9498c2ecf20Sopenharmony_ci	usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64);
9508c2ecf20Sopenharmony_ci	hsep->ep.ops = &s3c_hsudc_ep_ops;
9518c2ecf20Sopenharmony_ci	hsep->fifo = hsudc->regs + S3C_BR(epnum);
9528c2ecf20Sopenharmony_ci	hsep->ep.desc = NULL;
9538c2ecf20Sopenharmony_ci	hsep->stopped = 0;
9548c2ecf20Sopenharmony_ci	hsep->wedge = 0;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	if (epnum == 0) {
9578c2ecf20Sopenharmony_ci		hsep->ep.caps.type_control = true;
9588c2ecf20Sopenharmony_ci		hsep->ep.caps.dir_in = true;
9598c2ecf20Sopenharmony_ci		hsep->ep.caps.dir_out = true;
9608c2ecf20Sopenharmony_ci	} else {
9618c2ecf20Sopenharmony_ci		hsep->ep.caps.type_iso = true;
9628c2ecf20Sopenharmony_ci		hsep->ep.caps.type_bulk = true;
9638c2ecf20Sopenharmony_ci		hsep->ep.caps.type_int = true;
9648c2ecf20Sopenharmony_ci	}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (epnum & 1)
9678c2ecf20Sopenharmony_ci		hsep->ep.caps.dir_in = true;
9688c2ecf20Sopenharmony_ci	else
9698c2ecf20Sopenharmony_ci		hsep->ep.caps.dir_out = true;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	set_index(hsudc, epnum);
9728c2ecf20Sopenharmony_ci	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci/**
9768c2ecf20Sopenharmony_ci * s3c_hsudc_setup_ep - Configure all endpoints to default state.
9778c2ecf20Sopenharmony_ci * @hsudc: Reference to device controller.
9788c2ecf20Sopenharmony_ci *
9798c2ecf20Sopenharmony_ci * Configures all endpoints to default state.
9808c2ecf20Sopenharmony_ci */
9818c2ecf20Sopenharmony_cistatic void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	int epnum;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	hsudc->ep0state = WAIT_FOR_SETUP;
9868c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
9878c2ecf20Sopenharmony_ci	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
9888c2ecf20Sopenharmony_ci		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci/**
9928c2ecf20Sopenharmony_ci * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
9938c2ecf20Sopenharmony_ci * @hsudc: Reference to device controller.
9948c2ecf20Sopenharmony_ci *
9958c2ecf20Sopenharmony_ci * Reconfigures the device controller registers to a default state.
9968c2ecf20Sopenharmony_ci */
9978c2ecf20Sopenharmony_cistatic void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	writel(0xAA, hsudc->regs + S3C_EDR);
10008c2ecf20Sopenharmony_ci	writel(1, hsudc->regs + S3C_EIER);
10018c2ecf20Sopenharmony_ci	writel(0, hsudc->regs + S3C_TR);
10028c2ecf20Sopenharmony_ci	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
10038c2ecf20Sopenharmony_ci			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
10048c2ecf20Sopenharmony_ci	writel(0, hsudc->regs + S3C_EP0CR);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	s3c_hsudc_setup_ep(hsudc);
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci/**
10108c2ecf20Sopenharmony_ci * s3c_hsudc_irq - Interrupt handler for device controller.
10118c2ecf20Sopenharmony_ci * @irq: Not used.
10128c2ecf20Sopenharmony_ci * @_dev: Reference to the device controller.
10138c2ecf20Sopenharmony_ci *
10148c2ecf20Sopenharmony_ci * Interrupt handler for the device controller. This handler handles controller
10158c2ecf20Sopenharmony_ci * interrupts and endpoint interrupts.
10168c2ecf20Sopenharmony_ci */
10178c2ecf20Sopenharmony_cistatic irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = _dev;
10208c2ecf20Sopenharmony_ci	struct s3c_hsudc_ep *hsep;
10218c2ecf20Sopenharmony_ci	u32 ep_intr;
10228c2ecf20Sopenharmony_ci	u32 sys_status;
10238c2ecf20Sopenharmony_ci	u32 ep_idx;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	spin_lock(&hsudc->lock);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	sys_status = readl(hsudc->regs + S3C_SSR);
10288c2ecf20Sopenharmony_ci	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
10318c2ecf20Sopenharmony_ci		spin_unlock(&hsudc->lock);
10328c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (sys_status) {
10368c2ecf20Sopenharmony_ci		if (sys_status & S3C_SSR_VBUSON)
10378c2ecf20Sopenharmony_ci			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci		if (sys_status & S3C_SSR_ERR)
10408c2ecf20Sopenharmony_ci			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci		if (sys_status & S3C_SSR_SDE) {
10438c2ecf20Sopenharmony_ci			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
10448c2ecf20Sopenharmony_ci			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
10458c2ecf20Sopenharmony_ci				USB_SPEED_HIGH : USB_SPEED_FULL;
10468c2ecf20Sopenharmony_ci		}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci		if (sys_status & S3C_SSR_SUSPEND) {
10498c2ecf20Sopenharmony_ci			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
10508c2ecf20Sopenharmony_ci			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
10518c2ecf20Sopenharmony_ci				&& hsudc->driver && hsudc->driver->suspend)
10528c2ecf20Sopenharmony_ci				hsudc->driver->suspend(&hsudc->gadget);
10538c2ecf20Sopenharmony_ci		}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci		if (sys_status & S3C_SSR_RESUME) {
10568c2ecf20Sopenharmony_ci			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
10578c2ecf20Sopenharmony_ci			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
10588c2ecf20Sopenharmony_ci				&& hsudc->driver && hsudc->driver->resume)
10598c2ecf20Sopenharmony_ci				hsudc->driver->resume(&hsudc->gadget);
10608c2ecf20Sopenharmony_ci		}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci		if (sys_status & S3C_SSR_RESET) {
10638c2ecf20Sopenharmony_ci			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
10648c2ecf20Sopenharmony_ci			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
10658c2ecf20Sopenharmony_ci				hsep = &hsudc->ep[ep_idx];
10668c2ecf20Sopenharmony_ci				hsep->stopped = 1;
10678c2ecf20Sopenharmony_ci				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
10688c2ecf20Sopenharmony_ci			}
10698c2ecf20Sopenharmony_ci			s3c_hsudc_reconfig(hsudc);
10708c2ecf20Sopenharmony_ci			hsudc->ep0state = WAIT_FOR_SETUP;
10718c2ecf20Sopenharmony_ci		}
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	if (ep_intr & S3C_EIR_EP0) {
10758c2ecf20Sopenharmony_ci		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
10768c2ecf20Sopenharmony_ci		set_index(hsudc, 0);
10778c2ecf20Sopenharmony_ci		s3c_hsudc_handle_ep0_intr(hsudc);
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	ep_intr >>= 1;
10818c2ecf20Sopenharmony_ci	ep_idx = 1;
10828c2ecf20Sopenharmony_ci	while (ep_intr) {
10838c2ecf20Sopenharmony_ci		if (ep_intr & 1)  {
10848c2ecf20Sopenharmony_ci			hsep = &hsudc->ep[ep_idx];
10858c2ecf20Sopenharmony_ci			set_index(hsudc, ep_idx);
10868c2ecf20Sopenharmony_ci			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
10878c2ecf20Sopenharmony_ci			if (ep_is_in(hsep))
10888c2ecf20Sopenharmony_ci				s3c_hsudc_epin_intr(hsudc, ep_idx);
10898c2ecf20Sopenharmony_ci			else
10908c2ecf20Sopenharmony_ci				s3c_hsudc_epout_intr(hsudc, ep_idx);
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci		ep_intr >>= 1;
10938c2ecf20Sopenharmony_ci		ep_idx++;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	spin_unlock(&hsudc->lock);
10978c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic int s3c_hsudc_start(struct usb_gadget *gadget,
11018c2ecf20Sopenharmony_ci		struct usb_gadget_driver *driver)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = to_hsudc(gadget);
11048c2ecf20Sopenharmony_ci	int ret;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	if (!driver
11078c2ecf20Sopenharmony_ci		|| driver->max_speed < USB_SPEED_FULL
11088c2ecf20Sopenharmony_ci		|| !driver->setup)
11098c2ecf20Sopenharmony_ci		return -EINVAL;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	if (!hsudc)
11128c2ecf20Sopenharmony_ci		return -ENODEV;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if (hsudc->driver)
11158c2ecf20Sopenharmony_ci		return -EBUSY;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	hsudc->driver = driver;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies),
11208c2ecf20Sopenharmony_ci				    hsudc->supplies);
11218c2ecf20Sopenharmony_ci	if (ret != 0) {
11228c2ecf20Sopenharmony_ci		dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret);
11238c2ecf20Sopenharmony_ci		goto err_supplies;
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	/* connect to bus through transceiver */
11278c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(hsudc->transceiver)) {
11288c2ecf20Sopenharmony_ci		ret = otg_set_peripheral(hsudc->transceiver->otg,
11298c2ecf20Sopenharmony_ci					&hsudc->gadget);
11308c2ecf20Sopenharmony_ci		if (ret) {
11318c2ecf20Sopenharmony_ci			dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
11328c2ecf20Sopenharmony_ci					hsudc->gadget.name);
11338c2ecf20Sopenharmony_ci			goto err_otg;
11348c2ecf20Sopenharmony_ci		}
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	enable_irq(hsudc->irq);
11388c2ecf20Sopenharmony_ci	s3c_hsudc_reconfig(hsudc);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	pm_runtime_get_sync(hsudc->dev);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (hsudc->pd->phy_init)
11438c2ecf20Sopenharmony_ci		hsudc->pd->phy_init();
11448c2ecf20Sopenharmony_ci	if (hsudc->pd->gpio_init)
11458c2ecf20Sopenharmony_ci		hsudc->pd->gpio_init();
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_cierr_otg:
11498c2ecf20Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
11508c2ecf20Sopenharmony_cierr_supplies:
11518c2ecf20Sopenharmony_ci	hsudc->driver = NULL;
11528c2ecf20Sopenharmony_ci	return ret;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic int s3c_hsudc_stop(struct usb_gadget *gadget)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = to_hsudc(gadget);
11588c2ecf20Sopenharmony_ci	unsigned long flags;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	if (!hsudc)
11618c2ecf20Sopenharmony_ci		return -ENODEV;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hsudc->lock, flags);
11648c2ecf20Sopenharmony_ci	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
11658c2ecf20Sopenharmony_ci	if (hsudc->pd->phy_uninit)
11668c2ecf20Sopenharmony_ci		hsudc->pd->phy_uninit();
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	pm_runtime_put(hsudc->dev);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	if (hsudc->pd->gpio_uninit)
11718c2ecf20Sopenharmony_ci		hsudc->pd->gpio_uninit();
11728c2ecf20Sopenharmony_ci	s3c_hsudc_stop_activity(hsudc);
11738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hsudc->lock, flags);
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(hsudc->transceiver))
11768c2ecf20Sopenharmony_ci		(void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	disable_irq(hsudc->irq);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
11818c2ecf20Sopenharmony_ci	hsudc->driver = NULL;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	return 0;
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_cistatic int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
11928c2ecf20Sopenharmony_ci{
11938c2ecf20Sopenharmony_ci	return s3c_hsudc_read_frameno(to_hsudc(gadget));
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc = to_hsudc(gadget);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	if (!hsudc)
12018c2ecf20Sopenharmony_ci		return -ENODEV;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(hsudc->transceiver))
12048c2ecf20Sopenharmony_ci		return usb_phy_set_power(hsudc->transceiver, mA);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops s3c_hsudc_gadget_ops = {
12108c2ecf20Sopenharmony_ci	.get_frame	= s3c_hsudc_gadget_getframe,
12118c2ecf20Sopenharmony_ci	.udc_start	= s3c_hsudc_start,
12128c2ecf20Sopenharmony_ci	.udc_stop	= s3c_hsudc_stop,
12138c2ecf20Sopenharmony_ci	.vbus_draw	= s3c_hsudc_vbus_draw,
12148c2ecf20Sopenharmony_ci};
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic int s3c_hsudc_probe(struct platform_device *pdev)
12178c2ecf20Sopenharmony_ci{
12188c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
12198c2ecf20Sopenharmony_ci	struct s3c_hsudc *hsudc;
12208c2ecf20Sopenharmony_ci	struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev);
12218c2ecf20Sopenharmony_ci	int ret, i;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
12248c2ecf20Sopenharmony_ci			sizeof(struct s3c_hsudc_ep) * pd->epnum,
12258c2ecf20Sopenharmony_ci			GFP_KERNEL);
12268c2ecf20Sopenharmony_ci	if (!hsudc)
12278c2ecf20Sopenharmony_ci		return -ENOMEM;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dev);
12308c2ecf20Sopenharmony_ci	hsudc->dev = dev;
12318c2ecf20Sopenharmony_ci	hsudc->pd = dev_get_platdata(&pdev->dev);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
12368c2ecf20Sopenharmony_ci		hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
12398c2ecf20Sopenharmony_ci				 hsudc->supplies);
12408c2ecf20Sopenharmony_ci	if (ret != 0) {
12418c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
12428c2ecf20Sopenharmony_ci			dev_err(dev, "failed to request supplies: %d\n", ret);
12438c2ecf20Sopenharmony_ci		goto err_supplies;
12448c2ecf20Sopenharmony_ci	}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	hsudc->regs = devm_platform_ioremap_resource(pdev, 0);
12478c2ecf20Sopenharmony_ci	if (IS_ERR(hsudc->regs)) {
12488c2ecf20Sopenharmony_ci		ret = PTR_ERR(hsudc->regs);
12498c2ecf20Sopenharmony_ci		goto err_res;
12508c2ecf20Sopenharmony_ci	}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	spin_lock_init(&hsudc->lock);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	hsudc->gadget.max_speed = USB_SPEED_HIGH;
12558c2ecf20Sopenharmony_ci	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
12568c2ecf20Sopenharmony_ci	hsudc->gadget.name = dev_name(dev);
12578c2ecf20Sopenharmony_ci	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
12588c2ecf20Sopenharmony_ci	hsudc->gadget.is_otg = 0;
12598c2ecf20Sopenharmony_ci	hsudc->gadget.is_a_peripheral = 0;
12608c2ecf20Sopenharmony_ci	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	s3c_hsudc_setup_ep(hsudc);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
12658c2ecf20Sopenharmony_ci	if (ret < 0)
12668c2ecf20Sopenharmony_ci		goto err_res;
12678c2ecf20Sopenharmony_ci	hsudc->irq = ret;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0,
12708c2ecf20Sopenharmony_ci				driver_name, hsudc);
12718c2ecf20Sopenharmony_ci	if (ret < 0) {
12728c2ecf20Sopenharmony_ci		dev_err(dev, "irq request failed\n");
12738c2ecf20Sopenharmony_ci		goto err_res;
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	hsudc->uclk = devm_clk_get(&pdev->dev, "usb-device");
12778c2ecf20Sopenharmony_ci	if (IS_ERR(hsudc->uclk)) {
12788c2ecf20Sopenharmony_ci		dev_err(dev, "failed to find usb-device clock source\n");
12798c2ecf20Sopenharmony_ci		ret = PTR_ERR(hsudc->uclk);
12808c2ecf20Sopenharmony_ci		goto err_res;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci	clk_enable(hsudc->uclk);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	local_irq_disable();
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	disable_irq(hsudc->irq);
12878c2ecf20Sopenharmony_ci	local_irq_enable();
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);
12908c2ecf20Sopenharmony_ci	if (ret)
12918c2ecf20Sopenharmony_ci		goto err_add_udc;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	return 0;
12968c2ecf20Sopenharmony_cierr_add_udc:
12978c2ecf20Sopenharmony_ci	clk_disable(hsudc->uclk);
12988c2ecf20Sopenharmony_cierr_res:
12998c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(hsudc->transceiver))
13008c2ecf20Sopenharmony_ci		usb_put_phy(hsudc->transceiver);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cierr_supplies:
13038c2ecf20Sopenharmony_ci	return ret;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic struct platform_driver s3c_hsudc_driver = {
13078c2ecf20Sopenharmony_ci	.driver		= {
13088c2ecf20Sopenharmony_ci		.name	= "s3c-hsudc",
13098c2ecf20Sopenharmony_ci	},
13108c2ecf20Sopenharmony_ci	.probe		= s3c_hsudc_probe,
13118c2ecf20Sopenharmony_ci};
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cimodule_platform_driver(s3c_hsudc_driver);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
13168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
13178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
13188c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c-hsudc");
1319