u-boot-brain/drivers/usb/mtu3/mtu3_gadget_ep0.c
Chunfeng Yun e09b88cd08 usb: add MediaTek USB3 DRD driver
This patch adds support for the MediaTek USB3 DRD controller,
its host side is based on xHCI, this driver supports device mode
and host mode.

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Acked-by: Bin Meng <bmeng.cn@gmail.com>
2020-10-20 00:49:05 +02:00

934 lines
21 KiB
C

// 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"
/* 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 inline void writel_rep(volatile void *addr, const void *buffer,
unsigned int count)
{
if (count) {
const u32 *buf = buffer;
do {
writel(*buf++, addr);
} while (--count);
}
}
static inline void readl_rep(const volatile void *addr, void *buffer,
unsigned int count)
{
if (count) {
u32 *buf = buffer;
do {
u32 x = readl(addr);
*buf++ = x;
} while (--count);
}
}
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) {
writel_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) {
readl_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 = 0;
switch (le16_to_cpu(setup->wIndex) >> 8) {
case TEST_J:
dev_dbg(mtu->dev, "TEST_J\n");
mtu->test_mode_nr = TEST_J_MODE;
break;
case TEST_K:
dev_dbg(mtu->dev, "TEST_K\n");
mtu->test_mode_nr = TEST_K_MODE;
break;
case TEST_SE0_NAK:
dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
mtu->test_mode_nr = TEST_SE0_NAK_MODE;
break;
case TEST_PACKET:
dev_dbg(mtu->dev, "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(mbase + U3D_EP0CSR, value,
!(value & EP0_DATAEND), 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);
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));
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,
};