This patch defines a set of functions used to send commands to command ring. Commands added to Command Ring are handled sequentially by hardware. After completion, controller should add appropriate event to Event Ring. Controller specification said that command may not be completed. For this reason, driver should start watchdog timer before arming Command Ring. Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx> --- drivers/usb/usbssp/gadget-ring.c | 535 ++++++++++++++++++++++++++++++- drivers/usb/usbssp/gadget.h | 4 + 2 files changed, 538 insertions(+), 1 deletion(-) diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c index c3612f4bc2a9..e2fb81259ca1 100644 --- a/drivers/usb/usbssp/gadget-ring.c +++ b/drivers/usb/usbssp/gadget-ring.c @@ -10,10 +10,55 @@ * Origin: Copyright (C) 2008 Intel Corp */ +/* + * Ring initialization rules: + * 1. Each segment is initialized to zero, except for link TRBs. + * 2. Ring cycle state = 0. This represents Producer Cycle State (PCS) or + * Consumer Cycle State (CCS), depending on ring function. + * 3. Enqueue pointer = dequeue pointer = address of first TRB in the segment. + * + * Ring behavior rules: + * 1. A ring is empty if enqueue == dequeue. This means there will always be at + * least one free TRB in the ring. This is useful if you want to turn that + * into a link TRB and expand the ring. + * 2. When incrementing an enqueue or dequeue pointer, if the next TRB is a + * link TRB, then load the pointer with the address in the link TRB. If the + * link TRB had its toggle bit set, you may need to update the ring cycle + * state (see cycle bit rules). You may have to do this multiple times + * until you reach a non-link TRB. + * 3. A ring is full if enqueue++ (for the definition of increment above) + * equals the dequeue pointer. + * + * Cycle bit rules: + * 1. When a consumer increments a dequeue pointer and encounters a toggle bit + * in a link TRB, it must toggle the ring cycle state. + * 2. When a producer increments an enqueue pointer and encounters a toggle bit + * in a link TRB, it must toggle the ring cycle state. + * + * Producer rules: + * 1. Check if ring is full before you enqueue. + * 2. Write the ring cycle state to the cycle bit in the TRB you're enqueuing. + * Update enqueue pointer between each write (which may update the ring + * cycle state). + * 3. Notify consumer. If SW is producer, it rings the doorbell for command + * and endpoint rings. If DC is the producer for the event ring, + * and it generates an interrupt according to interrupt modulation rules. + * + * Consumer rules: + * 1. Check if TRB belongs to you. If the cycle bit == your ring cycle state, + * the TRB is owned by the consumer. + * 2. Update dequeue pointer (which may update the ring cycle state) and + * continue processing TRBs until you reach a TRB which is not owned by you. + * 3. Notify the producer. SW is the consumer for the event ring, and it + * updates event ring dequeue pointer. DC is the consumer for the command and + * endpoint rings; it generates events on the event ring for these. + */ + #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/irq.h> + #include "gadget-trace.h" #include "gadget.h" @@ -35,6 +80,145 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg, return seg->dma + (segment_offset * sizeof(*trb)); } +static bool trb_is_link(union usbssp_trb *trb) +{ + return TRB_TYPE_LINK_LE32(trb->link.control); +} + +static bool last_trb_on_seg(struct usbssp_segment *seg, union usbssp_trb *trb) +{ + return trb == &seg->trbs[TRBS_PER_SEGMENT - 1]; +} + +static bool last_trb_on_ring(struct usbssp_ring *ring, + struct usbssp_segment *seg, + union usbssp_trb *trb) +{ + return last_trb_on_seg(seg, trb) && (seg->next == ring->first_seg); +} + +static bool link_trb_toggles_cycle(union usbssp_trb *trb) +{ + return le32_to_cpu(trb->link.control) & LINK_TOGGLE; +} +/* + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs. That would be dumb and this would loop. + */ +void inc_deq(struct usbssp_udc *usbssp_data, struct usbssp_ring *ring) +{ + /* event ring doesn't have link trbs, check for last trb */ + if (ring->type == TYPE_EVENT) { + if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { + ring->dequeue++; + goto out; + } + if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue)) + ring->cycle_state ^= 1; + ring->deq_seg = ring->deq_seg->next; + ring->dequeue = ring->deq_seg->trbs; + goto out; + } + + /* All other rings have link trbs */ + if (!trb_is_link(ring->dequeue)) { + ring->dequeue++; + ring->num_trbs_free++; + } + while (trb_is_link(ring->dequeue)) { + ring->deq_seg = ring->deq_seg->next; + ring->dequeue = ring->deq_seg->trbs; + } +out: + trace_usbssp_inc_deq(ring); +} + +/* + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs. That would be dumb and this would loop. + * + * If we've just enqueued a TRB that is in the middle of a TD (meaning the + * chain bit is set), then set the chain bit in all the following link TRBs. + * If we've enqueued the last TRB in a TD, make sure the following link TRBs + * have their chain bit cleared (so that each Link TRB is a separate TD). + * + * @more_trbs_coming: Will you enqueue more TRBs before calling + * prepare_transfer()? + */ +static void inc_enq(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ring, + bool more_trbs_coming) +{ + u32 chain; + union usbssp_trb *next; + + chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; + /* If this is not event ring, there is one less usable TRB */ + if (!trb_is_link(ring->enqueue)) + ring->num_trbs_free--; + next = ++(ring->enqueue); + + /* Update the dequeue pointer further if that was a link TRB */ + while (trb_is_link(next)) { + + /* + * If the caller doesn't plan on enqueueing more TDs before + * ringing the doorbell, then we don't want to give the link TRB + * to the hardware just yet. We'll give the link TRB back in + * prepare_ring() just before we enqueue the TD at the top of + * the ring. + */ + if (!chain && !more_trbs_coming) + break; + + next->link.control &= cpu_to_le32(~TRB_CHAIN); + next->link.control |= cpu_to_le32(chain); + + /* Give this link TRB to the hardware */ + wmb(); + next->link.control ^= cpu_to_le32(TRB_CYCLE); + + /* Toggle the cycle bit after the last ring segment. */ + if (link_trb_toggles_cycle(next)) + ring->cycle_state ^= 1; + + ring->enq_seg = ring->enq_seg->next; + ring->enqueue = ring->enq_seg->trbs; + next = ring->enqueue; + } + trace_usbssp_inc_enq(ring); +} + +/* + * Check to see if there's room to enqueue num_trbs on the ring and make sure + * enqueue pointer will not advance into dequeue segment. See rules above. + */ +static inline int room_on_ring(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ring, + unsigned int num_trbs) +{ + int num_trbs_in_deq_seg; + + if (ring->num_trbs_free < num_trbs) + return 0; + + if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) { + num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs; + + if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg) + return 0; + } + + return 1; +} + +static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data, + unsigned long delay) +{ + return mod_delayed_work(system_wq, &usbssp_data->cmd_timer, delay); + return 0; +} + irqreturn_t usbssp_irq(int irq, void *priv) { struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv; @@ -47,7 +231,6 @@ irqreturn_t usbssp_irq(int irq, void *priv) return ret; } - void usbssp_handle_command_timeout(struct work_struct *work) { /*TODO: implements function*/ @@ -146,3 +329,353 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data, return NULL; } + +/**** Endpoint Ring Operations ****/ + +/* + * Generic function for queueing a TRB on a ring. + * The caller must have checked to make sure there's room on the ring. + * + * @more_trbs_coming: Will you enqueue more TRBs before calling + * prepare_transfer()? + */ +static void queue_trb(struct usbssp_udc *usbssp_data, struct usbssp_ring *ring, + bool more_trbs_coming, + u32 field1, u32 field2, u32 field3, u32 field4) +{ + struct usbssp_generic_trb *trb; + + trb = &ring->enqueue->generic; + + usbssp_dbg(usbssp_data, "Queue TRB at virt: %p, dma: %llx\n", trb, + usbssp_trb_virt_to_dma(ring->enq_seg, ring->enqueue)); + + trb->field[0] = cpu_to_le32(field1); + trb->field[1] = cpu_to_le32(field2); + trb->field[2] = cpu_to_le32(field3); + trb->field[3] = cpu_to_le32(field4); + + trace_usbssp_queue_trb(ring, trb); + inc_enq(usbssp_data, ring, more_trbs_coming); +} + +/* + * Does various checks on the endpoint ring, and makes it ready to + * queue num_trbs. + */ +static int prepare_ring(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ep_ring, + u32 ep_state, unsigned + int num_trbs, + gfp_t mem_flags) +{ + unsigned int num_trbs_needed; + + /* Make sure the endpoint has been added to USBSSP schedule */ + switch (ep_state) { + case EP_STATE_DISABLED: + usbssp_warn(usbssp_data, + "WARN request submitted to disabled ep\n"); + return -ENOENT; + case EP_STATE_ERROR: + usbssp_warn(usbssp_data, + "WARN waiting for error on ep to be cleared\n"); + return -EINVAL; + case EP_STATE_HALTED: + usbssp_dbg(usbssp_data, + "WARN halted endpoint, queueing request anyway.\n"); + case EP_STATE_STOPPED: + case EP_STATE_RUNNING: + break; + default: + usbssp_err(usbssp_data, + "ERROR unknown endpoint state for ep\n"); + return -EINVAL; + } + + while (1) { + if (room_on_ring(usbssp_data, ep_ring, num_trbs)) + break; + + if (ep_ring == usbssp_data->cmd_ring) { + usbssp_err(usbssp_data, + "Do not support expand command ring\n"); + return -ENOMEM; + } + + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_ring_expansion, + "ERROR no room on ep ring, try ring expansion"); + + num_trbs_needed = num_trbs - ep_ring->num_trbs_free; + if (usbssp_ring_expansion(usbssp_data, ep_ring, num_trbs_needed, + mem_flags)) { + usbssp_err(usbssp_data, "Ring expansion failed\n"); + return -ENOMEM; + } + } + + while (trb_is_link(ep_ring->enqueue)) { + + ep_ring->enqueue->link.control |= cpu_to_le32(TRB_CHAIN); + wmb(); + ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE); + + /* Toggle the cycle bit after the last ring segment. */ + if (link_trb_toggles_cycle(ep_ring->enqueue)) + ep_ring->cycle_state ^= 1; + ep_ring->enq_seg = ep_ring->enq_seg->next; + ep_ring->enqueue = ep_ring->enq_seg->trbs; + } + return 0; +} + +/**** Command Ring Operations ****/ +/* Generic function for queueing a command TRB on the command ring. + * Check to make sure there's room on the command ring for one command TRB. + * Also check that there's room reserved for commands that must not fail. + * If this is a command that must not fail, meaning command_must_succeed = TRUE, + * then only check for the number of reserved spots. + * Don't decrement usbssp_data->cmd_ring_reserved_trbs after we've queued the + * TRB because the command event handler may want to resubmit a failed command. + */ +static int queue_command(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + u32 field1, u32 field2, + u32 field3, u32 field4, + bool command_must_succeed) +{ + int reserved_trbs = usbssp_data->cmd_ring_reserved_trbs; + int ret; + + if ((usbssp_data->usbssp_state & USBSSP_STATE_DYING) || + (usbssp_data->usbssp_state & USBSSP_STATE_HALTED)) { + usbssp_dbg(usbssp_data, + "USBSSP dying or halted, can't queue command\n"); + return -ESHUTDOWN; + } + + if (!command_must_succeed) + reserved_trbs++; + + ret = prepare_ring(usbssp_data, usbssp_data->cmd_ring, EP_STATE_RUNNING, + reserved_trbs, GFP_ATOMIC); + if (ret < 0) { + usbssp_err(usbssp_data, + "ERR: No room for command on command ring\n"); + if (command_must_succeed) + usbssp_err(usbssp_data, + "ERR: Reserved TRB counting for " + "unfailable commands failed.\n"); + return ret; + } + + cmd->command_trb = usbssp_data->cmd_ring->enqueue; + + /* if there are no other commands queued we start the timeout timer */ + if (list_empty(&usbssp_data->cmd_list)) { + usbssp_data->current_cmd = cmd; + usbssp_mod_cmd_timer(usbssp_data, USBSSP_CMD_DEFAULT_TIMEOUT); + } + + list_add_tail(&cmd->cmd_list, &usbssp_data->cmd_list); + + queue_trb(usbssp_data, usbssp_data->cmd_ring, false, field1, field2, + field3, field4 | usbssp_data->cmd_ring->cycle_state); + return 0; +} + +/* Queue a slot enable or disable request on the command ring */ +int usbssp_queue_slot_control(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + u32 trb_type) +{ + return queue_command(usbssp_data, cmd, 0, 0, 0, + TRB_TYPE(trb_type) | + SLOT_ID_FOR_TRB(usbssp_data->slot_id), false); +} + +/* Queue an address device command TRB */ +int usbssp_queue_address_device(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + dma_addr_t in_ctx_ptr, + enum usbssp_setup_dev setup) +{ + return queue_command(usbssp_data, cmd, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, + TRB_TYPE(TRB_ADDR_DEV) | + SLOT_ID_FOR_TRB(usbssp_data->slot_id) + | (setup == SETUP_CONTEXT_ONLY ? TRB_BSR : 0), false); +} + +int usbssp_queue_vendor_command(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + u32 field1, u32 field2, u32 field3, u32 field4) +{ + return queue_command(usbssp_data, cmd, field1, field2, field3, + field4, false); +} + +/* Queue a reset device command TRB */ +int usbssp_queue_reset_device(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd) +{ + return queue_command(usbssp_data, cmd, 0, 0, 0, + TRB_TYPE(TRB_RESET_DEV) | + SLOT_ID_FOR_TRB(usbssp_data->slot_id), + false); +} + +/* Queue a configure endpoint command TRB */ +int usbssp_queue_configure_endpoint(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + dma_addr_t in_ctx_ptr, + bool command_must_succeed) +{ + return queue_command(usbssp_data, cmd, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, + TRB_TYPE(TRB_CONFIG_EP) | + SLOT_ID_FOR_TRB(usbssp_data->slot_id), + command_must_succeed); +} + +/* Queue an evaluate context command TRB */ +int usbssp_queue_evaluate_context(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + dma_addr_t in_ctx_ptr, + bool command_must_succeed) +{ + return queue_command(usbssp_data, cmd, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, + TRB_TYPE(TRB_EVAL_CONTEXT) | + SLOT_ID_FOR_TRB(usbssp_data->slot_id), + command_must_succeed); +} + +/* + * Suspend is set to indicate "Stop Endpoint Command" is being issued to stop + * activity on an endpoint that is about to be suspended. + */ +int usbssp_queue_stop_endpoint(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + unsigned int ep_index, int suspend) +{ + u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); + u32 type = TRB_TYPE(TRB_STOP_RING); + u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend); + + return queue_command(usbssp_data, cmd, 0, 0, 0, + trb_slot_id | trb_ep_index | type | trb_suspend, false); +} + +/* Set Transfer Ring Dequeue Pointer command */ +void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data, + unsigned int ep_index, + struct usbssp_dequeue_state *deq_state) +{ + dma_addr_t addr; + u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); + u32 trb_stream_id = STREAM_ID_FOR_TRB(deq_state->stream_id); + u32 trb_sct = 0; + u32 type = TRB_TYPE(TRB_SET_DEQ); + struct usbssp_ep *ep_priv; + struct usbssp_command *cmd; + int ret; + + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " + "new deq ptr = %p (0x%llx dma), new cycle = %u", + deq_state->new_deq_seg, + (unsigned long long)deq_state->new_deq_seg->dma, + deq_state->new_deq_ptr, + (unsigned long long)usbssp_trb_virt_to_dma( + deq_state->new_deq_seg, deq_state->new_deq_ptr), + deq_state->new_cycle_state); + + addr = usbssp_trb_virt_to_dma(deq_state->new_deq_seg, + deq_state->new_deq_ptr); + if (addr == 0) { + usbssp_warn(usbssp_data, "WARN Cannot submit Set TR Deq Ptr\n"); + usbssp_warn(usbssp_data, "WARN deq seg = %p, deq pt = %p\n", + deq_state->new_deq_seg, deq_state->new_deq_ptr); + return; + } + ep_priv = &usbssp_data->devs.eps[ep_index]; + if ((ep_priv->ep_state & SET_DEQ_PENDING)) { + usbssp_warn(usbssp_data, "WARN Cannot submit Set TR Deq Ptr\n"); + usbssp_warn(usbssp_data, + "A Set TR Deq Ptr command is pending.\n"); + return; + } + + /* This function gets called from contexts where it cannot sleep */ + cmd = usbssp_alloc_command(usbssp_data, false, GFP_ATOMIC); + if (!cmd) { + usbssp_warn(usbssp_data, + "WARN Cannot submit Set TR Deq Ptr: ENOMEM\n"); + return; + } + + ep_priv->queued_deq_seg = deq_state->new_deq_seg; + ep_priv->queued_deq_ptr = deq_state->new_deq_ptr; + if (deq_state->stream_id) + trb_sct = SCT_FOR_TRB(SCT_PRI_TR); + ret = queue_command(usbssp_data, cmd, + lower_32_bits(addr) | trb_sct | deq_state->new_cycle_state, + upper_32_bits(addr), trb_stream_id, + trb_slot_id | trb_ep_index | type, false); + if (ret < 0) { + usbssp_free_command(usbssp_data, cmd); + return; + } + + /* Stop the TD queueing code from ringing the doorbell until + * this command completes. The DC won't set the dequeue pointer + * if the ring is running, and ringing the doorbell starts the + * ring running. + */ + ep_priv->ep_state |= SET_DEQ_PENDING; +} + +int usbssp_queue_reset_ep(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + unsigned int ep_index, + enum usbssp_ep_reset_type reset_type) +{ + u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); + u32 type = TRB_TYPE(TRB_RESET_EP); + + if (reset_type == EP_SOFT_RESET) + type |= TRB_TSP; + + return queue_command(usbssp_data, cmd, 0, 0, 0, + trb_slot_id | trb_ep_index | type, false); +} + +/* + * Queue an NOP command TRB + */ +int usbssp_queue_nop(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd) +{ + return queue_command(usbssp_data, cmd, 0, 0, 0, + TRB_TYPE(TRB_CMD_NOOP), false); +} + +/* + * Queue a halt endpoint request on the command ring + */ +int usbssp_queue_halt_endpoint(struct usbssp_udc *usbssp_data, + struct usbssp_command *cmd, + unsigned int ep_index) +{ + u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); + + return queue_command(usbssp_data, cmd, 0, 0, 0, + TRB_TYPE(TRB_HALT_ENDPOINT) | trb_slot_id | + trb_ep_index, false); +} + diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h index 374c85995dd7..d0ce20f35ec6 100644 --- a/drivers/usb/usbssp/gadget.h +++ b/drivers/usb/usbssp/gadget.h @@ -1679,7 +1679,11 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data, /* USBSSP memory management */ void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data); int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags); +int usbssp_ring_expansion(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ring, unsigned int num_trbs, gfp_t flags); +struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data, + bool allocate_completion, gfp_t mem_flags); void usbssp_free_command(struct usbssp_udc *usbssp_data, struct usbssp_command *command); -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html