18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel PXA25x and IXP4xx on-chip full speed USB device controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) 68c2ecf20Sopenharmony_ci * Copyright (C) 2003 Robert Schwebel, Pengutronix 78c2ecf20Sopenharmony_ci * Copyright (C) 2003 Benedikt Spranger, Pengutronix 88c2ecf20Sopenharmony_ci * Copyright (C) 2003 David Brownell 98c2ecf20Sopenharmony_ci * Copyright (C) 2003 Joshua Wise 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* #define VERBOSE_DEBUG */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/ioport.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/err.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/timer.h> 258c2ecf20Sopenharmony_ci#include <linux/list.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/mm.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_data/pxa2xx_udc.h> 298c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 308c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 318c2ecf20Sopenharmony_ci#include <linux/irq.h> 328c2ecf20Sopenharmony_ci#include <linux/clk.h> 338c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 348c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 358c2ecf20Sopenharmony_ci#include <linux/io.h> 368c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 398c2ecf20Sopenharmony_ci#include <asm/dma.h> 408c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 418c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 448c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 458c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_LUBBOCK 488c2ecf20Sopenharmony_ci#include <mach/lubbock.h> 498c2ecf20Sopenharmony_ci#endif 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define UDCCR 0x0000 /* UDC Control Register */ 528c2ecf20Sopenharmony_ci#define UDC_RES1 0x0004 /* UDC Undocumented - Reserved1 */ 538c2ecf20Sopenharmony_ci#define UDC_RES2 0x0008 /* UDC Undocumented - Reserved2 */ 548c2ecf20Sopenharmony_ci#define UDC_RES3 0x000C /* UDC Undocumented - Reserved3 */ 558c2ecf20Sopenharmony_ci#define UDCCS0 0x0010 /* UDC Endpoint 0 Control/Status Register */ 568c2ecf20Sopenharmony_ci#define UDCCS1 0x0014 /* UDC Endpoint 1 (IN) Control/Status Register */ 578c2ecf20Sopenharmony_ci#define UDCCS2 0x0018 /* UDC Endpoint 2 (OUT) Control/Status Register */ 588c2ecf20Sopenharmony_ci#define UDCCS3 0x001C /* UDC Endpoint 3 (IN) Control/Status Register */ 598c2ecf20Sopenharmony_ci#define UDCCS4 0x0020 /* UDC Endpoint 4 (OUT) Control/Status Register */ 608c2ecf20Sopenharmony_ci#define UDCCS5 0x0024 /* UDC Endpoint 5 (Interrupt) Control/Status Register */ 618c2ecf20Sopenharmony_ci#define UDCCS6 0x0028 /* UDC Endpoint 6 (IN) Control/Status Register */ 628c2ecf20Sopenharmony_ci#define UDCCS7 0x002C /* UDC Endpoint 7 (OUT) Control/Status Register */ 638c2ecf20Sopenharmony_ci#define UDCCS8 0x0030 /* UDC Endpoint 8 (IN) Control/Status Register */ 648c2ecf20Sopenharmony_ci#define UDCCS9 0x0034 /* UDC Endpoint 9 (OUT) Control/Status Register */ 658c2ecf20Sopenharmony_ci#define UDCCS10 0x0038 /* UDC Endpoint 10 (Interrupt) Control/Status Register */ 668c2ecf20Sopenharmony_ci#define UDCCS11 0x003C /* UDC Endpoint 11 (IN) Control/Status Register */ 678c2ecf20Sopenharmony_ci#define UDCCS12 0x0040 /* UDC Endpoint 12 (OUT) Control/Status Register */ 688c2ecf20Sopenharmony_ci#define UDCCS13 0x0044 /* UDC Endpoint 13 (IN) Control/Status Register */ 698c2ecf20Sopenharmony_ci#define UDCCS14 0x0048 /* UDC Endpoint 14 (OUT) Control/Status Register */ 708c2ecf20Sopenharmony_ci#define UDCCS15 0x004C /* UDC Endpoint 15 (Interrupt) Control/Status Register */ 718c2ecf20Sopenharmony_ci#define UFNRH 0x0060 /* UDC Frame Number Register High */ 728c2ecf20Sopenharmony_ci#define UFNRL 0x0064 /* UDC Frame Number Register Low */ 738c2ecf20Sopenharmony_ci#define UBCR2 0x0068 /* UDC Byte Count Reg 2 */ 748c2ecf20Sopenharmony_ci#define UBCR4 0x006c /* UDC Byte Count Reg 4 */ 758c2ecf20Sopenharmony_ci#define UBCR7 0x0070 /* UDC Byte Count Reg 7 */ 768c2ecf20Sopenharmony_ci#define UBCR9 0x0074 /* UDC Byte Count Reg 9 */ 778c2ecf20Sopenharmony_ci#define UBCR12 0x0078 /* UDC Byte Count Reg 12 */ 788c2ecf20Sopenharmony_ci#define UBCR14 0x007c /* UDC Byte Count Reg 14 */ 798c2ecf20Sopenharmony_ci#define UDDR0 0x0080 /* UDC Endpoint 0 Data Register */ 808c2ecf20Sopenharmony_ci#define UDDR1 0x0100 /* UDC Endpoint 1 Data Register */ 818c2ecf20Sopenharmony_ci#define UDDR2 0x0180 /* UDC Endpoint 2 Data Register */ 828c2ecf20Sopenharmony_ci#define UDDR3 0x0200 /* UDC Endpoint 3 Data Register */ 838c2ecf20Sopenharmony_ci#define UDDR4 0x0400 /* UDC Endpoint 4 Data Register */ 848c2ecf20Sopenharmony_ci#define UDDR5 0x00A0 /* UDC Endpoint 5 Data Register */ 858c2ecf20Sopenharmony_ci#define UDDR6 0x0600 /* UDC Endpoint 6 Data Register */ 868c2ecf20Sopenharmony_ci#define UDDR7 0x0680 /* UDC Endpoint 7 Data Register */ 878c2ecf20Sopenharmony_ci#define UDDR8 0x0700 /* UDC Endpoint 8 Data Register */ 888c2ecf20Sopenharmony_ci#define UDDR9 0x0900 /* UDC Endpoint 9 Data Register */ 898c2ecf20Sopenharmony_ci#define UDDR10 0x00C0 /* UDC Endpoint 10 Data Register */ 908c2ecf20Sopenharmony_ci#define UDDR11 0x0B00 /* UDC Endpoint 11 Data Register */ 918c2ecf20Sopenharmony_ci#define UDDR12 0x0B80 /* UDC Endpoint 12 Data Register */ 928c2ecf20Sopenharmony_ci#define UDDR13 0x0C00 /* UDC Endpoint 13 Data Register */ 938c2ecf20Sopenharmony_ci#define UDDR14 0x0E00 /* UDC Endpoint 14 Data Register */ 948c2ecf20Sopenharmony_ci#define UDDR15 0x00E0 /* UDC Endpoint 15 Data Register */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define UICR0 0x0050 /* UDC Interrupt Control Register 0 */ 978c2ecf20Sopenharmony_ci#define UICR1 0x0054 /* UDC Interrupt Control Register 1 */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define USIR0 0x0058 /* UDC Status Interrupt Register 0 */ 1008c2ecf20Sopenharmony_ci#define USIR1 0x005C /* UDC Status Interrupt Register 1 */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define UDCCR_UDE (1 << 0) /* UDC enable */ 1038c2ecf20Sopenharmony_ci#define UDCCR_UDA (1 << 1) /* UDC active */ 1048c2ecf20Sopenharmony_ci#define UDCCR_RSM (1 << 2) /* Device resume */ 1058c2ecf20Sopenharmony_ci#define UDCCR_RESIR (1 << 3) /* Resume interrupt request */ 1068c2ecf20Sopenharmony_ci#define UDCCR_SUSIR (1 << 4) /* Suspend interrupt request */ 1078c2ecf20Sopenharmony_ci#define UDCCR_SRM (1 << 5) /* Suspend/resume interrupt mask */ 1088c2ecf20Sopenharmony_ci#define UDCCR_RSTIR (1 << 6) /* Reset interrupt request */ 1098c2ecf20Sopenharmony_ci#define UDCCR_REM (1 << 7) /* Reset interrupt mask */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define UDCCS0_OPR (1 << 0) /* OUT packet ready */ 1128c2ecf20Sopenharmony_ci#define UDCCS0_IPR (1 << 1) /* IN packet ready */ 1138c2ecf20Sopenharmony_ci#define UDCCS0_FTF (1 << 2) /* Flush Tx FIFO */ 1148c2ecf20Sopenharmony_ci#define UDCCS0_DRWF (1 << 3) /* Device remote wakeup feature */ 1158c2ecf20Sopenharmony_ci#define UDCCS0_SST (1 << 4) /* Sent stall */ 1168c2ecf20Sopenharmony_ci#define UDCCS0_FST (1 << 5) /* Force stall */ 1178c2ecf20Sopenharmony_ci#define UDCCS0_RNE (1 << 6) /* Receive FIFO no empty */ 1188c2ecf20Sopenharmony_ci#define UDCCS0_SA (1 << 7) /* Setup active */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define UDCCS_BI_TFS (1 << 0) /* Transmit FIFO service */ 1218c2ecf20Sopenharmony_ci#define UDCCS_BI_TPC (1 << 1) /* Transmit packet complete */ 1228c2ecf20Sopenharmony_ci#define UDCCS_BI_FTF (1 << 2) /* Flush Tx FIFO */ 1238c2ecf20Sopenharmony_ci#define UDCCS_BI_TUR (1 << 3) /* Transmit FIFO underrun */ 1248c2ecf20Sopenharmony_ci#define UDCCS_BI_SST (1 << 4) /* Sent stall */ 1258c2ecf20Sopenharmony_ci#define UDCCS_BI_FST (1 << 5) /* Force stall */ 1268c2ecf20Sopenharmony_ci#define UDCCS_BI_TSP (1 << 7) /* Transmit short packet */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define UDCCS_BO_RFS (1 << 0) /* Receive FIFO service */ 1298c2ecf20Sopenharmony_ci#define UDCCS_BO_RPC (1 << 1) /* Receive packet complete */ 1308c2ecf20Sopenharmony_ci#define UDCCS_BO_DME (1 << 3) /* DMA enable */ 1318c2ecf20Sopenharmony_ci#define UDCCS_BO_SST (1 << 4) /* Sent stall */ 1328c2ecf20Sopenharmony_ci#define UDCCS_BO_FST (1 << 5) /* Force stall */ 1338c2ecf20Sopenharmony_ci#define UDCCS_BO_RNE (1 << 6) /* Receive FIFO not empty */ 1348c2ecf20Sopenharmony_ci#define UDCCS_BO_RSP (1 << 7) /* Receive short packet */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define UDCCS_II_TFS (1 << 0) /* Transmit FIFO service */ 1378c2ecf20Sopenharmony_ci#define UDCCS_II_TPC (1 << 1) /* Transmit packet complete */ 1388c2ecf20Sopenharmony_ci#define UDCCS_II_FTF (1 << 2) /* Flush Tx FIFO */ 1398c2ecf20Sopenharmony_ci#define UDCCS_II_TUR (1 << 3) /* Transmit FIFO underrun */ 1408c2ecf20Sopenharmony_ci#define UDCCS_II_TSP (1 << 7) /* Transmit short packet */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define UDCCS_IO_RFS (1 << 0) /* Receive FIFO service */ 1438c2ecf20Sopenharmony_ci#define UDCCS_IO_RPC (1 << 1) /* Receive packet complete */ 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_IXP4XX /* FIXME: is this right?, datasheed says '2' */ 1458c2ecf20Sopenharmony_ci#define UDCCS_IO_ROF (1 << 3) /* Receive overflow */ 1468c2ecf20Sopenharmony_ci#endif 1478c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_PXA 1488c2ecf20Sopenharmony_ci#define UDCCS_IO_ROF (1 << 2) /* Receive overflow */ 1498c2ecf20Sopenharmony_ci#endif 1508c2ecf20Sopenharmony_ci#define UDCCS_IO_DME (1 << 3) /* DMA enable */ 1518c2ecf20Sopenharmony_ci#define UDCCS_IO_RNE (1 << 6) /* Receive FIFO not empty */ 1528c2ecf20Sopenharmony_ci#define UDCCS_IO_RSP (1 << 7) /* Receive short packet */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define UDCCS_INT_TFS (1 << 0) /* Transmit FIFO service */ 1558c2ecf20Sopenharmony_ci#define UDCCS_INT_TPC (1 << 1) /* Transmit packet complete */ 1568c2ecf20Sopenharmony_ci#define UDCCS_INT_FTF (1 << 2) /* Flush Tx FIFO */ 1578c2ecf20Sopenharmony_ci#define UDCCS_INT_TUR (1 << 3) /* Transmit FIFO underrun */ 1588c2ecf20Sopenharmony_ci#define UDCCS_INT_SST (1 << 4) /* Sent stall */ 1598c2ecf20Sopenharmony_ci#define UDCCS_INT_FST (1 << 5) /* Force stall */ 1608c2ecf20Sopenharmony_ci#define UDCCS_INT_TSP (1 << 7) /* Transmit short packet */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define UICR0_IM0 (1 << 0) /* Interrupt mask ep 0 */ 1638c2ecf20Sopenharmony_ci#define UICR0_IM1 (1 << 1) /* Interrupt mask ep 1 */ 1648c2ecf20Sopenharmony_ci#define UICR0_IM2 (1 << 2) /* Interrupt mask ep 2 */ 1658c2ecf20Sopenharmony_ci#define UICR0_IM3 (1 << 3) /* Interrupt mask ep 3 */ 1668c2ecf20Sopenharmony_ci#define UICR0_IM4 (1 << 4) /* Interrupt mask ep 4 */ 1678c2ecf20Sopenharmony_ci#define UICR0_IM5 (1 << 5) /* Interrupt mask ep 5 */ 1688c2ecf20Sopenharmony_ci#define UICR0_IM6 (1 << 6) /* Interrupt mask ep 6 */ 1698c2ecf20Sopenharmony_ci#define UICR0_IM7 (1 << 7) /* Interrupt mask ep 7 */ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define UICR1_IM8 (1 << 0) /* Interrupt mask ep 8 */ 1728c2ecf20Sopenharmony_ci#define UICR1_IM9 (1 << 1) /* Interrupt mask ep 9 */ 1738c2ecf20Sopenharmony_ci#define UICR1_IM10 (1 << 2) /* Interrupt mask ep 10 */ 1748c2ecf20Sopenharmony_ci#define UICR1_IM11 (1 << 3) /* Interrupt mask ep 11 */ 1758c2ecf20Sopenharmony_ci#define UICR1_IM12 (1 << 4) /* Interrupt mask ep 12 */ 1768c2ecf20Sopenharmony_ci#define UICR1_IM13 (1 << 5) /* Interrupt mask ep 13 */ 1778c2ecf20Sopenharmony_ci#define UICR1_IM14 (1 << 6) /* Interrupt mask ep 14 */ 1788c2ecf20Sopenharmony_ci#define UICR1_IM15 (1 << 7) /* Interrupt mask ep 15 */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define USIR0_IR0 (1 << 0) /* Interrupt request ep 0 */ 1818c2ecf20Sopenharmony_ci#define USIR0_IR1 (1 << 1) /* Interrupt request ep 1 */ 1828c2ecf20Sopenharmony_ci#define USIR0_IR2 (1 << 2) /* Interrupt request ep 2 */ 1838c2ecf20Sopenharmony_ci#define USIR0_IR3 (1 << 3) /* Interrupt request ep 3 */ 1848c2ecf20Sopenharmony_ci#define USIR0_IR4 (1 << 4) /* Interrupt request ep 4 */ 1858c2ecf20Sopenharmony_ci#define USIR0_IR5 (1 << 5) /* Interrupt request ep 5 */ 1868c2ecf20Sopenharmony_ci#define USIR0_IR6 (1 << 6) /* Interrupt request ep 6 */ 1878c2ecf20Sopenharmony_ci#define USIR0_IR7 (1 << 7) /* Interrupt request ep 7 */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define USIR1_IR8 (1 << 0) /* Interrupt request ep 8 */ 1908c2ecf20Sopenharmony_ci#define USIR1_IR9 (1 << 1) /* Interrupt request ep 9 */ 1918c2ecf20Sopenharmony_ci#define USIR1_IR10 (1 << 2) /* Interrupt request ep 10 */ 1928c2ecf20Sopenharmony_ci#define USIR1_IR11 (1 << 3) /* Interrupt request ep 11 */ 1938c2ecf20Sopenharmony_ci#define USIR1_IR12 (1 << 4) /* Interrupt request ep 12 */ 1948c2ecf20Sopenharmony_ci#define USIR1_IR13 (1 << 5) /* Interrupt request ep 13 */ 1958c2ecf20Sopenharmony_ci#define USIR1_IR14 (1 << 6) /* Interrupt request ep 14 */ 1968c2ecf20Sopenharmony_ci#define USIR1_IR15 (1 << 7) /* Interrupt request ep 15 */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x 2008c2ecf20Sopenharmony_ci * series processors. The UDC for the IXP 4xx series is very similar. 2018c2ecf20Sopenharmony_ci * There are fifteen endpoints, in addition to ep0. 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * Such controller drivers work with a gadget driver. The gadget driver 2048c2ecf20Sopenharmony_ci * returns descriptors, implements configuration and data protocols used 2058c2ecf20Sopenharmony_ci * by the host to interact with this device, and allocates endpoints to 2068c2ecf20Sopenharmony_ci * the different protocol interfaces. The controller driver virtualizes 2078c2ecf20Sopenharmony_ci * usb hardware so that the gadget drivers will be more portable. 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * This UDC hardware wants to implement a bit too much USB protocol, so 2108c2ecf20Sopenharmony_ci * it constrains the sorts of USB configuration change events that work. 2118c2ecf20Sopenharmony_ci * The errata for these chips are misleading; some "fixed" bugs from 2128c2ecf20Sopenharmony_ci * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Note that the UDC hardware supports DMA (except on IXP) but that's 2158c2ecf20Sopenharmony_ci * not used here. IN-DMA (to host) is simple enough, when the data is 2168c2ecf20Sopenharmony_ci * suitably aligned (16 bytes) ... the network stack doesn't do that, 2178c2ecf20Sopenharmony_ci * other software can. OUT-DMA is buggy in most chip versions, as well 2188c2ecf20Sopenharmony_ci * as poorly designed (data toggle not automatic). So this driver won't 2198c2ecf20Sopenharmony_ci * bother using DMA. (Mostly-working IN-DMA support was available in 2208c2ecf20Sopenharmony_ci * kernels before 2.6.23, but was never enabled or well tested.) 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#define DRIVER_VERSION "30-June-2007" 2248c2ecf20Sopenharmony_ci#define DRIVER_DESC "PXA 25x USB Device Controller driver" 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const char driver_name [] = "pxa25x_udc"; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic const char ep0name [] = "ep0"; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_IXP4XX 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* cpu-specific register addresses are compiled in to this code */ 2358c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_PXA 2368c2ecf20Sopenharmony_ci#error "Can't configure both IXP and PXA" 2378c2ecf20Sopenharmony_ci#endif 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* IXP doesn't yet support <linux/clk.h> */ 2408c2ecf20Sopenharmony_ci#define clk_get(dev,name) NULL 2418c2ecf20Sopenharmony_ci#define clk_enable(clk) do { } while (0) 2428c2ecf20Sopenharmony_ci#define clk_disable(clk) do { } while (0) 2438c2ecf20Sopenharmony_ci#define clk_put(clk) do { } while (0) 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci#endif 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#include "pxa25x_udc.h" 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PXA25X_SMALL 2518c2ecf20Sopenharmony_ci#define SIZE_STR " (small)" 2528c2ecf20Sopenharmony_ci#else 2538c2ecf20Sopenharmony_ci#define SIZE_STR "" 2548c2ecf20Sopenharmony_ci#endif 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------- 2578c2ecf20Sopenharmony_ci * endpoint related parts of the api to the usb controller hardware, 2588c2ecf20Sopenharmony_ci * used by gadget driver; and the inner talker-to-hardware core. 2598c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void pxa25x_ep_fifo_flush (struct usb_ep *ep); 2638c2ecf20Sopenharmony_cistatic void nuke (struct pxa25x_ep *, int status); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* one GPIO should control a D+ pullup, so host sees this device (or not) */ 2668c2ecf20Sopenharmony_cistatic void pullup_off(void) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct pxa2xx_udc_mach_info *mach = the_controller->mach; 2698c2ecf20Sopenharmony_ci int off_level = mach->gpio_pullup_inverted; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (gpio_is_valid(mach->gpio_pullup)) 2728c2ecf20Sopenharmony_ci gpio_set_value(mach->gpio_pullup, off_level); 2738c2ecf20Sopenharmony_ci else if (mach->udc_command) 2748c2ecf20Sopenharmony_ci mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void pullup_on(void) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct pxa2xx_udc_mach_info *mach = the_controller->mach; 2808c2ecf20Sopenharmony_ci int on_level = !mach->gpio_pullup_inverted; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (gpio_is_valid(mach->gpio_pullup)) 2838c2ecf20Sopenharmony_ci gpio_set_value(mach->gpio_pullup, on_level); 2848c2ecf20Sopenharmony_ci else if (mach->udc_command) 2858c2ecf20Sopenharmony_ci mach->udc_command(PXA2XX_UDC_CMD_CONNECT); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_BIG_ENDIAN) 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * IXP4xx has its buses wired up in a way that relies on never doing any 2918c2ecf20Sopenharmony_ci * byte swaps, independent of whether it runs in big-endian or little-endian 2928c2ecf20Sopenharmony_ci * mode, as explained by Krzysztof Hałasa. 2938c2ecf20Sopenharmony_ci * 2948c2ecf20Sopenharmony_ci * We only support pxa25x in little-endian mode, but it is very likely 2958c2ecf20Sopenharmony_ci * that it works the same way. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic inline void udc_set_reg(struct pxa25x_udc *dev, u32 reg, u32 val) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci iowrite32be(val, dev->regs + reg); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic inline u32 udc_get_reg(struct pxa25x_udc *dev, u32 reg) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci return ioread32be(dev->regs + reg); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci#else 3078c2ecf20Sopenharmony_cistatic inline void udc_set_reg(struct pxa25x_udc *dev, u32 reg, u32 val) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci writel(val, dev->regs + reg); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic inline u32 udc_get_reg(struct pxa25x_udc *dev, u32 reg) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci return readl(dev->regs + reg); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci#endif 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void pio_irq_enable(struct pxa25x_ep *ep) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci u32 bEndpointAddress = ep->bEndpointAddress & 0xf; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (bEndpointAddress < 8) 3238c2ecf20Sopenharmony_ci udc_set_reg(ep->dev, UICR0, udc_get_reg(ep->dev, UICR0) & 3248c2ecf20Sopenharmony_ci ~(1 << bEndpointAddress)); 3258c2ecf20Sopenharmony_ci else { 3268c2ecf20Sopenharmony_ci bEndpointAddress -= 8; 3278c2ecf20Sopenharmony_ci udc_set_reg(ep->dev, UICR1, udc_get_reg(ep->dev, UICR1) & 3288c2ecf20Sopenharmony_ci ~(1 << bEndpointAddress)); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void pio_irq_disable(struct pxa25x_ep *ep) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci u32 bEndpointAddress = ep->bEndpointAddress & 0xf; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (bEndpointAddress < 8) 3378c2ecf20Sopenharmony_ci udc_set_reg(ep->dev, UICR0, udc_get_reg(ep->dev, UICR0) | 3388c2ecf20Sopenharmony_ci (1 << bEndpointAddress)); 3398c2ecf20Sopenharmony_ci else { 3408c2ecf20Sopenharmony_ci bEndpointAddress -= 8; 3418c2ecf20Sopenharmony_ci udc_set_reg(ep->dev, UICR1, udc_get_reg(ep->dev, UICR1) | 3428c2ecf20Sopenharmony_ci (1 << bEndpointAddress)); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* The UDCCR reg contains mask and interrupt status bits, 3478c2ecf20Sopenharmony_ci * so using '|=' isn't safe as it may ack an interrupt. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic inline void udc_set_mask_UDCCR(struct pxa25x_udc *dev, int mask) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci u32 udccr = udc_get_reg(dev, UDCCR); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci udc_set_reg(dev, (udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic inline void udc_clear_mask_UDCCR(struct pxa25x_udc *dev, int mask) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci u32 udccr = udc_get_reg(dev, UDCCR); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci udc_set_reg(dev, (udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic inline void udc_ack_int_UDCCR(struct pxa25x_udc *dev, int mask) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci /* udccr contains the bits we dont want to change */ 3688c2ecf20Sopenharmony_ci u32 udccr = udc_get_reg(dev, UDCCR) & UDCCR_MASK_BITS; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci udc_set_reg(dev, udccr | (mask & ~UDCCR_MASK_BITS), UDCCR); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *ep) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci return udc_get_reg(ep->dev, ep->regoff_udccs); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic inline void udc_ep_set_UDCCS(struct pxa25x_ep *ep, u32 data) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci udc_set_reg(ep->dev, data, ep->regoff_udccs); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic inline u32 udc_ep0_get_UDCCS(struct pxa25x_udc *dev) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci return udc_get_reg(dev, UDCCS0); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic inline void udc_ep0_set_UDCCS(struct pxa25x_udc *dev, u32 data) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci udc_set_reg(dev, data, UDCCS0); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic inline u32 udc_ep_get_UDDR(struct pxa25x_ep *ep) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci return udc_get_reg(ep->dev, ep->regoff_uddr); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic inline void udc_ep_set_UDDR(struct pxa25x_ep *ep, u32 data) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci udc_set_reg(ep->dev, data, ep->regoff_uddr); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic inline u32 udc_ep_get_UBCR(struct pxa25x_ep *ep) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci return udc_get_reg(ep->dev, ep->regoff_ubcr); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* 4098c2ecf20Sopenharmony_ci * endpoint enable/disable 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * we need to verify the descriptors used to enable endpoints. since pxa25x 4128c2ecf20Sopenharmony_ci * endpoint configurations are fixed, and are pretty much always enabled, 4138c2ecf20Sopenharmony_ci * there's not a lot to manage here. 4148c2ecf20Sopenharmony_ci * 4158c2ecf20Sopenharmony_ci * because pxa25x can't selectively initialize bulk (or interrupt) endpoints, 4168c2ecf20Sopenharmony_ci * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except 4178c2ecf20Sopenharmony_ci * for a single interface (with only the default altsetting) and for gadget 4188c2ecf20Sopenharmony_ci * drivers that don't halt endpoints (not reset by set_interface). that also 4198c2ecf20Sopenharmony_ci * means that if you use ISO, you must violate the USB spec rule that all 4208c2ecf20Sopenharmony_ci * iso endpoints must be in non-default altsettings. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic int pxa25x_ep_enable (struct usb_ep *_ep, 4238c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 4268c2ecf20Sopenharmony_ci struct pxa25x_udc *dev; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ep = container_of (_ep, struct pxa25x_ep, ep); 4298c2ecf20Sopenharmony_ci if (!_ep || !desc || _ep->name == ep0name 4308c2ecf20Sopenharmony_ci || desc->bDescriptorType != USB_DT_ENDPOINT 4318c2ecf20Sopenharmony_ci || ep->bEndpointAddress != desc->bEndpointAddress 4328c2ecf20Sopenharmony_ci || ep->fifo_size < usb_endpoint_maxp (desc)) { 4338c2ecf20Sopenharmony_ci DMSG("%s, bad ep or descriptor\n", __func__); 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* xfer types must match, except that interrupt ~= bulk */ 4388c2ecf20Sopenharmony_ci if (ep->bmAttributes != desc->bmAttributes 4398c2ecf20Sopenharmony_ci && ep->bmAttributes != USB_ENDPOINT_XFER_BULK 4408c2ecf20Sopenharmony_ci && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { 4418c2ecf20Sopenharmony_ci DMSG("%s, %s type mismatch\n", __func__, _ep->name); 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* hardware _could_ do smaller, but driver doesn't */ 4468c2ecf20Sopenharmony_ci if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK 4478c2ecf20Sopenharmony_ci && usb_endpoint_maxp (desc) 4488c2ecf20Sopenharmony_ci != BULK_FIFO_SIZE) 4498c2ecf20Sopenharmony_ci || !desc->wMaxPacketSize) { 4508c2ecf20Sopenharmony_ci DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); 4518c2ecf20Sopenharmony_ci return -ERANGE; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci dev = ep->dev; 4558c2ecf20Sopenharmony_ci if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { 4568c2ecf20Sopenharmony_ci DMSG("%s, bogus device state\n", __func__); 4578c2ecf20Sopenharmony_ci return -ESHUTDOWN; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ep->ep.desc = desc; 4618c2ecf20Sopenharmony_ci ep->stopped = 0; 4628c2ecf20Sopenharmony_ci ep->pio_irqs = 0; 4638c2ecf20Sopenharmony_ci ep->ep.maxpacket = usb_endpoint_maxp (desc); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* flush fifo (mostly for OUT buffers) */ 4668c2ecf20Sopenharmony_ci pxa25x_ep_fifo_flush (_ep); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* ... reset halt state too, if we could ... */ 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int pxa25x_ep_disable (struct usb_ep *_ep) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 4778c2ecf20Sopenharmony_ci unsigned long flags; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ep = container_of (_ep, struct pxa25x_ep, ep); 4808c2ecf20Sopenharmony_ci if (!_ep || !ep->ep.desc) { 4818c2ecf20Sopenharmony_ci DMSG("%s, %s not enabled\n", __func__, 4828c2ecf20Sopenharmony_ci _ep ? ep->ep.name : NULL); 4838c2ecf20Sopenharmony_ci return -EINVAL; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci local_irq_save(flags); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci nuke (ep, -ESHUTDOWN); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* flush fifo (mostly for IN buffers) */ 4908c2ecf20Sopenharmony_ci pxa25x_ep_fifo_flush (_ep); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ep->ep.desc = NULL; 4938c2ecf20Sopenharmony_ci ep->stopped = 1; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci local_irq_restore(flags); 4968c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* for the pxa25x, these can just wrap kmalloc/kfree. gadget drivers 5038c2ecf20Sopenharmony_ci * must still pass correctly initialized endpoints, since other controller 5048c2ecf20Sopenharmony_ci * drivers may care about how it's currently set up (dma issues etc). 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci/* 5088c2ecf20Sopenharmony_ci * pxa25x_ep_alloc_request - allocate a request data structure 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_cistatic struct usb_request * 5118c2ecf20Sopenharmony_cipxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct pxa25x_request *req; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci req = kzalloc(sizeof(*req), gfp_flags); 5168c2ecf20Sopenharmony_ci if (!req) 5178c2ecf20Sopenharmony_ci return NULL; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&req->queue); 5208c2ecf20Sopenharmony_ci return &req->req; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* 5258c2ecf20Sopenharmony_ci * pxa25x_ep_free_request - deallocate a request data structure 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic void 5288c2ecf20Sopenharmony_cipxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct pxa25x_request *req; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci req = container_of (_req, struct pxa25x_request, req); 5338c2ecf20Sopenharmony_ci WARN_ON(!list_empty (&req->queue)); 5348c2ecf20Sopenharmony_ci kfree(req); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* 5408c2ecf20Sopenharmony_ci * done - retire a request; caller blocked irqs 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_cistatic void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci unsigned stopped = ep->stopped; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci list_del_init(&req->queue); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (likely (req->req.status == -EINPROGRESS)) 5498c2ecf20Sopenharmony_ci req->req.status = status; 5508c2ecf20Sopenharmony_ci else 5518c2ecf20Sopenharmony_ci status = req->req.status; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (status && status != -ESHUTDOWN) 5548c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", 5558c2ecf20Sopenharmony_ci ep->ep.name, &req->req, status, 5568c2ecf20Sopenharmony_ci req->req.actual, req->req.length); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* don't modify queue heads during completion callback */ 5598c2ecf20Sopenharmony_ci ep->stopped = 1; 5608c2ecf20Sopenharmony_ci usb_gadget_giveback_request(&ep->ep, &req->req); 5618c2ecf20Sopenharmony_ci ep->stopped = stopped; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic inline void ep0_idle (struct pxa25x_udc *dev) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci dev->ep0state = EP0_IDLE; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int 5718c2ecf20Sopenharmony_ciwrite_packet(struct pxa25x_ep *ep, struct pxa25x_request *req, unsigned max) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci u8 *buf; 5748c2ecf20Sopenharmony_ci unsigned length, count; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci buf = req->req.buf + req->req.actual; 5778c2ecf20Sopenharmony_ci prefetch(buf); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* how big will this packet be? */ 5808c2ecf20Sopenharmony_ci length = min(req->req.length - req->req.actual, max); 5818c2ecf20Sopenharmony_ci req->req.actual += length; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci count = length; 5848c2ecf20Sopenharmony_ci while (likely(count--)) 5858c2ecf20Sopenharmony_ci udc_ep_set_UDDR(ep, *buf++); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return length; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* 5918c2ecf20Sopenharmony_ci * write to an IN endpoint fifo, as many packets as possible. 5928c2ecf20Sopenharmony_ci * irqs will use this to write the rest later. 5938c2ecf20Sopenharmony_ci * caller guarantees at least one packet buffer is ready (or a zlp). 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_cistatic int 5968c2ecf20Sopenharmony_ciwrite_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci unsigned max; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci max = usb_endpoint_maxp(ep->ep.desc); 6018c2ecf20Sopenharmony_ci do { 6028c2ecf20Sopenharmony_ci unsigned count; 6038c2ecf20Sopenharmony_ci int is_last, is_short; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci count = write_packet(ep, req, max); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* last packet is usually short (or a zlp) */ 6088c2ecf20Sopenharmony_ci if (unlikely (count != max)) 6098c2ecf20Sopenharmony_ci is_last = is_short = 1; 6108c2ecf20Sopenharmony_ci else { 6118c2ecf20Sopenharmony_ci if (likely(req->req.length != req->req.actual) 6128c2ecf20Sopenharmony_ci || req->req.zero) 6138c2ecf20Sopenharmony_ci is_last = 0; 6148c2ecf20Sopenharmony_ci else 6158c2ecf20Sopenharmony_ci is_last = 1; 6168c2ecf20Sopenharmony_ci /* interrupt/iso maxpacket may not fill the fifo */ 6178c2ecf20Sopenharmony_ci is_short = unlikely (max < ep->fifo_size); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", 6218c2ecf20Sopenharmony_ci ep->ep.name, count, 6228c2ecf20Sopenharmony_ci is_last ? "/L" : "", is_short ? "/S" : "", 6238c2ecf20Sopenharmony_ci req->req.length - req->req.actual, req); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* let loose that packet. maybe try writing another one, 6268c2ecf20Sopenharmony_ci * double buffering might work. TSP, TPC, and TFS 6278c2ecf20Sopenharmony_ci * bit values are the same for all normal IN endpoints. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, UDCCS_BI_TPC); 6308c2ecf20Sopenharmony_ci if (is_short) 6318c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, UDCCS_BI_TSP); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* requests complete when all IN data is in the FIFO */ 6348c2ecf20Sopenharmony_ci if (is_last) { 6358c2ecf20Sopenharmony_ci done (ep, req, 0); 6368c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) 6378c2ecf20Sopenharmony_ci pio_irq_disable(ep); 6388c2ecf20Sopenharmony_ci return 1; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci // TODO experiment: how robust can fifo mode tweaking be? 6428c2ecf20Sopenharmony_ci // double buffering is off in the default fifo mode, which 6438c2ecf20Sopenharmony_ci // prevents TFS from being set here. 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci } while (udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS); 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/* caller asserts req->pending (ep0 irq status nyet cleared); starts 6508c2ecf20Sopenharmony_ci * ep0 data stage. these chips want very simple state transitions. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic inline 6538c2ecf20Sopenharmony_civoid ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, flags|UDCCS0_SA|UDCCS0_OPR); 6568c2ecf20Sopenharmony_ci udc_set_reg(dev, USIR0, USIR0_IR0); 6578c2ecf20Sopenharmony_ci dev->req_pending = 0; 6588c2ecf20Sopenharmony_ci DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", 6598c2ecf20Sopenharmony_ci __func__, tag, udc_ep0_get_UDCCS(dev), flags); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int 6638c2ecf20Sopenharmony_ciwrite_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = ep->dev; 6668c2ecf20Sopenharmony_ci unsigned count; 6678c2ecf20Sopenharmony_ci int is_short; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci count = write_packet(&dev->ep[0], req, EP0_FIFO_SIZE); 6708c2ecf20Sopenharmony_ci ep->dev->stats.write.bytes += count; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* last packet "must be" short (or a zlp) */ 6738c2ecf20Sopenharmony_ci is_short = (count != EP0_FIFO_SIZE); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, 6768c2ecf20Sopenharmony_ci req->req.length - req->req.actual, req); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (unlikely (is_short)) { 6798c2ecf20Sopenharmony_ci if (ep->dev->req_pending) 6808c2ecf20Sopenharmony_ci ep0start(ep->dev, UDCCS0_IPR, "short IN"); 6818c2ecf20Sopenharmony_ci else 6828c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_IPR); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci count = req->req.length; 6858c2ecf20Sopenharmony_ci done (ep, req, 0); 6868c2ecf20Sopenharmony_ci ep0_idle(ep->dev); 6878c2ecf20Sopenharmony_ci#ifndef CONFIG_ARCH_IXP4XX 6888c2ecf20Sopenharmony_ci#if 1 6898c2ecf20Sopenharmony_ci /* This seems to get rid of lost status irqs in some cases: 6908c2ecf20Sopenharmony_ci * host responds quickly, or next request involves config 6918c2ecf20Sopenharmony_ci * change automagic, or should have been hidden, or ... 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * FIXME get rid of all udelays possible... 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci if (count >= EP0_FIFO_SIZE) { 6968c2ecf20Sopenharmony_ci count = 100; 6978c2ecf20Sopenharmony_ci do { 6988c2ecf20Sopenharmony_ci if ((udc_ep0_get_UDCCS(dev) & UDCCS0_OPR) != 0) { 6998c2ecf20Sopenharmony_ci /* clear OPR, generate ack */ 7008c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_OPR); 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci count--; 7048c2ecf20Sopenharmony_ci udelay(1); 7058c2ecf20Sopenharmony_ci } while (count); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci#endif 7088c2ecf20Sopenharmony_ci#endif 7098c2ecf20Sopenharmony_ci } else if (ep->dev->req_pending) 7108c2ecf20Sopenharmony_ci ep0start(ep->dev, 0, "IN"); 7118c2ecf20Sopenharmony_ci return is_short; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* 7168c2ecf20Sopenharmony_ci * read_fifo - unload packet(s) from the fifo we use for usb OUT 7178c2ecf20Sopenharmony_ci * transfers and put them into the request. caller should have made 7188c2ecf20Sopenharmony_ci * sure there's at least one packet ready. 7198c2ecf20Sopenharmony_ci * 7208c2ecf20Sopenharmony_ci * returns true if the request completed because of short packet or the 7218c2ecf20Sopenharmony_ci * request buffer having filled (and maybe overran till end-of-packet). 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_cistatic int 7248c2ecf20Sopenharmony_ciread_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci for (;;) { 7278c2ecf20Sopenharmony_ci u32 udccs; 7288c2ecf20Sopenharmony_ci u8 *buf; 7298c2ecf20Sopenharmony_ci unsigned bufferspace, count, is_short; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* make sure there's a packet in the FIFO. 7328c2ecf20Sopenharmony_ci * UDCCS_{BO,IO}_RPC are all the same bit value. 7338c2ecf20Sopenharmony_ci * UDCCS_{BO,IO}_RNE are all the same bit value. 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci udccs = udc_ep_get_UDCCS(ep); 7368c2ecf20Sopenharmony_ci if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci buf = req->req.buf + req->req.actual; 7398c2ecf20Sopenharmony_ci prefetchw(buf); 7408c2ecf20Sopenharmony_ci bufferspace = req->req.length - req->req.actual; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* read all bytes from this packet */ 7438c2ecf20Sopenharmony_ci if (likely (udccs & UDCCS_BO_RNE)) { 7448c2ecf20Sopenharmony_ci count = 1 + (0x0ff & udc_ep_get_UBCR(ep)); 7458c2ecf20Sopenharmony_ci req->req.actual += min (count, bufferspace); 7468c2ecf20Sopenharmony_ci } else /* zlp */ 7478c2ecf20Sopenharmony_ci count = 0; 7488c2ecf20Sopenharmony_ci is_short = (count < ep->ep.maxpacket); 7498c2ecf20Sopenharmony_ci DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", 7508c2ecf20Sopenharmony_ci ep->ep.name, udccs, count, 7518c2ecf20Sopenharmony_ci is_short ? "/S" : "", 7528c2ecf20Sopenharmony_ci req, req->req.actual, req->req.length); 7538c2ecf20Sopenharmony_ci while (likely (count-- != 0)) { 7548c2ecf20Sopenharmony_ci u8 byte = (u8) udc_ep_get_UDDR(ep); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (unlikely (bufferspace == 0)) { 7578c2ecf20Sopenharmony_ci /* this happens when the driver's buffer 7588c2ecf20Sopenharmony_ci * is smaller than what the host sent. 7598c2ecf20Sopenharmony_ci * discard the extra data. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci if (req->req.status != -EOVERFLOW) 7628c2ecf20Sopenharmony_ci DMSG("%s overflow %d\n", 7638c2ecf20Sopenharmony_ci ep->ep.name, count); 7648c2ecf20Sopenharmony_ci req->req.status = -EOVERFLOW; 7658c2ecf20Sopenharmony_ci } else { 7668c2ecf20Sopenharmony_ci *buf++ = byte; 7678c2ecf20Sopenharmony_ci bufferspace--; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, UDCCS_BO_RPC); 7718c2ecf20Sopenharmony_ci /* RPC/RSP/RNE could now reflect the other packet buffer */ 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* iso is one request per packet */ 7748c2ecf20Sopenharmony_ci if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { 7758c2ecf20Sopenharmony_ci if (udccs & UDCCS_IO_ROF) 7768c2ecf20Sopenharmony_ci req->req.status = -EHOSTUNREACH; 7778c2ecf20Sopenharmony_ci /* more like "is_done" */ 7788c2ecf20Sopenharmony_ci is_short = 1; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* completion */ 7828c2ecf20Sopenharmony_ci if (is_short || req->req.actual == req->req.length) { 7838c2ecf20Sopenharmony_ci done (ep, req, 0); 7848c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) 7858c2ecf20Sopenharmony_ci pio_irq_disable(ep); 7868c2ecf20Sopenharmony_ci return 1; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* finished that packet. the next one may be waiting... */ 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/* 7958c2ecf20Sopenharmony_ci * special ep0 version of the above. no UBCR0 or double buffering; status 7968c2ecf20Sopenharmony_ci * handshaking is magic. most device protocols don't need control-OUT. 7978c2ecf20Sopenharmony_ci * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other 7988c2ecf20Sopenharmony_ci * protocols do use them. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_cistatic int 8018c2ecf20Sopenharmony_ciread_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci u8 *buf, byte; 8048c2ecf20Sopenharmony_ci unsigned bufferspace; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci buf = req->req.buf + req->req.actual; 8078c2ecf20Sopenharmony_ci bufferspace = req->req.length - req->req.actual; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci while (udc_ep_get_UDCCS(ep) & UDCCS0_RNE) { 8108c2ecf20Sopenharmony_ci byte = (u8) UDDR0; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (unlikely (bufferspace == 0)) { 8138c2ecf20Sopenharmony_ci /* this happens when the driver's buffer 8148c2ecf20Sopenharmony_ci * is smaller than what the host sent. 8158c2ecf20Sopenharmony_ci * discard the extra data. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci if (req->req.status != -EOVERFLOW) 8188c2ecf20Sopenharmony_ci DMSG("%s overflow\n", ep->ep.name); 8198c2ecf20Sopenharmony_ci req->req.status = -EOVERFLOW; 8208c2ecf20Sopenharmony_ci } else { 8218c2ecf20Sopenharmony_ci *buf++ = byte; 8228c2ecf20Sopenharmony_ci req->req.actual++; 8238c2ecf20Sopenharmony_ci bufferspace--; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, UDCCS0_OPR | UDCCS0_IPR); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* completion */ 8308c2ecf20Sopenharmony_ci if (req->req.actual >= req->req.length) 8318c2ecf20Sopenharmony_ci return 1; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* finished that packet. the next one may be waiting... */ 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic int 8408c2ecf20Sopenharmony_cipxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct pxa25x_request *req; 8438c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 8448c2ecf20Sopenharmony_ci struct pxa25x_udc *dev; 8458c2ecf20Sopenharmony_ci unsigned long flags; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci req = container_of(_req, struct pxa25x_request, req); 8488c2ecf20Sopenharmony_ci if (unlikely (!_req || !_req->complete || !_req->buf 8498c2ecf20Sopenharmony_ci || !list_empty(&req->queue))) { 8508c2ecf20Sopenharmony_ci DMSG("%s, bad params\n", __func__); 8518c2ecf20Sopenharmony_ci return -EINVAL; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci ep = container_of(_ep, struct pxa25x_ep, ep); 8558c2ecf20Sopenharmony_ci if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) { 8568c2ecf20Sopenharmony_ci DMSG("%s, bad ep\n", __func__); 8578c2ecf20Sopenharmony_ci return -EINVAL; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci dev = ep->dev; 8618c2ecf20Sopenharmony_ci if (unlikely (!dev->driver 8628c2ecf20Sopenharmony_ci || dev->gadget.speed == USB_SPEED_UNKNOWN)) { 8638c2ecf20Sopenharmony_ci DMSG("%s, bogus device state\n", __func__); 8648c2ecf20Sopenharmony_ci return -ESHUTDOWN; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* iso is always one packet per request, that's the only way 8688c2ecf20Sopenharmony_ci * we can report per-packet status. that also helps with dma. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC 8718c2ecf20Sopenharmony_ci && req->req.length > usb_endpoint_maxp(ep->ep.desc))) 8728c2ecf20Sopenharmony_ci return -EMSGSIZE; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", 8758c2ecf20Sopenharmony_ci _ep->name, _req, _req->length, _req->buf); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci local_irq_save(flags); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci _req->status = -EINPROGRESS; 8808c2ecf20Sopenharmony_ci _req->actual = 0; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* kickstart this i/o queue? */ 8838c2ecf20Sopenharmony_ci if (list_empty(&ep->queue) && !ep->stopped) { 8848c2ecf20Sopenharmony_ci if (ep->ep.desc == NULL/* ep0 */) { 8858c2ecf20Sopenharmony_ci unsigned length = _req->length; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci switch (dev->ep0state) { 8888c2ecf20Sopenharmony_ci case EP0_IN_DATA_PHASE: 8898c2ecf20Sopenharmony_ci dev->stats.write.ops++; 8908c2ecf20Sopenharmony_ci if (write_ep0_fifo(ep, req)) 8918c2ecf20Sopenharmony_ci req = NULL; 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci case EP0_OUT_DATA_PHASE: 8958c2ecf20Sopenharmony_ci dev->stats.read.ops++; 8968c2ecf20Sopenharmony_ci /* messy ... */ 8978c2ecf20Sopenharmony_ci if (dev->req_config) { 8988c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "ep0 config ack%s\n", 8998c2ecf20Sopenharmony_ci dev->has_cfr ? "" : " raced"); 9008c2ecf20Sopenharmony_ci if (dev->has_cfr) 9018c2ecf20Sopenharmony_ci udc_set_reg(dev, UDCCFR, UDCCFR_AREN | 9028c2ecf20Sopenharmony_ci UDCCFR_ACM | UDCCFR_MB1); 9038c2ecf20Sopenharmony_ci done(ep, req, 0); 9048c2ecf20Sopenharmony_ci dev->ep0state = EP0_END_XFER; 9058c2ecf20Sopenharmony_ci local_irq_restore (flags); 9068c2ecf20Sopenharmony_ci return 0; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci if (dev->req_pending) 9098c2ecf20Sopenharmony_ci ep0start(dev, UDCCS0_IPR, "OUT"); 9108c2ecf20Sopenharmony_ci if (length == 0 || ((udc_ep0_get_UDCCS(dev) & UDCCS0_RNE) != 0 9118c2ecf20Sopenharmony_ci && read_ep0_fifo(ep, req))) { 9128c2ecf20Sopenharmony_ci ep0_idle(dev); 9138c2ecf20Sopenharmony_ci done(ep, req, 0); 9148c2ecf20Sopenharmony_ci req = NULL; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci default: 9198c2ecf20Sopenharmony_ci DMSG("ep0 i/o, odd state %d\n", dev->ep0state); 9208c2ecf20Sopenharmony_ci local_irq_restore (flags); 9218c2ecf20Sopenharmony_ci return -EL2HLT; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci /* can the FIFO can satisfy the request immediately? */ 9248c2ecf20Sopenharmony_ci } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { 9258c2ecf20Sopenharmony_ci if ((udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS) != 0 9268c2ecf20Sopenharmony_ci && write_fifo(ep, req)) 9278c2ecf20Sopenharmony_ci req = NULL; 9288c2ecf20Sopenharmony_ci } else if ((udc_ep_get_UDCCS(ep) & UDCCS_BO_RFS) != 0 9298c2ecf20Sopenharmony_ci && read_fifo(ep, req)) { 9308c2ecf20Sopenharmony_ci req = NULL; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (likely(req && ep->ep.desc)) 9348c2ecf20Sopenharmony_ci pio_irq_enable(ep); 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* pio or dma irq handler advances the queue. */ 9388c2ecf20Sopenharmony_ci if (likely(req != NULL)) 9398c2ecf20Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 9408c2ecf20Sopenharmony_ci local_irq_restore(flags); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci/* 9478c2ecf20Sopenharmony_ci * nuke - dequeue ALL requests 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_cistatic void nuke(struct pxa25x_ep *ep, int status) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct pxa25x_request *req; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* called with irqs blocked */ 9548c2ecf20Sopenharmony_ci while (!list_empty(&ep->queue)) { 9558c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, 9568c2ecf20Sopenharmony_ci struct pxa25x_request, 9578c2ecf20Sopenharmony_ci queue); 9588c2ecf20Sopenharmony_ci done(ep, req, status); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci if (ep->ep.desc) 9618c2ecf20Sopenharmony_ci pio_irq_disable(ep); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/* dequeue JUST ONE request */ 9668c2ecf20Sopenharmony_cistatic int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 9698c2ecf20Sopenharmony_ci struct pxa25x_request *req; 9708c2ecf20Sopenharmony_ci unsigned long flags; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci ep = container_of(_ep, struct pxa25x_ep, ep); 9738c2ecf20Sopenharmony_ci if (!_ep || ep->ep.name == ep0name) 9748c2ecf20Sopenharmony_ci return -EINVAL; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci local_irq_save(flags); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* make sure it's actually queued on this endpoint */ 9798c2ecf20Sopenharmony_ci list_for_each_entry (req, &ep->queue, queue) { 9808c2ecf20Sopenharmony_ci if (&req->req == _req) 9818c2ecf20Sopenharmony_ci break; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci if (&req->req != _req) { 9848c2ecf20Sopenharmony_ci local_irq_restore(flags); 9858c2ecf20Sopenharmony_ci return -EINVAL; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci done(ep, req, -ECONNRESET); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci local_irq_restore(flags); 9918c2ecf20Sopenharmony_ci return 0; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 9998c2ecf20Sopenharmony_ci unsigned long flags; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci ep = container_of(_ep, struct pxa25x_ep, ep); 10028c2ecf20Sopenharmony_ci if (unlikely (!_ep 10038c2ecf20Sopenharmony_ci || (!ep->ep.desc && ep->ep.name != ep0name)) 10048c2ecf20Sopenharmony_ci || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { 10058c2ecf20Sopenharmony_ci DMSG("%s, bad ep\n", __func__); 10068c2ecf20Sopenharmony_ci return -EINVAL; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci if (value == 0) { 10098c2ecf20Sopenharmony_ci /* this path (reset toggle+halt) is needed to implement 10108c2ecf20Sopenharmony_ci * SET_INTERFACE on normal hardware. but it can't be 10118c2ecf20Sopenharmony_ci * done from software on the PXA UDC, and the hardware 10128c2ecf20Sopenharmony_ci * forgets to do it as part of SET_INTERFACE automagic. 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci DMSG("only host can clear %s halt\n", _ep->name); 10158c2ecf20Sopenharmony_ci return -EROFS; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci local_irq_save(flags); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if ((ep->bEndpointAddress & USB_DIR_IN) != 0 10218c2ecf20Sopenharmony_ci && ((udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS) == 0 10228c2ecf20Sopenharmony_ci || !list_empty(&ep->queue))) { 10238c2ecf20Sopenharmony_ci local_irq_restore(flags); 10248c2ecf20Sopenharmony_ci return -EAGAIN; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* FST bit is the same for control, bulk in, bulk out, interrupt in */ 10288c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, UDCCS_BI_FST|UDCCS_BI_FTF); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* ep0 needs special care */ 10318c2ecf20Sopenharmony_ci if (!ep->ep.desc) { 10328c2ecf20Sopenharmony_ci start_watchdog(ep->dev); 10338c2ecf20Sopenharmony_ci ep->dev->req_pending = 0; 10348c2ecf20Sopenharmony_ci ep->dev->ep0state = EP0_STALL; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* and bulk/intr endpoints like dropping stalls too */ 10378c2ecf20Sopenharmony_ci } else { 10388c2ecf20Sopenharmony_ci unsigned i; 10398c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i += 20) { 10408c2ecf20Sopenharmony_ci if (udc_ep_get_UDCCS(ep) & UDCCS_BI_SST) 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci udelay(20); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci local_irq_restore(flags); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "%s halt\n", _ep->name); 10488c2ecf20Sopenharmony_ci return 0; 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic int pxa25x_ep_fifo_status(struct usb_ep *_ep) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ep = container_of(_ep, struct pxa25x_ep, ep); 10568c2ecf20Sopenharmony_ci if (!_ep) { 10578c2ecf20Sopenharmony_ci DMSG("%s, bad ep\n", __func__); 10588c2ecf20Sopenharmony_ci return -ENODEV; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci /* pxa can't report unclaimed bytes from IN fifos */ 10618c2ecf20Sopenharmony_ci if ((ep->bEndpointAddress & USB_DIR_IN) != 0) 10628c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10638c2ecf20Sopenharmony_ci if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN 10648c2ecf20Sopenharmony_ci || (udc_ep_get_UDCCS(ep) & UDCCS_BO_RFS) == 0) 10658c2ecf20Sopenharmony_ci return 0; 10668c2ecf20Sopenharmony_ci else 10678c2ecf20Sopenharmony_ci return (udc_ep_get_UBCR(ep) & 0xfff) + 1; 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic void pxa25x_ep_fifo_flush(struct usb_ep *_ep) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci struct pxa25x_ep *ep; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci ep = container_of(_ep, struct pxa25x_ep, ep); 10758c2ecf20Sopenharmony_ci if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { 10768c2ecf20Sopenharmony_ci DMSG("%s, bad ep\n", __func__); 10778c2ecf20Sopenharmony_ci return; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* toggle and halt bits stay unchanged */ 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* for OUT, just read and discard the FIFO contents. */ 10838c2ecf20Sopenharmony_ci if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { 10848c2ecf20Sopenharmony_ci while (((udc_ep_get_UDCCS(ep)) & UDCCS_BO_RNE) != 0) 10858c2ecf20Sopenharmony_ci (void)udc_ep_get_UDDR(ep); 10868c2ecf20Sopenharmony_ci return; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* most IN status is the same, but ISO can't stall */ 10908c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR 10918c2ecf20Sopenharmony_ci | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC 10928c2ecf20Sopenharmony_ci ? 0 : UDCCS_BI_SST)); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic struct usb_ep_ops pxa25x_ep_ops = { 10978c2ecf20Sopenharmony_ci .enable = pxa25x_ep_enable, 10988c2ecf20Sopenharmony_ci .disable = pxa25x_ep_disable, 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci .alloc_request = pxa25x_ep_alloc_request, 11018c2ecf20Sopenharmony_ci .free_request = pxa25x_ep_free_request, 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci .queue = pxa25x_ep_queue, 11048c2ecf20Sopenharmony_ci .dequeue = pxa25x_ep_dequeue, 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci .set_halt = pxa25x_ep_set_halt, 11078c2ecf20Sopenharmony_ci .fifo_status = pxa25x_ep_fifo_status, 11088c2ecf20Sopenharmony_ci .fifo_flush = pxa25x_ep_fifo_flush, 11098c2ecf20Sopenharmony_ci}; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------- 11138c2ecf20Sopenharmony_ci * device-scoped parts of the api to the usb controller hardware 11148c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int pxa25x_udc_get_frame(struct usb_gadget *_gadget) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci struct pxa25x_udc *dev; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci dev = container_of(_gadget, struct pxa25x_udc, gadget); 11228c2ecf20Sopenharmony_ci return ((udc_get_reg(dev, UFNRH) & 0x07) << 8) | 11238c2ecf20Sopenharmony_ci (udc_get_reg(dev, UFNRL) & 0xff); 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic int pxa25x_udc_wakeup(struct usb_gadget *_gadget) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct pxa25x_udc *udc; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci udc = container_of(_gadget, struct pxa25x_udc, gadget); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* host may not have enabled remote wakeup */ 11338c2ecf20Sopenharmony_ci if ((udc_ep0_get_UDCCS(udc) & UDCCS0_DRWF) == 0) 11348c2ecf20Sopenharmony_ci return -EHOSTUNREACH; 11358c2ecf20Sopenharmony_ci udc_set_mask_UDCCR(udc, UDCCR_RSM); 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *); 11408c2ecf20Sopenharmony_cistatic void udc_enable (struct pxa25x_udc *); 11418c2ecf20Sopenharmony_cistatic void udc_disable(struct pxa25x_udc *); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci/* We disable the UDC -- and its 48 MHz clock -- whenever it's not 11448c2ecf20Sopenharmony_ci * in active use. 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_cistatic int pullup(struct pxa25x_udc *udc) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci int is_active = udc->vbus && udc->pullup && !udc->suspended; 11498c2ecf20Sopenharmony_ci DMSG("%s\n", is_active ? "active" : "inactive"); 11508c2ecf20Sopenharmony_ci if (is_active) { 11518c2ecf20Sopenharmony_ci if (!udc->active) { 11528c2ecf20Sopenharmony_ci udc->active = 1; 11538c2ecf20Sopenharmony_ci /* Enable clock for USB device */ 11548c2ecf20Sopenharmony_ci clk_enable(udc->clk); 11558c2ecf20Sopenharmony_ci udc_enable(udc); 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci } else { 11588c2ecf20Sopenharmony_ci if (udc->active) { 11598c2ecf20Sopenharmony_ci if (udc->gadget.speed != USB_SPEED_UNKNOWN) { 11608c2ecf20Sopenharmony_ci DMSG("disconnect %s\n", udc->driver 11618c2ecf20Sopenharmony_ci ? udc->driver->driver.name 11628c2ecf20Sopenharmony_ci : "(no driver)"); 11638c2ecf20Sopenharmony_ci stop_activity(udc, udc->driver); 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci udc_disable(udc); 11668c2ecf20Sopenharmony_ci /* Disable clock for USB device */ 11678c2ecf20Sopenharmony_ci clk_disable(udc->clk); 11688c2ecf20Sopenharmony_ci udc->active = 0; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/* VBUS reporting logically comes from a transceiver */ 11768c2ecf20Sopenharmony_cistatic int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct pxa25x_udc *udc; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci udc = container_of(_gadget, struct pxa25x_udc, gadget); 11818c2ecf20Sopenharmony_ci udc->vbus = is_active; 11828c2ecf20Sopenharmony_ci DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); 11838c2ecf20Sopenharmony_ci pullup(udc); 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/* drivers may have software control over D+ pullup */ 11888c2ecf20Sopenharmony_cistatic int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct pxa25x_udc *udc; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci udc = container_of(_gadget, struct pxa25x_udc, gadget); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci /* not all boards support pullup control */ 11958c2ecf20Sopenharmony_ci if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) 11968c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci udc->pullup = (is_active != 0); 11998c2ecf20Sopenharmony_ci pullup(udc); 12008c2ecf20Sopenharmony_ci return 0; 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci/* boards may consume current from VBUS, up to 100-500mA based on config. 12048c2ecf20Sopenharmony_ci * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs 12058c2ecf20Sopenharmony_ci * violate USB specs. 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_cistatic int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct pxa25x_udc *udc; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci udc = container_of(_gadget, struct pxa25x_udc, gadget); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(udc->transceiver)) 12148c2ecf20Sopenharmony_ci return usb_phy_set_power(udc->transceiver, mA); 12158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic int pxa25x_udc_start(struct usb_gadget *g, 12198c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver); 12208c2ecf20Sopenharmony_cistatic int pxa25x_udc_stop(struct usb_gadget *g); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops pxa25x_udc_ops = { 12238c2ecf20Sopenharmony_ci .get_frame = pxa25x_udc_get_frame, 12248c2ecf20Sopenharmony_ci .wakeup = pxa25x_udc_wakeup, 12258c2ecf20Sopenharmony_ci .vbus_session = pxa25x_udc_vbus_session, 12268c2ecf20Sopenharmony_ci .pullup = pxa25x_udc_pullup, 12278c2ecf20Sopenharmony_ci .vbus_draw = pxa25x_udc_vbus_draw, 12288c2ecf20Sopenharmony_ci .udc_start = pxa25x_udc_start, 12298c2ecf20Sopenharmony_ci .udc_stop = pxa25x_udc_stop, 12308c2ecf20Sopenharmony_ci}; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FS 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic int udc_debug_show(struct seq_file *m, void *_d) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = m->private; 12398c2ecf20Sopenharmony_ci unsigned long flags; 12408c2ecf20Sopenharmony_ci int i; 12418c2ecf20Sopenharmony_ci u32 tmp; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci local_irq_save(flags); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci /* basic device status */ 12468c2ecf20Sopenharmony_ci seq_printf(m, DRIVER_DESC "\n" 12478c2ecf20Sopenharmony_ci "%s version: %s\nGadget driver: %s\nHost %s\n\n", 12488c2ecf20Sopenharmony_ci driver_name, DRIVER_VERSION SIZE_STR "(pio)", 12498c2ecf20Sopenharmony_ci dev->driver ? dev->driver->driver.name : "(none)", 12508c2ecf20Sopenharmony_ci dev->gadget.speed == USB_SPEED_FULL ? "full speed" : "disconnected"); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* registers for device and ep0 */ 12538c2ecf20Sopenharmony_ci seq_printf(m, 12548c2ecf20Sopenharmony_ci "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", 12558c2ecf20Sopenharmony_ci udc_get_reg(dev, UICR1), udc_get_reg(dev, UICR0), 12568c2ecf20Sopenharmony_ci udc_get_reg(dev, USIR1), udc_get_reg(dev, USIR0), 12578c2ecf20Sopenharmony_ci udc_get_reg(dev, UFNRH), udc_get_reg(dev, UFNRL)); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci tmp = udc_get_reg(dev, UDCCR); 12608c2ecf20Sopenharmony_ci seq_printf(m, 12618c2ecf20Sopenharmony_ci "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, 12628c2ecf20Sopenharmony_ci (tmp & UDCCR_REM) ? " rem" : "", 12638c2ecf20Sopenharmony_ci (tmp & UDCCR_RSTIR) ? " rstir" : "", 12648c2ecf20Sopenharmony_ci (tmp & UDCCR_SRM) ? " srm" : "", 12658c2ecf20Sopenharmony_ci (tmp & UDCCR_SUSIR) ? " susir" : "", 12668c2ecf20Sopenharmony_ci (tmp & UDCCR_RESIR) ? " resir" : "", 12678c2ecf20Sopenharmony_ci (tmp & UDCCR_RSM) ? " rsm" : "", 12688c2ecf20Sopenharmony_ci (tmp & UDCCR_UDA) ? " uda" : "", 12698c2ecf20Sopenharmony_ci (tmp & UDCCR_UDE) ? " ude" : ""); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci tmp = udc_ep0_get_UDCCS(dev); 12728c2ecf20Sopenharmony_ci seq_printf(m, 12738c2ecf20Sopenharmony_ci "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, 12748c2ecf20Sopenharmony_ci (tmp & UDCCS0_SA) ? " sa" : "", 12758c2ecf20Sopenharmony_ci (tmp & UDCCS0_RNE) ? " rne" : "", 12768c2ecf20Sopenharmony_ci (tmp & UDCCS0_FST) ? " fst" : "", 12778c2ecf20Sopenharmony_ci (tmp & UDCCS0_SST) ? " sst" : "", 12788c2ecf20Sopenharmony_ci (tmp & UDCCS0_DRWF) ? " dwrf" : "", 12798c2ecf20Sopenharmony_ci (tmp & UDCCS0_FTF) ? " ftf" : "", 12808c2ecf20Sopenharmony_ci (tmp & UDCCS0_IPR) ? " ipr" : "", 12818c2ecf20Sopenharmony_ci (tmp & UDCCS0_OPR) ? " opr" : ""); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (dev->has_cfr) { 12848c2ecf20Sopenharmony_ci tmp = udc_get_reg(dev, UDCCFR); 12858c2ecf20Sopenharmony_ci seq_printf(m, 12868c2ecf20Sopenharmony_ci "udccfr %02X =%s%s\n", tmp, 12878c2ecf20Sopenharmony_ci (tmp & UDCCFR_AREN) ? " aren" : "", 12888c2ecf20Sopenharmony_ci (tmp & UDCCFR_ACM) ? " acm" : ""); 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (dev->gadget.speed != USB_SPEED_FULL || !dev->driver) 12928c2ecf20Sopenharmony_ci goto done; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", 12958c2ecf20Sopenharmony_ci dev->stats.write.bytes, dev->stats.write.ops, 12968c2ecf20Sopenharmony_ci dev->stats.read.bytes, dev->stats.read.ops, 12978c2ecf20Sopenharmony_ci dev->stats.irqs); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* dump endpoint queues */ 13008c2ecf20Sopenharmony_ci for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { 13018c2ecf20Sopenharmony_ci struct pxa25x_ep *ep = &dev->ep [i]; 13028c2ecf20Sopenharmony_ci struct pxa25x_request *req; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (i != 0) { 13058c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci desc = ep->ep.desc; 13088c2ecf20Sopenharmony_ci if (!desc) 13098c2ecf20Sopenharmony_ci continue; 13108c2ecf20Sopenharmony_ci tmp = udc_ep_get_UDCCS(&dev->ep[i]); 13118c2ecf20Sopenharmony_ci seq_printf(m, 13128c2ecf20Sopenharmony_ci "%s max %d %s udccs %02x irqs %lu\n", 13138c2ecf20Sopenharmony_ci ep->ep.name, usb_endpoint_maxp(desc), 13148c2ecf20Sopenharmony_ci "pio", tmp, ep->pio_irqs); 13158c2ecf20Sopenharmony_ci /* TODO translate all five groups of udccs bits! */ 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci } else /* ep0 should only have one transfer queued */ 13188c2ecf20Sopenharmony_ci seq_printf(m, "ep0 max 16 pio irqs %lu\n", 13198c2ecf20Sopenharmony_ci ep->pio_irqs); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) { 13228c2ecf20Sopenharmony_ci seq_printf(m, "\t(nothing queued)\n"); 13238c2ecf20Sopenharmony_ci continue; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci list_for_each_entry(req, &ep->queue, queue) { 13268c2ecf20Sopenharmony_ci seq_printf(m, 13278c2ecf20Sopenharmony_ci "\treq %p len %d/%d buf %p\n", 13288c2ecf20Sopenharmony_ci &req->req, req->req.actual, 13298c2ecf20Sopenharmony_ci req->req.length, req->req.buf); 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cidone: 13348c2ecf20Sopenharmony_ci local_irq_restore(flags); 13358c2ecf20Sopenharmony_ci return 0; 13368c2ecf20Sopenharmony_ci} 13378c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(udc_debug); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci#define create_debug_files(dev) \ 13408c2ecf20Sopenharmony_ci do { \ 13418c2ecf20Sopenharmony_ci dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \ 13428c2ecf20Sopenharmony_ci S_IRUGO, NULL, dev, &udc_debug_fops); \ 13438c2ecf20Sopenharmony_ci } while (0) 13448c2ecf20Sopenharmony_ci#define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc) 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci#define create_debug_files(dev) do {} while (0) 13498c2ecf20Sopenharmony_ci#define remove_debug_files(dev) do {} while (0) 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci/* 13568c2ecf20Sopenharmony_ci * udc_disable - disable USB device controller 13578c2ecf20Sopenharmony_ci */ 13588c2ecf20Sopenharmony_cistatic void udc_disable(struct pxa25x_udc *dev) 13598c2ecf20Sopenharmony_ci{ 13608c2ecf20Sopenharmony_ci /* block all irqs */ 13618c2ecf20Sopenharmony_ci udc_set_mask_UDCCR(dev, UDCCR_SRM|UDCCR_REM); 13628c2ecf20Sopenharmony_ci udc_set_reg(dev, UICR0, 0xff); 13638c2ecf20Sopenharmony_ci udc_set_reg(dev, UICR1, 0xff); 13648c2ecf20Sopenharmony_ci udc_set_reg(dev, UFNRH, UFNRH_SIM); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* if hardware supports it, disconnect from usb */ 13678c2ecf20Sopenharmony_ci pullup_off(); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci udc_clear_mask_UDCCR(dev, UDCCR_UDE); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci ep0_idle (dev); 13728c2ecf20Sopenharmony_ci dev->gadget.speed = USB_SPEED_UNKNOWN; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci/* 13778c2ecf20Sopenharmony_ci * udc_reinit - initialize software state 13788c2ecf20Sopenharmony_ci */ 13798c2ecf20Sopenharmony_cistatic void udc_reinit(struct pxa25x_udc *dev) 13808c2ecf20Sopenharmony_ci{ 13818c2ecf20Sopenharmony_ci u32 i; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci /* device/ep0 records init */ 13848c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&dev->gadget.ep_list); 13858c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); 13868c2ecf20Sopenharmony_ci dev->ep0state = EP0_IDLE; 13878c2ecf20Sopenharmony_ci dev->gadget.quirk_altset_not_supp = 1; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci /* basic endpoint records init */ 13908c2ecf20Sopenharmony_ci for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { 13918c2ecf20Sopenharmony_ci struct pxa25x_ep *ep = &dev->ep[i]; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci if (i != 0) 13948c2ecf20Sopenharmony_ci list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci ep->ep.desc = NULL; 13978c2ecf20Sopenharmony_ci ep->stopped = 0; 13988c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&ep->queue); 13998c2ecf20Sopenharmony_ci ep->pio_irqs = 0; 14008c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket); 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci /* the rest was statically initialized, and is read-only */ 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci/* until it's enabled, this UDC should be completely invisible 14078c2ecf20Sopenharmony_ci * to any USB host. 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_cistatic void udc_enable (struct pxa25x_udc *dev) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci udc_clear_mask_UDCCR(dev, UDCCR_UDE); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* try to clear these bits before we enable the udc */ 14148c2ecf20Sopenharmony_ci udc_ack_int_UDCCR(dev, UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci ep0_idle(dev); 14178c2ecf20Sopenharmony_ci dev->gadget.speed = USB_SPEED_UNKNOWN; 14188c2ecf20Sopenharmony_ci dev->stats.irqs = 0; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci /* 14218c2ecf20Sopenharmony_ci * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: 14228c2ecf20Sopenharmony_ci * - enable UDC 14238c2ecf20Sopenharmony_ci * - if RESET is already in progress, ack interrupt 14248c2ecf20Sopenharmony_ci * - unmask reset interrupt 14258c2ecf20Sopenharmony_ci */ 14268c2ecf20Sopenharmony_ci udc_set_mask_UDCCR(dev, UDCCR_UDE); 14278c2ecf20Sopenharmony_ci if (!(udc_get_reg(dev, UDCCR) & UDCCR_UDA)) 14288c2ecf20Sopenharmony_ci udc_ack_int_UDCCR(dev, UDCCR_RSTIR); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci if (dev->has_cfr /* UDC_RES2 is defined */) { 14318c2ecf20Sopenharmony_ci /* pxa255 (a0+) can avoid a set_config race that could 14328c2ecf20Sopenharmony_ci * prevent gadget drivers from configuring correctly 14338c2ecf20Sopenharmony_ci */ 14348c2ecf20Sopenharmony_ci udc_set_reg(dev, UDCCFR, UDCCFR_ACM | UDCCFR_MB1); 14358c2ecf20Sopenharmony_ci } else { 14368c2ecf20Sopenharmony_ci /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) 14378c2ecf20Sopenharmony_ci * which could result in missing packets and interrupts. 14388c2ecf20Sopenharmony_ci * supposedly one bit per endpoint, controlling whether it 14398c2ecf20Sopenharmony_ci * double buffers or not; ACM/AREN bits fit into the holes. 14408c2ecf20Sopenharmony_ci * zero bits (like USIR0_IRx) disable double buffering. 14418c2ecf20Sopenharmony_ci */ 14428c2ecf20Sopenharmony_ci udc_set_reg(dev, UDC_RES1, 0x00); 14438c2ecf20Sopenharmony_ci udc_set_reg(dev, UDC_RES2, 0x00); 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci /* enable suspend/resume and reset irqs */ 14478c2ecf20Sopenharmony_ci udc_clear_mask_UDCCR(dev, UDCCR_SRM | UDCCR_REM); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* enable ep0 irqs */ 14508c2ecf20Sopenharmony_ci udc_set_reg(dev, UICR0, udc_get_reg(dev, UICR0) & ~UICR0_IM0); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* if hardware supports it, pullup D+ and wait for reset */ 14538c2ecf20Sopenharmony_ci pullup_on(); 14548c2ecf20Sopenharmony_ci} 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci/* when a driver is successfully registered, it will receive 14588c2ecf20Sopenharmony_ci * control requests including set_configuration(), which enables 14598c2ecf20Sopenharmony_ci * non-control requests. then usb traffic follows until a 14608c2ecf20Sopenharmony_ci * disconnect is reported. then a host may connect again, or 14618c2ecf20Sopenharmony_ci * the driver might get unbound. 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_cistatic int pxa25x_udc_start(struct usb_gadget *g, 14648c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = to_pxa25x(g); 14678c2ecf20Sopenharmony_ci int retval; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* first hook up the driver ... */ 14708c2ecf20Sopenharmony_ci dev->driver = driver; 14718c2ecf20Sopenharmony_ci dev->pullup = 1; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* ... then enable host detection and ep0; and we're ready 14748c2ecf20Sopenharmony_ci * for set_configuration as well as eventual disconnect. 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ci /* connect to bus through transceiver */ 14778c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(dev->transceiver)) { 14788c2ecf20Sopenharmony_ci retval = otg_set_peripheral(dev->transceiver->otg, 14798c2ecf20Sopenharmony_ci &dev->gadget); 14808c2ecf20Sopenharmony_ci if (retval) 14818c2ecf20Sopenharmony_ci goto bind_fail; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci dump_state(dev); 14858c2ecf20Sopenharmony_ci return 0; 14868c2ecf20Sopenharmony_cibind_fail: 14878c2ecf20Sopenharmony_ci return retval; 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic void 14918c2ecf20Sopenharmony_cireset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci int i; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci /* don't disconnect drivers more than once */ 14968c2ecf20Sopenharmony_ci if (dev->gadget.speed == USB_SPEED_UNKNOWN) 14978c2ecf20Sopenharmony_ci driver = NULL; 14988c2ecf20Sopenharmony_ci dev->gadget.speed = USB_SPEED_UNKNOWN; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* prevent new request submissions, kill any outstanding requests */ 15018c2ecf20Sopenharmony_ci for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { 15028c2ecf20Sopenharmony_ci struct pxa25x_ep *ep = &dev->ep[i]; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci ep->stopped = 1; 15058c2ecf20Sopenharmony_ci nuke(ep, -ESHUTDOWN); 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci del_timer_sync(&dev->timer); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci /* report reset; the driver is already quiesced */ 15108c2ecf20Sopenharmony_ci if (driver) 15118c2ecf20Sopenharmony_ci usb_gadget_udc_reset(&dev->gadget, driver); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci /* re-init driver-visible data structures */ 15148c2ecf20Sopenharmony_ci udc_reinit(dev); 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_cistatic void 15188c2ecf20Sopenharmony_cistop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci int i; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci /* don't disconnect drivers more than once */ 15238c2ecf20Sopenharmony_ci if (dev->gadget.speed == USB_SPEED_UNKNOWN) 15248c2ecf20Sopenharmony_ci driver = NULL; 15258c2ecf20Sopenharmony_ci dev->gadget.speed = USB_SPEED_UNKNOWN; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci /* prevent new request submissions, kill any outstanding requests */ 15288c2ecf20Sopenharmony_ci for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { 15298c2ecf20Sopenharmony_ci struct pxa25x_ep *ep = &dev->ep[i]; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci ep->stopped = 1; 15328c2ecf20Sopenharmony_ci nuke(ep, -ESHUTDOWN); 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci del_timer_sync(&dev->timer); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* report disconnect; the driver is already quiesced */ 15378c2ecf20Sopenharmony_ci if (driver) 15388c2ecf20Sopenharmony_ci driver->disconnect(&dev->gadget); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci /* re-init driver-visible data structures */ 15418c2ecf20Sopenharmony_ci udc_reinit(dev); 15428c2ecf20Sopenharmony_ci} 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_cistatic int pxa25x_udc_stop(struct usb_gadget*g) 15458c2ecf20Sopenharmony_ci{ 15468c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = to_pxa25x(g); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci local_irq_disable(); 15498c2ecf20Sopenharmony_ci dev->pullup = 0; 15508c2ecf20Sopenharmony_ci stop_activity(dev, NULL); 15518c2ecf20Sopenharmony_ci local_irq_enable(); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(dev->transceiver)) 15548c2ecf20Sopenharmony_ci (void) otg_set_peripheral(dev->transceiver->otg, NULL); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci dev->driver = NULL; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci dump_state(dev); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci return 0; 15618c2ecf20Sopenharmony_ci} 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_LUBBOCK 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci/* Lubbock has separate connect and disconnect irqs. More typical designs 15688c2ecf20Sopenharmony_ci * use one GPIO as the VBUS IRQ, and another to control the D+ pullup. 15698c2ecf20Sopenharmony_ci */ 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_cistatic irqreturn_t 15728c2ecf20Sopenharmony_cilubbock_vbus_irq(int irq, void *_dev) 15738c2ecf20Sopenharmony_ci{ 15748c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = _dev; 15758c2ecf20Sopenharmony_ci int vbus; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci dev->stats.irqs++; 15788c2ecf20Sopenharmony_ci switch (irq) { 15798c2ecf20Sopenharmony_ci case LUBBOCK_USB_IRQ: 15808c2ecf20Sopenharmony_ci vbus = 1; 15818c2ecf20Sopenharmony_ci disable_irq(LUBBOCK_USB_IRQ); 15828c2ecf20Sopenharmony_ci enable_irq(LUBBOCK_USB_DISC_IRQ); 15838c2ecf20Sopenharmony_ci break; 15848c2ecf20Sopenharmony_ci case LUBBOCK_USB_DISC_IRQ: 15858c2ecf20Sopenharmony_ci vbus = 0; 15868c2ecf20Sopenharmony_ci disable_irq(LUBBOCK_USB_DISC_IRQ); 15878c2ecf20Sopenharmony_ci enable_irq(LUBBOCK_USB_IRQ); 15888c2ecf20Sopenharmony_ci break; 15898c2ecf20Sopenharmony_ci default: 15908c2ecf20Sopenharmony_ci return IRQ_NONE; 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci pxa25x_udc_vbus_session(&dev->gadget, vbus); 15948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15958c2ecf20Sopenharmony_ci} 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci#endif 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic inline void clear_ep_state (struct pxa25x_udc *dev) 16038c2ecf20Sopenharmony_ci{ 16048c2ecf20Sopenharmony_ci unsigned i; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint 16078c2ecf20Sopenharmony_ci * fifos, and pending transactions mustn't be continued in any case. 16088c2ecf20Sopenharmony_ci */ 16098c2ecf20Sopenharmony_ci for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) 16108c2ecf20Sopenharmony_ci nuke(&dev->ep[i], -ECONNABORTED); 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cistatic void udc_watchdog(struct timer_list *t) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = from_timer(dev, t, timer); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci local_irq_disable(); 16188c2ecf20Sopenharmony_ci if (dev->ep0state == EP0_STALL 16198c2ecf20Sopenharmony_ci && (udc_ep0_get_UDCCS(dev) & UDCCS0_FST) == 0 16208c2ecf20Sopenharmony_ci && (udc_ep0_get_UDCCS(dev) & UDCCS0_SST) == 0) { 16218c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_FST|UDCCS0_FTF); 16228c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "ep0 re-stall\n"); 16238c2ecf20Sopenharmony_ci start_watchdog(dev); 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci local_irq_enable(); 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_cistatic void handle_ep0 (struct pxa25x_udc *dev) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci u32 udccs0 = udc_ep0_get_UDCCS(dev); 16318c2ecf20Sopenharmony_ci struct pxa25x_ep *ep = &dev->ep [0]; 16328c2ecf20Sopenharmony_ci struct pxa25x_request *req; 16338c2ecf20Sopenharmony_ci union { 16348c2ecf20Sopenharmony_ci struct usb_ctrlrequest r; 16358c2ecf20Sopenharmony_ci u8 raw [8]; 16368c2ecf20Sopenharmony_ci u32 word [2]; 16378c2ecf20Sopenharmony_ci } u; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) 16408c2ecf20Sopenharmony_ci req = NULL; 16418c2ecf20Sopenharmony_ci else 16428c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, struct pxa25x_request, queue); 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci /* clear stall status */ 16458c2ecf20Sopenharmony_ci if (udccs0 & UDCCS0_SST) { 16468c2ecf20Sopenharmony_ci nuke(ep, -EPIPE); 16478c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_SST); 16488c2ecf20Sopenharmony_ci del_timer(&dev->timer); 16498c2ecf20Sopenharmony_ci ep0_idle(dev); 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci /* previous request unfinished? non-error iff back-to-back ... */ 16538c2ecf20Sopenharmony_ci if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { 16548c2ecf20Sopenharmony_ci nuke(ep, 0); 16558c2ecf20Sopenharmony_ci del_timer(&dev->timer); 16568c2ecf20Sopenharmony_ci ep0_idle(dev); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci switch (dev->ep0state) { 16608c2ecf20Sopenharmony_ci case EP0_IDLE: 16618c2ecf20Sopenharmony_ci /* late-breaking status? */ 16628c2ecf20Sopenharmony_ci udccs0 = udc_ep0_get_UDCCS(dev); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci /* start control request? */ 16658c2ecf20Sopenharmony_ci if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) 16668c2ecf20Sopenharmony_ci == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { 16678c2ecf20Sopenharmony_ci int i; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci nuke (ep, -EPROTO); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* read SETUP packet */ 16728c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 16738c2ecf20Sopenharmony_ci if (unlikely(!(udc_ep0_get_UDCCS(dev) & UDCCS0_RNE))) { 16748c2ecf20Sopenharmony_cibad_setup: 16758c2ecf20Sopenharmony_ci DMSG("SETUP %d!\n", i); 16768c2ecf20Sopenharmony_ci goto stall; 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci u.raw [i] = (u8) UDDR0; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci if (unlikely((udc_ep0_get_UDCCS(dev) & UDCCS0_RNE) != 0)) 16818c2ecf20Sopenharmony_ci goto bad_setup; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cigot_setup: 16848c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", 16858c2ecf20Sopenharmony_ci u.r.bRequestType, u.r.bRequest, 16868c2ecf20Sopenharmony_ci le16_to_cpu(u.r.wValue), 16878c2ecf20Sopenharmony_ci le16_to_cpu(u.r.wIndex), 16888c2ecf20Sopenharmony_ci le16_to_cpu(u.r.wLength)); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci /* cope with automagic for some standard requests. */ 16918c2ecf20Sopenharmony_ci dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) 16928c2ecf20Sopenharmony_ci == USB_TYPE_STANDARD; 16938c2ecf20Sopenharmony_ci dev->req_config = 0; 16948c2ecf20Sopenharmony_ci dev->req_pending = 1; 16958c2ecf20Sopenharmony_ci switch (u.r.bRequest) { 16968c2ecf20Sopenharmony_ci /* hardware restricts gadget drivers here! */ 16978c2ecf20Sopenharmony_ci case USB_REQ_SET_CONFIGURATION: 16988c2ecf20Sopenharmony_ci if (u.r.bRequestType == USB_RECIP_DEVICE) { 16998c2ecf20Sopenharmony_ci /* reflect hardware's automagic 17008c2ecf20Sopenharmony_ci * up to the gadget driver. 17018c2ecf20Sopenharmony_ci */ 17028c2ecf20Sopenharmony_ciconfig_change: 17038c2ecf20Sopenharmony_ci dev->req_config = 1; 17048c2ecf20Sopenharmony_ci clear_ep_state(dev); 17058c2ecf20Sopenharmony_ci /* if !has_cfr, there's no synch 17068c2ecf20Sopenharmony_ci * else use AREN (later) not SA|OPR 17078c2ecf20Sopenharmony_ci * USIR0_IR0 acts edge sensitive 17088c2ecf20Sopenharmony_ci */ 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci break; 17118c2ecf20Sopenharmony_ci /* ... and here, even more ... */ 17128c2ecf20Sopenharmony_ci case USB_REQ_SET_INTERFACE: 17138c2ecf20Sopenharmony_ci if (u.r.bRequestType == USB_RECIP_INTERFACE) { 17148c2ecf20Sopenharmony_ci /* udc hardware is broken by design: 17158c2ecf20Sopenharmony_ci * - altsetting may only be zero; 17168c2ecf20Sopenharmony_ci * - hw resets all interfaces' eps; 17178c2ecf20Sopenharmony_ci * - ep reset doesn't include halt(?). 17188c2ecf20Sopenharmony_ci */ 17198c2ecf20Sopenharmony_ci DMSG("broken set_interface (%d/%d)\n", 17208c2ecf20Sopenharmony_ci le16_to_cpu(u.r.wIndex), 17218c2ecf20Sopenharmony_ci le16_to_cpu(u.r.wValue)); 17228c2ecf20Sopenharmony_ci goto config_change; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci break; 17258c2ecf20Sopenharmony_ci /* hardware was supposed to hide this */ 17268c2ecf20Sopenharmony_ci case USB_REQ_SET_ADDRESS: 17278c2ecf20Sopenharmony_ci if (u.r.bRequestType == USB_RECIP_DEVICE) { 17288c2ecf20Sopenharmony_ci ep0start(dev, 0, "address"); 17298c2ecf20Sopenharmony_ci return; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci break; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci if (u.r.bRequestType & USB_DIR_IN) 17358c2ecf20Sopenharmony_ci dev->ep0state = EP0_IN_DATA_PHASE; 17368c2ecf20Sopenharmony_ci else 17378c2ecf20Sopenharmony_ci dev->ep0state = EP0_OUT_DATA_PHASE; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci i = dev->driver->setup(&dev->gadget, &u.r); 17408c2ecf20Sopenharmony_ci if (i < 0) { 17418c2ecf20Sopenharmony_ci /* hardware automagic preventing STALL... */ 17428c2ecf20Sopenharmony_ci if (dev->req_config) { 17438c2ecf20Sopenharmony_ci /* hardware sometimes neglects to tell 17448c2ecf20Sopenharmony_ci * tell us about config change events, 17458c2ecf20Sopenharmony_ci * so later ones may fail... 17468c2ecf20Sopenharmony_ci */ 17478c2ecf20Sopenharmony_ci WARNING("config change %02x fail %d?\n", 17488c2ecf20Sopenharmony_ci u.r.bRequest, i); 17498c2ecf20Sopenharmony_ci return; 17508c2ecf20Sopenharmony_ci /* TODO experiment: if has_cfr, 17518c2ecf20Sopenharmony_ci * hardware didn't ACK; maybe we 17528c2ecf20Sopenharmony_ci * could actually STALL! 17538c2ecf20Sopenharmony_ci */ 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "protocol STALL, " 17568c2ecf20Sopenharmony_ci "%02x err %d\n", udc_ep0_get_UDCCS(dev), i); 17578c2ecf20Sopenharmony_cistall: 17588c2ecf20Sopenharmony_ci /* the watchdog timer helps deal with cases 17598c2ecf20Sopenharmony_ci * where udc seems to clear FST wrongly, and 17608c2ecf20Sopenharmony_ci * then NAKs instead of STALLing. 17618c2ecf20Sopenharmony_ci */ 17628c2ecf20Sopenharmony_ci ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); 17638c2ecf20Sopenharmony_ci start_watchdog(dev); 17648c2ecf20Sopenharmony_ci dev->ep0state = EP0_STALL; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci /* deferred i/o == no response yet */ 17678c2ecf20Sopenharmony_ci } else if (dev->req_pending) { 17688c2ecf20Sopenharmony_ci if (likely(dev->ep0state == EP0_IN_DATA_PHASE 17698c2ecf20Sopenharmony_ci || dev->req_std || u.r.wLength)) 17708c2ecf20Sopenharmony_ci ep0start(dev, 0, "defer"); 17718c2ecf20Sopenharmony_ci else 17728c2ecf20Sopenharmony_ci ep0start(dev, UDCCS0_IPR, "defer/IPR"); 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci /* expect at least one data or status stage irq */ 17768c2ecf20Sopenharmony_ci return; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) 17798c2ecf20Sopenharmony_ci == (UDCCS0_OPR|UDCCS0_SA))) { 17808c2ecf20Sopenharmony_ci unsigned i; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci /* pxa210/250 erratum 131 for B0/B1 says RNE lies. 17838c2ecf20Sopenharmony_ci * still observed on a pxa255 a0. 17848c2ecf20Sopenharmony_ci */ 17858c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "e131\n"); 17868c2ecf20Sopenharmony_ci nuke(ep, -EPROTO); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci /* read SETUP data, but don't trust it too much */ 17898c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 17908c2ecf20Sopenharmony_ci u.raw [i] = (u8) UDDR0; 17918c2ecf20Sopenharmony_ci if ((u.r.bRequestType & USB_RECIP_MASK) 17928c2ecf20Sopenharmony_ci > USB_RECIP_OTHER) 17938c2ecf20Sopenharmony_ci goto stall; 17948c2ecf20Sopenharmony_ci if (u.word [0] == 0 && u.word [1] == 0) 17958c2ecf20Sopenharmony_ci goto stall; 17968c2ecf20Sopenharmony_ci goto got_setup; 17978c2ecf20Sopenharmony_ci } else { 17988c2ecf20Sopenharmony_ci /* some random early IRQ: 17998c2ecf20Sopenharmony_ci * - we acked FST 18008c2ecf20Sopenharmony_ci * - IPR cleared 18018c2ecf20Sopenharmony_ci * - OPR got set, without SA (likely status stage) 18028c2ecf20Sopenharmony_ci */ 18038c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, udccs0 & (UDCCS0_SA|UDCCS0_OPR)); 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci break; 18068c2ecf20Sopenharmony_ci case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ 18078c2ecf20Sopenharmony_ci if (udccs0 & UDCCS0_OPR) { 18088c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_OPR|UDCCS0_FTF); 18098c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "ep0in premature status\n"); 18108c2ecf20Sopenharmony_ci if (req) 18118c2ecf20Sopenharmony_ci done(ep, req, 0); 18128c2ecf20Sopenharmony_ci ep0_idle(dev); 18138c2ecf20Sopenharmony_ci } else /* irq was IPR clearing */ { 18148c2ecf20Sopenharmony_ci if (req) { 18158c2ecf20Sopenharmony_ci /* this IN packet might finish the request */ 18168c2ecf20Sopenharmony_ci (void) write_ep0_fifo(ep, req); 18178c2ecf20Sopenharmony_ci } /* else IN token before response was written */ 18188c2ecf20Sopenharmony_ci } 18198c2ecf20Sopenharmony_ci break; 18208c2ecf20Sopenharmony_ci case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ 18218c2ecf20Sopenharmony_ci if (udccs0 & UDCCS0_OPR) { 18228c2ecf20Sopenharmony_ci if (req) { 18238c2ecf20Sopenharmony_ci /* this OUT packet might finish the request */ 18248c2ecf20Sopenharmony_ci if (read_ep0_fifo(ep, req)) 18258c2ecf20Sopenharmony_ci done(ep, req, 0); 18268c2ecf20Sopenharmony_ci /* else more OUT packets expected */ 18278c2ecf20Sopenharmony_ci } /* else OUT token before read was issued */ 18288c2ecf20Sopenharmony_ci } else /* irq was IPR clearing */ { 18298c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "ep0out premature status\n"); 18308c2ecf20Sopenharmony_ci if (req) 18318c2ecf20Sopenharmony_ci done(ep, req, 0); 18328c2ecf20Sopenharmony_ci ep0_idle(dev); 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci break; 18358c2ecf20Sopenharmony_ci case EP0_END_XFER: 18368c2ecf20Sopenharmony_ci if (req) 18378c2ecf20Sopenharmony_ci done(ep, req, 0); 18388c2ecf20Sopenharmony_ci /* ack control-IN status (maybe in-zlp was skipped) 18398c2ecf20Sopenharmony_ci * also appears after some config change events. 18408c2ecf20Sopenharmony_ci */ 18418c2ecf20Sopenharmony_ci if (udccs0 & UDCCS0_OPR) 18428c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_OPR); 18438c2ecf20Sopenharmony_ci ep0_idle(dev); 18448c2ecf20Sopenharmony_ci break; 18458c2ecf20Sopenharmony_ci case EP0_STALL: 18468c2ecf20Sopenharmony_ci udc_ep0_set_UDCCS(dev, UDCCS0_FST); 18478c2ecf20Sopenharmony_ci break; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci udc_set_reg(dev, USIR0, USIR0_IR0); 18508c2ecf20Sopenharmony_ci} 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_cistatic void handle_ep(struct pxa25x_ep *ep) 18538c2ecf20Sopenharmony_ci{ 18548c2ecf20Sopenharmony_ci struct pxa25x_request *req; 18558c2ecf20Sopenharmony_ci int is_in = ep->bEndpointAddress & USB_DIR_IN; 18568c2ecf20Sopenharmony_ci int completed; 18578c2ecf20Sopenharmony_ci u32 udccs, tmp; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci do { 18608c2ecf20Sopenharmony_ci completed = 0; 18618c2ecf20Sopenharmony_ci if (likely (!list_empty(&ep->queue))) 18628c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, 18638c2ecf20Sopenharmony_ci struct pxa25x_request, queue); 18648c2ecf20Sopenharmony_ci else 18658c2ecf20Sopenharmony_ci req = NULL; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci // TODO check FST handling 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci udccs = udc_ep_get_UDCCS(ep); 18708c2ecf20Sopenharmony_ci if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ 18718c2ecf20Sopenharmony_ci tmp = UDCCS_BI_TUR; 18728c2ecf20Sopenharmony_ci if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) 18738c2ecf20Sopenharmony_ci tmp |= UDCCS_BI_SST; 18748c2ecf20Sopenharmony_ci tmp &= udccs; 18758c2ecf20Sopenharmony_ci if (likely (tmp)) 18768c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, tmp); 18778c2ecf20Sopenharmony_ci if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) 18788c2ecf20Sopenharmony_ci completed = write_fifo(ep, req); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci } else { /* irq from RPC (or for ISO, ROF) */ 18818c2ecf20Sopenharmony_ci if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) 18828c2ecf20Sopenharmony_ci tmp = UDCCS_BO_SST | UDCCS_BO_DME; 18838c2ecf20Sopenharmony_ci else 18848c2ecf20Sopenharmony_ci tmp = UDCCS_IO_ROF | UDCCS_IO_DME; 18858c2ecf20Sopenharmony_ci tmp &= udccs; 18868c2ecf20Sopenharmony_ci if (likely(tmp)) 18878c2ecf20Sopenharmony_ci udc_ep_set_UDCCS(ep, tmp); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci /* fifos can hold packets, ready for reading... */ 18908c2ecf20Sopenharmony_ci if (likely(req)) { 18918c2ecf20Sopenharmony_ci completed = read_fifo(ep, req); 18928c2ecf20Sopenharmony_ci } else 18938c2ecf20Sopenharmony_ci pio_irq_disable(ep); 18948c2ecf20Sopenharmony_ci } 18958c2ecf20Sopenharmony_ci ep->pio_irqs++; 18968c2ecf20Sopenharmony_ci } while (completed); 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci/* 19008c2ecf20Sopenharmony_ci * pxa25x_udc_irq - interrupt handler 19018c2ecf20Sopenharmony_ci * 19028c2ecf20Sopenharmony_ci * avoid delays in ep0 processing. the control handshaking isn't always 19038c2ecf20Sopenharmony_ci * under software control (pxa250c0 and the pxa255 are better), and delays 19048c2ecf20Sopenharmony_ci * could cause usb protocol errors. 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_cistatic irqreturn_t 19078c2ecf20Sopenharmony_cipxa25x_udc_irq(int irq, void *_dev) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = _dev; 19108c2ecf20Sopenharmony_ci int handled; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci dev->stats.irqs++; 19138c2ecf20Sopenharmony_ci do { 19148c2ecf20Sopenharmony_ci u32 udccr = udc_get_reg(dev, UDCCR); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci handled = 0; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci /* SUSpend Interrupt Request */ 19198c2ecf20Sopenharmony_ci if (unlikely(udccr & UDCCR_SUSIR)) { 19208c2ecf20Sopenharmony_ci udc_ack_int_UDCCR(dev, UDCCR_SUSIR); 19218c2ecf20Sopenharmony_ci handled = 1; 19228c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "USB suspend\n"); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (dev->gadget.speed != USB_SPEED_UNKNOWN 19258c2ecf20Sopenharmony_ci && dev->driver 19268c2ecf20Sopenharmony_ci && dev->driver->suspend) 19278c2ecf20Sopenharmony_ci dev->driver->suspend(&dev->gadget); 19288c2ecf20Sopenharmony_ci ep0_idle (dev); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci /* RESume Interrupt Request */ 19328c2ecf20Sopenharmony_ci if (unlikely(udccr & UDCCR_RESIR)) { 19338c2ecf20Sopenharmony_ci udc_ack_int_UDCCR(dev, UDCCR_RESIR); 19348c2ecf20Sopenharmony_ci handled = 1; 19358c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "USB resume\n"); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (dev->gadget.speed != USB_SPEED_UNKNOWN 19388c2ecf20Sopenharmony_ci && dev->driver 19398c2ecf20Sopenharmony_ci && dev->driver->resume) 19408c2ecf20Sopenharmony_ci dev->driver->resume(&dev->gadget); 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci /* ReSeT Interrupt Request - USB reset */ 19448c2ecf20Sopenharmony_ci if (unlikely(udccr & UDCCR_RSTIR)) { 19458c2ecf20Sopenharmony_ci udc_ack_int_UDCCR(dev, UDCCR_RSTIR); 19468c2ecf20Sopenharmony_ci handled = 1; 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci if ((udc_get_reg(dev, UDCCR) & UDCCR_UDA) == 0) { 19498c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "USB reset start\n"); 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci /* reset driver and endpoints, 19528c2ecf20Sopenharmony_ci * in case that's not yet done 19538c2ecf20Sopenharmony_ci */ 19548c2ecf20Sopenharmony_ci reset_gadget(dev, dev->driver); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci } else { 19578c2ecf20Sopenharmony_ci DBG(DBG_VERBOSE, "USB reset end\n"); 19588c2ecf20Sopenharmony_ci dev->gadget.speed = USB_SPEED_FULL; 19598c2ecf20Sopenharmony_ci memset(&dev->stats, 0, sizeof dev->stats); 19608c2ecf20Sopenharmony_ci /* driver and endpoints are still reset */ 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci } else { 19648c2ecf20Sopenharmony_ci u32 usir0 = udc_get_reg(dev, USIR0) & 19658c2ecf20Sopenharmony_ci ~udc_get_reg(dev, UICR0); 19668c2ecf20Sopenharmony_ci u32 usir1 = udc_get_reg(dev, USIR1) & 19678c2ecf20Sopenharmony_ci ~udc_get_reg(dev, UICR1); 19688c2ecf20Sopenharmony_ci int i; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci if (unlikely (!usir0 && !usir1)) 19718c2ecf20Sopenharmony_ci continue; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* control traffic */ 19768c2ecf20Sopenharmony_ci if (usir0 & USIR0_IR0) { 19778c2ecf20Sopenharmony_ci dev->ep[0].pio_irqs++; 19788c2ecf20Sopenharmony_ci handle_ep0(dev); 19798c2ecf20Sopenharmony_ci handled = 1; 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* endpoint data transfers */ 19838c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 19848c2ecf20Sopenharmony_ci u32 tmp = 1 << i; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci if (i && (usir0 & tmp)) { 19878c2ecf20Sopenharmony_ci handle_ep(&dev->ep[i]); 19888c2ecf20Sopenharmony_ci udc_set_reg(dev, USIR0, 19898c2ecf20Sopenharmony_ci udc_get_reg(dev, USIR0) | tmp); 19908c2ecf20Sopenharmony_ci handled = 1; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci#ifndef CONFIG_USB_PXA25X_SMALL 19938c2ecf20Sopenharmony_ci if (usir1 & tmp) { 19948c2ecf20Sopenharmony_ci handle_ep(&dev->ep[i+8]); 19958c2ecf20Sopenharmony_ci udc_set_reg(dev, USIR1, 19968c2ecf20Sopenharmony_ci udc_get_reg(dev, USIR1) | tmp); 19978c2ecf20Sopenharmony_ci handled = 1; 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci#endif 20008c2ecf20Sopenharmony_ci } 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* we could also ask for 1 msec SOF (SIR) interrupts */ 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci } while (handled); 20068c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20078c2ecf20Sopenharmony_ci} 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic void nop_release (struct device *dev) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci DMSG("%s %s\n", __func__, dev_name(dev)); 20148c2ecf20Sopenharmony_ci} 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci/* this uses load-time allocation and initialization (instead of 20178c2ecf20Sopenharmony_ci * doing it at run-time) to save code, eliminate fault paths, and 20188c2ecf20Sopenharmony_ci * be more obviously correct. 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_cistatic struct pxa25x_udc memory = { 20218c2ecf20Sopenharmony_ci .gadget = { 20228c2ecf20Sopenharmony_ci .ops = &pxa25x_udc_ops, 20238c2ecf20Sopenharmony_ci .ep0 = &memory.ep[0].ep, 20248c2ecf20Sopenharmony_ci .name = driver_name, 20258c2ecf20Sopenharmony_ci .dev = { 20268c2ecf20Sopenharmony_ci .init_name = "gadget", 20278c2ecf20Sopenharmony_ci .release = nop_release, 20288c2ecf20Sopenharmony_ci }, 20298c2ecf20Sopenharmony_ci }, 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci /* control endpoint */ 20328c2ecf20Sopenharmony_ci .ep[0] = { 20338c2ecf20Sopenharmony_ci .ep = { 20348c2ecf20Sopenharmony_ci .name = ep0name, 20358c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 20368c2ecf20Sopenharmony_ci .maxpacket = EP0_FIFO_SIZE, 20378c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, 20388c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 20398c2ecf20Sopenharmony_ci }, 20408c2ecf20Sopenharmony_ci .dev = &memory, 20418c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS0, 20428c2ecf20Sopenharmony_ci .regoff_uddr = UDDR0, 20438c2ecf20Sopenharmony_ci }, 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci /* first group of endpoints */ 20468c2ecf20Sopenharmony_ci .ep[1] = { 20478c2ecf20Sopenharmony_ci .ep = { 20488c2ecf20Sopenharmony_ci .name = "ep1in-bulk", 20498c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 20508c2ecf20Sopenharmony_ci .maxpacket = BULK_FIFO_SIZE, 20518c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 20528c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_IN), 20538c2ecf20Sopenharmony_ci }, 20548c2ecf20Sopenharmony_ci .dev = &memory, 20558c2ecf20Sopenharmony_ci .fifo_size = BULK_FIFO_SIZE, 20568c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 1, 20578c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 20588c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS1, 20598c2ecf20Sopenharmony_ci .regoff_uddr = UDDR1, 20608c2ecf20Sopenharmony_ci }, 20618c2ecf20Sopenharmony_ci .ep[2] = { 20628c2ecf20Sopenharmony_ci .ep = { 20638c2ecf20Sopenharmony_ci .name = "ep2out-bulk", 20648c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 20658c2ecf20Sopenharmony_ci .maxpacket = BULK_FIFO_SIZE, 20668c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 20678c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_OUT), 20688c2ecf20Sopenharmony_ci }, 20698c2ecf20Sopenharmony_ci .dev = &memory, 20708c2ecf20Sopenharmony_ci .fifo_size = BULK_FIFO_SIZE, 20718c2ecf20Sopenharmony_ci .bEndpointAddress = 2, 20728c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 20738c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS2, 20748c2ecf20Sopenharmony_ci .regoff_ubcr = UBCR2, 20758c2ecf20Sopenharmony_ci .regoff_uddr = UDDR2, 20768c2ecf20Sopenharmony_ci }, 20778c2ecf20Sopenharmony_ci#ifndef CONFIG_USB_PXA25X_SMALL 20788c2ecf20Sopenharmony_ci .ep[3] = { 20798c2ecf20Sopenharmony_ci .ep = { 20808c2ecf20Sopenharmony_ci .name = "ep3in-iso", 20818c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 20828c2ecf20Sopenharmony_ci .maxpacket = ISO_FIFO_SIZE, 20838c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 20848c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_IN), 20858c2ecf20Sopenharmony_ci }, 20868c2ecf20Sopenharmony_ci .dev = &memory, 20878c2ecf20Sopenharmony_ci .fifo_size = ISO_FIFO_SIZE, 20888c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 3, 20898c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_ISOC, 20908c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS3, 20918c2ecf20Sopenharmony_ci .regoff_uddr = UDDR3, 20928c2ecf20Sopenharmony_ci }, 20938c2ecf20Sopenharmony_ci .ep[4] = { 20948c2ecf20Sopenharmony_ci .ep = { 20958c2ecf20Sopenharmony_ci .name = "ep4out-iso", 20968c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 20978c2ecf20Sopenharmony_ci .maxpacket = ISO_FIFO_SIZE, 20988c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 20998c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_OUT), 21008c2ecf20Sopenharmony_ci }, 21018c2ecf20Sopenharmony_ci .dev = &memory, 21028c2ecf20Sopenharmony_ci .fifo_size = ISO_FIFO_SIZE, 21038c2ecf20Sopenharmony_ci .bEndpointAddress = 4, 21048c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_ISOC, 21058c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS4, 21068c2ecf20Sopenharmony_ci .regoff_ubcr = UBCR4, 21078c2ecf20Sopenharmony_ci .regoff_uddr = UDDR4, 21088c2ecf20Sopenharmony_ci }, 21098c2ecf20Sopenharmony_ci .ep[5] = { 21108c2ecf20Sopenharmony_ci .ep = { 21118c2ecf20Sopenharmony_ci .name = "ep5in-int", 21128c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 21138c2ecf20Sopenharmony_ci .maxpacket = INT_FIFO_SIZE, 21148c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(0, 0), 21158c2ecf20Sopenharmony_ci }, 21168c2ecf20Sopenharmony_ci .dev = &memory, 21178c2ecf20Sopenharmony_ci .fifo_size = INT_FIFO_SIZE, 21188c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 5, 21198c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 21208c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS5, 21218c2ecf20Sopenharmony_ci .regoff_uddr = UDDR5, 21228c2ecf20Sopenharmony_ci }, 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci /* second group of endpoints */ 21258c2ecf20Sopenharmony_ci .ep[6] = { 21268c2ecf20Sopenharmony_ci .ep = { 21278c2ecf20Sopenharmony_ci .name = "ep6in-bulk", 21288c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 21298c2ecf20Sopenharmony_ci .maxpacket = BULK_FIFO_SIZE, 21308c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 21318c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_IN), 21328c2ecf20Sopenharmony_ci }, 21338c2ecf20Sopenharmony_ci .dev = &memory, 21348c2ecf20Sopenharmony_ci .fifo_size = BULK_FIFO_SIZE, 21358c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 6, 21368c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 21378c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS6, 21388c2ecf20Sopenharmony_ci .regoff_uddr = UDDR6, 21398c2ecf20Sopenharmony_ci }, 21408c2ecf20Sopenharmony_ci .ep[7] = { 21418c2ecf20Sopenharmony_ci .ep = { 21428c2ecf20Sopenharmony_ci .name = "ep7out-bulk", 21438c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 21448c2ecf20Sopenharmony_ci .maxpacket = BULK_FIFO_SIZE, 21458c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 21468c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_OUT), 21478c2ecf20Sopenharmony_ci }, 21488c2ecf20Sopenharmony_ci .dev = &memory, 21498c2ecf20Sopenharmony_ci .fifo_size = BULK_FIFO_SIZE, 21508c2ecf20Sopenharmony_ci .bEndpointAddress = 7, 21518c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 21528c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS7, 21538c2ecf20Sopenharmony_ci .regoff_ubcr = UBCR7, 21548c2ecf20Sopenharmony_ci .regoff_uddr = UDDR7, 21558c2ecf20Sopenharmony_ci }, 21568c2ecf20Sopenharmony_ci .ep[8] = { 21578c2ecf20Sopenharmony_ci .ep = { 21588c2ecf20Sopenharmony_ci .name = "ep8in-iso", 21598c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 21608c2ecf20Sopenharmony_ci .maxpacket = ISO_FIFO_SIZE, 21618c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 21628c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_IN), 21638c2ecf20Sopenharmony_ci }, 21648c2ecf20Sopenharmony_ci .dev = &memory, 21658c2ecf20Sopenharmony_ci .fifo_size = ISO_FIFO_SIZE, 21668c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 8, 21678c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_ISOC, 21688c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS8, 21698c2ecf20Sopenharmony_ci .regoff_uddr = UDDR8, 21708c2ecf20Sopenharmony_ci }, 21718c2ecf20Sopenharmony_ci .ep[9] = { 21728c2ecf20Sopenharmony_ci .ep = { 21738c2ecf20Sopenharmony_ci .name = "ep9out-iso", 21748c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 21758c2ecf20Sopenharmony_ci .maxpacket = ISO_FIFO_SIZE, 21768c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 21778c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_OUT), 21788c2ecf20Sopenharmony_ci }, 21798c2ecf20Sopenharmony_ci .dev = &memory, 21808c2ecf20Sopenharmony_ci .fifo_size = ISO_FIFO_SIZE, 21818c2ecf20Sopenharmony_ci .bEndpointAddress = 9, 21828c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_ISOC, 21838c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS9, 21848c2ecf20Sopenharmony_ci .regoff_ubcr = UBCR9, 21858c2ecf20Sopenharmony_ci .regoff_uddr = UDDR9, 21868c2ecf20Sopenharmony_ci }, 21878c2ecf20Sopenharmony_ci .ep[10] = { 21888c2ecf20Sopenharmony_ci .ep = { 21898c2ecf20Sopenharmony_ci .name = "ep10in-int", 21908c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 21918c2ecf20Sopenharmony_ci .maxpacket = INT_FIFO_SIZE, 21928c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(0, 0), 21938c2ecf20Sopenharmony_ci }, 21948c2ecf20Sopenharmony_ci .dev = &memory, 21958c2ecf20Sopenharmony_ci .fifo_size = INT_FIFO_SIZE, 21968c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 10, 21978c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 21988c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS10, 21998c2ecf20Sopenharmony_ci .regoff_uddr = UDDR10, 22008c2ecf20Sopenharmony_ci }, 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* third group of endpoints */ 22038c2ecf20Sopenharmony_ci .ep[11] = { 22048c2ecf20Sopenharmony_ci .ep = { 22058c2ecf20Sopenharmony_ci .name = "ep11in-bulk", 22068c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 22078c2ecf20Sopenharmony_ci .maxpacket = BULK_FIFO_SIZE, 22088c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 22098c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_IN), 22108c2ecf20Sopenharmony_ci }, 22118c2ecf20Sopenharmony_ci .dev = &memory, 22128c2ecf20Sopenharmony_ci .fifo_size = BULK_FIFO_SIZE, 22138c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 11, 22148c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 22158c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS11, 22168c2ecf20Sopenharmony_ci .regoff_uddr = UDDR11, 22178c2ecf20Sopenharmony_ci }, 22188c2ecf20Sopenharmony_ci .ep[12] = { 22198c2ecf20Sopenharmony_ci .ep = { 22208c2ecf20Sopenharmony_ci .name = "ep12out-bulk", 22218c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 22228c2ecf20Sopenharmony_ci .maxpacket = BULK_FIFO_SIZE, 22238c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 22248c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_OUT), 22258c2ecf20Sopenharmony_ci }, 22268c2ecf20Sopenharmony_ci .dev = &memory, 22278c2ecf20Sopenharmony_ci .fifo_size = BULK_FIFO_SIZE, 22288c2ecf20Sopenharmony_ci .bEndpointAddress = 12, 22298c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 22308c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS12, 22318c2ecf20Sopenharmony_ci .regoff_ubcr = UBCR12, 22328c2ecf20Sopenharmony_ci .regoff_uddr = UDDR12, 22338c2ecf20Sopenharmony_ci }, 22348c2ecf20Sopenharmony_ci .ep[13] = { 22358c2ecf20Sopenharmony_ci .ep = { 22368c2ecf20Sopenharmony_ci .name = "ep13in-iso", 22378c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 22388c2ecf20Sopenharmony_ci .maxpacket = ISO_FIFO_SIZE, 22398c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 22408c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_IN), 22418c2ecf20Sopenharmony_ci }, 22428c2ecf20Sopenharmony_ci .dev = &memory, 22438c2ecf20Sopenharmony_ci .fifo_size = ISO_FIFO_SIZE, 22448c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 13, 22458c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_ISOC, 22468c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS13, 22478c2ecf20Sopenharmony_ci .regoff_uddr = UDDR13, 22488c2ecf20Sopenharmony_ci }, 22498c2ecf20Sopenharmony_ci .ep[14] = { 22508c2ecf20Sopenharmony_ci .ep = { 22518c2ecf20Sopenharmony_ci .name = "ep14out-iso", 22528c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 22538c2ecf20Sopenharmony_ci .maxpacket = ISO_FIFO_SIZE, 22548c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 22558c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_OUT), 22568c2ecf20Sopenharmony_ci }, 22578c2ecf20Sopenharmony_ci .dev = &memory, 22588c2ecf20Sopenharmony_ci .fifo_size = ISO_FIFO_SIZE, 22598c2ecf20Sopenharmony_ci .bEndpointAddress = 14, 22608c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_ISOC, 22618c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS14, 22628c2ecf20Sopenharmony_ci .regoff_ubcr = UBCR14, 22638c2ecf20Sopenharmony_ci .regoff_uddr = UDDR14, 22648c2ecf20Sopenharmony_ci }, 22658c2ecf20Sopenharmony_ci .ep[15] = { 22668c2ecf20Sopenharmony_ci .ep = { 22678c2ecf20Sopenharmony_ci .name = "ep15in-int", 22688c2ecf20Sopenharmony_ci .ops = &pxa25x_ep_ops, 22698c2ecf20Sopenharmony_ci .maxpacket = INT_FIFO_SIZE, 22708c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(0, 0), 22718c2ecf20Sopenharmony_ci }, 22728c2ecf20Sopenharmony_ci .dev = &memory, 22738c2ecf20Sopenharmony_ci .fifo_size = INT_FIFO_SIZE, 22748c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_IN | 15, 22758c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 22768c2ecf20Sopenharmony_ci .regoff_udccs = UDCCS15, 22778c2ecf20Sopenharmony_ci .regoff_uddr = UDDR15, 22788c2ecf20Sopenharmony_ci }, 22798c2ecf20Sopenharmony_ci#endif /* !CONFIG_USB_PXA25X_SMALL */ 22808c2ecf20Sopenharmony_ci}; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci#define CP15R0_VENDOR_MASK 0xffffe000 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_PXA) 22858c2ecf20Sopenharmony_ci#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */ 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci#elif defined(CONFIG_ARCH_IXP4XX) 22888c2ecf20Sopenharmony_ci#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */ 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci#endif 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci#define CP15R0_PROD_MASK 0x000003f0 22938c2ecf20Sopenharmony_ci#define PXA25x 0x00000100 /* and PXA26x */ 22948c2ecf20Sopenharmony_ci#define PXA210 0x00000120 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci#define CP15R0_REV_MASK 0x0000000f 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK) 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci#define PXA255_A0 0x00000106 /* or PXA260_B1 */ 23018c2ecf20Sopenharmony_ci#define PXA250_C0 0x00000105 /* or PXA26x_B0 */ 23028c2ecf20Sopenharmony_ci#define PXA250_B2 0x00000104 23038c2ecf20Sopenharmony_ci#define PXA250_B1 0x00000103 /* or PXA260_A0 */ 23048c2ecf20Sopenharmony_ci#define PXA250_B0 0x00000102 23058c2ecf20Sopenharmony_ci#define PXA250_A1 0x00000101 23068c2ecf20Sopenharmony_ci#define PXA250_A0 0x00000100 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci#define PXA210_C0 0x00000125 23098c2ecf20Sopenharmony_ci#define PXA210_B2 0x00000124 23108c2ecf20Sopenharmony_ci#define PXA210_B1 0x00000123 23118c2ecf20Sopenharmony_ci#define PXA210_B0 0x00000122 23128c2ecf20Sopenharmony_ci#define IXP425_A0 0x000001c1 23138c2ecf20Sopenharmony_ci#define IXP425_B0 0x000001f1 23148c2ecf20Sopenharmony_ci#define IXP465_AD 0x00000200 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci/* 23178c2ecf20Sopenharmony_ci * probe - binds to the platform device 23188c2ecf20Sopenharmony_ci */ 23198c2ecf20Sopenharmony_cistatic int pxa25x_udc_probe(struct platform_device *pdev) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = &memory; 23228c2ecf20Sopenharmony_ci int retval, irq; 23238c2ecf20Sopenharmony_ci u32 chiprev; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci /* insist on Intel/ARM/XScale */ 23288c2ecf20Sopenharmony_ci asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); 23298c2ecf20Sopenharmony_ci if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { 23308c2ecf20Sopenharmony_ci pr_err("%s: not XScale!\n", driver_name); 23318c2ecf20Sopenharmony_ci return -ENODEV; 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci /* trigger chiprev-specific logic */ 23358c2ecf20Sopenharmony_ci switch (chiprev & CP15R0_PRODREV_MASK) { 23368c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_PXA) 23378c2ecf20Sopenharmony_ci case PXA255_A0: 23388c2ecf20Sopenharmony_ci dev->has_cfr = 1; 23398c2ecf20Sopenharmony_ci break; 23408c2ecf20Sopenharmony_ci case PXA250_A0: 23418c2ecf20Sopenharmony_ci case PXA250_A1: 23428c2ecf20Sopenharmony_ci /* A0/A1 "not released"; ep 13, 15 unusable */ 23438c2ecf20Sopenharmony_ci fallthrough; 23448c2ecf20Sopenharmony_ci case PXA250_B2: case PXA210_B2: 23458c2ecf20Sopenharmony_ci case PXA250_B1: case PXA210_B1: 23468c2ecf20Sopenharmony_ci case PXA250_B0: case PXA210_B0: 23478c2ecf20Sopenharmony_ci /* OUT-DMA is broken ... */ 23488c2ecf20Sopenharmony_ci fallthrough; 23498c2ecf20Sopenharmony_ci case PXA250_C0: case PXA210_C0: 23508c2ecf20Sopenharmony_ci break; 23518c2ecf20Sopenharmony_ci#elif defined(CONFIG_ARCH_IXP4XX) 23528c2ecf20Sopenharmony_ci case IXP425_A0: 23538c2ecf20Sopenharmony_ci case IXP425_B0: 23548c2ecf20Sopenharmony_ci case IXP465_AD: 23558c2ecf20Sopenharmony_ci dev->has_cfr = 1; 23568c2ecf20Sopenharmony_ci break; 23578c2ecf20Sopenharmony_ci#endif 23588c2ecf20Sopenharmony_ci default: 23598c2ecf20Sopenharmony_ci pr_err("%s: unrecognized processor: %08x\n", 23608c2ecf20Sopenharmony_ci driver_name, chiprev); 23618c2ecf20Sopenharmony_ci /* iop3xx, ixp4xx, ... */ 23628c2ecf20Sopenharmony_ci return -ENODEV; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 23668c2ecf20Sopenharmony_ci if (irq < 0) 23678c2ecf20Sopenharmony_ci return -ENODEV; 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci dev->regs = devm_platform_ioremap_resource(pdev, 0); 23708c2ecf20Sopenharmony_ci if (IS_ERR(dev->regs)) 23718c2ecf20Sopenharmony_ci return PTR_ERR(dev->regs); 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci dev->clk = devm_clk_get(&pdev->dev, NULL); 23748c2ecf20Sopenharmony_ci if (IS_ERR(dev->clk)) 23758c2ecf20Sopenharmony_ci return PTR_ERR(dev->clk); 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, 23788c2ecf20Sopenharmony_ci dev->has_cfr ? "" : " (!cfr)", 23798c2ecf20Sopenharmony_ci SIZE_STR "(pio)" 23808c2ecf20Sopenharmony_ci ); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci /* other non-static parts of init */ 23838c2ecf20Sopenharmony_ci dev->dev = &pdev->dev; 23848c2ecf20Sopenharmony_ci dev->mach = dev_get_platdata(&pdev->dev); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci if (gpio_is_valid(dev->mach->gpio_pullup)) { 23898c2ecf20Sopenharmony_ci retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup, 23908c2ecf20Sopenharmony_ci "pca25x_udc GPIO PULLUP"); 23918c2ecf20Sopenharmony_ci if (retval) { 23928c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, 23938c2ecf20Sopenharmony_ci "can't get pullup gpio %d, err: %d\n", 23948c2ecf20Sopenharmony_ci dev->mach->gpio_pullup, retval); 23958c2ecf20Sopenharmony_ci goto err; 23968c2ecf20Sopenharmony_ci } 23978c2ecf20Sopenharmony_ci gpio_direction_output(dev->mach->gpio_pullup, 0); 23988c2ecf20Sopenharmony_ci } 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci timer_setup(&dev->timer, udc_watchdog, 0); 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci the_controller = dev; 24038c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci udc_disable(dev); 24068c2ecf20Sopenharmony_ci udc_reinit(dev); 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci dev->vbus = 0; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci /* irq setup after old hardware state is cleaned up */ 24118c2ecf20Sopenharmony_ci retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0, 24128c2ecf20Sopenharmony_ci driver_name, dev); 24138c2ecf20Sopenharmony_ci if (retval != 0) { 24148c2ecf20Sopenharmony_ci pr_err("%s: can't get irq %d, err %d\n", 24158c2ecf20Sopenharmony_ci driver_name, irq, retval); 24168c2ecf20Sopenharmony_ci goto err; 24178c2ecf20Sopenharmony_ci } 24188c2ecf20Sopenharmony_ci dev->got_irq = 1; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_LUBBOCK 24218c2ecf20Sopenharmony_ci if (machine_is_lubbock()) { 24228c2ecf20Sopenharmony_ci retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ, 24238c2ecf20Sopenharmony_ci lubbock_vbus_irq, 0, driver_name, 24248c2ecf20Sopenharmony_ci dev); 24258c2ecf20Sopenharmony_ci if (retval != 0) { 24268c2ecf20Sopenharmony_ci pr_err("%s: can't get irq %i, err %d\n", 24278c2ecf20Sopenharmony_ci driver_name, LUBBOCK_USB_DISC_IRQ, retval); 24288c2ecf20Sopenharmony_ci goto err; 24298c2ecf20Sopenharmony_ci } 24308c2ecf20Sopenharmony_ci retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ, 24318c2ecf20Sopenharmony_ci lubbock_vbus_irq, 0, driver_name, 24328c2ecf20Sopenharmony_ci dev); 24338c2ecf20Sopenharmony_ci if (retval != 0) { 24348c2ecf20Sopenharmony_ci pr_err("%s: can't get irq %i, err %d\n", 24358c2ecf20Sopenharmony_ci driver_name, LUBBOCK_USB_IRQ, retval); 24368c2ecf20Sopenharmony_ci goto err; 24378c2ecf20Sopenharmony_ci } 24388c2ecf20Sopenharmony_ci } else 24398c2ecf20Sopenharmony_ci#endif 24408c2ecf20Sopenharmony_ci create_debug_files(dev); 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); 24438c2ecf20Sopenharmony_ci if (!retval) 24448c2ecf20Sopenharmony_ci return retval; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci remove_debug_files(dev); 24478c2ecf20Sopenharmony_ci err: 24488c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(dev->transceiver)) 24498c2ecf20Sopenharmony_ci dev->transceiver = NULL; 24508c2ecf20Sopenharmony_ci return retval; 24518c2ecf20Sopenharmony_ci} 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_cistatic void pxa25x_udc_shutdown(struct platform_device *_dev) 24548c2ecf20Sopenharmony_ci{ 24558c2ecf20Sopenharmony_ci pullup_off(); 24568c2ecf20Sopenharmony_ci} 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_cistatic int pxa25x_udc_remove(struct platform_device *pdev) 24598c2ecf20Sopenharmony_ci{ 24608c2ecf20Sopenharmony_ci struct pxa25x_udc *dev = platform_get_drvdata(pdev); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci if (dev->driver) 24638c2ecf20Sopenharmony_ci return -EBUSY; 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci usb_del_gadget_udc(&dev->gadget); 24668c2ecf20Sopenharmony_ci dev->pullup = 0; 24678c2ecf20Sopenharmony_ci pullup(dev); 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci remove_debug_files(dev); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(dev->transceiver)) 24728c2ecf20Sopenharmony_ci dev->transceiver = NULL; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci the_controller = NULL; 24758c2ecf20Sopenharmony_ci return 0; 24768c2ecf20Sopenharmony_ci} 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci/* USB suspend (controlled by the host) and system suspend (controlled 24838c2ecf20Sopenharmony_ci * by the PXA) don't necessarily work well together. If USB is active, 24848c2ecf20Sopenharmony_ci * the 48 MHz clock is required; so the system can't enter 33 MHz idle 24858c2ecf20Sopenharmony_ci * mode, or any deeper PM saving state. 24868c2ecf20Sopenharmony_ci * 24878c2ecf20Sopenharmony_ci * For now, we punt and forcibly disconnect from the USB host when PXA 24888c2ecf20Sopenharmony_ci * enters any suspend state. While we're disconnected, we always disable 24898c2ecf20Sopenharmony_ci * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. 24908c2ecf20Sopenharmony_ci * Boards without software pullup control shouldn't use those states. 24918c2ecf20Sopenharmony_ci * VBUS IRQs should probably be ignored so that the PXA device just acts 24928c2ecf20Sopenharmony_ci * "dead" to USB hosts until system resume. 24938c2ecf20Sopenharmony_ci */ 24948c2ecf20Sopenharmony_cistatic int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) 24958c2ecf20Sopenharmony_ci{ 24968c2ecf20Sopenharmony_ci struct pxa25x_udc *udc = platform_get_drvdata(dev); 24978c2ecf20Sopenharmony_ci unsigned long flags; 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) 25008c2ecf20Sopenharmony_ci WARNING("USB host won't detect disconnect!\n"); 25018c2ecf20Sopenharmony_ci udc->suspended = 1; 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci local_irq_save(flags); 25048c2ecf20Sopenharmony_ci pullup(udc); 25058c2ecf20Sopenharmony_ci local_irq_restore(flags); 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci return 0; 25088c2ecf20Sopenharmony_ci} 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_cistatic int pxa25x_udc_resume(struct platform_device *dev) 25118c2ecf20Sopenharmony_ci{ 25128c2ecf20Sopenharmony_ci struct pxa25x_udc *udc = platform_get_drvdata(dev); 25138c2ecf20Sopenharmony_ci unsigned long flags; 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci udc->suspended = 0; 25168c2ecf20Sopenharmony_ci local_irq_save(flags); 25178c2ecf20Sopenharmony_ci pullup(udc); 25188c2ecf20Sopenharmony_ci local_irq_restore(flags); 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci return 0; 25218c2ecf20Sopenharmony_ci} 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci#else 25248c2ecf20Sopenharmony_ci#define pxa25x_udc_suspend NULL 25258c2ecf20Sopenharmony_ci#define pxa25x_udc_resume NULL 25268c2ecf20Sopenharmony_ci#endif 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_cistatic struct platform_driver udc_driver = { 25318c2ecf20Sopenharmony_ci .shutdown = pxa25x_udc_shutdown, 25328c2ecf20Sopenharmony_ci .probe = pxa25x_udc_probe, 25338c2ecf20Sopenharmony_ci .remove = pxa25x_udc_remove, 25348c2ecf20Sopenharmony_ci .suspend = pxa25x_udc_suspend, 25358c2ecf20Sopenharmony_ci .resume = pxa25x_udc_resume, 25368c2ecf20Sopenharmony_ci .driver = { 25378c2ecf20Sopenharmony_ci .name = "pxa25x-udc", 25388c2ecf20Sopenharmony_ci }, 25398c2ecf20Sopenharmony_ci}; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_cimodule_platform_driver(udc_driver); 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 25448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); 25458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 25468c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pxa25x-udc"); 2547