| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Renesas USBF USB Function driver |
| * |
| * Copyright 2022 Schneider Electric |
| * Author: Herve Codina <herve.codina@bootlin.com> |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/interrupt.h> |
| #include <linux/iopoll.h> |
| #include <linux/kernel.h> |
| #include <linux/kfifo.h> |
| #include <linux/mod_devicetable.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/types.h> |
| #include <linux/usb/composite.h> |
| #include <linux/usb/gadget.h> |
| #include <linux/usb/role.h> |
| |
| #define USBF_NUM_ENDPOINTS 16 |
| #define USBF_EP0_MAX_PCKT_SIZE 64 |
| |
| /* EPC registers */ |
| #define USBF_REG_USB_CONTROL 0x000 |
| #define USBF_USB_PUE2 BIT(2) |
| #define USBF_USB_CONNECTB BIT(3) |
| #define USBF_USB_DEFAULT BIT(4) |
| #define USBF_USB_CONF BIT(5) |
| #define USBF_USB_SUSPEND BIT(6) |
| #define USBF_USB_RSUM_IN BIT(7) |
| #define USBF_USB_SOF_RCV BIT(8) |
| #define USBF_USB_FORCEFS BIT(9) |
| #define USBF_USB_INT_SEL BIT(10) |
| #define USBF_USB_SOF_CLK_MODE BIT(11) |
| |
| #define USBF_REG_USB_STATUS 0x004 |
| #define USBF_USB_RSUM_OUT BIT(1) |
| #define USBF_USB_SPND_OUT BIT(2) |
| #define USBF_USB_USB_RST BIT(3) |
| #define USBF_USB_DEFAULT_ST BIT(4) |
| #define USBF_USB_CONF_ST BIT(5) |
| #define USBF_USB_SPEED_MODE BIT(6) |
| #define USBF_USB_SOF_DELAY_STATUS BIT(31) |
| |
| #define USBF_REG_USB_ADDRESS 0x008 |
| #define USBF_USB_SOF_STATUS BIT(15) |
| #define USBF_USB_SET_USB_ADDR(_a) ((_a) << 16) |
| #define USBF_USB_GET_FRAME(_r) ((_r) & 0x7FF) |
| |
| #define USBF_REG_SETUP_DATA0 0x018 |
| #define USBF_REG_SETUP_DATA1 0x01C |
| #define USBF_REG_USB_INT_STA 0x020 |
| #define USBF_USB_RSUM_INT BIT(1) |
| #define USBF_USB_SPND_INT BIT(2) |
| #define USBF_USB_USB_RST_INT BIT(3) |
| #define USBF_USB_SOF_INT BIT(4) |
| #define USBF_USB_SOF_ERROR_INT BIT(5) |
| #define USBF_USB_SPEED_MODE_INT BIT(6) |
| #define USBF_USB_EPN_INT(_n) (BIT(8) << (_n)) /* n=0..15 */ |
| |
| #define USBF_REG_USB_INT_ENA 0x024 |
| #define USBF_USB_RSUM_EN BIT(1) |
| #define USBF_USB_SPND_EN BIT(2) |
| #define USBF_USB_USB_RST_EN BIT(3) |
| #define USBF_USB_SOF_EN BIT(4) |
| #define USBF_USB_SOF_ERROR_EN BIT(5) |
| #define USBF_USB_SPEED_MODE_EN BIT(6) |
| #define USBF_USB_EPN_EN(_n) (BIT(8) << (_n)) /* n=0..15 */ |
| |
| #define USBF_BASE_EP0 0x028 |
| /* EP0 registers offsets from Base + USBF_BASE_EP0 (EP0 regs area) */ |
| #define USBF_REG_EP0_CONTROL 0x00 |
| #define USBF_EP0_ONAK BIT(0) |
| #define USBF_EP0_INAK BIT(1) |
| #define USBF_EP0_STL BIT(2) |
| #define USBF_EP0_PERR_NAK_CLR BIT(3) |
| #define USBF_EP0_INAK_EN BIT(4) |
| #define USBF_EP0_DW_MASK (0x3 << 5) |
| #define USBF_EP0_DW(_s) ((_s) << 5) |
| #define USBF_EP0_DEND BIT(7) |
| #define USBF_EP0_BCLR BIT(8) |
| #define USBF_EP0_PIDCLR BIT(9) |
| #define USBF_EP0_AUTO BIT(16) |
| #define USBF_EP0_OVERSEL BIT(17) |
| #define USBF_EP0_STGSEL BIT(18) |
| |
| #define USBF_REG_EP0_STATUS 0x04 |
| #define USBF_EP0_SETUP_INT BIT(0) |
| #define USBF_EP0_STG_START_INT BIT(1) |
| #define USBF_EP0_STG_END_INT BIT(2) |
| #define USBF_EP0_STALL_INT BIT(3) |
| #define USBF_EP0_IN_INT BIT(4) |
| #define USBF_EP0_OUT_INT BIT(5) |
| #define USBF_EP0_OUT_OR_INT BIT(6) |
| #define USBF_EP0_OUT_NULL_INT BIT(7) |
| #define USBF_EP0_IN_EMPTY BIT(8) |
| #define USBF_EP0_IN_FULL BIT(9) |
| #define USBF_EP0_IN_DATA BIT(10) |
| #define USBF_EP0_IN_NAK_INT BIT(11) |
| #define USBF_EP0_OUT_EMPTY BIT(12) |
| #define USBF_EP0_OUT_FULL BIT(13) |
| #define USBF_EP0_OUT_NULL BIT(14) |
| #define USBF_EP0_OUT_NAK_INT BIT(15) |
| #define USBF_EP0_PERR_NAK_INT BIT(16) |
| #define USBF_EP0_PERR_NAK BIT(17) |
| #define USBF_EP0_PID BIT(18) |
| |
| #define USBF_REG_EP0_INT_ENA 0x08 |
| #define USBF_EP0_SETUP_EN BIT(0) |
| #define USBF_EP0_STG_START_EN BIT(1) |
| #define USBF_EP0_STG_END_EN BIT(2) |
| #define USBF_EP0_STALL_EN BIT(3) |
| #define USBF_EP0_IN_EN BIT(4) |
| #define USBF_EP0_OUT_EN BIT(5) |
| #define USBF_EP0_OUT_OR_EN BIT(6) |
| #define USBF_EP0_OUT_NULL_EN BIT(7) |
| #define USBF_EP0_IN_NAK_EN BIT(11) |
| #define USBF_EP0_OUT_NAK_EN BIT(15) |
| #define USBF_EP0_PERR_NAK_EN BIT(16) |
| |
| #define USBF_REG_EP0_LENGTH 0x0C |
| #define USBF_EP0_LDATA (0x7FF << 0) |
| #define USBF_REG_EP0_READ 0x10 |
| #define USBF_REG_EP0_WRITE 0x14 |
| |
| #define USBF_BASE_EPN(_n) (0x040 + (_n) * 0x020) |
| /* EPn registers offsets from Base + USBF_BASE_EPN(n-1). n=1..15 */ |
| #define USBF_REG_EPN_CONTROL 0x000 |
| #define USBF_EPN_ONAK BIT(0) |
| #define USBF_EPN_OSTL BIT(2) |
| #define USBF_EPN_ISTL BIT(3) |
| #define USBF_EPN_OSTL_EN BIT(4) |
| #define USBF_EPN_DW_MASK (0x3 << 5) |
| #define USBF_EPN_DW(_s) ((_s) << 5) |
| #define USBF_EPN_DEND BIT(7) |
| #define USBF_EPN_CBCLR BIT(8) |
| #define USBF_EPN_BCLR BIT(9) |
| #define USBF_EPN_OPIDCLR BIT(10) |
| #define USBF_EPN_IPIDCLR BIT(11) |
| #define USBF_EPN_AUTO BIT(16) |
| #define USBF_EPN_OVERSEL BIT(17) |
| #define USBF_EPN_MODE_MASK (0x3 << 24) |
| #define USBF_EPN_MODE_BULK (0x0 << 24) |
| #define USBF_EPN_MODE_INTR (0x1 << 24) |
| #define USBF_EPN_MODE_ISO (0x2 << 24) |
| #define USBF_EPN_DIR0 BIT(26) |
| #define USBF_EPN_BUF_TYPE_DOUBLE BIT(30) |
| #define USBF_EPN_EN BIT(31) |
| |
| #define USBF_REG_EPN_STATUS 0x004 |
| #define USBF_EPN_IN_EMPTY BIT(0) |
| #define USBF_EPN_IN_FULL BIT(1) |
| #define USBF_EPN_IN_DATA BIT(2) |
| #define USBF_EPN_IN_INT BIT(3) |
| #define USBF_EPN_IN_STALL_INT BIT(4) |
| #define USBF_EPN_IN_NAK_ERR_INT BIT(5) |
| #define USBF_EPN_IN_END_INT BIT(7) |
| #define USBF_EPN_IPID BIT(10) |
| #define USBF_EPN_OUT_EMPTY BIT(16) |
| #define USBF_EPN_OUT_FULL BIT(17) |
| #define USBF_EPN_OUT_NULL_INT BIT(18) |
| #define USBF_EPN_OUT_INT BIT(19) |
| #define USBF_EPN_OUT_STALL_INT BIT(20) |
| #define USBF_EPN_OUT_NAK_ERR_INT BIT(21) |
| #define USBF_EPN_OUT_OR_INT BIT(22) |
| #define USBF_EPN_OUT_END_INT BIT(23) |
| #define USBF_EPN_ISO_CRC BIT(24) |
| #define USBF_EPN_ISO_OR BIT(26) |
| #define USBF_EPN_OUT_NOTKN BIT(27) |
| #define USBF_EPN_ISO_OPID BIT(28) |
| #define USBF_EPN_ISO_PIDERR BIT(29) |
| |
| #define USBF_REG_EPN_INT_ENA 0x008 |
| #define USBF_EPN_IN_EN BIT(3) |
| #define USBF_EPN_IN_STALL_EN BIT(4) |
| #define USBF_EPN_IN_NAK_ERR_EN BIT(5) |
| #define USBF_EPN_IN_END_EN BIT(7) |
| #define USBF_EPN_OUT_NULL_EN BIT(18) |
| #define USBF_EPN_OUT_EN BIT(19) |
| #define USBF_EPN_OUT_STALL_EN BIT(20) |
| #define USBF_EPN_OUT_NAK_ERR_EN BIT(21) |
| #define USBF_EPN_OUT_OR_EN BIT(22) |
| #define USBF_EPN_OUT_END_EN BIT(23) |
| |
| #define USBF_REG_EPN_DMA_CTRL 0x00C |
| #define USBF_EPN_DMAMODE0 BIT(0) |
| #define USBF_EPN_DMA_EN BIT(4) |
| #define USBF_EPN_STOP_SET BIT(8) |
| #define USBF_EPN_BURST_SET BIT(9) |
| #define USBF_EPN_DEND_SET BIT(10) |
| #define USBF_EPN_STOP_MODE BIT(11) |
| |
| #define USBF_REG_EPN_PCKT_ADRS 0x010 |
| #define USBF_EPN_MPKT(_l) ((_l) << 0) |
| #define USBF_EPN_BASEAD(_a) ((_a) << 16) |
| |
| #define USBF_REG_EPN_LEN_DCNT 0x014 |
| #define USBF_EPN_GET_LDATA(_r) ((_r) & 0x7FF) |
| #define USBF_EPN_SET_DMACNT(_c) ((_c) << 16) |
| #define USBF_EPN_GET_DMACNT(_r) (((_r) >> 16) & 0x1ff) |
| |
| #define USBF_REG_EPN_READ 0x018 |
| #define USBF_REG_EPN_WRITE 0x01C |
| |
| /* AHB-EPC Bridge registers */ |
| #define USBF_REG_AHBSCTR 0x1000 |
| #define USBF_REG_AHBMCTR 0x1004 |
| #define USBF_SYS_WBURST_TYPE BIT(2) |
| #define USBF_SYS_ARBITER_CTR BIT(31) |
| |
| #define USBF_REG_AHBBINT 0x1008 |
| #define USBF_SYS_ERR_MASTER (0x0F << 0) |
| #define USBF_SYS_SBUS_ERRINT0 BIT(4) |
| #define USBF_SYS_SBUS_ERRINT1 BIT(5) |
| #define USBF_SYS_MBUS_ERRINT BIT(6) |
| #define USBF_SYS_VBUS_INT BIT(13) |
| #define USBF_SYS_DMA_ENDINT_EPN(_n) (BIT(16) << (_n)) /* _n=1..15 */ |
| |
| #define USBF_REG_AHBBINTEN 0x100C |
| #define USBF_SYS_SBUS_ERRINT0EN BIT(4) |
| #define USBF_SYS_SBUS_ERRINT1EN BIT(5) |
| #define USBF_SYS_MBUS_ERRINTEN BIT(6) |
| #define USBF_SYS_VBUS_INTEN BIT(13) |
| #define USBF_SYS_DMA_ENDINTEN_EPN(_n) (BIT(16) << (_n)) /* _n=1..15 */ |
| |
| #define USBF_REG_EPCTR 0x1010 |
| #define USBF_SYS_EPC_RST BIT(0) |
| #define USBF_SYS_PLL_RST BIT(2) |
| #define USBF_SYS_PLL_LOCK BIT(4) |
| #define USBF_SYS_PLL_RESUME BIT(5) |
| #define USBF_SYS_VBUS_LEVEL BIT(8) |
| #define USBF_SYS_DIRPD BIT(12) |
| |
| #define USBF_REG_USBSSVER 0x1020 |
| #define USBF_REG_USBSSCONF 0x1024 |
| #define USBF_SYS_DMA_AVAILABLE(_n) (BIT(0) << (_n)) /* _n=0..15 */ |
| #define USBF_SYS_EP_AVAILABLE(_n) (BIT(16) << (_n)) /* _n=0..15 */ |
| |
| #define USBF_BASE_DMA_EPN(_n) (0x1110 + (_n) * 0x010) |
| /* EPn DMA registers offsets from Base USBF_BASE_DMA_EPN(n-1). n=1..15*/ |
| #define USBF_REG_DMA_EPN_DCR1 0x00 |
| #define USBF_SYS_EPN_REQEN BIT(0) |
| #define USBF_SYS_EPN_DIR0 BIT(1) |
| #define USBF_SYS_EPN_SET_DMACNT(_c) ((_c) << 16) |
| #define USBF_SYS_EPN_GET_DMACNT(_r) (((_r) >> 16) & 0x0FF) |
| |
| #define USBF_REG_DMA_EPN_DCR2 0x04 |
| #define USBF_SYS_EPN_MPKT(_s) ((_s) << 0) |
| #define USBF_SYS_EPN_LMPKT(_l) ((_l) << 16) |
| |
| #define USBF_REG_DMA_EPN_TADR 0x08 |
| |
| /* USB request */ |
| struct usbf_req { |
| struct usb_request req; |
| struct list_head queue; |
| unsigned int is_zero_sent : 1; |
| unsigned int is_mapped : 1; |
| enum { |
| USBF_XFER_START, |
| USBF_XFER_WAIT_DMA, |
| USBF_XFER_SEND_NULL, |
| USBF_XFER_WAIT_END, |
| USBF_XFER_WAIT_DMA_SHORT, |
| USBF_XFER_WAIT_BRIDGE, |
| } xfer_step; |
| size_t dma_size; |
| }; |
| |
| /* USB Endpoint */ |
| struct usbf_ep { |
| struct usb_ep ep; |
| char name[32]; |
| struct list_head queue; |
| unsigned int is_processing : 1; |
| unsigned int is_in : 1; |
| struct usbf_udc *udc; |
| void __iomem *regs; |
| void __iomem *dma_regs; |
| unsigned int id : 8; |
| unsigned int disabled : 1; |
| unsigned int is_wedged : 1; |
| unsigned int delayed_status : 1; |
| u32 status; |
| void (*bridge_on_dma_end)(struct usbf_ep *ep); |
| }; |
| |
| enum usbf_ep0state { |
| EP0_IDLE, |
| EP0_IN_DATA_PHASE, |
| EP0_OUT_DATA_PHASE, |
| EP0_OUT_STATUS_START_PHASE, |
| EP0_OUT_STATUS_PHASE, |
| EP0_OUT_STATUS_END_PHASE, |
| EP0_IN_STATUS_START_PHASE, |
| EP0_IN_STATUS_PHASE, |
| EP0_IN_STATUS_END_PHASE, |
| }; |
| |
| struct usbf_udc { |
| struct usb_gadget gadget; |
| struct usb_gadget_driver *driver; |
| struct device *dev; |
| void __iomem *regs; |
| spinlock_t lock; |
| bool is_remote_wakeup; |
| bool is_usb_suspended; |
| struct usbf_ep ep[USBF_NUM_ENDPOINTS]; |
| /* for EP0 control messages */ |
| enum usbf_ep0state ep0state; |
| struct usbf_req setup_reply; |
| u8 ep0_buf[USBF_EP0_MAX_PCKT_SIZE]; |
| }; |
| |
| struct usbf_ep_info { |
| const char *name; |
| struct usb_ep_caps caps; |
| u16 base_addr; |
| unsigned int is_double : 1; |
| u16 maxpacket_limit; |
| }; |
| |
| #define USBF_SINGLE_BUFFER 0 |
| #define USBF_DOUBLE_BUFFER 1 |
| #define USBF_EP_INFO(_name, _caps, _base_addr, _is_double, _maxpacket_limit) \ |
| { \ |
| .name = _name, \ |
| .caps = _caps, \ |
| .base_addr = _base_addr, \ |
| .is_double = _is_double, \ |
| .maxpacket_limit = _maxpacket_limit, \ |
| } |
| |
| /* This table is computed from the recommended values provided in the SOC |
| * datasheet. The buffer type (single/double) and the endpoint type cannot |
| * be changed. The mapping in internal RAM (base_addr and number of words) |
| * for each endpoints depends on the max packet size and the buffer type. |
| */ |
| static const struct usbf_ep_info usbf_ep_info[USBF_NUM_ENDPOINTS] = { |
| /* ep0: buf @0x0000 64 bytes, fixed 32 words */ |
| [0] = USBF_EP_INFO("ep0-ctrl", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0000, USBF_SINGLE_BUFFER, USBF_EP0_MAX_PCKT_SIZE), |
| /* ep1: buf @0x0020, 2 buffers 512 bytes -> (512 * 2 / 4) words */ |
| [1] = USBF_EP_INFO("ep1-bulk", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0020, USBF_DOUBLE_BUFFER, 512), |
| /* ep2: buf @0x0120, 2 buffers 512 bytes -> (512 * 2 / 4) words */ |
| [2] = USBF_EP_INFO("ep2-bulk", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0120, USBF_DOUBLE_BUFFER, 512), |
| /* ep3: buf @0x0220, 1 buffer 512 bytes -> (512 * 2 / 4) words */ |
| [3] = USBF_EP_INFO("ep3-bulk", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0220, USBF_SINGLE_BUFFER, 512), |
| /* ep4: buf @0x02A0, 1 buffer 512 bytes -> (512 * 1 / 4) words */ |
| [4] = USBF_EP_INFO("ep4-bulk", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, |
| USB_EP_CAPS_DIR_ALL), |
| 0x02A0, USBF_SINGLE_BUFFER, 512), |
| /* ep5: buf @0x0320, 1 buffer 512 bytes -> (512 * 2 / 4) words */ |
| [5] = USBF_EP_INFO("ep5-bulk", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0320, USBF_SINGLE_BUFFER, 512), |
| /* ep6: buf @0x03A0, 1 buffer 1024 bytes -> (1024 * 1 / 4) words */ |
| [6] = USBF_EP_INFO("ep6-int", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, |
| USB_EP_CAPS_DIR_ALL), |
| 0x03A0, USBF_SINGLE_BUFFER, 1024), |
| /* ep7: buf @0x04A0, 1 buffer 1024 bytes -> (1024 * 1 / 4) words */ |
| [7] = USBF_EP_INFO("ep7-int", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, |
| USB_EP_CAPS_DIR_ALL), |
| 0x04A0, USBF_SINGLE_BUFFER, 1024), |
| /* ep8: buf @0x0520, 1 buffer 1024 bytes -> (1024 * 1 / 4) words */ |
| [8] = USBF_EP_INFO("ep8-int", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0520, USBF_SINGLE_BUFFER, 1024), |
| /* ep9: buf @0x0620, 1 buffer 1024 bytes -> (1024 * 1 / 4) words */ |
| [9] = USBF_EP_INFO("ep9-int", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0620, USBF_SINGLE_BUFFER, 1024), |
| /* ep10: buf @0x0720, 2 buffers 1024 bytes -> (1024 * 2 / 4) words */ |
| [10] = USBF_EP_INFO("ep10-iso", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0720, USBF_DOUBLE_BUFFER, 1024), |
| /* ep11: buf @0x0920, 2 buffers 1024 bytes -> (1024 * 2 / 4) words */ |
| [11] = USBF_EP_INFO("ep11-iso", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0920, USBF_DOUBLE_BUFFER, 1024), |
| /* ep12: buf @0x0B20, 2 buffers 1024 bytes -> (1024 * 2 / 4) words */ |
| [12] = USBF_EP_INFO("ep12-iso", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0B20, USBF_DOUBLE_BUFFER, 1024), |
| /* ep13: buf @0x0D20, 2 buffers 1024 bytes -> (1024 * 2 / 4) words */ |
| [13] = USBF_EP_INFO("ep13-iso", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0D20, USBF_DOUBLE_BUFFER, 1024), |
| /* ep14: buf @0x0F20, 2 buffers 1024 bytes -> (1024 * 2 / 4) words */ |
| [14] = USBF_EP_INFO("ep14-iso", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, |
| USB_EP_CAPS_DIR_ALL), |
| 0x0F20, USBF_DOUBLE_BUFFER, 1024), |
| /* ep15: buf @0x1120, 2 buffers 1024 bytes -> (1024 * 2 / 4) words */ |
| [15] = USBF_EP_INFO("ep15-iso", |
| USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, |
| USB_EP_CAPS_DIR_ALL), |
| 0x1120, USBF_DOUBLE_BUFFER, 1024), |
| }; |
| |
| static inline u32 usbf_reg_readl(struct usbf_udc *udc, uint offset) |
| { |
| return readl(udc->regs + offset); |
| } |
| |
| static inline void usbf_reg_writel(struct usbf_udc *udc, uint offset, u32 val) |
| { |
| writel(val, udc->regs + offset); |
| } |
| |
| static inline void usbf_reg_bitset(struct usbf_udc *udc, uint offset, u32 set) |
| { |
| u32 tmp; |
| |
| tmp = usbf_reg_readl(udc, offset); |
| tmp |= set; |
| usbf_reg_writel(udc, offset, tmp); |
| } |
| |
| static inline void usbf_reg_bitclr(struct usbf_udc *udc, uint offset, u32 clr) |
| { |
| u32 tmp; |
| |
| tmp = usbf_reg_readl(udc, offset); |
| tmp &= ~clr; |
| usbf_reg_writel(udc, offset, tmp); |
| } |
| |
| static inline void usbf_reg_clrset(struct usbf_udc *udc, uint offset, |
| u32 clr, u32 set) |
| { |
| u32 tmp; |
| |
| tmp = usbf_reg_readl(udc, offset); |
| tmp &= ~clr; |
| tmp |= set; |
| usbf_reg_writel(udc, offset, tmp); |
| } |
| |
| static inline u32 usbf_ep_reg_readl(struct usbf_ep *ep, uint offset) |
| { |
| return readl(ep->regs + offset); |
| } |
| |
| static inline void usbf_ep_reg_read_rep(struct usbf_ep *ep, uint offset, |
| void *dst, uint count) |
| { |
| readsl(ep->regs + offset, dst, count); |
| } |
| |
| static inline void usbf_ep_reg_writel(struct usbf_ep *ep, uint offset, u32 val) |
| { |
| writel(val, ep->regs + offset); |
| } |
| |
| static inline void usbf_ep_reg_write_rep(struct usbf_ep *ep, uint offset, |
| const void *src, uint count) |
| { |
| writesl(ep->regs + offset, src, count); |
| } |
| |
| static inline void usbf_ep_reg_bitset(struct usbf_ep *ep, uint offset, u32 set) |
| { |
| u32 tmp; |
| |
| tmp = usbf_ep_reg_readl(ep, offset); |
| tmp |= set; |
| usbf_ep_reg_writel(ep, offset, tmp); |
| } |
| |
| static inline void usbf_ep_reg_bitclr(struct usbf_ep *ep, uint offset, u32 clr) |
| { |
| u32 tmp; |
| |
| tmp = usbf_ep_reg_readl(ep, offset); |
| tmp &= ~clr; |
| usbf_ep_reg_writel(ep, offset, tmp); |
| } |
| |
| static inline void usbf_ep_reg_clrset(struct usbf_ep *ep, uint offset, |
| u32 clr, u32 set) |
| { |
| u32 tmp; |
| |
| tmp = usbf_ep_reg_readl(ep, offset); |
| tmp &= ~clr; |
| tmp |= set; |
| usbf_ep_reg_writel(ep, offset, tmp); |
| } |
| |
| static inline u32 usbf_ep_dma_reg_readl(struct usbf_ep *ep, uint offset) |
| { |
| return readl(ep->dma_regs + offset); |
| } |
| |
| static inline void usbf_ep_dma_reg_writel(struct usbf_ep *ep, uint offset, |
| u32 val) |
| { |
| writel(val, ep->dma_regs + offset); |
| } |
| |
| static inline void usbf_ep_dma_reg_bitset(struct usbf_ep *ep, uint offset, |
| u32 set) |
| { |
| u32 tmp; |
| |
| tmp = usbf_ep_dma_reg_readl(ep, offset); |
| tmp |= set; |
| usbf_ep_dma_reg_writel(ep, offset, tmp); |
| } |
| |
| static inline void usbf_ep_dma_reg_bitclr(struct usbf_ep *ep, uint offset, |
| u32 clr) |
| { |
| u32 tmp; |
| |
| tmp = usbf_ep_dma_reg_readl(ep, offset); |
| tmp &= ~clr; |
| usbf_ep_dma_reg_writel(ep, offset, tmp); |
| } |
| |
| static void usbf_ep0_send_null(struct usbf_ep *ep0, bool is_data1) |
| { |
| u32 set; |
| |
| set = USBF_EP0_DEND; |
| if (is_data1) |
| set |= USBF_EP0_PIDCLR; |
| |
| usbf_ep_reg_bitset(ep0, USBF_REG_EP0_CONTROL, set); |
| } |
| |
| static int usbf_ep0_pio_in(struct usbf_ep *ep0, struct usbf_req *req) |
| { |
| unsigned int left; |
| unsigned int nb; |
| const void *buf; |
| u32 ctrl; |
| u32 last; |
| |
| left = req->req.length - req->req.actual; |
| |
| if (left == 0) { |
| if (!req->is_zero_sent) { |
| if (req->req.length == 0) { |
| dev_dbg(ep0->udc->dev, "ep0 send null\n"); |
| usbf_ep0_send_null(ep0, false); |
| req->is_zero_sent = 1; |
| return -EINPROGRESS; |
| } |
| if ((req->req.actual % ep0->ep.maxpacket) == 0) { |
| if (req->req.zero) { |
| dev_dbg(ep0->udc->dev, "ep0 send null\n"); |
| usbf_ep0_send_null(ep0, false); |
| req->is_zero_sent = 1; |
| return -EINPROGRESS; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| if (left > ep0->ep.maxpacket) |
| left = ep0->ep.maxpacket; |
| |
| buf = req->req.buf; |
| buf += req->req.actual; |
| |
| nb = left / sizeof(u32); |
| if (nb) { |
| usbf_ep_reg_write_rep(ep0, USBF_REG_EP0_WRITE, buf, nb); |
| buf += (nb * sizeof(u32)); |
| req->req.actual += (nb * sizeof(u32)); |
| left -= (nb * sizeof(u32)); |
| } |
| ctrl = usbf_ep_reg_readl(ep0, USBF_REG_EP0_CONTROL); |
| ctrl &= ~USBF_EP0_DW_MASK; |
| if (left) { |
| memcpy(&last, buf, left); |
| usbf_ep_reg_writel(ep0, USBF_REG_EP0_WRITE, last); |
| ctrl |= USBF_EP0_DW(left); |
| req->req.actual += left; |
| } |
| usbf_ep_reg_writel(ep0, USBF_REG_EP0_CONTROL, ctrl | USBF_EP0_DEND); |
| |
| dev_dbg(ep0->udc->dev, "ep0 send %u/%u\n", |
| req->req.actual, req->req.length); |
| |
| return -EINPROGRESS; |
| } |
| |
| static int usbf_ep0_pio_out(struct usbf_ep *ep0, struct usbf_req *req) |
| { |
| int req_status = 0; |
| unsigned int count; |
| unsigned int recv; |
| unsigned int left; |
| unsigned int nb; |
| void *buf; |
| u32 last; |
| |
| if (ep0->status & USBF_EP0_OUT_INT) { |
| recv = usbf_ep_reg_readl(ep0, USBF_REG_EP0_LENGTH) & USBF_EP0_LDATA; |
| count = recv; |
| |
| buf = req->req.buf; |
| buf += req->req.actual; |
| |
| left = req->req.length - req->req.actual; |
| |
| dev_dbg(ep0->udc->dev, "ep0 recv %u, left %u\n", count, left); |
| |
| if (left > ep0->ep.maxpacket) |
| left = ep0->ep.maxpacket; |
| |
| if (count > left) { |
| req_status = -EOVERFLOW; |
| count = left; |
| } |
| |
| if (count) { |
| nb = count / sizeof(u32); |
| if (nb) { |
| usbf_ep_reg_read_rep(ep0, USBF_REG_EP0_READ, |
| buf, nb); |
| buf += (nb * sizeof(u32)); |
| req->req.actual += (nb * sizeof(u32)); |
| count -= (nb * sizeof(u32)); |
| } |
| if (count) { |
| last = usbf_ep_reg_readl(ep0, USBF_REG_EP0_READ); |
| memcpy(buf, &last, count); |
| req->req.actual += count; |
| } |
| } |
| dev_dbg(ep0->udc->dev, "ep0 recv %u/%u\n", |
| req->req.actual, req->req.length); |
| |
| if (req_status) { |
| dev_dbg(ep0->udc->dev, "ep0 req.status=%d\n", req_status); |
| req->req.status = req_status; |
| return 0; |
| } |
| |
| if (recv < ep0->ep.maxpacket) { |
| dev_dbg(ep0->udc->dev, "ep0 short packet\n"); |
| /* This is a short packet -> It is the end */ |
| req->req.status = 0; |
| return 0; |
| } |
| |
| /* The Data stage of a control transfer from an endpoint to the |
| * host is complete when the endpoint does one of the following: |
| * - Has transferred exactly the expected amount of data |
| * - Transfers a packet with a payload size less than |
| * wMaxPacketSize or transfers a zero-length packet |
| */ |
| if (req->req.actual == req->req.length) { |
| req->req.status = 0; |
| return 0; |
| } |
| } |
| |
| if (ep0->status & USBF_EP0_OUT_NULL_INT) { |
| /* NULL packet received */ |
| dev_dbg(ep0->udc->dev, "ep0 null packet\n"); |
| if (req->req.actual != req->req.length) { |
| req->req.status = req->req.short_not_ok ? |
| -EREMOTEIO : 0; |
| } else { |
| req->req.status = 0; |
| } |
| return 0; |
| } |
| |
| return -EINPROGRESS; |
| } |
| |
| static void usbf_ep0_fifo_flush(struct usbf_ep *ep0) |
| { |
| u32 sts; |
| int ret; |
| |
| usbf_ep_reg_bitset(ep0, USBF_REG_EP0_CONTROL, USBF_EP0_BCLR); |
| |
| ret = readl_poll_timeout_atomic(ep0->regs + USBF_REG_EP0_STATUS, sts, |
| (sts & (USBF_EP0_IN_DATA | USBF_EP0_IN_EMPTY)) == USBF_EP0_IN_EMPTY, |
| 0, 10000); |
| if (ret) |
| dev_err(ep0->udc->dev, "ep0 flush fifo timed out\n"); |
| |
| } |
| |
| static void usbf_epn_send_null(struct usbf_ep *epn) |
| { |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_CONTROL, USBF_EPN_DEND); |
| } |
| |
| static void usbf_epn_send_residue(struct usbf_ep *epn, const void *buf, |
| unsigned int size) |
| { |
| u32 tmp; |
| |
| memcpy(&tmp, buf, size); |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_WRITE, tmp); |
| |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_CONTROL, |
| USBF_EPN_DW_MASK, |
| USBF_EPN_DW(size) | USBF_EPN_DEND); |
| } |
| |
| static int usbf_epn_pio_in(struct usbf_ep *epn, struct usbf_req *req) |
| { |
| unsigned int left; |
| unsigned int nb; |
| const void *buf; |
| |
| left = req->req.length - req->req.actual; |
| |
| if (left == 0) { |
| if (!req->is_zero_sent) { |
| if (req->req.length == 0) { |
| dev_dbg(epn->udc->dev, "ep%u send_null\n", epn->id); |
| usbf_epn_send_null(epn); |
| req->is_zero_sent = 1; |
| return -EINPROGRESS; |
| } |
| if ((req->req.actual % epn->ep.maxpacket) == 0) { |
| if (req->req.zero) { |
| dev_dbg(epn->udc->dev, "ep%u send_null\n", |
| epn->id); |
| usbf_epn_send_null(epn); |
| req->is_zero_sent = 1; |
| return -EINPROGRESS; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| if (left > epn->ep.maxpacket) |
| left = epn->ep.maxpacket; |
| |
| buf = req->req.buf; |
| buf += req->req.actual; |
| |
| nb = left / sizeof(u32); |
| if (nb) { |
| usbf_ep_reg_write_rep(epn, USBF_REG_EPN_WRITE, buf, nb); |
| buf += (nb * sizeof(u32)); |
| req->req.actual += (nb * sizeof(u32)); |
| left -= (nb * sizeof(u32)); |
| } |
| |
| if (left) { |
| usbf_epn_send_residue(epn, buf, left); |
| req->req.actual += left; |
| } else { |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_CONTROL, |
| USBF_EPN_DW_MASK, |
| USBF_EPN_DEND); |
| } |
| |
| dev_dbg(epn->udc->dev, "ep%u send %u/%u\n", epn->id, req->req.actual, |
| req->req.length); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void usbf_epn_enable_in_end_int(struct usbf_ep *epn) |
| { |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_INT_ENA, USBF_EPN_IN_END_EN); |
| } |
| |
| static int usbf_epn_dma_in(struct usbf_ep *epn, struct usbf_req *req) |
| { |
| unsigned int left; |
| u32 npkt; |
| u32 lastpkt; |
| int ret; |
| |
| if (!IS_ALIGNED((uintptr_t)req->req.buf, 4)) { |
| dev_dbg(epn->udc->dev, "ep%u buf unaligned -> fallback pio\n", |
| epn->id); |
| return usbf_epn_pio_in(epn, req); |
| } |
| |
| left = req->req.length - req->req.actual; |
| |
| switch (req->xfer_step) { |
| default: |
| case USBF_XFER_START: |
| if (left == 0) { |
| dev_dbg(epn->udc->dev, "ep%u send null\n", epn->id); |
| usbf_epn_send_null(epn); |
| req->xfer_step = USBF_XFER_WAIT_END; |
| break; |
| } |
| if (left < 4) { |
| dev_dbg(epn->udc->dev, "ep%u send residue %u\n", epn->id, |
| left); |
| usbf_epn_send_residue(epn, |
| req->req.buf + req->req.actual, left); |
| req->req.actual += left; |
| req->xfer_step = USBF_XFER_WAIT_END; |
| break; |
| } |
| |
| ret = usb_gadget_map_request(&epn->udc->gadget, &req->req, 1); |
| if (ret < 0) { |
| dev_err(epn->udc->dev, "usb_gadget_map_request failed (%d)\n", |
| ret); |
| return ret; |
| } |
| req->is_mapped = 1; |
| |
| npkt = DIV_ROUND_UP(left, epn->ep.maxpacket); |
| lastpkt = (left % epn->ep.maxpacket); |
| if (lastpkt == 0) |
| lastpkt = epn->ep.maxpacket; |
| lastpkt &= ~0x3; /* DMA is done on 32bit units */ |
| |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_DCR2, |
| USBF_SYS_EPN_MPKT(epn->ep.maxpacket) | USBF_SYS_EPN_LMPKT(lastpkt)); |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_TADR, |
| req->req.dma); |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_SET_DMACNT(npkt)); |
| usbf_ep_dma_reg_bitset(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_REQEN); |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_LEN_DCNT, USBF_EPN_SET_DMACNT(npkt)); |
| |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_CONTROL, USBF_EPN_AUTO); |
| |
| /* The end of DMA transfer at the USBF level needs to be handle |
| * after the detection of the end of DMA transfer at the brige |
| * level. |
| * To force this sequence, EPN_IN_END_EN will be set by the |
| * detection of the end of transfer at bridge level (ie. bridge |
| * interrupt). |
| */ |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_IN_EN | USBF_EPN_IN_END_EN); |
| epn->bridge_on_dma_end = usbf_epn_enable_in_end_int; |
| |
| /* Clear any pending IN_END interrupt */ |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, ~(u32)USBF_EPN_IN_END_INT); |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_DMA_CTRL, |
| USBF_EPN_BURST_SET | USBF_EPN_DMAMODE0); |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_DMA_CTRL, |
| USBF_EPN_DMA_EN); |
| |
| req->dma_size = (npkt - 1) * epn->ep.maxpacket + lastpkt; |
| |
| dev_dbg(epn->udc->dev, "ep%u dma xfer %zu\n", epn->id, |
| req->dma_size); |
| |
| req->xfer_step = USBF_XFER_WAIT_DMA; |
| break; |
| |
| case USBF_XFER_WAIT_DMA: |
| if (!(epn->status & USBF_EPN_IN_END_INT)) { |
| dev_dbg(epn->udc->dev, "ep%u dma not done\n", epn->id); |
| break; |
| } |
| dev_dbg(epn->udc->dev, "ep%u dma done\n", epn->id); |
| |
| usb_gadget_unmap_request(&epn->udc->gadget, &req->req, 1); |
| req->is_mapped = 0; |
| |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_CONTROL, USBF_EPN_AUTO); |
| |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_IN_END_EN, |
| USBF_EPN_IN_EN); |
| |
| req->req.actual += req->dma_size; |
| |
| left = req->req.length - req->req.actual; |
| if (left) { |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, ~(u32)USBF_EPN_IN_INT); |
| |
| dev_dbg(epn->udc->dev, "ep%u send residue %u\n", epn->id, |
| left); |
| usbf_epn_send_residue(epn, |
| req->req.buf + req->req.actual, left); |
| req->req.actual += left; |
| req->xfer_step = USBF_XFER_WAIT_END; |
| break; |
| } |
| |
| if (req->req.actual % epn->ep.maxpacket) { |
| /* last packet was a short packet. Tell the hardware to |
| * send it right now. |
| */ |
| dev_dbg(epn->udc->dev, "ep%u send short\n", epn->id); |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, |
| ~(u32)USBF_EPN_IN_INT); |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_CONTROL, |
| USBF_EPN_DEND); |
| |
| req->xfer_step = USBF_XFER_WAIT_END; |
| break; |
| } |
| |
| /* Last packet size was a maxpacket size |
| * Send null packet if needed |
| */ |
| if (req->req.zero) { |
| req->xfer_step = USBF_XFER_SEND_NULL; |
| break; |
| } |
| |
| /* No more action to do. Wait for the end of the USB transfer */ |
| req->xfer_step = USBF_XFER_WAIT_END; |
| break; |
| |
| case USBF_XFER_SEND_NULL: |
| dev_dbg(epn->udc->dev, "ep%u send null\n", epn->id); |
| usbf_epn_send_null(epn); |
| req->xfer_step = USBF_XFER_WAIT_END; |
| break; |
| |
| case USBF_XFER_WAIT_END: |
| if (!(epn->status & USBF_EPN_IN_INT)) { |
| dev_dbg(epn->udc->dev, "ep%u end not done\n", epn->id); |
| break; |
| } |
| dev_dbg(epn->udc->dev, "ep%u send done %u/%u\n", epn->id, |
| req->req.actual, req->req.length); |
| req->xfer_step = USBF_XFER_START; |
| return 0; |
| } |
| |
| return -EINPROGRESS; |
| } |
| |
| static void usbf_epn_recv_residue(struct usbf_ep *epn, void *buf, |
| unsigned int size) |
| { |
| u32 last; |
| |
| last = usbf_ep_reg_readl(epn, USBF_REG_EPN_READ); |
| memcpy(buf, &last, size); |
| } |
| |
| static int usbf_epn_pio_out(struct usbf_ep *epn, struct usbf_req *req) |
| { |
| int req_status = 0; |
| unsigned int count; |
| unsigned int recv; |
| unsigned int left; |
| unsigned int nb; |
| void *buf; |
| |
| if (epn->status & USBF_EPN_OUT_INT) { |
| recv = USBF_EPN_GET_LDATA( |
| usbf_ep_reg_readl(epn, USBF_REG_EPN_LEN_DCNT)); |
| count = recv; |
| |
| buf = req->req.buf; |
| buf += req->req.actual; |
| |
| left = req->req.length - req->req.actual; |
| |
| dev_dbg(epn->udc->dev, "ep%u recv %u, left %u, mpkt %u\n", epn->id, |
| recv, left, epn->ep.maxpacket); |
| |
| if (left > epn->ep.maxpacket) |
| left = epn->ep.maxpacket; |
| |
| if (count > left) { |
| req_status = -EOVERFLOW; |
| count = left; |
| } |
| |
| if (count) { |
| nb = count / sizeof(u32); |
| if (nb) { |
| usbf_ep_reg_read_rep(epn, USBF_REG_EPN_READ, |
| buf, nb); |
| buf += (nb * sizeof(u32)); |
| req->req.actual += (nb * sizeof(u32)); |
| count -= (nb * sizeof(u32)); |
| } |
| if (count) { |
| usbf_epn_recv_residue(epn, buf, count); |
| req->req.actual += count; |
| } |
| } |
| dev_dbg(epn->udc->dev, "ep%u recv %u/%u\n", epn->id, |
| req->req.actual, req->req.length); |
| |
| if (req_status) { |
| dev_dbg(epn->udc->dev, "ep%u req.status=%d\n", epn->id, |
| req_status); |
| req->req.status = req_status; |
| return 0; |
| } |
| |
| if (recv < epn->ep.maxpacket) { |
| dev_dbg(epn->udc->dev, "ep%u short packet\n", epn->id); |
| /* This is a short packet -> It is the end */ |
| req->req.status = 0; |
| return 0; |
| } |
| |
| /* Request full -> complete */ |
| if (req->req.actual == req->req.length) { |
| req->req.status = 0; |
| return 0; |
| } |
| } |
| |
| if (epn->status & USBF_EPN_OUT_NULL_INT) { |
| /* NULL packet received */ |
| dev_dbg(epn->udc->dev, "ep%u null packet\n", epn->id); |
| if (req->req.actual != req->req.length) { |
| req->req.status = req->req.short_not_ok ? |
| -EREMOTEIO : 0; |
| } else { |
| req->req.status = 0; |
| } |
| return 0; |
| } |
| |
| return -EINPROGRESS; |
| } |
| |
| static void usbf_epn_enable_out_end_int(struct usbf_ep *epn) |
| { |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_INT_ENA, USBF_EPN_OUT_END_EN); |
| } |
| |
| static void usbf_epn_process_queue(struct usbf_ep *epn); |
| |
| static void usbf_epn_dma_out_send_dma(struct usbf_ep *epn, dma_addr_t addr, u32 npkt, bool is_short) |
| { |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_DCR2, USBF_SYS_EPN_MPKT(epn->ep.maxpacket)); |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_TADR, addr); |
| |
| if (is_short) { |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_SET_DMACNT(1) | USBF_SYS_EPN_DIR0); |
| usbf_ep_dma_reg_bitset(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_REQEN); |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_LEN_DCNT, |
| USBF_EPN_SET_DMACNT(0)); |
| |
| /* The end of DMA transfer at the USBF level needs to be handled |
| * after the detection of the end of DMA transfer at the brige |
| * level. |
| * To force this sequence, enabling the OUT_END interrupt will |
| * be donee by the detection of the end of transfer at bridge |
| * level (ie. bridge interrupt). |
| */ |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_EN | USBF_EPN_OUT_NULL_EN | USBF_EPN_OUT_END_EN); |
| epn->bridge_on_dma_end = usbf_epn_enable_out_end_int; |
| |
| /* Clear any pending OUT_END interrupt */ |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, |
| ~(u32)USBF_EPN_OUT_END_INT); |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_DMA_CTRL, |
| USBF_EPN_STOP_MODE | USBF_EPN_STOP_SET | USBF_EPN_DMAMODE0); |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_DMA_CTRL, |
| USBF_EPN_DMA_EN); |
| return; |
| } |
| |
| usbf_ep_dma_reg_writel(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_SET_DMACNT(npkt) | USBF_SYS_EPN_DIR0); |
| usbf_ep_dma_reg_bitset(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_REQEN); |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_LEN_DCNT, |
| USBF_EPN_SET_DMACNT(npkt)); |
| |
| /* Here, the bridge may or may not generate an interrupt to signal the |
| * end of DMA transfer. |
| * Keep only OUT_END interrupt and let handle the bridge later during |
| * the OUT_END processing. |
| */ |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_EN | USBF_EPN_OUT_NULL_EN, |
| USBF_EPN_OUT_END_EN); |
| |
| /* Disable bridge interrupt. It will be renabled later */ |
| usbf_reg_bitclr(epn->udc, USBF_REG_AHBBINTEN, |
| USBF_SYS_DMA_ENDINTEN_EPN(epn->id)); |
| |
| /* Clear any pending DMA_END interrupt at bridge level */ |
| usbf_reg_writel(epn->udc, USBF_REG_AHBBINT, |
| USBF_SYS_DMA_ENDINT_EPN(epn->id)); |
| |
| /* Clear any pending OUT_END interrupt */ |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, |
| ~(u32)USBF_EPN_OUT_END_INT); |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_DMA_CTRL, |
| USBF_EPN_STOP_MODE | USBF_EPN_STOP_SET | USBF_EPN_DMAMODE0 | USBF_EPN_BURST_SET); |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_DMA_CTRL, |
| USBF_EPN_DMA_EN); |
| } |
| |
| static size_t usbf_epn_dma_out_complete_dma(struct usbf_ep *epn, bool is_short) |
| { |
| u32 dmacnt; |
| u32 tmp; |
| int ret; |
| |
| /* Restore interrupt mask */ |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_END_EN, |
| USBF_EPN_OUT_EN | USBF_EPN_OUT_NULL_EN); |
| |
| if (is_short) { |
| /* Nothing more to do when the DMA was for a short packet */ |
| return 0; |
| } |
| |
| /* Enable the bridge interrupt */ |
| usbf_reg_bitset(epn->udc, USBF_REG_AHBBINTEN, |
| USBF_SYS_DMA_ENDINTEN_EPN(epn->id)); |
| |
| tmp = usbf_ep_reg_readl(epn, USBF_REG_EPN_LEN_DCNT); |
| dmacnt = USBF_EPN_GET_DMACNT(tmp); |
| |
| if (dmacnt) { |
| /* Some packet were not received (halted by a short or a null |
| * packet. |
| * The bridge never raises an interrupt in this case. |
| * Wait for the end of transfer at bridge level |
| */ |
| ret = readl_poll_timeout_atomic( |
| epn->dma_regs + USBF_REG_DMA_EPN_DCR1, |
| tmp, (USBF_SYS_EPN_GET_DMACNT(tmp) == dmacnt), |
| 0, 10000); |
| if (ret) { |
| dev_err(epn->udc->dev, "ep%u wait bridge timed out\n", |
| epn->id); |
| } |
| |
| usbf_ep_dma_reg_bitclr(epn, USBF_REG_DMA_EPN_DCR1, |
| USBF_SYS_EPN_REQEN); |
| |
| /* The dmacnt value tells how many packet were not transferred |
| * from the maximum number of packet we set for the DMA transfer. |
| * Compute the left DMA size based on this value. |
| */ |
| return dmacnt * epn->ep.maxpacket; |
| } |
| |
| return 0; |
| } |
| |
| static int usbf_epn_dma_out(struct usbf_ep *epn, struct usbf_req *req) |
| { |
| unsigned int dma_left; |
| unsigned int count; |
| unsigned int recv; |
| unsigned int left; |
| u32 npkt; |
| int ret; |
| |
| if (!IS_ALIGNED((uintptr_t)req->req.buf, 4)) { |
| dev_dbg(epn->udc->dev, "ep%u buf unaligned -> fallback pio\n", |
| epn->id); |
| return usbf_epn_pio_out(epn, req); |
| } |
| |
| switch (req->xfer_step) { |
| default: |
| case USBF_XFER_START: |
| if (epn->status & USBF_EPN_OUT_NULL_INT) { |
| dev_dbg(epn->udc->dev, "ep%u null packet\n", epn->id); |
| if (req->req.actual != req->req.length) { |
| req->req.status = req->req.short_not_ok ? |
| -EREMOTEIO : 0; |
| } else { |
| req->req.status = 0; |
| } |
| return 0; |
| } |
| |
| if (!(epn->status & USBF_EPN_OUT_INT)) { |
| dev_dbg(epn->udc->dev, "ep%u OUT_INT not set -> spurious\n", |
| epn->id); |
| break; |
| } |
| |
| recv = USBF_EPN_GET_LDATA( |
| usbf_ep_reg_readl(epn, USBF_REG_EPN_LEN_DCNT)); |
| if (!recv) { |
| dev_dbg(epn->udc->dev, "ep%u recv = 0 -> spurious\n", |
| epn->id); |
| break; |
| } |
| |
| left = req->req.length - req->req.actual; |
| |
| dev_dbg(epn->udc->dev, "ep%u recv %u, left %u, mpkt %u\n", epn->id, |
| recv, left, epn->ep.maxpacket); |
| |
| if (recv > left) { |
| dev_err(epn->udc->dev, "ep%u overflow (%u/%u)\n", |
| epn->id, recv, left); |
| req->req.status = -EOVERFLOW; |
| return -EOVERFLOW; |
| } |
| |
| if (recv < epn->ep.maxpacket) { |
| /* Short packet received */ |
| dev_dbg(epn->udc->dev, "ep%u short packet\n", epn->id); |
| if (recv <= 3) { |
| usbf_epn_recv_residue(epn, |
| req->req.buf + req->req.actual, recv); |
| req->req.actual += recv; |
| |
| dev_dbg(epn->udc->dev, "ep%u recv done %u/%u\n", |
| epn->id, req->req.actual, req->req.length); |
| |
| req->xfer_step = USBF_XFER_START; |
| return 0; |
| } |
| |
| ret = usb_gadget_map_request(&epn->udc->gadget, &req->req, 0); |
| if (ret < 0) { |
| dev_err(epn->udc->dev, "map request failed (%d)\n", |
| ret); |
| return ret; |
| } |
| req->is_mapped = 1; |
| |
| usbf_epn_dma_out_send_dma(epn, |
| req->req.dma + req->req.actual, |
| 1, true); |
| req->dma_size = recv & ~0x3; |
| |
| dev_dbg(epn->udc->dev, "ep%u dma short xfer %zu\n", epn->id, |
| req->dma_size); |
| |
| req->xfer_step = USBF_XFER_WAIT_DMA_SHORT; |
| break; |
| } |
| |
| ret = usb_gadget_map_request(&epn->udc->gadget, &req->req, 0); |
| if (ret < 0) { |
| dev_err(epn->udc->dev, "map request failed (%d)\n", |
| ret); |
| return ret; |
| } |
| req->is_mapped = 1; |
| |
| /* Use the maximum DMA size according to the request buffer. |
| * We will adjust the received size later at the end of the DMA |
| * transfer with the left size computed from |
| * usbf_epn_dma_out_complete_dma(). |
| */ |
| npkt = left / epn->ep.maxpacket; |
| usbf_epn_dma_out_send_dma(epn, |
| req->req.dma + req->req.actual, |
| npkt, false); |
| req->dma_size = npkt * epn->ep.maxpacket; |
| |
| dev_dbg(epn->udc->dev, "ep%u dma xfer %zu (%u)\n", epn->id, |
| req->dma_size, npkt); |
| |
| req->xfer_step = USBF_XFER_WAIT_DMA; |
| break; |
| |
| case USBF_XFER_WAIT_DMA_SHORT: |
| if (!(epn->status & USBF_EPN_OUT_END_INT)) { |
| dev_dbg(epn->udc->dev, "ep%u dma short not done\n", epn->id); |
| break; |
| } |
| dev_dbg(epn->udc->dev, "ep%u dma short done\n", epn->id); |
| |
| usbf_epn_dma_out_complete_dma(epn, true); |
| |
| usb_gadget_unmap_request(&epn->udc->gadget, &req->req, 0); |
| req->is_mapped = 0; |
| |
| req->req.actual += req->dma_size; |
| |
| recv = USBF_EPN_GET_LDATA( |
| usbf_ep_reg_readl(epn, USBF_REG_EPN_LEN_DCNT)); |
| |
| count = recv & 0x3; |
| if (count) { |
| dev_dbg(epn->udc->dev, "ep%u recv residue %u\n", epn->id, |
| count); |
| usbf_epn_recv_residue(epn, |
| req->req.buf + req->req.actual, count); |
| req->req.actual += count; |
| } |
| |
| dev_dbg(epn->udc->dev, "ep%u recv done %u/%u\n", epn->id, |
| req->req.actual, req->req.length); |
| |
| req->xfer_step = USBF_XFER_START; |
| return 0; |
| |
| case USBF_XFER_WAIT_DMA: |
| if (!(epn->status & USBF_EPN_OUT_END_INT)) { |
| dev_dbg(epn->udc->dev, "ep%u dma not done\n", epn->id); |
| break; |
| } |
| dev_dbg(epn->udc->dev, "ep%u dma done\n", epn->id); |
| |
| dma_left = usbf_epn_dma_out_complete_dma(epn, false); |
| if (dma_left) { |
| /* Adjust the final DMA size with */ |
| count = req->dma_size - dma_left; |
| |
| dev_dbg(epn->udc->dev, "ep%u dma xfer done %u\n", epn->id, |
| count); |
| |
| req->req.actual += count; |
| |
| if (epn->status & USBF_EPN_OUT_NULL_INT) { |
| /* DMA was stopped by a null packet reception */ |
| dev_dbg(epn->udc->dev, "ep%u dma stopped by null pckt\n", |
| epn->id); |
| usb_gadget_unmap_request(&epn->udc->gadget, |
| &req->req, 0); |
| req->is_mapped = 0; |
| |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, |
| ~(u32)USBF_EPN_OUT_NULL_INT); |
| |
| if (req->req.actual != req->req.length) { |
| req->req.status = req->req.short_not_ok ? |
| -EREMOTEIO : 0; |
| } else { |
| req->req.status = 0; |
| } |
| dev_dbg(epn->udc->dev, "ep%u recv done %u/%u\n", |
| epn->id, req->req.actual, req->req.length); |
| req->xfer_step = USBF_XFER_START; |
| return 0; |
| } |
| |
| recv = USBF_EPN_GET_LDATA( |
| usbf_ep_reg_readl(epn, USBF_REG_EPN_LEN_DCNT)); |
| left = req->req.length - req->req.actual; |
| if (recv > left) { |
| dev_err(epn->udc->dev, |
| "ep%u overflow (%u/%u)\n", epn->id, |
| recv, left); |
| req->req.status = -EOVERFLOW; |
| usb_gadget_unmap_request(&epn->udc->gadget, |
| &req->req, 0); |
| req->is_mapped = 0; |
| |
| req->xfer_step = USBF_XFER_START; |
| return -EOVERFLOW; |
| } |
| |
| if (recv > 3) { |
| usbf_epn_dma_out_send_dma(epn, |
| req->req.dma + req->req.actual, |
| 1, true); |
| req->dma_size = recv & ~0x3; |
| |
| dev_dbg(epn->udc->dev, "ep%u dma short xfer %zu\n", |
| epn->id, req->dma_size); |
| |
| req->xfer_step = USBF_XFER_WAIT_DMA_SHORT; |
| break; |
| } |
| |
| usb_gadget_unmap_request(&epn->udc->gadget, &req->req, 0); |
| req->is_mapped = 0; |
| |
| count = recv & 0x3; |
| if (count) { |
| dev_dbg(epn->udc->dev, "ep%u recv residue %u\n", |
| epn->id, count); |
| usbf_epn_recv_residue(epn, |
| req->req.buf + req->req.actual, count); |
| req->req.actual += count; |
| } |
| |
| dev_dbg(epn->udc->dev, "ep%u recv done %u/%u\n", epn->id, |
| req->req.actual, req->req.length); |
| |
| req->xfer_step = USBF_XFER_START; |
| return 0; |
| } |
| |
| /* Process queue at bridge interrupt only */ |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_END_EN | USBF_EPN_OUT_EN | USBF_EPN_OUT_NULL_EN); |
| epn->status = 0; |
| epn->bridge_on_dma_end = usbf_epn_process_queue; |
| |
| req->xfer_step = USBF_XFER_WAIT_BRIDGE; |
| break; |
| |
| case USBF_XFER_WAIT_BRIDGE: |
| dev_dbg(epn->udc->dev, "ep%u bridge transfers done\n", epn->id); |
| |
| /* Restore interrupt mask */ |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_END_EN, |
| USBF_EPN_OUT_EN | USBF_EPN_OUT_NULL_EN); |
| |
| usb_gadget_unmap_request(&epn->udc->gadget, &req->req, 0); |
| req->is_mapped = 0; |
| |
| req->req.actual += req->dma_size; |
| |
| req->xfer_step = USBF_XFER_START; |
| left = req->req.length - req->req.actual; |
| if (!left) { |
| /* No more data can be added to the buffer */ |
| dev_dbg(epn->udc->dev, "ep%u recv done %u/%u\n", epn->id, |
| req->req.actual, req->req.length); |
| return 0; |
| } |
| dev_dbg(epn->udc->dev, "ep%u recv done %u/%u, wait more data\n", |
| epn->id, req->req.actual, req->req.length); |
| break; |
| } |
| |
| return -EINPROGRESS; |
| } |
| |
| static void usbf_epn_dma_stop(struct usbf_ep *epn) |
| { |
| usbf_ep_dma_reg_bitclr(epn, USBF_REG_DMA_EPN_DCR1, USBF_SYS_EPN_REQEN); |
| |
| /* In the datasheet: |
| * If EP[m]_REQEN = 0b is set during DMA transfer, AHB-EPC stops DMA |
| * after 1 packet transfer completed. |
| * Therefore, wait sufficient time for ensuring DMA transfer |
| * completion. The WAIT time depends on the system, especially AHB |
| * bus activity |
| * So arbitrary 10ms would be sufficient. |
| */ |
| mdelay(10); |
| |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_DMA_CTRL, USBF_EPN_DMA_EN); |
| } |
| |
| static void usbf_epn_dma_abort(struct usbf_ep *epn, struct usbf_req *req) |
| { |
| dev_dbg(epn->udc->dev, "ep%u %s dma abort\n", epn->id, |
| epn->is_in ? "in" : "out"); |
| |
| epn->bridge_on_dma_end = NULL; |
| |
| usbf_epn_dma_stop(epn); |
| |
| usb_gadget_unmap_request(&epn->udc->gadget, &req->req, |
| epn->is_in ? 1 : 0); |
| req->is_mapped = 0; |
| |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_CONTROL, USBF_EPN_AUTO); |
| |
| if (epn->is_in) { |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_IN_END_EN, |
| USBF_EPN_IN_EN); |
| } else { |
| usbf_ep_reg_clrset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_END_EN, |
| USBF_EPN_OUT_EN | USBF_EPN_OUT_NULL_EN); |
| } |
| |
| /* As dma is stopped, be sure that no DMA interrupt are pending */ |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, |
| USBF_EPN_IN_END_INT | USBF_EPN_OUT_END_INT); |
| |
| usbf_reg_writel(epn->udc, USBF_REG_AHBBINT, USBF_SYS_DMA_ENDINT_EPN(epn->id)); |
| |
| /* Enable DMA interrupt the bridge level */ |
| usbf_reg_bitset(epn->udc, USBF_REG_AHBBINTEN, |
| USBF_SYS_DMA_ENDINTEN_EPN(epn->id)); |
| |
| /* Reset transfer step */ |
| req->xfer_step = USBF_XFER_START; |
| } |
| |
| static void usbf_epn_fifo_flush(struct usbf_ep *epn) |
| { |
| u32 ctrl; |
| u32 sts; |
| int ret; |
| |
| dev_dbg(epn->udc->dev, "ep%u %s fifo flush\n", epn->id, |
| epn->is_in ? "in" : "out"); |
| |
| ctrl = usbf_ep_reg_readl(epn, USBF_REG_EPN_CONTROL); |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_CONTROL, ctrl | USBF_EPN_BCLR); |
| |
| if (ctrl & USBF_EPN_DIR0) |
| return; |
| |
| ret = readl_poll_timeout_atomic(epn->regs + USBF_REG_EPN_STATUS, sts, |
| (sts & (USBF_EPN_IN_DATA | USBF_EPN_IN_EMPTY)) == USBF_EPN_IN_EMPTY, |
| 0, 10000); |
| if (ret) |
| dev_err(epn->udc->dev, "ep%u flush fifo timed out\n", epn->id); |
| } |
| |
| static void usbf_ep_req_done(struct usbf_ep *ep, struct usbf_req *req, |
| int status) |
| { |
| list_del_init(&req->queue); |
| |
| if (status) { |
| req->req.status = status; |
| } else { |
| if (req->req.status == -EINPROGRESS) |
| req->req.status = status; |
| } |
| |
| dev_dbg(ep->udc->dev, "ep%u %s req done length %u/%u, status=%d\n", ep->id, |
| ep->is_in ? "in" : "out", |
| req->req.actual, req->req.length, req->req.status); |
| |
| if (req->is_mapped) |
| usbf_epn_dma_abort(ep, req); |
| |
| spin_unlock(&ep->udc->lock); |
| usb_gadget_giveback_request(&ep->ep, &req->req); |
| spin_lock(&ep->udc->lock); |
| } |
| |
| static void usbf_ep_nuke(struct usbf_ep *ep, int status) |
| { |
| struct usbf_req *req; |
| |
| dev_dbg(ep->udc->dev, "ep%u %s nuke status %d\n", ep->id, |
| ep->is_in ? "in" : "out", |
| status); |
| |
| while (!list_empty(&ep->queue)) { |
| req = list_first_entry(&ep->queue, struct usbf_req, queue); |
| usbf_ep_req_done(ep, req, status); |
| } |
| |
| if (ep->id == 0) |
| usbf_ep0_fifo_flush(ep); |
| else |
| usbf_epn_fifo_flush(ep); |
| } |
| |
| static bool usbf_ep_is_stalled(struct usbf_ep *ep) |
| { |
| u32 ctrl; |
| |
| if (ep->id == 0) { |
| ctrl = usbf_ep_reg_readl(ep, USBF_REG_EP0_CONTROL); |
| return (ctrl & USBF_EP0_STL) ? true : false; |
| } |
| |
| ctrl = usbf_ep_reg_readl(ep, USBF_REG_EPN_CONTROL); |
| if (ep->is_in) |
| return (ctrl & USBF_EPN_ISTL) ? true : false; |
| |
| return (ctrl & USBF_EPN_OSTL) ? true : false; |
| } |
| |
| static int usbf_epn_start_queue(struct usbf_ep *epn) |
| { |
| struct usbf_req *req; |
| int ret; |
| |
| if (usbf_ep_is_stalled(epn)) |
| return 0; |
| |
| req = list_first_entry_or_null(&epn->queue, struct usbf_req, queue); |
| |
| if (epn->is_in) { |
| if (req && !epn->is_processing) { |
| ret = epn->dma_regs ? |
| usbf_epn_dma_in(epn, req) : |
| usbf_epn_pio_in(epn, req); |
| if (ret != -EINPROGRESS) { |
| dev_err(epn->udc->dev, |
| "queued next request not in progress\n"); |
| /* The request cannot be completed (ie |
| * ret == 0) on the first call. |
| * stall and nuke the endpoint |
| */ |
| return ret ? ret : -EIO; |
| } |
| } |
| } else { |
| if (req) { |
| /* Clear ONAK to accept OUT tokens */ |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ONAK); |
| |
| /* Enable interrupts */ |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_INT | USBF_EPN_OUT_NULL_INT); |
| } else { |
| /* Disable incoming data and interrupt. |
| * They will be enable on next usb_eb_queue call |
| */ |
| usbf_ep_reg_bitset(epn, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ONAK); |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_INT | USBF_EPN_OUT_NULL_INT); |
| } |
| } |
| return 0; |
| } |
| |
| static int usbf_ep_process_queue(struct usbf_ep *ep) |
| { |
| int (*usbf_ep_xfer)(struct usbf_ep *ep, struct usbf_req *req); |
| struct usbf_req *req; |
| int is_processing; |
| int ret; |
| |
| if (ep->is_in) { |
| usbf_ep_xfer = usbf_ep0_pio_in; |
| if (ep->id) { |
| usbf_ep_xfer = ep->dma_regs ? |
| usbf_epn_dma_in : usbf_epn_pio_in; |
| } |
| } else { |
| usbf_ep_xfer = usbf_ep0_pio_out; |
| if (ep->id) { |
| usbf_ep_xfer = ep->dma_regs ? |
| usbf_epn_dma_out : usbf_epn_pio_out; |
| } |
| } |
| |
| req = list_first_entry_or_null(&ep->queue, struct usbf_req, queue); |
| if (!req) { |
| dev_err(ep->udc->dev, |
| "no request available for ep%u %s process\n", ep->id, |
| ep->is_in ? "in" : "out"); |
| return -ENOENT; |
| } |
| |
| do { |
| /* Were going to read the FIFO for this current request. |
| * NAK any other incoming data to avoid a race condition if no |
| * more request are available. |
| */ |
| if (!ep->is_in && ep->id != 0) { |
| usbf_ep_reg_bitset(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ONAK); |
| } |
| |
| ret = usbf_ep_xfer(ep, req); |
| if (ret == -EINPROGRESS) { |
| if (!ep->is_in && ep->id != 0) { |
| /* The current request needs more data. |
| * Allow incoming data |
| */ |
| usbf_ep_reg_bitclr(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ONAK); |
| } |
| return ret; |
| } |
| |
| is_processing = ep->is_processing; |
| ep->is_processing = 1; |
| usbf_ep_req_done(ep, req, ret); |
| ep->is_processing = is_processing; |
| |
| if (ret) { |
| /* An error was detected during the request transfer. |
| * Any pending DMA transfers were aborted by the |
| * usbf_ep_req_done() call. |
| * It's time to flush the fifo |
| */ |
| if (ep->id == 0) |
| usbf_ep0_fifo_flush(ep); |
| else |
| usbf_epn_fifo_flush(ep); |
| } |
| |
| req = list_first_entry_or_null(&ep->queue, struct usbf_req, |
| queue); |
| |
| if (ep->is_in) |
| continue; |
| |
| if (ep->id != 0) { |
| if (req) { |
| /* An other request is available. |
| * Allow incoming data |
| */ |
| usbf_ep_reg_bitclr(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ONAK); |
| } else { |
| /* No request queued. Disable interrupts. |
| * They will be enabled on usb_ep_queue |
| */ |
| usbf_ep_reg_bitclr(ep, USBF_REG_EPN_INT_ENA, |
| USBF_EPN_OUT_INT | USBF_EPN_OUT_NULL_INT); |
| } |
| } |
| /* Do not recall usbf_ep_xfer() */ |
| return req ? -EINPROGRESS : 0; |
| |
| } while (req); |
| |
| return 0; |
| } |
| |
| static void usbf_ep_stall(struct usbf_ep *ep, bool stall) |
| { |
| struct usbf_req *first; |
| |
| dev_dbg(ep->udc->dev, "ep%u %s %s\n", ep->id, |
| ep->is_in ? "in" : "out", |
| stall ? "stall" : "unstall"); |
| |
| if (ep->id == 0) { |
| if (stall) |
| usbf_ep_reg_bitset(ep, USBF_REG_EP0_CONTROL, USBF_EP0_STL); |
| else |
| usbf_ep_reg_bitclr(ep, USBF_REG_EP0_CONTROL, USBF_EP0_STL); |
| return; |
| } |
| |
| if (stall) { |
| if (ep->is_in) |
| usbf_ep_reg_bitset(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ISTL); |
| else |
| usbf_ep_reg_bitset(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_OSTL | USBF_EPN_OSTL_EN); |
| } else { |
| first = list_first_entry_or_null(&ep->queue, struct usbf_req, queue); |
| if (first && first->is_mapped) { |
| /* This can appear if the host halts an endpoint using |
| * SET_FEATURE and then un-halts the endpoint |
| */ |
| usbf_epn_dma_abort(ep, first); |
| } |
| usbf_epn_fifo_flush(ep); |
| if (ep->is_in) { |
| usbf_ep_reg_clrset(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_ISTL, |
| USBF_EPN_IPIDCLR); |
| } else { |
| usbf_ep_reg_clrset(ep, USBF_REG_EPN_CONTROL, |
| USBF_EPN_OSTL, |
| USBF_EPN_OSTL_EN | USBF_EPN_OPIDCLR); |
| } |
| usbf_epn_start_queue(ep); |
| } |
| } |
| |
| static void usbf_ep0_enable(struct usbf_ep *ep0) |
| { |
| usbf_ep_reg_writel(ep0, USBF_REG_EP0_CONTROL, USBF_EP0_INAK_EN | USBF_EP0_BCLR); |
| |
| usbf_ep_reg_writel(ep0, USBF_REG_EP0_INT_ENA, |
| USBF_EP0_SETUP_EN | USBF_EP0_STG_START_EN | USBF_EP0_STG_END_EN | |
| USBF_EP0_OUT_EN | USBF_EP0_OUT_NULL_EN | USBF_EP0_IN_EN); |
| |
| ep0->udc->ep0state = EP0_IDLE; |
| ep0->disabled = 0; |
| |
| /* enable interrupts for the ep0 */ |
| usbf_reg_bitset(ep0->udc, USBF_REG_USB_INT_ENA, USBF_USB_EPN_EN(0)); |
| } |
| |
| static int usbf_epn_enable(struct usbf_ep *epn) |
| { |
| u32 base_addr; |
| u32 ctrl; |
| |
| base_addr = usbf_ep_info[epn->id].base_addr; |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_PCKT_ADRS, |
| USBF_EPN_BASEAD(base_addr) | USBF_EPN_MPKT(epn->ep.maxpacket)); |
| |
| /* OUT transfer interrupt are enabled during usb_ep_queue */ |
| if (epn->is_in) { |
| /* Will be changed in DMA processing */ |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_INT_ENA, USBF_EPN_IN_EN); |
| } |
| |
| /* Clear, set endpoint direction, set IN/OUT STL, and enable |
| * Send NAK for Data out as request are not queued yet |
| */ |
| ctrl = USBF_EPN_EN | USBF_EPN_BCLR; |
| if (epn->is_in) |
| ctrl |= USBF_EPN_OSTL | USBF_EPN_OSTL_EN; |
| else |
| ctrl |= USBF_EPN_DIR0 | USBF_EPN_ISTL | USBF_EPN_OSTL_EN | USBF_EPN_ONAK; |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_CONTROL, ctrl); |
| |
| return 0; |
| } |
| |
| static int usbf_ep_enable(struct usb_ep *_ep, |
| const struct usb_endpoint_descriptor *desc) |
| { |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| struct usbf_udc *udc = ep->udc; |
| unsigned long flags; |
| int ret; |
| |
| if (ep->id == 0) |
| return -EINVAL; |
| |
| if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) |
| return -EINVAL; |
| |
| dev_dbg(ep->udc->dev, "ep%u %s mpkts %d\n", ep->id, |
| usb_endpoint_dir_in(desc) ? "in" : "out", |
| usb_endpoint_maxp(desc)); |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| ep->is_in = usb_endpoint_dir_in(desc); |
| ep->ep.maxpacket = usb_endpoint_maxp(desc); |
| |
| ret = usbf_epn_enable(ep); |
| if (ret) |
| goto end; |
| |
| ep->disabled = 0; |
| |
| /* enable interrupts for this endpoint */ |
| usbf_reg_bitset(udc, USBF_REG_USB_INT_ENA, USBF_USB_EPN_EN(ep->id)); |
| |
| /* enable DMA interrupt at bridge level if DMA is used */ |
| if (ep->dma_regs) { |
| ep->bridge_on_dma_end = NULL; |
| usbf_reg_bitset(udc, USBF_REG_AHBBINTEN, |
| USBF_SYS_DMA_ENDINTEN_EPN(ep->id)); |
| } |
| |
| ret = 0; |
| end: |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| return ret; |
| } |
| |
| static int usbf_epn_disable(struct usbf_ep *epn) |
| { |
| /* Disable interrupts */ |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_INT_ENA, 0); |
| |
| /* Disable endpoint */ |
| usbf_ep_reg_bitclr(epn, USBF_REG_EPN_CONTROL, USBF_EPN_EN); |
| |
| /* remove anything that was pending */ |
| usbf_ep_nuke(epn, -ESHUTDOWN); |
| |
| return 0; |
| } |
| |
| static int usbf_ep_disable(struct usb_ep *_ep) |
| { |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| struct usbf_udc *udc = ep->udc; |
| unsigned long flags; |
| int ret; |
| |
| if (ep->id == 0) |
| return -EINVAL; |
| |
| dev_dbg(ep->udc->dev, "ep%u %s mpkts %d\n", ep->id, |
| ep->is_in ? "in" : "out", ep->ep.maxpacket); |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| ep->disabled = 1; |
| /* Disable DMA interrupt */ |
| if (ep->dma_regs) { |
| usbf_reg_bitclr(udc, USBF_REG_AHBBINTEN, |
| USBF_SYS_DMA_ENDINTEN_EPN(ep->id)); |
| ep->bridge_on_dma_end = NULL; |
| } |
| /* disable interrupts for this endpoint */ |
| usbf_reg_bitclr(udc, USBF_REG_USB_INT_ENA, USBF_USB_EPN_EN(ep->id)); |
| /* and the endpoint itself */ |
| ret = usbf_epn_disable(ep); |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| |
| return ret; |
| } |
| |
| static int usbf_ep0_queue(struct usbf_ep *ep0, struct usbf_req *req, |
| gfp_t gfp_flags) |
| { |
| int ret; |
| |
| req->req.actual = 0; |
| req->req.status = -EINPROGRESS; |
| req->is_zero_sent = 0; |
| |
| list_add_tail(&req->queue, &ep0->queue); |
| |
| if (ep0->udc->ep0state == EP0_IN_STATUS_START_PHASE) |
| return 0; |
| |
| if (!ep0->is_in) |
| return 0; |
| |
| if (ep0->udc->ep0state == EP0_IN_STATUS_PHASE) { |
| if (req->req.length) { |
| dev_err(ep0->udc->dev, |
| "request lng %u for ep0 in status phase\n", |
| req->req.length); |
| return -EINVAL; |
| } |
| ep0->delayed_status = 0; |
| } |
| if (!ep0->is_processing) { |
| ret = usbf_ep0_pio_in(ep0, req); |
| if (ret != -EINPROGRESS) { |
| dev_err(ep0->udc->dev, |
| "queued request not in progress\n"); |
| /* The request cannot be completed (ie |
| * ret == 0) on the first call |
| */ |
| return ret ? ret : -EIO; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int usbf_epn_queue(struct usbf_ep *ep, struct usbf_req *req, |
| gfp_t gfp_flags) |
| { |
| int was_empty; |
| int ret; |
| |
| if (ep->disabled) { |
| dev_err(ep->udc->dev, "ep%u request queue while disable\n", |
| ep->id); |
| return -ESHUTDOWN; |
| } |
| |
| req->req.actual = 0; |
| req->req.status = -EINPROGRESS; |
| req->is_zero_sent = 0; |
| req->xfer_step = USBF_XFER_START; |
| |
| was_empty = list_empty(&ep->queue); |
| list_add_tail(&req->queue, &ep->queue); |
| if (was_empty) { |
| ret = usbf_epn_start_queue(ep); |
| if (ret) |
| return ret; |
| } |
| return 0; |
| } |
| |
| static int usbf_ep_queue(struct usb_ep *_ep, struct usb_request *_req, |
| gfp_t gfp_flags) |
| { |
| struct usbf_req *req = container_of(_req, struct usbf_req, req); |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| struct usbf_udc *udc = ep->udc; |
| unsigned long flags; |
| int ret; |
| |
| if (!_req || !_req->buf) |
| return -EINVAL; |
| |
| if (!udc || !udc->driver) |
| return -EINVAL; |
| |
| dev_dbg(ep->udc->dev, "ep%u %s req queue length %u, zero %u, short_not_ok %u\n", |
| ep->id, ep->is_in ? "in" : "out", |
| req->req.length, req->req.zero, req->req.short_not_ok); |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| if (ep->id == 0) |
| ret = usbf_ep0_queue(ep, req, gfp_flags); |
| else |
| ret = usbf_epn_queue(ep, req, gfp_flags); |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| return ret; |
| } |
| |
| static int usbf_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) |
| { |
| struct usbf_req *req = container_of(_req, struct usbf_req, req); |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| unsigned long flags; |
| int is_processing; |
| int first; |
| int ret; |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| |
| dev_dbg(ep->udc->dev, "ep%u %s req dequeue length %u/%u\n", |
| ep->id, ep->is_in ? "in" : "out", |
| req->req.actual, req->req.length); |
| |
| first = list_is_first(&req->queue, &ep->queue); |
| |
| /* Complete the request but avoid any operation that could be done |
| * if a new request is queued during the request completion |
| */ |
| is_processing = ep->is_processing; |
| ep->is_processing = 1; |
| usbf_ep_req_done(ep, req, -ECONNRESET); |
| ep->is_processing = is_processing; |
| |
| if (first) { |
| /* The first item in the list was dequeued. |
| * This item could already be submitted to the hardware. |
| * So, flush the fifo |
| */ |
| if (ep->id) |
| usbf_epn_fifo_flush(ep); |
| else |
| usbf_ep0_fifo_flush(ep); |
| } |
| |
| if (ep->id == 0) { |
| /* We dequeue a request on ep0. On this endpoint, we can have |
| * 1 request related to the data stage and/or 1 request |
| * related to the status stage. |
| * We dequeue one of them and so the USB control transaction |
| * is no more coherent. The simple way to be consistent after |
| * dequeuing is to stall and nuke the endpoint and wait the |
| * next SETUP packet. |
| */ |
| usbf_ep_stall(ep, true); |
| usbf_ep_nuke(ep, -ECONNRESET); |
| ep->udc->ep0state = EP0_IDLE; |
| goto end; |
| } |
| |
| if (!first) |
| goto end; |
| |
| ret = usbf_epn_start_queue(ep); |
| if (ret) { |
| usbf_ep_stall(ep, true); |
| usbf_ep_nuke(ep, -EIO); |
| } |
| end: |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| return 0; |
| } |
| |
| static struct usb_request *usbf_ep_alloc_request(struct usb_ep *_ep, |
| gfp_t gfp_flags) |
| { |
| struct usbf_req *req; |
| |
| if (!_ep) |
| return NULL; |
| |
| req = kzalloc(sizeof(*req), gfp_flags); |
| if (!req) |
| return NULL; |
| |
| INIT_LIST_HEAD(&req->queue); |
| |
| return &req->req; |
| } |
| |
| static void usbf_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) |
| { |
| struct usbf_req *req; |
| unsigned long flags; |
| struct usbf_ep *ep; |
| |
| if (!_ep || !_req) |
| return; |
| |
| req = container_of(_req, struct usbf_req, req); |
| ep = container_of(_ep, struct usbf_ep, ep); |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| list_del_init(&req->queue); |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| kfree(req); |
| } |
| |
| static int usbf_ep_set_halt(struct usb_ep *_ep, int halt) |
| { |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| unsigned long flags; |
| int ret; |
| |
| if (ep->id == 0) |
| return -EINVAL; |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| |
| if (!list_empty(&ep->queue)) { |
| ret = -EAGAIN; |
| goto end; |
| } |
| |
| usbf_ep_stall(ep, halt); |
| if (!halt) |
| ep->is_wedged = 0; |
| |
| ret = 0; |
| end: |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| |
| return ret; |
| } |
| |
| static int usbf_ep_set_wedge(struct usb_ep *_ep) |
| { |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| unsigned long flags; |
| int ret; |
| |
| if (ep->id == 0) |
| return -EINVAL; |
| |
| spin_lock_irqsave(&ep->udc->lock, flags); |
| if (!list_empty(&ep->queue)) { |
| ret = -EAGAIN; |
| goto end; |
| } |
| usbf_ep_stall(ep, 1); |
| ep->is_wedged = 1; |
| |
| ret = 0; |
| end: |
| spin_unlock_irqrestore(&ep->udc->lock, flags); |
| return ret; |
| } |
| |
| static struct usb_ep_ops usbf_ep_ops = { |
| .enable = usbf_ep_enable, |
| .disable = usbf_ep_disable, |
| .queue = usbf_ep_queue, |
| .dequeue = usbf_ep_dequeue, |
| .set_halt = usbf_ep_set_halt, |
| .set_wedge = usbf_ep_set_wedge, |
| .alloc_request = usbf_ep_alloc_request, |
| .free_request = usbf_ep_free_request, |
| }; |
| |
| static void usbf_ep0_req_complete(struct usb_ep *_ep, struct usb_request *_req) |
| { |
| } |
| |
| static void usbf_ep0_fill_req(struct usbf_ep *ep0, struct usbf_req *req, |
| void *buf, unsigned int length, |
| void (*complete)(struct usb_ep *_ep, |
| struct usb_request *_req)) |
| { |
| if (buf && length) |
| memcpy(ep0->udc->ep0_buf, buf, length); |
| |
| req->req.buf = ep0->udc->ep0_buf; |
| req->req.length = length; |
| req->req.dma = 0; |
| req->req.zero = true; |
| req->req.complete = complete ? complete : usbf_ep0_req_complete; |
| req->req.status = -EINPROGRESS; |
| req->req.context = NULL; |
| req->req.actual = 0; |
| } |
| |
| static struct usbf_ep *usbf_get_ep_by_addr(struct usbf_udc *udc, u8 address) |
| { |
| struct usbf_ep *ep; |
| unsigned int i; |
| |
| if ((address & USB_ENDPOINT_NUMBER_MASK) == 0) |
| return &udc->ep[0]; |
| |
| for (i = 1; i < ARRAY_SIZE(udc->ep); i++) { |
| ep = &udc->ep[i]; |
| |
| if (!ep->ep.desc) |
| continue; |
| |
| if (ep->ep.desc->bEndpointAddress == address) |
| return ep; |
| } |
| |
| return NULL; |
| } |
| |
| static int usbf_req_delegate(struct usbf_udc *udc, |
| const struct usb_ctrlrequest *ctrlrequest) |
| { |
| int ret; |
| |
| spin_unlock(&udc->lock); |
| ret = udc->driver->setup(&udc->gadget, ctrlrequest); |
| spin_lock(&udc->lock); |
| if (ret < 0) { |
| dev_dbg(udc->dev, "udc driver setup failed %d\n", ret); |
| return ret; |
| } |
| if (ret == USB_GADGET_DELAYED_STATUS) { |
| dev_dbg(udc->dev, "delayed status set\n"); |
| udc->ep[0].delayed_status = 1; |
| return 0; |
| } |
| return ret; |
| } |
| |
| static int usbf_req_get_status(struct usbf_udc *udc, |
| const struct usb_ctrlrequest *ctrlrequest) |
| { |
| struct usbf_ep *ep; |
| u16 status_data; |
| u16 wLength; |
| u16 wValue; |
| u16 wIndex; |
| |
| wValue = le16_to_cpu(ctrlrequest->wValue); |
| wLength = le16_to_cpu(ctrlrequest->wLength); |
| wIndex = le16_to_cpu(ctrlrequest->wIndex); |
| |
| switch (ctrlrequest->bRequestType) { |
| case USB_DIR_IN | USB_RECIP_DEVICE | USB_TYPE_STANDARD: |
| if ((wValue != 0) || (wIndex != 0) || (wLength != 2)) |
| goto delegate; |
| |
| status_data = 0; |
| if (udc->gadget.is_selfpowered) |
| status_data |= BIT(USB_DEVICE_SELF_POWERED); |
| |
| if (udc->is_remote_wakeup) |
| status_data |= BIT(USB_DEVICE_REMOTE_WAKEUP); |
| |
| break; |
| |
| case USB_DIR_IN | USB_RECIP_ENDPOINT | USB_TYPE_STANDARD: |
| if ((wValue != 0) || (wLength != 2)) |
| goto delegate; |
| |
| ep = usbf_get_ep_by_addr(udc, wIndex); |
| if (!ep) |
| return -EINVAL; |
| |
| status_data = 0; |
| if (usbf_ep_is_stalled(ep)) |
| status_data |= cpu_to_le16(1); |
| break; |
| |
| case USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_STANDARD: |
| if ((wValue != 0) || (wLength != 2)) |
| goto delegate; |
| status_data = 0; |
| break; |
| |
| default: |
| goto delegate; |
| } |
| |
| usbf_ep0_fill_req(&udc->ep[0], &udc->setup_reply, &status_data, |
| sizeof(status_data), NULL); |
| usbf_ep0_queue(&udc->ep[0], &udc->setup_reply, GFP_ATOMIC); |
| |
| return 0; |
| |
| delegate: |
| return usbf_req_delegate(udc, ctrlrequest); |
| } |
| |
| static int usbf_req_clear_set_feature(struct usbf_udc *udc, |
| const struct usb_ctrlrequest *ctrlrequest, |
| bool is_set) |
| { |
| struct usbf_ep *ep; |
| u16 wLength; |
| u16 wValue; |
| u16 wIndex; |
| |
| wValue = le16_to_cpu(ctrlrequest->wValue); |
| wLength = le16_to_cpu(ctrlrequest->wLength); |
| wIndex = le16_to_cpu(ctrlrequest->wIndex); |
| |
| switch (ctrlrequest->bRequestType) { |
| case USB_DIR_OUT | USB_RECIP_DEVICE: |
| if ((wIndex != 0) || (wLength != 0)) |
| goto delegate; |
| |
| if (wValue != cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) |
| goto delegate; |
| |
| udc->is_remote_wakeup = is_set; |
| break; |
| |
| case USB_DIR_OUT | USB_RECIP_ENDPOINT: |
| if (wLength != 0) |
| goto delegate; |
| |
| ep = usbf_get_ep_by_addr(udc, wIndex); |
| if (!ep) |
| return -EINVAL; |
| |
| if ((ep->id == 0) && is_set) { |
| /* Endpoint 0 cannot be halted (stalled) |
| * Returning an error code leads to a STALL on this ep0 |
| * but keep the automate in a consistent state. |
| */ |
| return -EINVAL; |
| } |
| if (ep->is_wedged && !is_set) { |
| /* Ignore CLEAR_FEATURE(HALT ENDPOINT) when the |
| * endpoint is wedged |
| */ |
| break; |
| } |
| usbf_ep_stall(ep, is_set); |
| break; |
| |
| default: |
| goto delegate; |
| } |
| |
| return 0; |
| |
| delegate: |
| return usbf_req_delegate(udc, ctrlrequest); |
| } |
| |
| static void usbf_ep0_req_set_address_complete(struct usb_ep *_ep, |
| struct usb_request *_req) |
| { |
| struct usbf_ep *ep = container_of(_ep, struct usbf_ep, ep); |
| |
| /* The status phase of the SET_ADDRESS request is completed ... */ |
| if (_req->status == 0) { |
| /* ... without any errors -> Signaled the state to the core. */ |
| usb_gadget_set_state(&ep->udc->gadget, USB_STATE_ADDRESS); |
| } |
| |
| /* In case of request failure, there is no need to revert the address |
| * value set to the hardware as the hardware will take care of the |
| * value only if the status stage is completed normally. |
| */ |
| } |
| |
| static int usbf_req_set_address(struct usbf_udc *udc, |
| const struct usb_ctrlrequest *ctrlrequest) |
| { |
| u16 wLength; |
| u16 wValue; |
| u16 wIndex; |
| u32 addr; |
| |
| wValue = le16_to_cpu(ctrlrequest->wValue); |
| wLength = le16_to_cpu(ctrlrequest->wLength); |
| wIndex = le16_to_cpu(ctrlrequest->wIndex); |
| |
| if (ctrlrequest->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) |
| goto delegate; |
| |
| if ((wIndex != 0) || (wLength != 0) || (wValue > 127)) |
| return -EINVAL; |
| |
| addr = wValue; |
| /* The hardware will take care of this USB address after the status |
| * stage of the SET_ADDRESS request is completed normally. |
| * It is safe to write it now |
| */ |
| usbf_reg_writel(udc, USBF_REG_USB_ADDRESS, USBF_USB_SET_USB_ADDR(addr)); |
| |
| /* Queued the status request */ |
| usbf_ep0_fill_req(&udc->ep[0], &udc->setup_reply, NULL, 0, |
| usbf_ep0_req_set_address_complete); |
| usbf_ep0_queue(&udc->ep[0], &udc->setup_reply, GFP_ATOMIC); |
| |
| return 0; |
| |
| delegate: |
| return usbf_req_delegate(udc, ctrlrequest); |
| } |
| |
| static int usbf_req_set_configuration(struct usbf_udc *udc, |
| const struct usb_ctrlrequest *ctrlrequest) |
| { |
| u16 wLength; |
| u16 wValue; |
| u16 wIndex; |
| int ret; |
| |
| ret = usbf_req_delegate(udc, ctrlrequest); |
| if (ret) |
| return ret; |
| |
| wValue = le16_to_cpu(ctrlrequest->wValue); |
| wLength = le16_to_cpu(ctrlrequest->wLength); |
| wIndex = le16_to_cpu(ctrlrequest->wIndex); |
| |
| if ((ctrlrequest->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) || |
| (wIndex != 0) || (wLength != 0)) { |
| /* No error detected by driver->setup() but it is not an USB2.0 |
| * Ch9 SET_CONFIGURATION. |
| * Nothing more to do |
| */ |
| return 0; |
| } |
| |
| if (wValue & 0x00FF) { |
| usbf_reg_bitset(udc, USBF_REG_USB_CONTROL, USBF_USB_CONF); |
| } else { |
| usbf_reg_bitclr(udc, USBF_REG_USB_CONTROL, USBF_USB_CONF); |
| /* Go back to Address State */ |
| spin_unlock(&udc->lock); |
| usb_gadget_set_state(&udc->gadget, USB_STATE_ADDRESS); |
| spin_lock(&udc->lock); |
| } |
| |
| return 0; |
| } |
| |
| static int usbf_handle_ep0_setup(struct usbf_ep *ep0) |
| { |
| union { |
| struct usb_ctrlrequest ctrlreq; |
| u32 raw[2]; |
| } crq; |
| struct usbf_udc *udc = ep0->udc; |
| int ret; |
| |
| /* Read setup data (ie the USB control request) */ |
| crq.raw[0] = usbf_reg_readl(udc, USBF_REG_SETUP_DATA0); |
| crq.raw[1] = usbf_reg_readl(udc, USBF_REG_SETUP_DATA1); |
| |
| dev_dbg(ep0->udc->dev, |
| "ep0 req%02x.%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\n", |
| crq.ctrlreq.bRequestType, crq.ctrlreq.bRequest, |
| crq.ctrlreq.wValue, crq.ctrlreq.wIndex, crq.ctrlreq.wLength); |
| |
| /* Set current EP0 state according to the received request */ |
| if (crq.ctrlreq.wLength) { |
| if (crq.ctrlreq.bRequestType & USB_DIR_IN) { |
| udc->ep0state = EP0_IN_DATA_PHASE; |
| usbf_ep_reg_clrset(ep0, USBF_REG_EP0_CONTROL, |
| USBF_EP0_INAK, |
| USBF_EP0_INAK_EN); |
| ep0->is_in = 1; |
| } else { |
| udc->ep0state = EP0_OUT_DATA_PHASE; |
| usbf_ep_reg_bitclr(ep0, USBF_REG_EP0_CONTROL, |
| USBF_EP0_ONAK); |
| ep0->is_in = 0; |
| } |
| } else { |
| udc->ep0state = EP0_IN_STATUS_START_PHASE; |
| ep0->is_in = 1; |
| } |
| |
| /* We starts a new control transfer -> Clear the delayed status flag */ |
| ep0->delayed_status = 0; |
| |
| if ((crq.ctrlreq.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { |
| /* This is not a USB standard request -> delelate */ |
| goto delegate; |
| } |
| |
| switch (crq.ctrlreq.bRequest) { |
| case USB_REQ_GET_STATUS: |
| ret = usbf_req_get_status(udc, &crq.ctrlreq); |
| break; |
| |
| case USB_REQ_CLEAR_FEATURE: |
| ret = usbf_req_clear_set_feature(udc, &crq.ctrlreq, false); |
| break; |
| |
| case USB_REQ_SET_FEATURE: |
| ret = usbf_req_clear_set_feature(udc, &crq.ctrlreq, true); |
| break; |
| |
| case USB_REQ_SET_ADDRESS: |
| ret = usbf_req_set_address(udc, &crq.ctrlreq); |
| break; |
| |
| case USB_REQ_SET_CONFIGURATION: |
| ret = usbf_req_set_configuration(udc, &crq.ctrlreq); |
| break; |
| |
| default: |
| goto delegate; |
| } |
| |
| return ret; |
| |
| delegate: |
| return usbf_req_delegate(udc, &crq.ctrlreq); |
| } |
| |
| static int usbf_handle_ep0_data_status(struct usbf_ep *ep0, |
| const char *ep0state_name, |
| enum usbf_ep0state next_ep0state) |
| { |
| struct usbf_udc *udc = ep0->udc; |
| int ret; |
| |
| ret = usbf_ep_process_queue(ep0); |
| switch (ret) { |
| case -ENOENT: |
| dev_err(udc->dev, |
| "no request available for ep0 %s phase\n", |
| ep0state_name); |
| break; |
| case -EINPROGRESS: |
| /* More data needs to be processed */ |
| ret = 0; |
| break; |
| case 0: |
| /* All requests in the queue are processed */ |
| udc->ep0state = next_ep0state; |
| break; |
| default: |
| dev_err(udc->dev, |
| "process queue failed for ep0 %s phase (%d)\n", |
| ep0state_name, ret); |
| break; |
| } |
| return ret; |
| } |
| |
| static int usbf_handle_ep0_out_status_start(struct usbf_ep *ep0) |
| { |
| struct usbf_udc *udc = ep0->udc; |
| struct usbf_req *req; |
| |
| usbf_ep_reg_clrset(ep0, USBF_REG_EP0_CONTROL, |
| USBF_EP0_ONAK, |
| USBF_EP0_PIDCLR); |
| ep0->is_in = 0; |
| |
| req = list_first_entry_or_null(&ep0->queue, struct usbf_req, queue); |
| if (!req) { |
| usbf_ep0_fill_req(ep0, &udc->setup_reply, NULL, 0, NULL); |
| usbf_ep0_queue(ep0, &udc->setup_reply, GFP_ATOMIC); |
| } else { |
| if (req->req.length) { |
| dev_err(udc->dev, |
| "queued request length %u for ep0 out status phase\n", |
| req->req.length); |
| } |
| } |
| udc->ep0state = EP0_OUT_STATUS_PHASE; |
| return 0; |
| } |
| |
| static int usbf_handle_ep0_in_status_start(struct usbf_ep *ep0) |
| { |
| struct usbf_udc *udc = ep0->udc; |
| struct usbf_req *req; |
| int ret; |
| |
| usbf_ep_reg_clrset(ep0, USBF_REG_EP0_CONTROL, |
| USBF_EP0_INAK, |
| USBF_EP0_INAK_EN | USBF_EP0_PIDCLR); |
| ep0->is_in = 1; |
| |
| /* Queue request for status if needed */ |
| req = list_first_entry_or_null(&ep0->queue, struct usbf_req, queue); |
| if (!req) { |
| if (ep0->delayed_status) { |
| dev_dbg(ep0->udc->dev, |
| "EP0_IN_STATUS_START_PHASE ep0->delayed_status set\n"); |
| udc->ep0state = EP0_IN_STATUS_PHASE; |
| return 0; |
| } |
| |
| usbf_ep0_fill_req(ep0, &udc->setup_reply, NULL, |
| 0, NULL); |
| usbf_ep0_queue(ep0, &udc->setup_reply, |
| GFP_ATOMIC); |
| |
| req = list_first_entry_or_null(&ep0->queue, struct usbf_req, queue); |
| } else { |
| if (req->req.length) { |
| dev_err(udc->dev, |
| "queued request length %u for ep0 in status phase\n", |
| req->req.length); |
| } |
| } |
| |
| ret = usbf_ep0_pio_in(ep0, req); |
| if (ret != -EINPROGRESS) { |
| usbf_ep_req_done(ep0, req, ret); |
| udc->ep0state = EP0_IN_STATUS_END_PHASE; |
| return 0; |
| } |
| |
| udc->ep0state = EP0_IN_STATUS_PHASE; |
| return 0; |
| } |
| |
| static void usbf_ep0_interrupt(struct usbf_ep *ep0) |
| { |
| struct usbf_udc *udc = ep0->udc; |
| u32 sts, prev_sts; |
| int prev_ep0state; |
| int ret; |
| |
| ep0->status = usbf_ep_reg_readl(ep0, USBF_REG_EP0_STATUS); |
| usbf_ep_reg_writel(ep0, USBF_REG_EP0_STATUS, ~ep0->status); |
| |
| dev_dbg(ep0->udc->dev, "ep0 status=0x%08x, enable=%08x\n, ctrl=0x%08x\n", |
| ep0->status, |
| usbf_ep_reg_readl(ep0, USBF_REG_EP0_INT_ENA), |
| usbf_ep_reg_readl(ep0, USBF_REG_EP0_CONTROL)); |
| |
| sts = ep0->status & (USBF_EP0_SETUP_INT | USBF_EP0_IN_INT | USBF_EP0_OUT_INT | |
| USBF_EP0_OUT_NULL_INT | USBF_EP0_STG_START_INT | |
| USBF_EP0_STG_END_INT); |
| |
| ret = 0; |
| do { |
| dev_dbg(ep0->udc->dev, "udc->ep0state=%d\n", udc->ep0state); |
| |
| prev_sts = sts; |
| prev_ep0state = udc->ep0state; |
| switch (udc->ep0state) { |
| case EP0_IDLE: |
| if (!(sts & USBF_EP0_SETUP_INT)) |
| break; |
| |
| sts &= ~USBF_EP0_SETUP_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle setup\n"); |
| ret = usbf_handle_ep0_setup(ep0); |
| break; |
| |
| case EP0_IN_DATA_PHASE: |
| if (!(sts & USBF_EP0_IN_INT)) |
| break; |
| |
| sts &= ~USBF_EP0_IN_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle in data phase\n"); |
| ret = usbf_handle_ep0_data_status(ep0, |
| "in data", EP0_OUT_STATUS_START_PHASE); |
| break; |
| |
| case EP0_OUT_STATUS_START_PHASE: |
| if (!(sts & USBF_EP0_STG_START_INT)) |
| break; |
| |
| sts &= ~USBF_EP0_STG_START_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle out status start phase\n"); |
| ret = usbf_handle_ep0_out_status_start(ep0); |
| break; |
| |
| case EP0_OUT_STATUS_PHASE: |
| if (!(sts & (USBF_EP0_OUT_INT | USBF_EP0_OUT_NULL_INT))) |
| break; |
| |
| sts &= ~(USBF_EP0_OUT_INT | USBF_EP0_OUT_NULL_INT); |
| dev_dbg(ep0->udc->dev, "ep0 handle out status phase\n"); |
| ret = usbf_handle_ep0_data_status(ep0, |
| "out status", |
| EP0_OUT_STATUS_END_PHASE); |
| break; |
| |
| case EP0_OUT_STATUS_END_PHASE: |
| if (!(sts & (USBF_EP0_STG_END_INT | USBF_EP0_SETUP_INT))) |
| break; |
| |
| sts &= ~USBF_EP0_STG_END_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle out status end phase\n"); |
| udc->ep0state = EP0_IDLE; |
| break; |
| |
| case EP0_OUT_DATA_PHASE: |
| if (!(sts & (USBF_EP0_OUT_INT | USBF_EP0_OUT_NULL_INT))) |
| break; |
| |
| sts &= ~(USBF_EP0_OUT_INT | USBF_EP0_OUT_NULL_INT); |
| dev_dbg(ep0->udc->dev, "ep0 handle out data phase\n"); |
| ret = usbf_handle_ep0_data_status(ep0, |
| "out data", EP0_IN_STATUS_START_PHASE); |
| break; |
| |
| case EP0_IN_STATUS_START_PHASE: |
| if (!(sts & USBF_EP0_STG_START_INT)) |
| break; |
| |
| sts &= ~USBF_EP0_STG_START_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle in status start phase\n"); |
| ret = usbf_handle_ep0_in_status_start(ep0); |
| break; |
| |
| case EP0_IN_STATUS_PHASE: |
| if (!(sts & USBF_EP0_IN_INT)) |
| break; |
| |
| sts &= ~USBF_EP0_IN_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle in status phase\n"); |
| ret = usbf_handle_ep0_data_status(ep0, |
| "in status", EP0_IN_STATUS_END_PHASE); |
| break; |
| |
| case EP0_IN_STATUS_END_PHASE: |
| if (!(sts & (USBF_EP0_STG_END_INT | USBF_EP0_SETUP_INT))) |
| break; |
| |
| sts &= ~USBF_EP0_STG_END_INT; |
| dev_dbg(ep0->udc->dev, "ep0 handle in status end\n"); |
| udc->ep0state = EP0_IDLE; |
| break; |
| |
| default: |
| udc->ep0state = EP0_IDLE; |
| break; |
| } |
| |
| if (ret) { |
| dev_dbg(ep0->udc->dev, "ep0 failed (%d)\n", ret); |
| /* Failure -> stall. |
| * This stall state will be automatically cleared when |
| * the IP receives the next SETUP packet |
| */ |
| usbf_ep_stall(ep0, true); |
| |
| /* Remove anything that was pending */ |
| usbf_ep_nuke(ep0, -EPROTO); |
| |
| udc->ep0state = EP0_IDLE; |
| break; |
| } |
| |
| } while ((prev_ep0state != udc->ep0state) || (prev_sts != sts)); |
| |
| dev_dbg(ep0->udc->dev, "ep0 done udc->ep0state=%d, status=0x%08x. next=0x%08x\n", |
| udc->ep0state, sts, |
| usbf_ep_reg_readl(ep0, USBF_REG_EP0_STATUS)); |
| } |
| |
| static void usbf_epn_process_queue(struct usbf_ep *epn) |
| { |
| int ret; |
| |
| ret = usbf_ep_process_queue(epn); |
| switch (ret) { |
| case -ENOENT: |
| dev_warn(epn->udc->dev, "ep%u %s, no request available\n", |
| epn->id, epn->is_in ? "in" : "out"); |
| break; |
| case -EINPROGRESS: |
| /* More data needs to be processed */ |
| ret = 0; |
| break; |
| case 0: |
| /* All requests in the queue are processed */ |
| break; |
| default: |
| dev_err(epn->udc->dev, "ep%u %s, process queue failed (%d)\n", |
| epn->id, epn->is_in ? "in" : "out", ret); |
| break; |
| } |
| |
| if (ret) { |
| dev_dbg(epn->udc->dev, "ep%u %s failed (%d)\n", epn->id, |
| epn->is_in ? "in" : "out", ret); |
| usbf_ep_stall(epn, true); |
| usbf_ep_nuke(epn, ret); |
| } |
| } |
| |
| static void usbf_epn_interrupt(struct usbf_ep *epn) |
| { |
| u32 sts; |
| u32 ena; |
| |
| epn->status = usbf_ep_reg_readl(epn, USBF_REG_EPN_STATUS); |
| ena = usbf_ep_reg_readl(epn, USBF_REG_EPN_INT_ENA); |
| usbf_ep_reg_writel(epn, USBF_REG_EPN_STATUS, ~(epn->status & ena)); |
| |
| dev_dbg(epn->udc->dev, "ep%u %s status=0x%08x, enable=%08x\n, ctrl=0x%08x\n", |
| epn->id, epn->is_in ? "in" : "out", epn->status, ena, |
| usbf_ep_reg_readl(epn, USBF_REG_EPN_CONTROL)); |
| |
| if (epn->disabled) { |
| dev_warn(epn->udc->dev, "ep%u %s, interrupt while disabled\n", |
| epn->id, epn->is_in ? "in" : "out"); |
| return; |
| } |
| |
| sts = epn->status & ena; |
| |
| if (sts & (USBF_EPN_IN_END_INT | USBF_EPN_IN_INT)) { |
| sts &= ~(USBF_EPN_IN_END_INT | USBF_EPN_IN_INT); |
| dev_dbg(epn->udc->dev, "ep%u %s process queue (in interrupts)\n", |
| epn->id, epn->is_in ? "in" : "out"); |
| usbf_epn_process_queue(epn); |
| } |
| |
| if (sts & (USBF_EPN_OUT_END_INT | USBF_EPN_OUT_INT | USBF_EPN_OUT_NULL_INT)) { |
| sts &= ~(USBF_EPN_OUT_END_INT | USBF_EPN_OUT_INT | USBF_EPN_OUT_NULL_INT); |
| dev_dbg(epn->udc->dev, "ep%u %s process queue (out interrupts)\n", |
| epn->id, epn->is_in ? "in" : "out"); |
| usbf_epn_process_queue(epn); |
| } |
| |
| dev_dbg(epn->udc->dev, "ep%u %s done status=0x%08x. next=0x%08x\n", |
| epn->id, epn->is_in ? "in" : "out", |
| sts, usbf_ep_reg_readl(epn, USBF_REG_EPN_STATUS)); |
| } |
| |
| static void usbf_ep_reset(struct usbf_ep *ep) |
| { |
| ep->status = 0; |
| /* Remove anything that was pending */ |
| usbf_ep_nuke(ep, -ESHUTDOWN); |
| } |
| |
| static void usbf_reset(struct usbf_udc *udc) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(udc->ep); i++) { |
| if (udc->ep[i].disabled) |
| continue; |
| |
| usbf_ep_reset(&udc->ep[i]); |
| } |
| |
| if (usbf_reg_readl(udc, USBF_REG_USB_STATUS) & USBF_USB_SPEED_MODE) |
| udc->gadget.speed = USB_SPEED_HIGH; |
| else |
| udc->gadget.speed = USB_SPEED_FULL; |
| |
| /* Remote wakeup feature must be disabled on USB bus reset */ |
| udc->is_remote_wakeup = false; |
| |
| /* Enable endpoint zero */ |
| usbf_ep0_enable(&udc->ep[0]); |
| |
| if (udc->driver) { |
| /* Signal the reset */ |
| spin_unlock(&udc->lock); |
| usb_gadget_udc_reset(&udc->gadget, udc->driver); |
| spin_lock(&udc->lock); |
| } |
| } |
| |
| static void usbf_driver_suspend(struct usbf_udc *udc) |
| { |
| if (udc->is_usb_suspended) { |
| dev_dbg(udc->dev, "already suspended\n"); |
| return; |
| } |
| |
| dev_dbg(udc->dev, "do usb suspend\n"); |
| udc->is_usb_suspended = true; |
| |
| if (udc->driver && udc->driver->suspend) { |
| spin_unlock(&udc->lock); |
| udc->driver->suspend(&udc->gadget); |
| spin_lock(&udc->lock); |
| |
| /* The datasheet tells to set the USB_CONTROL register SUSPEND |
| * bit when the USB bus suspend is detected. |
| * This bit stops the clocks (clocks for EPC, SIE, USBPHY) but |
| * these clocks seems not used only by the USB device. Some |
| * UARTs can be lost ... |
| * So, do not set the USB_CONTROL register SUSPEND bit. |
| */ |
| } |
| } |
| |
| static void usbf_driver_resume(struct usbf_udc *udc) |
| { |
| if (!udc->is_usb_suspended) |
| return; |
| |
| dev_dbg(udc->dev, "do usb resume\n"); |
| udc->is_usb_suspended = false; |
| |
| if (udc->driver && udc->driver->resume) { |
| spin_unlock(&udc->lock); |
| udc->driver->resume(&udc->gadget); |
| spin_lock(&udc->lock); |
| } |
| } |
| |
| static irqreturn_t usbf_epc_irq(int irq, void *_udc) |
| { |
| struct usbf_udc *udc = (struct usbf_udc *)_udc; |
| unsigned long flags; |
| struct usbf_ep *ep; |
| u32 int_sts; |
| u32 int_en; |
| int i; |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| |
| int_en = usbf_reg_readl(udc, USBF_REG_USB_INT_ENA); |
| int_sts = usbf_reg_readl(udc, USBF_REG_USB_INT_STA) & int_en; |
| usbf_reg_writel(udc, USBF_REG_USB_INT_STA, ~int_sts); |
| |
| dev_dbg(udc->dev, "int_sts=0x%08x\n", int_sts); |
| |
| if (int_sts & USBF_USB_RSUM_INT) { |
| dev_dbg(udc->dev, "handle resume\n"); |
| usbf_driver_resume(udc); |
| } |
| |
| if (int_sts & USBF_USB_USB_RST_INT) { |
| dev_dbg(udc->dev, "handle bus reset\n"); |
| usbf_driver_resume(udc); |
| usbf_reset(udc); |
| } |
| |
| if (int_sts & USBF_USB_SPEED_MODE_INT) { |
| if (usbf_reg_readl(udc, USBF_REG_USB_STATUS) & USBF_USB_SPEED_MODE) |
| udc->gadget.speed = USB_SPEED_HIGH; |
| else |
| udc->gadget.speed = USB_SPEED_FULL; |
| dev_dbg(udc->dev, "handle speed change (%s)\n", |
| udc->gadget.speed == USB_SPEED_HIGH ? "High" : "Full"); |
| } |
| |
| if (int_sts & USBF_USB_EPN_INT(0)) { |
| usbf_driver_resume(udc); |
| usbf_ep0_interrupt(&udc->ep[0]); |
| } |
| |
| for (i = 1; i < ARRAY_SIZE(udc->ep); i++) { |
| ep = &udc->ep[i]; |
| |
| if (int_sts & USBF_USB_EPN_INT(i)) { |
| usbf_driver_resume(udc); |
| usbf_epn_interrupt(ep); |
| } |
| } |
| |
| if (int_sts & USBF_USB_SPND_INT) { |
| dev_dbg(udc->dev, "handle suspend\n"); |
| usbf_driver_suspend(udc); |
| } |
| |
| spin_unlock_irqrestore(&udc->lock, flags); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t usbf_ahb_epc_irq(int irq, void *_udc) |
| { |
| struct usbf_udc *udc = (struct usbf_udc *)_udc; |
| unsigned long flags; |
| struct usbf_ep *epn; |
| u32 sysbint; |
| void (*ep_action)(struct usbf_ep *epn); |
| int i; |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| |
| /* Read and ack interrupts */ |
| sysbint = usbf_reg_readl(udc, USBF_REG_AHBBINT); |
| usbf_reg_writel(udc, USBF_REG_AHBBINT, sysbint); |
| |
| if ((sysbint & USBF_SYS_VBUS_INT) == USBF_SYS_VBUS_INT) { |
| if (usbf_reg_readl(udc, USBF_REG_EPCTR) & USBF_SYS_VBUS_LEVEL) { |
| dev_dbg(udc->dev, "handle vbus (1)\n"); |
| spin_unlock(&udc->lock); |
| usb_udc_vbus_handler(&udc->gadget, true); |
| usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED); |
| spin_lock(&udc->lock); |
| } else { |
| dev_dbg(udc->dev, "handle vbus (0)\n"); |
| udc->is_usb_suspended = false; |
| spin_unlock(&udc->lock); |
| usb_udc_vbus_handler(&udc->gadget, false); |
| usb_gadget_set_state(&udc->gadget, |
| USB_STATE_NOTATTACHED); |
| spin_lock(&udc->lock); |
| } |
| } |
| |
| for (i = 1; i < ARRAY_SIZE(udc->ep); i++) { |
| if (sysbint & USBF_SYS_DMA_ENDINT_EPN(i)) { |
| epn = &udc->ep[i]; |
| dev_dbg(epn->udc->dev, |
| "ep%u handle DMA complete. action=%ps\n", |
| epn->id, epn->bridge_on_dma_end); |
| ep_action = epn->bridge_on_dma_end; |
| if (ep_action) { |
| epn->bridge_on_dma_end = NULL; |
| ep_action(epn); |
| } |
| } |
| } |
| |
| spin_unlock_irqrestore(&udc->lock, flags); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int usbf_udc_start(struct usb_gadget *gadget, |
| struct usb_gadget_driver *driver) |
| { |
| struct usbf_udc *udc = container_of(gadget, struct usbf_udc, gadget); |
| unsigned long flags; |
| |
| dev_info(udc->dev, "start (driver '%s')\n", driver->driver.name); |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| |
| /* hook up the driver */ |
| udc->driver = driver; |
| |
| /* Enable VBUS interrupt */ |
| usbf_reg_writel(udc, USBF_REG_AHBBINTEN, USBF_SYS_VBUS_INTEN); |
| |
| spin_unlock_irqrestore(&udc->lock, flags); |
| |
| return 0; |
| } |
| |
| static int usbf_udc_stop(struct usb_gadget *gadget) |
| { |
| struct usbf_udc *udc = container_of(gadget, struct usbf_udc, gadget); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| |
| /* Disable VBUS interrupt */ |
| usbf_reg_writel(udc, USBF_REG_AHBBINTEN, 0); |
| |
| udc->driver = NULL; |
| |
| spin_unlock_irqrestore(&udc->lock, flags); |
| |
| dev_info(udc->dev, "stopped\n"); |
| |
| return 0; |
| } |
| |
| static int usbf_get_frame(struct usb_gadget *gadget) |
| { |
| struct usbf_udc *udc = container_of(gadget, struct usbf_udc, gadget); |
| |
| return USBF_USB_GET_FRAME(usbf_reg_readl(udc, USBF_REG_USB_ADDRESS)); |
| } |
| |
| static void usbf_attach(struct usbf_udc *udc) |
| { |
| /* Enable USB signal to Function PHY |
| * D+ signal Pull-up |
| * Disable endpoint 0, it will be automatically enable when a USB reset |
| * is received. |
| * Disable the other endpoints |
| */ |
| usbf_reg_clrset(udc, USBF_REG_USB_CONTROL, |
| USBF_USB_CONNECTB | USBF_USB_DEFAULT | USBF_USB_CONF, |
| USBF_USB_PUE2); |
| |
| /* Enable reset and mode change interrupts */ |
| usbf_reg_bitset(udc, USBF_REG_USB_INT_ENA, |
| USBF_USB_USB_RST_EN | USBF_USB_SPEED_MODE_EN | USBF_USB_RSUM_EN | USBF_USB_SPND_EN); |
| } |
| |
| static void usbf_detach(struct usbf_udc *udc) |
| { |
| int i; |
| |
| /* Disable interrupts */ |
| usbf_reg_writel(udc, USBF_REG_USB_INT_ENA, 0); |
| |
| for (i = 0; i < ARRAY_SIZE(udc->ep); i++) { |
| if (udc->ep[i].disabled) |
| continue; |
| |
| usbf_ep_reset(&udc->ep[i]); |
| } |
| |
| /* Disable USB signal to Function PHY |
| * Do not Pull-up D+ signal |
| * Disable endpoint 0 |
| * Disable the other endpoints |
| */ |
| usbf_reg_clrset(udc, USBF_REG_USB_CONTROL, |
| USBF_USB_PUE2 | USBF_USB_DEFAULT | USBF_USB_CONF, |
| USBF_USB_CONNECTB); |
| } |
| |
| static int usbf_pullup(struct usb_gadget *gadget, int is_on) |
| { |
| struct usbf_udc *udc = container_of(gadget, struct usbf_udc, gadget); |
| unsigned long flags; |
| |
| dev_dbg(udc->dev, "pullup %d\n", is_on); |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| if (is_on) |
| usbf_attach(udc); |
| else |
| usbf_detach(udc); |
| spin_unlock_irqrestore(&udc->lock, flags); |
| |
| return 0; |
| } |
| |
| static int usbf_udc_set_selfpowered(struct usb_gadget *gadget, |
| int is_selfpowered) |
| { |
| struct usbf_udc *udc = container_of(gadget, struct usbf_udc, gadget); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| gadget->is_selfpowered = (is_selfpowered != 0); |
| spin_unlock_irqrestore(&udc->lock, flags); |
| |
| return 0; |
| } |
| |
| static int usbf_udc_wakeup(struct usb_gadget *gadget) |
| { |
| struct usbf_udc *udc = container_of(gadget, struct usbf_udc, gadget); |
| unsigned long flags; |
| int ret; |
| |
| spin_lock_irqsave(&udc->lock, flags); |
| |
| if (!udc->is_remote_wakeup) { |
| dev_dbg(udc->dev, "remote wakeup not allowed\n"); |
| ret = -EINVAL; |
| goto end; |
| } |
| |
| dev_dbg(udc->dev, "do wakeup\n"); |
| |
| /* Send the resume signal */ |
| usbf_reg_bitset(udc, USBF_REG_USB_CONTROL, USBF_USB_RSUM_IN); |
| usbf_reg_bitclr(udc, USBF_REG_USB_CONTROL, USBF_USB_RSUM_IN); |
| |
| ret = 0; |
| end: |
| spin_unlock_irqrestore(&udc->lock, flags); |
| return ret; |
| } |
| |
| static struct usb_gadget_ops usbf_gadget_ops = { |
| .get_frame = usbf_get_frame, |
| .pullup = usbf_pullup, |
| .udc_start = usbf_udc_start, |
| .udc_stop = usbf_udc_stop, |
| .set_selfpowered = usbf_udc_set_selfpowered, |
| .wakeup = usbf_udc_wakeup, |
| }; |
| |
| static int usbf_epn_check(struct usbf_ep *epn) |
| { |
| const char *type_txt; |
| const char *buf_txt; |
| int ret = 0; |
| u32 ctrl; |
| |
| ctrl = usbf_ep_reg_readl(epn, USBF_REG_EPN_CONTROL); |
| |
| switch (ctrl & USBF_EPN_MODE_MASK) { |
| case USBF_EPN_MODE_BULK: |
| type_txt = "bulk"; |
| if (epn->ep.caps.type_control || epn->ep.caps.type_iso || |
| !epn->ep.caps.type_bulk || epn->ep.caps.type_int) { |
| dev_err(epn->udc->dev, |
| "ep%u caps mismatch, bulk expected\n", epn->id); |
| ret = -EINVAL; |
| } |
| break; |
| case USBF_EPN_MODE_INTR: |
| type_txt = "intr"; |
| if (epn->ep.caps.type_control || epn->ep.caps.type_iso || |
| epn->ep.caps.type_bulk || !epn->ep.caps.type_int) { |
| dev_err(epn->udc->dev, |
| "ep%u caps mismatch, int expected\n", epn->id); |
| ret = -EINVAL; |
| } |
| break; |
| case USBF_EPN_MODE_ISO: |
| type_txt = "iso"; |
| if (epn->ep.caps.type_control || !epn->ep.caps.type_iso || |
| epn->ep.caps.type_bulk || epn->ep.caps.type_int) { |
| dev_err(epn->udc->dev, |
| "ep%u caps mismatch, iso expected\n", epn->id); |
| ret = -EINVAL; |
| } |
| break; |
| default: |
| type_txt = "unknown"; |
| dev_err(epn->udc->dev, "ep%u unknown type\n", epn->id); |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ctrl & USBF_EPN_BUF_TYPE_DOUBLE) { |
| buf_txt = "double"; |
| if (!usbf_ep_info[epn->id].is_double) { |
| dev_err(epn->udc->dev, |
| "ep%u buffer mismatch, double expected\n", |
| epn->id); |
| ret = -EINVAL; |
| } |
| } else { |
| buf_txt = "single"; |
| if (usbf_ep_info[epn->id].is_double) { |
| dev_err(epn->udc->dev, |
| "ep%u buffer mismatch, single expected\n", |
| epn->id); |
| ret = -EINVAL; |
| } |
| } |
| |
| dev_dbg(epn->udc->dev, "ep%u (%s) %s, %s buffer %u, checked %s\n", |
| epn->id, epn->ep.name, type_txt, buf_txt, |
| epn->ep.maxpacket_limit, ret ? "failed" : "ok"); |
| |
| return ret; |
| } |
| |
| static int usbf_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct usbf_udc *udc; |
| struct usbf_ep *ep; |
| unsigned int i; |
| int irq; |
| int ret; |
| |
| udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); |
| if (!udc) |
| return -ENOMEM; |
| platform_set_drvdata(pdev, udc); |
| |
| udc->dev = dev; |
| spin_lock_init(&udc->lock); |
| |
| udc->regs = devm_platform_ioremap_resource(pdev, 0); |
| if (IS_ERR(udc->regs)) |
| return PTR_ERR(udc->regs); |
| |
| devm_pm_runtime_enable(&pdev->dev); |
| ret = pm_runtime_resume_and_get(&pdev->dev); |
| if (ret < 0) |
| return ret; |
| |
| dev_info(dev, "USBF version: %08x\n", |
| usbf_reg_readl(udc, USBF_REG_USBSSVER)); |
| |
| /* Resetting the PLL is handled via the clock driver as it has common |
| * registers with USB Host |
| */ |
| usbf_reg_bitclr(udc, USBF_REG_EPCTR, USBF_SYS_EPC_RST); |
| |
| /* modify in register gadget process */ |
| udc->gadget.speed = USB_SPEED_FULL; |
| udc->gadget.max_speed = USB_SPEED_HIGH; |
| udc->gadget.ops = &usbf_gadget_ops; |
| |
| udc->gadget.name = dev->driver->name; |
| udc->gadget.dev.parent = dev; |
| udc->gadget.ep0 = &udc->ep[0].ep; |
| |
| /* The hardware DMA controller needs dma addresses aligned on 32bit. |
| * A fallback to pio is done if DMA addresses are not aligned. |
| */ |
| udc->gadget.quirk_avoids_skb_reserve = 1; |
| |
| INIT_LIST_HEAD(&udc->gadget.ep_list); |
| /* we have a canned request structure to allow sending packets as reply |
| * to get_status requests |
| */ |
| INIT_LIST_HEAD(&udc->setup_reply.queue); |
| |
| for (i = 0; i < ARRAY_SIZE(udc->ep); i++) { |
| ep = &udc->ep[i]; |
| |
| if (!(usbf_reg_readl(udc, USBF_REG_USBSSCONF) & |
| USBF_SYS_EP_AVAILABLE(i))) { |
| continue; |
| } |
| |
| INIT_LIST_HEAD(&ep->queue); |
| |
| ep->id = i; |
| ep->disabled = 1; |
| ep->udc = udc; |
| ep->ep.ops = &usbf_ep_ops; |
| ep->ep.name = usbf_ep_info[i].name; |
| ep->ep.caps = usbf_ep_info[i].caps; |
| usb_ep_set_maxpacket_limit(&ep->ep, |
| usbf_ep_info[i].maxpacket_limit); |
| |
| if (ep->id == 0) { |
| ep->regs = ep->udc->regs + USBF_BASE_EP0; |
| } else { |
| ep->regs = ep->udc->regs + USBF_BASE_EPN(ep->id - 1); |
| ret = usbf_epn_check(ep); |
| if (ret) |
| return ret; |
| if (usbf_reg_readl(udc, USBF_REG_USBSSCONF) & |
| USBF_SYS_DMA_AVAILABLE(i)) { |
| ep->dma_regs = ep->udc->regs + |
| USBF_BASE_DMA_EPN(ep->id - 1); |
| } |
| list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); |
| } |
| } |
| |
| irq = platform_get_irq(pdev, 0); |
| if (irq < 0) |
| return irq; |
| ret = devm_request_irq(dev, irq, usbf_epc_irq, 0, "usbf-epc", udc); |
| if (ret) { |
| dev_err(dev, "cannot request irq %d err %d\n", irq, ret); |
| return ret; |
| } |
| |
| irq = platform_get_irq(pdev, 1); |
| if (irq < 0) |
| return irq; |
| ret = devm_request_irq(dev, irq, usbf_ahb_epc_irq, 0, "usbf-ahb-epc", udc); |
| if (ret) { |
| dev_err(dev, "cannot request irq %d err %d\n", irq, ret); |
| return ret; |
| } |
| |
| usbf_reg_bitset(udc, USBF_REG_AHBMCTR, USBF_SYS_WBURST_TYPE); |
| |
| usbf_reg_bitset(udc, USBF_REG_USB_CONTROL, |
| USBF_USB_INT_SEL | USBF_USB_SOF_RCV | USBF_USB_SOF_CLK_MODE); |
| |
| ret = usb_add_gadget_udc(dev, &udc->gadget); |
| if (ret) |
| return ret; |
| |
| return 0; |
| } |
| |
| static void usbf_remove(struct platform_device *pdev) |
| { |
| struct usbf_udc *udc = platform_get_drvdata(pdev); |
| |
| usb_del_gadget_udc(&udc->gadget); |
| |
| pm_runtime_put(&pdev->dev); |
| } |
| |
| static const struct of_device_id usbf_match[] = { |
| { .compatible = "renesas,rzn1-usbf" }, |
| {} /* sentinel */ |
| }; |
| MODULE_DEVICE_TABLE(of, usbf_match); |
| |
| static struct platform_driver udc_driver = { |
| .driver = { |
| .name = "usbf_renesas", |
| .of_match_table = usbf_match, |
| }, |
| .probe = usbf_probe, |
| .remove_new = usbf_remove, |
| }; |
| |
| module_platform_driver(udc_driver); |
| |
| MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); |
| MODULE_DESCRIPTION("Renesas R-Car Gen3 & RZ/N1 USB Function driver"); |
| MODULE_LICENSE("GPL"); |