|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling | 
|  | * | 
|  | * Copyright (c) 2016 MediaTek Inc. | 
|  | * | 
|  | * Author:  Chunfeng.Yun <chunfeng.yun@mediatek.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/iopoll.h> | 
|  | #include <linux/usb/composite.h> | 
|  |  | 
|  | #include "mtu3.h" | 
|  | #include "mtu3_debug.h" | 
|  | #include "mtu3_trace.h" | 
|  |  | 
|  | /* ep0 is always mtu3->in_eps[0] */ | 
|  | #define	next_ep0_request(mtu)	next_request((mtu)->ep0) | 
|  |  | 
|  | /* for high speed test mode; see USB 2.0 spec 7.1.20 */ | 
|  | static const u8 mtu3_test_packet[53] = { | 
|  | /* implicit SYNC then DATA0 to start */ | 
|  |  | 
|  | /* JKJKJKJK x9 */ | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | /* JJKKJJKK x8 */ | 
|  | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, | 
|  | /* JJJJKKKK x8 */ | 
|  | 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, | 
|  | /* JJJJJJJKKKKKKK x8 */ | 
|  | 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | /* JJJJJJJK x8 */ | 
|  | 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, | 
|  | /* JKKKKKKK x10, JK */ | 
|  | 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e, | 
|  | /* implicit CRC16 then EOP to end */ | 
|  | }; | 
|  |  | 
|  | static char *decode_ep0_state(struct mtu3 *mtu) | 
|  | { | 
|  | switch (mtu->ep0_state) { | 
|  | case MU3D_EP0_STATE_SETUP: | 
|  | return "SETUP"; | 
|  | case MU3D_EP0_STATE_TX: | 
|  | return "IN"; | 
|  | case MU3D_EP0_STATE_RX: | 
|  | return "OUT"; | 
|  | case MU3D_EP0_STATE_TX_END: | 
|  | return "TX-END"; | 
|  | case MU3D_EP0_STATE_STALL: | 
|  | return "STALL"; | 
|  | default: | 
|  | return "??"; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req) | 
|  | { | 
|  | mtu3_req_complete(mtu->ep0, req, 0); | 
|  | } | 
|  |  | 
|  | static int | 
|  | forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) | 
|  | __releases(mtu->lock) | 
|  | __acquires(mtu->lock) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (!mtu->gadget_driver) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | spin_unlock(&mtu->lock); | 
|  | ret = mtu->gadget_driver->setup(&mtu->g, setup); | 
|  | spin_lock(&mtu->lock); | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len) | 
|  | { | 
|  | void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0; | 
|  | u16 index = 0; | 
|  |  | 
|  | dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n", | 
|  | __func__, mep->epnum, len, src); | 
|  |  | 
|  | if (len >= 4) { | 
|  | iowrite32_rep(fifo, src, len >> 2); | 
|  | index = len & ~0x03; | 
|  | } | 
|  | if (len & 0x02) { | 
|  | writew(*(u16 *)&src[index], fifo); | 
|  | index += 2; | 
|  | } | 
|  | if (len & 0x01) | 
|  | writeb(src[index], fifo); | 
|  | } | 
|  |  | 
|  | static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len) | 
|  | { | 
|  | void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0; | 
|  | u32 value; | 
|  | u16 index = 0; | 
|  |  | 
|  | dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n", | 
|  | __func__, mep->epnum, len, dst); | 
|  |  | 
|  | if (len >= 4) { | 
|  | ioread32_rep(fifo, dst, len >> 2); | 
|  | index = len & ~0x03; | 
|  | } | 
|  | if (len & 0x3) { | 
|  | value = readl(fifo); | 
|  | memcpy(&dst[index], &value, len & 0x3); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | static void ep0_load_test_packet(struct mtu3 *mtu) | 
|  | { | 
|  | /* | 
|  | * because the length of test packet is less than max packet of HS ep0, | 
|  | * write it into fifo directly. | 
|  | */ | 
|  | ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * A. send STALL for setup transfer without data stage: | 
|  | *		set SENDSTALL and SETUPPKTRDY at the same time; | 
|  | * B. send STALL for other cases: | 
|  | *		set SENDSTALL only. | 
|  | */ | 
|  | static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy) | 
|  | { | 
|  | struct mtu3 *mtu = mep0->mtu; | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | u32 csr; | 
|  |  | 
|  | /* EP0_SENTSTALL is W1C */ | 
|  | csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; | 
|  | if (set) | 
|  | csr |= EP0_SENDSTALL | pktrdy; | 
|  | else | 
|  | csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL; | 
|  | mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); | 
|  |  | 
|  | mtu->delayed_status = false; | 
|  | mtu->ep0_state = MU3D_EP0_STATE_SETUP; | 
|  |  | 
|  | dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n", | 
|  | set ? "SEND" : "CLEAR", decode_ep0_state(mtu)); | 
|  | } | 
|  |  | 
|  | static void ep0_do_status_stage(struct mtu3 *mtu) | 
|  | { | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | u32 value; | 
|  |  | 
|  | value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; | 
|  | mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); | 
|  | } | 
|  |  | 
|  | static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq); | 
|  |  | 
|  | static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req) | 
|  | {} | 
|  |  | 
|  | static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req) | 
|  | { | 
|  | struct mtu3_request *mreq; | 
|  | struct mtu3 *mtu; | 
|  | struct usb_set_sel_req sel; | 
|  |  | 
|  | memcpy(&sel, req->buf, sizeof(sel)); | 
|  |  | 
|  | mreq = to_mtu3_request(req); | 
|  | mtu = mreq->mtu; | 
|  | dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n", | 
|  | sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel); | 
|  | } | 
|  |  | 
|  | /* queue data stage to handle 6 byte SET_SEL request */ | 
|  | static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup) | 
|  | { | 
|  | int ret; | 
|  | u16 length = le16_to_cpu(setup->wLength); | 
|  |  | 
|  | if (unlikely(length != 6)) { | 
|  | dev_err(mtu->dev, "%s wrong wLength:%d\n", | 
|  | __func__, length); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | mtu->ep0_req.mep = mtu->ep0; | 
|  | mtu->ep0_req.request.length = 6; | 
|  | mtu->ep0_req.request.buf = mtu->setup_buf; | 
|  | mtu->ep0_req.request.complete = ep0_set_sel_complete; | 
|  | ret = ep0_queue(mtu->ep0, &mtu->ep0_req); | 
|  |  | 
|  | return ret < 0 ? ret : 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) | 
|  | { | 
|  | struct mtu3_ep *mep = NULL; | 
|  | int handled = 1; | 
|  | u8 result[2] = {0, 0}; | 
|  | u8 epnum = 0; | 
|  | int is_in; | 
|  |  | 
|  | switch (setup->bRequestType & USB_RECIP_MASK) { | 
|  | case USB_RECIP_DEVICE: | 
|  | result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED; | 
|  | result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; | 
|  |  | 
|  | if (mtu->g.speed >= USB_SPEED_SUPER) { | 
|  | result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED; | 
|  | result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED; | 
|  | } | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__, | 
|  | result[0], mtu->u1_enable, mtu->u2_enable); | 
|  |  | 
|  | break; | 
|  | case USB_RECIP_INTERFACE: | 
|  | break; | 
|  | case USB_RECIP_ENDPOINT: | 
|  | epnum = (u8) le16_to_cpu(setup->wIndex); | 
|  | is_in = epnum & USB_DIR_IN; | 
|  | epnum &= USB_ENDPOINT_NUMBER_MASK; | 
|  |  | 
|  | if (epnum >= mtu->num_eps) { | 
|  | handled = -EINVAL; | 
|  | break; | 
|  | } | 
|  | if (!epnum) | 
|  | break; | 
|  |  | 
|  | mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum; | 
|  | if (!mep->desc) { | 
|  | handled = -EINVAL; | 
|  | break; | 
|  | } | 
|  | if (mep->flags & MTU3_EP_STALL) | 
|  | result[0] |= 1 << USB_ENDPOINT_HALT; | 
|  |  | 
|  | break; | 
|  | default: | 
|  | /* class, vendor, etc ... delegate */ | 
|  | handled = 0; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (handled > 0) { | 
|  | int ret; | 
|  |  | 
|  | /* prepare a data stage for GET_STATUS */ | 
|  | dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result); | 
|  | memcpy(mtu->setup_buf, result, sizeof(result)); | 
|  | mtu->ep0_req.mep = mtu->ep0; | 
|  | mtu->ep0_req.request.length = 2; | 
|  | mtu->ep0_req.request.buf = &mtu->setup_buf; | 
|  | mtu->ep0_req.request.complete = ep0_dummy_complete; | 
|  | ret = ep0_queue(mtu->ep0, &mtu->ep0_req); | 
|  | if (ret < 0) | 
|  | handled = ret; | 
|  | } | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup) | 
|  | { | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | int handled = 1; | 
|  | u32 value; | 
|  |  | 
|  | switch (le16_to_cpu(setup->wIndex) >> 8) { | 
|  | case USB_TEST_J: | 
|  | dev_dbg(mtu->dev, "USB_TEST_J\n"); | 
|  | mtu->test_mode_nr = TEST_J_MODE; | 
|  | break; | 
|  | case USB_TEST_K: | 
|  | dev_dbg(mtu->dev, "USB_TEST_K\n"); | 
|  | mtu->test_mode_nr = TEST_K_MODE; | 
|  | break; | 
|  | case USB_TEST_SE0_NAK: | 
|  | dev_dbg(mtu->dev, "USB_TEST_SE0_NAK\n"); | 
|  | mtu->test_mode_nr = TEST_SE0_NAK_MODE; | 
|  | break; | 
|  | case USB_TEST_PACKET: | 
|  | dev_dbg(mtu->dev, "USB_TEST_PACKET\n"); | 
|  | mtu->test_mode_nr = TEST_PACKET_MODE; | 
|  | break; | 
|  | default: | 
|  | handled = -EINVAL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | mtu->test_mode = true; | 
|  |  | 
|  | /* no TX completion interrupt, and need restart platform after test */ | 
|  | if (mtu->test_mode_nr == TEST_PACKET_MODE) | 
|  | ep0_load_test_packet(mtu); | 
|  |  | 
|  | /* send status before entering test mode. */ | 
|  | ep0_do_status_stage(mtu); | 
|  |  | 
|  | /* wait for ACK status sent by host */ | 
|  | readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value, | 
|  | !(value & EP0_DATAEND), 100, 5000); | 
|  |  | 
|  | mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr); | 
|  |  | 
|  | mtu->ep0_state = MU3D_EP0_STATE_SETUP; | 
|  |  | 
|  | out: | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | static int ep0_handle_feature_dev(struct mtu3 *mtu, | 
|  | struct usb_ctrlrequest *setup, bool set) | 
|  | { | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | int handled = -EINVAL; | 
|  | u32 lpc; | 
|  |  | 
|  | switch (le16_to_cpu(setup->wValue)) { | 
|  | case USB_DEVICE_REMOTE_WAKEUP: | 
|  | mtu->may_wakeup = !!set; | 
|  | handled = 1; | 
|  | break; | 
|  | case USB_DEVICE_TEST_MODE: | 
|  | if (!set || (mtu->g.speed != USB_SPEED_HIGH) || | 
|  | (le16_to_cpu(setup->wIndex) & 0xff)) | 
|  | break; | 
|  |  | 
|  | handled = handle_test_mode(mtu, setup); | 
|  | break; | 
|  | case USB_DEVICE_U1_ENABLE: | 
|  | if (mtu->g.speed < USB_SPEED_SUPER || | 
|  | mtu->g.state != USB_STATE_CONFIGURED) | 
|  | break; | 
|  |  | 
|  | lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); | 
|  | if (set) | 
|  | lpc |= SW_U1_REQUEST_ENABLE; | 
|  | else | 
|  | lpc &= ~SW_U1_REQUEST_ENABLE; | 
|  | mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); | 
|  |  | 
|  | mtu->u1_enable = !!set; | 
|  | handled = 1; | 
|  | break; | 
|  | case USB_DEVICE_U2_ENABLE: | 
|  | if (mtu->g.speed < USB_SPEED_SUPER || | 
|  | mtu->g.state != USB_STATE_CONFIGURED) | 
|  | break; | 
|  |  | 
|  | lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); | 
|  | if (set) | 
|  | lpc |= SW_U2_REQUEST_ENABLE; | 
|  | else | 
|  | lpc &= ~SW_U2_REQUEST_ENABLE; | 
|  | mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); | 
|  |  | 
|  | mtu->u2_enable = !!set; | 
|  | handled = 1; | 
|  | break; | 
|  | default: | 
|  | handled = -EINVAL; | 
|  | break; | 
|  | } | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | static int ep0_handle_feature(struct mtu3 *mtu, | 
|  | struct usb_ctrlrequest *setup, bool set) | 
|  | { | 
|  | struct mtu3_ep *mep; | 
|  | int handled = -EINVAL; | 
|  | int is_in; | 
|  | u16 value; | 
|  | u16 index; | 
|  | u8 epnum; | 
|  |  | 
|  | value = le16_to_cpu(setup->wValue); | 
|  | index = le16_to_cpu(setup->wIndex); | 
|  |  | 
|  | switch (setup->bRequestType & USB_RECIP_MASK) { | 
|  | case USB_RECIP_DEVICE: | 
|  | handled = ep0_handle_feature_dev(mtu, setup, set); | 
|  | break; | 
|  | case USB_RECIP_INTERFACE: | 
|  | /* superspeed only */ | 
|  | if (value == USB_INTRF_FUNC_SUSPEND && | 
|  | mtu->g.speed >= USB_SPEED_SUPER) { | 
|  | /* | 
|  | * forward the request because function drivers | 
|  | * should handle it | 
|  | */ | 
|  | handled = 0; | 
|  | } | 
|  | break; | 
|  | case USB_RECIP_ENDPOINT: | 
|  | epnum = index & USB_ENDPOINT_NUMBER_MASK; | 
|  | if (epnum == 0 || epnum >= mtu->num_eps || | 
|  | value != USB_ENDPOINT_HALT) | 
|  | break; | 
|  |  | 
|  | is_in = index & USB_DIR_IN; | 
|  | mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum; | 
|  | if (!mep->desc) | 
|  | break; | 
|  |  | 
|  | handled = 1; | 
|  | /* ignore request if endpoint is wedged */ | 
|  | if (mep->flags & MTU3_EP_WEDGE) | 
|  | break; | 
|  |  | 
|  | mtu3_ep_stall_set(mep, set); | 
|  | break; | 
|  | default: | 
|  | /* class, vendor, etc ... delegate */ | 
|  | handled = 0; | 
|  | break; | 
|  | } | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * handle all control requests can be handled | 
|  | * returns: | 
|  | *	negative errno - error happened | 
|  | *	zero - need delegate SETUP to gadget driver | 
|  | *	positive - already handled | 
|  | */ | 
|  | static int handle_standard_request(struct mtu3 *mtu, | 
|  | struct usb_ctrlrequest *setup) | 
|  | { | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | enum usb_device_state state = mtu->g.state; | 
|  | int handled = -EINVAL; | 
|  | u32 dev_conf; | 
|  | u16 value; | 
|  |  | 
|  | value = le16_to_cpu(setup->wValue); | 
|  |  | 
|  | /* the gadget driver handles everything except what we must handle */ | 
|  | switch (setup->bRequest) { | 
|  | case USB_REQ_SET_ADDRESS: | 
|  | /* change it after the status stage */ | 
|  | mtu->address = (u8) (value & 0x7f); | 
|  | dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address); | 
|  |  | 
|  | dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF); | 
|  | dev_conf &= ~DEV_ADDR_MSK; | 
|  | dev_conf |= DEV_ADDR(mtu->address); | 
|  | mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf); | 
|  |  | 
|  | if (mtu->address) | 
|  | usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS); | 
|  | else | 
|  | usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT); | 
|  |  | 
|  | handled = 1; | 
|  | break; | 
|  | case USB_REQ_SET_CONFIGURATION: | 
|  | if (state == USB_STATE_ADDRESS) { | 
|  | usb_gadget_set_state(&mtu->g, | 
|  | USB_STATE_CONFIGURED); | 
|  | } else if (state == USB_STATE_CONFIGURED) { | 
|  | /* | 
|  | * USB2 spec sec 9.4.7, if wValue is 0 then dev | 
|  | * is moved to addressed state | 
|  | */ | 
|  | if (!value) | 
|  | usb_gadget_set_state(&mtu->g, | 
|  | USB_STATE_ADDRESS); | 
|  | } | 
|  | handled = 0; | 
|  | break; | 
|  | case USB_REQ_CLEAR_FEATURE: | 
|  | handled = ep0_handle_feature(mtu, setup, 0); | 
|  | break; | 
|  | case USB_REQ_SET_FEATURE: | 
|  | handled = ep0_handle_feature(mtu, setup, 1); | 
|  | break; | 
|  | case USB_REQ_GET_STATUS: | 
|  | handled = ep0_get_status(mtu, setup); | 
|  | break; | 
|  | case USB_REQ_SET_SEL: | 
|  | handled = ep0_set_sel(mtu, setup); | 
|  | break; | 
|  | case USB_REQ_SET_ISOCH_DELAY: | 
|  | handled = 1; | 
|  | break; | 
|  | default: | 
|  | /* delegate SET_CONFIGURATION, etc */ | 
|  | handled = 0; | 
|  | } | 
|  |  | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | /* receive an data packet (OUT) */ | 
|  | static void ep0_rx_state(struct mtu3 *mtu) | 
|  | { | 
|  | struct mtu3_request *mreq; | 
|  | struct usb_request *req; | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | u32 maxp; | 
|  | u32 csr; | 
|  | u16 count = 0; | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s\n", __func__); | 
|  |  | 
|  | csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; | 
|  | mreq = next_ep0_request(mtu); | 
|  | req = &mreq->request; | 
|  |  | 
|  | /* read packet and ack; or stall because of gadget driver bug */ | 
|  | if (req) { | 
|  | void *buf = req->buf + req->actual; | 
|  | unsigned int len = req->length - req->actual; | 
|  |  | 
|  | /* read the buffer */ | 
|  | count = mtu3_readl(mbase, U3D_RXCOUNT0); | 
|  | if (count > len) { | 
|  | req->status = -EOVERFLOW; | 
|  | count = len; | 
|  | } | 
|  | ep0_read_fifo(mtu->ep0, buf, count); | 
|  | req->actual += count; | 
|  | csr |= EP0_RXPKTRDY; | 
|  |  | 
|  | maxp = mtu->g.ep0->maxpacket; | 
|  | if (count < maxp || req->actual == req->length) { | 
|  | mtu->ep0_state = MU3D_EP0_STATE_SETUP; | 
|  | dev_dbg(mtu->dev, "ep0 state: %s\n", | 
|  | decode_ep0_state(mtu)); | 
|  |  | 
|  | csr |= EP0_DATAEND; | 
|  | } else { | 
|  | req = NULL; | 
|  | } | 
|  | } else { | 
|  | csr |= EP0_RXPKTRDY | EP0_SENDSTALL; | 
|  | dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__); | 
|  | } | 
|  |  | 
|  | mtu3_writel(mbase, U3D_EP0CSR, csr); | 
|  |  | 
|  | /* give back the request if have received all data */ | 
|  | if (req) | 
|  | ep0_req_giveback(mtu, req); | 
|  |  | 
|  | } | 
|  |  | 
|  | /* transmitting to the host (IN) */ | 
|  | static void ep0_tx_state(struct mtu3 *mtu) | 
|  | { | 
|  | struct mtu3_request *mreq = next_ep0_request(mtu); | 
|  | struct usb_request *req; | 
|  | u32 csr; | 
|  | u8 *src; | 
|  | u32 count; | 
|  | u32 maxp; | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s\n", __func__); | 
|  |  | 
|  | if (!mreq) | 
|  | return; | 
|  |  | 
|  | maxp = mtu->g.ep0->maxpacket; | 
|  | req = &mreq->request; | 
|  |  | 
|  | /* load the data */ | 
|  | src = (u8 *)req->buf + req->actual; | 
|  | count = min(maxp, req->length - req->actual); | 
|  | if (count) | 
|  | ep0_write_fifo(mtu->ep0, src, count); | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n", | 
|  | __func__, req->actual, req->length, count, maxp, req->zero); | 
|  |  | 
|  | req->actual += count; | 
|  |  | 
|  | if ((count < maxp) | 
|  | || ((req->actual == req->length) && !req->zero)) | 
|  | mtu->ep0_state = MU3D_EP0_STATE_TX_END; | 
|  |  | 
|  | /* send it out, triggering a "txpktrdy cleared" irq */ | 
|  | csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; | 
|  | mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY); | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__, | 
|  | mtu3_readl(mtu->mac_base, U3D_EP0CSR)); | 
|  | } | 
|  |  | 
|  | static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup) | 
|  | { | 
|  | struct mtu3_request *mreq; | 
|  | u32 count; | 
|  | u32 csr; | 
|  |  | 
|  | csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; | 
|  | count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0); | 
|  |  | 
|  | ep0_read_fifo(mtu->ep0, (u8 *)setup, count); | 
|  |  | 
|  | dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n", | 
|  | setup->bRequestType, setup->bRequest, | 
|  | le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex), | 
|  | le16_to_cpu(setup->wLength)); | 
|  |  | 
|  | /* clean up any leftover transfers */ | 
|  | mreq = next_ep0_request(mtu); | 
|  | if (mreq) | 
|  | ep0_req_giveback(mtu, &mreq->request); | 
|  |  | 
|  | if (le16_to_cpu(setup->wLength) == 0) { | 
|  | ;	/* no data stage, nothing to do */ | 
|  | } else if (setup->bRequestType & USB_DIR_IN) { | 
|  | mtu3_writel(mtu->mac_base, U3D_EP0CSR, | 
|  | csr | EP0_SETUPPKTRDY | EP0_DPHTX); | 
|  | mtu->ep0_state = MU3D_EP0_STATE_TX; | 
|  | } else { | 
|  | mtu3_writel(mtu->mac_base, U3D_EP0CSR, | 
|  | (csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX)); | 
|  | mtu->ep0_state = MU3D_EP0_STATE_RX; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ep0_handle_setup(struct mtu3 *mtu) | 
|  | __releases(mtu->lock) | 
|  | __acquires(mtu->lock) | 
|  | { | 
|  | struct usb_ctrlrequest setup; | 
|  | struct mtu3_request *mreq; | 
|  | int handled = 0; | 
|  |  | 
|  | ep0_read_setup(mtu, &setup); | 
|  | trace_mtu3_handle_setup(&setup); | 
|  |  | 
|  | if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) | 
|  | handled = handle_standard_request(mtu, &setup); | 
|  |  | 
|  | dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n", | 
|  | handled, decode_ep0_state(mtu)); | 
|  |  | 
|  | if (handled < 0) | 
|  | goto stall; | 
|  | else if (handled > 0) | 
|  | goto finish; | 
|  |  | 
|  | handled = forward_to_driver(mtu, &setup); | 
|  | if (handled < 0) { | 
|  | stall: | 
|  | dev_dbg(mtu->dev, "%s stall (%d)\n", __func__, handled); | 
|  |  | 
|  | ep0_stall_set(mtu->ep0, true, | 
|  | le16_to_cpu(setup.wLength) ? 0 : EP0_SETUPPKTRDY); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | finish: | 
|  | if (mtu->test_mode) { | 
|  | ;	/* nothing to do */ | 
|  | } else if (handled == USB_GADGET_DELAYED_STATUS) { | 
|  |  | 
|  | mreq = next_ep0_request(mtu); | 
|  | if (mreq) { | 
|  | /* already asked us to continue delayed status */ | 
|  | ep0_do_status_stage(mtu); | 
|  | ep0_req_giveback(mtu, &mreq->request); | 
|  | } else { | 
|  | /* do delayed STATUS stage till receive ep0_queue */ | 
|  | mtu->delayed_status = true; | 
|  | } | 
|  | } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ | 
|  |  | 
|  | ep0_do_status_stage(mtu); | 
|  | /* complete zlp request directly */ | 
|  | mreq = next_ep0_request(mtu); | 
|  | if (mreq && !mreq->request.length) | 
|  | ep0_req_giveback(mtu, &mreq->request); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu) | 
|  | { | 
|  | void __iomem *mbase = mtu->mac_base; | 
|  | struct mtu3_request *mreq; | 
|  | u32 int_status; | 
|  | irqreturn_t ret = IRQ_NONE; | 
|  | u32 csr; | 
|  | u32 len; | 
|  |  | 
|  | int_status = mtu3_readl(mbase, U3D_EPISR); | 
|  | int_status &= mtu3_readl(mbase, U3D_EPIER); | 
|  | mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */ | 
|  |  | 
|  | /* only handle ep0's */ | 
|  | if (!(int_status & (EP0ISR | SETUPENDISR))) | 
|  | return IRQ_NONE; | 
|  |  | 
|  | /* abort current SETUP, and process new one */ | 
|  | if (int_status & SETUPENDISR) | 
|  | mtu->ep0_state = MU3D_EP0_STATE_SETUP; | 
|  |  | 
|  | csr = mtu3_readl(mbase, U3D_EP0CSR); | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr); | 
|  |  | 
|  | /* we sent a stall.. need to clear it now.. */ | 
|  | if (csr & EP0_SENTSTALL) { | 
|  | ep0_stall_set(mtu->ep0, false, 0); | 
|  | csr = mtu3_readl(mbase, U3D_EP0CSR); | 
|  | ret = IRQ_HANDLED; | 
|  | } | 
|  | dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu)); | 
|  | mtu3_dbg_trace(mtu->dev, "ep0_state %s", decode_ep0_state(mtu)); | 
|  |  | 
|  | switch (mtu->ep0_state) { | 
|  | case MU3D_EP0_STATE_TX: | 
|  | /* irq on clearing txpktrdy */ | 
|  | if ((csr & EP0_FIFOFULL) == 0) { | 
|  | ep0_tx_state(mtu); | 
|  | ret = IRQ_HANDLED; | 
|  | } | 
|  | break; | 
|  | case MU3D_EP0_STATE_RX: | 
|  | /* irq on set rxpktrdy */ | 
|  | if (csr & EP0_RXPKTRDY) { | 
|  | ep0_rx_state(mtu); | 
|  | ret = IRQ_HANDLED; | 
|  | } | 
|  | break; | 
|  | case MU3D_EP0_STATE_TX_END: | 
|  | mtu3_writel(mbase, U3D_EP0CSR, | 
|  | (csr & EP0_W1C_BITS) | EP0_DATAEND); | 
|  |  | 
|  | mreq = next_ep0_request(mtu); | 
|  | if (mreq) | 
|  | ep0_req_giveback(mtu, &mreq->request); | 
|  |  | 
|  | mtu->ep0_state = MU3D_EP0_STATE_SETUP; | 
|  | ret = IRQ_HANDLED; | 
|  | dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu)); | 
|  | break; | 
|  | case MU3D_EP0_STATE_SETUP: | 
|  | if (!(csr & EP0_SETUPPKTRDY)) | 
|  | break; | 
|  |  | 
|  | len = mtu3_readl(mbase, U3D_RXCOUNT0); | 
|  | if (len != 8) { | 
|  | dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len); | 
|  | break; | 
|  | } | 
|  |  | 
|  | ep0_handle_setup(mtu); | 
|  | ret = IRQ_HANDLED; | 
|  | break; | 
|  | default: | 
|  | /* can't happen */ | 
|  | ep0_stall_set(mtu->ep0, true, 0); | 
|  | WARN_ON(1); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int mtu3_ep0_enable(struct usb_ep *ep, | 
|  | const struct usb_endpoint_descriptor *desc) | 
|  | { | 
|  | /* always enabled */ | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | static int mtu3_ep0_disable(struct usb_ep *ep) | 
|  | { | 
|  | /* always enabled */ | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq) | 
|  | { | 
|  | struct mtu3 *mtu = mep->mtu; | 
|  |  | 
|  | mreq->mtu = mtu; | 
|  | mreq->request.actual = 0; | 
|  | mreq->request.status = -EINPROGRESS; | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__, | 
|  | mep->name, decode_ep0_state(mtu), mreq->request.length); | 
|  |  | 
|  | switch (mtu->ep0_state) { | 
|  | case MU3D_EP0_STATE_SETUP: | 
|  | case MU3D_EP0_STATE_RX:	/* control-OUT data */ | 
|  | case MU3D_EP0_STATE_TX:	/* control-IN data */ | 
|  | break; | 
|  | default: | 
|  | dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__, | 
|  | decode_ep0_state(mtu)); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (mtu->delayed_status) { | 
|  |  | 
|  | mtu->delayed_status = false; | 
|  | ep0_do_status_stage(mtu); | 
|  | /* needn't giveback the request for handling delay STATUS */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (!list_empty(&mep->req_list)) | 
|  | return -EBUSY; | 
|  |  | 
|  | list_add_tail(&mreq->list, &mep->req_list); | 
|  |  | 
|  | /* sequence #1, IN ... start writing the data */ | 
|  | if (mtu->ep0_state == MU3D_EP0_STATE_TX) | 
|  | ep0_tx_state(mtu); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int mtu3_ep0_queue(struct usb_ep *ep, | 
|  | struct usb_request *req, gfp_t gfp) | 
|  | { | 
|  | struct mtu3_ep *mep; | 
|  | struct mtu3_request *mreq; | 
|  | struct mtu3 *mtu; | 
|  | unsigned long flags; | 
|  | int ret = 0; | 
|  |  | 
|  | if (!ep || !req) | 
|  | return -EINVAL; | 
|  |  | 
|  | mep = to_mtu3_ep(ep); | 
|  | mtu = mep->mtu; | 
|  | mreq = to_mtu3_request(req); | 
|  |  | 
|  | spin_lock_irqsave(&mtu->lock, flags); | 
|  | ret = ep0_queue(mep, mreq); | 
|  | spin_unlock_irqrestore(&mtu->lock, flags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req) | 
|  | { | 
|  | /* we just won't support this */ | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | static int mtu3_ep0_halt(struct usb_ep *ep, int value) | 
|  | { | 
|  | struct mtu3_ep *mep; | 
|  | struct mtu3 *mtu; | 
|  | unsigned long flags; | 
|  | int ret = 0; | 
|  |  | 
|  | if (!ep || !value) | 
|  | return -EINVAL; | 
|  |  | 
|  | mep = to_mtu3_ep(ep); | 
|  | mtu = mep->mtu; | 
|  |  | 
|  | dev_dbg(mtu->dev, "%s\n", __func__); | 
|  |  | 
|  | spin_lock_irqsave(&mtu->lock, flags); | 
|  |  | 
|  | if (!list_empty(&mep->req_list)) { | 
|  | ret = -EBUSY; | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | switch (mtu->ep0_state) { | 
|  | /* | 
|  | * stalls are usually issued after parsing SETUP packet, either | 
|  | * directly in irq context from setup() or else later. | 
|  | */ | 
|  | case MU3D_EP0_STATE_TX: | 
|  | case MU3D_EP0_STATE_TX_END: | 
|  | case MU3D_EP0_STATE_RX: | 
|  | case MU3D_EP0_STATE_SETUP: | 
|  | ep0_stall_set(mtu->ep0, true, 0); | 
|  | break; | 
|  | default: | 
|  | dev_dbg(mtu->dev, "ep0 can't halt in state %s\n", | 
|  | decode_ep0_state(mtu)); | 
|  | ret = -EINVAL; | 
|  | } | 
|  |  | 
|  | cleanup: | 
|  | spin_unlock_irqrestore(&mtu->lock, flags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | const struct usb_ep_ops mtu3_ep0_ops = { | 
|  | .enable = mtu3_ep0_enable, | 
|  | .disable = mtu3_ep0_disable, | 
|  | .alloc_request = mtu3_alloc_request, | 
|  | .free_request = mtu3_free_request, | 
|  | .queue = mtu3_ep0_queue, | 
|  | .dequeue = mtu3_ep0_dequeue, | 
|  | .set_halt = mtu3_ep0_halt, | 
|  | }; |