diff options
Diffstat (limited to 'drivers/usb/renesas_usbhs/fifo.c')
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.c | 168 |
1 files changed, 130 insertions, 38 deletions
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 088bfd787e4a..b5031e3a1569 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; |