162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * epn.c - Generic endpoints management 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2017 IBM Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/ioport.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/proc_fs.h> 2062306a36Sopenharmony_ci#include <linux/prefetch.h> 2162306a36Sopenharmony_ci#include <linux/clk.h> 2262306a36Sopenharmony_ci#include <linux/usb/gadget.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/regmap.h> 2562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "vhub.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define EXTRA_CHECKS 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef EXTRA_CHECKS 3262306a36Sopenharmony_ci#define CHECK(ep, expr, fmt...) \ 3362306a36Sopenharmony_ci do { \ 3462306a36Sopenharmony_ci if (!(expr)) EPDBG(ep, "CHECK:" fmt); \ 3562306a36Sopenharmony_ci } while(0) 3662306a36Sopenharmony_ci#else 3762306a36Sopenharmony_ci#define CHECK(ep, expr, fmt...) do { } while(0) 3862306a36Sopenharmony_ci#endif 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci unsigned int act = req->req.actual; 4362306a36Sopenharmony_ci unsigned int len = req->req.length; 4462306a36Sopenharmony_ci unsigned int chunk; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* There should be no DMA ongoing */ 4762306a36Sopenharmony_ci WARN_ON(req->active); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Calculate next chunk size */ 5062306a36Sopenharmony_ci chunk = len - act; 5162306a36Sopenharmony_ci if (chunk > ep->ep.maxpacket) 5262306a36Sopenharmony_ci chunk = ep->ep.maxpacket; 5362306a36Sopenharmony_ci else if ((chunk < ep->ep.maxpacket) || !req->req.zero) 5462306a36Sopenharmony_ci req->last_desc = 1; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n", 5762306a36Sopenharmony_ci req, act, len, chunk, req->last_desc); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* If DMA unavailable, using staging EP buffer */ 6062306a36Sopenharmony_ci if (!req->req.dma) { 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* For IN transfers, copy data over first */ 6362306a36Sopenharmony_ci if (ep->epn.is_in) { 6462306a36Sopenharmony_ci memcpy(ep->buf, req->req.buf + act, chunk); 6562306a36Sopenharmony_ci vhub_dma_workaround(ep->buf); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE); 6862306a36Sopenharmony_ci } else { 6962306a36Sopenharmony_ci if (ep->epn.is_in) 7062306a36Sopenharmony_ci vhub_dma_workaround(req->req.buf); 7162306a36Sopenharmony_ci writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Start DMA */ 7562306a36Sopenharmony_ci req->active = true; 7662306a36Sopenharmony_ci writel(VHUB_EP_DMA_SET_TX_SIZE(chunk), 7762306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 7862306a36Sopenharmony_ci writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK, 7962306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct ast_vhub_req *req; 8562306a36Sopenharmony_ci unsigned int len; 8662306a36Sopenharmony_ci int status = 0; 8762306a36Sopenharmony_ci u32 stat; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Read EP status */ 9062306a36Sopenharmony_ci stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Grab current request if any */ 9362306a36Sopenharmony_ci req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n", 9662306a36Sopenharmony_ci stat, ep->epn.is_in, req, req ? req->active : 0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* In absence of a request, bail out, must have been dequeued */ 9962306a36Sopenharmony_ci if (!req) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * Request not active, move on to processing queue, active request 10462306a36Sopenharmony_ci * was probably dequeued 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (!req->active) 10762306a36Sopenharmony_ci goto next_chunk; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Check if HW has moved on */ 11062306a36Sopenharmony_ci if (VHUB_EP_DMA_RPTR(stat) != 0) { 11162306a36Sopenharmony_ci EPDBG(ep, "DMA read pointer not 0 !\n"); 11262306a36Sopenharmony_ci return; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* No current DMA ongoing */ 11662306a36Sopenharmony_ci req->active = false; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Grab length out of HW */ 11962306a36Sopenharmony_ci len = VHUB_EP_DMA_TX_SIZE(stat); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* If not using DMA, copy data out if needed */ 12262306a36Sopenharmony_ci if (!req->req.dma && !ep->epn.is_in && len) { 12362306a36Sopenharmony_ci if (req->req.actual + len > req->req.length) { 12462306a36Sopenharmony_ci req->last_desc = 1; 12562306a36Sopenharmony_ci status = -EOVERFLOW; 12662306a36Sopenharmony_ci goto done; 12762306a36Sopenharmony_ci } else { 12862306a36Sopenharmony_ci memcpy(req->req.buf + req->req.actual, ep->buf, len); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci /* Adjust size */ 13262306a36Sopenharmony_ci req->req.actual += len; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Check for short packet */ 13562306a36Sopenharmony_ci if (len < ep->ep.maxpacket) 13662306a36Sopenharmony_ci req->last_desc = 1; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cidone: 13962306a36Sopenharmony_ci /* That's it ? complete the request and pick a new one */ 14062306a36Sopenharmony_ci if (req->last_desc >= 0) { 14162306a36Sopenharmony_ci ast_vhub_done(ep, req, status); 14262306a36Sopenharmony_ci req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, 14362306a36Sopenharmony_ci queue); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * Due to lock dropping inside "done" the next request could 14762306a36Sopenharmony_ci * already be active, so check for that and bail if needed. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (!req || req->active) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci next_chunk: 15462306a36Sopenharmony_ci ast_vhub_epn_kick(ep, req); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * d_next == d_last means descriptor list empty to HW, 16162306a36Sopenharmony_ci * thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors 16262306a36Sopenharmony_ci * in the list 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) & 16562306a36Sopenharmony_ci (AST_VHUB_DESCS_COUNT - 1); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep, 16962306a36Sopenharmony_ci struct ast_vhub_req *req) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct ast_vhub_desc *desc = NULL; 17262306a36Sopenharmony_ci unsigned int act = req->act_count; 17362306a36Sopenharmony_ci unsigned int len = req->req.length; 17462306a36Sopenharmony_ci unsigned int chunk; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Mark request active if not already */ 17762306a36Sopenharmony_ci req->active = true; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* If the request was already completely written, do nothing */ 18062306a36Sopenharmony_ci if (req->last_desc >= 0) 18162306a36Sopenharmony_ci return; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n", 18462306a36Sopenharmony_ci act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep)); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* While we can create descriptors */ 18762306a36Sopenharmony_ci while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) { 18862306a36Sopenharmony_ci unsigned int d_num; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Grab next free descriptor */ 19162306a36Sopenharmony_ci d_num = ep->epn.d_next; 19262306a36Sopenharmony_ci desc = &ep->epn.descs[d_num]; 19362306a36Sopenharmony_ci ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Calculate next chunk size */ 19662306a36Sopenharmony_ci chunk = len - act; 19762306a36Sopenharmony_ci if (chunk <= ep->epn.chunk_max) { 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * Is this the last packet ? Because of having up to 8 20062306a36Sopenharmony_ci * packets in a descriptor we can't just compare "chunk" 20162306a36Sopenharmony_ci * with ep.maxpacket. We have to see if it's a multiple 20262306a36Sopenharmony_ci * of it to know if we have to send a zero packet. 20362306a36Sopenharmony_ci * Sadly that involves a modulo which is a bit expensive 20462306a36Sopenharmony_ci * but probably still better than not doing it. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0) 20762306a36Sopenharmony_ci req->last_desc = d_num; 20862306a36Sopenharmony_ci } else { 20962306a36Sopenharmony_ci chunk = ep->epn.chunk_max; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n", 21362306a36Sopenharmony_ci act, len, chunk, req->last_desc, d_num, 21462306a36Sopenharmony_ci ast_vhub_count_free_descs(ep)); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Populate descriptor */ 21762306a36Sopenharmony_ci desc->w0 = cpu_to_le32(req->req.dma + act); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Interrupt if end of request or no more descriptors */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * TODO: Be smarter about it, if we don't have enough 22362306a36Sopenharmony_ci * descriptors request an interrupt before queue empty 22462306a36Sopenharmony_ci * or so in order to be able to populate more before 22562306a36Sopenharmony_ci * the HW runs out. This isn't a problem at the moment 22662306a36Sopenharmony_ci * as we use 256 descriptors and only put at most one 22762306a36Sopenharmony_ci * request in the ring. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk)); 23062306a36Sopenharmony_ci if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep)) 23162306a36Sopenharmony_ci desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Account packet */ 23462306a36Sopenharmony_ci req->act_count = act = act + chunk; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (likely(desc)) 23862306a36Sopenharmony_ci vhub_dma_workaround(desc); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Tell HW about new descriptors */ 24162306a36Sopenharmony_ci writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next), 24262306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n", 24562306a36Sopenharmony_ci ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS)); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct ast_vhub_req *req; 25162306a36Sopenharmony_ci unsigned int len, d_last; 25262306a36Sopenharmony_ci u32 stat, stat1; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Read EP status, workaround HW race */ 25562306a36Sopenharmony_ci do { 25662306a36Sopenharmony_ci stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 25762306a36Sopenharmony_ci stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 25862306a36Sopenharmony_ci } while(stat != stat1); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Extract RPTR */ 26162306a36Sopenharmony_ci d_last = VHUB_EP_DMA_RPTR(stat); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Grab current request if any */ 26462306a36Sopenharmony_ci req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n", 26762306a36Sopenharmony_ci stat, ep->epn.is_in, ep->epn.d_last, d_last); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Check all completed descriptors */ 27062306a36Sopenharmony_ci while (ep->epn.d_last != d_last) { 27162306a36Sopenharmony_ci struct ast_vhub_desc *desc; 27262306a36Sopenharmony_ci unsigned int d_num; 27362306a36Sopenharmony_ci bool is_last_desc; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Grab next completed descriptor */ 27662306a36Sopenharmony_ci d_num = ep->epn.d_last; 27762306a36Sopenharmony_ci desc = &ep->epn.descs[d_num]; 27862306a36Sopenharmony_ci ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Grab len out of descriptor */ 28162306a36Sopenharmony_ci len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1)); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n", 28462306a36Sopenharmony_ci d_num, len, req, req ? req->active : 0); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* If no active request pending, move on */ 28762306a36Sopenharmony_ci if (!req || !req->active) 28862306a36Sopenharmony_ci continue; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Adjust size */ 29162306a36Sopenharmony_ci req->req.actual += len; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Is that the last chunk ? */ 29462306a36Sopenharmony_ci is_last_desc = req->last_desc == d_num; 29562306a36Sopenharmony_ci CHECK(ep, is_last_desc == (len < ep->ep.maxpacket || 29662306a36Sopenharmony_ci (req->req.actual >= req->req.length && 29762306a36Sopenharmony_ci !req->req.zero)), 29862306a36Sopenharmony_ci "Last packet discrepancy: last_desc=%d len=%d r.act=%d " 29962306a36Sopenharmony_ci "r.len=%d r.zero=%d mp=%d\n", 30062306a36Sopenharmony_ci is_last_desc, len, req->req.actual, req->req.length, 30162306a36Sopenharmony_ci req->req.zero, ep->ep.maxpacket); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (is_last_desc) { 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * Because we can only have one request at a time 30662306a36Sopenharmony_ci * in our descriptor list in this implementation, 30762306a36Sopenharmony_ci * d_last and ep->d_last should now be equal 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci CHECK(ep, d_last == ep->epn.d_last, 31062306a36Sopenharmony_ci "DMA read ptr mismatch %d vs %d\n", 31162306a36Sopenharmony_ci d_last, ep->epn.d_last); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Note: done will drop and re-acquire the lock */ 31462306a36Sopenharmony_ci ast_vhub_done(ep, req, 0); 31562306a36Sopenharmony_ci req = list_first_entry_or_null(&ep->queue, 31662306a36Sopenharmony_ci struct ast_vhub_req, 31762306a36Sopenharmony_ci queue); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* More work ? */ 32362306a36Sopenharmony_ci if (req) 32462306a36Sopenharmony_ci ast_vhub_epn_kick_desc(ep, req); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_civoid ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci if (ep->epn.desc_mode) 33062306a36Sopenharmony_ci ast_vhub_epn_handle_ack_desc(ep); 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci ast_vhub_epn_handle_ack(ep); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req, 33662306a36Sopenharmony_ci gfp_t gfp_flags) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct ast_vhub_req *req = to_ast_req(u_req); 33962306a36Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 34062306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 34162306a36Sopenharmony_ci unsigned long flags; 34262306a36Sopenharmony_ci bool empty; 34362306a36Sopenharmony_ci int rc; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Paranoid checks */ 34662306a36Sopenharmony_ci if (!u_req || !u_req->complete || !u_req->buf) { 34762306a36Sopenharmony_ci dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req); 34862306a36Sopenharmony_ci if (u_req) { 34962306a36Sopenharmony_ci dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n", 35062306a36Sopenharmony_ci u_req->complete, req->internal); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Endpoint enabled ? */ 35662306a36Sopenharmony_ci if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx || 35762306a36Sopenharmony_ci !ep->dev->enabled) { 35862306a36Sopenharmony_ci EPDBG(ep, "Enqueuing request on wrong or disabled EP\n"); 35962306a36Sopenharmony_ci return -ESHUTDOWN; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Map request for DMA if possible. For now, the rule for DMA is 36362306a36Sopenharmony_ci * that: 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * * For single stage mode (no descriptors): 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * - The buffer is aligned to a 8 bytes boundary (HW requirement) 36862306a36Sopenharmony_ci * - For a OUT endpoint, the request size is a multiple of the EP 36962306a36Sopenharmony_ci * packet size (otherwise the controller will DMA past the end 37062306a36Sopenharmony_ci * of the buffer if the host is sending a too long packet). 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * * For descriptor mode (tx only for now), always. 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * We could relax the latter by making the decision to use the bounce 37562306a36Sopenharmony_ci * buffer based on the size of a given *segment* of the request rather 37662306a36Sopenharmony_ci * than the whole request. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci if (ep->epn.desc_mode || 37962306a36Sopenharmony_ci ((((unsigned long)u_req->buf & 7) == 0) && 38062306a36Sopenharmony_ci (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) { 38162306a36Sopenharmony_ci rc = usb_gadget_map_request_by_dev(&vhub->pdev->dev, u_req, 38262306a36Sopenharmony_ci ep->epn.is_in); 38362306a36Sopenharmony_ci if (rc) { 38462306a36Sopenharmony_ci dev_warn(&vhub->pdev->dev, 38562306a36Sopenharmony_ci "Request mapping failure %d\n", rc); 38662306a36Sopenharmony_ci return rc; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } else 38962306a36Sopenharmony_ci u_req->dma = 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci EPVDBG(ep, "enqueue req @%p\n", req); 39262306a36Sopenharmony_ci EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n", 39362306a36Sopenharmony_ci u_req->length, (u32)u_req->dma, u_req->zero, 39462306a36Sopenharmony_ci u_req->short_not_ok, u_req->no_interrupt, 39562306a36Sopenharmony_ci ep->epn.is_in); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Initialize request progress fields */ 39862306a36Sopenharmony_ci u_req->status = -EINPROGRESS; 39962306a36Sopenharmony_ci u_req->actual = 0; 40062306a36Sopenharmony_ci req->act_count = 0; 40162306a36Sopenharmony_ci req->active = false; 40262306a36Sopenharmony_ci req->last_desc = -1; 40362306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 40462306a36Sopenharmony_ci empty = list_empty(&ep->queue); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Add request to list and kick processing if empty */ 40762306a36Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 40862306a36Sopenharmony_ci if (empty) { 40962306a36Sopenharmony_ci if (ep->epn.desc_mode) 41062306a36Sopenharmony_ci ast_vhub_epn_kick_desc(ep, req); 41162306a36Sopenharmony_ci else 41262306a36Sopenharmony_ci ast_vhub_epn_kick(ep, req); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void ast_vhub_stop_active_req(struct ast_vhub_ep *ep, 42062306a36Sopenharmony_ci bool restart_ep) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci u32 state, reg, loops; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Stop DMA activity */ 42562306a36Sopenharmony_ci if (ep->epn.desc_mode) 42662306a36Sopenharmony_ci writel(VHUB_EP_DMA_CTRL_RESET, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 42762306a36Sopenharmony_ci else 42862306a36Sopenharmony_ci writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Wait for it to complete */ 43162306a36Sopenharmony_ci for (loops = 0; loops < 1000; loops++) { 43262306a36Sopenharmony_ci state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 43362306a36Sopenharmony_ci state = VHUB_EP_DMA_PROC_STATUS(state); 43462306a36Sopenharmony_ci if (state == EP_DMA_PROC_RX_IDLE || 43562306a36Sopenharmony_ci state == EP_DMA_PROC_TX_IDLE) 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci udelay(1); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci if (loops >= 1000) 44062306a36Sopenharmony_ci dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n"); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* If we don't have to restart the endpoint, that's it */ 44362306a36Sopenharmony_ci if (!restart_ep) 44462306a36Sopenharmony_ci return; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Restart the endpoint */ 44762306a36Sopenharmony_ci if (ep->epn.desc_mode) { 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * Take out descriptors by resetting the DMA read 45062306a36Sopenharmony_ci * pointer to be equal to the CPU write pointer. 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * Note: If we ever support creating descriptors for 45362306a36Sopenharmony_ci * requests that aren't the head of the queue, we 45462306a36Sopenharmony_ci * may have to do something more complex here, 45562306a36Sopenharmony_ci * especially if the request being taken out is 45662306a36Sopenharmony_ci * not the current head descriptors. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) | 45962306a36Sopenharmony_ci VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next); 46062306a36Sopenharmony_ci writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Then turn it back on */ 46362306a36Sopenharmony_ci writel(ep->epn.dma_conf, 46462306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci /* Single mode: just turn it back on */ 46762306a36Sopenharmony_ci writel(ep->epn.dma_conf, 46862306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 47562306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 47662306a36Sopenharmony_ci struct ast_vhub_req *req = NULL, *iter; 47762306a36Sopenharmony_ci unsigned long flags; 47862306a36Sopenharmony_ci int rc = -EINVAL; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Make sure it's actually queued on this endpoint */ 48362306a36Sopenharmony_ci list_for_each_entry(iter, &ep->queue, queue) { 48462306a36Sopenharmony_ci if (&iter->req != u_req) 48562306a36Sopenharmony_ci continue; 48662306a36Sopenharmony_ci req = iter; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (req) { 49162306a36Sopenharmony_ci EPVDBG(ep, "dequeue req @%p active=%d\n", 49262306a36Sopenharmony_ci req, req->active); 49362306a36Sopenharmony_ci if (req->active) 49462306a36Sopenharmony_ci ast_vhub_stop_active_req(ep, true); 49562306a36Sopenharmony_ci ast_vhub_done(ep, req, -ECONNRESET); 49662306a36Sopenharmony_ci rc = 0; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 50062306a36Sopenharmony_ci return rc; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_civoid ast_vhub_update_epn_stall(struct ast_vhub_ep *ep) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci u32 reg; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (WARN_ON(ep->d_idx == 0)) 50862306a36Sopenharmony_ci return; 50962306a36Sopenharmony_ci reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG); 51062306a36Sopenharmony_ci if (ep->epn.stalled || ep->epn.wedged) 51162306a36Sopenharmony_ci reg |= VHUB_EP_CFG_STALL_CTRL; 51262306a36Sopenharmony_ci else 51362306a36Sopenharmony_ci reg &= ~VHUB_EP_CFG_STALL_CTRL; 51462306a36Sopenharmony_ci writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (!ep->epn.stalled && !ep->epn.wedged) 51762306a36Sopenharmony_ci writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx), 51862306a36Sopenharmony_ci ep->vhub->regs + AST_VHUB_EP_TOGGLE); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt, 52262306a36Sopenharmony_ci bool wedge) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 52562306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 52662306a36Sopenharmony_ci unsigned long flags; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!u_ep || !u_ep->desc) 53162306a36Sopenharmony_ci return -EINVAL; 53262306a36Sopenharmony_ci if (ep->d_idx == 0) 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci if (ep->epn.is_iso) 53562306a36Sopenharmony_ci return -EOPNOTSUPP; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Fail with still-busy IN endpoints */ 54062306a36Sopenharmony_ci if (halt && ep->epn.is_in && !list_empty(&ep->queue)) { 54162306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 54262306a36Sopenharmony_ci return -EAGAIN; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci ep->epn.stalled = halt; 54562306a36Sopenharmony_ci ep->epn.wedged = wedge; 54662306a36Sopenharmony_ci ast_vhub_update_epn_stall(ep); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int ast_vhub_epn_set_wedge(struct usb_ep *u_ep) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci return ast_vhub_set_halt_and_wedge(u_ep, true, true); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int ast_vhub_epn_disable(struct usb_ep* u_ep) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 56662306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 56762306a36Sopenharmony_ci unsigned long flags; 56862306a36Sopenharmony_ci u32 imask, ep_ier; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci EPDBG(ep, "Disabling !\n"); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci ep->epn.enabled = false; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Stop active DMA if any */ 57762306a36Sopenharmony_ci ast_vhub_stop_active_req(ep, false); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Disable endpoint */ 58062306a36Sopenharmony_ci writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Disable ACK interrupt */ 58362306a36Sopenharmony_ci imask = VHUB_EP_IRQ(ep->epn.g_idx); 58462306a36Sopenharmony_ci ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER); 58562306a36Sopenharmony_ci ep_ier &= ~imask; 58662306a36Sopenharmony_ci writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER); 58762306a36Sopenharmony_ci writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Nuke all pending requests */ 59062306a36Sopenharmony_ci ast_vhub_nuke(ep, -ESHUTDOWN); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* No more descriptor associated with request */ 59362306a36Sopenharmony_ci ep->ep.desc = NULL; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int ast_vhub_epn_enable(struct usb_ep* u_ep, 60162306a36Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 60462306a36Sopenharmony_ci struct ast_vhub_dev *dev; 60562306a36Sopenharmony_ci struct ast_vhub *vhub; 60662306a36Sopenharmony_ci u16 maxpacket, type; 60762306a36Sopenharmony_ci unsigned long flags; 60862306a36Sopenharmony_ci u32 ep_conf, ep_ier, imask; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* Check arguments */ 61162306a36Sopenharmony_ci if (!u_ep || !desc) 61262306a36Sopenharmony_ci return -EINVAL; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci maxpacket = usb_endpoint_maxp(desc); 61562306a36Sopenharmony_ci if (!ep->d_idx || !ep->dev || 61662306a36Sopenharmony_ci desc->bDescriptorType != USB_DT_ENDPOINT || 61762306a36Sopenharmony_ci maxpacket == 0 || maxpacket > ep->ep.maxpacket) { 61862306a36Sopenharmony_ci EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n", 61962306a36Sopenharmony_ci ep->d_idx, ep->dev, desc->bDescriptorType, 62062306a36Sopenharmony_ci maxpacket, ep->ep.maxpacket); 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci if (ep->d_idx != usb_endpoint_num(desc)) { 62462306a36Sopenharmony_ci EPDBG(ep, "EP number mismatch !\n"); 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (ep->epn.enabled) { 62962306a36Sopenharmony_ci EPDBG(ep, "Already enabled\n"); 63062306a36Sopenharmony_ci return -EBUSY; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci dev = ep->dev; 63362306a36Sopenharmony_ci vhub = ep->vhub; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Check device state */ 63662306a36Sopenharmony_ci if (!dev->driver) { 63762306a36Sopenharmony_ci EPDBG(ep, "Bogus device state: driver=%p speed=%d\n", 63862306a36Sopenharmony_ci dev->driver, dev->gadget.speed); 63962306a36Sopenharmony_ci return -ESHUTDOWN; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* Grab some info from the descriptor */ 64362306a36Sopenharmony_ci ep->epn.is_in = usb_endpoint_dir_in(desc); 64462306a36Sopenharmony_ci ep->ep.maxpacket = maxpacket; 64562306a36Sopenharmony_ci type = usb_endpoint_type(desc); 64662306a36Sopenharmony_ci ep->epn.d_next = ep->epn.d_last = 0; 64762306a36Sopenharmony_ci ep->epn.is_iso = false; 64862306a36Sopenharmony_ci ep->epn.stalled = false; 64962306a36Sopenharmony_ci ep->epn.wedged = false; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n", 65262306a36Sopenharmony_ci ep->epn.is_in ? "in" : "out", usb_ep_type_string(type), 65362306a36Sopenharmony_ci usb_endpoint_num(desc), maxpacket); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* Can we use DMA descriptor mode ? */ 65662306a36Sopenharmony_ci ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in; 65762306a36Sopenharmony_ci if (ep->epn.desc_mode) 65862306a36Sopenharmony_ci memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* 66162306a36Sopenharmony_ci * Large send function can send up to 8 packets from 66262306a36Sopenharmony_ci * one descriptor with a limit of 4095 bytes. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci ep->epn.chunk_max = ep->ep.maxpacket; 66562306a36Sopenharmony_ci if (ep->epn.is_in) { 66662306a36Sopenharmony_ci ep->epn.chunk_max <<= 3; 66762306a36Sopenharmony_ci while (ep->epn.chunk_max > 4095) 66862306a36Sopenharmony_ci ep->epn.chunk_max -= ep->ep.maxpacket; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci switch(type) { 67262306a36Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 67362306a36Sopenharmony_ci EPDBG(ep, "Only one control endpoint\n"); 67462306a36Sopenharmony_ci return -EINVAL; 67562306a36Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 67662306a36Sopenharmony_ci ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 67962306a36Sopenharmony_ci ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK); 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 68262306a36Sopenharmony_ci ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO); 68362306a36Sopenharmony_ci ep->epn.is_iso = true; 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci default: 68662306a36Sopenharmony_ci return -EINVAL; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Encode the rest of the EP config register */ 69062306a36Sopenharmony_ci if (maxpacket < 1024) 69162306a36Sopenharmony_ci ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket); 69262306a36Sopenharmony_ci if (!ep->epn.is_in) 69362306a36Sopenharmony_ci ep_conf |= VHUB_EP_CFG_DIR_OUT; 69462306a36Sopenharmony_ci ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc)); 69562306a36Sopenharmony_ci ep_conf |= VHUB_EP_CFG_ENABLE; 69662306a36Sopenharmony_ci ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1); 69762306a36Sopenharmony_ci EPVDBG(ep, "config=%08x\n", ep_conf); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* Disable HW and reset DMA */ 70262306a36Sopenharmony_ci writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG); 70362306a36Sopenharmony_ci writel(VHUB_EP_DMA_CTRL_RESET, 70462306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* Configure and enable */ 70762306a36Sopenharmony_ci writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (ep->epn.desc_mode) { 71062306a36Sopenharmony_ci /* Clear DMA status, including the DMA read ptr */ 71162306a36Sopenharmony_ci writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Set descriptor base */ 71462306a36Sopenharmony_ci writel(ep->epn.descs_dma, 71562306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DESC_BASE); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* Set base DMA config value */ 71862306a36Sopenharmony_ci ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE; 71962306a36Sopenharmony_ci if (ep->epn.is_in) 72062306a36Sopenharmony_ci ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* First reset and disable all operations */ 72362306a36Sopenharmony_ci writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET, 72462306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Enable descriptor mode */ 72762306a36Sopenharmony_ci writel(ep->epn.dma_conf, 72862306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 72962306a36Sopenharmony_ci } else { 73062306a36Sopenharmony_ci /* Set base DMA config value */ 73162306a36Sopenharmony_ci ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Reset and switch to single stage mode */ 73462306a36Sopenharmony_ci writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET, 73562306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 73662306a36Sopenharmony_ci writel(ep->epn.dma_conf, 73762306a36Sopenharmony_ci ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 73862306a36Sopenharmony_ci writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Cleanup data toggle just in case */ 74262306a36Sopenharmony_ci writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx), 74362306a36Sopenharmony_ci vhub->regs + AST_VHUB_EP_TOGGLE); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Cleanup and enable ACK interrupt */ 74662306a36Sopenharmony_ci imask = VHUB_EP_IRQ(ep->epn.g_idx); 74762306a36Sopenharmony_ci writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR); 74862306a36Sopenharmony_ci ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER); 74962306a36Sopenharmony_ci ep_ier |= imask; 75062306a36Sopenharmony_ci writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Woot, we are online ! */ 75362306a36Sopenharmony_ci ep->epn.enabled = true; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic void ast_vhub_epn_dispose(struct usb_ep *u_ep) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (WARN_ON(!ep->dev || !ep->d_idx)) 76562306a36Sopenharmony_ci return; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci EPDBG(ep, "Releasing endpoint\n"); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Take it out of the EP list */ 77062306a36Sopenharmony_ci list_del_init(&ep->ep.ep_list); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Mark the address free in the device */ 77362306a36Sopenharmony_ci ep->dev->epns[ep->d_idx - 1] = NULL; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Free name & DMA buffers */ 77662306a36Sopenharmony_ci kfree(ep->ep.name); 77762306a36Sopenharmony_ci ep->ep.name = NULL; 77862306a36Sopenharmony_ci dma_free_coherent(&ep->vhub->pdev->dev, 77962306a36Sopenharmony_ci AST_VHUB_EPn_MAX_PACKET + 78062306a36Sopenharmony_ci 8 * AST_VHUB_DESCS_COUNT, 78162306a36Sopenharmony_ci ep->buf, ep->buf_dma); 78262306a36Sopenharmony_ci ep->buf = NULL; 78362306a36Sopenharmony_ci ep->epn.descs = NULL; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Mark free */ 78662306a36Sopenharmony_ci ep->dev = NULL; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic const struct usb_ep_ops ast_vhub_epn_ops = { 79062306a36Sopenharmony_ci .enable = ast_vhub_epn_enable, 79162306a36Sopenharmony_ci .disable = ast_vhub_epn_disable, 79262306a36Sopenharmony_ci .dispose = ast_vhub_epn_dispose, 79362306a36Sopenharmony_ci .queue = ast_vhub_epn_queue, 79462306a36Sopenharmony_ci .dequeue = ast_vhub_epn_dequeue, 79562306a36Sopenharmony_ci .set_halt = ast_vhub_epn_set_halt, 79662306a36Sopenharmony_ci .set_wedge = ast_vhub_epn_set_wedge, 79762306a36Sopenharmony_ci .alloc_request = ast_vhub_alloc_request, 79862306a36Sopenharmony_ci .free_request = ast_vhub_free_request, 79962306a36Sopenharmony_ci}; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistruct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct ast_vhub *vhub = d->vhub; 80462306a36Sopenharmony_ci struct ast_vhub_ep *ep; 80562306a36Sopenharmony_ci unsigned long flags; 80662306a36Sopenharmony_ci int i; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Find a free one (no device) */ 80962306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 81062306a36Sopenharmony_ci for (i = 0; i < vhub->max_epns; i++) 81162306a36Sopenharmony_ci if (vhub->epns[i].dev == NULL) 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci if (i >= vhub->max_epns) { 81462306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 81562306a36Sopenharmony_ci return NULL; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Set it up */ 81962306a36Sopenharmony_ci ep = &vhub->epns[i]; 82062306a36Sopenharmony_ci ep->dev = d; 82162306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr); 82462306a36Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 82562306a36Sopenharmony_ci ep->d_idx = addr; 82662306a36Sopenharmony_ci ep->vhub = vhub; 82762306a36Sopenharmony_ci ep->ep.ops = &ast_vhub_epn_ops; 82862306a36Sopenharmony_ci ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr); 82962306a36Sopenharmony_ci d->epns[addr-1] = ep; 83062306a36Sopenharmony_ci ep->epn.g_idx = i; 83162306a36Sopenharmony_ci ep->epn.regs = vhub->regs + 0x200 + (i * 0x10); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ep->buf = dma_alloc_coherent(&vhub->pdev->dev, 83462306a36Sopenharmony_ci AST_VHUB_EPn_MAX_PACKET + 83562306a36Sopenharmony_ci 8 * AST_VHUB_DESCS_COUNT, 83662306a36Sopenharmony_ci &ep->buf_dma, GFP_KERNEL); 83762306a36Sopenharmony_ci if (!ep->buf) { 83862306a36Sopenharmony_ci kfree(ep->ep.name); 83962306a36Sopenharmony_ci ep->ep.name = NULL; 84062306a36Sopenharmony_ci return NULL; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET; 84362306a36Sopenharmony_ci ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET); 84662306a36Sopenharmony_ci list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list); 84762306a36Sopenharmony_ci ep->ep.caps.type_iso = true; 84862306a36Sopenharmony_ci ep->ep.caps.type_bulk = true; 84962306a36Sopenharmony_ci ep->ep.caps.type_int = true; 85062306a36Sopenharmony_ci ep->ep.caps.dir_in = true; 85162306a36Sopenharmony_ci ep->ep.caps.dir_out = true; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return ep; 85462306a36Sopenharmony_ci} 855