123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
- *
- * epn.c - Generic endpoints management
- *
- * Copyright 2017 IBM Corporation
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/delay.h>
- #include <linux/ioport.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/list.h>
- #include <linux/interrupt.h>
- #include <linux/proc_fs.h>
- #include <linux/prefetch.h>
- #include <linux/clk.h>
- #include <linux/usb/gadget.h>
- #include <linux/of.h>
- #include <linux/of_gpio.h>
- #include <linux/regmap.h>
- #include <linux/dma-mapping.h>
- #include "vhub.h"
- #define EXTRA_CHECKS
- #ifdef EXTRA_CHECKS
- #define CHECK(ep, expr, fmt...) \
- do { \
- if (!(expr)) EPDBG(ep, "CHECK:" fmt); \
- } while(0)
- #else
- #define CHECK(ep, expr, fmt...) do { } while(0)
- #endif
- static void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req)
- {
- unsigned int act = req->req.actual;
- unsigned int len = req->req.length;
- unsigned int chunk;
- /* There should be no DMA ongoing */
- WARN_ON(req->active);
- /* Calculate next chunk size */
- chunk = len - act;
- if (chunk > ep->ep.maxpacket)
- chunk = ep->ep.maxpacket;
- else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
- req->last_desc = 1;
- EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n",
- req, act, len, chunk, req->last_desc);
- /* If DMA unavailable, using staging EP buffer */
- if (!req->req.dma) {
- /* For IN transfers, copy data over first */
- if (ep->epn.is_in) {
- memcpy(ep->buf, req->req.buf + act, chunk);
- vhub_dma_workaround(ep->buf);
- }
- writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
- } else {
- if (ep->epn.is_in)
- vhub_dma_workaround(req->req.buf);
- writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
- }
- /* Start DMA */
- req->active = true;
- writel(VHUB_EP_DMA_SET_TX_SIZE(chunk),
- ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK,
- ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- }
- static void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep)
- {
- struct ast_vhub_req *req;
- unsigned int len;
- u32 stat;
- /* Read EP status */
- stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- /* Grab current request if any */
- req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
- EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n",
- stat, ep->epn.is_in, req, req ? req->active : 0);
- /* In absence of a request, bail out, must have been dequeued */
- if (!req)
- return;
- /*
- * Request not active, move on to processing queue, active request
- * was probably dequeued
- */
- if (!req->active)
- goto next_chunk;
- /* Check if HW has moved on */
- if (VHUB_EP_DMA_RPTR(stat) != 0) {
- EPDBG(ep, "DMA read pointer not 0 !\n");
- return;
- }
- /* No current DMA ongoing */
- req->active = false;
- /* Grab length out of HW */
- len = VHUB_EP_DMA_TX_SIZE(stat);
- /* If not using DMA, copy data out if needed */
- if (!req->req.dma && !ep->epn.is_in && len)
- memcpy(req->req.buf + req->req.actual, ep->buf, len);
- /* Adjust size */
- req->req.actual += len;
- /* Check for short packet */
- if (len < ep->ep.maxpacket)
- req->last_desc = 1;
- /* That's it ? complete the request and pick a new one */
- if (req->last_desc >= 0) {
- ast_vhub_done(ep, req, 0);
- req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req,
- queue);
- /*
- * Due to lock dropping inside "done" the next request could
- * already be active, so check for that and bail if needed.
- */
- if (!req || req->active)
- return;
- }
- next_chunk:
- ast_vhub_epn_kick(ep, req);
- }
- static inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep)
- {
- /*
- * d_next == d_last means descriptor list empty to HW,
- * thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors
- * in the list
- */
- return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) &
- (AST_VHUB_DESCS_COUNT - 1);
- }
- static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
- struct ast_vhub_req *req)
- {
- struct ast_vhub_desc *desc = NULL;
- unsigned int act = req->act_count;
- unsigned int len = req->req.length;
- unsigned int chunk;
- /* Mark request active if not already */
- req->active = true;
- /* If the request was already completely written, do nothing */
- if (req->last_desc >= 0)
- return;
- EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n",
- act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep));
- /* While we can create descriptors */
- while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) {
- unsigned int d_num;
- /* Grab next free descriptor */
- d_num = ep->epn.d_next;
- desc = &ep->epn.descs[d_num];
- ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
- /* Calculate next chunk size */
- chunk = len - act;
- if (chunk <= ep->epn.chunk_max) {
- /*
- * Is this the last packet ? Because of having up to 8
- * packets in a descriptor we can't just compare "chunk"
- * with ep.maxpacket. We have to see if it's a multiple
- * of it to know if we have to send a zero packet.
- * Sadly that involves a modulo which is a bit expensive
- * but probably still better than not doing it.
- */
- if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0)
- req->last_desc = d_num;
- } else {
- chunk = ep->epn.chunk_max;
- }
- EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n",
- act, len, chunk, req->last_desc, d_num,
- ast_vhub_count_free_descs(ep));
- /* Populate descriptor */
- desc->w0 = cpu_to_le32(req->req.dma + act);
- /* Interrupt if end of request or no more descriptors */
- /*
- * TODO: Be smarter about it, if we don't have enough
- * descriptors request an interrupt before queue empty
- * or so in order to be able to populate more before
- * the HW runs out. This isn't a problem at the moment
- * as we use 256 descriptors and only put at most one
- * request in the ring.
- */
- desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk));
- if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep))
- desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT);
- /* Account packet */
- req->act_count = act = act + chunk;
- }
- if (likely(desc))
- vhub_dma_workaround(desc);
- /* Tell HW about new descriptors */
- writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next),
- ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n",
- ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS));
- }
- static void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep)
- {
- struct ast_vhub_req *req;
- unsigned int len, d_last;
- u32 stat, stat1;
- /* Read EP status, workaround HW race */
- do {
- stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- } while(stat != stat1);
- /* Extract RPTR */
- d_last = VHUB_EP_DMA_RPTR(stat);
- /* Grab current request if any */
- req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
- EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n",
- stat, ep->epn.is_in, ep->epn.d_last, d_last);
- /* Check all completed descriptors */
- while (ep->epn.d_last != d_last) {
- struct ast_vhub_desc *desc;
- unsigned int d_num;
- bool is_last_desc;
- /* Grab next completed descriptor */
- d_num = ep->epn.d_last;
- desc = &ep->epn.descs[d_num];
- ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
- /* Grab len out of descriptor */
- len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1));
- EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n",
- d_num, len, req, req ? req->active : 0);
- /* If no active request pending, move on */
- if (!req || !req->active)
- continue;
- /* Adjust size */
- req->req.actual += len;
- /* Is that the last chunk ? */
- is_last_desc = req->last_desc == d_num;
- CHECK(ep, is_last_desc == (len < ep->ep.maxpacket ||
- (req->req.actual >= req->req.length &&
- !req->req.zero)),
- "Last packet discrepancy: last_desc=%d len=%d r.act=%d "
- "r.len=%d r.zero=%d mp=%d\n",
- is_last_desc, len, req->req.actual, req->req.length,
- req->req.zero, ep->ep.maxpacket);
- if (is_last_desc) {
- /*
- * Because we can only have one request at a time
- * in our descriptor list in this implementation,
- * d_last and ep->d_last should now be equal
- */
- CHECK(ep, d_last == ep->epn.d_last,
- "DMA read ptr mismatch %d vs %d\n",
- d_last, ep->epn.d_last);
- /* Note: done will drop and re-acquire the lock */
- ast_vhub_done(ep, req, 0);
- req = list_first_entry_or_null(&ep->queue,
- struct ast_vhub_req,
- queue);
- break;
- }
- }
- /* More work ? */
- if (req)
- ast_vhub_epn_kick_desc(ep, req);
- }
- void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep)
- {
- if (ep->epn.desc_mode)
- ast_vhub_epn_handle_ack_desc(ep);
- else
- ast_vhub_epn_handle_ack(ep);
- }
- static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
- gfp_t gfp_flags)
- {
- struct ast_vhub_req *req = to_ast_req(u_req);
- struct ast_vhub_ep *ep = to_ast_ep(u_ep);
- struct ast_vhub *vhub = ep->vhub;
- unsigned long flags;
- bool empty;
- int rc;
- /* Paranoid checks */
- if (!u_req || !u_req->complete || !u_req->buf) {
- dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req);
- if (u_req) {
- dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n",
- u_req->complete, req->internal);
- }
- return -EINVAL;
- }
- /* Endpoint enabled ? */
- if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
- !ep->dev->enabled) {
- EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
- return -ESHUTDOWN;
- }
- /* Map request for DMA if possible. For now, the rule for DMA is
- * that:
- *
- * * For single stage mode (no descriptors):
- *
- * - The buffer is aligned to a 8 bytes boundary (HW requirement)
- * - For a OUT endpoint, the request size is a multiple of the EP
- * packet size (otherwise the controller will DMA past the end
- * of the buffer if the host is sending a too long packet).
- *
- * * For descriptor mode (tx only for now), always.
- *
- * We could relax the latter by making the decision to use the bounce
- * buffer based on the size of a given *segment* of the request rather
- * than the whole request.
- */
- if (ep->epn.desc_mode ||
- ((((unsigned long)u_req->buf & 7) == 0) &&
- (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) {
- rc = usb_gadget_map_request_by_dev(&vhub->pdev->dev, u_req,
- ep->epn.is_in);
- if (rc) {
- dev_warn(&vhub->pdev->dev,
- "Request mapping failure %d\n", rc);
- return rc;
- }
- } else
- u_req->dma = 0;
- EPVDBG(ep, "enqueue req @%p\n", req);
- EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n",
- u_req->length, (u32)u_req->dma, u_req->zero,
- u_req->short_not_ok, u_req->no_interrupt,
- ep->epn.is_in);
- /* Initialize request progress fields */
- u_req->status = -EINPROGRESS;
- u_req->actual = 0;
- req->act_count = 0;
- req->active = false;
- req->last_desc = -1;
- spin_lock_irqsave(&vhub->lock, flags);
- empty = list_empty(&ep->queue);
- /* Add request to list and kick processing if empty */
- list_add_tail(&req->queue, &ep->queue);
- if (empty) {
- if (ep->epn.desc_mode)
- ast_vhub_epn_kick_desc(ep, req);
- else
- ast_vhub_epn_kick(ep, req);
- }
- spin_unlock_irqrestore(&vhub->lock, flags);
- return 0;
- }
- static void ast_vhub_stop_active_req(struct ast_vhub_ep *ep,
- bool restart_ep)
- {
- u32 state, reg, loops;
- /* Stop DMA activity */
- if (ep->epn.desc_mode)
- writel(VHUB_EP_DMA_CTRL_RESET, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- else
- writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- /* Wait for it to complete */
- for (loops = 0; loops < 1000; loops++) {
- state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- state = VHUB_EP_DMA_PROC_STATUS(state);
- if (state == EP_DMA_PROC_RX_IDLE ||
- state == EP_DMA_PROC_TX_IDLE)
- break;
- udelay(1);
- }
- if (loops >= 1000)
- dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n");
- /* If we don't have to restart the endpoint, that's it */
- if (!restart_ep)
- return;
- /* Restart the endpoint */
- if (ep->epn.desc_mode) {
- /*
- * Take out descriptors by resetting the DMA read
- * pointer to be equal to the CPU write pointer.
- *
- * Note: If we ever support creating descriptors for
- * requests that aren't the head of the queue, we
- * may have to do something more complex here,
- * especially if the request being taken out is
- * not the current head descriptors.
- */
- reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) |
- VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next);
- writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- /* Then turn it back on */
- writel(ep->epn.dma_conf,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- } else {
- /* Single mode: just turn it back on */
- writel(ep->epn.dma_conf,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- }
- }
- static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
- {
- struct ast_vhub_ep *ep = to_ast_ep(u_ep);
- struct ast_vhub *vhub = ep->vhub;
- struct ast_vhub_req *req = NULL, *iter;
- unsigned long flags;
- int rc = -EINVAL;
- spin_lock_irqsave(&vhub->lock, flags);
- /* Make sure it's actually queued on this endpoint */
- list_for_each_entry(iter, &ep->queue, queue) {
- if (&iter->req != u_req)
- continue;
- req = iter;
- break;
- }
- if (req) {
- EPVDBG(ep, "dequeue req @%p active=%d\n",
- req, req->active);
- if (req->active)
- ast_vhub_stop_active_req(ep, true);
- ast_vhub_done(ep, req, -ECONNRESET);
- rc = 0;
- }
- spin_unlock_irqrestore(&vhub->lock, flags);
- return rc;
- }
- void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep)
- {
- u32 reg;
- if (WARN_ON(ep->d_idx == 0))
- return;
- reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG);
- if (ep->epn.stalled || ep->epn.wedged)
- reg |= VHUB_EP_CFG_STALL_CTRL;
- else
- reg &= ~VHUB_EP_CFG_STALL_CTRL;
- writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG);
- if (!ep->epn.stalled && !ep->epn.wedged)
- writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
- ep->vhub->regs + AST_VHUB_EP_TOGGLE);
- }
- static int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt,
- bool wedge)
- {
- struct ast_vhub_ep *ep = to_ast_ep(u_ep);
- struct ast_vhub *vhub = ep->vhub;
- unsigned long flags;
- EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge);
- if (!u_ep || !u_ep->desc)
- return -EINVAL;
- if (ep->d_idx == 0)
- return 0;
- if (ep->epn.is_iso)
- return -EOPNOTSUPP;
- spin_lock_irqsave(&vhub->lock, flags);
- /* Fail with still-busy IN endpoints */
- if (halt && ep->epn.is_in && !list_empty(&ep->queue)) {
- spin_unlock_irqrestore(&vhub->lock, flags);
- return -EAGAIN;
- }
- ep->epn.stalled = halt;
- ep->epn.wedged = wedge;
- ast_vhub_update_epn_stall(ep);
- spin_unlock_irqrestore(&vhub->lock, flags);
- return 0;
- }
- static int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value)
- {
- return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false);
- }
- static int ast_vhub_epn_set_wedge(struct usb_ep *u_ep)
- {
- return ast_vhub_set_halt_and_wedge(u_ep, true, true);
- }
- static int ast_vhub_epn_disable(struct usb_ep* u_ep)
- {
- struct ast_vhub_ep *ep = to_ast_ep(u_ep);
- struct ast_vhub *vhub = ep->vhub;
- unsigned long flags;
- u32 imask, ep_ier;
- EPDBG(ep, "Disabling !\n");
- spin_lock_irqsave(&vhub->lock, flags);
- ep->epn.enabled = false;
- /* Stop active DMA if any */
- ast_vhub_stop_active_req(ep, false);
- /* Disable endpoint */
- writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
- /* Disable ACK interrupt */
- imask = VHUB_EP_IRQ(ep->epn.g_idx);
- ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
- ep_ier &= ~imask;
- writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
- writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
- /* Nuke all pending requests */
- ast_vhub_nuke(ep, -ESHUTDOWN);
- /* No more descriptor associated with request */
- ep->ep.desc = NULL;
- spin_unlock_irqrestore(&vhub->lock, flags);
- return 0;
- }
- static int ast_vhub_epn_enable(struct usb_ep* u_ep,
- const struct usb_endpoint_descriptor *desc)
- {
- struct ast_vhub_ep *ep = to_ast_ep(u_ep);
- struct ast_vhub_dev *dev;
- struct ast_vhub *vhub;
- u16 maxpacket, type;
- unsigned long flags;
- u32 ep_conf, ep_ier, imask;
- /* Check arguments */
- if (!u_ep || !desc)
- return -EINVAL;
- maxpacket = usb_endpoint_maxp(desc);
- if (!ep->d_idx || !ep->dev ||
- desc->bDescriptorType != USB_DT_ENDPOINT ||
- maxpacket == 0 || maxpacket > ep->ep.maxpacket) {
- EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n",
- ep->d_idx, ep->dev, desc->bDescriptorType,
- maxpacket, ep->ep.maxpacket);
- return -EINVAL;
- }
- if (ep->d_idx != usb_endpoint_num(desc)) {
- EPDBG(ep, "EP number mismatch !\n");
- return -EINVAL;
- }
- if (ep->epn.enabled) {
- EPDBG(ep, "Already enabled\n");
- return -EBUSY;
- }
- dev = ep->dev;
- vhub = ep->vhub;
- /* Check device state */
- if (!dev->driver) {
- EPDBG(ep, "Bogus device state: driver=%p speed=%d\n",
- dev->driver, dev->gadget.speed);
- return -ESHUTDOWN;
- }
- /* Grab some info from the descriptor */
- ep->epn.is_in = usb_endpoint_dir_in(desc);
- ep->ep.maxpacket = maxpacket;
- type = usb_endpoint_type(desc);
- ep->epn.d_next = ep->epn.d_last = 0;
- ep->epn.is_iso = false;
- ep->epn.stalled = false;
- ep->epn.wedged = false;
- EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
- ep->epn.is_in ? "in" : "out", usb_ep_type_string(type),
- usb_endpoint_num(desc), maxpacket);
- /* Can we use DMA descriptor mode ? */
- ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in;
- if (ep->epn.desc_mode)
- memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT);
- /*
- * Large send function can send up to 8 packets from
- * one descriptor with a limit of 4095 bytes.
- */
- ep->epn.chunk_max = ep->ep.maxpacket;
- if (ep->epn.is_in) {
- ep->epn.chunk_max <<= 3;
- while (ep->epn.chunk_max > 4095)
- ep->epn.chunk_max -= ep->ep.maxpacket;
- }
- switch(type) {
- case USB_ENDPOINT_XFER_CONTROL:
- EPDBG(ep, "Only one control endpoint\n");
- return -EINVAL;
- case USB_ENDPOINT_XFER_INT:
- ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT);
- break;
- case USB_ENDPOINT_XFER_BULK:
- ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK);
- break;
- case USB_ENDPOINT_XFER_ISOC:
- ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO);
- ep->epn.is_iso = true;
- break;
- default:
- return -EINVAL;
- }
- /* Encode the rest of the EP config register */
- if (maxpacket < 1024)
- ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket);
- if (!ep->epn.is_in)
- ep_conf |= VHUB_EP_CFG_DIR_OUT;
- ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc));
- ep_conf |= VHUB_EP_CFG_ENABLE;
- ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1);
- EPVDBG(ep, "config=%08x\n", ep_conf);
- spin_lock_irqsave(&vhub->lock, flags);
- /* Disable HW and reset DMA */
- writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
- writel(VHUB_EP_DMA_CTRL_RESET,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- /* Configure and enable */
- writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG);
- if (ep->epn.desc_mode) {
- /* Clear DMA status, including the DMA read ptr */
- writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- /* Set descriptor base */
- writel(ep->epn.descs_dma,
- ep->epn.regs + AST_VHUB_EP_DESC_BASE);
- /* Set base DMA config value */
- ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE;
- if (ep->epn.is_in)
- ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE;
- /* First reset and disable all operations */
- writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- /* Enable descriptor mode */
- writel(ep->epn.dma_conf,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- } else {
- /* Set base DMA config value */
- ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE;
- /* Reset and switch to single stage mode */
- writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- writel(ep->epn.dma_conf,
- ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
- writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
- }
- /* Cleanup data toggle just in case */
- writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
- vhub->regs + AST_VHUB_EP_TOGGLE);
- /* Cleanup and enable ACK interrupt */
- imask = VHUB_EP_IRQ(ep->epn.g_idx);
- writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
- ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
- ep_ier |= imask;
- writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
- /* Woot, we are online ! */
- ep->epn.enabled = true;
- spin_unlock_irqrestore(&vhub->lock, flags);
- return 0;
- }
- static void ast_vhub_epn_dispose(struct usb_ep *u_ep)
- {
- struct ast_vhub_ep *ep = to_ast_ep(u_ep);
- if (WARN_ON(!ep->dev || !ep->d_idx))
- return;
- EPDBG(ep, "Releasing endpoint\n");
- /* Take it out of the EP list */
- list_del_init(&ep->ep.ep_list);
- /* Mark the address free in the device */
- ep->dev->epns[ep->d_idx - 1] = NULL;
- /* Free name & DMA buffers */
- kfree(ep->ep.name);
- ep->ep.name = NULL;
- dma_free_coherent(&ep->vhub->pdev->dev,
- AST_VHUB_EPn_MAX_PACKET +
- 8 * AST_VHUB_DESCS_COUNT,
- ep->buf, ep->buf_dma);
- ep->buf = NULL;
- ep->epn.descs = NULL;
- /* Mark free */
- ep->dev = NULL;
- }
- static const struct usb_ep_ops ast_vhub_epn_ops = {
- .enable = ast_vhub_epn_enable,
- .disable = ast_vhub_epn_disable,
- .dispose = ast_vhub_epn_dispose,
- .queue = ast_vhub_epn_queue,
- .dequeue = ast_vhub_epn_dequeue,
- .set_halt = ast_vhub_epn_set_halt,
- .set_wedge = ast_vhub_epn_set_wedge,
- .alloc_request = ast_vhub_alloc_request,
- .free_request = ast_vhub_free_request,
- };
- struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
- {
- struct ast_vhub *vhub = d->vhub;
- struct ast_vhub_ep *ep;
- unsigned long flags;
- int i;
- /* Find a free one (no device) */
- spin_lock_irqsave(&vhub->lock, flags);
- for (i = 0; i < vhub->max_epns; i++)
- if (vhub->epns[i].dev == NULL)
- break;
- if (i >= vhub->max_epns) {
- spin_unlock_irqrestore(&vhub->lock, flags);
- return NULL;
- }
- /* Set it up */
- ep = &vhub->epns[i];
- ep->dev = d;
- spin_unlock_irqrestore(&vhub->lock, flags);
- DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr);
- INIT_LIST_HEAD(&ep->queue);
- ep->d_idx = addr;
- ep->vhub = vhub;
- ep->ep.ops = &ast_vhub_epn_ops;
- ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr);
- d->epns[addr-1] = ep;
- ep->epn.g_idx = i;
- ep->epn.regs = vhub->regs + 0x200 + (i * 0x10);
- ep->buf = dma_alloc_coherent(&vhub->pdev->dev,
- AST_VHUB_EPn_MAX_PACKET +
- 8 * AST_VHUB_DESCS_COUNT,
- &ep->buf_dma, GFP_KERNEL);
- if (!ep->buf) {
- kfree(ep->ep.name);
- ep->ep.name = NULL;
- return NULL;
- }
- ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET;
- ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET;
- usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET);
- list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list);
- ep->ep.caps.type_iso = true;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
- return ep;
- }
|