17ecca2a4SBenjamin Herrenschmidt // SPDX-License-Identifier: GPL-2.0+ 27ecca2a4SBenjamin Herrenschmidt /* 37ecca2a4SBenjamin Herrenschmidt * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget 47ecca2a4SBenjamin Herrenschmidt * 57ecca2a4SBenjamin Herrenschmidt * epn.c - Generic endpoints management 67ecca2a4SBenjamin Herrenschmidt * 77ecca2a4SBenjamin Herrenschmidt * Copyright 2017 IBM Corporation 87ecca2a4SBenjamin Herrenschmidt * 97ecca2a4SBenjamin Herrenschmidt * This program is free software; you can redistribute it and/or modify 107ecca2a4SBenjamin Herrenschmidt * it under the terms of the GNU General Public License as published by 117ecca2a4SBenjamin Herrenschmidt * the Free Software Foundation; either version 2 of the License, or 127ecca2a4SBenjamin Herrenschmidt * (at your option) any later version. 137ecca2a4SBenjamin Herrenschmidt */ 147ecca2a4SBenjamin Herrenschmidt 157ecca2a4SBenjamin Herrenschmidt #include <linux/kernel.h> 167ecca2a4SBenjamin Herrenschmidt #include <linux/module.h> 177ecca2a4SBenjamin Herrenschmidt #include <linux/platform_device.h> 187ecca2a4SBenjamin Herrenschmidt #include <linux/delay.h> 197ecca2a4SBenjamin Herrenschmidt #include <linux/ioport.h> 207ecca2a4SBenjamin Herrenschmidt #include <linux/slab.h> 217ecca2a4SBenjamin Herrenschmidt #include <linux/errno.h> 227ecca2a4SBenjamin Herrenschmidt #include <linux/list.h> 237ecca2a4SBenjamin Herrenschmidt #include <linux/interrupt.h> 247ecca2a4SBenjamin Herrenschmidt #include <linux/proc_fs.h> 257ecca2a4SBenjamin Herrenschmidt #include <linux/prefetch.h> 267ecca2a4SBenjamin Herrenschmidt #include <linux/clk.h> 277ecca2a4SBenjamin Herrenschmidt #include <linux/usb/gadget.h> 287ecca2a4SBenjamin Herrenschmidt #include <linux/of.h> 297ecca2a4SBenjamin Herrenschmidt #include <linux/of_gpio.h> 307ecca2a4SBenjamin Herrenschmidt #include <linux/regmap.h> 317ecca2a4SBenjamin Herrenschmidt #include <linux/dma-mapping.h> 327ecca2a4SBenjamin Herrenschmidt 337ecca2a4SBenjamin Herrenschmidt #include "vhub.h" 347ecca2a4SBenjamin Herrenschmidt 357ecca2a4SBenjamin Herrenschmidt #define EXTRA_CHECKS 367ecca2a4SBenjamin Herrenschmidt 377ecca2a4SBenjamin Herrenschmidt #ifdef EXTRA_CHECKS 387ecca2a4SBenjamin Herrenschmidt #define CHECK(ep, expr, fmt...) \ 397ecca2a4SBenjamin Herrenschmidt do { \ 407ecca2a4SBenjamin Herrenschmidt if (!(expr)) EPDBG(ep, "CHECK:" fmt); \ 417ecca2a4SBenjamin Herrenschmidt } while(0) 427ecca2a4SBenjamin Herrenschmidt #else 437ecca2a4SBenjamin Herrenschmidt #define CHECK(ep, expr, fmt...) do { } while(0) 447ecca2a4SBenjamin Herrenschmidt #endif 457ecca2a4SBenjamin Herrenschmidt 467ecca2a4SBenjamin Herrenschmidt static void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req) 477ecca2a4SBenjamin Herrenschmidt { 487ecca2a4SBenjamin Herrenschmidt unsigned int act = req->req.actual; 497ecca2a4SBenjamin Herrenschmidt unsigned int len = req->req.length; 507ecca2a4SBenjamin Herrenschmidt unsigned int chunk; 517ecca2a4SBenjamin Herrenschmidt 527ecca2a4SBenjamin Herrenschmidt /* There should be no DMA ongoing */ 537ecca2a4SBenjamin Herrenschmidt WARN_ON(req->active); 547ecca2a4SBenjamin Herrenschmidt 557ecca2a4SBenjamin Herrenschmidt /* Calculate next chunk size */ 567ecca2a4SBenjamin Herrenschmidt chunk = len - act; 577ecca2a4SBenjamin Herrenschmidt if (chunk > ep->ep.maxpacket) 587ecca2a4SBenjamin Herrenschmidt chunk = ep->ep.maxpacket; 597ecca2a4SBenjamin Herrenschmidt else if ((chunk < ep->ep.maxpacket) || !req->req.zero) 607ecca2a4SBenjamin Herrenschmidt req->last_desc = 1; 617ecca2a4SBenjamin Herrenschmidt 627ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n", 637ecca2a4SBenjamin Herrenschmidt req, act, len, chunk, req->last_desc); 647ecca2a4SBenjamin Herrenschmidt 657ecca2a4SBenjamin Herrenschmidt /* If DMA unavailable, using staging EP buffer */ 667ecca2a4SBenjamin Herrenschmidt if (!req->req.dma) { 677ecca2a4SBenjamin Herrenschmidt 687ecca2a4SBenjamin Herrenschmidt /* For IN transfers, copy data over first */ 69bb286336SBenjamin Herrenschmidt if (ep->epn.is_in) { 707ecca2a4SBenjamin Herrenschmidt memcpy(ep->buf, req->req.buf + act, chunk); 71bb286336SBenjamin Herrenschmidt vhub_dma_workaround(ep->buf); 72bb286336SBenjamin Herrenschmidt } 737ecca2a4SBenjamin Herrenschmidt writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE); 74bb286336SBenjamin Herrenschmidt } else { 75bb286336SBenjamin Herrenschmidt if (ep->epn.is_in) 76bb286336SBenjamin Herrenschmidt vhub_dma_workaround(req->req.buf); 777ecca2a4SBenjamin Herrenschmidt writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE); 78bb286336SBenjamin Herrenschmidt } 797ecca2a4SBenjamin Herrenschmidt 807ecca2a4SBenjamin Herrenschmidt /* Start DMA */ 817ecca2a4SBenjamin Herrenschmidt req->active = true; 827ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP_DMA_SET_TX_SIZE(chunk), 837ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 847ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK, 857ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 867ecca2a4SBenjamin Herrenschmidt } 877ecca2a4SBenjamin Herrenschmidt 887ecca2a4SBenjamin Herrenschmidt static void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep) 897ecca2a4SBenjamin Herrenschmidt { 907ecca2a4SBenjamin Herrenschmidt struct ast_vhub_req *req; 917ecca2a4SBenjamin Herrenschmidt unsigned int len; 927ecca2a4SBenjamin Herrenschmidt u32 stat; 937ecca2a4SBenjamin Herrenschmidt 947ecca2a4SBenjamin Herrenschmidt /* Read EP status */ 957ecca2a4SBenjamin Herrenschmidt stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 967ecca2a4SBenjamin Herrenschmidt 977ecca2a4SBenjamin Herrenschmidt /* Grab current request if any */ 987ecca2a4SBenjamin Herrenschmidt req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue); 997ecca2a4SBenjamin Herrenschmidt 1007ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n", 1017ecca2a4SBenjamin Herrenschmidt stat, ep->epn.is_in, req, req ? req->active : 0); 1027ecca2a4SBenjamin Herrenschmidt 1037ecca2a4SBenjamin Herrenschmidt /* In absence of a request, bail out, must have been dequeued */ 1047ecca2a4SBenjamin Herrenschmidt if (!req) 1057ecca2a4SBenjamin Herrenschmidt return; 1067ecca2a4SBenjamin Herrenschmidt 1077ecca2a4SBenjamin Herrenschmidt /* 1087ecca2a4SBenjamin Herrenschmidt * Request not active, move on to processing queue, active request 1097ecca2a4SBenjamin Herrenschmidt * was probably dequeued 1107ecca2a4SBenjamin Herrenschmidt */ 1117ecca2a4SBenjamin Herrenschmidt if (!req->active) 1127ecca2a4SBenjamin Herrenschmidt goto next_chunk; 1137ecca2a4SBenjamin Herrenschmidt 1147ecca2a4SBenjamin Herrenschmidt /* Check if HW has moved on */ 1157ecca2a4SBenjamin Herrenschmidt if (VHUB_EP_DMA_RPTR(stat) != 0) { 1167ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "DMA read pointer not 0 !\n"); 1177ecca2a4SBenjamin Herrenschmidt return; 1187ecca2a4SBenjamin Herrenschmidt } 1197ecca2a4SBenjamin Herrenschmidt 1207ecca2a4SBenjamin Herrenschmidt /* No current DMA ongoing */ 1217ecca2a4SBenjamin Herrenschmidt req->active = false; 1227ecca2a4SBenjamin Herrenschmidt 1234d8cd616SMatteo Croce /* Grab length out of HW */ 1247ecca2a4SBenjamin Herrenschmidt len = VHUB_EP_DMA_TX_SIZE(stat); 1257ecca2a4SBenjamin Herrenschmidt 1267ecca2a4SBenjamin Herrenschmidt /* If not using DMA, copy data out if needed */ 1277ecca2a4SBenjamin Herrenschmidt if (!req->req.dma && !ep->epn.is_in && len) 1287ecca2a4SBenjamin Herrenschmidt memcpy(req->req.buf + req->req.actual, ep->buf, len); 1297ecca2a4SBenjamin Herrenschmidt 1307ecca2a4SBenjamin Herrenschmidt /* Adjust size */ 1317ecca2a4SBenjamin Herrenschmidt req->req.actual += len; 1327ecca2a4SBenjamin Herrenschmidt 1337ecca2a4SBenjamin Herrenschmidt /* Check for short packet */ 1347ecca2a4SBenjamin Herrenschmidt if (len < ep->ep.maxpacket) 1357ecca2a4SBenjamin Herrenschmidt req->last_desc = 1; 1367ecca2a4SBenjamin Herrenschmidt 1377ecca2a4SBenjamin Herrenschmidt /* That's it ? complete the request and pick a new one */ 1387ecca2a4SBenjamin Herrenschmidt if (req->last_desc >= 0) { 1397ecca2a4SBenjamin Herrenschmidt ast_vhub_done(ep, req, 0); 1407ecca2a4SBenjamin Herrenschmidt req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, 1417ecca2a4SBenjamin Herrenschmidt queue); 1427ecca2a4SBenjamin Herrenschmidt 1437ecca2a4SBenjamin Herrenschmidt /* 1447ecca2a4SBenjamin Herrenschmidt * Due to lock dropping inside "done" the next request could 1457ecca2a4SBenjamin Herrenschmidt * already be active, so check for that and bail if needed. 1467ecca2a4SBenjamin Herrenschmidt */ 1477ecca2a4SBenjamin Herrenschmidt if (!req || req->active) 1487ecca2a4SBenjamin Herrenschmidt return; 1497ecca2a4SBenjamin Herrenschmidt } 1507ecca2a4SBenjamin Herrenschmidt 1517ecca2a4SBenjamin Herrenschmidt next_chunk: 1527ecca2a4SBenjamin Herrenschmidt ast_vhub_epn_kick(ep, req); 1537ecca2a4SBenjamin Herrenschmidt } 1547ecca2a4SBenjamin Herrenschmidt 1557ecca2a4SBenjamin Herrenschmidt static inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep) 1567ecca2a4SBenjamin Herrenschmidt { 1577ecca2a4SBenjamin Herrenschmidt /* 1587ecca2a4SBenjamin Herrenschmidt * d_next == d_last means descriptor list empty to HW, 1597ecca2a4SBenjamin Herrenschmidt * thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors 1607ecca2a4SBenjamin Herrenschmidt * in the list 1617ecca2a4SBenjamin Herrenschmidt */ 1627ecca2a4SBenjamin Herrenschmidt return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) & 1637ecca2a4SBenjamin Herrenschmidt (AST_VHUB_DESCS_COUNT - 1); 1647ecca2a4SBenjamin Herrenschmidt } 1657ecca2a4SBenjamin Herrenschmidt 1667ecca2a4SBenjamin Herrenschmidt static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep, 1677ecca2a4SBenjamin Herrenschmidt struct ast_vhub_req *req) 1687ecca2a4SBenjamin Herrenschmidt { 169bb286336SBenjamin Herrenschmidt struct ast_vhub_desc *desc = NULL; 1707ecca2a4SBenjamin Herrenschmidt unsigned int act = req->act_count; 1717ecca2a4SBenjamin Herrenschmidt unsigned int len = req->req.length; 1727ecca2a4SBenjamin Herrenschmidt unsigned int chunk; 1737ecca2a4SBenjamin Herrenschmidt 1747ecca2a4SBenjamin Herrenschmidt /* Mark request active if not already */ 1757ecca2a4SBenjamin Herrenschmidt req->active = true; 1767ecca2a4SBenjamin Herrenschmidt 1777ecca2a4SBenjamin Herrenschmidt /* If the request was already completely written, do nothing */ 1787ecca2a4SBenjamin Herrenschmidt if (req->last_desc >= 0) 1797ecca2a4SBenjamin Herrenschmidt return; 1807ecca2a4SBenjamin Herrenschmidt 1817ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n", 1827ecca2a4SBenjamin Herrenschmidt act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep)); 1837ecca2a4SBenjamin Herrenschmidt 1847ecca2a4SBenjamin Herrenschmidt /* While we can create descriptors */ 1857ecca2a4SBenjamin Herrenschmidt while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) { 1867ecca2a4SBenjamin Herrenschmidt unsigned int d_num; 1877ecca2a4SBenjamin Herrenschmidt 1887ecca2a4SBenjamin Herrenschmidt /* Grab next free descriptor */ 1897ecca2a4SBenjamin Herrenschmidt d_num = ep->epn.d_next; 1907ecca2a4SBenjamin Herrenschmidt desc = &ep->epn.descs[d_num]; 1917ecca2a4SBenjamin Herrenschmidt ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1); 1927ecca2a4SBenjamin Herrenschmidt 1937ecca2a4SBenjamin Herrenschmidt /* Calculate next chunk size */ 1947ecca2a4SBenjamin Herrenschmidt chunk = len - act; 1957ecca2a4SBenjamin Herrenschmidt if (chunk <= ep->epn.chunk_max) { 1967ecca2a4SBenjamin Herrenschmidt /* 1977ecca2a4SBenjamin Herrenschmidt * Is this the last packet ? Because of having up to 8 1987ecca2a4SBenjamin Herrenschmidt * packets in a descriptor we can't just compare "chunk" 1997ecca2a4SBenjamin Herrenschmidt * with ep.maxpacket. We have to see if it's a multiple 2007ecca2a4SBenjamin Herrenschmidt * of it to know if we have to send a zero packet. 2017ecca2a4SBenjamin Herrenschmidt * Sadly that involves a modulo which is a bit expensive 2027ecca2a4SBenjamin Herrenschmidt * but probably still better than not doing it. 2037ecca2a4SBenjamin Herrenschmidt */ 2047ecca2a4SBenjamin Herrenschmidt if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0) 2057ecca2a4SBenjamin Herrenschmidt req->last_desc = d_num; 2067ecca2a4SBenjamin Herrenschmidt } else { 2077ecca2a4SBenjamin Herrenschmidt chunk = ep->epn.chunk_max; 2087ecca2a4SBenjamin Herrenschmidt } 2097ecca2a4SBenjamin Herrenschmidt 2107ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n", 2117ecca2a4SBenjamin Herrenschmidt act, len, chunk, req->last_desc, d_num, 2127ecca2a4SBenjamin Herrenschmidt ast_vhub_count_free_descs(ep)); 2137ecca2a4SBenjamin Herrenschmidt 2147ecca2a4SBenjamin Herrenschmidt /* Populate descriptor */ 2157ecca2a4SBenjamin Herrenschmidt desc->w0 = cpu_to_le32(req->req.dma + act); 2167ecca2a4SBenjamin Herrenschmidt 2177ecca2a4SBenjamin Herrenschmidt /* Interrupt if end of request or no more descriptors */ 2187ecca2a4SBenjamin Herrenschmidt 2197ecca2a4SBenjamin Herrenschmidt /* 2207ecca2a4SBenjamin Herrenschmidt * TODO: Be smarter about it, if we don't have enough 2217ecca2a4SBenjamin Herrenschmidt * descriptors request an interrupt before queue empty 2227ecca2a4SBenjamin Herrenschmidt * or so in order to be able to populate more before 2237ecca2a4SBenjamin Herrenschmidt * the HW runs out. This isn't a problem at the moment 2247ecca2a4SBenjamin Herrenschmidt * as we use 256 descriptors and only put at most one 2257ecca2a4SBenjamin Herrenschmidt * request in the ring. 2267ecca2a4SBenjamin Herrenschmidt */ 2277ecca2a4SBenjamin Herrenschmidt desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk)); 2287ecca2a4SBenjamin Herrenschmidt if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep)) 2297ecca2a4SBenjamin Herrenschmidt desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT); 2307ecca2a4SBenjamin Herrenschmidt 2317ecca2a4SBenjamin Herrenschmidt /* Account packet */ 2327ecca2a4SBenjamin Herrenschmidt req->act_count = act = act + chunk; 2337ecca2a4SBenjamin Herrenschmidt } 2347ecca2a4SBenjamin Herrenschmidt 235bb286336SBenjamin Herrenschmidt if (likely(desc)) 236bb286336SBenjamin Herrenschmidt vhub_dma_workaround(desc); 237bb286336SBenjamin Herrenschmidt 2387ecca2a4SBenjamin Herrenschmidt /* Tell HW about new descriptors */ 2397ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next), 2407ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 2417ecca2a4SBenjamin Herrenschmidt 2427ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n", 2437ecca2a4SBenjamin Herrenschmidt ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS)); 2447ecca2a4SBenjamin Herrenschmidt } 2457ecca2a4SBenjamin Herrenschmidt 2467ecca2a4SBenjamin Herrenschmidt static void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep) 2477ecca2a4SBenjamin Herrenschmidt { 2487ecca2a4SBenjamin Herrenschmidt struct ast_vhub_req *req; 2497ecca2a4SBenjamin Herrenschmidt unsigned int len, d_last; 2507ecca2a4SBenjamin Herrenschmidt u32 stat, stat1; 2517ecca2a4SBenjamin Herrenschmidt 2527ecca2a4SBenjamin Herrenschmidt /* Read EP status, workaround HW race */ 2537ecca2a4SBenjamin Herrenschmidt do { 2547ecca2a4SBenjamin Herrenschmidt stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 2557ecca2a4SBenjamin Herrenschmidt stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 2567ecca2a4SBenjamin Herrenschmidt } while(stat != stat1); 2577ecca2a4SBenjamin Herrenschmidt 2587ecca2a4SBenjamin Herrenschmidt /* Extract RPTR */ 2597ecca2a4SBenjamin Herrenschmidt d_last = VHUB_EP_DMA_RPTR(stat); 2607ecca2a4SBenjamin Herrenschmidt 2617ecca2a4SBenjamin Herrenschmidt /* Grab current request if any */ 2627ecca2a4SBenjamin Herrenschmidt req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue); 2637ecca2a4SBenjamin Herrenschmidt 2647ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n", 2657ecca2a4SBenjamin Herrenschmidt stat, ep->epn.is_in, ep->epn.d_last, d_last); 2667ecca2a4SBenjamin Herrenschmidt 2677ecca2a4SBenjamin Herrenschmidt /* Check all completed descriptors */ 2687ecca2a4SBenjamin Herrenschmidt while (ep->epn.d_last != d_last) { 2697ecca2a4SBenjamin Herrenschmidt struct ast_vhub_desc *desc; 2707ecca2a4SBenjamin Herrenschmidt unsigned int d_num; 2717ecca2a4SBenjamin Herrenschmidt bool is_last_desc; 2727ecca2a4SBenjamin Herrenschmidt 2737ecca2a4SBenjamin Herrenschmidt /* Grab next completed descriptor */ 2747ecca2a4SBenjamin Herrenschmidt d_num = ep->epn.d_last; 2757ecca2a4SBenjamin Herrenschmidt desc = &ep->epn.descs[d_num]; 2767ecca2a4SBenjamin Herrenschmidt ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1); 2777ecca2a4SBenjamin Herrenschmidt 2787ecca2a4SBenjamin Herrenschmidt /* Grab len out of descriptor */ 2797ecca2a4SBenjamin Herrenschmidt len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1)); 2807ecca2a4SBenjamin Herrenschmidt 2817ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n", 2827ecca2a4SBenjamin Herrenschmidt d_num, len, req, req ? req->active : 0); 2837ecca2a4SBenjamin Herrenschmidt 2847ecca2a4SBenjamin Herrenschmidt /* If no active request pending, move on */ 2857ecca2a4SBenjamin Herrenschmidt if (!req || !req->active) 2867ecca2a4SBenjamin Herrenschmidt continue; 2877ecca2a4SBenjamin Herrenschmidt 2887ecca2a4SBenjamin Herrenschmidt /* Adjust size */ 2897ecca2a4SBenjamin Herrenschmidt req->req.actual += len; 2907ecca2a4SBenjamin Herrenschmidt 2917ecca2a4SBenjamin Herrenschmidt /* Is that the last chunk ? */ 2927ecca2a4SBenjamin Herrenschmidt is_last_desc = req->last_desc == d_num; 2937ecca2a4SBenjamin Herrenschmidt CHECK(ep, is_last_desc == (len < ep->ep.maxpacket || 2947ecca2a4SBenjamin Herrenschmidt (req->req.actual >= req->req.length && 2957ecca2a4SBenjamin Herrenschmidt !req->req.zero)), 2967ecca2a4SBenjamin Herrenschmidt "Last packet discrepancy: last_desc=%d len=%d r.act=%d " 2977ecca2a4SBenjamin Herrenschmidt "r.len=%d r.zero=%d mp=%d\n", 2987ecca2a4SBenjamin Herrenschmidt is_last_desc, len, req->req.actual, req->req.length, 2997ecca2a4SBenjamin Herrenschmidt req->req.zero, ep->ep.maxpacket); 3007ecca2a4SBenjamin Herrenschmidt 3017ecca2a4SBenjamin Herrenschmidt if (is_last_desc) { 3027ecca2a4SBenjamin Herrenschmidt /* 3037ecca2a4SBenjamin Herrenschmidt * Because we can only have one request at a time 3047ecca2a4SBenjamin Herrenschmidt * in our descriptor list in this implementation, 3057ecca2a4SBenjamin Herrenschmidt * d_last and ep->d_last should now be equal 3067ecca2a4SBenjamin Herrenschmidt */ 3077ecca2a4SBenjamin Herrenschmidt CHECK(ep, d_last == ep->epn.d_last, 3087ecca2a4SBenjamin Herrenschmidt "DMA read ptr mismatch %d vs %d\n", 3097ecca2a4SBenjamin Herrenschmidt d_last, ep->epn.d_last); 3107ecca2a4SBenjamin Herrenschmidt 3117ecca2a4SBenjamin Herrenschmidt /* Note: done will drop and re-acquire the lock */ 3127ecca2a4SBenjamin Herrenschmidt ast_vhub_done(ep, req, 0); 3137ecca2a4SBenjamin Herrenschmidt req = list_first_entry_or_null(&ep->queue, 3147ecca2a4SBenjamin Herrenschmidt struct ast_vhub_req, 3157ecca2a4SBenjamin Herrenschmidt queue); 3167ecca2a4SBenjamin Herrenschmidt break; 3177ecca2a4SBenjamin Herrenschmidt } 3187ecca2a4SBenjamin Herrenschmidt } 3197ecca2a4SBenjamin Herrenschmidt 3207ecca2a4SBenjamin Herrenschmidt /* More work ? */ 3217ecca2a4SBenjamin Herrenschmidt if (req) 3227ecca2a4SBenjamin Herrenschmidt ast_vhub_epn_kick_desc(ep, req); 3237ecca2a4SBenjamin Herrenschmidt } 3247ecca2a4SBenjamin Herrenschmidt 3257ecca2a4SBenjamin Herrenschmidt void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep) 3267ecca2a4SBenjamin Herrenschmidt { 3277ecca2a4SBenjamin Herrenschmidt if (ep->epn.desc_mode) 3287ecca2a4SBenjamin Herrenschmidt ast_vhub_epn_handle_ack_desc(ep); 3297ecca2a4SBenjamin Herrenschmidt else 3307ecca2a4SBenjamin Herrenschmidt ast_vhub_epn_handle_ack(ep); 3317ecca2a4SBenjamin Herrenschmidt } 3327ecca2a4SBenjamin Herrenschmidt 3337ecca2a4SBenjamin Herrenschmidt static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req, 3347ecca2a4SBenjamin Herrenschmidt gfp_t gfp_flags) 3357ecca2a4SBenjamin Herrenschmidt { 3367ecca2a4SBenjamin Herrenschmidt struct ast_vhub_req *req = to_ast_req(u_req); 3377ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep = to_ast_ep(u_ep); 3387ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub; 3397ecca2a4SBenjamin Herrenschmidt unsigned long flags; 3407ecca2a4SBenjamin Herrenschmidt bool empty; 3417ecca2a4SBenjamin Herrenschmidt int rc; 3427ecca2a4SBenjamin Herrenschmidt 3437ecca2a4SBenjamin Herrenschmidt /* Paranoid checks */ 3447ecca2a4SBenjamin Herrenschmidt if (!u_req || !u_req->complete || !u_req->buf) { 3457ecca2a4SBenjamin Herrenschmidt dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req); 3467ecca2a4SBenjamin Herrenschmidt if (u_req) { 3477ecca2a4SBenjamin Herrenschmidt dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n", 3487ecca2a4SBenjamin Herrenschmidt u_req->complete, req->internal); 3497ecca2a4SBenjamin Herrenschmidt } 3507ecca2a4SBenjamin Herrenschmidt return -EINVAL; 3517ecca2a4SBenjamin Herrenschmidt } 3527ecca2a4SBenjamin Herrenschmidt 3537ecca2a4SBenjamin Herrenschmidt /* Endpoint enabled ? */ 3547ecca2a4SBenjamin Herrenschmidt if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx || 3557ecca2a4SBenjamin Herrenschmidt !ep->dev->enabled || ep->dev->suspended) { 3563c168909SColin Ian King EPDBG(ep, "Enqueuing request on wrong or disabled EP\n"); 3577ecca2a4SBenjamin Herrenschmidt return -ESHUTDOWN; 3587ecca2a4SBenjamin Herrenschmidt } 3597ecca2a4SBenjamin Herrenschmidt 3607ecca2a4SBenjamin Herrenschmidt /* Map request for DMA if possible. For now, the rule for DMA is 3617ecca2a4SBenjamin Herrenschmidt * that: 3627ecca2a4SBenjamin Herrenschmidt * 3637ecca2a4SBenjamin Herrenschmidt * * For single stage mode (no descriptors): 3647ecca2a4SBenjamin Herrenschmidt * 3657ecca2a4SBenjamin Herrenschmidt * - The buffer is aligned to a 8 bytes boundary (HW requirement) 3667ecca2a4SBenjamin Herrenschmidt * - For a OUT endpoint, the request size is a multiple of the EP 3677ecca2a4SBenjamin Herrenschmidt * packet size (otherwise the controller will DMA past the end 3687ecca2a4SBenjamin Herrenschmidt * of the buffer if the host is sending a too long packet). 3697ecca2a4SBenjamin Herrenschmidt * 3707ecca2a4SBenjamin Herrenschmidt * * For descriptor mode (tx only for now), always. 3717ecca2a4SBenjamin Herrenschmidt * 3727ecca2a4SBenjamin Herrenschmidt * We could relax the latter by making the decision to use the bounce 3737ecca2a4SBenjamin Herrenschmidt * buffer based on the size of a given *segment* of the request rather 3747ecca2a4SBenjamin Herrenschmidt * than the whole request. 3757ecca2a4SBenjamin Herrenschmidt */ 3767ecca2a4SBenjamin Herrenschmidt if (ep->epn.desc_mode || 3777ecca2a4SBenjamin Herrenschmidt ((((unsigned long)u_req->buf & 7) == 0) && 3787ecca2a4SBenjamin Herrenschmidt (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) { 3797ecca2a4SBenjamin Herrenschmidt rc = usb_gadget_map_request(&ep->dev->gadget, u_req, 3807ecca2a4SBenjamin Herrenschmidt ep->epn.is_in); 3817ecca2a4SBenjamin Herrenschmidt if (rc) { 3827ecca2a4SBenjamin Herrenschmidt dev_warn(&vhub->pdev->dev, 3837ecca2a4SBenjamin Herrenschmidt "Request mapping failure %d\n", rc); 3847ecca2a4SBenjamin Herrenschmidt return rc; 3857ecca2a4SBenjamin Herrenschmidt } 3867ecca2a4SBenjamin Herrenschmidt } else 3877ecca2a4SBenjamin Herrenschmidt u_req->dma = 0; 3887ecca2a4SBenjamin Herrenschmidt 3897ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "enqueue req @%p\n", req); 3907ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n", 3917ecca2a4SBenjamin Herrenschmidt u_req->length, (u32)u_req->dma, u_req->zero, 3927ecca2a4SBenjamin Herrenschmidt u_req->short_not_ok, u_req->no_interrupt, 3937ecca2a4SBenjamin Herrenschmidt ep->epn.is_in); 3947ecca2a4SBenjamin Herrenschmidt 3957ecca2a4SBenjamin Herrenschmidt /* Initialize request progress fields */ 3967ecca2a4SBenjamin Herrenschmidt u_req->status = -EINPROGRESS; 3977ecca2a4SBenjamin Herrenschmidt u_req->actual = 0; 3987ecca2a4SBenjamin Herrenschmidt req->act_count = 0; 3997ecca2a4SBenjamin Herrenschmidt req->active = false; 4007ecca2a4SBenjamin Herrenschmidt req->last_desc = -1; 4017ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags); 4027ecca2a4SBenjamin Herrenschmidt empty = list_empty(&ep->queue); 4037ecca2a4SBenjamin Herrenschmidt 4047ecca2a4SBenjamin Herrenschmidt /* Add request to list and kick processing if empty */ 4057ecca2a4SBenjamin Herrenschmidt list_add_tail(&req->queue, &ep->queue); 4067ecca2a4SBenjamin Herrenschmidt if (empty) { 4077ecca2a4SBenjamin Herrenschmidt if (ep->epn.desc_mode) 4087ecca2a4SBenjamin Herrenschmidt ast_vhub_epn_kick_desc(ep, req); 4097ecca2a4SBenjamin Herrenschmidt else 4107ecca2a4SBenjamin Herrenschmidt ast_vhub_epn_kick(ep, req); 4117ecca2a4SBenjamin Herrenschmidt } 4127ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 4137ecca2a4SBenjamin Herrenschmidt 4147ecca2a4SBenjamin Herrenschmidt return 0; 4157ecca2a4SBenjamin Herrenschmidt } 4167ecca2a4SBenjamin Herrenschmidt 4177ecca2a4SBenjamin Herrenschmidt static void ast_vhub_stop_active_req(struct ast_vhub_ep *ep, 4187ecca2a4SBenjamin Herrenschmidt bool restart_ep) 4197ecca2a4SBenjamin Herrenschmidt { 4207ecca2a4SBenjamin Herrenschmidt u32 state, reg, loops; 4217ecca2a4SBenjamin Herrenschmidt 4227ecca2a4SBenjamin Herrenschmidt /* Stop DMA activity */ 4237ecca2a4SBenjamin Herrenschmidt writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 4247ecca2a4SBenjamin Herrenschmidt 4257ecca2a4SBenjamin Herrenschmidt /* Wait for it to complete */ 4267ecca2a4SBenjamin Herrenschmidt for (loops = 0; loops < 1000; loops++) { 4277ecca2a4SBenjamin Herrenschmidt state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 4287ecca2a4SBenjamin Herrenschmidt state = VHUB_EP_DMA_PROC_STATUS(state); 4297ecca2a4SBenjamin Herrenschmidt if (state == EP_DMA_PROC_RX_IDLE || 4307ecca2a4SBenjamin Herrenschmidt state == EP_DMA_PROC_TX_IDLE) 4317ecca2a4SBenjamin Herrenschmidt break; 4327ecca2a4SBenjamin Herrenschmidt udelay(1); 4337ecca2a4SBenjamin Herrenschmidt } 4347ecca2a4SBenjamin Herrenschmidt if (loops >= 1000) 4357ecca2a4SBenjamin Herrenschmidt dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n"); 4367ecca2a4SBenjamin Herrenschmidt 4377ecca2a4SBenjamin Herrenschmidt /* If we don't have to restart the endpoint, that's it */ 4387ecca2a4SBenjamin Herrenschmidt if (!restart_ep) 4397ecca2a4SBenjamin Herrenschmidt return; 4407ecca2a4SBenjamin Herrenschmidt 4417ecca2a4SBenjamin Herrenschmidt /* Restart the endpoint */ 4427ecca2a4SBenjamin Herrenschmidt if (ep->epn.desc_mode) { 4437ecca2a4SBenjamin Herrenschmidt /* 4447ecca2a4SBenjamin Herrenschmidt * Take out descriptors by resetting the DMA read 4457ecca2a4SBenjamin Herrenschmidt * pointer to be equal to the CPU write pointer. 4467ecca2a4SBenjamin Herrenschmidt * 4477ecca2a4SBenjamin Herrenschmidt * Note: If we ever support creating descriptors for 4487ecca2a4SBenjamin Herrenschmidt * requests that aren't the head of the queue, we 4497ecca2a4SBenjamin Herrenschmidt * may have to do something more complex here, 4507ecca2a4SBenjamin Herrenschmidt * especially if the request being taken out is 4517ecca2a4SBenjamin Herrenschmidt * not the current head descriptors. 4527ecca2a4SBenjamin Herrenschmidt */ 4537ecca2a4SBenjamin Herrenschmidt reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) | 4547ecca2a4SBenjamin Herrenschmidt VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next); 4557ecca2a4SBenjamin Herrenschmidt writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 4567ecca2a4SBenjamin Herrenschmidt 4577ecca2a4SBenjamin Herrenschmidt /* Then turn it back on */ 4587ecca2a4SBenjamin Herrenschmidt writel(ep->epn.dma_conf, 4597ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 4607ecca2a4SBenjamin Herrenschmidt } else { 4617ecca2a4SBenjamin Herrenschmidt /* Single mode: just turn it back on */ 4627ecca2a4SBenjamin Herrenschmidt writel(ep->epn.dma_conf, 4637ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 4647ecca2a4SBenjamin Herrenschmidt } 4657ecca2a4SBenjamin Herrenschmidt } 4667ecca2a4SBenjamin Herrenschmidt 4677ecca2a4SBenjamin Herrenschmidt static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req) 4687ecca2a4SBenjamin Herrenschmidt { 4697ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep = to_ast_ep(u_ep); 4707ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub; 4717ecca2a4SBenjamin Herrenschmidt struct ast_vhub_req *req; 4727ecca2a4SBenjamin Herrenschmidt unsigned long flags; 4737ecca2a4SBenjamin Herrenschmidt int rc = -EINVAL; 4747ecca2a4SBenjamin Herrenschmidt 4757ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags); 4767ecca2a4SBenjamin Herrenschmidt 4777ecca2a4SBenjamin Herrenschmidt /* Make sure it's actually queued on this endpoint */ 4787ecca2a4SBenjamin Herrenschmidt list_for_each_entry (req, &ep->queue, queue) { 4797ecca2a4SBenjamin Herrenschmidt if (&req->req == u_req) 4807ecca2a4SBenjamin Herrenschmidt break; 4817ecca2a4SBenjamin Herrenschmidt } 4827ecca2a4SBenjamin Herrenschmidt 4837ecca2a4SBenjamin Herrenschmidt if (&req->req == u_req) { 4847ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "dequeue req @%p active=%d\n", 4857ecca2a4SBenjamin Herrenschmidt req, req->active); 4867ecca2a4SBenjamin Herrenschmidt if (req->active) 4877ecca2a4SBenjamin Herrenschmidt ast_vhub_stop_active_req(ep, true); 4887ecca2a4SBenjamin Herrenschmidt ast_vhub_done(ep, req, -ECONNRESET); 4897ecca2a4SBenjamin Herrenschmidt rc = 0; 4907ecca2a4SBenjamin Herrenschmidt } 4917ecca2a4SBenjamin Herrenschmidt 4927ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 4937ecca2a4SBenjamin Herrenschmidt return rc; 4947ecca2a4SBenjamin Herrenschmidt } 4957ecca2a4SBenjamin Herrenschmidt 4967ecca2a4SBenjamin Herrenschmidt void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep) 4977ecca2a4SBenjamin Herrenschmidt { 4987ecca2a4SBenjamin Herrenschmidt u32 reg; 4997ecca2a4SBenjamin Herrenschmidt 5007ecca2a4SBenjamin Herrenschmidt if (WARN_ON(ep->d_idx == 0)) 5017ecca2a4SBenjamin Herrenschmidt return; 5027ecca2a4SBenjamin Herrenschmidt reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG); 5037ecca2a4SBenjamin Herrenschmidt if (ep->epn.stalled || ep->epn.wedged) 5047ecca2a4SBenjamin Herrenschmidt reg |= VHUB_EP_CFG_STALL_CTRL; 5057ecca2a4SBenjamin Herrenschmidt else 5067ecca2a4SBenjamin Herrenschmidt reg &= ~VHUB_EP_CFG_STALL_CTRL; 5077ecca2a4SBenjamin Herrenschmidt writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG); 5087ecca2a4SBenjamin Herrenschmidt 5097ecca2a4SBenjamin Herrenschmidt if (!ep->epn.stalled && !ep->epn.wedged) 5107ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx), 5117ecca2a4SBenjamin Herrenschmidt ep->vhub->regs + AST_VHUB_EP_TOGGLE); 5127ecca2a4SBenjamin Herrenschmidt } 5137ecca2a4SBenjamin Herrenschmidt 5147ecca2a4SBenjamin Herrenschmidt static int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt, 5157ecca2a4SBenjamin Herrenschmidt bool wedge) 5167ecca2a4SBenjamin Herrenschmidt { 5177ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep = to_ast_ep(u_ep); 5187ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub; 5197ecca2a4SBenjamin Herrenschmidt unsigned long flags; 5207ecca2a4SBenjamin Herrenschmidt 5217ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge); 5227ecca2a4SBenjamin Herrenschmidt 5237ecca2a4SBenjamin Herrenschmidt if (!u_ep || !u_ep->desc) 5247ecca2a4SBenjamin Herrenschmidt return -EINVAL; 5257ecca2a4SBenjamin Herrenschmidt if (ep->d_idx == 0) 5267ecca2a4SBenjamin Herrenschmidt return 0; 5277ecca2a4SBenjamin Herrenschmidt if (ep->epn.is_iso) 5287ecca2a4SBenjamin Herrenschmidt return -EOPNOTSUPP; 5297ecca2a4SBenjamin Herrenschmidt 5307ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags); 5317ecca2a4SBenjamin Herrenschmidt 5327ecca2a4SBenjamin Herrenschmidt /* Fail with still-busy IN endpoints */ 5337ecca2a4SBenjamin Herrenschmidt if (halt && ep->epn.is_in && !list_empty(&ep->queue)) { 5347ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 5357ecca2a4SBenjamin Herrenschmidt return -EAGAIN; 5367ecca2a4SBenjamin Herrenschmidt } 5377ecca2a4SBenjamin Herrenschmidt ep->epn.stalled = halt; 5387ecca2a4SBenjamin Herrenschmidt ep->epn.wedged = wedge; 5397ecca2a4SBenjamin Herrenschmidt ast_vhub_update_epn_stall(ep); 5407ecca2a4SBenjamin Herrenschmidt 5417ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 5427ecca2a4SBenjamin Herrenschmidt 5437ecca2a4SBenjamin Herrenschmidt return 0; 5447ecca2a4SBenjamin Herrenschmidt } 5457ecca2a4SBenjamin Herrenschmidt 5467ecca2a4SBenjamin Herrenschmidt static int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value) 5477ecca2a4SBenjamin Herrenschmidt { 5487ecca2a4SBenjamin Herrenschmidt return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false); 5497ecca2a4SBenjamin Herrenschmidt } 5507ecca2a4SBenjamin Herrenschmidt 5517ecca2a4SBenjamin Herrenschmidt static int ast_vhub_epn_set_wedge(struct usb_ep *u_ep) 5527ecca2a4SBenjamin Herrenschmidt { 5537ecca2a4SBenjamin Herrenschmidt return ast_vhub_set_halt_and_wedge(u_ep, true, true); 5547ecca2a4SBenjamin Herrenschmidt } 5557ecca2a4SBenjamin Herrenschmidt 5567ecca2a4SBenjamin Herrenschmidt static int ast_vhub_epn_disable(struct usb_ep* u_ep) 5577ecca2a4SBenjamin Herrenschmidt { 5587ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep = to_ast_ep(u_ep); 5597ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = ep->vhub; 5607ecca2a4SBenjamin Herrenschmidt unsigned long flags; 5617ecca2a4SBenjamin Herrenschmidt u32 imask, ep_ier; 5627ecca2a4SBenjamin Herrenschmidt 5637ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Disabling !\n"); 5647ecca2a4SBenjamin Herrenschmidt 5657ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags); 5667ecca2a4SBenjamin Herrenschmidt 5677ecca2a4SBenjamin Herrenschmidt ep->epn.enabled = false; 5687ecca2a4SBenjamin Herrenschmidt 5697ecca2a4SBenjamin Herrenschmidt /* Stop active DMA if any */ 5707ecca2a4SBenjamin Herrenschmidt ast_vhub_stop_active_req(ep, false); 5717ecca2a4SBenjamin Herrenschmidt 5727ecca2a4SBenjamin Herrenschmidt /* Disable endpoint */ 5737ecca2a4SBenjamin Herrenschmidt writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG); 5747ecca2a4SBenjamin Herrenschmidt 5757ecca2a4SBenjamin Herrenschmidt /* Disable ACK interrupt */ 5767ecca2a4SBenjamin Herrenschmidt imask = VHUB_EP_IRQ(ep->epn.g_idx); 5777ecca2a4SBenjamin Herrenschmidt ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER); 5787ecca2a4SBenjamin Herrenschmidt ep_ier &= ~imask; 5797ecca2a4SBenjamin Herrenschmidt writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER); 5807ecca2a4SBenjamin Herrenschmidt writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR); 5817ecca2a4SBenjamin Herrenschmidt 5827ecca2a4SBenjamin Herrenschmidt /* Nuke all pending requests */ 5837ecca2a4SBenjamin Herrenschmidt ast_vhub_nuke(ep, -ESHUTDOWN); 5847ecca2a4SBenjamin Herrenschmidt 5857ecca2a4SBenjamin Herrenschmidt /* No more descriptor associated with request */ 5867ecca2a4SBenjamin Herrenschmidt ep->ep.desc = NULL; 5877ecca2a4SBenjamin Herrenschmidt 5887ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 5897ecca2a4SBenjamin Herrenschmidt 5907ecca2a4SBenjamin Herrenschmidt return 0; 5917ecca2a4SBenjamin Herrenschmidt } 5927ecca2a4SBenjamin Herrenschmidt 5937ecca2a4SBenjamin Herrenschmidt static int ast_vhub_epn_enable(struct usb_ep* u_ep, 5947ecca2a4SBenjamin Herrenschmidt const struct usb_endpoint_descriptor *desc) 5957ecca2a4SBenjamin Herrenschmidt { 5967ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep = to_ast_ep(u_ep); 5977ecca2a4SBenjamin Herrenschmidt struct ast_vhub_dev *dev; 5987ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub; 5997ecca2a4SBenjamin Herrenschmidt u16 maxpacket, type; 6007ecca2a4SBenjamin Herrenschmidt unsigned long flags; 6017ecca2a4SBenjamin Herrenschmidt u32 ep_conf, ep_ier, imask; 6027ecca2a4SBenjamin Herrenschmidt 6037ecca2a4SBenjamin Herrenschmidt /* Check arguments */ 6047ecca2a4SBenjamin Herrenschmidt if (!u_ep || !desc) 6057ecca2a4SBenjamin Herrenschmidt return -EINVAL; 6067ecca2a4SBenjamin Herrenschmidt 6077ecca2a4SBenjamin Herrenschmidt maxpacket = usb_endpoint_maxp(desc); 6087ecca2a4SBenjamin Herrenschmidt if (!ep->d_idx || !ep->dev || 6097ecca2a4SBenjamin Herrenschmidt desc->bDescriptorType != USB_DT_ENDPOINT || 6107ecca2a4SBenjamin Herrenschmidt maxpacket == 0 || maxpacket > ep->ep.maxpacket) { 6117ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n", 6127ecca2a4SBenjamin Herrenschmidt ep->d_idx, ep->dev, desc->bDescriptorType, 6137ecca2a4SBenjamin Herrenschmidt maxpacket, ep->ep.maxpacket); 6147ecca2a4SBenjamin Herrenschmidt return -EINVAL; 6157ecca2a4SBenjamin Herrenschmidt } 6167ecca2a4SBenjamin Herrenschmidt if (ep->d_idx != usb_endpoint_num(desc)) { 6177ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "EP number mismatch !\n"); 6187ecca2a4SBenjamin Herrenschmidt return -EINVAL; 6197ecca2a4SBenjamin Herrenschmidt } 6207ecca2a4SBenjamin Herrenschmidt 6217ecca2a4SBenjamin Herrenschmidt if (ep->epn.enabled) { 6227ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Already enabled\n"); 6237ecca2a4SBenjamin Herrenschmidt return -EBUSY; 6247ecca2a4SBenjamin Herrenschmidt } 6257ecca2a4SBenjamin Herrenschmidt dev = ep->dev; 6267ecca2a4SBenjamin Herrenschmidt vhub = ep->vhub; 6277ecca2a4SBenjamin Herrenschmidt 6287ecca2a4SBenjamin Herrenschmidt /* Check device state */ 6297ecca2a4SBenjamin Herrenschmidt if (!dev->driver) { 6307ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Bogus device state: driver=%p speed=%d\n", 6317ecca2a4SBenjamin Herrenschmidt dev->driver, dev->gadget.speed); 6327ecca2a4SBenjamin Herrenschmidt return -ESHUTDOWN; 6337ecca2a4SBenjamin Herrenschmidt } 6347ecca2a4SBenjamin Herrenschmidt 6357ecca2a4SBenjamin Herrenschmidt /* Grab some info from the descriptor */ 6367ecca2a4SBenjamin Herrenschmidt ep->epn.is_in = usb_endpoint_dir_in(desc); 6377ecca2a4SBenjamin Herrenschmidt ep->ep.maxpacket = maxpacket; 6387ecca2a4SBenjamin Herrenschmidt type = usb_endpoint_type(desc); 6397ecca2a4SBenjamin Herrenschmidt ep->epn.d_next = ep->epn.d_last = 0; 6407ecca2a4SBenjamin Herrenschmidt ep->epn.is_iso = false; 6417ecca2a4SBenjamin Herrenschmidt ep->epn.stalled = false; 6427ecca2a4SBenjamin Herrenschmidt ep->epn.wedged = false; 6437ecca2a4SBenjamin Herrenschmidt 6447ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n", 6454d537f37SChunfeng Yun ep->epn.is_in ? "in" : "out", usb_ep_type_string(type), 6467ecca2a4SBenjamin Herrenschmidt usb_endpoint_num(desc), maxpacket); 6477ecca2a4SBenjamin Herrenschmidt 6487ecca2a4SBenjamin Herrenschmidt /* Can we use DMA descriptor mode ? */ 6497ecca2a4SBenjamin Herrenschmidt ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in; 6507ecca2a4SBenjamin Herrenschmidt if (ep->epn.desc_mode) 6517ecca2a4SBenjamin Herrenschmidt memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT); 6527ecca2a4SBenjamin Herrenschmidt 6537ecca2a4SBenjamin Herrenschmidt /* 6547ecca2a4SBenjamin Herrenschmidt * Large send function can send up to 8 packets from 6557ecca2a4SBenjamin Herrenschmidt * one descriptor with a limit of 4095 bytes. 6567ecca2a4SBenjamin Herrenschmidt */ 6577ecca2a4SBenjamin Herrenschmidt ep->epn.chunk_max = ep->ep.maxpacket; 6587ecca2a4SBenjamin Herrenschmidt if (ep->epn.is_in) { 6597ecca2a4SBenjamin Herrenschmidt ep->epn.chunk_max <<= 3; 6607ecca2a4SBenjamin Herrenschmidt while (ep->epn.chunk_max > 4095) 6617ecca2a4SBenjamin Herrenschmidt ep->epn.chunk_max -= ep->ep.maxpacket; 6627ecca2a4SBenjamin Herrenschmidt } 6637ecca2a4SBenjamin Herrenschmidt 6647ecca2a4SBenjamin Herrenschmidt switch(type) { 6657ecca2a4SBenjamin Herrenschmidt case USB_ENDPOINT_XFER_CONTROL: 6667ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Only one control endpoint\n"); 6677ecca2a4SBenjamin Herrenschmidt return -EINVAL; 6687ecca2a4SBenjamin Herrenschmidt case USB_ENDPOINT_XFER_INT: 6697ecca2a4SBenjamin Herrenschmidt ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT); 6707ecca2a4SBenjamin Herrenschmidt break; 6717ecca2a4SBenjamin Herrenschmidt case USB_ENDPOINT_XFER_BULK: 6727ecca2a4SBenjamin Herrenschmidt ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK); 6737ecca2a4SBenjamin Herrenschmidt break; 6747ecca2a4SBenjamin Herrenschmidt case USB_ENDPOINT_XFER_ISOC: 6757ecca2a4SBenjamin Herrenschmidt ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO); 6767ecca2a4SBenjamin Herrenschmidt ep->epn.is_iso = true; 6777ecca2a4SBenjamin Herrenschmidt break; 6787ecca2a4SBenjamin Herrenschmidt default: 6797ecca2a4SBenjamin Herrenschmidt return -EINVAL; 6807ecca2a4SBenjamin Herrenschmidt } 6817ecca2a4SBenjamin Herrenschmidt 6827ecca2a4SBenjamin Herrenschmidt /* Encode the rest of the EP config register */ 6837ecca2a4SBenjamin Herrenschmidt if (maxpacket < 1024) 6847ecca2a4SBenjamin Herrenschmidt ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket); 6857ecca2a4SBenjamin Herrenschmidt if (!ep->epn.is_in) 6867ecca2a4SBenjamin Herrenschmidt ep_conf |= VHUB_EP_CFG_DIR_OUT; 6877ecca2a4SBenjamin Herrenschmidt ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc)); 6887ecca2a4SBenjamin Herrenschmidt ep_conf |= VHUB_EP_CFG_ENABLE; 6897ecca2a4SBenjamin Herrenschmidt ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1); 6907ecca2a4SBenjamin Herrenschmidt EPVDBG(ep, "config=%08x\n", ep_conf); 6917ecca2a4SBenjamin Herrenschmidt 6927ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags); 6937ecca2a4SBenjamin Herrenschmidt 6947ecca2a4SBenjamin Herrenschmidt /* Disable HW and reset DMA */ 6957ecca2a4SBenjamin Herrenschmidt writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG); 6967ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP_DMA_CTRL_RESET, 6977ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 6987ecca2a4SBenjamin Herrenschmidt 6997ecca2a4SBenjamin Herrenschmidt /* Configure and enable */ 7007ecca2a4SBenjamin Herrenschmidt writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG); 7017ecca2a4SBenjamin Herrenschmidt 7027ecca2a4SBenjamin Herrenschmidt if (ep->epn.desc_mode) { 7037ecca2a4SBenjamin Herrenschmidt /* Clear DMA status, including the DMA read ptr */ 7047ecca2a4SBenjamin Herrenschmidt writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 7057ecca2a4SBenjamin Herrenschmidt 7067ecca2a4SBenjamin Herrenschmidt /* Set descriptor base */ 7077ecca2a4SBenjamin Herrenschmidt writel(ep->epn.descs_dma, 7087ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DESC_BASE); 7097ecca2a4SBenjamin Herrenschmidt 7107ecca2a4SBenjamin Herrenschmidt /* Set base DMA config value */ 7117ecca2a4SBenjamin Herrenschmidt ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE; 7127ecca2a4SBenjamin Herrenschmidt if (ep->epn.is_in) 7137ecca2a4SBenjamin Herrenschmidt ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE; 7147ecca2a4SBenjamin Herrenschmidt 7157ecca2a4SBenjamin Herrenschmidt /* First reset and disable all operations */ 7167ecca2a4SBenjamin Herrenschmidt writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET, 7177ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 7187ecca2a4SBenjamin Herrenschmidt 7197ecca2a4SBenjamin Herrenschmidt /* Enable descriptor mode */ 7207ecca2a4SBenjamin Herrenschmidt writel(ep->epn.dma_conf, 7217ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 7227ecca2a4SBenjamin Herrenschmidt } else { 7237ecca2a4SBenjamin Herrenschmidt /* Set base DMA config value */ 7247ecca2a4SBenjamin Herrenschmidt ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE; 7257ecca2a4SBenjamin Herrenschmidt 7267ecca2a4SBenjamin Herrenschmidt /* Reset and switch to single stage mode */ 7277ecca2a4SBenjamin Herrenschmidt writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET, 7287ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 7297ecca2a4SBenjamin Herrenschmidt writel(ep->epn.dma_conf, 7307ecca2a4SBenjamin Herrenschmidt ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT); 7317ecca2a4SBenjamin Herrenschmidt writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS); 7327ecca2a4SBenjamin Herrenschmidt } 7337ecca2a4SBenjamin Herrenschmidt 7347ecca2a4SBenjamin Herrenschmidt /* Cleanup data toggle just in case */ 7357ecca2a4SBenjamin Herrenschmidt writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx), 7367ecca2a4SBenjamin Herrenschmidt vhub->regs + AST_VHUB_EP_TOGGLE); 7377ecca2a4SBenjamin Herrenschmidt 7387ecca2a4SBenjamin Herrenschmidt /* Cleanup and enable ACK interrupt */ 7397ecca2a4SBenjamin Herrenschmidt imask = VHUB_EP_IRQ(ep->epn.g_idx); 7407ecca2a4SBenjamin Herrenschmidt writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR); 7417ecca2a4SBenjamin Herrenschmidt ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER); 7427ecca2a4SBenjamin Herrenschmidt ep_ier |= imask; 7437ecca2a4SBenjamin Herrenschmidt writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER); 7447ecca2a4SBenjamin Herrenschmidt 7457ecca2a4SBenjamin Herrenschmidt /* Woot, we are online ! */ 7467ecca2a4SBenjamin Herrenschmidt ep->epn.enabled = true; 7477ecca2a4SBenjamin Herrenschmidt 7487ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 7497ecca2a4SBenjamin Herrenschmidt 7507ecca2a4SBenjamin Herrenschmidt return 0; 7517ecca2a4SBenjamin Herrenschmidt } 7527ecca2a4SBenjamin Herrenschmidt 7537ecca2a4SBenjamin Herrenschmidt static void ast_vhub_epn_dispose(struct usb_ep *u_ep) 7547ecca2a4SBenjamin Herrenschmidt { 7557ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep = to_ast_ep(u_ep); 7567ecca2a4SBenjamin Herrenschmidt 7577ecca2a4SBenjamin Herrenschmidt if (WARN_ON(!ep->dev || !ep->d_idx)) 7587ecca2a4SBenjamin Herrenschmidt return; 7597ecca2a4SBenjamin Herrenschmidt 7607ecca2a4SBenjamin Herrenschmidt EPDBG(ep, "Releasing endpoint\n"); 7617ecca2a4SBenjamin Herrenschmidt 7627ecca2a4SBenjamin Herrenschmidt /* Take it out of the EP list */ 7637ecca2a4SBenjamin Herrenschmidt list_del_init(&ep->ep.ep_list); 7647ecca2a4SBenjamin Herrenschmidt 7657ecca2a4SBenjamin Herrenschmidt /* Mark the address free in the device */ 7667ecca2a4SBenjamin Herrenschmidt ep->dev->epns[ep->d_idx - 1] = NULL; 7677ecca2a4SBenjamin Herrenschmidt 7687ecca2a4SBenjamin Herrenschmidt /* Free name & DMA buffers */ 7697ecca2a4SBenjamin Herrenschmidt kfree(ep->ep.name); 7707ecca2a4SBenjamin Herrenschmidt ep->ep.name = NULL; 7717ecca2a4SBenjamin Herrenschmidt dma_free_coherent(&ep->vhub->pdev->dev, 7727ecca2a4SBenjamin Herrenschmidt AST_VHUB_EPn_MAX_PACKET + 7737ecca2a4SBenjamin Herrenschmidt 8 * AST_VHUB_DESCS_COUNT, 7747ecca2a4SBenjamin Herrenschmidt ep->buf, ep->buf_dma); 7757ecca2a4SBenjamin Herrenschmidt ep->buf = NULL; 7767ecca2a4SBenjamin Herrenschmidt ep->epn.descs = NULL; 7777ecca2a4SBenjamin Herrenschmidt 7787ecca2a4SBenjamin Herrenschmidt /* Mark free */ 7797ecca2a4SBenjamin Herrenschmidt ep->dev = NULL; 7807ecca2a4SBenjamin Herrenschmidt } 7817ecca2a4SBenjamin Herrenschmidt 7827ecca2a4SBenjamin Herrenschmidt static const struct usb_ep_ops ast_vhub_epn_ops = { 7837ecca2a4SBenjamin Herrenschmidt .enable = ast_vhub_epn_enable, 7847ecca2a4SBenjamin Herrenschmidt .disable = ast_vhub_epn_disable, 7857ecca2a4SBenjamin Herrenschmidt .dispose = ast_vhub_epn_dispose, 7867ecca2a4SBenjamin Herrenschmidt .queue = ast_vhub_epn_queue, 7877ecca2a4SBenjamin Herrenschmidt .dequeue = ast_vhub_epn_dequeue, 7887ecca2a4SBenjamin Herrenschmidt .set_halt = ast_vhub_epn_set_halt, 7897ecca2a4SBenjamin Herrenschmidt .set_wedge = ast_vhub_epn_set_wedge, 7907ecca2a4SBenjamin Herrenschmidt .alloc_request = ast_vhub_alloc_request, 7917ecca2a4SBenjamin Herrenschmidt .free_request = ast_vhub_free_request, 7927ecca2a4SBenjamin Herrenschmidt }; 7937ecca2a4SBenjamin Herrenschmidt 7947ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr) 7957ecca2a4SBenjamin Herrenschmidt { 7967ecca2a4SBenjamin Herrenschmidt struct ast_vhub *vhub = d->vhub; 7977ecca2a4SBenjamin Herrenschmidt struct ast_vhub_ep *ep; 7987ecca2a4SBenjamin Herrenschmidt unsigned long flags; 7997ecca2a4SBenjamin Herrenschmidt int i; 8007ecca2a4SBenjamin Herrenschmidt 8017ecca2a4SBenjamin Herrenschmidt /* Find a free one (no device) */ 8027ecca2a4SBenjamin Herrenschmidt spin_lock_irqsave(&vhub->lock, flags); 8037ecca2a4SBenjamin Herrenschmidt for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) 8047ecca2a4SBenjamin Herrenschmidt if (vhub->epns[i].dev == NULL) 8057ecca2a4SBenjamin Herrenschmidt break; 8067ecca2a4SBenjamin Herrenschmidt if (i >= AST_VHUB_NUM_GEN_EPs) { 8077ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 8087ecca2a4SBenjamin Herrenschmidt return NULL; 8097ecca2a4SBenjamin Herrenschmidt } 8107ecca2a4SBenjamin Herrenschmidt 8117ecca2a4SBenjamin Herrenschmidt /* Set it up */ 8127ecca2a4SBenjamin Herrenschmidt ep = &vhub->epns[i]; 8137ecca2a4SBenjamin Herrenschmidt ep->dev = d; 8147ecca2a4SBenjamin Herrenschmidt spin_unlock_irqrestore(&vhub->lock, flags); 8157ecca2a4SBenjamin Herrenschmidt 8167ecca2a4SBenjamin Herrenschmidt DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr); 8177ecca2a4SBenjamin Herrenschmidt INIT_LIST_HEAD(&ep->queue); 8187ecca2a4SBenjamin Herrenschmidt ep->d_idx = addr; 8197ecca2a4SBenjamin Herrenschmidt ep->vhub = vhub; 8207ecca2a4SBenjamin Herrenschmidt ep->ep.ops = &ast_vhub_epn_ops; 8217ecca2a4SBenjamin Herrenschmidt ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr); 8227ecca2a4SBenjamin Herrenschmidt d->epns[addr-1] = ep; 8237ecca2a4SBenjamin Herrenschmidt ep->epn.g_idx = i; 8247ecca2a4SBenjamin Herrenschmidt ep->epn.regs = vhub->regs + 0x200 + (i * 0x10); 8257ecca2a4SBenjamin Herrenschmidt 8267ecca2a4SBenjamin Herrenschmidt ep->buf = dma_alloc_coherent(&vhub->pdev->dev, 8277ecca2a4SBenjamin Herrenschmidt AST_VHUB_EPn_MAX_PACKET + 8287ecca2a4SBenjamin Herrenschmidt 8 * AST_VHUB_DESCS_COUNT, 8297ecca2a4SBenjamin Herrenschmidt &ep->buf_dma, GFP_KERNEL); 8307ecca2a4SBenjamin Herrenschmidt if (!ep->buf) { 8317ecca2a4SBenjamin Herrenschmidt kfree(ep->ep.name); 8327ecca2a4SBenjamin Herrenschmidt ep->ep.name = NULL; 8337ecca2a4SBenjamin Herrenschmidt return NULL; 8347ecca2a4SBenjamin Herrenschmidt } 8357ecca2a4SBenjamin Herrenschmidt ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET; 8367ecca2a4SBenjamin Herrenschmidt ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET; 8377ecca2a4SBenjamin Herrenschmidt 8387ecca2a4SBenjamin Herrenschmidt usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET); 8397ecca2a4SBenjamin Herrenschmidt list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list); 8407ecca2a4SBenjamin Herrenschmidt ep->ep.caps.type_iso = true; 8417ecca2a4SBenjamin Herrenschmidt ep->ep.caps.type_bulk = true; 8427ecca2a4SBenjamin Herrenschmidt ep->ep.caps.type_int = true; 8437ecca2a4SBenjamin Herrenschmidt ep->ep.caps.dir_in = true; 8447ecca2a4SBenjamin Herrenschmidt ep->ep.caps.dir_out = true; 8457ecca2a4SBenjamin Herrenschmidt 8467ecca2a4SBenjamin Herrenschmidt return ep; 8477ecca2a4SBenjamin Herrenschmidt } 848