diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.c | 168 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.h | 6 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/mod_gadget.c | 137 |
3 files changed, 148 insertions, 163 deletions
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 088bfd787e4..b5031e3a156 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -27,20 +27,17 @@ void usbhs_pkt_init(struct usbhs_pkt *pkt) INIT_LIST_HEAD(&pkt->node); } -void usbhs_pkt_update(struct usbhs_pkt *pkt, void *buf, int len) -{ - pkt->buf = buf; - pkt->length = len; - pkt->actual = 0; - pkt->maxp = 0; -} - -void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) +void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, + void *buf, int len, int zero) { list_del_init(&pkt->node); list_add_tail(&pkt->node, &pipe->list); - pkt->pipe = pipe; + pkt->pipe = pipe; + pkt->buf = buf; + pkt->length = len; + pkt->zero = zero; + pkt->actual = 0; } void usbhs_pkt_pop(struct usbhs_pkt *pkt) @@ -57,6 +54,47 @@ struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe) } /* + * irq enable/disable function + */ +#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, bempsts, e) +#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, brdysts, e) +#define usbhsf_irq_callback_ctrl(pipe, status, enable) \ + ({ \ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); \ + struct usbhs_mod *mod = usbhs_mod_get_current(priv); \ + u16 status = (1 << usbhs_pipe_number(pipe)); \ + if (!mod) \ + return; \ + if (enable) \ + mod->irq_##status |= status; \ + else \ + mod->irq_##status &= ~status; \ + usbhs_irq_callback_update(priv, mod); \ + }) + +static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable) +{ + /* + * And DCP pipe can NOT use "ready interrupt" for "send" + * it should use "empty" interrupt. + * see + * "Operation" - "Interrupt Function" - "BRDY Interrupt" + * + * on the other hand, normal pipe can use "ready interrupt" for "send" + * even though it is single/double buffer + */ + if (usbhs_pipe_is_dcp(pipe)) + usbhsf_irq_empty_ctrl(pipe, enable); + else + usbhsf_irq_ready_ctrl(pipe, enable); +} + +static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable) +{ + usbhsf_irq_ready_ctrl(pipe, enable); +} + +/* * FIFO ctrl */ static void usbhsf_send_terminator(struct usbhs_pipe *pipe) @@ -135,34 +173,38 @@ int usbhs_fifo_write(struct usbhs_pkt *pkt) struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); + struct device *dev = usbhs_priv_to_dev(priv); void __iomem *addr = priv->base + CFIFO; + u8 *buf; int maxp = usbhs_pipe_get_maxpacket(pipe); int total_len; - u8 *buf = pkt->buf; int i, ret, len; + int is_short, is_done; ret = usbhs_pipe_is_accessible(pipe); if (ret < 0) - return ret; + goto usbhs_fifo_write_busy; ret = usbhsf_fifo_select(pipe, 1); if (ret < 0) - return ret; + goto usbhs_fifo_write_busy; ret = usbhsf_fifo_barrier(priv); if (ret < 0) - return ret; + goto usbhs_fifo_write_busy; - len = min(pkt->length, maxp); - total_len = len; + buf = pkt->buf + pkt->actual; + len = pkt->length - pkt->actual; + len = min(len, maxp); + total_len = len; + is_short = total_len < maxp; /* * FIXME * * 32-bit access only */ - if (len >= 4 && - !((unsigned long)buf & 0x03)) { + if (len >= 4 && !((unsigned long)buf & 0x03)) { iowrite32_rep(addr, buf, len / 4); len %= 4; buf += total_len - len; @@ -172,19 +214,52 @@ int usbhs_fifo_write(struct usbhs_pkt *pkt) for (i = 0; i < len; i++) iowrite8(buf[i], addr + (0x03 - (i & 0x03))); - if (total_len < maxp) + /* + * variable update + */ + pkt->actual += total_len; + + if (pkt->actual < pkt->length) + is_done = 0; /* there are remainder data */ + else if (is_short) + is_done = 1; /* short packet */ + else + is_done = !pkt->zero; /* send zero packet ? */ + + /* + * pipe/irq handling + */ + if (is_short) usbhsf_send_terminator(pipe); + usbhsf_tx_irq_ctrl(pipe, !is_done); usbhs_pipe_enable(pipe); - /* update pkt */ - if (info->tx_done) { - pkt->actual = total_len; - pkt->maxp = maxp; - info->tx_done(pkt); + dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", + usbhs_pipe_number(pipe), + pkt->length, pkt->actual, is_done, pkt->zero); + + /* + * Transmission end + */ + if (is_done) { + if (usbhs_pipe_is_dcp(pipe)) + usbhs_dcp_control_transfer_done(pipe); + + if (info->tx_done) + info->tx_done(pkt); } return 0; + +usbhs_fifo_write_busy: + /* + * pipe is busy. + * retry in interrupt + */ + usbhsf_tx_irq_ctrl(pipe, 1); + + return ret; } int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe) @@ -199,6 +274,7 @@ int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe) return ret; usbhs_pipe_enable(pipe); + usbhsf_rx_irq_ctrl(pipe, 1); return ret; } @@ -207,13 +283,15 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); + struct device *dev = usbhs_priv_to_dev(priv); void __iomem *addr = priv->base + CFIFO; - u8 *buf = pkt->buf; + u8 *buf; + u32 data = 0; + int maxp = usbhs_pipe_get_maxpacket(pipe); int rcv_len, len; int i, ret; int total_len = 0; - u32 data = 0; + int is_done = 0; ret = usbhsf_fifo_select(pipe, 0); if (ret < 0) @@ -225,6 +303,11 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) rcv_len = usbhsf_fifo_rcv_len(priv); + buf = pkt->buf + pkt->actual; + len = pkt->length - pkt->actual; + len = min(len, rcv_len); + total_len = len; + /* * Buffer clear if Zero-Length packet * @@ -236,19 +319,15 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) goto usbhs_fifo_read_end; } - len = min(rcv_len, pkt->length); - total_len = len; - /* * FIXME * * 32-bit access only */ - if (len >= 4 && - !((unsigned long)buf & 0x03)) { + if (len >= 4 && !((unsigned long)buf & 0x03)) { ioread32_rep(addr, buf, len / 4); len %= 4; - buf += rcv_len - len; + buf += total_len - len; } /* the rest operation */ @@ -259,12 +338,25 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; } + pkt->actual += total_len; + usbhs_fifo_read_end: - if (info->rx_done) { - /* update pkt */ - pkt->actual = total_len; - pkt->maxp = usbhs_pipe_get_maxpacket(pipe); - info->rx_done(pkt); + if ((pkt->actual == pkt->length) || /* receive all data */ + (total_len < maxp)) /* short packet */ + is_done = 1; + + dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", + usbhs_pipe_number(pipe), + pkt->length, pkt->actual, is_done, pkt->zero); + + if (is_done) { + struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); + + usbhsf_rx_irq_ctrl(pipe, 0); + usbhs_pipe_disable(pipe); + + if (info->rx_done) + info->rx_done(pkt); } return 0; diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index c34d1d111a2..04d8cddaf81 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -22,10 +22,10 @@ struct usbhs_pkt { struct list_head node; struct usbhs_pipe *pipe; - int maxp; void *buf; int length; int actual; + int zero; }; /* @@ -40,8 +40,8 @@ int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe); * packet info */ void usbhs_pkt_init(struct usbhs_pkt *pkt); -void usbhs_pkt_update(struct usbhs_pkt *pkt, void *buf, int len); -void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); +void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, + void *buf, int len, int zero); void usbhs_pkt_pop(struct usbhs_pkt *pkt); struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index a3818773dec..298984f533d 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -60,7 +60,6 @@ struct usbhsg_gpriv { struct usbhsg_pipe_handle { int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq); int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq); - void (*irq_mask)(struct usbhsg_uep *uep, int enable); }; struct usbhsg_recip_handle { @@ -160,17 +159,18 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, struct device *dev = usbhsg_gpriv_to_dev(gpriv); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); + struct usb_request *req = &ureq->req; /* ********* assume under spin lock ********* */ - usbhs_pkt_push(pipe, pkt); - ureq->req.actual = 0; - ureq->req.status = -EINPROGRESS; + usbhs_pkt_push(pipe, pkt, req->buf, req->length, req->zero); + req->actual = 0; + req->status = -EINPROGRESS; dev_dbg(dev, "pipe %d : queue push (%d)\n", usbhs_pipe_number(pipe), - ureq->req.length); + req->length); } static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep) @@ -329,83 +329,27 @@ static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep, /* * packet send hander */ -static void usbhsg_try_run_send_packet_bh(struct usbhs_pkt *pkt) +static void usbhsg_send_packet_done(struct usbhs_pkt *pkt) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); - struct usb_request *req = &ureq->req; - struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - struct device *dev = usbhsg_gpriv_to_dev(gpriv); - int remainder, send, maxp; - int is_done = 0; - int enable; - maxp = pkt->maxp; - send = pkt->actual; - remainder = pkt->length; + ureq->req.actual = pkt->actual; - /* - * send = 0 : send zero packet - * send > 0 : send data - * - * send <= max_packet - */ - req->actual += send; - - /* send all packet ? */ - if (send < remainder) - is_done = 0; /* there are remainder data */ - else if (send < maxp) - is_done = 1; /* short packet */ - else - is_done = !req->zero; /* send zero packet ? */ - - dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", - usbhs_pipe_number(pipe), - remainder, send, is_done, req->zero); - - /* - * enable interrupt and send again in irq handler - * if it still have remainder data which should be sent. - */ - enable = !is_done; - uep->handler->irq_mask(uep, enable); - - /* - * all data were sent ? - */ - if (is_done) { - /* it care below call in - "function mode" */ - if (usbhsg_is_dcp(uep)) - usbhs_dcp_control_transfer_done(pipe); - - usbhsg_queue_pop(uep, ureq, 0); - } + usbhsg_queue_pop(uep, ureq, 0); } static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep, struct usbhsg_request *ureq) { - struct usb_request *req = &ureq->req; struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); - int ret; /* ********* assume under spin lock ********* */ - usbhs_pkt_update(pkt, - req->buf + req->actual, - req->length - req->actual); - - ret = usbhs_fifo_write(pkt); - if (ret < 0) { - /* pipe is busy. - * retry in interrupt */ - uep->handler->irq_mask(uep, 1); - } + usbhs_fifo_write(pkt); return 0; } @@ -428,63 +372,26 @@ static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep, /* * packet recv hander */ -static void usbhsg_try_run_receive_packet_bh(struct usbhs_pkt *pkt) +static void usbhsg_receive_packet_done(struct usbhs_pkt *pkt) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); - struct usb_request *req = &ureq->req; - struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - struct device *dev = usbhsg_gpriv_to_dev(gpriv); - int remainder, recv, maxp; - int is_done = 0; - - maxp = pkt->maxp; - remainder = pkt->length; - recv = pkt->actual; - - /* - * recv < 0 : pipe busy - * recv >= 0 : receive data - * - * recv <= max_packet - */ - - /* update parameters */ - req->actual += recv; - - if ((recv == remainder) || /* receive all data */ - (recv < maxp)) /* short packet */ - is_done = 1; - dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", - usbhs_pipe_number(pipe), - remainder, recv, is_done, req->zero); - - /* read all data ? */ - if (is_done) { - int disable = 0; + ureq->req.actual = pkt->actual; - uep->handler->irq_mask(uep, disable); - usbhs_pipe_disable(pipe); - usbhsg_queue_pop(uep, ureq, 0); - } + usbhsg_queue_pop(uep, ureq, 0); } static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep, struct usbhsg_request *ureq) { - struct usb_request *req = &ureq->req; struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); /* ********* assume under spin lock ********* */ - usbhs_pkt_update(pkt, - req->buf + req->actual, - req->length - req->actual); - return usbhs_fifo_read(pkt); } @@ -492,41 +399,27 @@ static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep, struct usbhsg_request *ureq) { struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); - int enable = 1; - int ret; /* ********* assume under spin lock ********* */ - ret = usbhs_fifo_prepare_read(pipe); - if (ret < 0) - return ret; - - /* - * data will be read in interrupt handler - */ - uep->handler->irq_mask(uep, enable); - - return ret; + return usbhs_fifo_prepare_read(pipe); } static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = { .prepare = usbhsg_prepare_send_packet, .try_run = usbhsg_try_run_send_packet, - .irq_mask = usbhsg_irq_empty_ctrl, }; static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = { .prepare = usbhsg_prepare_send_packet, .try_run = usbhsg_try_run_send_packet, - .irq_mask = usbhsg_irq_ready_ctrl, }; static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = { .prepare = usbhsg_prepare_receive_packet, .try_run = usbhsg_try_run_receive_packet, - .irq_mask = usbhsg_irq_ready_ctrl, }; static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = { @@ -1113,8 +1006,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * pipe initialize and enable DCP */ usbhs_pipe_init(priv, - usbhsg_try_run_send_packet_bh, - usbhsg_try_run_receive_packet_bh); + usbhsg_send_packet_done, + usbhsg_receive_packet_done); usbhsg_uep_init(gpriv); usbhsg_dcp_enable(dcp); |