summaryrefslogtreecommitdiff
path: root/drivers/usb/cdns3/cdnsp-ring.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/cdns3/cdnsp-ring.c')
-rw-r--r--drivers/usb/cdns3/cdnsp-ring.c183
1 files changed, 103 insertions, 80 deletions
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
index b23e543b3a3d..0758f171f73e 100644
--- a/drivers/usb/cdns3/cdnsp-ring.c
+++ b/drivers/usb/cdns3/cdnsp-ring.c
@@ -308,7 +308,8 @@ static bool cdnsp_ring_ep_doorbell(struct cdnsp_device *pdev,
writel(db_value, reg_addr);
- cdnsp_force_l0_go(pdev);
+ if (pdev->rtl_revision < RTL_REVISION_NEW_LPM)
+ cdnsp_force_l0_go(pdev);
/* Doorbell was set. */
return true;
@@ -402,7 +403,7 @@ static u64 cdnsp_get_hw_deq(struct cdnsp_device *pdev,
struct cdnsp_stream_ctx *st_ctx;
struct cdnsp_ep *pep;
- pep = &pdev->eps[stream_id];
+ pep = &pdev->eps[ep_index];
if (pep->ep_state & EP_HAS_STREAMS) {
st_ctx = &pep->stream_info.stream_ctx_array[stream_id];
@@ -718,7 +719,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
seg = cdnsp_trb_in_td(pdev, cur_td->start_seg, cur_td->first_trb,
cur_td->last_trb, hw_deq);
- if (seg && (pep->ep_state & EP_ENABLED))
+ if (seg && (pep->ep_state & EP_ENABLED) &&
+ !(pep->ep_state & EP_DIS_IN_RROGRESS))
cdnsp_find_new_dequeue_state(pdev, pep, preq->request.stream_id,
cur_td, &deq_state);
else
@@ -736,7 +738,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
* During disconnecting all endpoint will be disabled so we don't
* have to worry about updating dequeue pointer.
*/
- if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING) {
+ if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING ||
+ pep->ep_state & EP_DIS_IN_RROGRESS) {
status = -ESHUTDOWN;
ret = cdnsp_cmd_set_deq(pdev, pep, &deq_state);
}
@@ -769,7 +772,9 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id)
}
if (port_id != old_port) {
- cdnsp_disable_slot(pdev);
+ if (pdev->slot_id)
+ cdnsp_disable_slot(pdev);
+
pdev->active_port = port;
cdnsp_enable_slot(pdev);
}
@@ -1333,6 +1338,20 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
ep_ring->dequeue, td->last_trb,
ep_trb_dma);
+ desc = td->preq->pep->endpoint.desc;
+
+ if (ep_seg) {
+ ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma)
+ / sizeof(*ep_trb)];
+
+ trace_cdnsp_handle_transfer(ep_ring,
+ (struct cdnsp_generic_trb *)ep_trb);
+
+ if (pep->skip && usb_endpoint_xfer_isoc(desc) &&
+ td->last_trb != ep_trb)
+ return -EAGAIN;
+ }
+
/*
* Skip the Force Stopped Event. The event_trb(ep_trb_dma)
* of FSE is not in the current TD pointed by ep_ring->dequeue
@@ -1347,7 +1366,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
goto cleanup;
}
- desc = td->preq->pep->endpoint.desc;
if (!ep_seg) {
if (!pep->skip || !usb_endpoint_xfer_isoc(desc)) {
/* Something is busted, give up! */
@@ -1374,12 +1392,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
goto cleanup;
}
- ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma)
- / sizeof(*ep_trb)];
-
- trace_cdnsp_handle_transfer(ep_ring,
- (struct cdnsp_generic_trb *)ep_trb);
-
if (cdnsp_trb_is_noop(ep_trb))
goto cleanup;
@@ -1522,6 +1534,7 @@ irqreturn_t cdnsp_thread_irq_handler(int irq, void *data)
unsigned long flags;
int counter = 0;
+ local_bh_disable();
spin_lock_irqsave(&pdev->lock, flags);
if (pdev->cdnsp_state & (CDNSP_STATE_HALTED | CDNSP_STATE_DYING)) {
@@ -1534,6 +1547,7 @@ irqreturn_t cdnsp_thread_irq_handler(int irq, void *data)
cdnsp_died(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
+ local_bh_enable();
return IRQ_HANDLED;
}
@@ -1550,6 +1564,7 @@ irqreturn_t cdnsp_thread_irq_handler(int irq, void *data)
cdnsp_update_erst_dequeue(pdev, event_ring_deq, 1);
spin_unlock_irqrestore(&pdev->lock, flags);
+ local_bh_enable();
return IRQ_HANDLED;
}
@@ -1726,11 +1741,6 @@ static unsigned int count_sg_trbs_needed(struct cdnsp_request *preq)
return num_trbs;
}
-static unsigned int count_isoc_trbs_needed(struct cdnsp_request *preq)
-{
- return cdnsp_count_trbs(preq->request.dma, preq->request.length);
-}
-
static void cdnsp_check_trb_math(struct cdnsp_request *preq, int running_total)
{
if (running_total != preq->request.length)
@@ -1900,6 +1910,23 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
return ret;
/*
+ * workaround 1: STOP EP command on LINK TRB with TC bit set to 1
+ * causes that internal cycle bit can have incorrect state after
+ * command complete. In consequence empty transfer ring can be
+ * incorrectly detected when EP is resumed.
+ * NOP TRB before LINK TRB avoid such scenario. STOP EP command is
+ * then on NOP TRB and internal cycle bit is not changed and have
+ * correct value.
+ */
+ if (pep->wa1_nop_trb) {
+ field = le32_to_cpu(pep->wa1_nop_trb->trans_event.flags);
+ field ^= TRB_CYCLE;
+
+ pep->wa1_nop_trb->trans_event.flags = cpu_to_le32(field);
+ pep->wa1_nop_trb = NULL;
+ }
+
+ /*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
* state may change as we enqueue the other TRBs, so save it too.
@@ -1994,6 +2021,17 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
send_addr = addr;
}
+ if (cdnsp_trb_is_link(ring->enqueue + 1)) {
+ field = TRB_TYPE(TRB_TR_NOOP) | TRB_IOC;
+ if (!ring->cycle_state)
+ field |= TRB_CYCLE;
+
+ pep->wa1_nop_trb = ring->enqueue;
+
+ cdnsp_queue_trb(pdev, ring, 0, 0x0, 0x0,
+ TRB_INTR_TARGET(0), field);
+ }
+
cdnsp_check_trb_math(preq, enqd_len);
ret = cdnsp_giveback_first_trb(pdev, pep, preq->request.stream_id,
start_cycle, start_trb);
@@ -2121,19 +2159,6 @@ ep_stopped:
return ret;
}
-int cdnsp_cmd_flush_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep)
-{
- int ret;
-
- cdnsp_queue_flush_endpoint(pdev, pep->idx);
- cdnsp_ring_cmd_db(pdev);
- ret = cdnsp_wait_for_cmd_compl(pdev);
-
- trace_cdnsp_handle_cmd_flush_ep(pep->out_ctx);
-
- return ret;
-}
-
/*
* The transfer burst count field of the isochronous TRB defines the number of
* bursts that are required to move all packets in this TD. Only SuperSpeed
@@ -2192,28 +2217,48 @@ static unsigned int
}
/* Queue function isoc transfer */
-static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
- struct cdnsp_request *preq)
+int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
+ struct cdnsp_request *preq)
{
- int trb_buff_len, td_len, td_remain_len, ret;
+ unsigned int trb_buff_len, td_len, td_remain_len, block_len;
unsigned int burst_count, last_burst_pkt;
unsigned int total_pkt_count, max_pkt;
struct cdnsp_generic_trb *start_trb;
+ struct scatterlist *sg = NULL;
bool more_trbs_coming = true;
struct cdnsp_ring *ep_ring;
+ unsigned int num_sgs = 0;
int running_total = 0;
u32 field, length_field;
+ u64 addr, send_addr;
int start_cycle;
int trbs_per_td;
- u64 addr;
- int i;
+ int i, sent_len, ret;
ep_ring = preq->pep->ring;
+
+ td_len = preq->request.length;
+
+ if (preq->request.num_sgs) {
+ num_sgs = preq->request.num_sgs;
+ sg = preq->request.sg;
+ addr = (u64)sg_dma_address(sg);
+ block_len = sg_dma_len(sg);
+ trbs_per_td = count_sg_trbs_needed(preq);
+ } else {
+ addr = (u64)preq->request.dma;
+ block_len = td_len;
+ trbs_per_td = count_trbs_needed(preq);
+ }
+
+ ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td);
+ if (ret)
+ return ret;
+
start_trb = &ep_ring->enqueue->generic;
start_cycle = ep_ring->cycle_state;
- td_len = preq->request.length;
- addr = (u64)preq->request.dma;
td_remain_len = td_len;
+ send_addr = addr;
max_pkt = usb_endpoint_maxp(preq->pep->endpoint.desc);
total_pkt_count = DIV_ROUND_UP(td_len, max_pkt);
@@ -2225,11 +2270,6 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
burst_count = cdnsp_get_burst_count(pdev, preq, total_pkt_count);
last_burst_pkt = cdnsp_get_last_burst_packet_count(pdev, preq,
total_pkt_count);
- trbs_per_td = count_isoc_trbs_needed(preq);
-
- ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td);
- if (ret)
- goto cleanup;
/*
* Set isoc specific data for the first TRB in a TD.
@@ -2248,6 +2288,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
/* Calculate TRB length. */
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
+ trb_buff_len = min(trb_buff_len, block_len);
if (trb_buff_len > td_remain_len)
trb_buff_len = td_remain_len;
@@ -2256,7 +2297,8 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
trb_buff_len, td_len, preq,
more_trbs_coming, 0);
- length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
+ length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) |
+ TRB_INTR_TARGET(0);
/* Only first TRB is isoc, overwrite otherwise. */
if (i) {
@@ -2281,12 +2323,27 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
}
cdnsp_queue_trb(pdev, ep_ring, more_trbs_coming,
- lower_32_bits(addr), upper_32_bits(addr),
+ lower_32_bits(send_addr), upper_32_bits(send_addr),
length_field, field);
running_total += trb_buff_len;
addr += trb_buff_len;
td_remain_len -= trb_buff_len;
+
+ sent_len = trb_buff_len;
+ while (sg && sent_len >= block_len) {
+ /* New sg entry */
+ --num_sgs;
+ sent_len -= block_len;
+ if (num_sgs != 0) {
+ sg = sg_next(sg);
+ block_len = sg_dma_len(sg);
+ addr = (u64)sg_dma_address(sg);
+ addr += sent_len;
+ }
+ }
+ block_len -= sent_len;
+ send_addr = addr;
}
/* Check TD length */
@@ -2324,30 +2381,6 @@ cleanup:
return ret;
}
-int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev,
- struct cdnsp_request *preq)
-{
- struct cdnsp_ring *ep_ring;
- u32 ep_state;
- int num_trbs;
- int ret;
-
- ep_ring = preq->pep->ring;
- ep_state = GET_EP_CTX_STATE(preq->pep->out_ctx);
- num_trbs = count_isoc_trbs_needed(preq);
-
- /*
- * Check the ring to guarantee there is enough room for the whole
- * request. Do not insert any td of the USB Request to the ring if the
- * check failed.
- */
- ret = cdnsp_prepare_ring(pdev, ep_ring, ep_state, num_trbs, GFP_ATOMIC);
- if (ret)
- return ret;
-
- return cdnsp_queue_isoc_tx(pdev, preq);
-}
-
/**** Command Ring Operations ****/
/*
* Generic function for queuing a command TRB on the command ring.
@@ -2452,18 +2485,8 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index)
{
cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_HALT_ENDPOINT) |
SLOT_ID_FOR_TRB(pdev->slot_id) |
- EP_ID_FOR_TRB(ep_index));
-}
-
-/*
- * Queue a flush endpoint request on the command ring.
- */
-void cdnsp_queue_flush_endpoint(struct cdnsp_device *pdev,
- unsigned int ep_index)
-{
- cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_FLUSH_ENDPOINT) |
- SLOT_ID_FOR_TRB(pdev->slot_id) |
- EP_ID_FOR_TRB(ep_index));
+ EP_ID_FOR_TRB(ep_index) |
+ (!ep_index ? TRB_ESP : 0));
}
void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num)