Commit 1c90ee0b authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Felipe Balbi

usb: renesas_usbhs: use transfer counter if IN direction bulk pipe

received data will break if it was bulk pipe and large data size,
because pipe kept BUF PID even though it doesn't have enough buffer.
To avoid this issue, renesas_usbhs can use transfer counter.
Pipe PID will be NAK if it didn't have enough buffer by this patch.

renesas_usbhs has strange address mapping.
Thus, it is difficult to calculate transfer counter setting address.
This patch use fixed table for it.
Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 7b332e5f
...@@ -488,6 +488,8 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) ...@@ -488,6 +488,8 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_data_sequence(pipe, pkt->sequence); usbhs_pipe_data_sequence(pipe, pkt->sequence);
pkt->sequence = -1; /* -1 sequence will be ignored */ pkt->sequence = -1; /* -1 sequence will be ignored */
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
ret = usbhsf_fifo_select(pipe, fifo, 1); ret = usbhsf_fifo_select(pipe, fifo, 1);
if (ret < 0) if (ret < 0)
return 0; return 0;
...@@ -594,6 +596,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) ...@@ -594,6 +596,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_data_sequence(pipe, pkt->sequence); usbhs_pipe_data_sequence(pipe, pkt->sequence);
pkt->sequence = -1; /* -1 sequence will be ignored */ pkt->sequence = -1; /* -1 sequence will be ignored */
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
usbhs_pipe_enable(pipe); usbhs_pipe_enable(pipe);
usbhsf_rx_irq_ctrl(pipe, 1); usbhsf_rx_irq_ctrl(pipe, 1);
...@@ -795,6 +798,7 @@ static void xfer_work(struct work_struct *work) ...@@ -795,6 +798,7 @@ static void xfer_work(struct work_struct *work)
dev_dbg(dev, " %s %d (%d/ %d)\n", dev_dbg(dev, " %s %d (%d/ %d)\n",
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
usbhsf_dma_start(pipe, fifo); usbhsf_dma_start(pipe, fifo);
dma_async_issue_pending(chan); dma_async_issue_pending(chan);
} }
......
...@@ -92,6 +92,82 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val) ...@@ -92,6 +92,82 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val); __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
} }
/*
* PIPEnTRN/PIPEnTRE functions
*/
static void usbhsp_pipe_trn_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct device *dev = usbhs_priv_to_dev(priv);
int num = usbhs_pipe_number(pipe);
u16 reg;
/*
* It is impossible to calculate address,
* since PIPEnTRN addresses were mapped randomly.
*/
#define CASE_PIPExTRN(a) \
case 0x ## a: \
reg = PIPE ## a ## TRN; \
break;
switch (num) {
CASE_PIPExTRN(1);
CASE_PIPExTRN(2);
CASE_PIPExTRN(3);
CASE_PIPExTRN(4);
CASE_PIPExTRN(5);
CASE_PIPExTRN(B);
CASE_PIPExTRN(C);
CASE_PIPExTRN(D);
CASE_PIPExTRN(E);
CASE_PIPExTRN(F);
CASE_PIPExTRN(9);
CASE_PIPExTRN(A);
default:
dev_err(dev, "unknown pipe (%d)\n", num);
return;
}
__usbhsp_pipe_xxx_set(pipe, 0, reg, mask, val);
}
static void usbhsp_pipe_tre_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct device *dev = usbhs_priv_to_dev(priv);
int num = usbhs_pipe_number(pipe);
u16 reg;
/*
* It is impossible to calculate address,
* since PIPEnTRE addresses were mapped randomly.
*/
#define CASE_PIPExTRE(a) \
case 0x ## a: \
reg = PIPE ## a ## TRE; \
break;
switch (num) {
CASE_PIPExTRE(1);
CASE_PIPExTRE(2);
CASE_PIPExTRE(3);
CASE_PIPExTRE(4);
CASE_PIPExTRE(5);
CASE_PIPExTRE(B);
CASE_PIPExTRE(C);
CASE_PIPExTRE(D);
CASE_PIPExTRE(E);
CASE_PIPExTRE(F);
CASE_PIPExTRE(9);
CASE_PIPExTRE(A);
default:
dev_err(dev, "unknown pipe (%d)\n", num);
return;
}
__usbhsp_pipe_xxx_set(pipe, 0, reg, mask, val);
}
/* /*
* PIPEBUF * PIPEBUF
*/ */
...@@ -264,6 +340,31 @@ int usbhs_pipe_is_stall(struct usbhs_pipe *pipe) ...@@ -264,6 +340,31 @@ int usbhs_pipe_is_stall(struct usbhs_pipe *pipe)
return (int)(pid == PID_STALL10 || pid == PID_STALL11); return (int)(pid == PID_STALL10 || pid == PID_STALL11);
} }
void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len)
{
if (!usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
return;
/*
* clear and disable transfer counter for IN/OUT pipe
*/
usbhsp_pipe_tre_set(pipe, TRCLR | TRENB, TRCLR);
/*
* Only IN direction bulk pipe can use transfer count.
* Without using this function,
* received data will break if it was large data size.
* see PIPEnTRN/PIPEnTRE for detail
*/
if (usbhs_pipe_is_dir_in(pipe)) {
int maxp = usbhs_pipe_get_maxpacket(pipe);
usbhsp_pipe_trn_set(pipe, 0xffff, DIV_ROUND_UP(len, maxp));
usbhsp_pipe_tre_set(pipe, TRENB, TRENB); /* enable */
}
}
/* /*
* pipe setup * pipe setup
*/ */
......
...@@ -88,6 +88,7 @@ void usbhs_pipe_enable(struct usbhs_pipe *pipe); ...@@ -88,6 +88,7 @@ void usbhs_pipe_enable(struct usbhs_pipe *pipe);
void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe);
void usbhs_pipe_stall(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe);
int usbhs_pipe_is_stall(struct usbhs_pipe *pipe); int usbhs_pipe_is_stall(struct usbhs_pipe *pipe);
void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len);
void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
u16 epnum, u16 maxp); u16 epnum, u16 maxp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment