From 57cd166afc4b430d7b98dca71911bfef478d236b Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 24 Jul 2019 21:18:38 +0800 Subject: [PATCH 001/159] USB: core: hcd-pci: Use dev_get_drvdata where possible Instead of using to_pci_dev + pci_get_drvdata, use dev_get_drvdata to make code simpler. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20190724131838.1931-1-hslester96@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 03432467b05f..03bee698d7eb 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -407,8 +407,7 @@ static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable) static int check_root_hub_suspended(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); if (HCD_RH_RUNNING(hcd)) { dev_warn(dev, "Root hub is not suspended\n"); From 07e803ec9f6be4b216b89f8530ea30d2a397696d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 22:16:39 +0300 Subject: [PATCH 002/159] usb: dwc2: Switch to use device_property_count_u32() Use use device_property_count_u32() directly, that makes code neater. Signed-off-by: Andy Shevchenko Acked-by: Minas Harutyunyan Link: https://lore.kernel.org/r/20190723191639.67883-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/params.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 55f841a54015..31e090ac9f1e 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -404,10 +404,7 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg) device_property_read_u32(hsotg->dev, "g-np-tx-fifo-size", &p->g_np_tx_fifo_size); - num = device_property_read_u32_array(hsotg->dev, - "g-tx-fifo-size", - NULL, 0); - + num = device_property_count_u32(hsotg->dev, "g-tx-fifo-size"); if (num > 0) { num = min(num, 15); memset(p->g_tx_fifo_size, 0, From 41b57327b270935de8023f5473520dd6a81bd013 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 22:17:04 +0300 Subject: [PATCH 003/159] usb: dwc3: Switch to use device_property_count_u32() Use use device_property_count_u32() directly, that makes code neater. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190723191704.67945-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c9bb93a2c81e..98bce85c29d0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -813,8 +813,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) * result = 1, means INCRx burst mode supported. * result > 1, means undefined length burst mode supported. */ - ntype = device_property_read_u32_array(dev, - "snps,incr-burst-type-adjustment", NULL, 0); + ntype = device_property_count_u32(dev, "snps,incr-burst-type-adjustment"); if (ntype <= 0) return; From bc7514925cf50bf81f514d0f2d7521b25973a639 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 22:37:50 +0300 Subject: [PATCH 004/159] usb: typec: tcpm: Switch to use fwnode_property_count_uXX() Use use fwnode_property_count_uXX() directly, that makes code neater. Signed-off-by: Andy Shevchenko Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20190723193750.69038-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index fba32d84e578..ec525811a9eb 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4410,8 +4410,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, goto sink; /* Get source pdos */ - ret = fwnode_property_read_u32_array(fwnode, "source-pdos", - NULL, 0); + ret = fwnode_property_count_u32(fwnode, "source-pdos"); if (ret <= 0) return -EINVAL; @@ -4435,8 +4434,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, return -EINVAL; sink: /* Get sink pdos */ - ret = fwnode_property_read_u32_array(fwnode, "sink-pdos", - NULL, 0); + ret = fwnode_property_count_u32(fwnode, "sink-pdos"); if (ret <= 0) return -EINVAL; From 6ce9ba07b1b78ca3412eac808cd59eea09c80468 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 22:47:26 +0300 Subject: [PATCH 005/159] usb: typec: mux: Switch to use fwnode_property_count_uXX() Use fwnode_property_count_uXX() directly, that makes code neater. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190723194726.69545-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 61b7bc58dd81..57907f26f681 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -215,7 +215,7 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data) } /* Alternate Mode muxes */ - nval = fwnode_property_read_u16_array(con->fwnode, "svid", NULL, 0); + nval = fwnode_property_count_u16(con->fwnode, "svid"); if (nval <= 0) return NULL; From 110f87a6a5f65e825852632b9557f463e65251d7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 21 Jul 2019 23:49:08 +0900 Subject: [PATCH 006/159] usb: host: oxu210hp-hcd: remove include/linux/oxu210hp.h struct oxu210hp_platform_data is defined, but not used at all. $ git grep oxu210hp_platform_data include/linux/oxu210hp.h:struct oxu210hp_platform_data { include/linux/oxu210hp.h exists just for defining an unused structure, so it can go away. Signed-off-by: Masahiro Yamada Link: https://lore.kernel.org/r/20190721144909.5295-1-yamada.masahiro@socionext.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/oxu210hp.h | 2 -- include/linux/oxu210hp.h | 8 -------- 2 files changed, 10 deletions(-) delete mode 100644 include/linux/oxu210hp.h diff --git a/drivers/usb/host/oxu210hp.h b/drivers/usb/host/oxu210hp.h index 437044147862..67ebea4993b6 100644 --- a/drivers/usb/host/oxu210hp.h +++ b/drivers/usb/host/oxu210hp.h @@ -444,5 +444,3 @@ enum ehci_timer_action { TIMER_ASYNC_SHRINK, TIMER_ASYNC_OFF, }; - -#include diff --git a/include/linux/oxu210hp.h b/include/linux/oxu210hp.h deleted file mode 100644 index 94cd25165c08..000000000000 --- a/include/linux/oxu210hp.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* platform data for the OXU210HP HCD */ - -struct oxu210hp_platform_data { - unsigned int bus16:1; - unsigned int use_hcd_otg:1; - unsigned int use_hcd_sph:1; -}; From 6443c671d5cbc60932b31f19f48091cddb2285eb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 21 Jul 2019 23:49:09 +0900 Subject: [PATCH 007/159] usb: host: oxu210hp-hcd: squash oxu210hp.h into oxu210hp-hcd.c The header, oxu210hp.h is only included from oxu210hp-hcd.c so squash it. When I moved the code, I also fixed the following warnings from scripts/checkpatch.pl: drivers/usb/host/oxu210hp-hcd.c:117: warning: __packed is preferred over __attribute__((packed)) drivers/usb/host/oxu210hp-hcd.c:196: warning: __packed is preferred over __attribute__((packed)) drivers/usb/host/oxu210hp-hcd.c:221: warning: __packed is preferred over __attribute__((packed)) drivers/usb/host/oxu210hp-hcd.c:266: warning: __aligned(size) is preferred over __attribute__((aligned(size))) drivers/usb/host/oxu210hp-hcd.c:336: warning: Prefer 'unsigned int' to bare use of 'unsigned' drivers/usb/host/oxu210hp-hcd.c:354: warning: __aligned(size) is preferred over __attribute__((aligned(size))) drivers/usb/host/oxu210hp-hcd.c:385: warning: __aligned(size) is preferred over __attribute__((aligned(size))) drivers/usb/host/oxu210hp-hcd.c:393: warning: __aligned(size) is preferred over __attribute__((aligned(size))) drivers/usb/host/oxu210hp-hcd.c:429: warning: Prefer 'unsigned int' to bare use of 'unsigned' drivers/usb/host/oxu210hp-hcd.c:432: warning: Prefer 'unsigned int' to bare use of 'unsigned' drivers/usb/host/oxu210hp-hcd.c:436: warning: Prefer 'unsigned int' to bare use of 'unsigned' drivers/usb/host/oxu210hp-hcd.c:451: warning: Prefer 'unsigned int' to bare use of 'unsigned' drivers/usb/host/oxu210hp-hcd.c:461: warning: Prefer 'unsigned int' to bare use of 'unsigned' drivers/usb/host/oxu210hp-hcd.c:467: warning: please, no space before tabs Signed-off-by: Masahiro Yamada Link: https://lore.kernel.org/r/20190721144909.5295-2-yamada.masahiro@socionext.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/oxu210hp-hcd.c | 443 ++++++++++++++++++++++++++++++- drivers/usb/host/oxu210hp.h | 446 -------------------------------- 2 files changed, 441 insertions(+), 448 deletions(-) delete mode 100644 drivers/usb/host/oxu210hp.h diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 47c5515a9ce4..09cc19df798e 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -31,10 +31,449 @@ #include #include -#include "oxu210hp.h" - #define DRIVER_VERSION "0.0.50" +#define OXU_DEVICEID 0x00 + #define OXU_REV_MASK 0xffff0000 + #define OXU_REV_SHIFT 16 + #define OXU_REV_2100 0x2100 + #define OXU_BO_SHIFT 8 + #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT) + #define OXU_MAJ_REV_SHIFT 4 + #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT) + #define OXU_MIN_REV_SHIFT 0 + #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT) +#define OXU_HOSTIFCONFIG 0x04 +#define OXU_SOFTRESET 0x08 + #define OXU_SRESET (1 << 0) + +#define OXU_PIOBURSTREADCTRL 0x0C + +#define OXU_CHIPIRQSTATUS 0x10 +#define OXU_CHIPIRQEN_SET 0x14 +#define OXU_CHIPIRQEN_CLR 0x18 + #define OXU_USBSPHLPWUI 0x00000080 + #define OXU_USBOTGLPWUI 0x00000040 + #define OXU_USBSPHI 0x00000002 + #define OXU_USBOTGI 0x00000001 + +#define OXU_CLKCTRL_SET 0x1C + #define OXU_SYSCLKEN 0x00000008 + #define OXU_USBSPHCLKEN 0x00000002 + #define OXU_USBOTGCLKEN 0x00000001 + +#define OXU_ASO 0x68 + #define OXU_SPHPOEN 0x00000100 + #define OXU_OVRCCURPUPDEN 0x00000800 + #define OXU_ASO_OP (1 << 10) + #define OXU_COMPARATOR 0x000004000 + +#define OXU_USBMODE 0x1A8 + #define OXU_VBPS 0x00000020 + #define OXU_ES_LITTLE 0x00000000 + #define OXU_CM_HOST_ONLY 0x00000003 + +/* + * Proper EHCI structs & defines + */ + +/* Magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +struct oxu_hcd; + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute[8]; /* nibbles for routing - offset 0xC */ +} __packed; + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved[9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status[0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __packed; + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __packed; + +#define QTD_NEXT(dma) cpu_to_le32((u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct ehci_qtd { + /* first part defined by EHCI spec */ + __le32 hw_next; /* see EHCI 3.5.1 */ + __le32 hw_alt_next; /* see EHCI 3.5.2 */ + __le32 hw_token; /* see EHCI 3.5.3 */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + __le32 hw_buf[5]; /* see EHCI 3.5.4 */ + __le32 hw_buf_hi[5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ + + u32 qtd_buffer_len; + void *buffer; + dma_addr_t buffer_dma; + void *transfer_buffer; + void *transfer_dma; +} __aligned(32); + +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK cpu_to_le32 (~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) + +/* Type tag from {qh, itd, sitd, fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_le32 (3 << 1)) + +/* values for that type tag */ +#define Q_TYPE_QH cpu_to_le32 (1 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END cpu_to_le32(1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union ehci_shadow { + struct ehci_qh *qh; /* Q_TYPE_QH */ + __le32 *hw_next; /* (all types) */ + void *ptr; +}; + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +struct ehci_qh { + /* first part defined by EHCI spec */ + __le32 hw_next; /* see EHCI 3.6.1 */ + __le32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_HEAD 0x00008000 + __le32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 + __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + __le32 hw_qtd_next; + __le32 hw_alt_next; + __le32 hw_token; + __le32 hw_buf[5]; + __le32 hw_buf_hi[5]; + + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + union ehci_shadow qh_next; /* ptr to qh; or periodic */ + struct list_head qtd_list; /* sw qtd list */ + struct ehci_qtd *dummy; + struct ehci_qh *reclaim; /* next to reclaim */ + + struct oxu_hcd *oxu; + struct kref kref; + unsigned int stamp; + + u8 qh_state; +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + u16 tt_usecs; /* tt downstream bandwidth */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + struct usb_device *dev; /* access to TT */ +} __aligned(32); + +/* + * Proper OXU210HP structs + */ + +#define OXU_OTG_CORE_OFFSET 0x00400 +#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100) +#define OXU_SPH_CORE_OFFSET 0x00800 +#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100) + +#define OXU_OTG_MEM 0xE000 +#define OXU_SPH_MEM 0x16000 + +/* Only how many elements & element structure are specifies here. */ +/* 2 host controllers are enabled - total size <= 28 kbytes */ +#define DEFAULT_I_TDPS 1024 +#define QHEAD_NUM 16 +#define QTD_NUM 32 +#define SITD_NUM 8 +#define MURB_NUM 8 + +#define BUFFER_NUM 8 +#define BUFFER_SIZE 512 + +struct oxu_info { + struct usb_hcd *hcd[2]; +}; + +struct oxu_buf { + u8 buffer[BUFFER_SIZE]; +} __aligned(BUFFER_SIZE); + +struct oxu_onchip_mem { + struct oxu_buf db_pool[BUFFER_NUM]; + + u32 frame_list[DEFAULT_I_TDPS]; + struct ehci_qh qh_pool[QHEAD_NUM]; + struct ehci_qtd qtd_pool[QTD_NUM]; +} __aligned(4 << 10); + +#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ + +struct oxu_murb { + struct urb urb; + struct urb *main; + u8 last; +}; + +struct oxu_hcd { /* one per controller */ + unsigned int is_otg:1; + + u8 qh_used[QHEAD_NUM]; + u8 qtd_used[QTD_NUM]; + u8 db_used[BUFFER_NUM]; + u8 murb_used[MURB_NUM]; + + struct oxu_onchip_mem __iomem *mem; + spinlock_t mem_lock; + + struct timer_list urb_timer; + + struct ehci_caps __iomem *caps; + struct ehci_regs __iomem *regs; + + u32 hcs_params; /* cached register copy */ + spinlock_t lock; + + /* async schedule support */ + struct ehci_qh *async; + struct ehci_qh *reclaim; + unsigned int reclaim_ready:1; + unsigned int scanning:1; + + /* periodic schedule support */ + unsigned int periodic_size; + __le32 *periodic; /* hw periodic table */ + dma_addr_t periodic_dma; + unsigned int i_thresh; /* uframes HC might cache */ + + union ehci_shadow *pshadow; /* mirror hw periodic table */ + int next_uframe; /* scan periodic, start here */ + unsigned int periodic_sched; /* periodic activity count */ + + /* per root hub port */ + unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; + /* bit vectors (one bit per port) */ + unsigned long bus_suspended; /* which ports were + * already suspended at the + * start of a bus suspend + */ + unsigned long companion_ports;/* which ports are dedicated + * to the companion controller + */ + + struct timer_list watchdog; + unsigned long actions; + unsigned int stamp; + unsigned long next_statechange; + u32 command; + + /* SILICON QUIRKS */ + struct list_head urb_list; /* this is the head to urb + * queue that didn't get enough + * resources + */ + struct oxu_murb *murb_pool; /* murb per split big urb */ + unsigned int urb_len; + + u8 sbrn; /* packed release number */ +}; + +#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ +#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ +#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ + +enum ehci_timer_action { + TIMER_IO_WATCHDOG, + TIMER_IAA_WATCHDOG, + TIMER_ASYNC_SHRINK, + TIMER_ASYNC_OFF, +}; + /* * Main defines */ diff --git a/drivers/usb/host/oxu210hp.h b/drivers/usb/host/oxu210hp.h deleted file mode 100644 index 67ebea4993b6..000000000000 --- a/drivers/usb/host/oxu210hp.h +++ /dev/null @@ -1,446 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Host interface registers - */ - -#define OXU_DEVICEID 0x00 - #define OXU_REV_MASK 0xffff0000 - #define OXU_REV_SHIFT 16 - #define OXU_REV_2100 0x2100 - #define OXU_BO_SHIFT 8 - #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT) - #define OXU_MAJ_REV_SHIFT 4 - #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT) - #define OXU_MIN_REV_SHIFT 0 - #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT) -#define OXU_HOSTIFCONFIG 0x04 -#define OXU_SOFTRESET 0x08 - #define OXU_SRESET (1 << 0) - -#define OXU_PIOBURSTREADCTRL 0x0C - -#define OXU_CHIPIRQSTATUS 0x10 -#define OXU_CHIPIRQEN_SET 0x14 -#define OXU_CHIPIRQEN_CLR 0x18 - #define OXU_USBSPHLPWUI 0x00000080 - #define OXU_USBOTGLPWUI 0x00000040 - #define OXU_USBSPHI 0x00000002 - #define OXU_USBOTGI 0x00000001 - -#define OXU_CLKCTRL_SET 0x1C - #define OXU_SYSCLKEN 0x00000008 - #define OXU_USBSPHCLKEN 0x00000002 - #define OXU_USBOTGCLKEN 0x00000001 - -#define OXU_ASO 0x68 - #define OXU_SPHPOEN 0x00000100 - #define OXU_OVRCCURPUPDEN 0x00000800 - #define OXU_ASO_OP (1 << 10) - #define OXU_COMPARATOR 0x000004000 - -#define OXU_USBMODE 0x1A8 - #define OXU_VBPS 0x00000020 - #define OXU_ES_LITTLE 0x00000000 - #define OXU_CM_HOST_ONLY 0x00000003 - -/* - * Proper EHCI structs & defines - */ - -/* Magic numbers that can affect system performance */ -#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define EHCI_TUNE_RL_TT 0 -#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define EHCI_TUNE_MULT_TT 1 -#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ - -struct oxu_hcd; - -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct ehci_caps { - /* these fields are specified as 8 and 16 bit registers, - * but some hosts can't perform 8 or 16 bit PCI accesses. - */ - u32 hc_capbase; -#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ -#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ - u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ -#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ -#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ -#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ -#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ -#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ -#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ -#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ -#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ -#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ - u8 portroute[8]; /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); - - -/* Section 2.3 Host Controller Operational Registers */ -struct ehci_regs { - /* USBCMD: offset 0x00 */ - u32 command; -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK (1<<11) /* enable "park" on async qh */ -#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ -#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -#define CMD_ASE (1<<5) /* async schedule enable */ -#define CMD_PSE (1<<4) /* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ - - /* USBSTS: offset 0x04 */ - u32 status; -#define STS_ASS (1<<15) /* Async Schedule Status */ -#define STS_PSS (1<<14) /* Periodic Schedule Status */ -#define STS_RECL (1<<13) /* Reclamation */ -#define STS_HALT (1<<12) /* Not running (any reason) */ -/* some bits reserved */ - /* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA (1<<5) /* Interrupted on async advance */ -#define STS_FATAL (1<<4) /* such as some PCI access errors */ -#define STS_FLR (1<<3) /* frame list rolled over */ -#define STS_PCD (1<<2) /* port change detect */ -#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -#define STS_INT (1<<0) /* "normal" completion (short, ...) */ - -#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) - - /* USBINTR: offset 0x08 */ - u32 intr_enable; - - /* FRINDEX: offset 0x0C */ - u32 frame_index; /* current microframe number */ - /* CTRLDSSEGMENT: offset 0x10 */ - u32 segment; /* address bits 63:32 if needed */ - /* PERIODICLISTBASE: offset 0x14 */ - u32 frame_list; /* points to periodic list */ - /* ASYNCLISTADDR: offset 0x18 */ - u32 async_next; /* address of next async queue head */ - - u32 reserved[9]; - - /* CONFIGFLAG: offset 0x40 */ - u32 configured_flag; -#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ - - /* PORTSC: offset 0x44 */ - u32 port_status[0]; /* up to N_PORTS */ -/* 31:23 reserved */ -#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ -#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ -#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ -/* 19:16 for port testing */ -#define PORT_LED_OFF (0<<14) -#define PORT_LED_AMBER (1<<14) -#define PORT_LED_GREEN (2<<14) -#define PORT_LED_MASK (3<<14) -#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ -#define PORT_POWER (1<<12) /* true: has power (see PPC) */ -#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ -/* 9 reserved */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_OCC (1<<5) /* over current change */ -#define PORT_OC (1<<4) /* over current active */ -#define PORT_PEC (1<<3) /* port enable change */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -} __attribute__ ((packed)); - -/* Appendix C, Debug port ... intended for use with special "debug devices" - * that can help if there's no serial console. (nonstandard enumeration.) - */ -struct ehci_dbg_port { - u32 control; -#define DBGP_OWNER (1<<30) -#define DBGP_ENABLED (1<<28) -#define DBGP_DONE (1<<16) -#define DBGP_INUSE (1<<10) -#define DBGP_ERRCODE(x) (((x)>>7)&0x07) -# define DBGP_ERR_BAD 1 -# define DBGP_ERR_SIGNAL 2 -#define DBGP_ERROR (1<<6) -#define DBGP_GO (1<<5) -#define DBGP_OUT (1<<4) -#define DBGP_LEN(x) (((x)>>0)&0x0f) - u32 pids; -#define DBGP_PID_GET(x) (((x)>>16)&0xff) -#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) - u32 data03; - u32 data47; - u32 address; -#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) -} __attribute__ ((packed)); - - -#define QTD_NEXT(dma) cpu_to_le32((u32)dma) - -/* - * EHCI Specification 0.95 Section 3.5 - * QTD: describe data transfer components (buffer, direction, ...) - * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". - * - * These are associated only with "QH" (Queue Head) structures, - * used with control, bulk, and interrupt transfers. - */ -struct ehci_qtd { - /* first part defined by EHCI spec */ - __le32 hw_next; /* see EHCI 3.5.1 */ - __le32 hw_alt_next; /* see EHCI 3.5.2 */ - __le32 hw_token; /* see EHCI 3.5.3 */ -#define QTD_TOGGLE (1 << 31) /* data toggle */ -#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) -#define QTD_IOC (1 << 15) /* interrupt on complete */ -#define QTD_CERR(tok) (((tok)>>10) & 0x3) -#define QTD_PID(tok) (((tok)>>8) & 0x3) -#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ -#define QTD_STS_HALT (1 << 6) /* halted on error */ -#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ -#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ -#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ -#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ -#define QTD_STS_STS (1 << 1) /* split transaction state */ -#define QTD_STS_PING (1 << 0) /* issue PING? */ - __le32 hw_buf[5]; /* see EHCI 3.5.4 */ - __le32 hw_buf_hi[5]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t qtd_dma; /* qtd address */ - struct list_head qtd_list; /* sw qtd list */ - struct urb *urb; /* qtd's urb */ - size_t length; /* length of buffer */ - - u32 qtd_buffer_len; - void *buffer; - dma_addr_t buffer_dma; - void *transfer_buffer; - void *transfer_dma; -} __attribute__ ((aligned(32))); - -/* mask NakCnt+T in qh->hw_alt_next */ -#define QTD_MASK cpu_to_le32 (~0x1f) - -#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) - -/* Type tag from {qh, itd, sitd, fstn}->hw_next */ -#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_le32 (3 << 1)) - -/* values for that type tag */ -#define Q_TYPE_QH cpu_to_le32 (1 << 1) - -/* next async queue entry, or pointer to interrupt/periodic QH */ -#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) - -/* for periodic/async schedules and qtd lists, mark end of list */ -#define EHCI_LIST_END cpu_to_le32(1) /* "null pointer" to hw */ - -/* - * Entries in periodic shadow table are pointers to one of four kinds - * of data structure. That's dictated by the hardware; a type tag is - * encoded in the low bits of the hardware's periodic schedule. Use - * Q_NEXT_TYPE to get the tag. - * - * For entries in the async schedule, the type tag always says "qh". - */ -union ehci_shadow { - struct ehci_qh *qh; /* Q_TYPE_QH */ - __le32 *hw_next; /* (all types) */ - void *ptr; -}; - -/* - * EHCI Specification 0.95 Section 3.6 - * QH: describes control/bulk/interrupt endpoints - * See Fig 3-7 "Queue Head Structure Layout". - * - * These appear in both the async and (for interrupt) periodic schedules. - */ - -struct ehci_qh { - /* first part defined by EHCI spec */ - __le32 hw_next; /* see EHCI 3.6.1 */ - __le32 hw_info1; /* see EHCI 3.6.2 */ -#define QH_HEAD 0x00008000 - __le32 hw_info2; /* see EHCI 3.6.2 */ -#define QH_SMASK 0x000000ff -#define QH_CMASK 0x0000ff00 -#define QH_HUBADDR 0x007f0000 -#define QH_HUBPORT 0x3f800000 -#define QH_MULT 0xc0000000 - __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ - - /* qtd overlay (hardware parts of a struct ehci_qtd) */ - __le32 hw_qtd_next; - __le32 hw_alt_next; - __le32 hw_token; - __le32 hw_buf[5]; - __le32 hw_buf_hi[5]; - - /* the rest is HCD-private */ - dma_addr_t qh_dma; /* address of qh */ - union ehci_shadow qh_next; /* ptr to qh; or periodic */ - struct list_head qtd_list; /* sw qtd list */ - struct ehci_qtd *dummy; - struct ehci_qh *reclaim; /* next to reclaim */ - - struct oxu_hcd *oxu; - struct kref kref; - unsigned stamp; - - u8 qh_state; -#define QH_STATE_LINKED 1 /* HC sees this */ -#define QH_STATE_UNLINK 2 /* HC may still see this */ -#define QH_STATE_IDLE 3 /* HC doesn't see this */ -#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ -#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ - - /* periodic schedule info */ - u8 usecs; /* intr bandwidth */ - u8 gap_uf; /* uframes split/csplit gap */ - u8 c_usecs; /* ... split completion bw */ - u16 tt_usecs; /* tt downstream bandwidth */ - unsigned short period; /* polling interval */ - unsigned short start; /* where polling starts */ -#define NO_FRAME ((unsigned short)~0) /* pick new start */ - struct usb_device *dev; /* access to TT */ -} __attribute__ ((aligned(32))); - -/* - * Proper OXU210HP structs - */ - -#define OXU_OTG_CORE_OFFSET 0x00400 -#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100) -#define OXU_SPH_CORE_OFFSET 0x00800 -#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100) - -#define OXU_OTG_MEM 0xE000 -#define OXU_SPH_MEM 0x16000 - -/* Only how many elements & element structure are specifies here. */ -/* 2 host controllers are enabled - total size <= 28 kbytes */ -#define DEFAULT_I_TDPS 1024 -#define QHEAD_NUM 16 -#define QTD_NUM 32 -#define SITD_NUM 8 -#define MURB_NUM 8 - -#define BUFFER_NUM 8 -#define BUFFER_SIZE 512 - -struct oxu_info { - struct usb_hcd *hcd[2]; -}; - -struct oxu_buf { - u8 buffer[BUFFER_SIZE]; -} __attribute__ ((aligned(BUFFER_SIZE))); - -struct oxu_onchip_mem { - struct oxu_buf db_pool[BUFFER_NUM]; - - u32 frame_list[DEFAULT_I_TDPS]; - struct ehci_qh qh_pool[QHEAD_NUM]; - struct ehci_qtd qtd_pool[QTD_NUM]; -} __attribute__ ((aligned(4 << 10))); - -#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ - -struct oxu_murb { - struct urb urb; - struct urb *main; - u8 last; -}; - -struct oxu_hcd { /* one per controller */ - unsigned int is_otg:1; - - u8 qh_used[QHEAD_NUM]; - u8 qtd_used[QTD_NUM]; - u8 db_used[BUFFER_NUM]; - u8 murb_used[MURB_NUM]; - - struct oxu_onchip_mem __iomem *mem; - spinlock_t mem_lock; - - struct timer_list urb_timer; - - struct ehci_caps __iomem *caps; - struct ehci_regs __iomem *regs; - - __u32 hcs_params; /* cached register copy */ - spinlock_t lock; - - /* async schedule support */ - struct ehci_qh *async; - struct ehci_qh *reclaim; - unsigned reclaim_ready:1; - unsigned scanning:1; - - /* periodic schedule support */ - unsigned periodic_size; - __le32 *periodic; /* hw periodic table */ - dma_addr_t periodic_dma; - unsigned i_thresh; /* uframes HC might cache */ - - union ehci_shadow *pshadow; /* mirror hw periodic table */ - int next_uframe; /* scan periodic, start here */ - unsigned periodic_sched; /* periodic activity count */ - - /* per root hub port */ - unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; - /* bit vectors (one bit per port) */ - unsigned long bus_suspended; /* which ports were - * already suspended at the - * start of a bus suspend - */ - unsigned long companion_ports;/* which ports are dedicated - * to the companion controller - */ - - struct timer_list watchdog; - unsigned long actions; - unsigned stamp; - unsigned long next_statechange; - u32 command; - - /* SILICON QUIRKS */ - struct list_head urb_list; /* this is the head to urb - * queue that didn't get enough - * resources - */ - struct oxu_murb *murb_pool; /* murb per split big urb */ - unsigned urb_len; - - u8 sbrn; /* packed release number */ -}; - -#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ -#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ -#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ -#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ - -enum ehci_timer_action { - TIMER_IO_WATCHDOG, - TIMER_IAA_WATCHDOG, - TIMER_ASYNC_SHRINK, - TIMER_ASYNC_OFF, -}; From 3114bc89c0217254d3df10a42eab6b6ca00f547c Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Mon, 15 Jul 2019 11:20:10 +0800 Subject: [PATCH 008/159] usb: host: Remove call to memset after dma_alloc_coherent In commit 518a2f1925c3 ("dma-mapping: zero memory returned from dma_alloc_*"), dma_alloc_coherent has already zeroed the memory. So memset is not needed. Signed-off-by: Fuqian Huang Link: https://lore.kernel.org/r/20190715032010.7258-1-huangfq.daxian@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 1 - drivers/usb/host/xhci-mem.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 52e32644a4b2..93e2cca5262d 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -22,7 +22,6 @@ dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size, vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev, size, dma_handle, flags); - memset(vaddr, 0, size); return vaddr; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index cf5e17962179..e16eda6e2b8b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2399,7 +2399,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) flags); if (!xhci->dcbaa) goto fail; - memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); xhci->dcbaa->dma = dma; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Device context base array address = 0x%llx (DMA), %p (virt)", From 6269e4c76eacabaea0d0099200ae1a455768d208 Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Sun, 7 Jul 2019 15:17:19 +0300 Subject: [PATCH 009/159] usb: host: xhci-hub: fix extra endianness conversion Don't do extra cpu_to_le32 conversion for put_unaligned_le32 because it is already implemented in this function. Fixes sparse error: xhci-hub.c:1152:44: warning: incorrect type in argument 1 (different base types) xhci-hub.c:1152:44: expected unsigned int [usertype] val xhci-hub.c:1152:44: got restricted __le32 [usertype] Fixes: 395f540 "xhci: support new USB 3.1 hub request to get extended port status" Cc: Mathias Nyman Signed-off-by: Ruslan Bilovol Link: https://lore.kernel.org/r/1562501839-26522-1-git-send-email-ruslan.bilovol@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 3abe70ff1b1e..b7d23c438756 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1149,7 +1149,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } port_li = readl(ports[wIndex]->addr + PORTLI); status = xhci_get_ext_port_status(temp, port_li); - put_unaligned_le32(cpu_to_le32(status), &buf[4]); + put_unaligned_le32(status, &buf[4]); } break; case SetPortFeature: From ed7fe551575cd6dbbe31282a0869682aeea2627c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 4 Jul 2019 16:03:41 +0100 Subject: [PATCH 010/159] usb: chipidea: udc: remove redundant assignment to variable retval The variable retval is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Also remove a blank line. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190704150341.759-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 6a5ee8e6da10..053432d79bf7 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1746,12 +1746,11 @@ static int ci_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); - int retval = -ENOMEM; + int retval; if (driver->disconnect == NULL) return -EINVAL; - ci->ep0out->ep.desc = &ctrl_endpt_out_desc; retval = usb_ep_enable(&ci->ep0out->ep); if (retval) From 34d0545978b65f0a4ced613eb99cd75b2a6132e9 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 12 Jun 2019 13:55:17 +0800 Subject: [PATCH 011/159] dt-bindings: usb: mtu3: fix typo of DMA clock name Fix typo of dma_ck Signed-off-by: Chunfeng Yun Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/5e06482a0be15476c7b5825f155accf98275afa8.1560246390.git.chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/mediatek,mtu3.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt index 3382b5cb471d..5d740e9d4525 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt @@ -16,7 +16,7 @@ Required properties: entry in clock-names - clock-names : must contain "sys_ck" for clock of controller, the following clocks are optional: - "ref_ck", "mcu_ck" and "dam_ck"; + "ref_ck", "mcu_ck" and "dma_ck"; - phys : see usb-hcd.txt in the current directory - dr_mode : should be one of "host", "peripheral" or "otg", refer to usb/generic.txt From 6389a62ff798e781567645c0b0ca3dd7b8a4289d Mon Sep 17 00:00:00 2001 From: "Liu, Changcheng" Date: Thu, 25 Jul 2019 21:22:09 +0800 Subject: [PATCH 012/159] usbip: tools: fix GCC8 warning for strncpy GCC8 started emitting warning about using strncpy with number of bytes exactly equal destination size which could lead to non-zero terminated string being copied. Use "SYSFS_PATH_MAX - 1" & "SYSFS_BUS_ID_SIZE - 1" as number of bytes to ensure name is always zero-terminated. Signed-off-by: Changcheng Liu Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20190725132209.GA27590@jerryopenix Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/libsrc/usbip_common.c | 6 ++++-- tools/usb/usbip/libsrc/usbip_device_driver.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c index bb424638d75b..b8d7d480595a 100644 --- a/tools/usb/usbip/libsrc/usbip_common.c +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -226,8 +226,10 @@ int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev) path = udev_device_get_syspath(sdev); name = udev_device_get_sysname(sdev); - strncpy(udev->path, path, SYSFS_PATH_MAX); - strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE); + strncpy(udev->path, path, SYSFS_PATH_MAX - 1); + udev->path[SYSFS_PATH_MAX - 1] = '\0'; + strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1); + udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; sscanf(name, "%u-%u", &busnum, &devnum); udev->busnum = busnum; diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c index 5a3726eb44ab..051d7d3f443b 100644 --- a/tools/usb/usbip/libsrc/usbip_device_driver.c +++ b/tools/usb/usbip/libsrc/usbip_device_driver.c @@ -91,7 +91,8 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev) copy_descr_attr16(dev, &descr, idProduct); copy_descr_attr16(dev, &descr, bcdDevice); - strncpy(dev->path, path, SYSFS_PATH_MAX); + strncpy(dev->path, path, SYSFS_PATH_MAX - 1); + dev->path[SYSFS_PATH_MAX - 1] = '\0'; dev->speed = USB_SPEED_UNKNOWN; speed = udev_device_get_sysattr_value(sdev, "current_speed"); @@ -110,7 +111,8 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev) dev->busnum = 0; name = udev_device_get_sysname(plat); - strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE); + strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE - 1); + dev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; return 0; err: fclose(fd); From c27989cc536b5e0ba179d7ab50ebc2a06fddf663 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 26 Jul 2019 10:14:51 +0200 Subject: [PATCH 013/159] dt-bindings: switch Exynos EHCI/OHCI bindings to use array of generic PHYs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 69bec7259853 ("USB: core: let USB device know device node") added support for attaching devicetree node for USB devices. Those nodes are children of their USB host controller. However Exynos EHCI and OHCI driver bindings already define child-nodes for each physical root hub port and assigns respective PHY controller and parameters to them. This leads to the conflict. A workaround for it has been merged as commit 01d4071486fe ("usb: exynos: add workaround for the USB device bindings conflict"), but it disabled support for USB device binding for Exynos EHCI/OHCI controllers. To resolve it properly, lets move PHYs from the sub-nodes to a standard array under the 'phys' property. Suggested-by: Måns Rullgård Reviewed-by: Rob Herring Signed-off-by: Marek Szyprowski Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20190726081453.9456-2-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/exynos-usb.txt | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt index b7111f43fa59..66c394f9e11f 100644 --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt @@ -12,13 +12,11 @@ Required properties: - interrupts: interrupt number to the cpu. - clocks: from common clock binding: handle to usb clock. - clock-names: from common clock binding: Shall be "usbhost". - - port: if in the SoC there are EHCI phys, they should be listed here. - One phy per port. Each port should have following entries: - - reg: port number on EHCI controller, e.g - On Exynos5250, port 0 is USB2.0 otg phy - port 1 is HSIC phy0 - port 2 is HSIC phy1 - - phys: from the *Generic PHY* bindings; specifying phy used by port. + - phys: from the *Generic PHY* bindings; array specifying phy(s) used + by the root port. + - phy-names: from the *Generic PHY* bindings; array of the names for + each phy for the root ports, must be a subset of the following: + "host", "hsic0", "hsic1". Optional properties: - samsung,vbus-gpio: if present, specifies the GPIO that @@ -35,12 +33,8 @@ Example: clocks = <&clock 285>; clock-names = "usbhost"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&usb2phy 1>; - }; + phys = <&usb2phy 1>; + phy-names = "host"; }; OHCI @@ -52,13 +46,11 @@ Required properties: - interrupts: interrupt number to the cpu. - clocks: from common clock binding: handle to usb clock. - clock-names: from common clock binding: Shall be "usbhost". - - port: if in the SoC there are OHCI phys, they should be listed here. - One phy per port. Each port should have following entries: - - reg: port number on OHCI controller, e.g - On Exynos5250, port 0 is USB2.0 otg phy - port 1 is HSIC phy0 - port 2 is HSIC phy1 - - phys: from the *Generic PHY* bindings, specifying phy used by port. + - phys: from the *Generic PHY* bindings; array specifying phy(s) used + by the root port. + - phy-names: from the *Generic PHY* bindings; array of the names for + each phy for the root ports, must be a subset of the following: + "host", "hsic0", "hsic1". Example: usb@12120000 { @@ -69,13 +61,8 @@ Example: clocks = <&clock 285>; clock-names = "usbhost"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&usb2phy 1>; - }; - + phys = <&usb2phy 1>; + phy-names = "host"; }; DWC3 From 214b606e90dd087d190b7307183ec8c16f500371 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 26 Jul 2019 10:14:52 +0200 Subject: [PATCH 014/159] usb: exynos: add support for getting PHYs from the standard dt array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the code for getting generic PHYs from standard device tree array from the main controller device node. This is a first step in resolving the conflict between Exynos EHCI/OHCI sub-nodes and generic USB device bindings. Later the sub-nodes currently used for assigning PHYs to root ports of the controller will be removed making a place for the generic USB device bindings nodes. Suggested-by: Måns Rullgård Signed-off-by: Marek Szyprowski Acked-by: Alan Stern Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20190726081453.9456-3-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-exynos.c | 23 +++++++++++++++++++---- drivers/usb/host/ohci-exynos.c | 23 +++++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 3a29a1a8519c..01debfd03d4a 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -41,6 +41,7 @@ struct exynos_ehci_hcd { struct clk *clk; struct device_node *of_node; struct phy *phy[PHY_NUMBER]; + bool legacy_phy; }; #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv) @@ -50,10 +51,22 @@ static int exynos_ehci_get_phy(struct device *dev, { struct device_node *child; struct phy *phy; - int phy_number; + int phy_number, num_phys; int ret; /* Get PHYs for the controller */ + num_phys = of_count_phandle_with_args(dev->of_node, "phys", + "#phy-cells"); + for (phy_number = 0; phy_number < num_phys; phy_number++) { + phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number); + if (IS_ERR(phy)) + return PTR_ERR(phy); + exynos_ehci->phy[phy_number] = phy; + } + if (num_phys > 0) + return 0; + + /* Get PHYs using legacy bindings */ for_each_available_child_of_node(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number); if (ret) { @@ -84,6 +97,7 @@ static int exynos_ehci_get_phy(struct device *dev, } } + exynos_ehci->legacy_phy = true; return 0; } @@ -205,11 +219,12 @@ static int exynos_ehci_probe(struct platform_device *pdev) ehci->caps = hcd->regs; /* - * Workaround: reset of_node pointer to avoid conflict between Exynos - * EHCI port subnodes and generic USB device bindings + * Workaround: reset of_node pointer to avoid conflict between legacy + * Exynos EHCI port subnodes and generic USB device bindings */ exynos_ehci->of_node = pdev->dev.of_node; - pdev->dev.of_node = NULL; + if (exynos_ehci->legacy_phy) + pdev->dev.of_node = NULL; /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 905c6317e0c3..d5ce98e205c7 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -32,6 +32,7 @@ struct exynos_ohci_hcd { struct clk *clk; struct device_node *of_node; struct phy *phy[PHY_NUMBER]; + bool legacy_phy; }; static int exynos_ohci_get_phy(struct device *dev, @@ -39,10 +40,22 @@ static int exynos_ohci_get_phy(struct device *dev, { struct device_node *child; struct phy *phy; - int phy_number; + int phy_number, num_phys; int ret; /* Get PHYs for the controller */ + num_phys = of_count_phandle_with_args(dev->of_node, "phys", + "#phy-cells"); + for (phy_number = 0; phy_number < num_phys; phy_number++) { + phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number); + if (IS_ERR(phy)) + return PTR_ERR(phy); + exynos_ohci->phy[phy_number] = phy; + } + if (num_phys > 0) + return 0; + + /* Get PHYs using legacy bindings */ for_each_available_child_of_node(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number); if (ret) { @@ -73,6 +86,7 @@ static int exynos_ohci_get_phy(struct device *dev, } } + exynos_ohci->legacy_phy = true; return 0; } @@ -172,11 +186,12 @@ static int exynos_ohci_probe(struct platform_device *pdev) } /* - * Workaround: reset of_node pointer to avoid conflict between Exynos - * OHCI port subnodes and generic USB device bindings + * Workaround: reset of_node pointer to avoid conflict between legacy + * Exynos OHCI port subnodes and generic USB device bindings */ exynos_ohci->of_node = pdev->dev.of_node; - pdev->dev.of_node = NULL; + if (exynos_ohci->legacy_phy) + pdev->dev.of_node = NULL; err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { From 314de2f6b577a55772674dd2e4907a8c94b00975 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 26 Jul 2019 10:14:53 +0200 Subject: [PATCH 015/159] ARM: dts: exynos: Use standard arrays of generic PHYs for EHCI/OHCI devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move USB PHYs to a standard arrays for Exynos EHCI/OHCI devices. This resolves the conflict between Exynos EHCI/OHCI sub-nodes and generic USB device bindings. Once the Exynos EHCI/OHCI sub-nodes are removed, the boards can finally provide sub-nodes for the USB devices using generic USB device bindings. Suggested-by: Måns Rullgård Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20190726081453.9456-4-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/exynos4.dtsi | 28 +++---------------- .../boot/dts/exynos4210-universal_c210.dts | 8 ++---- arch/arm/boot/dts/exynos4412-itop-elite.dts | 9 ++---- arch/arm/boot/dts/exynos4412-odroidu3.dts | 8 ++---- arch/arm/boot/dts/exynos4412-odroidx.dts | 5 ++-- arch/arm/boot/dts/exynos4412-origen.dts | 9 ++---- arch/arm/boot/dts/exynos5250.dtsi | 16 +++-------- arch/arm/boot/dts/exynos54xx.dtsi | 18 +++--------- 8 files changed, 22 insertions(+), 79 deletions(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 1264cc431ff6..433f109d97ca 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -380,23 +380,8 @@ clocks = <&clock CLK_USB_HOST>; clock-names = "usbhost"; status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&exynos_usbphy 1>; - status = "disabled"; - }; - port@1 { - reg = <1>; - phys = <&exynos_usbphy 2>; - status = "disabled"; - }; - port@2 { - reg = <2>; - phys = <&exynos_usbphy 3>; - status = "disabled"; - }; + phys = <&exynos_usbphy 1>, <&exynos_usbphy 2>, <&exynos_usbphy 3>; + phy-names = "host", "hsic0", "hsic1"; }; ohci: ohci@12590000 { @@ -406,13 +391,8 @@ clocks = <&clock CLK_USB_HOST>; clock-names = "usbhost"; status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&exynos_usbphy 1>; - status = "disabled"; - }; + phys = <&exynos_usbphy 1>; + phy-names = "host"; }; gpu: gpu@13000000 { diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts index 82a8b5449978..09d3d54d09ff 100644 --- a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts @@ -204,9 +204,8 @@ &ehci { status = "okay"; - port@0 { - status = "okay"; - }; + phys = <&exynos_usbphy 1>; + phy-names = "host"; }; &exynos_usbphy { @@ -520,9 +519,6 @@ &ohci { status = "okay"; - port@0 { - status = "okay"; - }; }; &pinctrl_1 { diff --git a/arch/arm/boot/dts/exynos4412-itop-elite.dts b/arch/arm/boot/dts/exynos4412-itop-elite.dts index 0dedeba89b5f..f6d0a5f5d339 100644 --- a/arch/arm/boot/dts/exynos4412-itop-elite.dts +++ b/arch/arm/boot/dts/exynos4412-itop-elite.dts @@ -146,13 +146,8 @@ /* In order to reset USB ethernet */ samsung,vbus-gpio = <&gpc0 1 GPIO_ACTIVE_HIGH>; - port@0 { - status = "okay"; - }; - - port@2 { - status = "okay"; - }; + phys = <&exynos_usbphy 1>, <&exynos_usbphy 3>; + phy-names = "host", "hsic1"; }; &exynos_usbphy { diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 96d99887bceb..8ff243ba4542 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -105,12 +105,8 @@ }; &ehci { - port@1 { - status = "okay"; - }; - port@2 { - status = "okay"; - }; + phys = <&exynos_usbphy 2>, <&exynos_usbphy 3>; + phy-names = "hsic0", "hsic1"; }; &sound { diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts index a2251581f6b6..3ea2a0101e80 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx.dts @@ -72,9 +72,8 @@ }; &ehci { - port@1 { - status = "okay"; - }; + phys = <&exynos_usbphy 2>; + phy-names = "hsic0"; }; &mshc_0 { diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts index 698de4345d16..ecd14b283a6b 100644 --- a/arch/arm/boot/dts/exynos4412-origen.dts +++ b/arch/arm/boot/dts/exynos4412-origen.dts @@ -88,13 +88,8 @@ &ehci { samsung,vbus-gpio = <&gpx3 5 1>; status = "okay"; - - port@1 { - status = "okay"; - }; - port@2 { - status = "okay"; - }; + phys = <&exynos_usbphy 2>, <&exynos_usbphy 3>; + phy-names = "hsic0", "hsic1"; }; &fimd { diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index d5e0392b409e..c5584f40ebfb 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -617,12 +617,8 @@ clocks = <&clock CLK_USB2>; clock-names = "usbhost"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&usb2_phy_gen 1>; - }; + phys = <&usb2_phy_gen 1>; + phy-names = "host"; }; ohci: usb@12120000 { @@ -632,12 +628,8 @@ clocks = <&clock CLK_USB2>; clock-names = "usbhost"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&usb2_phy_gen 1>; - }; + phys = <&usb2_phy_gen 1>; + phy-names = "host"; }; usb2_phy_gen: phy@12130000 { diff --git a/arch/arm/boot/dts/exynos54xx.dtsi b/arch/arm/boot/dts/exynos54xx.dtsi index 0b27bebf9528..9c3b63b7cac6 100644 --- a/arch/arm/boot/dts/exynos54xx.dtsi +++ b/arch/arm/boot/dts/exynos54xx.dtsi @@ -189,26 +189,16 @@ compatible = "samsung,exynos4210-ehci"; reg = <0x12110000 0x100>; interrupts = ; - - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&usb2_phy 1>; - }; + phys = <&usb2_phy 1>; + phy-names = "host"; }; usbhost1: usb@12120000 { compatible = "samsung,exynos4210-ohci"; reg = <0x12120000 0x100>; interrupts = ; - - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&usb2_phy 1>; - }; + phys = <&usb2_phy 1>; + phy-names = "host"; }; usb2_phy: phy@12130000 { From 9e22cb82998253072ec05ee1f8b06daf5ca50800 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 29 Jul 2019 17:22:01 -0500 Subject: [PATCH 016/159] usb: host: ohci-tmio: Mark expected switch fall-throughs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark switch cases where we are expecting to fall through. This patch fixes the following warning (Building: arm): drivers/usb/host/ohci-tmio.c: In function ‘tmio_stop_hc’: ./include/linux/device.h:1499:2: warning: this statement may fall through [-Wimplicit-fallthrough=] _dev_err(dev, dev_fmt(fmt), ##__VA_ARGS__) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/usb/host/ohci-tmio.c:99:4: note: in expansion of macro ‘dev_err’ dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports); ^~~~~~~ In file included from drivers/usb/host/ohci-hcd.c:1257:0: drivers/usb/host/ohci-tmio.c:100:3: note: here case 3: ^~~~ drivers/usb/host/ohci-tmio.c:101:7: warning: this statement may fall through [-Wimplicit-fallthrough=] pm |= CCR_PM_USBPW3; ^ drivers/usb/host/ohci-tmio.c:102:3: note: here case 2: ^~~~ drivers/usb/host/ohci-tmio.c:103:7: warning: this statement may fall through [-Wimplicit-fallthrough=] pm |= CCR_PM_USBPW2; ^ drivers/usb/host/ohci-tmio.c:104:3: note: here case 1: ^~~~ Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20190729222201.GA19408@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-tmio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index d5a293a707b6..fb6f5e9ae5c6 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -97,10 +97,13 @@ static void tmio_stop_hc(struct platform_device *dev) switch (ohci->num_ports) { default: dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports); + /* fall through */ case 3: pm |= CCR_PM_USBPW3; + /* fall through */ case 2: pm |= CCR_PM_USBPW2; + /* fall through */ case 1: pm |= CCR_PM_USBPW1; } From dcf8f7ecb36185f4cf96ad1f098704eb690f792e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sun, 28 Jul 2019 19:06:31 -0500 Subject: [PATCH 017/159] usb: phy: ab8500-usb: Mark expected switch fall-throughs Mark switch cases where we are expecting to fall through. This patch fixes the following warnings: drivers/usb/phy/phy-ab8500-usb.c: In function 'ab8500_usb_link_status_update': drivers/usb/phy/phy-ab8500-usb.c:424:9: warning: this statement may fall through [-Wimplicit-fallthrough=] event = UX500_MUSB_RIDB; ~~~~~~^~~~~~~~~~~~~~~~~ drivers/usb/phy/phy-ab8500-usb.c:425:2: note: here case USB_LINK_NOT_CONFIGURED_8500: ^~~~ drivers/usb/phy/phy-ab8500-usb.c:440:9: warning: this statement may fall through [-Wimplicit-fallthrough=] event = UX500_MUSB_RIDC; ~~~~~~^~~~~~~~~~~~~~~~~ drivers/usb/phy/phy-ab8500-usb.c:441:2: note: here case USB_LINK_STD_HOST_NC_8500: ^~~~ drivers/usb/phy/phy-ab8500-usb.c:459:9: warning: this statement may fall through [-Wimplicit-fallthrough=] event = UX500_MUSB_RIDA; ~~~~~~^~~~~~~~~~~~~~~~~ drivers/usb/phy/phy-ab8500-usb.c:460:2: note: here case USB_LINK_HM_IDGND_8500: ^~~~ drivers/usb/phy/phy-ab8500-usb.c: In function 'ab8505_usb_link_status_update': drivers/usb/phy/phy-ab8500-usb.c:332:9: warning: this statement may fall through [-Wimplicit-fallthrough=] event = UX500_MUSB_RIDB; ~~~~~~^~~~~~~~~~~~~~~~~ drivers/usb/phy/phy-ab8500-usb.c:333:2: note: here case USB_LINK_NOT_CONFIGURED_8505: ^~~~ drivers/usb/phy/phy-ab8500-usb.c:352:9: warning: this statement may fall through [-Wimplicit-fallthrough=] event = UX500_MUSB_RIDC; ~~~~~~^~~~~~~~~~~~~~~~~ drivers/usb/phy/phy-ab8500-usb.c:353:2: note: here case USB_LINK_STD_HOST_NC_8505: ^~~~ drivers/usb/phy/phy-ab8500-usb.c:370:9: warning: this statement may fall through [-Wimplicit-fallthrough=] event = UX500_MUSB_RIDA; ~~~~~~^~~~~~~~~~~~~~~~~ drivers/usb/phy/phy-ab8500-usb.c:371:2: note: here case USB_LINK_HM_IDGND_8505: ^~~~ Reported-by: Stephen Rothwell Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20190729000631.GA24165@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-ab8500-usb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index aaf363f19714..14b432982fd3 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -330,6 +330,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab, switch (lsts) { case USB_LINK_ACA_RID_B_8505: event = UX500_MUSB_RIDB; + /* Fall through */ case USB_LINK_NOT_CONFIGURED_8505: case USB_LINK_RESERVED0_8505: case USB_LINK_RESERVED1_8505: @@ -350,6 +351,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab, case USB_LINK_ACA_RID_C_NM_8505: event = UX500_MUSB_RIDC; + /* Fall through */ case USB_LINK_STD_HOST_NC_8505: case USB_LINK_STD_HOST_C_NS_8505: case USB_LINK_STD_HOST_C_S_8505: @@ -368,6 +370,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab, case USB_LINK_ACA_RID_A_8505: case USB_LINK_ACA_DOCK_CHGR_8505: event = UX500_MUSB_RIDA; + /* Fall through */ case USB_LINK_HM_IDGND_8505: if (ab->mode == USB_IDLE) { ab->mode = USB_HOST; @@ -422,6 +425,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab, switch (lsts) { case USB_LINK_ACA_RID_B_8500: event = UX500_MUSB_RIDB; + /* Fall through */ case USB_LINK_NOT_CONFIGURED_8500: case USB_LINK_NOT_VALID_LINK_8500: ab->mode = USB_IDLE; @@ -438,6 +442,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab, case USB_LINK_ACA_RID_C_HS_8500: case USB_LINK_ACA_RID_C_HS_CHIRP_8500: event = UX500_MUSB_RIDC; + /* Fall through */ case USB_LINK_STD_HOST_NC_8500: case USB_LINK_STD_HOST_C_NS_8500: case USB_LINK_STD_HOST_C_S_8500: @@ -457,6 +462,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab, case USB_LINK_ACA_RID_A_8500: event = UX500_MUSB_RIDA; + /* Fall through */ case USB_LINK_HM_IDGND_8500: if (ab->mode == USB_IDLE) { ab->mode = USB_HOST; From b33f37064b743d4c5771d3bfc65976790b7396cc Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:46 -0700 Subject: [PATCH 018/159] usb: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20190730181557.90391-47-swboyd@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 1 - drivers/usb/dwc2/platform.c | 4 +--- drivers/usb/dwc3/dwc3-keystone.c | 1 - drivers/usb/dwc3/dwc3-omap.c | 4 +--- drivers/usb/gadget/udc/aspeed-vhub/core.c | 1 - drivers/usb/gadget/udc/bcm63xx_udc.c | 8 ++------ drivers/usb/gadget/udc/bdc/bdc_core.c | 4 +--- drivers/usb/gadget/udc/gr_udc.c | 8 ++------ drivers/usb/gadget/udc/lpc32xx_udc.c | 5 +---- drivers/usb/gadget/udc/renesas_usb3.c | 4 +--- drivers/usb/gadget/udc/s3c-hsudc.c | 4 +--- drivers/usb/gadget/udc/udc-xilinx.c | 4 +--- drivers/usb/host/ehci-atmel.c | 3 --- drivers/usb/host/ehci-omap.c | 4 +--- drivers/usb/host/ehci-orion.c | 3 --- drivers/usb/host/ehci-platform.c | 4 +--- drivers/usb/host/ehci-sh.c | 3 --- drivers/usb/host/ehci-st.c | 4 +--- drivers/usb/host/imx21-hcd.c | 4 +--- drivers/usb/host/ohci-platform.c | 4 +--- drivers/usb/host/ohci-st.c | 4 +--- drivers/usb/mtu3/mtu3_core.c | 4 +--- drivers/usb/phy/phy-ab8500-usb.c | 12 +++--------- drivers/usb/typec/tcpm/wcove.c | 4 +--- 24 files changed, 22 insertions(+), 79 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 26062d610c20..36c964cd40a3 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1008,7 +1008,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->irq = platform_get_irq(pdev, 0); if (ci->irq < 0) { - dev_err(dev, "missing IRQ\n"); ret = ci->irq; goto deinit_phy; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 80fd3c6dcd1c..3c6ce09a6db5 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -407,10 +407,8 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); hsotg->irq = platform_get_irq(dev, 0); - if (hsotg->irq < 0) { - dev_err(&dev->dev, "missing IRQ resource\n"); + if (hsotg->irq < 0) return hsotg->irq; - } dev_dbg(hsotg->dev, "registering common handler for irq%d\n", hsotg->irq); diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index cbee5fb9b9fb..a69eb4a7b832 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -112,7 +112,6 @@ static int kdwc3_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "missing irq\n"); error = irq; goto err_irq; } diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ed8b86517675..6f711d58d82f 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -469,10 +469,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) platform_set_drvdata(pdev, omap); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "missing IRQ resource: %d\n", irq); + if (irq < 0) return irq; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(dev, res); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index db3628be38c0..c08d385e2723 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -348,7 +348,6 @@ static int ast_vhub_probe(struct platform_device *pdev) /* Find interrupt and install handler */ vhub->irq = platform_get_irq(pdev, 0); if (vhub->irq < 0) { - dev_err(&pdev->dev, "Failed to get interrupt\n"); rc = vhub->irq; goto err; } diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index c1fcc77403ea..97b16463f3ef 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2328,10 +2328,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) /* IRQ resource #0: control interrupt (VBUS, speed, etc.) */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "missing IRQ resource #0\n"); + if (irq < 0) goto out_uninit; - } if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0, dev_name(dev), udc) < 0) goto report_request_failure; @@ -2339,10 +2337,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { irq = platform_get_irq(pdev, i + 1); - if (irq < 0) { - dev_err(dev, "missing IRQ resource #%d\n", i + 1); + if (irq < 0) goto out_uninit; - } if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0, dev_name(dev), &udc->iudma[i]) < 0) goto report_request_failure; diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index ccbd1d34eb2a..cc4a16e253ac 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -515,10 +515,8 @@ static int bdc_probe(struct platform_device *pdev) return -ENOMEM; } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "platform_get_irq failed:%d\n", irq); + if (irq < 0) return irq; - } spin_lock_init(&bdc->lock); platform_set_drvdata(pdev, bdc); bdc->irq = irq; diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 729e60e49564..7a0e9a58c2d8 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2134,19 +2134,15 @@ static int gr_probe(struct platform_device *pdev) return PTR_ERR(regs); dev->irq = platform_get_irq(pdev, 0); - if (dev->irq <= 0) { - dev_err(dev->dev, "No irq found\n"); + if (dev->irq <= 0) return -ENODEV; - } /* Some core configurations has separate irqs for IN and OUT events */ dev->irqi = platform_get_irq(pdev, 1); if (dev->irqi > 0) { dev->irqo = platform_get_irq(pdev, 2); - if (dev->irqo <= 0) { - dev_err(dev->dev, "Found irqi but not irqo\n"); + if (dev->irqo <= 0) return -ENODEV; - } } else { dev->irqi = 0; } diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 5f1b14f3e5a0..e9cf20979bf6 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3061,11 +3061,8 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) /* Get IRQs */ for (i = 0; i < 4; i++) { udc->udp_irq[i] = platform_get_irq(pdev, i); - if (udc->udp_irq[i] < 0) { - dev_err(udc->dev, - "irq resource %d not available!\n", i); + if (udc->udp_irq[i] < 0) return udc->udp_irq[i]; - } } udc->udp_baseaddr = devm_ioremap_resource(dev, res); diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 87062d22134d..027a25694a68 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2743,10 +2743,8 @@ static int renesas_usb3_probe(struct platform_device *pdev) priv = of_device_get_match_data(&pdev->dev); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); + if (irq < 0) return irq; - } usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL); if (!usb3) diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 31c7c5587cf9..858993c73442 100644 --- a/drivers/usb/gadget/udc/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -1311,10 +1311,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev) s3c_hsudc_setup_ep(hsudc); ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "unable to obtain IRQ number\n"); + if (ret < 0) goto err_res; - } hsudc->irq = ret; ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0, diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index b1f4104d1283..29d8e5f8bb58 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2074,10 +2074,8 @@ static int xudc_probe(struct platform_device *pdev) return PTR_ERR(udc->addr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "unable to get irq\n"); + if (irq < 0) return irq; - } ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0, dev_name(&pdev->dev), udc); if (ret < 0) { diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 3ba140ceaf52..e893467d659c 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -100,9 +100,6 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { - dev_err(&pdev->dev, - "Found HC with no IRQ. Check %s setup!\n", - dev_name(&pdev->dev)); retval = -ENODEV; goto fail_create_hcd; } diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7d20296cbe9f..fc125b3d06e7 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -115,10 +115,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "EHCI irq failed: %d\n", irq); + if (irq < 0) return irq; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 790acf3633e8..a319b1df3011 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -223,9 +223,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { - dev_err(&pdev->dev, - "Found HC with no IRQ. Check %s setup!\n", - dev_name(&pdev->dev)); err = -ENODEV; goto err; } diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 4c306fb6b069..769749ca5961 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -145,10 +145,8 @@ static int ehci_platform_probe(struct platform_device *dev) } irq = platform_get_irq(dev, 0); - if (irq < 0) { - dev_err(&dev->dev, "no irq provided"); + if (irq < 0) return irq; - } hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, dev_name(&dev->dev)); diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index a9ee767952c1..ef75b9d70eb4 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -85,9 +85,6 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { - dev_err(&pdev->dev, - "Found HC with no IRQ. Check %s setup!\n", - dev_name(&pdev->dev)); ret = -ENODEV; goto fail_create_hcd; } diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index ccb4e611001d..f74433aac948 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -158,10 +158,8 @@ static int st_ehci_platform_probe(struct platform_device *dev) return -ENODEV; irq = platform_get_irq(dev, 0); - if (irq < 0) { - dev_err(&dev->dev, "no irq provided"); + if (irq < 0) return irq; - } res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!res_mem) { dev_err(&dev->dev, "no memory resource provided"); diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 6e3dad19d369..e406c5459a97 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1836,10 +1836,8 @@ static int imx21_probe(struct platform_device *pdev) if (!res) return -ENODEV; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); + if (irq < 0) return irq; - } hcd = usb_create_hcd(&imx21_hc_driver, &pdev->dev, dev_name(&pdev->dev)); diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 65a1c3fdc88c..7addfc2cbadc 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -111,10 +111,8 @@ static int ohci_platform_probe(struct platform_device *dev) return err; irq = platform_get_irq(dev, 0); - if (irq < 0) { - dev_err(&dev->dev, "no irq provided"); + if (irq < 0) return irq; - } hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, dev_name(&dev->dev)); diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 638a92bd2cdc..ac796ccd93ef 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -138,10 +138,8 @@ static int st_ohci_platform_probe(struct platform_device *dev) return -ENODEV; irq = platform_get_irq(dev, 0); - if (irq < 0) { - dev_err(&dev->dev, "no irq provided"); + if (irq < 0) return irq; - } res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!res_mem) { diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index f8bd1d57e795..c3d5c1206eec 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -835,10 +835,8 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) return -ENOMEM; mtu->irq = platform_get_irq(pdev, 0); - if (mtu->irq < 0) { - dev_err(dev, "fail to get irq number\n"); + if (mtu->irq < 0) return mtu->irq; - } dev_info(dev, "irq %d\n", mtu->irq); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac"); diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 14b432982fd3..4bb4b1d42f32 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -718,10 +718,8 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev, if (ab->flags & AB8500_USB_FLAG_USE_LINK_STATUS_IRQ) { irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS"); - if (irq < 0) { - dev_err(&pdev->dev, "Link status irq not found\n"); + if (irq < 0) return irq; - } err = devm_request_threaded_irq(&pdev->dev, irq, NULL, ab8500_usb_link_status_irq, IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, @@ -734,10 +732,8 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev, if (ab->flags & AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ) { irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); - if (irq < 0) { - dev_err(&pdev->dev, "ID fall irq not found\n"); + if (irq < 0) return irq; - } err = devm_request_threaded_irq(&pdev->dev, irq, NULL, ab8500_usb_disconnect_irq, IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, @@ -750,10 +746,8 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev, if (ab->flags & AB8500_USB_FLAG_USE_VBUS_DET_IRQ) { irq = platform_get_irq_byname(pdev, "VBUS_DET_F"); - if (irq < 0) { - dev_err(&pdev->dev, "VBUS fall irq not found\n"); + if (irq < 0) return irq; - } err = devm_request_threaded_irq(&pdev->dev, irq, NULL, ab8500_usb_disconnect_irq, IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 6b317c150bdd..edc271da14f4 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -617,10 +617,8 @@ static int wcove_typec_probe(struct platform_device *pdev) wcove->regmap = pmic->regmap; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); + if (irq < 0) return irq; - } irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr, irq); if (irq < 0) From 82e17a09c412ad981dd29c0243b73b363835899e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 31 Jul 2019 23:39:17 +0100 Subject: [PATCH 019/159] usb: musb: remove redundant assignment to variable ret Variable ret is being initialized with a value that is never read and ret is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190731223917.16532-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 9f5a4819a744..2bc55e0ceace 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1721,7 +1721,7 @@ mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct musb *musb = dev_to_musb(dev); unsigned long flags; - int ret = -EINVAL; + int ret; spin_lock_irqsave(&musb->lock, flags); ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->otg->state)); From d1f54df4fbb5e525bea4ecc60ceb1e5cf2387689 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 31 Jul 2019 15:42:41 +0200 Subject: [PATCH 020/159] usb: wusbcore: Spelling s/disconenct/disconnect/ Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20190731134241.18647-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/devconnect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index a93837d57d53..1170f8baf608 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -49,7 +49,7 @@ * for processing a DN_Alive pong from a device. * * wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to - * process a disconenct request from a + * process a disconnect request from a * device. * * __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when From eb6c2eb6c7fbd24b51fba2fa4b270c839b8b8349 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 31 Jul 2019 14:11:50 +0200 Subject: [PATCH 021/159] usb: host: xhci-plat: Prevent an abnormally restrictive PHY init skipping In the past, USB PHY handling has been moved in the HCD core. Some host controller drivers needing more control of the PHYs, they have been granted the freedom to handle themselves the PHY states and to prevent the HCD core to do so in commit 4e88d4c08301 ("usb: add a flag to skip PHY initialization to struct usb_hcd"). With this change, any USB host controller could set the hcd->skip_phy_initialization flag so that the HCD core would just skip the PHY initialization sequence. However, in the USB subsystem, there are currently two entirely different forms of PHY: one is called 'usb_phy' and is USB-subsystem-wide, while there is also the generic and kernel-wide 'phy' from the (recent) generic PHY framework. When the commit above was introduced, both type of PHYs where handled by the HCD core. Later, commit bc40f5341741 ("USB: core: hcd: drop support for legacy phys") removed the support for the former type of PHYs in the HCD core. These 'usb_phy' are still present though, but managed from the controller drivers only. Hence, setting the hcd->skip_phy_initialization flag just because a 'usb_phy' is initialized by a controller driver is a non-sense. For instance on Armada CP110, a 'usb_phy' is there to enable the power supply to the USB host, while there is also a COMPHY block providing SERDES lanes configuration that is referenced as a PHY from the common PHY framework. Right now, users of the xhci-plat.c driver either use a 'usb_phy' only and do not care about the attempt of generic PHY initialization within the HCD core (as there is none); or they use a single 'phy' and the code flow does not pass through the block setting hcd->skip_phy_initialization anyway. While there is not users of both PHY types at the same time, drop this limitation from the xhci-plat.c driver. Note that the tegra driver probably has the same limitation and could definitely benefit from a similar change. Cc: Johan Hovold Cc: Martin Blumenstingl Signed-off-by: Miquel Raynal Acked-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20190731121150.2253-1-miquel.raynal@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 998241f5fce3..a1e5ce484bf8 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -307,7 +307,6 @@ static int xhci_plat_probe(struct platform_device *pdev) ret = usb_phy_init(hcd->usb_phy); if (ret) goto put_usb3_hcd; - hcd->skip_phy_initialization = 1; } hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node); From d3523b631403897bf560927321ca8c2ea0aed873 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 2 Aug 2019 21:04:08 +0800 Subject: [PATCH 022/159] usb: dwc3: meson-g12a: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Acked-by: Neil Armstrong Link: https://lore.kernel.org/r/20190802130408.20336-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index bca7e92a10e9..d73ccd9e1366 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -386,7 +386,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; void __iomem *base; - struct resource *res; enum phy_mode otg_id; int ret, i, irq; @@ -394,8 +393,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); From 582ee9c5ad2661b359309f379f5c411fd33e511a Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 5 Aug 2019 22:24:12 +0800 Subject: [PATCH 023/159] USB: Disable USB2 LPM at shutdown The QCA Rome USB Bluetooth controller has several issues once LPM gets enabled: - Fails to get enumerated in coldboot. [1] - Drains more power (~ 0.2W) when the system is in S5. [2] - Disappears after a warmboot. [2] The issue happens because the device lingers at LPM L1 in S5, so device can't get enumerated even after a reboot. Disable LPM at shutdown to solve the issue. [1] https://bugs.launchpad.net/bugs/1757218 [2] https://patchwork.kernel.org/patch/10607097/ Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20190805142412.23965-1-kai.heng.feng@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/port.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 1a06a4b5fbb1..bbbb35fa639f 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -285,6 +285,14 @@ static int usb_port_runtime_suspend(struct device *dev) } #endif +static void usb_port_shutdown(struct device *dev) +{ + struct usb_port *port_dev = to_usb_port(dev); + + if (port_dev->child) + usb_disable_usb2_hardware_lpm(port_dev->child); +} + static const struct dev_pm_ops usb_port_pm_ops = { #ifdef CONFIG_PM .runtime_suspend = usb_port_runtime_suspend, @@ -301,6 +309,7 @@ struct device_type usb_port_device_type = { static struct device_driver usb_port_driver = { .name = "usb", .owner = THIS_MODULE, + .shutdown = usb_port_shutdown, }; static int link_peers(struct usb_port *left, struct usb_port *right) From 91148dbad80bb6024389287be4aefcbf20b2d0e4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2019 21:36:35 +0200 Subject: [PATCH 024/159] USB: usbip: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Cc: Valentina Manea Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20190805193636.25560-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc.h | 2 +- drivers/usb/usbip/vudc_dev.c | 9 --------- drivers/usb/usbip/vudc_main.c | 1 + drivers/usb/usbip/vudc_sysfs.c | 7 ++++++- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index cf968192e59f..1bd4bc005829 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -115,7 +115,7 @@ struct vudc_device { struct list_head dev_entry; }; -extern const struct attribute_group vudc_attr_group; +extern const struct attribute_group *vudc_groups[]; /* visible everywhere */ diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index a72c17ff1c6a..c8eeabdd9b56 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -616,18 +616,10 @@ int vudc_probe(struct platform_device *pdev) if (ret < 0) goto err_add_udc; - ret = sysfs_create_group(&pdev->dev.kobj, &vudc_attr_group); - if (ret) { - dev_err(&udc->pdev->dev, "create sysfs files\n"); - goto err_sysfs; - } - platform_set_drvdata(pdev, udc); return ret; -err_sysfs: - usb_del_gadget_udc(&udc->gadget); err_add_udc: cleanup_vudc_hw(udc); err_init_vudc_hw: @@ -640,7 +632,6 @@ int vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); - sysfs_remove_group(&pdev->dev.kobj, &vudc_attr_group); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 390733e6937e..678faa82598c 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -22,6 +22,7 @@ static struct platform_driver vudc_driver = { .remove = vudc_remove, .driver = { .name = GADGET_NAME, + .dev_groups = vudc_groups, }, }; diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 6dcd3ff655c3..100f680c572a 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -215,7 +215,12 @@ static struct bin_attribute *dev_bin_attrs[] = { NULL, }; -const struct attribute_group vudc_attr_group = { +static const struct attribute_group vudc_attr_group = { .attrs = dev_attrs, .bin_attrs = dev_bin_attrs, }; + +const struct attribute_group *vudc_groups[] = { + &vudc_attr_group, + NULL, +}; From b71b283e3d6df40cef03b5ce882b2385971f58a3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:51 +0200 Subject: [PATCH 025/159] USB: add support for dev_groups to struct usb_driver Now that the driver core supports dev_groups for individual drivers, expose that pointer to struct usb_driver to make it easier for USB drivers to also use it. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20190806144502.17792-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 1 + include/linux/usb.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ebcadaad89d1..687fc5df4c17 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -954,6 +954,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, new_driver->drvwrap.driver.remove = usb_unbind_interface; new_driver->drvwrap.driver.owner = owner; new_driver->drvwrap.driver.mod_name = mod_name; + new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups; spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); diff --git a/include/linux/usb.h b/include/linux/usb.h index 83d35d993e8c..af4eb6419ae8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1151,6 +1151,8 @@ struct usbdrv_wrap { * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set * or your driver's probe function will never get called. + * @dev_groups: Attributes attached to the device that will be created once it + * is bound to the driver. * @dynids: used internally to hold the list of dynamically added device * ids for this driver. * @drvwrap: Driver-model core structure wrapper. @@ -1198,6 +1200,7 @@ struct usb_driver { int (*post_reset)(struct usb_interface *intf); const struct usb_device_id *id_table; + const struct attribute_group **dev_groups; struct usb_dynids dynids; struct usbdrv_wrap drvwrap; From 7d9c1d2f7aca2651b3821947bf928ee131df102a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:52 +0200 Subject: [PATCH 026/159] USB: add support for dev_groups to struct usb_device_driver Now that the driver core supports dev_groups for individual drivers, expose that pointer to struct usb_device_driver to make it easier for USB drivers to also use it. Yes, users of usb_device_driver are much rare, but there are instances already that use custom sysfs files, so adding this support will make things easier for those drivers. usbip is one example, hubs might be another one. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20190806144502.17792-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 1 + include/linux/usb.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 687fc5df4c17..2b27d232d7a7 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -892,6 +892,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver, new_udriver->drvwrap.driver.probe = usb_probe_device; new_udriver->drvwrap.driver.remove = usb_unbind_device; new_udriver->drvwrap.driver.owner = owner; + new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups; retval = driver_register(&new_udriver->drvwrap.driver); diff --git a/include/linux/usb.h b/include/linux/usb.h index af4eb6419ae8..57f667cad3ec 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1224,6 +1224,8 @@ struct usb_driver { * module is being unloaded. * @suspend: Called when the device is going to be suspended by the system. * @resume: Called when the device is being resumed by the system. + * @dev_groups: Attributes attached to the device that will be created once it + * is bound to the driver. * @drvwrap: Driver-model core structure wrapper. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for devices bound to this driver. @@ -1238,6 +1240,7 @@ struct usb_device_driver { int (*suspend) (struct usb_device *udev, pm_message_t message); int (*resume) (struct usb_device *udev, pm_message_t message); + const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; unsigned int supports_autosuspend:1; }; From 71ed79b0e4be0db254640c3beb9a1a0316eb5f61 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 12:15:09 +0200 Subject: [PATCH 027/159] USB: Move wusbcore and UWB to staging as it is obsolete The UWB and wusbcore code is long obsolete, so let us just move the code out of the real part of the kernel and into the drivers/staging/ location with plans to remove it entirely in a few releases. Link: https://lore.kernel.org/r/20190806101509.GA11280@kroah.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 15 +++------- drivers/Kconfig | 2 -- drivers/Makefile | 1 - drivers/staging/Kconfig | 3 ++ drivers/staging/Makefile | 2 ++ drivers/{ => staging}/uwb/Kconfig | 0 drivers/{ => staging}/uwb/Makefile | 0 drivers/staging/uwb/TODO | 8 ++++++ drivers/{ => staging}/uwb/address.c | 0 drivers/{ => staging}/uwb/allocator.c | 2 +- drivers/{ => staging}/uwb/beacon.c | 0 drivers/{ => staging}/uwb/driver.c | 0 drivers/{ => staging}/uwb/drp-avail.c | 0 drivers/{ => staging}/uwb/drp-ie.c | 2 +- drivers/{ => staging}/uwb/drp.c | 0 drivers/{ => staging}/uwb/est.c | 0 drivers/{ => staging}/uwb/hwa-rc.c | 6 ++-- drivers/{ => staging}/uwb/i1480/Makefile | 0 drivers/{ => staging}/uwb/i1480/dfu/Makefile | 0 drivers/{ => staging}/uwb/i1480/dfu/dfu.c | 2 +- .../{ => staging}/uwb/i1480/dfu/i1480-dfu.h | 2 +- drivers/{ => staging}/uwb/i1480/dfu/mac.c | 2 +- drivers/{ => staging}/uwb/i1480/dfu/phy.c | 2 +- drivers/{ => staging}/uwb/i1480/dfu/usb.c | 6 ++-- drivers/{ => staging}/uwb/i1480/i1480-est.c | 2 +- drivers/{ => staging}/uwb/ie-rcv.c | 0 drivers/{ => staging}/uwb/ie.c | 0 .../staging/uwb/include}/debug-cmd.h | 0 .../staging/uwb/include}/spec.h | 0 .../uwb => drivers/staging/uwb/include}/umc.h | 0 .../staging/uwb/include}/whci.h | 0 drivers/{ => staging}/uwb/lc-dev.c | 0 drivers/{ => staging}/uwb/lc-rc.c | 0 drivers/{ => staging}/uwb/neh.c | 0 drivers/{ => staging}/uwb/pal.c | 2 +- drivers/{ => staging}/uwb/radio.c | 2 +- drivers/{ => staging}/uwb/reset.c | 0 drivers/{ => staging}/uwb/rsv.c | 2 +- drivers/{ => staging}/uwb/scan.c | 0 drivers/{ => staging}/uwb/umc-bus.c | 2 +- drivers/{ => staging}/uwb/umc-dev.c | 2 +- drivers/{ => staging}/uwb/umc-drv.c | 2 +- drivers/{ => staging}/uwb/uwb-debug.c | 3 +- drivers/{ => staging}/uwb/uwb-internal.h | 2 +- {include/linux => drivers/staging/uwb}/uwb.h | 2 +- drivers/{ => staging}/uwb/uwbd.c | 0 drivers/{ => staging}/uwb/whc-rc.c | 6 ++-- drivers/{ => staging}/uwb/whci.c | 4 +-- .../staging/wusbcore/Documentation}/wusb-cbaf | 0 .../Documentation}/wusb-design-overview.rst | 0 drivers/{usb => staging}/wusbcore/Kconfig | 1 + drivers/{usb => staging}/wusbcore/Makefile | 2 ++ drivers/staging/wusbcore/TODO | 8 ++++++ drivers/{usb => staging}/wusbcore/cbaf.c | 6 ++-- drivers/{usb => staging}/wusbcore/crypto.c | 4 +-- drivers/{usb => staging}/wusbcore/dev-sysfs.c | 0 .../{usb => staging}/wusbcore/devconnect.c | 0 drivers/staging/wusbcore/host/Kconfig | 28 +++++++++++++++++++ drivers/staging/wusbcore/host/Makefile | 3 ++ .../{usb => staging/wusbcore}/host/hwa-hc.c | 4 +-- .../wusbcore}/host/whci/Makefile | 0 .../{usb => staging/wusbcore}/host/whci/asl.c | 4 +-- .../wusbcore}/host/whci/debug.c | 2 +- .../{usb => staging/wusbcore}/host/whci/hcd.c | 4 +-- .../{usb => staging/wusbcore}/host/whci/hw.c | 4 +-- .../wusbcore}/host/whci/init.c | 4 +-- .../{usb => staging/wusbcore}/host/whci/int.c | 4 +-- .../{usb => staging/wusbcore}/host/whci/pzl.c | 4 +-- .../wusbcore}/host/whci/qset.c | 4 +-- .../wusbcore}/host/whci/whcd.h | 4 +-- .../wusbcore}/host/whci/whci-hc.h | 0 .../wusbcore}/host/whci/wusb.c | 4 +-- .../staging/wusbcore/include}/association.h | 0 .../staging/wusbcore/include}/wusb-wa.h | 0 .../staging/wusbcore/include}/wusb.h | 2 +- drivers/{usb => staging}/wusbcore/mmc.c | 2 +- drivers/{usb => staging}/wusbcore/pal.c | 0 .../{usb => staging}/wusbcore/reservation.c | 2 +- drivers/{usb => staging}/wusbcore/rh.c | 0 drivers/{usb => staging}/wusbcore/security.c | 0 drivers/{usb => staging}/wusbcore/wa-hc.c | 0 drivers/{usb => staging}/wusbcore/wa-hc.h | 6 ++-- drivers/{usb => staging}/wusbcore/wa-nep.c | 0 drivers/{usb => staging}/wusbcore/wa-rpipe.c | 0 drivers/{usb => staging}/wusbcore/wa-xfer.c | 0 drivers/{usb => staging}/wusbcore/wusbhc.c | 0 drivers/{usb => staging}/wusbcore/wusbhc.h | 4 +-- drivers/usb/Kconfig | 2 -- drivers/usb/Makefile | 2 -- drivers/usb/host/Kconfig | 26 ----------------- drivers/usb/host/Makefile | 3 -- 91 files changed, 120 insertions(+), 109 deletions(-) rename drivers/{ => staging}/uwb/Kconfig (100%) rename drivers/{ => staging}/uwb/Makefile (100%) create mode 100644 drivers/staging/uwb/TODO rename drivers/{ => staging}/uwb/address.c (100%) rename drivers/{ => staging}/uwb/allocator.c (99%) rename drivers/{ => staging}/uwb/beacon.c (100%) rename drivers/{ => staging}/uwb/driver.c (100%) rename drivers/{ => staging}/uwb/drp-avail.c (100%) rename drivers/{ => staging}/uwb/drp-ie.c (99%) rename drivers/{ => staging}/uwb/drp.c (100%) rename drivers/{ => staging}/uwb/est.c (100%) rename drivers/{ => staging}/uwb/hwa-rc.c (99%) rename drivers/{ => staging}/uwb/i1480/Makefile (100%) rename drivers/{ => staging}/uwb/i1480/dfu/Makefile (100%) rename drivers/{ => staging}/uwb/i1480/dfu/dfu.c (99%) rename drivers/{ => staging}/uwb/i1480/dfu/i1480-dfu.h (99%) rename drivers/{ => staging}/uwb/i1480/dfu/mac.c (99%) rename drivers/{ => staging}/uwb/i1480/dfu/phy.c (99%) rename drivers/{ => staging}/uwb/i1480/dfu/usb.c (99%) rename drivers/{ => staging}/uwb/i1480/i1480-est.c (99%) rename drivers/{ => staging}/uwb/ie-rcv.c (100%) rename drivers/{ => staging}/uwb/ie.c (100%) rename {include/linux/uwb => drivers/staging/uwb/include}/debug-cmd.h (100%) rename {include/linux/uwb => drivers/staging/uwb/include}/spec.h (100%) rename {include/linux/uwb => drivers/staging/uwb/include}/umc.h (100%) rename {include/linux/uwb => drivers/staging/uwb/include}/whci.h (100%) rename drivers/{ => staging}/uwb/lc-dev.c (100%) rename drivers/{ => staging}/uwb/lc-rc.c (100%) rename drivers/{ => staging}/uwb/neh.c (100%) rename drivers/{ => staging}/uwb/pal.c (99%) rename drivers/{ => staging}/uwb/radio.c (99%) rename drivers/{ => staging}/uwb/reset.c (100%) rename drivers/{ => staging}/uwb/rsv.c (99%) rename drivers/{ => staging}/uwb/scan.c (100%) rename drivers/{ => staging}/uwb/umc-bus.c (99%) rename drivers/{ => staging}/uwb/umc-dev.c (98%) rename drivers/{ => staging}/uwb/umc-drv.c (96%) rename drivers/{ => staging}/uwb/uwb-debug.c (99%) rename drivers/{ => staging}/uwb/uwb-internal.h (99%) rename {include/linux => drivers/staging/uwb}/uwb.h (99%) rename drivers/{ => staging}/uwb/uwbd.c (100%) rename drivers/{ => staging}/uwb/whc-rc.c (99%) rename drivers/{ => staging}/uwb/whci.c (99%) rename {Documentation/usb => drivers/staging/wusbcore/Documentation}/wusb-cbaf (100%) rename {Documentation/usb => drivers/staging/wusbcore/Documentation}/wusb-design-overview.rst (100%) rename drivers/{usb => staging}/wusbcore/Kconfig (95%) rename drivers/{usb => staging}/wusbcore/Makefile (96%) create mode 100644 drivers/staging/wusbcore/TODO rename drivers/{usb => staging}/wusbcore/cbaf.c (99%) rename drivers/{usb => staging}/wusbcore/crypto.c (99%) rename drivers/{usb => staging}/wusbcore/dev-sysfs.c (100%) rename drivers/{usb => staging}/wusbcore/devconnect.c (100%) create mode 100644 drivers/staging/wusbcore/host/Kconfig create mode 100644 drivers/staging/wusbcore/host/Makefile rename drivers/{usb => staging/wusbcore}/host/hwa-hc.c (99%) rename drivers/{usb => staging/wusbcore}/host/whci/Makefile (100%) rename drivers/{usb => staging/wusbcore}/host/whci/asl.c (99%) rename drivers/{usb => staging/wusbcore}/host/whci/debug.c (99%) rename drivers/{usb => staging/wusbcore}/host/whci/hcd.c (99%) rename drivers/{usb => staging/wusbcore}/host/whci/hw.c (97%) rename drivers/{usb => staging/wusbcore}/host/whci/init.c (98%) rename drivers/{usb => staging/wusbcore}/host/whci/int.c (96%) rename drivers/{usb => staging/wusbcore}/host/whci/pzl.c (99%) rename drivers/{usb => staging/wusbcore}/host/whci/qset.c (99%) rename drivers/{usb => staging/wusbcore}/host/whci/whcd.h (98%) rename drivers/{usb => staging/wusbcore}/host/whci/whci-hc.h (100%) rename drivers/{usb => staging/wusbcore}/host/whci/wusb.c (98%) rename {include/linux/usb => drivers/staging/wusbcore/include}/association.h (100%) rename {include/linux/usb => drivers/staging/wusbcore/include}/wusb-wa.h (100%) rename {include/linux/usb => drivers/staging/wusbcore/include}/wusb.h (99%) rename drivers/{usb => staging}/wusbcore/mmc.c (99%) rename drivers/{usb => staging}/wusbcore/pal.c (100%) rename drivers/{usb => staging}/wusbcore/reservation.c (99%) rename drivers/{usb => staging}/wusbcore/rh.c (100%) rename drivers/{usb => staging}/wusbcore/security.c (100%) rename drivers/{usb => staging}/wusbcore/wa-hc.c (100%) rename drivers/{usb => staging}/wusbcore/wa-hc.h (99%) rename drivers/{usb => staging}/wusbcore/wa-nep.c (100%) rename drivers/{usb => staging}/wusbcore/wa-rpipe.c (100%) rename drivers/{usb => staging}/wusbcore/wa-xfer.c (100%) rename drivers/{usb => staging}/wusbcore/wusbhc.c (100%) rename drivers/{usb => staging}/wusbcore/wusbhc.h (99%) diff --git a/MAINTAINERS b/MAINTAINERS index 6426db5198f0..f4463fb48249 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3800,14 +3800,9 @@ F: scripts/sign-file.c F: scripts/extract-cert.c CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: -L: linux-usb@vger.kernel.org +L: devel@driverdev.osuosl.org S: Orphan -F: Documentation/usb/wusb-design-overview.rst -F: Documentation/usb/wusb-cbaf -F: drivers/usb/host/hwa-hc.c -F: drivers/usb/host/whci/ -F: drivers/usb/wusbcore/ -F: include/linux/usb/wusb* +F: drivers/staging/wbusbcore/ CFAG12864B LCD DRIVER M: Miguel Ojeda Sandonis @@ -16447,11 +16442,9 @@ F: drivers/usb/common/ulpi.c F: include/linux/ulpi/ ULTRA-WIDEBAND (UWB) SUBSYSTEM: -L: linux-usb@vger.kernel.org +L: devel@driverdev.osuosl.org S: Orphan -F: drivers/uwb/ -F: include/linux/uwb.h -F: include/linux/uwb/ +F: drivers/staging/uwb/ UNICODE SUBSYSTEM: M: Gabriel Krisman Bertazi diff --git a/drivers/Kconfig b/drivers/Kconfig index 61cf4ea2c229..e8852c09184b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -108,8 +108,6 @@ source "drivers/hid/Kconfig" source "drivers/usb/Kconfig" -source "drivers/uwb/Kconfig" - source "drivers/mmc/Kconfig" source "drivers/memstick/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 6d37564e783c..cf046e9bd88c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -100,7 +100,6 @@ obj-$(CONFIG_ZORRO) += zorro/ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ -obj-$(CONFIG_UWB) += uwb/ obj-$(CONFIG_USB_PHY) += usb/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_SUPPORT) += usb/ diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 7c96a01eef6c..cf419d9c942d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -120,4 +120,7 @@ source "drivers/staging/kpc2000/Kconfig" source "drivers/staging/isdn/Kconfig" +source "drivers/staging/wusbcore/Kconfig" +source "drivers/staging/uwb/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index fcaac9693b83..38179bc842a8 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -50,3 +50,5 @@ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ obj-$(CONFIG_KPC2000) += kpc2000/ obj-$(CONFIG_ISDN_CAPI) += isdn/ +obj-$(CONFIG_UWB) += uwb/ +obj-$(CONFIG_USB_WUSB) += wusbcore/ diff --git a/drivers/uwb/Kconfig b/drivers/staging/uwb/Kconfig similarity index 100% rename from drivers/uwb/Kconfig rename to drivers/staging/uwb/Kconfig diff --git a/drivers/uwb/Makefile b/drivers/staging/uwb/Makefile similarity index 100% rename from drivers/uwb/Makefile rename to drivers/staging/uwb/Makefile diff --git a/drivers/staging/uwb/TODO b/drivers/staging/uwb/TODO new file mode 100644 index 000000000000..abae57000534 --- /dev/null +++ b/drivers/staging/uwb/TODO @@ -0,0 +1,8 @@ +TODO: Remove in late 2019 unless there are users + +There seems to not be any real wireless USB devices anywhere in the wild +anymore. It turned out to be a failed technology :( + +This will be removed from the tree if no one objects. + +Greg Kroah-Hartman diff --git a/drivers/uwb/address.c b/drivers/staging/uwb/address.c similarity index 100% rename from drivers/uwb/address.c rename to drivers/staging/uwb/address.c diff --git a/drivers/uwb/allocator.c b/drivers/staging/uwb/allocator.c similarity index 99% rename from drivers/uwb/allocator.c rename to drivers/staging/uwb/allocator.c index 2e1590124d5f..1f429fba20b7 100644 --- a/drivers/uwb/allocator.c +++ b/drivers/staging/uwb/allocator.c @@ -6,7 +6,7 @@ */ #include #include -#include +#include "uwb.h" #include "uwb-internal.h" diff --git a/drivers/uwb/beacon.c b/drivers/staging/uwb/beacon.c similarity index 100% rename from drivers/uwb/beacon.c rename to drivers/staging/uwb/beacon.c diff --git a/drivers/uwb/driver.c b/drivers/staging/uwb/driver.c similarity index 100% rename from drivers/uwb/driver.c rename to drivers/staging/uwb/driver.c diff --git a/drivers/uwb/drp-avail.c b/drivers/staging/uwb/drp-avail.c similarity index 100% rename from drivers/uwb/drp-avail.c rename to drivers/staging/uwb/drp-avail.c diff --git a/drivers/uwb/drp-ie.c b/drivers/staging/uwb/drp-ie.c similarity index 99% rename from drivers/uwb/drp-ie.c rename to drivers/staging/uwb/drp-ie.c index 4b545b41161c..b2a862cf76de 100644 --- a/drivers/uwb/drp-ie.c +++ b/drivers/staging/uwb/drp-ie.c @@ -8,8 +8,8 @@ #include #include #include -#include +#include "uwb.h" #include "uwb-internal.h" diff --git a/drivers/uwb/drp.c b/drivers/staging/uwb/drp.c similarity index 100% rename from drivers/uwb/drp.c rename to drivers/staging/uwb/drp.c diff --git a/drivers/uwb/est.c b/drivers/staging/uwb/est.c similarity index 100% rename from drivers/uwb/est.c rename to drivers/staging/uwb/est.c diff --git a/drivers/uwb/hwa-rc.c b/drivers/staging/uwb/hwa-rc.c similarity index 99% rename from drivers/uwb/hwa-rc.c rename to drivers/staging/uwb/hwa-rc.c index cd03b7f827c1..b6effad749d7 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/staging/uwb/hwa-rc.c @@ -38,9 +38,9 @@ #include #include #include -#include -#include -#include +#include "../wusbcore/include/wusb.h" +#include "../wusbcore/include/wusb-wa.h" +#include "uwb.h" #include "uwb-internal.h" diff --git a/drivers/uwb/i1480/Makefile b/drivers/staging/uwb/i1480/Makefile similarity index 100% rename from drivers/uwb/i1480/Makefile rename to drivers/staging/uwb/i1480/Makefile diff --git a/drivers/uwb/i1480/dfu/Makefile b/drivers/staging/uwb/i1480/dfu/Makefile similarity index 100% rename from drivers/uwb/i1480/dfu/Makefile rename to drivers/staging/uwb/i1480/dfu/Makefile diff --git a/drivers/uwb/i1480/dfu/dfu.c b/drivers/staging/uwb/i1480/dfu/dfu.c similarity index 99% rename from drivers/uwb/i1480/dfu/dfu.c rename to drivers/staging/uwb/i1480/dfu/dfu.c index ec1af858ead9..9d51ce8faad1 100644 --- a/drivers/uwb/i1480/dfu/dfu.c +++ b/drivers/staging/uwb/i1480/dfu/dfu.c @@ -17,9 +17,9 @@ #include #include #include -#include #include #include +#include "../../uwb.h" /* * i1480_rceb_check - Check RCEB for expected field values diff --git a/drivers/uwb/i1480/dfu/i1480-dfu.h b/drivers/staging/uwb/i1480/dfu/i1480-dfu.h similarity index 99% rename from drivers/uwb/i1480/dfu/i1480-dfu.h rename to drivers/staging/uwb/i1480/dfu/i1480-dfu.h index 9dd567d174b3..b21d058ecc23 100644 --- a/drivers/uwb/i1480/dfu/i1480-dfu.h +++ b/drivers/staging/uwb/i1480/dfu/i1480-dfu.h @@ -50,9 +50,9 @@ #ifndef __i1480_DFU_H__ #define __i1480_DFU_H__ -#include #include #include +#include "../../include/spec.h" #define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018)) diff --git a/drivers/uwb/i1480/dfu/mac.c b/drivers/staging/uwb/i1480/dfu/mac.c similarity index 99% rename from drivers/uwb/i1480/dfu/mac.c rename to drivers/staging/uwb/i1480/dfu/mac.c index ddc224f01a7f..6e4d6c9cecf5 100644 --- a/drivers/uwb/i1480/dfu/mac.c +++ b/drivers/staging/uwb/i1480/dfu/mac.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include "../../uwb.h" #include "i1480-dfu.h" /* diff --git a/drivers/uwb/i1480/dfu/phy.c b/drivers/staging/uwb/i1480/dfu/phy.c similarity index 99% rename from drivers/uwb/i1480/dfu/phy.c rename to drivers/staging/uwb/i1480/dfu/phy.c index 50da4527c113..13512c7dda0b 100644 --- a/drivers/uwb/i1480/dfu/phy.c +++ b/drivers/staging/uwb/i1480/dfu/phy.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include "../../../wusbcore/include/wusb.h" #include "i1480-dfu.h" diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/staging/uwb/i1480/dfu/usb.c similarity index 99% rename from drivers/uwb/i1480/dfu/usb.c rename to drivers/staging/uwb/i1480/dfu/usb.c index 6129a8f4b5f2..d41086bdd783 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/staging/uwb/i1480/dfu/usb.c @@ -25,9 +25,9 @@ #include #include #include -#include -#include -#include +#include "../../uwb.h" +#include "../../../wusbcore/include/wusb.h" +#include "../../../wusbcore/include/wusb-wa.h" #include "i1480-dfu.h" struct i1480_usb { diff --git a/drivers/uwb/i1480/i1480-est.c b/drivers/staging/uwb/i1480/i1480-est.c similarity index 99% rename from drivers/uwb/i1480/i1480-est.c rename to drivers/staging/uwb/i1480/i1480-est.c index 1346c409d10e..106e0a44b138 100644 --- a/drivers/uwb/i1480/i1480-est.c +++ b/drivers/staging/uwb/i1480/i1480-est.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include "../uwb.h" #include "dfu/i1480-dfu.h" diff --git a/drivers/uwb/ie-rcv.c b/drivers/staging/uwb/ie-rcv.c similarity index 100% rename from drivers/uwb/ie-rcv.c rename to drivers/staging/uwb/ie-rcv.c diff --git a/drivers/uwb/ie.c b/drivers/staging/uwb/ie.c similarity index 100% rename from drivers/uwb/ie.c rename to drivers/staging/uwb/ie.c diff --git a/include/linux/uwb/debug-cmd.h b/drivers/staging/uwb/include/debug-cmd.h similarity index 100% rename from include/linux/uwb/debug-cmd.h rename to drivers/staging/uwb/include/debug-cmd.h diff --git a/include/linux/uwb/spec.h b/drivers/staging/uwb/include/spec.h similarity index 100% rename from include/linux/uwb/spec.h rename to drivers/staging/uwb/include/spec.h diff --git a/include/linux/uwb/umc.h b/drivers/staging/uwb/include/umc.h similarity index 100% rename from include/linux/uwb/umc.h rename to drivers/staging/uwb/include/umc.h diff --git a/include/linux/uwb/whci.h b/drivers/staging/uwb/include/whci.h similarity index 100% rename from include/linux/uwb/whci.h rename to drivers/staging/uwb/include/whci.h diff --git a/drivers/uwb/lc-dev.c b/drivers/staging/uwb/lc-dev.c similarity index 100% rename from drivers/uwb/lc-dev.c rename to drivers/staging/uwb/lc-dev.c diff --git a/drivers/uwb/lc-rc.c b/drivers/staging/uwb/lc-rc.c similarity index 100% rename from drivers/uwb/lc-rc.c rename to drivers/staging/uwb/lc-rc.c diff --git a/drivers/uwb/neh.c b/drivers/staging/uwb/neh.c similarity index 100% rename from drivers/uwb/neh.c rename to drivers/staging/uwb/neh.c diff --git a/drivers/uwb/pal.c b/drivers/staging/uwb/pal.c similarity index 99% rename from drivers/uwb/pal.c rename to drivers/staging/uwb/pal.c index 765fd426dbd1..a541e646a603 100644 --- a/drivers/uwb/pal.c +++ b/drivers/staging/uwb/pal.c @@ -6,9 +6,9 @@ */ #include #include -#include #include +#include "uwb.h" #include "uwb-internal.h" /** diff --git a/drivers/uwb/radio.c b/drivers/staging/uwb/radio.c similarity index 99% rename from drivers/uwb/radio.c rename to drivers/staging/uwb/radio.c index 240dd755927e..6afb75ce1b5f 100644 --- a/drivers/uwb/radio.c +++ b/drivers/staging/uwb/radio.c @@ -5,9 +5,9 @@ * Copyright (C) 2008 Cambridge Silicon Radio Ltd. */ #include -#include #include +#include "uwb.h" #include "uwb-internal.h" diff --git a/drivers/uwb/reset.c b/drivers/staging/uwb/reset.c similarity index 100% rename from drivers/uwb/reset.c rename to drivers/staging/uwb/reset.c diff --git a/drivers/uwb/rsv.c b/drivers/staging/uwb/rsv.c similarity index 99% rename from drivers/uwb/rsv.c rename to drivers/staging/uwb/rsv.c index ec924deb0a32..f45a04ff7275 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/staging/uwb/rsv.c @@ -5,11 +5,11 @@ * Copyright (C) 2008 Cambridge Silicon Radio Ltd. */ #include -#include #include #include #include +#include "uwb.h" #include "uwb-internal.h" static void uwb_rsv_timer(struct timer_list *t); diff --git a/drivers/uwb/scan.c b/drivers/staging/uwb/scan.c similarity index 100% rename from drivers/uwb/scan.c rename to drivers/staging/uwb/scan.c diff --git a/drivers/uwb/umc-bus.c b/drivers/staging/uwb/umc-bus.c similarity index 99% rename from drivers/uwb/umc-bus.c rename to drivers/staging/uwb/umc-bus.c index 0fdc38078eee..8b931f66a720 100644 --- a/drivers/uwb/umc-bus.c +++ b/drivers/staging/uwb/umc-bus.c @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include "include/umc.h" static int umc_bus_pre_reset_helper(struct device *dev, void *data) { diff --git a/drivers/uwb/umc-dev.c b/drivers/staging/uwb/umc-dev.c similarity index 98% rename from drivers/uwb/umc-dev.c rename to drivers/staging/uwb/umc-dev.c index c845ca414bb2..0c71caae00be 100644 --- a/drivers/uwb/umc-dev.c +++ b/drivers/staging/uwb/umc-dev.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include "include/umc.h" static void umc_device_release(struct device *dev) { diff --git a/drivers/uwb/umc-drv.c b/drivers/staging/uwb/umc-drv.c similarity index 96% rename from drivers/uwb/umc-drv.c rename to drivers/staging/uwb/umc-drv.c index b141d520efbf..ed3bd220e8c2 100644 --- a/drivers/uwb/umc-drv.c +++ b/drivers/staging/uwb/umc-drv.c @@ -6,7 +6,7 @@ */ #include #include -#include +#include "include/umc.h" int __umc_driver_register(struct umc_driver *umc_drv, struct module *module, const char *mod_name) diff --git a/drivers/uwb/uwb-debug.c b/drivers/staging/uwb/uwb-debug.c similarity index 99% rename from drivers/uwb/uwb-debug.c rename to drivers/staging/uwb/uwb-debug.c index 5457b6d42387..dd14df219ef8 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/staging/uwb/uwb-debug.c @@ -19,8 +19,7 @@ #include #include -#include - +#include "include/debug-cmd.h" #include "uwb-internal.h" /* diff --git a/drivers/uwb/uwb-internal.h b/drivers/staging/uwb/uwb-internal.h similarity index 99% rename from drivers/uwb/uwb-internal.h rename to drivers/staging/uwb/uwb-internal.h index 00de0a5333d2..4c2fdac7f610 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/staging/uwb/uwb-internal.h @@ -17,8 +17,8 @@ #include #include -#include #include +#include "uwb.h" struct uwb_beca_e; diff --git a/include/linux/uwb.h b/drivers/staging/uwb/uwb.h similarity index 99% rename from include/linux/uwb.h rename to drivers/staging/uwb/uwb.h index 6918a61e1ac1..6a59706ba3a0 100644 --- a/include/linux/uwb.h +++ b/drivers/staging/uwb/uwb.h @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include "include/spec.h" struct uwb_dev; struct uwb_beca_e; diff --git a/drivers/uwb/uwbd.c b/drivers/staging/uwb/uwbd.c similarity index 100% rename from drivers/uwb/uwbd.c rename to drivers/staging/uwb/uwbd.c diff --git a/drivers/uwb/whc-rc.c b/drivers/staging/uwb/whc-rc.c similarity index 99% rename from drivers/uwb/whc-rc.c rename to drivers/staging/uwb/whc-rc.c index 22397f70dee2..34020ed351ab 100644 --- a/drivers/uwb/whc-rc.c +++ b/drivers/staging/uwb/whc-rc.c @@ -33,9 +33,9 @@ #include #include #include -#include -#include -#include +#include "uwb.h" +#include "include/whci.h" +#include "include/umc.h" #include "uwb-internal.h" diff --git a/drivers/uwb/whci.c b/drivers/staging/uwb/whci.c similarity index 99% rename from drivers/uwb/whci.c rename to drivers/staging/uwb/whci.c index be8a8b8e857b..a8832f64d708 100644 --- a/drivers/uwb/whci.c +++ b/drivers/staging/uwb/whci.c @@ -10,8 +10,8 @@ #include #include #include -#include -#include +#include "include/whci.h" +#include "include/umc.h" struct whci_card { struct pci_dev *pci; diff --git a/Documentation/usb/wusb-cbaf b/drivers/staging/wusbcore/Documentation/wusb-cbaf similarity index 100% rename from Documentation/usb/wusb-cbaf rename to drivers/staging/wusbcore/Documentation/wusb-cbaf diff --git a/Documentation/usb/wusb-design-overview.rst b/drivers/staging/wusbcore/Documentation/wusb-design-overview.rst similarity index 100% rename from Documentation/usb/wusb-design-overview.rst rename to drivers/staging/wusbcore/Documentation/wusb-design-overview.rst diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/staging/wusbcore/Kconfig similarity index 95% rename from drivers/usb/wusbcore/Kconfig rename to drivers/staging/wusbcore/Kconfig index abc0f361021f..056c60b4d57f 100644 --- a/drivers/usb/wusbcore/Kconfig +++ b/drivers/staging/wusbcore/Kconfig @@ -36,3 +36,4 @@ config USB_WUSB_CBAF_DEBUG to the system log. Select this if you are having a problem with CBA support and want to see more of what is going on. +source "drivers/staging/wusbcore/host/Kconfig" diff --git a/drivers/usb/wusbcore/Makefile b/drivers/staging/wusbcore/Makefile similarity index 96% rename from drivers/usb/wusbcore/Makefile rename to drivers/staging/wusbcore/Makefile index d604ccdd916f..b47b874268ac 100644 --- a/drivers/usb/wusbcore/Makefile +++ b/drivers/staging/wusbcore/Makefile @@ -24,3 +24,5 @@ wusb-wa-y := \ wa-nep.o \ wa-rpipe.o \ wa-xfer.o + +obj-y += host/ diff --git a/drivers/staging/wusbcore/TODO b/drivers/staging/wusbcore/TODO new file mode 100644 index 000000000000..abae57000534 --- /dev/null +++ b/drivers/staging/wusbcore/TODO @@ -0,0 +1,8 @@ +TODO: Remove in late 2019 unless there are users + +There seems to not be any real wireless USB devices anywhere in the wild +anymore. It turned out to be a failed technology :( + +This will be removed from the tree if no one objects. + +Greg Kroah-Hartman diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/staging/wusbcore/cbaf.c similarity index 99% rename from drivers/usb/wusbcore/cbaf.c rename to drivers/staging/wusbcore/cbaf.c index af77064c7456..57062eaf7558 100644 --- a/drivers/usb/wusbcore/cbaf.c +++ b/drivers/staging/wusbcore/cbaf.c @@ -80,9 +80,9 @@ #include #include #include -#include -#include -#include +#include "../uwb/uwb.h" +#include "include/wusb.h" +#include "include/association.h" #define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */ diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/staging/wusbcore/crypto.c similarity index 99% rename from drivers/usb/wusbcore/crypto.c rename to drivers/staging/wusbcore/crypto.c index 9ee66483ee54..d7d55ed19a98 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/staging/wusbcore/crypto.c @@ -38,10 +38,10 @@ #include #include #include -#include #include -#include #include +#include "../uwb/uwb.h" +#include "include/wusb.h" static int debug_crypto_verify; diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/staging/wusbcore/dev-sysfs.c similarity index 100% rename from drivers/usb/wusbcore/dev-sysfs.c rename to drivers/staging/wusbcore/dev-sysfs.c diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/staging/wusbcore/devconnect.c similarity index 100% rename from drivers/usb/wusbcore/devconnect.c rename to drivers/staging/wusbcore/devconnect.c diff --git a/drivers/staging/wusbcore/host/Kconfig b/drivers/staging/wusbcore/host/Kconfig new file mode 100644 index 000000000000..9a73f9360a08 --- /dev/null +++ b/drivers/staging/wusbcore/host/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 + +config USB_WHCI_HCD + tristate "Wireless USB Host Controller Interface (WHCI) driver" + depends on USB_PCI && USB && UWB + select USB_WUSB + select UWB_WHCI + help + A driver for PCI-based Wireless USB Host Controllers that are + compliant with the WHCI specification. + + To compile this driver a module, choose M here: the module + will be called "whci-hcd". + +config USB_HWA_HCD + tristate "Host Wire Adapter (HWA) driver" + depends on USB && UWB + select USB_WUSB + select UWB_HWA + help + This driver enables you to connect Wireless USB devices to + your system using a Host Wire Adaptor USB dongle. This is an + UWB Radio Controller and WUSB Host Controller connected to + your machine via USB (specified in WUSB1.0). + + To compile this driver a module, choose M here: the module + will be called "hwa-hc". + diff --git a/drivers/staging/wusbcore/host/Makefile b/drivers/staging/wusbcore/host/Makefile new file mode 100644 index 000000000000..d65ee8a73e21 --- /dev/null +++ b/drivers/staging/wusbcore/host/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_USB_WHCI_HCD) += whci/ +obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o diff --git a/drivers/usb/host/hwa-hc.c b/drivers/staging/wusbcore/host/hwa-hc.c similarity index 99% rename from drivers/usb/host/hwa-hc.c rename to drivers/staging/wusbcore/host/hwa-hc.c index 6968b9f2b76b..8d959e91fe27 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/staging/wusbcore/host/hwa-hc.c @@ -45,8 +45,8 @@ #include #include #include -#include "../wusbcore/wa-hc.h" -#include "../wusbcore/wusbhc.h" +#include "../wa-hc.h" +#include "../wusbhc.h" struct hwahc { struct wusbhc wusbhc; /* has to be 1st */ diff --git a/drivers/usb/host/whci/Makefile b/drivers/staging/wusbcore/host/whci/Makefile similarity index 100% rename from drivers/usb/host/whci/Makefile rename to drivers/staging/wusbcore/host/whci/Makefile diff --git a/drivers/usb/host/whci/asl.c b/drivers/staging/wusbcore/host/whci/asl.c similarity index 99% rename from drivers/usb/host/whci/asl.c rename to drivers/staging/wusbcore/host/whci/asl.c index 276fb34c8efd..a2b9a50cfb80 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/staging/wusbcore/host/whci/asl.c @@ -7,10 +7,10 @@ #include #include #include -#include #include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/debug.c b/drivers/staging/wusbcore/host/whci/debug.c similarity index 99% rename from drivers/usb/host/whci/debug.c rename to drivers/staging/wusbcore/host/whci/debug.c index 8ddfe3f1f693..443da6719147 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/staging/wusbcore/host/whci/debug.c @@ -10,7 +10,7 @@ #include #include -#include "../../wusbcore/wusbhc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/hcd.c b/drivers/staging/wusbcore/host/whci/hcd.c similarity index 99% rename from drivers/usb/host/whci/hcd.c rename to drivers/staging/wusbcore/host/whci/hcd.c index 8af9dcfea127..bee1ff2d35be 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/staging/wusbcore/host/whci/hcd.c @@ -7,9 +7,9 @@ #include #include #include -#include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/hw.c b/drivers/staging/wusbcore/host/whci/hw.c similarity index 97% rename from drivers/usb/host/whci/hw.c rename to drivers/staging/wusbcore/host/whci/hw.c index 22b3b7f7419d..e4e8914abf42 100644 --- a/drivers/usb/host/whci/hw.c +++ b/drivers/staging/wusbcore/host/whci/hw.c @@ -6,9 +6,9 @@ */ #include #include -#include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/init.c b/drivers/staging/wusbcore/host/whci/init.c similarity index 98% rename from drivers/usb/host/whci/init.c rename to drivers/staging/wusbcore/host/whci/init.c index 82416973f773..55fd458a8f30 100644 --- a/drivers/usb/host/whci/init.c +++ b/drivers/staging/wusbcore/host/whci/init.c @@ -7,9 +7,9 @@ #include #include #include -#include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/int.c b/drivers/staging/wusbcore/host/whci/int.c similarity index 96% rename from drivers/usb/host/whci/int.c rename to drivers/staging/wusbcore/host/whci/int.c index 7e4ad1b8f3e3..bdbe35e9366f 100644 --- a/drivers/usb/host/whci/int.c +++ b/drivers/staging/wusbcore/host/whci/int.c @@ -5,9 +5,9 @@ * Copyright (C) 2007 Cambridge Silicon Radio Ltd. */ #include -#include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/pzl.c b/drivers/staging/wusbcore/host/whci/pzl.c similarity index 99% rename from drivers/usb/host/whci/pzl.c rename to drivers/staging/wusbcore/host/whci/pzl.c index ef52aeb02fde..6dfc075f5798 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/staging/wusbcore/host/whci/pzl.c @@ -7,10 +7,10 @@ #include #include #include -#include #include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/qset.c b/drivers/staging/wusbcore/host/whci/qset.c similarity index 99% rename from drivers/usb/host/whci/qset.c rename to drivers/staging/wusbcore/host/whci/qset.c index 925166a207aa..66459b77dc77 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/staging/wusbcore/host/whci/qset.c @@ -7,10 +7,10 @@ #include #include #include -#include #include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/drivers/usb/host/whci/whcd.h b/drivers/staging/wusbcore/host/whci/whcd.h similarity index 98% rename from drivers/usb/host/whci/whcd.h rename to drivers/staging/wusbcore/host/whci/whcd.h index 139476997e7c..a442a2589e83 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/staging/wusbcore/host/whci/whcd.h @@ -7,10 +7,10 @@ #ifndef __WHCD_H #define __WHCD_H -#include -#include #include +#include "../../../uwb/include/whci.h" +#include "../../../uwb/include/umc.h" #include "whci-hc.h" /* Generic command timeout. */ diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/staging/wusbcore/host/whci/whci-hc.h similarity index 100% rename from drivers/usb/host/whci/whci-hc.h rename to drivers/staging/wusbcore/host/whci/whci-hc.h diff --git a/drivers/usb/host/whci/wusb.c b/drivers/staging/wusbcore/host/whci/wusb.c similarity index 98% rename from drivers/usb/host/whci/wusb.c rename to drivers/staging/wusbcore/host/whci/wusb.c index 8a4d805ff63a..6d0068ab35e4 100644 --- a/drivers/usb/host/whci/wusb.c +++ b/drivers/staging/wusbcore/host/whci/wusb.c @@ -5,9 +5,9 @@ * Copyright (C) 2007 Cambridge Silicon Radio Ltd. */ #include -#include -#include "../../wusbcore/wusbhc.h" +#include "../../../uwb/include/umc.h" +#include "../../wusbhc.h" #include "whcd.h" diff --git a/include/linux/usb/association.h b/drivers/staging/wusbcore/include/association.h similarity index 100% rename from include/linux/usb/association.h rename to drivers/staging/wusbcore/include/association.h diff --git a/include/linux/usb/wusb-wa.h b/drivers/staging/wusbcore/include/wusb-wa.h similarity index 100% rename from include/linux/usb/wusb-wa.h rename to drivers/staging/wusbcore/include/wusb-wa.h diff --git a/include/linux/usb/wusb.h b/drivers/staging/wusbcore/include/wusb.h similarity index 99% rename from include/linux/usb/wusb.h rename to drivers/staging/wusbcore/include/wusb.h index 65adee629106..09771d1da7bc 100644 --- a/include/linux/usb/wusb.h +++ b/drivers/staging/wusbcore/include/wusb.h @@ -33,9 +33,9 @@ #include #include -#include #include #include +#include "../../uwb/include/spec.h" /** * WUSB Information Element header diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/staging/wusbcore/mmc.c similarity index 99% rename from drivers/usb/wusbcore/mmc.c rename to drivers/staging/wusbcore/mmc.c index acce0d551eb2..881e1f20d718 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/staging/wusbcore/mmc.c @@ -22,9 +22,9 @@ * FIXME: * - add timers that autoremove intervalled IEs? */ -#include #include #include +#include "include/wusb.h" #include "wusbhc.h" /* Initialize the MMCIEs handling mechanism */ diff --git a/drivers/usb/wusbcore/pal.c b/drivers/staging/wusbcore/pal.c similarity index 100% rename from drivers/usb/wusbcore/pal.c rename to drivers/staging/wusbcore/pal.c diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/staging/wusbcore/reservation.c similarity index 99% rename from drivers/usb/wusbcore/reservation.c rename to drivers/staging/wusbcore/reservation.c index 6dcfc6825f55..b921faac698b 100644 --- a/drivers/usb/wusbcore/reservation.c +++ b/drivers/staging/wusbcore/reservation.c @@ -5,8 +5,8 @@ * Copyright (C) 2007 Cambridge Silicon Radio Ltd. */ #include -#include +#include "../uwb/uwb.h" #include "wusbhc.h" /* diff --git a/drivers/usb/wusbcore/rh.c b/drivers/staging/wusbcore/rh.c similarity index 100% rename from drivers/usb/wusbcore/rh.c rename to drivers/staging/wusbcore/rh.c diff --git a/drivers/usb/wusbcore/security.c b/drivers/staging/wusbcore/security.c similarity index 100% rename from drivers/usb/wusbcore/security.c rename to drivers/staging/wusbcore/security.c diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/staging/wusbcore/wa-hc.c similarity index 100% rename from drivers/usb/wusbcore/wa-hc.c rename to drivers/staging/wusbcore/wa-hc.c diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/staging/wusbcore/wa-hc.h similarity index 99% rename from drivers/usb/wusbcore/wa-hc.h rename to drivers/staging/wusbcore/wa-hc.h index ec90fff21deb..5a38465724c2 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/staging/wusbcore/wa-hc.h @@ -70,9 +70,9 @@ #include #include #include -#include -#include -#include +#include "../uwb/uwb.h" +#include "include/wusb.h" +#include "include/wusb-wa.h" struct wusbhc; struct wahc; diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/staging/wusbcore/wa-nep.c similarity index 100% rename from drivers/usb/wusbcore/wa-nep.c rename to drivers/staging/wusbcore/wa-nep.c diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/staging/wusbcore/wa-rpipe.c similarity index 100% rename from drivers/usb/wusbcore/wa-rpipe.c rename to drivers/staging/wusbcore/wa-rpipe.c diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/staging/wusbcore/wa-xfer.c similarity index 100% rename from drivers/usb/wusbcore/wa-xfer.c rename to drivers/staging/wusbcore/wa-xfer.c diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/staging/wusbcore/wusbhc.c similarity index 100% rename from drivers/usb/wusbcore/wusbhc.c rename to drivers/staging/wusbcore/wusbhc.c diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/staging/wusbcore/wusbhc.h similarity index 99% rename from drivers/usb/wusbcore/wusbhc.h rename to drivers/staging/wusbcore/wusbhc.h index 7681d796ca5b..716244a2ec44 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/staging/wusbcore/wusbhc.h @@ -45,8 +45,8 @@ #include #include #include -#include -#include +#include "../uwb/uwb.h" +#include "include/wusb.h" /* * Time from a WUSB channel stop request to the last transmitted MMC. diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 6e59d370ef81..9987c399819f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -98,8 +98,6 @@ source "drivers/usb/core/Kconfig" source "drivers/usb/mon/Kconfig" -source "drivers/usb/wusbcore/Kconfig" - source "drivers/usb/host/Kconfig" source "drivers/usb/renesas_usbhs/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ecc2de1ffaae..db064dd59e08 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -35,8 +35,6 @@ obj-$(CONFIG_USB_MAX3421_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ -obj-$(CONFIG_USB_WUSB) += wusbcore/ - obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_PRINTER) += class/ obj-$(CONFIG_USB_WDM) += class/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 40b5de597112..d040408f5baa 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -717,32 +717,6 @@ config USB_RENESAS_USBHS_HCD To compile this driver as a module, choose M here: the module will be called renesas-usbhs. -config USB_WHCI_HCD - tristate "Wireless USB Host Controller Interface (WHCI) driver" - depends on USB_PCI && USB && UWB - select USB_WUSB - select UWB_WHCI - help - A driver for PCI-based Wireless USB Host Controllers that are - compliant with the WHCI specification. - - To compile this driver a module, choose M here: the module - will be called "whci-hcd". - -config USB_HWA_HCD - tristate "Host Wire Adapter (HWA) driver" - depends on USB && UWB - select USB_WUSB - select UWB_HWA - help - This driver enables you to connect Wireless USB devices to - your system using a Host Wire Adaptor USB dongle. This is an - UWB Radio Controller and WUSB Host Controller connected to - your machine via USB (specified in WUSB1.0). - - To compile this driver a module, choose M here: the module - will be called "hwa-hc". - config USB_IMX21_HCD tristate "i.MX21 HCD support" depends on ARM && ARCH_MXC diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 84514f71ae44..59b39e6b350b 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -35,8 +35,6 @@ ifneq ($(CONFIG_DEBUG_FS),) xhci-hcd-y += xhci-debugfs.o endif -obj-$(CONFIG_USB_WHCI_HCD) += whci/ - obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o @@ -82,7 +80,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o -obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o From d3b5e319a19e6c6b612528c57c4ad55cb10273a9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2019 21:36:32 +0200 Subject: [PATCH 028/159] USB: musb: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Acked-by: Bin Liu Link: https://lore.kernel.org/r/20190805193636.25560-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 2bc55e0ceace..bd63450af76a 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1829,16 +1829,13 @@ static ssize_t srp_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_WO(srp); -static struct attribute *musb_attributes[] = { +static struct attribute *musb_attrs[] = { &dev_attr_mode.attr, &dev_attr_vbus.attr, &dev_attr_srp.attr, NULL }; - -static const struct attribute_group musb_attr_group = { - .attrs = musb_attributes, -}; +ATTRIBUTE_GROUPS(musb); #define MUSB_QUIRK_B_INVALID_VBUS_91 (MUSB_DEVCTL_BDEVICE | \ (2 << MUSB_DEVCTL_VBUS_SHIFT) | \ @@ -2038,10 +2035,6 @@ static void musb_free(struct musb *musb) * cleanup after everything's been de-activated. */ -#ifdef CONFIG_SYSFS - sysfs_remove_group(&musb->controller->kobj, &musb_attr_group); -#endif - if (musb->nIrq >= 0) { if (musb->irq_wake) disable_irq_wake(musb->nIrq); @@ -2390,22 +2383,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_init_debugfs(musb); - status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); - if (status) - goto fail5; - musb->is_initialized = 1; pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; -fail5: - musb_exit_debugfs(musb); - - musb_gadget_cleanup(musb); - musb_host_cleanup(musb); - fail3: cancel_delayed_work_sync(&musb->irq_work); cancel_delayed_work_sync(&musb->finish_resume_work); @@ -2798,6 +2781,7 @@ static struct platform_driver musb_driver = { .name = (char *)musb_driver_name, .bus = &platform_bus_type, .pm = MUSB_DEV_PM_OPS, + .dev_groups = musb_groups, }, .probe = musb_probe, .remove = musb_remove, From 2e18b14e164213e608cb609430c17a6fe2ba894c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2019 21:36:36 +0200 Subject: [PATCH 029/159] USB: typec: ucsi_ccg: convert i2c driver to use dev_groups The driver core now supports the option to automatically create and remove any needed sysfs attribute files for a driver when the device is bound/removed from it. Convert the uscsi_ccg code to use that instead of trying to create sysfs files "by hand". Cc: Ajay Gupta Cc: Wolfram Sang Cc: Wei Yongjun Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190805193636.25560-6-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index f7a79a23ebed..e283a42e4f06 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1104,14 +1104,11 @@ static ssize_t do_flash_store(struct device *dev, static DEVICE_ATTR_WO(do_flash); -static struct attribute *ucsi_ccg_sysfs_attrs[] = { +static struct attribute *ucsi_ccg_attrs[] = { &dev_attr_do_flash.attr, NULL, }; - -static struct attribute_group ucsi_ccg_attr_group = { - .attrs = ucsi_ccg_sysfs_attrs, -}; +ATTRIBUTE_GROUPS(ucsi_ccg); static int ucsi_ccg_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1189,10 +1186,6 @@ static int ucsi_ccg_probe(struct i2c_client *client, i2c_set_clientdata(client, uc); - status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group); - if (status) - dev_err(uc->dev, "cannot create sysfs group: %d\n", status); - pm_runtime_set_active(uc->dev); pm_runtime_enable(uc->dev); pm_runtime_idle(uc->dev); @@ -1209,7 +1202,6 @@ static int ucsi_ccg_remove(struct i2c_client *client) ucsi_unregister_ppm(uc->ucsi); pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); - sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); return 0; } @@ -1270,6 +1262,7 @@ static struct i2c_driver ucsi_ccg_driver = { .driver = { .name = "ucsi_ccg", .pm = &ucsi_ccg_pm, + .dev_groups = ucsi_ccg_groups, }, .probe = ucsi_ccg_probe, .remove = ucsi_ccg_remove, From b6fd948832496f01750d6c87dfdcef713043da4c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Aug 2019 11:25:09 +0200 Subject: [PATCH 030/159] MAINTAINERS: mark wusbcore and UWB as obsolete Joe rightly points out that we should be using the "Obsolete" status for these two subsystems. Also I got the path name wrong for the wusbcore tree. Reported-by: Joe Perches Fixes: 71ed79b0e4be ("USB: Move wusbcore and UWB to staging as it is obsolete") Link: https://lore.kernel.org/r/20190808092509.GA20173@kroah.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f4463fb48249..6f2d988fe7b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3801,8 +3801,8 @@ F: scripts/extract-cert.c CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: L: devel@driverdev.osuosl.org -S: Orphan -F: drivers/staging/wbusbcore/ +S: Obsolete +F: drivers/staging/wusbcore/ CFAG12864B LCD DRIVER M: Miguel Ojeda Sandonis @@ -16443,7 +16443,7 @@ F: include/linux/ulpi/ ULTRA-WIDEBAND (UWB) SUBSYSTEM: L: devel@driverdev.osuosl.org -S: Orphan +S: Obsolete F: drivers/staging/uwb/ UNICODE SUBSYSTEM: From dccce749ac66aa01593273d86cf3c55542ce6fc0 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 5 Aug 2019 14:14:26 -0500 Subject: [PATCH 031/159] USB: gadget: udc: s3c2410_udc: Mark expected switch fall-throughs Mark switch cases where we are expecting to fall through. This patch fixes the following warning (Building: tct_hammer_defconfig arm): drivers/usb/gadget/udc/s3c2410_udc.c:314:7: warning: this statement may fall through [-Wimplicit-fallthrough=] drivers/usb/gadget/udc/s3c2410_udc.c:418:7: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/s3c2410_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index af3e63316ace..f82208fbc249 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -312,6 +312,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, switch (idx) { default: idx = 0; + /* fall through */ case 0: fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; @@ -416,6 +417,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, switch (idx) { default: idx = 0; + /* fall through */ case 0: fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; From 27125cf8b5aee9f62dd108051203ccbd2e426b51 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 5 Aug 2019 13:48:42 -0500 Subject: [PATCH 032/159] usb: gadget: atmel_usba_udc: Mark expected switch fall-through Mark switch cases where we are expecting to fall through. This patch fixes the following warning (Building: at91_dt_defconfig arm): drivers/usb/gadget/udc/atmel_usba_udc.c:329:13: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 503d275bc4c4..86ffc8307864 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -327,6 +327,7 @@ static int usba_config_fifo_table(struct usba_udc *udc) switch (fifo_mode) { default: fifo_mode = 0; + /* fall through */ case 0: udc->fifo_cfg = NULL; n = 0; From 37bea42fec575203c1b49d64322f717c29e82b90 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 5 Aug 2019 17:01:21 +0100 Subject: [PATCH 033/159] usb: dwc2: gadget: Fix kill_all_requests race When a gadget is disabled, kill_all_requests() can be called simultaneously from both a user process via dwc2_hsotg_pullup() and from the interrupt handler if the hardware detects disconnection. Since we drop the lock in dwc2_hsotg_complete_request() in order to call the completion handler, this means that the list is modified concurrently and leads to an infinite loop in kill_all_requests(). Replace the foreach loop with a while-not-empty loop in order to remove the danger of this concurrent modification. Note: I observed this with threadirqs, I'm not sure if it can be triggered without threaded interrupts. Signed-off-by: John Keeping Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index bff48a8a1984..6be10e496e10 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3224,14 +3224,15 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_ep *ep, int result) { - struct dwc2_hsotg_req *req, *treq; unsigned int size; ep->req = NULL; - list_for_each_entry_safe(req, treq, &ep->queue, queue) - dwc2_hsotg_complete_request(hsotg, ep, req, - result); + while (!list_empty(&ep->queue)) { + struct dwc2_hsotg_req *req = get_ep_head(ep); + + dwc2_hsotg_complete_request(hsotg, ep, req, result); + } if (!hsotg->dedicated_fifos) return; From c6e4999cd930b8bd11dd8d4767e871b47f502845 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 2 Aug 2019 21:04:08 +0800 Subject: [PATCH 034/159] usb: dwc3: meson-g12a: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Acked-by: Neil Armstrong Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-meson-g12a.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index bca7e92a10e9..d73ccd9e1366 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -386,7 +386,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; void __iomem *base; - struct resource *res; enum phy_mode otg_id; int ret, i, irq; @@ -394,8 +393,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); From 58dd0bad25543f95ffb5627d183eb4d01f38e633 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 2 Aug 2019 21:06:16 +0800 Subject: [PATCH 035/159] usb: dwc3: omap: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ed8b86517675..84c459c15222 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -446,7 +446,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct dwc3_omap *omap; - struct resource *res; struct device *dev = &pdev->dev; struct regulator *vbus_reg = NULL; @@ -474,8 +473,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) return irq; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); From 5b76f6a0b0f868d8f4fae4e79a92e433a250a2eb Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 2 Aug 2019 21:01:04 +0800 Subject: [PATCH 036/159] usb: dwc3: keystone: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-keystone.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index cbee5fb9b9fb..d3c0233fed2f 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -81,7 +81,6 @@ static int kdwc3_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; struct dwc3_keystone *kdwc; - struct resource *res; int error, irq; kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL); @@ -92,8 +91,7 @@ static int kdwc3_probe(struct platform_device *pdev) kdwc->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - kdwc->usbss = devm_ioremap_resource(dev, res); + kdwc->usbss = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kdwc->usbss)) return PTR_ERR(kdwc->usbss); From 18425e5eb029eadbf82407d4fc224ced52fde986 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 22:16:39 +0300 Subject: [PATCH 037/159] usb: dwc2: Switch to use device_property_count_u32() Use use device_property_count_u32() directly, that makes code neater. Acked-by: Minas Harutyunyan Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 55f841a54015..31e090ac9f1e 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -404,10 +404,7 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg) device_property_read_u32(hsotg->dev, "g-np-tx-fifo-size", &p->g_np_tx_fifo_size); - num = device_property_read_u32_array(hsotg->dev, - "g-tx-fifo-size", - NULL, 0); - + num = device_property_count_u32(hsotg->dev, "g-tx-fifo-size"); if (num > 0) { num = min(num, 15); memset(p->g_tx_fifo_size, 0, From 8484aa0cb36d8fd751db77e46763719c78a03b22 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 24 Jul 2019 21:18:49 +0800 Subject: [PATCH 038/159] usb: gadget: pch_udc: Use dev_get_drvdata Instead of using to_pci_dev + pci_get_drvdata, use dev_get_drvdata to make code simpler. Signed-off-by: Chuhong Yuan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pch_udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index cded51f36fc1..265dab2bbfac 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -3046,8 +3046,7 @@ static void pch_udc_remove(struct pci_dev *pdev) #ifdef CONFIG_PM_SLEEP static int pch_udc_suspend(struct device *d) { - struct pci_dev *pdev = to_pci_dev(d); - struct pch_udc_dev *dev = pci_get_drvdata(pdev); + struct pch_udc_dev *dev = dev_get_drvdata(d); pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK); pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL); From a6e5e6794a538534e797f422e6f31888ff5b6b64 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 22:17:04 +0300 Subject: [PATCH 039/159] usb: dwc3: Switch to use device_property_count_u32() Use use device_property_count_u32() directly, that makes code neater. Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c9bb93a2c81e..98bce85c29d0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -813,8 +813,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) * result = 1, means INCRx burst mode supported. * result > 1, means undefined length burst mode supported. */ - ntype = device_property_read_u32_array(dev, - "snps,incr-burst-type-adjustment", NULL, 0); + ntype = device_property_count_u32(dev, "snps,incr-burst-type-adjustment"); if (ntype <= 0) return; From b2a3974253d32374af556541141d7fdad8fe2ce0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 21 Jul 2019 16:36:49 +0900 Subject: [PATCH 040/159] usb: dwc3: omap: squash include/linux/platform_data/dwc3-omap.h This enum is only used in drivers/usb/dwc3/dwc3-omap3.c Signed-off-by: Masahiro Yamada Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 7 +++- include/linux/platform_data/dwc3-omap.h | 43 ------------------------- 2 files changed, 6 insertions(+), 44 deletions(-) delete mode 100644 include/linux/platform_data/dwc3-omap.h diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 84c459c15222..c2940dbfb683 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -106,6 +105,12 @@ #define USBOTGSS_UTMI_OTG_CTRL_SESSVALID BIT(2) #define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID BIT(1) +enum dwc3_omap_utmi_mode { + DWC3_OMAP_UTMI_MODE_UNKNOWN = 0, + DWC3_OMAP_UTMI_MODE_HW, + DWC3_OMAP_UTMI_MODE_SW, +}; + struct dwc3_omap { struct device *dev; diff --git a/include/linux/platform_data/dwc3-omap.h b/include/linux/platform_data/dwc3-omap.h deleted file mode 100644 index 1d36ca874cc8..000000000000 --- a/include/linux/platform_data/dwc3-omap.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * dwc3-omap.h - OMAP Specific Glue layer, header. - * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. - * - * Author: Felipe Balbi - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -enum dwc3_omap_utmi_mode { - DWC3_OMAP_UTMI_MODE_UNKNOWN = 0, - DWC3_OMAP_UTMI_MODE_HW, - DWC3_OMAP_UTMI_MODE_SW, -}; From 4749e0e61241cc121de572520a39dab365b9ea1d Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 8 Aug 2019 16:39:42 -0700 Subject: [PATCH 041/159] usb: dwc3: Update soft-reset wait polling rate Starting from DWC_usb31 version 1.90a and later, the DCTL.CSFRST bit will not be cleared until after all the internal clocks are synchronized during soft-reset. This may take a little more than 50ms. Set the polling rate at 20ms instead. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 23 ++++++++++++++++++----- drivers/usb/dwc3/core.h | 2 ++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 98bce85c29d0..252c397860ef 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -252,12 +252,25 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) reg |= DWC3_DCTL_CSFTRST; dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* + * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit + * is cleared only after all the clocks are synchronized. This can + * take a little more than 50ms. Set the polling rate at 20ms + * for 10 times instead. + */ + if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A) + retries = 10; + do { reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) goto done; - udelay(1); + if (dwc3_is_usb31(dwc) && + dwc->revision >= DWC3_USB31_REVISION_190A) + msleep(20); + else + udelay(1); } while (--retries); phy_exit(dwc->usb3_generic_phy); @@ -267,11 +280,11 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) done: /* - * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared, - * we must wait at least 50ms before accessing the PHY domain - * (synchronization delay). DWC_usb31 programming guide section 1.3.2. + * For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit + * is cleared, we must wait at least 50ms before accessing the PHY + * domain (synchronization delay). */ - if (dwc3_is_usb31(dwc)) + if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A) msleep(50); return 0; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 3dd783b889cb..1c8b349379af 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1137,6 +1137,8 @@ struct dwc3 { #define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) #define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31) #define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31) u32 version_type; From 938a76ed64860b361d73a6d80f78865ffdc7e0ea Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 5 Aug 2019 13:48:42 -0500 Subject: [PATCH 042/159] usb: gadget: atmel_usba_udc: Mark expected switch fall-through Mark switch cases where we are expecting to fall through. This patch fixes the following warning (Building: at91_dt_defconfig arm): drivers/usb/gadget/udc/atmel_usba_udc.c:329:13: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20190805184842.GA8627@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/atmel_usba_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 503d275bc4c4..86ffc8307864 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -327,6 +327,7 @@ static int usba_config_fifo_table(struct usba_udc *udc) switch (fifo_mode) { default: fifo_mode = 0; + /* fall through */ case 0: udc->fifo_cfg = NULL; n = 0; From f0104b0c0fd34f176b305e339e614023b6e27610 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 5 Aug 2019 14:14:26 -0500 Subject: [PATCH 043/159] USB: gadget: udc: s3c2410_udc: Mark expected switch fall-throughs Mark switch cases where we are expecting to fall through. This patch fixes the following warning (Building: tct_hammer_defconfig arm): drivers/usb/gadget/udc/s3c2410_udc.c:314:7: warning: this statement may fall through [-Wimplicit-fallthrough=] drivers/usb/gadget/udc/s3c2410_udc.c:418:7: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20190805191426.GA12414@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/s3c2410_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index af3e63316ace..f82208fbc249 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -312,6 +312,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, switch (idx) { default: idx = 0; + /* fall through */ case 0: fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; @@ -416,6 +417,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, switch (idx) { default: idx = 0; + /* fall through */ case 0: fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; From 891c1da6ceff10a500e56790e7272e4ac521dc26 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 09:32:34 +0200 Subject: [PATCH 044/159] USB: phy: twl6030: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Cc: Felipe Balbi Link: https://lore.kernel.org/r/20190806073235.25140-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-twl6030-usb.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index dade34d70419..bfebf1f2e991 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -196,6 +196,12 @@ static ssize_t vbus_show(struct device *dev, } static DEVICE_ATTR_RO(vbus); +static struct attribute *twl6030_attrs[] = { + &dev_attr_vbus.attr, + NULL, +}; +ATTRIBUTE_GROUPS(twl6030); + static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; @@ -361,8 +367,6 @@ static int twl6030_usb_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); INIT_DELAYED_WORK(&twl->get_status_work, twl6030_status_work); @@ -373,7 +377,6 @@ static int twl6030_usb_probe(struct platform_device *pdev) if (status < 0) { dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq1, status); - device_remove_file(twl->dev, &dev_attr_vbus); return status; } @@ -384,7 +387,6 @@ static int twl6030_usb_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq2, status); free_irq(twl->irq1, twl); - device_remove_file(twl->dev, &dev_attr_vbus); return status; } @@ -408,7 +410,6 @@ static int twl6030_usb_remove(struct platform_device *pdev) free_irq(twl->irq1, twl); free_irq(twl->irq2, twl); regulator_put(twl->usb3v3); - device_remove_file(twl->dev, &dev_attr_vbus); cancel_work_sync(&twl->set_vbus_work); return 0; @@ -426,6 +427,7 @@ static struct platform_driver twl6030_usb_driver = { .driver = { .name = "twl6030_usb", .of_match_table = of_match_ptr(twl6030_usb_id_table), + .dev_groups = twl6030_groups, }, }; From f4d09e9f53ae2e45a9abb85227fc5f3731184da5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2019 21:36:34 +0200 Subject: [PATCH 045/159] USB: phy: tahvo: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Cc: Felipe Balbi Link: https://lore.kernel.org/r/20190805193636.25560-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tahvo.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 0981abc3d1ad..baebb1f5a973 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -312,15 +312,12 @@ static ssize_t otg_mode_store(struct device *device, } static DEVICE_ATTR_RW(otg_mode); -static struct attribute *tahvo_attributes[] = { +static struct attribute *tahvo_attrs[] = { &dev_attr_vbus.attr, &dev_attr_otg_mode.attr, NULL }; - -static const struct attribute_group tahvo_attr_group = { - .attrs = tahvo_attributes, -}; +ATTRIBUTE_GROUPS(tahvo); static int tahvo_usb_probe(struct platform_device *pdev) { @@ -406,17 +403,8 @@ static int tahvo_usb_probe(struct platform_device *pdev) goto err_remove_phy; } - /* Attributes */ - ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); - if (ret) { - dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); - goto err_free_irq; - } - return 0; -err_free_irq: - free_irq(tu->irq, tu); err_remove_phy: usb_remove_phy(&tu->phy); err_disable_clk: @@ -430,7 +418,6 @@ static int tahvo_usb_remove(struct platform_device *pdev) { struct tahvo_usb *tu = platform_get_drvdata(pdev); - sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); free_irq(tu->irq, tu); usb_remove_phy(&tu->phy); if (!IS_ERR(tu->ick)) @@ -444,6 +431,7 @@ static struct platform_driver tahvo_usb_driver = { .remove = tahvo_usb_remove, .driver = { .name = "tahvo-usb", + .dev_groups = tahvo_groups, }, }; module_platform_driver(tahvo_usb_driver); From 3e2cb866b2b1497a2246c8d222cd672694ac9f15 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2019 21:36:33 +0200 Subject: [PATCH 046/159] USB: phy: mv-usb: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Cc: Felipe Balbi Link: https://lore.kernel.org/r/20190805193636.25560-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-mv-usb.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index cf7ecdc9a9d4..06b47f1028b3 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -641,12 +641,15 @@ static const struct attribute_group inputs_attr_group = { .attrs = inputs_attrs, }; +static const struct attribute_group *mv_otg_groups[] = { + &inputs_attr_group, + NULL, +}; + static int mv_otg_remove(struct platform_device *pdev) { struct mv_otg *mvotg = platform_get_drvdata(pdev); - sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group); - if (mvotg->qwork) { flush_workqueue(mvotg->qwork); destroy_workqueue(mvotg->qwork); @@ -809,13 +812,6 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group); - if (retval < 0) { - dev_dbg(&pdev->dev, - "Can't register sysfs attr group: %d\n", retval); - goto err_remove_phy; - } - spin_lock_init(&mvotg->wq_lock); if (spin_trylock(&mvotg->wq_lock)) { mv_otg_run_state_machine(mvotg, 2 * HZ); @@ -828,8 +824,6 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; -err_remove_phy: - usb_remove_phy(&mvotg->phy); err_disable_clk: mv_otg_disable_internal(mvotg); err_destroy_workqueue: @@ -883,6 +877,7 @@ static struct platform_driver mv_otg_driver = { .remove = mv_otg_remove, .driver = { .name = driver_name, + .dev_groups = mv_otg_groups, }, #ifdef CONFIG_PM .suspend = mv_otg_suspend, From 524f3ac1542c294a9337e4b82c6a22a9e7362290 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2019 21:36:31 +0200 Subject: [PATCH 047/159] USB: chipidea: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Cc: Peter Chen Link: https://lore.kernel.org/r/20190805193636.25560-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 36c964cd40a3..215c655295b8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -903,10 +903,7 @@ static struct attribute *ci_attrs[] = { &dev_attr_role.attr, NULL, }; - -static const struct attribute_group ci_attr_group = { - .attrs = ci_attrs, -}; +ATTRIBUTE_GROUPS(ci); static int ci_hdrc_probe(struct platform_device *pdev) { @@ -1105,14 +1102,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) device_set_wakeup_capable(&pdev->dev, true); dbg_create_files(ci); - ret = sysfs_create_group(&dev->kobj, &ci_attr_group); - if (ret) - goto remove_debug; - return 0; -remove_debug: - dbg_remove_files(ci); stop: if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) ci_hdrc_otg_destroy(ci); @@ -1139,7 +1130,6 @@ static int ci_hdrc_remove(struct platform_device *pdev) } dbg_remove_files(ci); - sysfs_remove_group(&ci->dev->kobj, &ci_attr_group); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); ci_usb_phy_exit(ci); @@ -1318,6 +1308,7 @@ static struct platform_driver ci_hdrc_driver = { .driver = { .name = "ci_hdrc", .pm = &ci_pm_ops, + .dev_groups = ci_groups, }, }; From 899ad6d68ec8751d07f537346fbdead53cd43bca Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:59 +0200 Subject: [PATCH 048/159] USB: lvstest: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Link: https://lore.kernel.org/r/20190806144502.17792-10-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/lvstest.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c index e5c03c6d16e9..407fe7570f3b 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -310,7 +310,7 @@ static ssize_t enable_compliance_store(struct device *dev, } static DEVICE_ATTR_WO(enable_compliance); -static struct attribute *lvs_attributes[] = { +static struct attribute *lvs_attrs[] = { &dev_attr_get_dev_desc.attr, &dev_attr_u1_timeout.attr, &dev_attr_u2_timeout.attr, @@ -321,10 +321,7 @@ static struct attribute *lvs_attributes[] = { &dev_attr_enable_compliance.attr, NULL }; - -static const struct attribute_group lvs_attr_group = { - .attrs = lvs_attributes, -}; +ATTRIBUTE_GROUPS(lvs); static void lvs_rh_work(struct work_struct *work) { @@ -439,12 +436,6 @@ static int lvs_rh_probe(struct usb_interface *intf, INIT_WORK(&lvs->rh_work, lvs_rh_work); - ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group); - if (ret < 0) { - dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret); - goto free_urb; - } - pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp, @@ -453,13 +444,11 @@ static int lvs_rh_probe(struct usb_interface *intf, ret = usb_submit_urb(lvs->urb, GFP_KERNEL); if (ret < 0) { dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret); - goto sysfs_remove; + goto free_urb; } return ret; -sysfs_remove: - sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); free_urb: usb_free_urb(lvs->urb); return ret; @@ -469,7 +458,6 @@ static void lvs_rh_disconnect(struct usb_interface *intf) { struct lvs_rh *lvs = usb_get_intfdata(intf); - sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); usb_poison_urb(lvs->urb); /* used in scheduled work */ flush_work(&lvs->rh_work); usb_free_urb(lvs->urb); @@ -479,6 +467,7 @@ static struct usb_driver lvs_driver = { .name = "lvs", .probe = lvs_rh_probe, .disconnect = lvs_rh_disconnect, + .dev_groups = lvs_groups, }; module_usb_driver(lvs_driver); From 761ef1e4bc20e7b47e17be3022a2eae87de30560 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:57 +0200 Subject: [PATCH 049/159] USB: cypress_cy7c63: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Link: https://lore.kernel.org/r/20190806144502.17792-8-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/cypress_cy7c63.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 9d780b77314b..14faec51d7a5 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -183,6 +183,7 @@ static ssize_t port0_show(struct device *dev, { return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0); } +static DEVICE_ATTR_RW(port0); /* attribute callback handler (read) */ static ssize_t port1_show(struct device *dev, @@ -190,11 +191,14 @@ static ssize_t port1_show(struct device *dev, { return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1); } - -static DEVICE_ATTR_RW(port0); - static DEVICE_ATTR_RW(port1); +static struct attribute *cypress_attrs[] = { + &dev_attr_port0.attr, + &dev_attr_port1.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cypress); static int cypress_probe(struct usb_interface *interface, const struct usb_device_id *id) @@ -212,26 +216,11 @@ static int cypress_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); - /* create device attribute files */ - retval = device_create_file(&interface->dev, &dev_attr_port0); - if (retval) - goto error; - retval = device_create_file(&interface->dev, &dev_attr_port1); - if (retval) - goto error; - /* let the user know that the device is now attached */ dev_info(&interface->dev, "Cypress CY7C63xxx device now attached\n"); return 0; -error: - device_remove_file(&interface->dev, &dev_attr_port0); - device_remove_file(&interface->dev, &dev_attr_port1); - usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - kfree(dev); - error_mem: return retval; } @@ -242,9 +231,6 @@ static void cypress_disconnect(struct usb_interface *interface) dev = usb_get_intfdata(interface); - /* remove device attribute files */ - device_remove_file(&interface->dev, &dev_attr_port0); - device_remove_file(&interface->dev, &dev_attr_port1); /* the intfdata can be set to NULL only after the * device files have been removed */ usb_set_intfdata(interface, NULL); @@ -262,6 +248,7 @@ static struct usb_driver cypress_driver = { .probe = cypress_probe, .disconnect = cypress_disconnect, .id_table = cypress_table, + .dev_groups = cypress_groups, }; module_usb_driver(cypress_driver); From c847a36521b08dbdaa4a23cb0795c0ee01241f8f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:58 +0200 Subject: [PATCH 050/159] USB: cytherm: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Link: https://lore.kernel.org/r/20190806144502.17792-9-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/cytherm.c | 64 +++++++++++--------------------------- 1 file changed, 19 insertions(+), 45 deletions(-) diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 8b15ab5e1450..3e3802aaefa3 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -36,20 +36,6 @@ struct usb_cytherm { }; -/* local function prototypes */ -static int cytherm_probe(struct usb_interface *interface, - const struct usb_device_id *id); -static void cytherm_disconnect(struct usb_interface *interface); - - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver cytherm_driver = { - .name = "cytherm", - .probe = cytherm_probe, - .disconnect = cytherm_disconnect, - .id_table = id_table, -}; - /* Vendor requests */ /* They all operate on one byte at a time */ #define PING 0x00 @@ -304,6 +290,15 @@ static ssize_t port1_store(struct device *dev, struct device_attribute *attr, co } static DEVICE_ATTR_RW(port1); +static struct attribute *cytherm_attrs[] = { + &dev_attr_brightness.attr, + &dev_attr_temp.attr, + &dev_attr_button.attr, + &dev_attr_port0.attr, + &dev_attr_port1.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cytherm); static int cytherm_probe(struct usb_interface *interface, const struct usb_device_id *id) @@ -322,34 +317,10 @@ static int cytherm_probe(struct usb_interface *interface, dev->brightness = 0xFF; - retval = device_create_file(&interface->dev, &dev_attr_brightness); - if (retval) - goto error; - retval = device_create_file(&interface->dev, &dev_attr_temp); - if (retval) - goto error; - retval = device_create_file(&interface->dev, &dev_attr_button); - if (retval) - goto error; - retval = device_create_file(&interface->dev, &dev_attr_port0); - if (retval) - goto error; - retval = device_create_file(&interface->dev, &dev_attr_port1); - if (retval) - goto error; - dev_info (&interface->dev, "Cypress thermometer device now attached\n"); return 0; -error: - device_remove_file(&interface->dev, &dev_attr_brightness); - device_remove_file(&interface->dev, &dev_attr_temp); - device_remove_file(&interface->dev, &dev_attr_button); - device_remove_file(&interface->dev, &dev_attr_port0); - device_remove_file(&interface->dev, &dev_attr_port1); - usb_set_intfdata (interface, NULL); - usb_put_dev(dev->udev); - kfree(dev); + error_mem: return retval; } @@ -360,12 +331,6 @@ static void cytherm_disconnect(struct usb_interface *interface) dev = usb_get_intfdata (interface); - device_remove_file(&interface->dev, &dev_attr_brightness); - device_remove_file(&interface->dev, &dev_attr_temp); - device_remove_file(&interface->dev, &dev_attr_button); - device_remove_file(&interface->dev, &dev_attr_port0); - device_remove_file(&interface->dev, &dev_attr_port1); - /* first remove the files, then NULL the pointer */ usb_set_intfdata (interface, NULL); @@ -376,6 +341,15 @@ static void cytherm_disconnect(struct usb_interface *interface) dev_info(&interface->dev, "Cypress thermometer now disconnected\n"); } +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver cytherm_driver = { + .name = "cytherm", + .probe = cytherm_probe, + .disconnect = cytherm_disconnect, + .id_table = id_table, + .dev_groups = cytherm_groups, +}; + module_usb_driver(cytherm_driver); MODULE_AUTHOR(DRIVER_AUTHOR); From 04a0625e3b374a418139547431eadb0d8676bcba Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:56 +0200 Subject: [PATCH 051/159] USB: usbtmc: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Cc: Guido Kiener Cc: Steve Bayless Link: https://lore.kernel.org/r/20190806144502.17792-7-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 4942122b2346..7ff831f2fd21 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1836,17 +1836,14 @@ capability_attribute(device_capabilities); capability_attribute(usb488_interface_capabilities); capability_attribute(usb488_device_capabilities); -static struct attribute *capability_attrs[] = { +static struct attribute *usbtmc_attrs[] = { &dev_attr_interface_capabilities.attr, &dev_attr_device_capabilities.attr, &dev_attr_usb488_interface_capabilities.attr, &dev_attr_usb488_device_capabilities.attr, NULL, }; - -static const struct attribute_group capability_attr_grp = { - .attrs = capability_attrs, -}; +ATTRIBUTE_GROUPS(usbtmc); static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data) { @@ -2383,9 +2380,6 @@ static int usbtmc_probe(struct usb_interface *intf, retcode = get_capabilities(data); if (retcode) dev_err(&intf->dev, "can't read capabilities\n"); - else - retcode = sysfs_create_group(&intf->dev.kobj, - &capability_attr_grp); if (data->iin_ep_present) { /* allocate int urb */ @@ -2432,7 +2426,6 @@ static int usbtmc_probe(struct usb_interface *intf, return 0; error_register: - sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); usbtmc_free_int(data); err_put: kref_put(&data->kref, usbtmc_delete); @@ -2445,7 +2438,6 @@ static void usbtmc_disconnect(struct usb_interface *intf) struct list_head *elem; usb_deregister_dev(intf, &usbtmc_class); - sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); mutex_lock(&data->io_mutex); data->zombie = 1; wake_up_interruptible_all(&data->waitq); @@ -2551,6 +2543,7 @@ static struct usb_driver usbtmc_driver = { .resume = usbtmc_resume, .pre_reset = usbtmc_pre_reset, .post_reset = usbtmc_post_reset, + .dev_groups = usbtmc_groups, }; module_usb_driver(usbtmc_driver); From 02c4d45d838957971ec5b7ee359380a0ccfdb963 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:55 +0200 Subject: [PATCH 052/159] USB: usblp: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Cc: Pete Zaitcev Link: https://lore.kernel.org/r/20190806144502.17792-6-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 407a7a6198a2..7fea4999d352 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1082,6 +1082,12 @@ static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *att static DEVICE_ATTR_RO(ieee1284_id); +static struct attribute *usblp_attrs[] = { + &dev_attr_ieee1284_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(usblp); + static int usblp_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1156,9 +1162,6 @@ static int usblp_probe(struct usb_interface *intf, /* Retrieve and store the device ID string. */ usblp_cache_device_id_string(usblp); - retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id); - if (retval) - goto abort_intfdata; #ifdef DEBUG usblp_check_status(usblp, 0); @@ -1189,7 +1192,6 @@ static int usblp_probe(struct usb_interface *intf, abort_intfdata: usb_set_intfdata(intf, NULL); - device_remove_file(&intf->dev, &dev_attr_ieee1284_id); abort: kfree(usblp->readbuf); kfree(usblp->statusbuf); @@ -1360,8 +1362,6 @@ static void usblp_disconnect(struct usb_interface *intf) BUG(); } - device_remove_file(&intf->dev, &dev_attr_ieee1284_id); - mutex_lock(&usblp_mutex); mutex_lock(&usblp->mut); usblp->present = 0; @@ -1421,6 +1421,7 @@ static struct usb_driver usblp_driver = { .suspend = usblp_suspend, .resume = usblp_resume, .id_table = usblp_ids, + .dev_groups = usblp_groups, .supports_autosuspend = 1, }; From ec935791ec52ae5098de8fcbf5ac484745b5bd79 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:54 +0200 Subject: [PATCH 053/159] USB: ueagle-atm: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Cc: Matthieu CASTET Cc: Stanislaw Gruszka Link: https://lore.kernel.org/r/20190806144502.17792-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 8faa51b1a520..8b0ea8c70d73 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2458,7 +2458,7 @@ static int claim_interface(struct usb_device *usb_dev, return ret; } -static struct attribute *attrs[] = { +static struct attribute *uea_attrs[] = { &dev_attr_stat_status.attr, &dev_attr_stat_mflags.attr, &dev_attr_stat_human_status.attr, @@ -2479,9 +2479,7 @@ static struct attribute *attrs[] = { &dev_attr_stat_firmid.attr, NULL, }; -static const struct attribute_group attr_grp = { - .attrs = attrs, -}; +ATTRIBUTE_GROUPS(uea); static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, const struct usb_device_id *id) @@ -2550,18 +2548,12 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, } } - ret = sysfs_create_group(&intf->dev.kobj, &attr_grp); + ret = uea_boot(sc); if (ret < 0) goto error; - ret = uea_boot(sc); - if (ret < 0) - goto error_rm_grp; - return 0; -error_rm_grp: - sysfs_remove_group(&intf->dev.kobj, &attr_grp); error: kfree(sc); return ret; @@ -2571,7 +2563,6 @@ static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) { struct uea_softc *sc = usbatm->driver_data; - sysfs_remove_group(&intf->dev.kobj, &attr_grp); uea_stop(sc); kfree(sc); } @@ -2721,6 +2712,7 @@ static struct usb_driver uea_driver = { .id_table = uea_ids, .probe = uea_probe, .disconnect = uea_disconnect, + .dev_groups = uea_groups, }; MODULE_DEVICE_TABLE(usb, uea_ids); From e605c30977bb5f48af6d14149a252b1619851f33 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:44:53 +0200 Subject: [PATCH 054/159] USB: atm: cxacru: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Link: https://lore.kernel.org/r/20190806144502.17792-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/cxacru.c | 58 +++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index e57a2be8754a..5d41f85a7445 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -539,6 +539,37 @@ CXACRU_SET_##_action( adsl_config); CXACRU_ALL_FILES(INIT); +static struct attribute *cxacru_attrs[] = { + &dev_attr_adsl_config.attr, + &dev_attr_adsl_state.attr, + &dev_attr_adsl_controller_version.attr, + &dev_attr_adsl_headend_environment.attr, + &dev_attr_adsl_headend.attr, + &dev_attr_modulation.attr, + &dev_attr_line_startable.attr, + &dev_attr_downstream_hec_errors.attr, + &dev_attr_upstream_hec_errors.attr, + &dev_attr_downstream_fec_errors.attr, + &dev_attr_upstream_fec_errors.attr, + &dev_attr_downstream_crc_errors.attr, + &dev_attr_upstream_crc_errors.attr, + &dev_attr_startup_attempts.attr, + &dev_attr_downstream_bits_per_frame.attr, + &dev_attr_upstream_bits_per_frame.attr, + &dev_attr_transmitter_power.attr, + &dev_attr_downstream_attenuation.attr, + &dev_attr_upstream_attenuation.attr, + &dev_attr_downstream_snr_margin.attr, + &dev_attr_upstream_snr_margin.attr, + &dev_attr_mac_address.attr, + &dev_attr_line_status.attr, + &dev_attr_link_status.attr, + &dev_attr_upstream_rate.attr, + &dev_attr_downstream_rate.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cxacru); + /* the following three functions are stolen from drivers/usb/core/message.c */ static void cxacru_blocking_completion(struct urb *urb) { @@ -736,17 +767,6 @@ static int cxacru_card_status(struct cxacru_data *instance) return 0; } -static void cxacru_remove_device_files(struct usbatm_data *usbatm_instance, - struct atm_dev *atm_dev) -{ - struct usb_interface *intf = usbatm_instance->usb_intf; - - #define CXACRU_DEVICE_REMOVE_FILE(_name) \ - device_remove_file(&intf->dev, &dev_attr_##_name); - CXACRU_ALL_FILES(REMOVE); - #undef CXACRU_DEVICE_REMOVE_FILE -} - static int cxacru_atm_start(struct usbatm_data *usbatm_instance, struct atm_dev *atm_dev) { @@ -765,13 +785,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, return ret; } - #define CXACRU_DEVICE_CREATE_FILE(_name) \ - ret = device_create_file(&intf->dev, &dev_attr_##_name); \ - if (unlikely(ret)) \ - goto fail_sysfs; - CXACRU_ALL_FILES(CREATE); - #undef CXACRU_DEVICE_CREATE_FILE - /* start ADSL */ mutex_lock(&instance->adsl_state_serialize); ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); @@ -804,11 +817,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, if (start_polling) cxacru_poll_status(&instance->poll_work.work); return 0; - -fail_sysfs: - usb_err(usbatm_instance, "cxacru_atm_start: device_create_file failed (%d)\n", ret); - cxacru_remove_device_files(usbatm_instance, atm_dev); - return ret; } static void cxacru_poll_status(struct work_struct *work) @@ -1332,7 +1340,6 @@ static struct usbatm_driver cxacru_driver = { .heavy_init = cxacru_heavy_init, .unbind = cxacru_unbind, .atm_start = cxacru_atm_start, - .atm_stop = cxacru_remove_device_files, .bulk_in = CXACRU_EP_DATA, .bulk_out = CXACRU_EP_DATA, .rx_padding = 3, @@ -1364,7 +1371,8 @@ static struct usb_driver cxacru_usb_driver = { .name = cxacru_driver_name, .probe = cxacru_usb_probe, .disconnect = usbatm_usb_disconnect, - .id_table = cxacru_usb_ids + .id_table = cxacru_usb_ids, + .dev_groups = cxacru_groups, }; module_usb_driver(cxacru_usb_driver); From c5501d23e38df1f1e7afd373a5d924a18936930c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:45:02 +0200 Subject: [PATCH 055/159] USB: usbip: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Cc: Valentina Manea Cc: Shuah Khan Link: https://lore.kernel.org/r/20190806144502.17792-13-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_dev.c | 50 ++++++------------------------------ 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index 7931e6cecc70..2305d425e6c9 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -106,38 +106,13 @@ err: } static DEVICE_ATTR_WO(usbip_sockfd); -static int stub_add_files(struct device *dev) -{ - int err = 0; - - err = device_create_file(dev, &dev_attr_usbip_status); - if (err) - goto err_status; - - err = device_create_file(dev, &dev_attr_usbip_sockfd); - if (err) - goto err_sockfd; - - err = device_create_file(dev, &dev_attr_usbip_debug); - if (err) - goto err_debug; - - return 0; - -err_debug: - device_remove_file(dev, &dev_attr_usbip_sockfd); -err_sockfd: - device_remove_file(dev, &dev_attr_usbip_status); -err_status: - return err; -} - -static void stub_remove_files(struct device *dev) -{ - device_remove_file(dev, &dev_attr_usbip_status); - device_remove_file(dev, &dev_attr_usbip_sockfd); - device_remove_file(dev, &dev_attr_usbip_debug); -} +static struct attribute *usbip_attrs[] = { + &dev_attr_usbip_status.attr, + &dev_attr_usbip_sockfd.attr, + &dev_attr_usbip_debug.attr, + NULL, +}; +ATTRIBUTE_GROUPS(usbip); static void stub_shutdown_connection(struct usbip_device *ud) { @@ -379,17 +354,8 @@ static int stub_probe(struct usb_device *udev) goto err_port; } - rc = stub_add_files(&udev->dev); - if (rc) { - dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid); - goto err_files; - } - return 0; -err_files: - usb_hub_release_port(udev->parent, udev->portnum, - (struct usb_dev_state *) udev); err_port: dev_set_drvdata(&udev->dev, NULL); usb_put_dev(udev); @@ -457,7 +423,6 @@ static void stub_disconnect(struct usb_device *udev) /* * NOTE: rx/tx threads are invoked for each usb_device. */ - stub_remove_files(&udev->dev); /* release port */ rc = usb_hub_release_port(udev->parent, udev->portnum, @@ -526,4 +491,5 @@ struct usb_device_driver stub_driver = { .resume = stub_resume, #endif .supports_autosuspend = 0, + .dev_groups = usbip_groups, }; From f9bbcbef0c50b766cf5931ab7d8f3c97a784257e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:45:00 +0200 Subject: [PATCH 056/159] USB: trancevibrator: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Cc: Ding Xiang Link: https://lore.kernel.org/r/20190806144502.17792-11-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/trancevibrator.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index ac357ce2d1a6..a3dfc77578ea 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -71,9 +71,14 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr, } return count; } - static DEVICE_ATTR_RW(speed); +static struct attribute *tv_attrs[] = { + &dev_attr_speed.attr, + NULL, +}; +ATTRIBUTE_GROUPS(tv); + static int tv_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -89,15 +94,9 @@ static int tv_probe(struct usb_interface *interface, dev->udev = usb_get_dev(udev); usb_set_intfdata(interface, dev); - retval = device_create_file(&interface->dev, &dev_attr_speed); - if (retval) - goto error_create_file; return 0; -error_create_file: - usb_put_dev(udev); - usb_set_intfdata(interface, NULL); error: kfree(dev); return retval; @@ -108,7 +107,6 @@ static void tv_disconnect(struct usb_interface *interface) struct trancevibrator *dev; dev = usb_get_intfdata (interface); - device_remove_file(&interface->dev, &dev_attr_speed); usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); kfree(dev); @@ -120,6 +118,7 @@ static struct usb_driver tv_driver = { .probe = tv_probe, .disconnect = tv_disconnect, .id_table = id_table, + .dev_groups = tv_groups, }; module_usb_driver(tv_driver); From a21350feb9ae4d03062eda1fe344ec2a93d6bccc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 6 Aug 2019 16:45:01 +0200 Subject: [PATCH 057/159] USB: usbsevseg: convert to use dev_groups USB drivers now support the ability for the driver core to handle the creation and removal of device-specific sysfs files in a race-free manner. Take advantage of that by converting the driver to use this by moving the sysfs attributes into a group and assigning the dev_groups pointer to it. Link: https://lore.kernel.org/r/20190806144502.17792-12-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbsevseg.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 1923d5b6d9c9..551074f5b7ad 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -316,7 +316,7 @@ MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); -static struct attribute *dev_attrs[] = { +static struct attribute *sevseg_attrs[] = { &dev_attr_powered.attr, &dev_attr_text.attr, &dev_attr_textmode.attr, @@ -325,10 +325,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_mode_lsb.attr, NULL }; - -static const struct attribute_group dev_attr_grp = { - .attrs = dev_attrs, -}; +ATTRIBUTE_GROUPS(sevseg); static int sevseg_probe(struct usb_interface *interface, const struct usb_device_id *id) @@ -354,17 +351,9 @@ static int sevseg_probe(struct usb_interface *interface, mydev->mode_msb = 0x06; /* 6 characters */ mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ - rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); - if (rc) - goto error; - dev_info(&interface->dev, "USB 7 Segment device now attached\n"); return 0; -error: - usb_set_intfdata(interface, NULL); - usb_put_dev(mydev->udev); - kfree(mydev); error_mem: return rc; } @@ -374,7 +363,6 @@ static void sevseg_disconnect(struct usb_interface *interface) struct usb_sevsegdev *mydev; mydev = usb_get_intfdata(interface); - sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); usb_set_intfdata(interface, NULL); usb_put_dev(mydev->udev); kfree(mydev); @@ -423,6 +411,7 @@ static struct usb_driver sevseg_driver = { .resume = sevseg_resume, .reset_resume = sevseg_reset_resume, .id_table = id_table, + .dev_groups = sevseg_groups, .supports_autosuspend = 1, }; From 7794f486ed0b1fa8022dd0a27b9babf86a46d1cf Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 7 Aug 2019 10:29:50 -0400 Subject: [PATCH 058/159] usbfs: Add ioctls for runtime power management It has been requested that usbfs should implement runtime power management, instead of forcing the device to remain at full power as long as the device file is open. This patch introduces that new feature. It does so by adding three new usbfs ioctls: USBDEVFS_FORBID_SUSPEND: Prevents the device from going into runtime suspend (and causes a resume if the device is already suspended). USBDEVFS_ALLOW_SUSPEND: Allows the device to go into runtime suspend. Some time may elapse before the device actually is suspended, depending on things like the autosuspend delay. USBDEVFS_WAIT_FOR_RESUME: Blocks until the call is interrupted by a signal or at least one runtime resume has occurred since the most recent ALLOW_SUSPEND ioctl call (which may mean immediately, even if the device is currently suspended). In the latter case, the device is prevented from suspending again just as if FORBID_SUSPEND was called before the ioctl returns. For backward compatibility, when the device file is first opened runtime suspends are forbidden. The userspace program can then allow suspends whenever it wants, and either resume the device directly (by forbidding suspends again) or wait for a resume from some other source (such as a remote wakeup). URBs submitted to a suspended device will fail or will complete with an appropriate error code. This combination of ioctls is sufficient for user programs to have nearly the same degree of control over a device's runtime power behavior as kernel drivers do. Still lacking is documentation for the new ioctls. I intend to add it later, after the existing documentation for the usbfs userspace API is straightened out into a reasonable form. Suggested-by: Mayuresh Kulkarni Signed-off-by: Alan Stern Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.1908071013220.1514-100000@iolanthe.rowland.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 96 +++++++++++++++++++++++++++++-- drivers/usb/core/generic.c | 5 ++ drivers/usb/core/usb.h | 3 + include/uapi/linux/usbdevice_fs.h | 3 + 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index b265ab5405f9..cdd34dcb2395 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -48,6 +48,9 @@ #define USB_DEVICE_MAX (USB_MAXBUS * 128) #define USB_SG_SIZE 16384 /* split-size for large txs */ +/* Mutual exclusion for ps->list in resume vs. release and remove */ +static DEFINE_MUTEX(usbfs_mutex); + struct usb_dev_state { struct list_head list; /* state list */ struct usb_device *dev; @@ -57,14 +60,17 @@ struct usb_dev_state { struct list_head async_completed; struct list_head memory_list; wait_queue_head_t wait; /* wake up if a request completed */ + wait_queue_head_t wait_for_resume; /* wake up upon runtime resume */ unsigned int discsignr; struct pid *disc_pid; const struct cred *cred; sigval_t disccontext; unsigned long ifclaimed; u32 disabled_bulk_eps; - bool privileges_dropped; unsigned long interface_allowed_mask; + int not_yet_resumed; + bool suspend_allowed; + bool privileges_dropped; }; struct usb_memory { @@ -694,9 +700,7 @@ static void driver_disconnect(struct usb_interface *intf) destroy_async_on_interface(ps, ifnum); } -/* The following routines are merely placeholders. There is no way - * to inform a user task about suspend or resumes. - */ +/* We don't care about suspend/resume of claimed interfaces */ static int driver_suspend(struct usb_interface *intf, pm_message_t msg) { return 0; @@ -707,12 +711,32 @@ static int driver_resume(struct usb_interface *intf) return 0; } +/* The following routines apply to the entire device, not interfaces */ +void usbfs_notify_suspend(struct usb_device *udev) +{ + /* We don't need to handle this */ +} + +void usbfs_notify_resume(struct usb_device *udev) +{ + struct usb_dev_state *ps; + + /* Protect against simultaneous remove or release */ + mutex_lock(&usbfs_mutex); + list_for_each_entry(ps, &udev->filelist, list) { + WRITE_ONCE(ps->not_yet_resumed, 0); + wake_up_all(&ps->wait_for_resume); + } + mutex_unlock(&usbfs_mutex); +} + struct usb_driver usbfs_driver = { .name = "usbfs", .probe = driver_probe, .disconnect = driver_disconnect, .suspend = driver_suspend, .resume = driver_resume, + .supports_autosuspend = 1, }; static int claimintf(struct usb_dev_state *ps, unsigned int ifnum) @@ -997,9 +1021,12 @@ static int usbdev_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&ps->async_completed); INIT_LIST_HEAD(&ps->memory_list); init_waitqueue_head(&ps->wait); + init_waitqueue_head(&ps->wait_for_resume); ps->disc_pid = get_pid(task_pid(current)); ps->cred = get_current_cred(); smp_wmb(); + + /* Can't race with resume; the device is already active */ list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; usb_unlock_device(dev); @@ -1025,7 +1052,10 @@ static int usbdev_release(struct inode *inode, struct file *file) usb_lock_device(dev); usb_hub_release_all_ports(dev, ps); + /* Protect against simultaneous resume */ + mutex_lock(&usbfs_mutex); list_del_init(&ps->list); + mutex_unlock(&usbfs_mutex); for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) { @@ -1033,7 +1063,8 @@ static int usbdev_release(struct inode *inode, struct file *file) releaseintf(ps, ifnum); } destroy_all_async(ps); - usb_autosuspend_device(dev); + if (!ps->suspend_allowed) + usb_autosuspend_device(dev); usb_unlock_device(dev); usb_put_dev(dev); put_pid(ps->disc_pid); @@ -2384,6 +2415,47 @@ static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg) return 0; } +static int proc_forbid_suspend(struct usb_dev_state *ps) +{ + int ret = 0; + + if (ps->suspend_allowed) { + ret = usb_autoresume_device(ps->dev); + if (ret == 0) + ps->suspend_allowed = false; + else if (ret != -ENODEV) + ret = -EIO; + } + return ret; +} + +static int proc_allow_suspend(struct usb_dev_state *ps) +{ + if (!connected(ps)) + return -ENODEV; + + WRITE_ONCE(ps->not_yet_resumed, 1); + if (!ps->suspend_allowed) { + usb_autosuspend_device(ps->dev); + ps->suspend_allowed = true; + } + return 0; +} + +static int proc_wait_for_resume(struct usb_dev_state *ps) +{ + int ret; + + usb_unlock_device(ps->dev); + ret = wait_event_interruptible(ps->wait_for_resume, + READ_ONCE(ps->not_yet_resumed) == 0); + usb_lock_device(ps->dev); + + if (ret != 0) + return -EINTR; + return proc_forbid_suspend(ps); +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -2578,6 +2650,15 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, case USBDEVFS_GET_SPEED: ret = ps->dev->speed; break; + case USBDEVFS_FORBID_SUSPEND: + ret = proc_forbid_suspend(ps); + break; + case USBDEVFS_ALLOW_SUSPEND: + ret = proc_allow_suspend(ps); + break; + case USBDEVFS_WAIT_FOR_RESUME: + ret = proc_wait_for_resume(ps); + break; } /* Handle variable-length commands */ @@ -2651,15 +2732,20 @@ static void usbdev_remove(struct usb_device *udev) { struct usb_dev_state *ps; + /* Protect against simultaneous resume */ + mutex_lock(&usbfs_mutex); while (!list_empty(&udev->filelist)) { ps = list_entry(udev->filelist.next, struct usb_dev_state, list); destroy_all_async(ps); wake_up_all(&ps->wait); + WRITE_ONCE(ps->not_yet_resumed, 0); + wake_up_all(&ps->wait_for_resume); list_del_init(&ps->list); if (ps->discsignr) kill_pid_usb_asyncio(ps->discsignr, EPIPE, ps->disccontext, ps->disc_pid, ps->cred); } + mutex_unlock(&usbfs_mutex); } static int usbdev_notify(struct notifier_block *self, diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 1ac9c1e5f773..38f8b3e31762 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -257,6 +257,8 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) else rc = usb_port_suspend(udev, msg); + if (rc == 0) + usbfs_notify_suspend(udev); return rc; } @@ -273,6 +275,9 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg) rc = hcd_bus_resume(udev, msg); else rc = usb_port_resume(udev, msg); + + if (rc == 0) + usbfs_notify_resume(udev); return rc; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index bd8d01f85a13..2932d1ec5def 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -95,6 +95,9 @@ extern int usb_runtime_idle(struct device *dev); extern int usb_enable_usb2_hardware_lpm(struct usb_device *udev); extern int usb_disable_usb2_hardware_lpm(struct usb_device *udev); +extern void usbfs_notify_suspend(struct usb_device *udev); +extern void usbfs_notify_resume(struct usb_device *udev); + #else static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg) diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h index 78efe870c2b7..d24bbb6d3ca1 100644 --- a/include/uapi/linux/usbdevice_fs.h +++ b/include/uapi/linux/usbdevice_fs.h @@ -223,5 +223,8 @@ struct usbdevfs_streams { * extending size of the data returned. */ #define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len) +#define USBDEVFS_FORBID_SUSPEND _IO('U', 33) +#define USBDEVFS_ALLOW_SUSPEND _IO('U', 34) +#define USBDEVFS_WAIT_FOR_RESUME _IO('U', 35) #endif /* _UAPI_LINUX_USBDEVICE_FS_H */ From 3cd703f483d1d8cfcd7fd6f304ab6fc74e29cf15 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 19 Jul 2019 11:30:37 +0200 Subject: [PATCH 059/159] usb: dwc3: remove generic PHYs forwarding for XHCI device Commit 08f871a3aca2 ("usb: dwc3: host: convey the PHYs to xhci") added forwarding of the generic PHYs from DWC3 core to the instantiated XHCI-plat device. However XHCI(-plat) driver never gained support for generic PHYs, thus the lookup added by that commit is never used. In meantime the commit d64ff406e51e ("usb: dwc3: use bus->sysdev for DMA configuration") incorrectly changed the device used for creating lookup, making the lookup useless and generic PHYs inaccessible from XHCI device. However since commit 178a0bce05cb ("usb: core: hcd: integrate the PHY wrapper into the HCD core") USB HCD already handles generic PHYs acquired from the HCD's 'sysdev', which in this case is DWC3 core device. This means that creating any custom lookup entries for XHCI driver is no longer needed and can be simply removed. Signed-off-by: Marek Szyprowski Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/host.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index f55947294f7c..8deea8c91e03 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -85,7 +85,7 @@ int dwc3_host_init(struct dwc3 *dwc) DWC3_XHCI_RESOURCES_NUM); if (ret) { dev_err(dwc->dev, "couldn't add resources to xHCI device\n"); - goto err1; + goto err; } memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props)); @@ -112,37 +112,23 @@ int dwc3_host_init(struct dwc3 *dwc) ret = platform_device_add_properties(xhci, props); if (ret) { dev_err(dwc->dev, "failed to add properties to xHCI\n"); - goto err1; + goto err; } } - phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy", - dev_name(dwc->dev)); - phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy", - dev_name(dwc->dev)); - ret = platform_device_add(xhci); if (ret) { dev_err(dwc->dev, "failed to register xHCI device\n"); - goto err2; + goto err; } return 0; -err2: - phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy", - dev_name(dwc->dev)); - phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy", - dev_name(dwc->dev)); -err1: +err: platform_device_put(xhci); return ret; } void dwc3_host_exit(struct dwc3 *dwc) { - phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy", - dev_name(dwc->dev)); - phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy", - dev_name(dwc->dev)); platform_device_unregister(dwc->xhci); } From 03bf32bbb607e07bf9ac6ea126088d1dc88ea948 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 12 Jul 2019 00:26:33 -0700 Subject: [PATCH 060/159] usb: dwc3: Use devres to get clocks Use devres to get clocks and drop explicit clock freeing. No functional change intended. Signed-off-by: Andrey Smirnov Cc: Felipe Balbi Cc: Chris Healy Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 252c397860ef..7ae2eee72f90 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1448,7 +1448,7 @@ static int dwc3_probe(struct platform_device *pdev) if (dev->of_node) { dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); - ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); + ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks); if (ret == -EPROBE_DEFER) return ret; /* @@ -1461,7 +1461,7 @@ static int dwc3_probe(struct platform_device *pdev) ret = reset_control_deassert(dwc->reset); if (ret) - goto put_clks; + return ret; ret = clk_bulk_prepare(dwc->num_clks, dwc->clks); if (ret) @@ -1548,8 +1548,6 @@ unprepare_clks: clk_bulk_unprepare(dwc->num_clks, dwc->clks); assert_reset: reset_control_assert(dwc->reset); -put_clks: - clk_bulk_put(dwc->num_clks, dwc->clks); return ret; } @@ -1572,7 +1570,6 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_free_event_buffers(dwc); dwc3_free_scratch_buffers(dwc); - clk_bulk_put(dwc->num_clks, dwc->clks); return 0; } From 240b65dc1e89dd83d0c4946f58e49e7ee669b25d Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 12 Jul 2019 00:26:34 -0700 Subject: [PATCH 061/159] usb: dwc3: Use clk_bulk_prepare_enable() Use clk_bulk_prepare_enable() and clk_bulk_disable_unprepare() to simplify code a bit. No functional change intended. Signed-off-by: Andrey Smirnov Cc: Felipe Balbi Cc: Chris Healy Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 7ae2eee72f90..30ebf3c65a63 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -699,8 +699,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) usb_phy_set_suspend(dwc->usb3_phy, 1); phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb3_generic_phy); - clk_bulk_disable(dwc->num_clks, dwc->clks); - clk_bulk_unprepare(dwc->num_clks, dwc->clks); + clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks); reset_control_assert(dwc->reset); } @@ -1463,14 +1462,10 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) return ret; - ret = clk_bulk_prepare(dwc->num_clks, dwc->clks); + ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks); if (ret) goto assert_reset; - ret = clk_bulk_enable(dwc->num_clks, dwc->clks); - if (ret) - goto unprepare_clks; - if (!dwc3_core_is_valid(dwc)) { dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); ret = -ENODEV; @@ -1543,9 +1538,7 @@ err1: pm_runtime_disable(&pdev->dev); disable_clks: - clk_bulk_disable(dwc->num_clks, dwc->clks); -unprepare_clks: - clk_bulk_unprepare(dwc->num_clks, dwc->clks); + clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks); assert_reset: reset_control_assert(dwc->reset); @@ -1583,14 +1576,10 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) if (ret) return ret; - ret = clk_bulk_prepare(dwc->num_clks, dwc->clks); + ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks); if (ret) goto assert_reset; - ret = clk_bulk_enable(dwc->num_clks, dwc->clks); - if (ret) - goto unprepare_clks; - ret = dwc3_core_init(dwc); if (ret) goto disable_clks; @@ -1598,9 +1587,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) return 0; disable_clks: - clk_bulk_disable(dwc->num_clks, dwc->clks); -unprepare_clks: - clk_bulk_unprepare(dwc->num_clks, dwc->clks); + clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks); assert_reset: reset_control_assert(dwc->reset); From 894efaf5236d3abf0512d8a71ae927ea9f99b509 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 9 Aug 2019 18:21:50 +0800 Subject: [PATCH 062/159] staging: wusbcore: Fix build error without CONFIG_USB USB_WUSB should depends on CONFIG_USB, otherwise building fails drivers/staging/wusbcore/wusbhc.o: In function `wusbhc_giveback_urb': wusbhc.c:(.text+0xa28): undefined reference to `usb_hcd_giveback_urb' Reported-by: Hulk Robot Fixes: 71ed79b0e4be ("USB: Move wusbcore and UWB to staging as it is obsolete") Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190809102150.66896-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wusbcore/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/wusbcore/Kconfig b/drivers/staging/wusbcore/Kconfig index 056c60b4d57f..a559d023b508 100644 --- a/drivers/staging/wusbcore/Kconfig +++ b/drivers/staging/wusbcore/Kconfig @@ -4,7 +4,7 @@ # config USB_WUSB tristate "Enable Wireless USB extensions" - depends on UWB + depends on UWB && USB select CRYPTO select CRYPTO_AES select CRYPTO_CCM From a190d94830c30e41bd5fbdf803a636263208fa2e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:43 +0200 Subject: [PATCH 063/159] usb: remove ehci-w90x900 driver The ARM w90x900 platform is getting removed, so this driver is obsolete. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20190809202749.742267-16-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 6 -- drivers/usb/host/Makefile | 1 - drivers/usb/host/ehci-w90x900.c | 130 -------------------------------- 3 files changed, 137 deletions(-) delete mode 100644 drivers/usb/host/ehci-w90x900.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d040408f5baa..f39f24f107f4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -287,12 +287,6 @@ config USB_EHCI_MV Dova, Armada 370 and Armada XP. See "Support for Marvell EBU on-chip EHCI USB controller" for those. -config USB_W90X900_EHCI - tristate "W90X900(W90P910) EHCI support" - depends on ARCH_W90X900 - ---help--- - Enables support for the W90X900 USB controller - config USB_CNS3XXX_EHCI bool "Cavium CNS3XXX EHCI Module (DEPRECATED)" depends on ARCH_CNS3XXX diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 59b39e6b350b..b191361257cc 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-st.o obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o -obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c deleted file mode 100644 index 6d77ace1697b..000000000000 --- a/drivers/usb/host/ehci-w90x900.c +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/driver/usb/host/ehci-w90x900.c - * - * Copyright (c) 2008 Nuvoton technology corporation. - * - * Wan ZongShun - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ehci.h" - -/* enable phy0 and phy1 for w90p910 */ -#define ENPHY (0x01<<8) -#define PHY0_CTR (0xA4) -#define PHY1_CTR (0xA8) - -#define DRIVER_DESC "EHCI w90x900 driver" - -static const char hcd_name[] = "ehci-w90x900 "; - -static struct hc_driver __read_mostly ehci_w90x900_hc_driver; - -static int ehci_w90x900_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct ehci_hcd *ehci; - struct resource *res; - int retval = 0, irq; - unsigned long val; - - hcd = usb_create_hcd(&ehci_w90x900_hc_driver, - &pdev->dev, "w90x900 EHCI"); - if (!hcd) { - retval = -ENOMEM; - goto err1; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(hcd->regs)) { - retval = PTR_ERR(hcd->regs); - goto err2; - } - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - ehci = hcd_to_ehci(hcd); - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* enable PHY 0,1,the regs only apply to w90p910 - * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of - * w90p910 IC relative to ehci->regs. - */ - val = __raw_readl(ehci->regs+PHY0_CTR); - val |= ENPHY; - __raw_writel(val, ehci->regs+PHY0_CTR); - - val = __raw_readl(ehci->regs+PHY1_CTR); - val |= ENPHY; - __raw_writel(val, ehci->regs+PHY1_CTR); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - retval = irq; - goto err2; - } - - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (retval != 0) - goto err2; - - device_wakeup_enable(hcd->self.controller); - return retval; -err2: - usb_put_hcd(hcd); -err1: - return retval; -} - -static int ehci_w90x900_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - usb_remove_hcd(hcd); - usb_put_hcd(hcd); - - return 0; -} - -static struct platform_driver ehci_hcd_w90x900_driver = { - .probe = ehci_w90x900_probe, - .remove = ehci_w90x900_remove, - .driver = { - .name = "w90x900-ehci", - }, -}; - -static int __init ehci_w90X900_init(void) -{ - if (usb_disabled()) - return -ENODEV; - - pr_info("%s: " DRIVER_DESC "\n", hcd_name); - - ehci_init_driver(&ehci_w90x900_hc_driver, NULL); - return platform_driver_register(&ehci_hcd_w90x900_driver); -} -module_init(ehci_w90X900_init); - -static void __exit ehci_w90X900_cleanup(void) -{ - platform_driver_unregister(&ehci_hcd_w90x900_driver); -} -module_exit(ehci_w90X900_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Wan ZongShun "); -MODULE_ALIAS("platform:w90p910-ehci"); -MODULE_LICENSE("GPL v2"); From 92b8608691bf129b1137be46a3a0058bcacc97a9 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 9 Aug 2019 14:37:10 -0700 Subject: [PATCH 064/159] dt-bindings: usb: renesas_gen3: Rename bindings documentation file to reflect IP block For consistency with the naming of (most) other documentation files for DT bindings for Renesas IP blocks rename the Renesas USB3.0 peripheral documentation file from renesas,usb3.txt to renesas,usb3-peri.txt This refines a recent rename from renesas_usb3.txt to renesas,usb3.txt. The motivation is to more accurately reflect the IP block documented in this file. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Sderlund Link: https://lore.kernel.org/r/20190809213710.31783-1-horms+renesas@verge.net.au Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/{renesas,usb3.txt => renesas,usb3-peri.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Documentation/devicetree/bindings/usb/{renesas,usb3.txt => renesas,usb3-peri.txt} (100%) diff --git a/Documentation/devicetree/bindings/usb/renesas,usb3.txt b/Documentation/devicetree/bindings/usb/renesas,usb3-peri.txt similarity index 100% rename from Documentation/devicetree/bindings/usb/renesas,usb3.txt rename to Documentation/devicetree/bindings/usb/renesas,usb3-peri.txt From 9fbbeb4eff076859891c022c151ad20eda83d983 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:30 +1000 Subject: [PATCH 065/159] usb: gadget: aspeed: Don't set port enable change bit on reset This bit should be only set when the port enable goes down, for example, on errors. Not when it gets set after a port reset. Some USB stacks seem to be sensitive to this and fails enumeration. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/hub.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 7c040f56100e..0755115fd90d 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -449,8 +449,15 @@ static void ast_vhub_change_port_stat(struct ast_vhub *vhub, USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_L1; - p->change |= chg; + /* + * We only set USB_PORT_STAT_C_ENABLE if we are disabling + * the port as per USB spec, otherwise MacOS gets upset + */ + if (p->status & USB_PORT_STAT_ENABLE) + chg &= ~USB_PORT_STAT_C_ENABLE; + + p->change = chg; ast_vhub_update_hub_ep1(vhub, port); } } From d042c35eb86a8ef13c4b5180fd6953a51d9e1efc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:31 +1000 Subject: [PATCH 066/159] usb: gadget: aspeed: Cleanup EP0 state on port reset Otherwise, we can have a stale state after a disconnect and reconnect causing errors on the first SETUP packet to the device. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 3 +++ drivers/usb/gadget/udc/aspeed-vhub/ep0.c | 9 +++++++++ drivers/usb/gadget/udc/aspeed-vhub/vhub.h | 1 + 3 files changed, 13 insertions(+) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 6b1b16b17d7d..678bbdbd0971 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -55,6 +55,9 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d) if (d->enabled) return; + /* Cleanup EP0 state */ + ast_vhub_reset_ep0(d); + /* Enable device and its EP0 interrupts */ reg = VHUB_DEV_EN_ENABLE_PORT | VHUB_DEV_EN_EP0_IN_ACK_IRQEN | diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c index e2927fb083cf..5e4714d7febb 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c @@ -459,6 +459,15 @@ static const struct usb_ep_ops ast_vhub_ep0_ops = { .free_request = ast_vhub_free_request, }; +void ast_vhub_reset_ep0(struct ast_vhub_dev *dev) +{ + struct ast_vhub_ep *ep = &dev->ep0; + + ast_vhub_nuke(ep, -EIO); + ep->ep0.state = ep0_state_token; +} + + void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep, struct ast_vhub_dev *dev) { diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index 4ed03d33a5a9..2e7ef387f4f0 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -507,6 +507,7 @@ void ast_vhub_init_hw(struct ast_vhub *vhub); /* ep0.c */ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack); void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep); +void ast_vhub_reset_ep0(struct ast_vhub_dev *dev); void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep, struct ast_vhub_dev *dev); int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len); From 7e420cef9047dc945b019dc235ec91c792c30f2d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:32 +1000 Subject: [PATCH 067/159] usb: gadget: aspeed: Fix EP0 stall handling When stalling EP0, we need to wait for an ACK interrupt, otherwise we may get out of sync on the next setup packet data phase. Also we need to ignore the direction when processing that interrupt as the HW reports a potential mismatch. Implement this by adding a stall state to EP0. This fixes some reported issues with mass storage and some hosts. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/ep0.c | 48 +++++++++++++++-------- drivers/usb/gadget/udc/aspeed-vhub/vhub.h | 1 + 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c index 5e4714d7febb..b64dca7933b0 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c @@ -105,18 +105,20 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep) (crq.bRequestType & USB_DIR_IN) ? "in" : "out", ep->ep0.state); - /* Check our state, cancel pending requests if needed */ - if (ep->ep0.state != ep0_state_token) { + /* + * Check our state, cancel pending requests if needed + * + * Note: Under some circumstances, we can get a new setup + * packet while waiting for the stall ack, just accept it. + * + * In any case, a SETUP packet in wrong state should have + * reset the HW state machine, so let's just log, nuke + * requests, move on. + */ + if (ep->ep0.state != ep0_state_token && + ep->ep0.state != ep0_state_stall) { EPDBG(ep, "wrong state\n"); ast_vhub_nuke(ep, -EIO); - - /* - * Accept the packet regardless, this seems to happen - * when stalling a SETUP packet that has an OUT data - * phase. - */ - ast_vhub_nuke(ep, 0); - goto stall; } /* Calculate next state for EP0 */ @@ -165,7 +167,7 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep) stall: EPDBG(ep, "stalling\n"); writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); - ep->ep0.state = ep0_state_status; + ep->ep0.state = ep0_state_stall; ep->ep0.dir_in = false; return; @@ -299,8 +301,8 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack) if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) || (!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) || (ep->ep0.dir_in != in_ack)) { + /* In that case, ignore interrupt */ dev_warn(dev, "irq state mismatch"); - stall = true; break; } /* @@ -335,12 +337,22 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack) dev_warn(dev, "status direction mismatch\n"); stall = true; } + break; + case ep0_state_stall: + /* + * There shouldn't be any request left, but nuke just in case + * otherwise the stale request will block subsequent ones + */ + ast_vhub_nuke(ep, -EIO); + break; } - /* Reset to token state */ - ep->ep0.state = ep0_state_token; - if (stall) + /* Reset to token state or stall */ + if (stall) { writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); + ep->ep0.state = ep0_state_stall; + } else + ep->ep0.state = ep0_state_token; } static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, @@ -390,8 +402,12 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, spin_lock_irqsave(&vhub->lock, flags); /* EP0 can only support a single request at a time */ - if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) { + if (!list_empty(&ep->queue) || + ep->ep0.state == ep0_state_token || + ep->ep0.state == ep0_state_stall) { dev_warn(dev, "EP0: Request in wrong state\n"); + EPVDBG(ep, "EP0: list_empty=%d state=%d\n", + list_empty(&ep->queue), ep->ep0.state); spin_unlock_irqrestore(&vhub->lock, flags); return -EBUSY; } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index 2e7ef387f4f0..00f922604a1c 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -257,6 +257,7 @@ enum ep0_state { ep0_state_token, ep0_state_data, ep0_state_status, + ep0_state_stall, }; /* From ef9d34686bae33de38b0198a4c6c1a50daab31d7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:33 +1000 Subject: [PATCH 068/159] usb: gadget: aspeed: Don't reject requests on suspended devices A disconnect may just suspend the hub in absence of a physical disconnect detection. If we start rejecting requests, the mass storage function gets into a spin trying to requeue the same request for ever and hangs. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 13 +++++++++---- drivers/usb/gadget/udc/aspeed-vhub/ep0.c | 2 +- drivers/usb/gadget/udc/aspeed-vhub/epn.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 678bbdbd0971..71e2416858fd 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -204,14 +204,19 @@ int ast_vhub_std_dev_request(struct ast_vhub_ep *ep, u16 wValue, wIndex; /* No driver, we shouldn't be enabled ... */ - if (!d->driver || !d->enabled || d->suspended) { + if (!d->driver || !d->enabled) { EPDBG(ep, - "Device is wrong state driver=%p enabled=%d" - " suspended=%d\n", - d->driver, d->enabled, d->suspended); + "Device is wrong state driver=%p enabled=%d\n", + d->driver, d->enabled); return std_req_stall; } + /* + * Note: we used to reject/stall requests while suspended, + * we don't do that anymore as we seem to have cases of + * mass storage getting very upset. + */ + /* First packet, grab speed */ if (d->gadget.speed == USB_SPEED_UNKNOWN) { d->gadget.speed = ep->vhub->speed; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c index b64dca7933b0..022b777b85f8 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c @@ -379,7 +379,7 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, return -EINVAL; /* Disabled device */ - if (ep->dev && (!ep->dev->enabled || ep->dev->suspended)) + if (ep->dev && !ep->dev->enabled) return -ESHUTDOWN; /* Data, no buffer and not internal ? */ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c index 35941dc125f9..7475c74aa5c5 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c @@ -352,7 +352,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req, /* Endpoint enabled ? */ if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx || - !ep->dev->enabled || ep->dev->suspended) { + !ep->dev->enabled) { EPDBG(ep, "Enqueuing request on wrong or disabled EP\n"); return -ESHUTDOWN; } From 155940a16d688b5376f7c955a5f53259c6fe5689 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:34 +1000 Subject: [PATCH 069/159] usb: gadget: aspeed: Check suspend/resume callback existence .. before calling them Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 71e2416858fd..5f7e3b6de531 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -458,7 +458,7 @@ static const struct usb_gadget_ops ast_vhub_udc_ops = { void ast_vhub_dev_suspend(struct ast_vhub_dev *d) { d->suspended = true; - if (d->driver) { + if (d->driver && d->driver->suspend) { spin_unlock(&d->vhub->lock); d->driver->suspend(&d->gadget); spin_lock(&d->vhub->lock); @@ -468,7 +468,7 @@ void ast_vhub_dev_suspend(struct ast_vhub_dev *d) void ast_vhub_dev_resume(struct ast_vhub_dev *d) { d->suspended = false; - if (d->driver) { + if (d->driver && d->driver->resume) { spin_unlock(&d->vhub->lock); d->driver->resume(&d->gadget); spin_lock(&d->vhub->lock); From 5f0625aaba5e4c0db75e942ff3c76516465a77d9 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:35 +1000 Subject: [PATCH 070/159] usb: gadget: aspeed: Rework the reset logic We had some dodgy code using the speed setting to decide whether a port reset would reset the device or just enable it. Instead, if the device is disabled and has a gadget attached, a reset will enable it. If it's already enabled, a reset will reset it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 58 +++++++++++------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 5f7e3b6de531..79d3cb6bd2e7 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -50,7 +50,7 @@ void ast_vhub_dev_irq(struct ast_vhub_dev *d) static void ast_vhub_dev_enable(struct ast_vhub_dev *d) { - u32 reg, hmsk; + u32 reg, hmsk, i; if (d->enabled) return; @@ -76,6 +76,20 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d) /* Set EP0 DMA buffer address */ writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA); + /* Clear stall on all EPs */ + for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { + struct ast_vhub_ep *ep = d->epns[i]; + + if (ep && (ep->epn.stalled || ep->epn.wedged)) { + ep->epn.stalled = false; + ep->epn.wedged = false; + ast_vhub_update_epn_stall(ep); + } + } + + /* Additional cleanups */ + d->wakeup_en = false; + d->suspended = false; d->enabled = true; } @@ -477,46 +491,28 @@ void ast_vhub_dev_resume(struct ast_vhub_dev *d) void ast_vhub_dev_reset(struct ast_vhub_dev *d) { - /* - * If speed is not set, we enable the port. If it is, - * send reset to the gadget and reset "speed". - * - * Speed is an indication that we have got the first - * setup packet to the device. - */ - if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) { - DDBG(d, "Reset at unknown speed of disabled device, enabling...\n"); - ast_vhub_dev_enable(d); - d->suspended = false; + /* No driver, just disable the device and return */ + if (!d->driver) { + ast_vhub_dev_disable(d); + return; } - if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) { - unsigned int i; - DDBG(d, "Reset at known speed of bound device, resetting...\n"); + /* If the port isn't enabled, just enable it */ + if (!d->enabled) { + DDBG(d, "Reset of disabled device, enabling...\n"); + ast_vhub_dev_enable(d); + } else { + DDBG(d, "Reset of enabled device, resetting...\n"); spin_unlock(&d->vhub->lock); - d->driver->reset(&d->gadget); + usb_gadget_udc_reset(&d->gadget, d->driver); spin_lock(&d->vhub->lock); /* - * Disable/re-enable HW, this will clear the address + * Disable and maybe re-enable HW, this will clear the address * and speed setting. */ ast_vhub_dev_disable(d); ast_vhub_dev_enable(d); - - /* Clear stall on all EPs */ - for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { - struct ast_vhub_ep *ep = d->epns[i]; - - if (ep && ep->epn.stalled) { - ep->epn.stalled = false; - ast_vhub_update_epn_stall(ep); - } - } - - /* Additional cleanups */ - d->wakeup_en = false; - d->suspended = false; } } From 8016759894089ef79415852d253eb54217b44539 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:36 +1000 Subject: [PATCH 071/159] usb: gadget: aspeed: Remove unused "suspended" flag The state bit in the hub is sufficient Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 4 ---- drivers/usb/gadget/udc/aspeed-vhub/vhub.h | 1 - 2 files changed, 5 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 79d3cb6bd2e7..4008e7a51188 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -89,7 +89,6 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d) /* Additional cleanups */ d->wakeup_en = false; - d->suspended = false; d->enabled = true; } @@ -110,7 +109,6 @@ static void ast_vhub_dev_disable(struct ast_vhub_dev *d) writel(0, d->regs + AST_VHUB_DEV_EN_CTRL); d->gadget.speed = USB_SPEED_UNKNOWN; d->enabled = false; - d->suspended = false; } static int ast_vhub_dev_feature(struct ast_vhub_dev *d, @@ -471,7 +469,6 @@ static const struct usb_gadget_ops ast_vhub_udc_ops = { void ast_vhub_dev_suspend(struct ast_vhub_dev *d) { - d->suspended = true; if (d->driver && d->driver->suspend) { spin_unlock(&d->vhub->lock); d->driver->suspend(&d->gadget); @@ -481,7 +478,6 @@ void ast_vhub_dev_suspend(struct ast_vhub_dev *d) void ast_vhub_dev_resume(struct ast_vhub_dev *d) { - d->suspended = false; if (d->driver && d->driver->resume) { spin_unlock(&d->vhub->lock); d->driver->resume(&d->gadget); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index 00f922604a1c..761919e220d3 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -354,7 +354,6 @@ struct ast_vhub_dev { struct usb_gadget_driver *driver; bool registered : 1; bool wakeup_en : 1; - bool suspended : 1; bool enabled : 1; /* Endpoint structures */ From cca1754c1d004370b86f41ad346130589fd41152 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:37 +1000 Subject: [PATCH 072/159] usb: gadget: aspeed: Improve debugging when nuking When nuking requests, it's useful to display how many were actually nuked. It has proven handy when debugging issues where EP0 went in a wrong state. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index db3628be38c0..9e698de4b1e8 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -65,14 +65,16 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req, void ast_vhub_nuke(struct ast_vhub_ep *ep, int status) { struct ast_vhub_req *req; - - EPDBG(ep, "Nuking\n"); + int count = 0; /* Beware, lock will be dropped & req-acquired by done() */ while (!list_empty(&ep->queue)) { req = list_first_entry(&ep->queue, struct ast_vhub_req, queue); ast_vhub_done(ep, req, status); + count++; } + if (count) + EPDBG(ep, "Nuked %d request(s)\n", count); } struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep, From 22a5d3ce0d032d843e36d431e78e6a7dd0efa193 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:38 +1000 Subject: [PATCH 073/159] usb: Add definitions for the USB2.0 hub TT requests Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- include/linux/usb/hcd.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index bab27ccc8ff5..367f9b39ac56 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -591,6 +591,10 @@ extern void usb_ep0_reinit(struct usb_device *); #define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, USB_REQ_GET_STATUS) #define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_SET_FEATURE) #define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_SET_FEATURE) +#define ClearTTBuffer HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_CLEAR_TT_BUFFER) +#define ResetTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_RESET_TT) +#define GetTTState HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, HUB_GET_TT_STATE) +#define StopTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_STOP_TT) /*-------------------------------------------------------------------------*/ From aa31332f67ac5418d0ca448153cf49ad09dba360 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 26 Jul 2019 15:05:39 +1000 Subject: [PATCH 074/159] usb: gadget: aspeed: Implement dummy hub TT requests We just accept them instead of stalling and return zeros on GetTTState. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/hub.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 0755115fd90d..19b3517e04c0 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -730,6 +730,12 @@ enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep, case ClearPortFeature: EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue); return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue); + case ClearTTBuffer: + case ResetTT: + case StopTT: + return std_req_complete; + case GetTTState: + return ast_vhub_simple_reply(ep, 0, 0, 0, 0); default: EPDBG(ep, "Unknown class request\n"); } From 115f3bad1a36791b5293aeebde342a95a516198e Mon Sep 17 00:00:00 2001 From: Matthias Maennich Date: Tue, 13 Aug 2019 13:17:06 +0100 Subject: [PATCH 075/159] usb-storage: remove single-use define for debugging USB_STORAGE was defined as "usb-storage: " and used in a single location as argument to printk. In order to be able to use the name 'USB_STORAGE', drop the definition and use the string directly for the printk call. Signed-off-by: Matthias Maennich Link: https://lore.kernel.org/r/20190813121733.52480-10-maennich@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/debug.h | 2 -- drivers/usb/storage/scsiglue.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 6d64f342f587..16ce06039a4d 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -29,8 +29,6 @@ #include -#define USB_STORAGE "usb-storage: " - #ifdef CONFIG_USB_STORAGE_DEBUG void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb); void usb_stor_show_sense(const struct us_data *us, unsigned char key, diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 05b80211290d..df4de8323eff 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -379,7 +379,7 @@ static int queuecommand_lck(struct scsi_cmnd *srb, /* check for state-transition errors */ if (us->srb != NULL) { - printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n", + printk(KERN_ERR "usb-storage: Error in %s: us->srb = %p\n", __func__, us->srb); return SCSI_MLQUEUE_HOST_BUSY; } From 4ed3350539aa931f58c939fcd803c7510584e143 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 13 Aug 2019 16:15:38 -0400 Subject: [PATCH 076/159] USB: usbfs: Add a capability flag for runtime suspend The recent commit 7794f486ed0b ("usbfs: Add ioctls for runtime power management") neglected to add a corresponding capability flag. This patch rectifies the omission. Signed-off-by: Alan Stern CC: Mayuresh Kulkarni Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.1908131613490.1941-100000@iolanthe.rowland.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 9 ++++++++- include/uapi/linux/usbdevice_fs.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 5db7b6dbcb44..24d4a801ca64 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -44,6 +44,12 @@ #include "usb.h" +#ifdef CONFIG_PM +#define MAYBE_CAP_SUSPEND USBDEVFS_CAP_SUSPEND +#else +#define MAYBE_CAP_SUSPEND 0 +#endif + #define USB_MAXBUS 64 #define USB_DEVICE_MAX (USB_MAXBUS * 128) #define USB_SG_SIZE 16384 /* split-size for large txs */ @@ -2310,7 +2316,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg) caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM | USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP | - USBDEVFS_CAP_DROP_PRIVILEGES | USBDEVFS_CAP_CONNINFO_EX; + USBDEVFS_CAP_DROP_PRIVILEGES | + USBDEVFS_CAP_CONNINFO_EX | MAYBE_CAP_SUSPEND; if (!ps->dev->bus->no_stop_on_short) caps |= USBDEVFS_CAP_BULK_CONTINUATION; if (ps->dev->bus->sg_tablesize) diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h index d24bbb6d3ca1..cf525cddeb94 100644 --- a/include/uapi/linux/usbdevice_fs.h +++ b/include/uapi/linux/usbdevice_fs.h @@ -158,6 +158,7 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_MMAP 0x20 #define USBDEVFS_CAP_DROP_PRIVILEGES 0x40 #define USBDEVFS_CAP_CONNINFO_EX 0x80 +#define USBDEVFS_CAP_SUSPEND 0x100 /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ From eceddc4071e39807f080913656252e4526227647 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 14 Aug 2019 16:24:17 +0300 Subject: [PATCH 077/159] usb: typec: fusb302: Remove unused properties Removing the deprecated fusb302 specific properties. There are no more platforms using them. Signed-off-by: Heikki Krogerus Reviewed-by: Hans de Goede Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20190814132419.39759-2-heikki.krogerus@linux.intel.com Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index c524088246ee..074fbb17fa8b 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1670,29 +1670,6 @@ static int init_gpio(struct fusb302_chip *chip) return 0; } -static int fusb302_composite_snk_pdo_array(struct fusb302_chip *chip) -{ - struct device *dev = chip->dev; - u32 max_uv, max_ua; - - chip->snk_pdo[0] = PDO_FIXED(5000, 400, PDO_FIXED_FLAGS); - - /* - * As max_snk_ma/mv/mw is not needed for tcpc_config, - * those settings should be passed in via sink PDO, so - * "fcs, max-sink-*" properties will be deprecated, to - * perserve compatibility with existing users of them, - * we read those properties to convert them to be a var - * PDO. - */ - if (device_property_read_u32(dev, "fcs,max-sink-microvolt", &max_uv) || - device_property_read_u32(dev, "fcs,max-sink-microamp", &max_ua)) - return 1; - - chip->snk_pdo[1] = PDO_VAR(5000, max_uv / 1000, max_ua / 1000); - return 2; -} - static int fusb302_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1701,7 +1678,6 @@ static int fusb302_probe(struct i2c_client *client, struct device *dev = &client->dev; const char *name; int ret = 0; - u32 v; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { dev_err(&client->dev, @@ -1721,11 +1697,10 @@ static int fusb302_probe(struct i2c_client *client, chip->tcpc_dev.fwnode = device_get_named_child_node(dev, "connector"); - if (!device_property_read_u32(dev, "fcs,operating-sink-microwatt", &v)) - chip->tcpc_config.operating_snk_mw = v / 1000; - /* Composite sink PDO */ - chip->tcpc_config.nr_snk_pdo = fusb302_composite_snk_pdo_array(chip); + chip->snk_pdo[0] = PDO_FIXED(5000, 400, PDO_FIXED_FLAGS); + + chip->tcpc_config.nr_snk_pdo = 1; chip->tcpc_config.snk_pdo = chip->snk_pdo; /* From c7316315a22164ee09059b8e148b52920a2521de Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 14 Aug 2019 16:24:18 +0300 Subject: [PATCH 078/159] dt-bindings: usb: fusb302: Remove deprecated properties There are no platforms using them anymore. Signed-off-by: Heikki Krogerus Reviewed-by: Hans de Goede Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20190814132419.39759-3-heikki.krogerus@linux.intel.com Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/fcs,fusb302.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/fcs,fusb302.txt b/Documentation/devicetree/bindings/usb/fcs,fusb302.txt index a5d011d2efc8..ba2e32d500c0 100644 --- a/Documentation/devicetree/bindings/usb/fcs,fusb302.txt +++ b/Documentation/devicetree/bindings/usb/fcs,fusb302.txt @@ -11,13 +11,6 @@ Required sub-node: Documentation/devicetree/bindings/connector/usb-connector.txt -Deprecated properties : -- fcs,max-sink-microvolt : Maximum sink voltage accepted by port controller -- fcs,max-sink-microamp : Maximum sink current accepted by port controller -- fcs,max-sink-microwatt : Maximum sink power accepted by port controller -- fcs,operating-sink-microwatt : Minimum amount of power accepted from a sink - when negotiating - Example: From 8f6244055bd33695ce28f6b9b454fc1ae05bcd9c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 14 Aug 2019 16:24:19 +0300 Subject: [PATCH 079/159] usb: typec: fusb302: Always provide fwnode for the port By registering a software fwnode for the port when the firmware does not supply one, we can always provide tcpm the connector capabilities by using the common USB connector device properties instead of using tcpc_config platform data. Signed-off-by: Heikki Krogerus Reviewed-by: Hans de Goede Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20190814132419.39759-4-heikki.krogerus@linux.intel.com Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 70 +++++++++++++++++++------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 074fbb17fa8b..93244d6c4bff 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -75,7 +75,6 @@ struct fusb302_chip { struct i2c_client *i2c_client; struct tcpm_port *tcpm_port; struct tcpc_dev tcpc_dev; - struct tcpc_config tcpc_config; struct regulator *vbus; @@ -1110,23 +1109,6 @@ done: mutex_unlock(&chip->lock); } -#define PDO_FIXED_FLAGS \ - (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM) - -static const u32 src_pdo[] = { - PDO_FIXED(5000, 400, PDO_FIXED_FLAGS), -}; - -static const struct tcpc_config fusb302_tcpc_config = { - .src_pdo = src_pdo, - .nr_src_pdo = ARRAY_SIZE(src_pdo), - .operating_snk_mw = 2500, - .type = TYPEC_PORT_DRP, - .data = TYPEC_PORT_DRD, - .default_role = TYPEC_SINK, - .alt_modes = NULL, -}; - static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev) { fusb302_tcpc_dev->init = tcpm_init; @@ -1670,6 +1652,38 @@ static int init_gpio(struct fusb302_chip *chip) return 0; } +#define PDO_FIXED_FLAGS \ + (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM) + +static const u32 src_pdo[] = { + PDO_FIXED(5000, 400, PDO_FIXED_FLAGS) +}; + +static const u32 snk_pdo[] = { + PDO_FIXED(5000, 400, PDO_FIXED_FLAGS) +}; + +static const struct property_entry port_props[] = { + PROPERTY_ENTRY_STRING("data-role", "dual"), + PROPERTY_ENTRY_STRING("power-role", "dual"), + PROPERTY_ENTRY_STRING("try-power-role", "sink"), + PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo), + PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo), + PROPERTY_ENTRY_U32("op-sink-microwatt", 2500), + { } +}; + +static struct fwnode_handle *fusb302_fwnode_get(struct device *dev) +{ + struct fwnode_handle *fwnode; + + fwnode = device_get_named_child_node(dev, "connector"); + if (!fwnode) + fwnode = fwnode_create_software_node(port_props, NULL); + + return fwnode; +} + static int fusb302_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1690,19 +1704,8 @@ static int fusb302_probe(struct i2c_client *client, chip->i2c_client = client; chip->dev = &client->dev; - chip->tcpc_config = fusb302_tcpc_config; - chip->tcpc_dev.config = &chip->tcpc_config; mutex_init(&chip->lock); - chip->tcpc_dev.fwnode = - device_get_named_child_node(dev, "connector"); - - /* Composite sink PDO */ - chip->snk_pdo[0] = PDO_FIXED(5000, 400, PDO_FIXED_FLAGS); - - chip->tcpc_config.nr_snk_pdo = 1; - chip->tcpc_config.snk_pdo = chip->snk_pdo; - /* * Devicetree platforms should get extcon via phandle (not yet * supported). On ACPI platforms, we get the name from a device prop. @@ -1737,8 +1740,15 @@ static int fusb302_probe(struct i2c_client *client, goto destroy_workqueue; } + chip->tcpc_dev.fwnode = fusb302_fwnode_get(dev); + if (IS_ERR(chip->tcpc_dev.fwnode)) { + ret = PTR_ERR(chip->tcpc_dev.fwnode); + goto destroy_workqueue; + } + chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { + fwnode_handle_put(chip->tcpc_dev.fwnode); ret = PTR_ERR(chip->tcpm_port); if (ret != -EPROBE_DEFER) dev_err(dev, "cannot register tcpm port, ret=%d", ret); @@ -1760,6 +1770,7 @@ static int fusb302_probe(struct i2c_client *client, tcpm_unregister_port: tcpm_unregister_port(chip->tcpm_port); + fwnode_handle_put(chip->tcpc_dev.fwnode); destroy_workqueue: destroy_workqueue(chip->wq); @@ -1775,6 +1786,7 @@ static int fusb302_remove(struct i2c_client *client) cancel_work_sync(&chip->irq_work); cancel_delayed_work_sync(&chip->bc_lvl_handler); tcpm_unregister_port(chip->tcpm_port); + fwnode_handle_put(chip->tcpc_dev.fwnode); destroy_workqueue(chip->wq); fusb302_debugfs_exit(chip); From b98052e9b5c4878f0e13b11cd5b7a04242c2d848 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 9 Aug 2019 18:33:14 +0100 Subject: [PATCH 080/159] USB: storage: isd200: remove redundant assignment to variable sendToTransport The variable sendToTransport is being initialized with a value that is never read and is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Acked-by: Alan Stern Link: https://lore.kernel.org/r/20190809173314.4623-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/isd200.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 2b474d60b4db..28e1128d53a4 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1511,7 +1511,7 @@ static int isd200_Initialization(struct us_data *us) static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) { - int sendToTransport = 1, orig_bufflen; + int sendToTransport, orig_bufflen; union ata_cdb ataCdb; /* Make sure driver was initialized */ From 6d03c140db2e9296407bef470473e0e8be31b2c5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 15 Aug 2019 14:59:03 +0200 Subject: [PATCH 081/159] USB: phy: fsl-usb: convert platform driver to use dev_groups Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Acked-by: Felipe Balbi Link: https://lore.kernel.org/r/20190815125903.GA17065@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-fsl-usb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index f7c96d209eda..f6a037b5e9ef 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -1043,6 +1043,11 @@ static ssize_t show_fsl_usb2_otg_state(struct device *dev, static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); +static struct attribute *fsl_otg_attrs[] = { + &dev_attr_fsl_usb2_otg_state.attr, + NULL, +}; +ATTRIBUTE_GROUPS(fsl_otg); /* Char driver interface to control some OTG input */ @@ -1132,10 +1137,6 @@ static int fsl_otg_probe(struct platform_device *pdev) return ret; } - ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); - if (ret) - dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); - return ret; } @@ -1152,8 +1153,6 @@ static int fsl_otg_remove(struct platform_device *pdev) kfree(fsl_otg_dev->phy.otg); kfree(fsl_otg_dev); - device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); - unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); if (pdata->exit) @@ -1168,6 +1167,7 @@ struct platform_driver fsl_otg_driver = { .driver = { .name = driver_name, .owner = THIS_MODULE, + .dev_groups = fsl_otg_groups, }, }; From 9f4bc2ed74086e51cfc75754adfcbff86f28597a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 Aug 2019 20:43:38 +0200 Subject: [PATCH 082/159] usb: typec: tcpm: Use usb_debug_root as root for our debugfs entry Use usb_debug_root as root for our debugfs entry instead of creating our own subdirectory under the debugfs root. Another patch in this series will make the same change to the fusb302 driver, which also uses dev_name() (on the same device) for the debugfs entry name. So we also prefix dev_name() with "tcpm-" here to avoid a name conflict. Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20190817184340.64086-1-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ec9460d9d330..166b28562395 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -571,17 +572,13 @@ static int tcpm_debug_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(tcpm_debug); -static struct dentry *rootdir; - static void tcpm_debugfs_init(struct tcpm_port *port) { - mutex_init(&port->logbuffer_lock); - /* /sys/kernel/debug/tcpm/usbcX */ - if (!rootdir) - rootdir = debugfs_create_dir("tcpm", NULL); + char name[NAME_MAX]; - port->dentry = debugfs_create_file(dev_name(port->dev), - S_IFREG | 0444, rootdir, + mutex_init(&port->logbuffer_lock); + snprintf(name, NAME_MAX, "tcpm-%s", dev_name(port->dev)); + port->dentry = debugfs_create_file(name, S_IFREG | 0444, usb_debug_root, port, &tcpm_debug_fops); } @@ -597,10 +594,6 @@ static void tcpm_debugfs_exit(struct tcpm_port *port) mutex_unlock(&port->logbuffer_lock); debugfs_remove(port->dentry); - if (list_empty(&rootdir->d_subdirs)) { - debugfs_remove(rootdir); - rootdir = NULL; - } } #else From b7cd6f64ff577d88ff532d9bdbdc7bbc53f27616 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 Aug 2019 20:43:39 +0200 Subject: [PATCH 083/159] usb: typec: fusb302: Use usb_debug_root as root for our debugfs entry Use usb_debug_root as root for our debugfs entry instead of creating our own subdirectory under the debugfs root. Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20190817184340.64086-2-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 93244d6c4bff..603ab87b466e 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -206,23 +207,19 @@ static int fusb302_debug_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(fusb302_debug); -static struct dentry *rootdir; - static void fusb302_debugfs_init(struct fusb302_chip *chip) { - mutex_init(&chip->logbuffer_lock); - if (!rootdir) - rootdir = debugfs_create_dir("fusb302", NULL); + char name[NAME_MAX]; - chip->dentry = debugfs_create_file(dev_name(chip->dev), - S_IFREG | 0444, rootdir, + mutex_init(&chip->logbuffer_lock); + snprintf(name, NAME_MAX, "fusb302-%s", dev_name(chip->dev)); + chip->dentry = debugfs_create_file(name, S_IFREG | 0444, usb_debug_root, chip, &fusb302_debug_fops); } static void fusb302_debugfs_exit(struct fusb302_chip *chip) { debugfs_remove(chip->dentry); - debugfs_remove(rootdir); } #else From 1e296b5be40d309a1585c14bc55da6ff6a29ecf0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 Aug 2019 20:43:40 +0200 Subject: [PATCH 084/159] usb: typec: fusb302: Call fusb302_debugfs_init earlier tcpm_register_port() will call some of the fusb302 code's callbacks wich in turn will call fusb302_log(). So we need to call fusb302_debugfs_init() before we call tcpm_register_port(). This fixes the following warning, which was caused by the logbuffer_lock not yet being initialized (which is done by fusb302_debugfs_init): DEBUG_LOCKS_WARN_ON(lock->magic != lock) WARNING: CPU: 0 PID: 1306 at kernel/locking/mutex.c:912 __mutex_lock+0x978/0x9a0 Modules linked in: fusb302(+) tcpm pi3usb30532 typec bq24190_charger snd_soc_sst_cht_bsw_rt5645 mei_hdcp dwc3 intel_rapl_msr udc_core ulpi gpio_keys intel_powerclamp coretemp kvm_intel brcmfmac kvm brcmutil joydev cfg80211 wdat_wdt irqbypass pcspkr intel_cstate extcon_intel_cht_wc i2c_cht_wc(E) snd_intel_sst_acpi snd_intel_sst_core snd_soc_rt5645 snd_soc_sst_atom_hifi2_platform snd_soc_acpi_intel_match snd_soc_rl6231 snd_soc_acpi intel_xhci_usb_role_switch roles hci_uart snd_soc_core btqca mei_txe btrtl processor_thermal_device mei snd_hdmi_lpe_audio lpc_ich snd_compress btbcm intel_rapl_common ac97_bus dwc3_pci snd_pcm_dmaengine intel_soc_dts_iosf btintel snd_seq bluetooth snd_seq_device snd_pcm intel_cht_int33fe_musb snd_timer intel_cht_int33fe_typec intel_hid intel_cht_int33fe_common sparse_keymap snd ecdh_generic goodix rfkill soundcore ecc spi_pxa2xx_platform max17042_battery dw_dmac int3406_thermal dptf_power acpi_pad soc_button_array int3400_thermal int3403_thermal gpd_pocket_fan intel_int0002_vgpio int340x_thermal_zone acpi_thermal_rel dm_crypt mmc_block i915 crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel i2c_algo_bit drm_kms_helper drm video sdhci_acpi sdhci mmc_core pwm_lpss_platform pwm_lpss i2c_dev CPU: 0 PID: 1306 Comm: systemd-udevd Tainted: G E 5.3.0-rc4+ #83 Hardware name: Default string Default string/Default string, BIOS 5.11 06/28/2017 RIP: 0010:__mutex_lock+0x978/0x9a0 Code: c0 0f 84 26 f7 ff ff 44 8b 05 24 25 c8 00 45 85 c0 0f 85 16 f7 ff ff 48 c7 c6 da 55 2f ae 48 c7 c7 98 8c 2d ae e8 a0 f9 5c ff <0f> 0b e9 fc f6 ff ff 4c 89 f0 4d 89 fe 49 89 c7 e9 cf fa ff ff e8 RSP: 0018:ffffb7a8c0523800 EFLAGS: 00010286 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000002 RSI: 0000000000000001 RDI: 0000000000000246 RBP: ffffb7a8c05238c0 R08: 0000000000000000 R09: 0000000000000000 R10: ffffb7a8c0523648 R11: 0000000000000030 R12: 0000000000000000 R13: ffffb7a8c0523990 R14: ffff9bf22f70c028 R15: ffff9bf22f70c360 FS: 00007f39ca234940(0000) GS:ffff9bf237400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f1f108481a0 CR3: 0000000271f28000 CR4: 00000000001006f0 Call Trace: ? find_held_lock+0x39/0x90 ? _fusb302_log+0x81/0x1d0 [fusb302] ? vsnprintf+0x3aa/0x4f0 ? _fusb302_log+0x81/0x1d0 [fusb302] _fusb302_log+0x81/0x1d0 [fusb302] ... Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20190817184340.64086-3-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 603ab87b466e..ed8655c6af8c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1728,6 +1728,7 @@ static int fusb302_probe(struct i2c_client *client, INIT_WORK(&chip->irq_work, fusb302_irq_work); INIT_DELAYED_WORK(&chip->bc_lvl_handler, fusb302_bc_lvl_handler_work); init_tcpc_dev(&chip->tcpc_dev); + fusb302_debugfs_init(chip); if (client->irq) { chip->gpio_int_n_irq = client->irq; @@ -1760,7 +1761,6 @@ static int fusb302_probe(struct i2c_client *client, goto tcpm_unregister_port; } enable_irq_wake(chip->gpio_int_n_irq); - fusb302_debugfs_init(chip); i2c_set_clientdata(client, chip); return ret; @@ -1769,6 +1769,7 @@ tcpm_unregister_port: tcpm_unregister_port(chip->tcpm_port); fwnode_handle_put(chip->tcpc_dev.fwnode); destroy_workqueue: + fusb302_debugfs_exit(chip); destroy_workqueue(chip->wq); return ret; From a599e48662b4b505bef45d4831061f9d50703e17 Mon Sep 17 00:00:00 2001 From: Jacob Huisman Date: Thu, 15 Aug 2019 18:42:10 +0100 Subject: [PATCH 085/159] usb: usb-skeleton: make comment block in line with coding style Comment block was not in accordance with coding style. Fixes two checkpatch warnings: WARNING: Block comments use * on subsequent lines WARNING: Block comments use a trailing */ on a separate line Signed-off-by: Jacob Huisman Link: https://lore.kernel.org/r/20190815174210.580-1-jacobhuisman@kernelthusiast.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usb-skeleton.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index f101347e3ea3..c31d17d05810 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -35,9 +35,11 @@ MODULE_DEVICE_TABLE(usb, skel_table); /* our private defines. if this grows any larger, use your own .h file */ #define MAX_TRANSFER (PAGE_SIZE - 512) -/* MAX_TRANSFER is chosen so that the VM is not stressed by - allocations > PAGE_SIZE and the number of packets in a page - is an integer 512 is the largest possible packet on EHCI */ +/* + * MAX_TRANSFER is chosen so that the VM is not stressed by + * allocations > PAGE_SIZE and the number of packets in a page + * is an integer 512 is the largest possible packet on EHCI + */ #define WRITES_IN_FLIGHT 8 /* arbitrarily chosen */ From b08a6259a141baba98c57524ee4283fd91445bb8 Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Wed, 14 Aug 2019 14:29:24 -0700 Subject: [PATCH 086/159] usb: usbfs: only account once for mmap()'ed usb memory usage Memory usage for USB memory allocated via mmap() is already accounted for at mmap() time; no need to account for it again at submiturb time. Signed-off-by: Gavin Li Acked-by: Alan Stern Link: https://lore.kernel.org/r/20190814212924.10381-1-gavinli@thegavinli.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 24d4a801ca64..63f23f567680 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1664,7 +1664,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb if (as->usbm) num_sgs = 0; - u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + + u += sizeof(struct async) + sizeof(struct urb) + + (as->usbm ? 0 : uurb->buffer_length) + num_sgs * sizeof(struct scatterlist); ret = usbfs_increase_memory_usage(u); if (ret) From 0709831a50d31b3caf2237e8d7fe89e15b0d919d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 17 Aug 2019 09:55:20 +0300 Subject: [PATCH 087/159] usb: host: ohci-pxa27x: Fix and & vs | typo The code is supposed to clear the RH_A_NPS and RH_A_PSM bits, but it's a no-op because of the & vs | typo. This bug predates git and it was only discovered using static analysis so it must not affect too many people in real life. Signed-off-by: Dan Carpenter Acked-by: Alan Stern Link: https://lore.kernel.org/r/20190817065520.GA29951@mwanda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pxa27x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 3e2474959735..7679fb583e41 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -148,7 +148,7 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode) uhcrhda |= RH_A_NPS; break; case PMM_GLOBAL_MODE: - uhcrhda &= ~(RH_A_NPS & RH_A_PSM); + uhcrhda &= ~(RH_A_NPS | RH_A_PSM); break; case PMM_PERPORT_MODE: uhcrhda &= ~(RH_A_NPS); From 7b81cb6bddd2c4f2489506771070924bd0ae9902 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 16 Aug 2019 08:24:32 +0200 Subject: [PATCH 088/159] usb: add a HCD_DMA flag instead of guestimating DMA capabilities The usb core is the only major place in the kernel that checks for a non-NULL device dma_mask to see if a device is DMA capable. This is generally a bad idea, as all major busses always set up a DMA mask, even if the device is not DMA capable - in fact bus layers like PCI can't even know if a device is DMA capable at enumeration time. This leads to lots of workaround in HCD drivers, and also prevented us from setting up a DMA mask for platform devices by default last time we tried. Replace this guess with an explicit HCD_DMA that is set by drivers that appear to have DMA support. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190816062435.881-4-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon-usb/octeon-hcd.c | 2 +- drivers/usb/core/hcd.c | 1 - drivers/usb/dwc2/hcd.c | 6 +++--- drivers/usb/host/ehci-grlib.c | 2 +- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-pmcmsp.c | 2 +- drivers/usb/host/ehci-ppc-of.c | 2 +- drivers/usb/host/ehci-ps3.c | 2 +- drivers/usb/host/ehci-sh.c | 2 +- drivers/usb/host/ehci-xilinx-of.c | 2 +- drivers/usb/host/fhci-hcd.c | 2 +- drivers/usb/host/fotg210-hcd.c | 2 +- drivers/usb/host/imx21-hcd.c | 2 +- drivers/usb/host/isp116x-hcd.c | 6 ------ drivers/usb/host/isp1362-hcd.c | 5 ----- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/host/ohci-ppc-of.c | 2 +- drivers/usb/host/ohci-ps3.c | 2 +- drivers/usb/host/ohci-sa1111.c | 2 +- drivers/usb/host/ohci-sm501.c | 2 +- drivers/usb/host/ohci-tmio.c | 2 +- drivers/usb/host/oxu210hp-hcd.c | 3 --- drivers/usb/host/r8a66597-hcd.c | 6 ------ drivers/usb/host/sl811-hcd.c | 6 ------ drivers/usb/host/u132-hcd.c | 2 -- drivers/usb/host/uhci-grlib.c | 2 +- drivers/usb/host/uhci-pci.c | 2 +- drivers/usb/host/uhci-platform.c | 2 +- drivers/usb/host/xhci.c | 2 +- drivers/usb/isp1760/isp1760-core.c | 3 --- drivers/usb/isp1760/isp1760-if.c | 1 - drivers/usb/musb/musb_host.c | 2 +- drivers/usb/renesas_usbhs/mod_host.c | 2 +- include/linux/usb.h | 1 - include/linux/usb/hcd.h | 7 +++++-- 35 files changed, 31 insertions(+), 62 deletions(-) diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/staging/octeon-usb/octeon-hcd.c index cd2b777073c4..a5321cc692c5 100644 --- a/drivers/staging/octeon-usb/octeon-hcd.c +++ b/drivers/staging/octeon-usb/octeon-hcd.c @@ -3512,7 +3512,7 @@ static const struct hc_driver octeon_hc_driver = { .product_desc = "Octeon Host Controller", .hcd_priv_size = sizeof(struct octeon_hcd), .irq = octeon_usb_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, .start = octeon_usb_start, .stop = octeon_usb_stop, .urb_enqueue = octeon_usb_urb_enqueue, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8592c0344fe8..add2af4af766 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2454,7 +2454,6 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver, hcd->self.controller = dev; hcd->self.sysdev = sysdev; hcd->self.bus_name = bus_name; - hcd->self.uses_dma = (sysdev->dma_mask != NULL); timer_setup(&hcd->rh_timer, rh_timer_func, 0); #ifdef CONFIG_PM diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 111787a137ee..81afe553aa66 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5062,13 +5062,13 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) dwc2_hc_driver.reset_device = dwc2_reset_device; } + if (hsotg->params.host_dma) + dwc2_hc_driver.flags |= HCD_DMA; + hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev)); if (!hcd) goto error1; - if (!hsotg->params.host_dma) - hcd->self.uses_dma = 0; - hcd->has_tt = 1; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 656b8c08efc8..a2c3b4ec8a8b 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -30,7 +30,7 @@ static const struct hc_driver ehci_grlib_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9da7e22848c9..cf2b7ae93b7e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1193,7 +1193,7 @@ static const struct hc_driver ehci_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 46e160370d6e..a2b610dbedfc 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -250,7 +250,7 @@ static const struct hc_driver ehci_msp_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 576f7d79ad4e..6bbaee74f7e7 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -31,7 +31,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 454d8c624a3f..fb52133c3557 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -59,7 +59,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { .product_desc = "PS3 EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd), .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, .reset = ps3_ehci_hc_reset, .start = ehci_run, .stop = ehci_stop, diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index ef75b9d70eb4..2afde14dc425 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -33,7 +33,7 @@ static const struct hc_driver ehci_sh_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, + .flags = HCD_USB2 | HCD_DMA | HCD_MEMORY | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index d2a27578e440..67a6ee8cb5d8 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -66,7 +66,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 48fe9e6c2465..04733876c9c6 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -538,7 +538,7 @@ static const struct hc_driver fhci_driver = { /* generic hardware linkage */ .irq = fhci_irq, - .flags = HCD_USB11 | HCD_MEMORY, + .flags = HCD_DMA | HCD_USB11 | HCD_MEMORY, /* basic lifecycle operation */ .start = fhci_start, diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 0dbfa5c10703..9e0c98d6bdb0 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -5508,7 +5508,7 @@ static const struct hc_driver fotg210_fotg210_hc_driver = { * generic hardware linkage */ .irq = fotg210_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, /* * basic lifecycle operations diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index e406c5459a97..5835f9966204 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1771,7 +1771,7 @@ static const struct hc_driver imx21_hc_driver = { .product_desc = "IMX21 USB Host Controller", .hcd_priv_size = sizeof(struct imx21), - .flags = HCD_USB11, + .flags = HCD_DMA | HCD_USB11, .irq = imx21_irq, .reset = imx21_hc_reset, diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 74da136d322a..a87c0b26279e 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1581,12 +1581,6 @@ static int isp116x_probe(struct platform_device *pdev) irq = ires->start; irqflags = ires->flags & IRQF_TRIGGER_MASK; - if (pdev->dev.dma_mask) { - DBG("DMA not supported\n"); - ret = -EINVAL; - goto err1; - } - if (!request_mem_region(addr->start, 2, hcd_name)) { ret = -EBUSY; goto err1; diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 28bf8bfb091e..96f8daa11f25 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2645,11 +2645,6 @@ static int isp1362_probe(struct platform_device *pdev) if (pdev->num_resources < 3) return -ENODEV; - if (pdev->dev.dma_mask) { - DBG(1, "won't do DMA"); - return -ENODEV; - } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) return -ENODEV; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index b457fdaff297..1eb8d17e19db 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1178,7 +1178,7 @@ static const struct hc_driver ohci_hc_driver = { * generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_MEMORY | HCD_USB11, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB11, /* * basic lifecycle operations diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 76a9b40b08f1..45f7cceb6df3 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -50,7 +50,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = { * generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, + .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, /* * basic lifecycle operations diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 395f9d3bc849..f77cd6af0ccf 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -46,7 +46,7 @@ static const struct hc_driver ps3_ohci_hc_driver = { .product_desc = "PS3 OHCI Host Controller", .hcd_priv_size = sizeof(struct ohci_hcd), .irq = ohci_irq, - .flags = HCD_MEMORY | HCD_USB11, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB11, .reset = ps3_ohci_hc_reset, .start = ps3_ohci_hc_start, .stop = ohci_stop, diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index ebec9a7699e3..8e19a5eb5b62 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -84,7 +84,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = { * generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, + .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, /* * basic lifecycle operations diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index c158cda9e4b9..0b2aea6e28d4 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -49,7 +49,7 @@ static const struct hc_driver ohci_sm501_hc_driver = { * generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, + .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, /* * basic lifecycle operations diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index fb6f5e9ae5c6..221593d75556 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -156,7 +156,7 @@ static const struct hc_driver ohci_tmio_hc_driver = { /* generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, + .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, /* basic lifecycle operations */ .start = ohci_tmio_start, diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 09cc19df798e..e67242e437ed 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -3088,9 +3088,6 @@ static int oxu_reset(struct usb_hcd *hcd) INIT_LIST_HEAD(&oxu->urb_list); oxu->urb_len = 0; - /* FIMXE */ - hcd->self.controller->dma_mask = NULL; - if (oxu->is_otg) { oxu->caps = hcd->regs + OXU_OTG_CAP_OFFSET; oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \ diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 42668aeca57c..0c03ac6b0213 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2411,12 +2411,6 @@ static int r8a66597_probe(struct platform_device *pdev) if (usb_disabled()) return -ENODEV; - if (pdev->dev.dma_mask) { - ret = -EINVAL; - dev_err(&pdev->dev, "dma not supported\n"); - goto clean_up; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 5b061e599948..72a34a1eb618 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1632,12 +1632,6 @@ sl811h_probe(struct platform_device *dev) irq = ires->start; irqflags = ires->flags & IRQF_TRIGGER_MASK; - /* refuse to confuse usbcore */ - if (dev->dev.dma_mask) { - dev_dbg(&dev->dev, "no we won't dma\n"); - return -EINVAL; - } - /* the chip may be wired for either kind of addressing */ addr = platform_get_resource(dev, IORESOURCE_MEM, 0); data = platform_get_resource(dev, IORESOURCE_MEM, 1); diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 400c40bc43a6..4efee34f154f 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3077,8 +3077,6 @@ static int u132_probe(struct platform_device *pdev) retval = ftdi_read_pcimem(pdev, roothub.a, &rh_a); if (retval) return retval; - if (pdev->dev.dma_mask) - return -EINVAL; hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index 2103b1ed0f8f..0a201a73b196 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -63,7 +63,7 @@ static const struct hc_driver uhci_grlib_hc_driver = { /* Generic hardware linkage */ .irq = uhci_irq, - .flags = HCD_MEMORY | HCD_USB11, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB11, /* Basic lifecycle operations */ .reset = uhci_grlib_init, diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 0dd944277c99..0fa3d72bae26 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -261,7 +261,7 @@ static const struct hc_driver uhci_driver = { /* Generic hardware linkage */ .irq = uhci_irq, - .flags = HCD_USB11, + .flags = HCD_DMA | HCD_USB11, /* Basic lifecycle operations */ .reset = uhci_pci_init, diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 89700e26fb29..70dbd95c3f06 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -41,7 +41,7 @@ static const struct hc_driver uhci_platform_hc_driver = { /* Generic hardware linkage */ .irq = uhci_irq, - .flags = HCD_MEMORY | HCD_USB11, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB11, /* Basic lifecycle operations */ .reset = uhci_platform_init, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 03d1e552769b..e315c0158e90 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5217,7 +5217,7 @@ static const struct hc_driver xhci_hc_driver = { * generic hardware linkage */ .irq = xhci_irq, - .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED, /* * basic lifecycle operations diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index 55b94fd10331..fdeb4cf97cc5 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -120,9 +120,6 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled)) return -ENODEV; - /* prevent usb-core allocating DMA pages */ - dev->dma_mask = NULL; - isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); if (!isp) return -ENOMEM; diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c index 241a00d75027..07cc82ff327c 100644 --- a/drivers/usb/isp1760/isp1760-if.c +++ b/drivers/usb/isp1760/isp1760-if.c @@ -139,7 +139,6 @@ static int isp1761_pci_probe(struct pci_dev *dev, pci_set_master(dev); - dev->dev.dma_mask = NULL; ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev, devflags); if (ret < 0) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index eb308ec35c66..5a44b70372d9 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2689,7 +2689,7 @@ static const struct hc_driver musb_hc_driver = { .description = "musb-hcd", .product_desc = "MUSB HDRC host driver", .hcd_priv_size = sizeof(struct musb *), - .flags = HCD_USB2 | HCD_MEMORY, + .flags = HCD_USB2 | HCD_DMA | HCD_MEMORY, /* not using irq handler or reset hooks from usbcore, since * those must be shared with peripheral code for OTG configs diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index ddd3be48f948..ae54221011c3 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -1283,7 +1283,7 @@ static const struct hc_driver usbhsh_driver = { /* * generic hardware linkage */ - .flags = HCD_USB2, + .flags = HCD_DMA | HCD_USB2, .start = usbhsh_host_start, .stop = usbhsh_host_stop, diff --git a/include/linux/usb.h b/include/linux/usb.h index b548c530f988..e656e7b4b1e4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -426,7 +426,6 @@ struct usb_bus { struct device *sysdev; /* as seen from firmware or bus */ int busnum; /* Bus number (in order of reg) */ const char *bus_name; /* stable id (PCI slot_name etc) */ - u8 uses_dma; /* Does the host controller use DMA? */ u8 uses_pio_for_control; /* * Does the host controller use PIO * for control transfers? diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index a20e7815d814..8d3869c7de85 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -256,6 +256,7 @@ struct hc_driver { int flags; #define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ +#define HCD_DMA 0x0002 /* HC uses DMA */ #define HCD_SHARED 0x0004 /* Two (or more) usb_hcds share HW */ #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 */ @@ -422,8 +423,10 @@ static inline bool hcd_periodic_completion_in_progress(struct usb_hcd *hcd, return hcd->high_prio_bh.completing_ep == ep; } -#define hcd_uses_dma(hcd) \ - (IS_ENABLED(CONFIG_HAS_DMA) && (hcd)->self.uses_dma) +static inline bool hcd_uses_dma(struct usb_hcd *hcd) +{ + return IS_ENABLED(CONFIG_HAS_DMA) && (hcd->driver->flags & HCD_DMA); +} extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, From 5d6ff300f0114e64f4ad911d256ac1587204ac5d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 16 Aug 2019 08:24:33 +0200 Subject: [PATCH 089/159] usb/max3421: remove the dummy {un,}map_urb_for_dma methods Now that we have an explicit HCD_DMA flag, there is not need to override these methods. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190816062435.881-5-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/max3421-hcd.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index afa321ab55fc..8819f502b6a6 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1800,21 +1800,6 @@ max3421_bus_resume(struct usb_hcd *hcd) return -1; } -/* - * The SPI driver already takes care of DMA-mapping/unmapping, so no - * reason to do it twice. - */ -static int -max3421_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) -{ - return 0; -} - -static void -max3421_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) -{ -} - static const struct hc_driver max3421_hcd_desc = { .description = "max3421", .product_desc = DRIVER_DESC, @@ -1826,8 +1811,6 @@ static const struct hc_driver max3421_hcd_desc = { .get_frame_number = max3421_get_frame_number, .urb_enqueue = max3421_urb_enqueue, .urb_dequeue = max3421_urb_dequeue, - .map_urb_for_dma = max3421_map_urb_for_dma, - .unmap_urb_for_dma = max3421_unmap_urb_for_dma, .endpoint_disable = max3421_endpoint_disable, .hub_status_data = max3421_hub_status_data, .hub_control = max3421_hub_control, From bd5defaee872da9b81e3c72045eb6794445cd2e6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 16 Aug 2019 08:24:34 +0200 Subject: [PATCH 090/159] dma-mapping: remove is_device_dma_capable No users left. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190816062435.881-6-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- include/linux/dma-mapping.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index f7d1eea32c78..14702e2d6fa8 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -149,11 +149,6 @@ static inline int valid_dma_direction(int dma_direction) (dma_direction == DMA_FROM_DEVICE)); } -static inline int is_device_dma_capable(struct device *dev) -{ - return dev->dma_mask != NULL && *dev->dma_mask != DMA_MASK_NONE; -} - #ifdef CONFIG_DMA_DECLARE_COHERENT /* * These three functions are only for dma allocator. From cdfee5623290bc893f595636b44fa28e8207c5b3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 16 Aug 2019 08:24:35 +0200 Subject: [PATCH 091/159] driver core: initialize a default DMA mask for platform device We still treat devices without a DMA mask as defaulting to 32-bits for both mask, but a few releases ago we've started warning about such cases, as they require special cases to work around this sloppyness. Add a dma_mask field to struct platform_device so that we can initialize the dma_mask pointer in struct device and initialize both masks to 32-bits by default, replacing similar functionality in m68k and powerpc. The arch_setup_pdev_archdata hooks is now unused and removed. Note that the code looks a little odd with the various conditionals because we have to support platform_device structures that are statically allocated. Signed-off-by: Christoph Hellwig Acked-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20190816062435.881-7-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- arch/m68k/kernel/dma.c | 9 ------- arch/powerpc/kernel/setup-common.c | 6 ----- arch/sh/boards/mach-ap325rxa/setup.c | 1 - arch/sh/boards/mach-ecovec24/setup.c | 2 -- arch/sh/boards/mach-kfr2r09/setup.c | 1 - arch/sh/boards/mach-migor/setup.c | 1 - arch/sh/boards/mach-se/7724/setup.c | 2 -- drivers/base/platform.c | 37 ++++++++++++---------------- include/linux/platform_device.h | 2 +- 9 files changed, 17 insertions(+), 44 deletions(-) diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c index 30cd59caf037..447849d1d645 100644 --- a/arch/m68k/kernel/dma.c +++ b/arch/m68k/kernel/dma.c @@ -79,12 +79,3 @@ void arch_sync_dma_for_device(struct device *dev, phys_addr_t handle, break; } } - -void arch_setup_pdev_archdata(struct platform_device *pdev) -{ - if (pdev->dev.coherent_dma_mask == DMA_MASK_NONE && - pdev->dev.dma_mask == NULL) { - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - } -} diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 1f8db666468d..5e6543aba1b3 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -778,12 +778,6 @@ void ppc_printk_progress(char *s, unsigned short hex) pr_info("%s\n", s); } -void arch_setup_pdev_archdata(struct platform_device *pdev) -{ - pdev->archdata.dma_mask = DMA_BIT_MASK(32); - pdev->dev.dma_mask = &pdev->archdata.dma_mask; -} - static __init void print_system_info(void) { pr_info("-----------------------------------------------------\n"); diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index 8301a4378f50..665cad452798 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -527,7 +527,6 @@ static int __init ap325rxa_devices_setup(void) /* Initialize CEU platform device separately to map memory first */ device_initialize(&ap325rxa_ceu_device.dev); - arch_setup_pdev_archdata(&ap325rxa_ceu_device); dma_declare_coherent_memory(&ap325rxa_ceu_device.dev, ceu_dma_membase, ceu_dma_membase, ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1); diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index f402aa741bf3..acaa97459531 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -1440,7 +1440,6 @@ static int __init arch_setup(void) /* Initialize CEU platform devices separately to map memory first */ device_initialize(&ecovec_ceu_devices[0]->dev); - arch_setup_pdev_archdata(ecovec_ceu_devices[0]); dma_declare_coherent_memory(&ecovec_ceu_devices[0]->dev, ceu0_dma_membase, ceu0_dma_membase, ceu0_dma_membase + @@ -1448,7 +1447,6 @@ static int __init arch_setup(void) platform_device_add(ecovec_ceu_devices[0]); device_initialize(&ecovec_ceu_devices[1]->dev); - arch_setup_pdev_archdata(ecovec_ceu_devices[1]); dma_declare_coherent_memory(&ecovec_ceu_devices[1]->dev, ceu1_dma_membase, ceu1_dma_membase, ceu1_dma_membase + diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index 1cf9a47ac90e..96538ba3aa32 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -601,7 +601,6 @@ static int __init kfr2r09_devices_setup(void) /* Initialize CEU platform device separately to map memory first */ device_initialize(&kfr2r09_ceu_device.dev); - arch_setup_pdev_archdata(&kfr2r09_ceu_device); dma_declare_coherent_memory(&kfr2r09_ceu_device.dev, ceu_dma_membase, ceu_dma_membase, ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1); diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 90702740f207..9ed369dad62d 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -602,7 +602,6 @@ static int __init migor_devices_setup(void) /* Initialize CEU platform device separately to map memory first */ device_initialize(&migor_ceu_device.dev); - arch_setup_pdev_archdata(&migor_ceu_device); dma_declare_coherent_memory(&migor_ceu_device.dev, ceu_dma_membase, ceu_dma_membase, ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1); diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 3674064816c7..32f5dd944889 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -937,7 +937,6 @@ static int __init devices_setup(void) /* Initialize CEU platform devices separately to map memory first */ device_initialize(&ms7724se_ceu_devices[0]->dev); - arch_setup_pdev_archdata(ms7724se_ceu_devices[0]); dma_declare_coherent_memory(&ms7724se_ceu_devices[0]->dev, ceu0_dma_membase, ceu0_dma_membase, ceu0_dma_membase + @@ -945,7 +944,6 @@ static int __init devices_setup(void) platform_device_add(ms7724se_ceu_devices[0]); device_initialize(&ms7724se_ceu_devices[1]->dev); - arch_setup_pdev_archdata(ms7724se_ceu_devices[1]); dma_declare_coherent_memory(&ms7724se_ceu_devices[1]->dev, ceu1_dma_membase, ceu1_dma_membase, ceu1_dma_membase + diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ec974ba9c0c4..600913aea73b 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -39,25 +39,6 @@ struct device platform_bus = { }; EXPORT_SYMBOL_GPL(platform_bus); -/** - * arch_setup_pdev_archdata - Allow manipulation of archdata before its used - * @pdev: platform device - * - * This is called before platform_device_add() such that any pdev_archdata may - * be setup before the platform_notifier is called. So if a user needs to - * manipulate any relevant information in the pdev_archdata they can do: - * - * platform_device_alloc() - * ... manipulate ... - * platform_device_add() - * - * And if they don't care they can just call platform_device_register() and - * everything will just work out. - */ -void __weak arch_setup_pdev_archdata(struct platform_device *pdev) -{ -} - /** * platform_get_resource - get a resource for a device * @dev: platform device @@ -264,6 +245,20 @@ struct platform_object { char name[]; }; +/* + * Set up default DMA mask for platform devices if the they weren't + * previously set by the architecture / DT. + */ +static void setup_pdev_dma_masks(struct platform_device *pdev) +{ + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + if (!pdev->dma_mask) + pdev->dma_mask = DMA_BIT_MASK(32); + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dma_mask; +}; + /** * platform_device_put - destroy a platform device * @pdev: platform device to free @@ -310,7 +305,7 @@ struct platform_device *platform_device_alloc(const char *name, int id) pa->pdev.id = id; device_initialize(&pa->pdev.dev); pa->pdev.dev.release = platform_device_release; - arch_setup_pdev_archdata(&pa->pdev); + setup_pdev_dma_masks(&pa->pdev); } return pa ? &pa->pdev : NULL; @@ -512,7 +507,7 @@ EXPORT_SYMBOL_GPL(platform_device_del); int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); - arch_setup_pdev_archdata(pdev); + setup_pdev_dma_masks(pdev); return platform_device_add(pdev); } EXPORT_SYMBOL_GPL(platform_device_register); diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 9bc36b589827..34a3d8ed8ba7 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -24,6 +24,7 @@ struct platform_device { int id; bool id_auto; struct device dev; + u64 dma_mask; u32 num_resources; struct resource *resource; @@ -48,7 +49,6 @@ extern void platform_device_unregister(struct platform_device *); extern struct bus_type platform_bus_type; extern struct device platform_bus; -extern void arch_setup_pdev_archdata(struct platform_device *); extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int); extern void __iomem * From 58fb8beda201f00b139e8993cbda937cd9a4e603 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 28 Jan 2019 15:36:09 +0800 Subject: [PATCH 092/159] dt-binding: usb: ci-hdrc-usb2: add imx7ulp compatible Add imx7ulp and imx8qm compatible Signed-off-by: Peter Chen --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index a254386a91ad..cfc9f40ab641 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -10,6 +10,7 @@ Required properties: "fsl,imx6sx-usb" "fsl,imx6ul-usb" "fsl,imx7d-usb" + "fsl,imx7ulp-usb" "lsi,zevio-usb" "qcom,ci-hdrc" "chipidea,usb2" From 71fcb8bdf5ab68e91c05b69e80c2d0ab4c51d78b Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 28 Jan 2019 15:37:30 +0800 Subject: [PATCH 093/159] dt-binding: usb: usbmisc-imx: add imx7ulp compatible Add imx7ulp compatible Signed-off-by: Peter Chen --- Documentation/devicetree/bindings/usb/usbmisc-imx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt index a85a631ec434..b353b9816487 100644 --- a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt +++ b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt @@ -7,6 +7,7 @@ Required properties: "fsl,vf610-usbmisc" for Vybrid vf610 "fsl,imx6sx-usbmisc" for imx6sx "fsl,imx7d-usbmisc" for imx7d + "fsl,imx7ulp-usbmisc" for imx7ulp - reg: Should contain registers location and length Examples: From 05559f10ed797b79f7fa47313682c48919a2b111 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 26 Aug 2019 18:25:12 +0800 Subject: [PATCH 094/159] usb: chipidea: add role switch class support USB role is fully controlled by usb role switch consumer(e.g. typec), usb port can be at host mode(USB_ROLE_HOST), device mode connected to host(USB_ROLE_DEVICE), or not connecting any partner(USB_ROLE_NONE). Signed-off-by: Li Jun Signed-off-by: Peter Chen --- drivers/usb/chipidea/Kconfig | 1 + drivers/usb/chipidea/ci.h | 12 ++++++ drivers/usb/chipidea/core.c | 83 ++++++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg.c | 8 ++-- 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index eb37ebfcb123..ae850b3fddf2 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -6,6 +6,7 @@ config USB_CHIPIDEA select EXTCON select RESET_CONTROLLER select USB_ULPI_BUS + select USB_ROLE_SWITCH help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. It supports: diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 6a2cc5cd0281..6911aef500e9 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /****************************************************************************** @@ -217,6 +218,7 @@ struct ci_hdrc { ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS]; unsigned enabled_otg_timer_bits; enum otg_fsm_timer next_otg_timer; + struct usb_role_switch *role_switch; struct work_struct work; struct workqueue_struct *wq; @@ -290,6 +292,16 @@ static inline void ci_role_stop(struct ci_hdrc *ci) ci->roles[role]->stop(ci); } +static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci) +{ + if (ci->role == CI_ROLE_HOST) + return USB_ROLE_HOST; + else if (ci->role == CI_ROLE_GADGET && ci->vbus_active) + return USB_ROLE_DEVICE; + else + return USB_ROLE_NONE; +} + /** * hw_read_id_reg: reads from a identification register * @ci: the controller diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 215c655295b8..98ee575ee500 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -600,6 +600,71 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; } +static enum usb_role ci_usb_role_switch_get(struct device *dev) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + enum usb_role role; + unsigned long flags; + + spin_lock_irqsave(&ci->lock, flags); + role = ci_role_to_usb_role(ci); + spin_unlock_irqrestore(&ci->lock, flags); + + return role; +} + +static int ci_usb_role_switch_set(struct device *dev, enum usb_role role) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + struct ci_hdrc_cable *cable = NULL; + enum usb_role current_role = ci_role_to_usb_role(ci); + unsigned long flags; + + if (current_role == role) + return 0; + + pm_runtime_get_sync(ci->dev); + /* Stop current role */ + spin_lock_irqsave(&ci->lock, flags); + if (current_role == USB_ROLE_DEVICE) + cable = &ci->platdata->vbus_extcon; + else if (current_role == USB_ROLE_HOST) + cable = &ci->platdata->id_extcon; + + if (cable) { + cable->changed = true; + cable->connected = false; + ci_irq(ci->irq, ci); + spin_unlock_irqrestore(&ci->lock, flags); + if (ci->wq && role != USB_ROLE_NONE) + flush_workqueue(ci->wq); + spin_lock_irqsave(&ci->lock, flags); + } + + cable = NULL; + + /* Start target role */ + if (role == USB_ROLE_DEVICE) + cable = &ci->platdata->vbus_extcon; + else if (role == USB_ROLE_HOST) + cable = &ci->platdata->id_extcon; + + if (cable) { + cable->changed = true; + cable->connected = true; + ci_irq(ci->irq, ci); + } + spin_unlock_irqrestore(&ci->lock, flags); + pm_runtime_put_sync(ci->dev); + + return 0; +} + +static struct usb_role_switch_desc ci_role_switch = { + .set = ci_usb_role_switch_set, + .get = ci_usb_role_switch_get, +}; + static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { @@ -726,6 +791,9 @@ static int ci_get_platdata(struct device *dev, cable->connected = false; } + if (device_property_read_bool(dev, "usb-role-switch")) + ci_role_switch.fwnode = dev->fwnode; + platdata->pctl = devm_pinctrl_get(dev); if (!IS_ERR(platdata->pctl)) { struct pinctrl_state *p; @@ -1047,6 +1115,15 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } + if (ci_role_switch.fwnode) { + ci->role_switch = usb_role_switch_register(dev, + &ci_role_switch); + if (IS_ERR(ci->role_switch)) { + ret = PTR_ERR(ci->role_switch); + goto deinit_otg; + } + } + if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { if (ci->is_otg) { ci->role = ci_otg_role(ci); @@ -1105,6 +1182,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) return 0; stop: + if (ci->role_switch) + usb_role_switch_unregister(ci->role_switch); +deinit_otg: if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) ci_hdrc_otg_destroy(ci); deinit_gadget: @@ -1123,6 +1203,9 @@ static int ci_hdrc_remove(struct platform_device *pdev) { struct ci_hdrc *ci = platform_get_drvdata(pdev); + if (ci->role_switch) + usb_role_switch_unregister(ci->role_switch); + if (ci->supports_runtime_pm) { pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index f25d4827fd49..fbfb02e05c97 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -35,7 +35,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) * detection overwrite OTGSC register value */ cable = &ci->platdata->vbus_extcon; - if (!IS_ERR(cable->edev)) { + if (!IS_ERR(cable->edev) || ci->role_switch) { if (cable->changed) val |= OTGSC_BSVIS; else @@ -53,7 +53,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) } cable = &ci->platdata->id_extcon; - if (!IS_ERR(cable->edev)) { + if (!IS_ERR(cable->edev) || ci->role_switch) { if (cable->changed) val |= OTGSC_IDIS; else @@ -83,7 +83,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) struct ci_hdrc_cable *cable; cable = &ci->platdata->vbus_extcon; - if (!IS_ERR(cable->edev)) { + if (!IS_ERR(cable->edev) || ci->role_switch) { if (data & mask & OTGSC_BSVIS) cable->changed = false; @@ -97,7 +97,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) } cable = &ci->platdata->id_extcon; - if (!IS_ERR(cable->edev)) { + if (!IS_ERR(cable->edev) || ci->role_switch) { if (data & mask & OTGSC_IDIS) cable->changed = false; From c09b73cfac2a9317f1104169045c519c6021aa1d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Aug 2019 16:10:58 +0300 Subject: [PATCH 095/159] usb: dwc3: don't set gadget->is_otg flag This reverts commit 6a4290cc28be1 ("usb: dwc3: gadget: set the OTG flag in dwc3 gadget driver.") We don't yet support any of the OTG mechanisms (HNP/SRP/ADP) and are not setting gadget->otg_caps, so don't set gadget->is_otg flag. If we do then we end up publishing a OTG1.0 descriptor in the gadget descriptor which causes device enumeration to fail if we are connected to a host with CONFIG_USB_OTG enabled. Host side log without this patch [ 96.720453] usb 1-1: new high-speed USB device number 2 using xhci-hcd [ 96.901391] usb 1-1: Dual-Role OTG device on non-HNP port [ 96.907552] usb 1-1: set a_alt_hnp_support failed: -32 [ 97.060447] usb 1-1: new high-speed USB device number 3 using xhci-hcd [ 97.241378] usb 1-1: Dual-Role OTG device on non-HNP port [ 97.247536] usb 1-1: set a_alt_hnp_support failed: -32 [ 97.253606] usb usb1-port1: attempt power cycle [ 97.960449] usb 1-1: new high-speed USB device number 4 using xhci-hcd [ 98.141383] usb 1-1: Dual-Role OTG device on non-HNP port [ 98.147540] usb 1-1: set a_alt_hnp_support failed: -32 [ 98.300453] usb 1-1: new high-speed USB device number 5 using xhci-hcd [ 98.481391] usb 1-1: Dual-Role OTG device on non-HNP port [ 98.487545] usb 1-1: set a_alt_hnp_support failed: -32 [ 98.493532] usb usb1-port1: unable to enumerate USB device Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 173f5329d3d9..48213421e9e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3318,7 +3318,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.sg_supported = true; dwc->gadget.name = "dwc3-gadget"; - dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG; dwc->gadget.lpm_capable = true; /* From e36721b90144bb46e1b6477be3ab63439c7fb79b Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Mon, 19 Aug 2019 12:54:35 +0530 Subject: [PATCH 096/159] usb: dwc3: st: Add of_node_put() before return in probe function The local variable child in the function st_dwc3_probe takes the return value of of_get_child_by_name, which gets a node and does not put it. If the function returns without releasing child, this could cause a memory error. Hence put child as soon as there is no more use for it. Also create a new label, err_node_put, just before label undo_softreset; so that err_node_put puts child. In between initialisation of child and its first put, modify all statements that go to undo_softreset to now go to err_node_put instead, from where they can fall through to undo_softreset. Issue found with Coccinelle. Reviewed-by: Patrice Chotard Signed-off-by: Nishka Dasgupta Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-st.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 16081383c401..6f52c749cd58 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -255,24 +255,25 @@ static int st_dwc3_probe(struct platform_device *pdev) if (!child) { dev_err(&pdev->dev, "failed to find dwc3 core node\n"); ret = -ENODEV; - goto undo_softreset; + goto err_node_put; } /* Allocate and initialize the core */ ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to add dwc3 core\n"); - goto undo_softreset; + goto err_node_put; } child_pdev = of_find_device_by_node(child); if (!child_pdev) { dev_err(dev, "failed to find dwc3 core device\n"); ret = -ENODEV; - goto undo_softreset; + goto err_node_put; } dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev); + of_node_put(child); /* * Configure the USB port as device or host according to the static @@ -292,6 +293,8 @@ static int st_dwc3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dwc3_data); return 0; +err_node_put: + of_node_put(child); undo_softreset: reset_control_assert(dwc3_data->rstc_rst); undo_powerdown: From bceb418b65b917af6d36a92ea1eaca54cafc1c2e Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Mon, 19 Aug 2019 12:54:36 +0530 Subject: [PATCH 097/159] usb: dwc3: st: Add of_dev_put() in probe function In function st_dwc3_probe, variable child_pdev takes the value returned by of_find_device_by_node, which gets a device pointer but does not put it. If child_pdev is not put before the probe function returns, it may cause a reference leak. Hence put child_pdev after its last usage. Issue found with Coccinelle. Reviewed-by: Patrice Chotard Signed-off-by: Nishka Dasgupta Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 6f52c749cd58..c682420f25ca 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -274,6 +274,7 @@ static int st_dwc3_probe(struct platform_device *pdev) dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev); of_node_put(child); + of_dev_put(child_pdev); /* * Configure the USB port as device or host according to the static From 163be6ff7739b12ff300d77897d340f661821da2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 22 Aug 2019 16:40:28 +0300 Subject: [PATCH 098/159] usb: gadget: udc: core: Fix segfault if udc_bind_to_driver() for pending driver fails If a gadget driver is in the pending drivers list, a UDC becomes available and udc_bind_to_driver() fails, then it gets deleted from the pending list. i.e. list_del(&driver->pending) in check_pending_gadget_drivers(). Then if that gadget driver is unregistered, usb_gadget_unregister_driver() does a list_del(&driver->pending) again thus causing a page fault as that list entry has been poisoned by the previous list_del(). Fix this by using list_del_init() instead of list_del() in check_pending_gadget_drivers(). Test case: - Make sure no UDC is available - modprobe g_mass_storage file=wrongfile - Load UDC driver so it becomes available lun0: unable to open backing file: wrongfile - modprobe -r g_mass_storage [ 60.900431] Unable to handle kernel paging request at virtual address dead000000000108 [ 60.908346] Mem abort info: [ 60.911145] ESR = 0x96000044 [ 60.914227] Exception class = DABT (current EL), IL = 32 bits [ 60.920162] SET = 0, FnV = 0 [ 60.923217] EA = 0, S1PTW = 0 [ 60.926354] Data abort info: [ 60.929228] ISV = 0, ISS = 0x00000044 [ 60.933058] CM = 0, WnR = 1 [ 60.936011] [dead000000000108] address between user and kernel address ranges [ 60.943136] Internal error: Oops: 96000044 [#1] PREEMPT SMP [ 60.948691] Modules linked in: g_mass_storage(-) usb_f_mass_storage libcomposite xhci_plat_hcd xhci_hcd usbcore ti_am335x_adc kfifo_buf omap_rng cdns3 rng_core udc_core crc32_ce xfrm_user crct10dif_ce snd_so6 [ 60.993995] Process modprobe (pid: 834, stack limit = 0x00000000c2aebc69) [ 61.000765] CPU: 0 PID: 834 Comm: modprobe Not tainted 4.19.59-01963-g065f42a60499 #92 [ 61.008658] Hardware name: Texas Instruments SoC (DT) [ 61.014472] pstate: 60000005 (nZCv daif -PAN -UAO) [ 61.019253] pc : usb_gadget_unregister_driver+0x7c/0x108 [udc_core] [ 61.025503] lr : usb_gadget_unregister_driver+0x30/0x108 [udc_core] [ 61.031750] sp : ffff00001338fda0 [ 61.035049] x29: ffff00001338fda0 x28: ffff800846d40000 [ 61.040346] x27: 0000000000000000 x26: 0000000000000000 [ 61.045642] x25: 0000000056000000 x24: 0000000000000800 [ 61.050938] x23: ffff000008d7b0d0 x22: ffff0000088b07c8 [ 61.056234] x21: ffff000001100000 x20: ffff000002020260 [ 61.061530] x19: ffff0000010ffd28 x18: 0000000000000000 [ 61.066825] x17: 0000000000000000 x16: 0000000000000000 [ 61.072121] x15: 0000000000000000 x14: 0000000000000000 [ 61.077417] x13: ffff000000000000 x12: ffffffffffffffff [ 61.082712] x11: 0000000000000030 x10: 7f7f7f7f7f7f7f7f [ 61.088008] x9 : fefefefefefefeff x8 : 0000000000000000 [ 61.093304] x7 : ffffffffffffffff x6 : 000000000000ffff [ 61.098599] x5 : 8080000000000000 x4 : 0000000000000000 [ 61.103895] x3 : ffff000001100020 x2 : ffff800846d40000 [ 61.109190] x1 : dead000000000100 x0 : dead000000000200 [ 61.114486] Call trace: [ 61.116922] usb_gadget_unregister_driver+0x7c/0x108 [udc_core] [ 61.122828] usb_composite_unregister+0x10/0x18 [libcomposite] [ 61.128643] msg_cleanup+0x18/0xfce0 [g_mass_storage] [ 61.133682] __arm64_sys_delete_module+0x17c/0x1f0 [ 61.138458] el0_svc_common+0x90/0x158 [ 61.142192] el0_svc_handler+0x2c/0x80 [ 61.145926] el0_svc+0x8/0xc [ 61.148794] Code: eb03003f d10be033 54ffff21 a94d0281 (f9000420) [ 61.154869] ---[ end trace afb22e9b637bd9a7 ]--- Segmentation fault Acked-by: Alan Stern Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 7cf34beb50df..92af8dc98c3d 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1143,7 +1143,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc) dev_name(&udc->dev)) == 0) { ret = udc_bind_to_driver(udc, driver); if (ret != -EPROBE_DEFER) - list_del(&driver->pending); + list_del_init(&driver->pending); break; } From 566aacaf80b8ae0cb0e6bc3d11d49a36ab7f32ca Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Thu, 22 Aug 2019 15:05:57 +0800 Subject: [PATCH 099/159] usb: udc: lpc32xx: remove set but not used 3 variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/gadget/udc/lpc32xx_udc.c: In function ‘udc_protocol_cmd_r’: drivers/usb/gadget/udc/lpc32xx_udc.c:744:6: warning: variable ‘tmp’ set but not used [-Wunused-but-set-variable] drivers/usb/gadget/udc/lpc32xx_udc.c: In function ‘udc_handle_dma_ep’: drivers/usb/gadget/udc/lpc32xx_udc.c:1994:14: warning: variable ‘epstatus’ set but not used [-Wunused-but-set-variable] drivers/usb/gadget/udc/lpc32xx_udc.c: In function ‘udc_handle_ep0_setup’: drivers/usb/gadget/udc/lpc32xx_udc.c:2200:22: warning: variable ‘wLength’ set but not used [-Wunused-but-set-variable] It is not used since commit 90fccb529d24 ("usb: gadget: Gadget directory cleanup - group UDC drivers") Signed-off-by: Mao Wenan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 5f1b14f3e5a0..2c8084deb671 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -742,7 +742,6 @@ static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd, * response data */ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) { - u32 tmp; int to = 1000; /* Write a command and read data from the protocol engine */ @@ -752,7 +751,6 @@ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) /* Write command code */ udc_protocol_cmd_w(udc, cmd); - tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL)) && (to > 0)) to--; @@ -1992,7 +1990,7 @@ void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) /* DMA end of transfer completion */ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) { - u32 status, epstatus; + u32 status; struct lpc32xx_request *req; struct lpc32xx_usbd_dd_gad *dd; @@ -2086,7 +2084,7 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) { udc_clearep_getsts(udc, ep->hwep_num); uda_enable_hwepint(udc, ep->hwep_num); - epstatus = udc_clearep_getsts(udc, ep->hwep_num); + udc_clearep_getsts(udc, ep->hwep_num); /* Let the EP interrupt handle the ZLP */ return; @@ -2198,7 +2196,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) struct lpc32xx_ep *ep, *ep0 = &udc->ep[0]; struct usb_ctrlrequest ctrlpkt; int i, bytes; - u16 wIndex, wValue, wLength, reqtype, req, tmp; + u16 wIndex, wValue, reqtype, req, tmp; /* Nuke previous transfers */ nuke(ep0, -EPROTO); @@ -2214,7 +2212,6 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) /* Native endianness */ wIndex = le16_to_cpu(ctrlpkt.wIndex); wValue = le16_to_cpu(ctrlpkt.wValue); - wLength = le16_to_cpu(ctrlpkt.wLength); reqtype = le16_to_cpu(ctrlpkt.bRequestType); /* Set direction of EP0 */ From 1cf084d161f40baf4aa6976bb2dcef114adbebd6 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 21 Aug 2019 15:35:18 +0200 Subject: [PATCH 100/159] usb: dwc3: meson-g12a: fix suspend resume regulator unbalanced disables When going in suspend, in Device mode, then resuming back leads to the following: unbalanced disables for USB_PWR_EN WARNING: CPU: 0 PID: 163 at ../drivers/regulator/core.c:2590 _regulator_disable+0x104/0x180 Hardware name: Amlogic Meson G12A U200 Development Board (DT) [...] pc : _regulator_disable+0x104/0x180 lr : _regulator_disable+0x104/0x180 [...] Call trace: _regulator_disable+0x104/0x180 regulator_disable+0x40/0x78 dwc3_meson_g12a_otg_mode_set+0x84/0xb0 dwc3_meson_g12a_irq_thread+0x58/0xb8 irq_thread_fn+0x28/0x80 irq_thread+0x118/0x1b8 kthread+0xf4/0x120 ret_from_fork+0x10/0x18 This disables the regulator if enabled on suspend, and the reverse on resume. Fixes: c99993376f72 ("usb: dwc3: Add Amlogic G12A DWC3 glue") Tested-by: Kevin Hilman Reported-by: Kevin Hilman Signed-off-by: Neil Armstrong Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-meson-g12a.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index d73ccd9e1366..8a3ec1a951fe 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -562,7 +562,13 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) { struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); - int i; + int i, ret; + + if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { + ret = regulator_disable(priv->vbus); + if (ret) + return ret; + } for (i = 0 ; i < PHY_COUNT ; ++i) { phy_power_off(priv->phys[i]); @@ -597,6 +603,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) return ret; } + if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + return 0; } From c33f141615c2850ab512f7dca654f8f0914ab9ab Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 20 Aug 2019 21:16:27 -0500 Subject: [PATCH 101/159] usb: udc: lpc32xx: silence fall-through warning Silence the following fall-through warning by adding a break statement: drivers/usb/gadget/udc/lpc32xx_udc.c:2230:3: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 2c8084deb671..bb287da20e16 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -2262,7 +2262,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) default: break; } - + break; case USB_REQ_SET_ADDRESS: if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { From f1b19006f4c9e83c9e19c58102157ff1fdebca22 Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Mon, 19 Aug 2019 13:12:34 +0530 Subject: [PATCH 102/159] usb: phy: phy-fsl-usb: Make structure fsl_otg_initdata constant Static structure fsl_otg_initdata, of type fsl_otg_config, is used only once, when the value of its field otg_port is assigned to another variable. As fsl_otg_initdata.otg_port is of type u8, any changes to its copy do not affect the original, and fsl_otg_initdata itself is never modified. Hence make fsl_otg_initdata constant to protect it from unintended modification. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsl-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index f7c96d209eda..dbb2d4d54894 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -65,7 +65,7 @@ struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, static struct list_head active_timers; -static struct fsl_otg_config fsl_otg_initdata = { +static const struct fsl_otg_config fsl_otg_initdata = { .otg_port = 1, }; From 05f0b20b67f4185cc0022e49de08e1b9b2dbeb36 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Mon, 19 Aug 2019 18:35:47 -0700 Subject: [PATCH 103/159] usb: gadget: Export recommended BESL values Currently there's no option for the controller driver to report the recommended Best Effort Service Latency (BESL) when operating with LPM support. Add new fields in usb_dcd_config_params to export the recommended baseline and deep BESL values for the function drivers to set the proper BESL value in the BOS descriptor. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- include/linux/usb/gadget.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index fb19141151d8..124462d65eac 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -291,6 +291,9 @@ struct usb_dcd_config_params { #define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ __le16 bU2DevExitLat; /* U2 Device exit Latency */ #define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */ + __u8 besl_baseline; /* Recommended baseline BESL (0-15) */ + __u8 besl_deep; /* Recommended deep BESL (0-15) */ +#define USB_DEFAULT_BESL_UNSPECIFIED 0xFF /* No recommended value */ }; From 16fe4f304015c39aa3a7aeef1beeac2ed585c7c6 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Mon, 19 Aug 2019 18:35:58 -0700 Subject: [PATCH 104/159] usb: dwc3: Separate field holding multiple properties dwc->hird_threshold field should store "snps,hird_threshold" property only and not a combination of multiple properties. Remove the value of "snps,is-utmi-l1-suspend" property from the field dwc->hird_threshold. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 3 +-- drivers/usb/dwc3/gadget.c | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 30ebf3c65a63..f56d5058a66c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1321,8 +1321,7 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; - dwc->hird_threshold = hird_threshold - | (dwc->is_utmi_l1_suspend << 4); + dwc->hird_threshold = hird_threshold; dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd; dwc->rx_max_burst_prd = rx_max_burst_prd; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 48213421e9e9..cfd07808ce62 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2868,7 +2868,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); - reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold); + reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold | + (dwc->is_utmi_l1_suspend << 4)); /* * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and From 54fb5ba647f8f942e372dee45025bce4adc08c38 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Mon, 19 Aug 2019 18:36:06 -0700 Subject: [PATCH 105/159] usb: dwc3: gadget: Set BESL config parameter When operating with LPM signals, the controller asserts the deep low-power signal (utmi_l1_suspend_n) to the phy when the BESL value of the LPM token is equal to or greater than DCTL.HIRD_Thres[3:0] (and with DCTL.HIRD_Thres[4] set). Otherwise, the shallow low-power signal (utmi_sleep_n) is asserted. Set the recommended deep BESL equal to the controller's DCTL.HIRD_Thres[3:0] setting, and set the baseline BESL to 0 for the shallow low-power signal. This maximizes the opportunity for L1 residency and optimizes power savings. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index cfd07808ce62..7b58e0e1e438 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2078,6 +2078,16 @@ static void dwc3_gadget_config_params(struct usb_gadget *g, { struct dwc3 *dwc = gadget_to_dwc(g); + params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED; + params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED; + + /* Recommended BESL */ + if (!dwc->dis_enblslpm_quirk) { + params->besl_baseline = 0; + if (dwc->is_utmi_l1_suspend) + params->besl_deep = min_t(u8, dwc->hird_threshold, 15); + } + /* U1 Device exit Latency */ if (dwc->dis_u1_entry_quirk) params->bU1devExitLat = 0; From cca3854010c5aebf3a06be0de04f26c4f1ae5810 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Mon, 19 Aug 2019 18:36:12 -0700 Subject: [PATCH 106/159] usb: gadget: composite: Set recommended BESL values Set the recommended BESL deep and baseline values based on the gadget's configuration parameters to the extended BOS descriptor. This feature helps to optimize power savings by maximizing the opportunity for longer L1 residency time. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 38 +++++++++++++++++++++++----------- include/uapi/linux/usb/ch9.h | 2 ++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9118b42c70b6..7ed009dc0f92 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -612,6 +612,7 @@ static int bos_desc(struct usb_composite_dev *cdev) struct usb_ext_cap_descriptor *usb_ext; struct usb_dcd_config_params dcd_config_params; struct usb_bos_descriptor *bos = cdev->req->buf; + unsigned int besl = 0; bos->bLength = USB_DT_BOS_SIZE; bos->bDescriptorType = USB_DT_BOS; @@ -619,6 +620,29 @@ static int bos_desc(struct usb_composite_dev *cdev) bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); bos->bNumDeviceCaps = 0; + /* Get Controller configuration */ + if (cdev->gadget->ops->get_config_params) { + cdev->gadget->ops->get_config_params(cdev->gadget, + &dcd_config_params); + } else { + dcd_config_params.besl_baseline = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.besl_deep = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.bU1devExitLat = + USB_DEFAULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU2DevExitLat = + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); + } + + if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED) + besl = USB_BESL_BASELINE_VALID | + USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline); + + if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED) + besl |= USB_BESL_DEEP_VALID | + USB_SET_BESL_DEEP(dcd_config_params.besl_deep); + /* * A SuperSpeed device shall include the USB2.0 extension descriptor * and shall support LPM when operating in USB2.0 HS mode. @@ -629,7 +653,8 @@ static int bos_desc(struct usb_composite_dev *cdev) usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; - usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT); + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | + USB_BESL_SUPPORT | besl); /* * The Superspeed USB Capability descriptor shall be implemented by all @@ -650,17 +675,6 @@ static int bos_desc(struct usb_composite_dev *cdev) USB_HIGH_SPEED_OPERATION | USB_5GBPS_OPERATION); ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; - - /* Get Controller configuration */ - if (cdev->gadget->ops->get_config_params) { - cdev->gadget->ops->get_config_params(cdev->gadget, - &dcd_config_params); - } else { - dcd_config_params.bU1devExitLat = - USB_DEFAULT_U1_DEV_EXIT_LAT; - dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); - } ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; } diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index d5a5caec8fbc..2b623f36af6b 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -894,6 +894,8 @@ struct usb_ext_cap_descriptor { /* Link Power Management */ #define USB_BESL_SUPPORT (1 << 2) /* supports BESL */ #define USB_BESL_BASELINE_VALID (1 << 3) /* Baseline BESL valid*/ #define USB_BESL_DEEP_VALID (1 << 4) /* Deep BESL valid */ +#define USB_SET_BESL_BASELINE(p) (((p) & 0xf) << 8) +#define USB_SET_BESL_DEEP(p) (((p) & 0xf) << 12) #define USB_GET_BESL_BASELINE(p) (((p) & (0xf << 8)) >> 8) #define USB_GET_BESL_DEEP(p) (((p) & (0xf << 12)) >> 12) } __attribute__((packed)); From 7a786b84790789eff5bad49e3f6c15f75b7bf691 Mon Sep 17 00:00:00 2001 From: Matthew Michilot Date: Thu, 15 Aug 2019 17:40:54 +0000 Subject: [PATCH 107/159] USB: serial: ftdi_sio: add support for FT232H CBUS gpios Enable support for cbus gpios on FT232H. The cbus configuration is stored in two words in the EEPROM at byte-offset 0x1a with the mux config for ACBUS5, ACBUS6, ACBUS8 and ACBUS9 (only pins that can be configured as I/O mode). Tested using FT232H by configuring one ACBUS pin at a time. Reviewed-by: Tim Harvey Signed-off-by: Matthew Michilot [ johan: fix copy-paste error in commit message ] Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4b3a049561f3..f0688c44b04c 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2023,6 +2023,46 @@ static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr, return 0; } +static int ftdi_gpio_init_ft232h(struct usb_serial_port *port) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + u16 cbus_config; + u8 *buf; + int ret; + int i; + + buf = kmalloc(4, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ftdi_read_eeprom(port->serial, buf, 0x1a, 4); + if (ret < 0) + goto out_free; + + /* + * FT232H CBUS Memory Map + * + * 0x1a: X- (upper nibble -> AC5) + * 0x1b: -X (lower nibble -> AC6) + * 0x1c: XX (upper nibble -> AC9 | lower nibble -> AC8) + */ + cbus_config = buf[2] << 8 | (buf[1] & 0xf) << 4 | (buf[0] & 0xf0) >> 4; + + priv->gc.ngpio = 4; + priv->gpio_altfunc = 0xff; + + for (i = 0; i < priv->gc.ngpio; ++i) { + if ((cbus_config & 0xf) == FTDI_FTX_CBUS_MUX_GPIO) + priv->gpio_altfunc &= ~BIT(i); + cbus_config >>= 4; + } + +out_free: + kfree(buf); + + return ret; +} + static int ftdi_gpio_init_ft232r(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -2098,6 +2138,9 @@ static int ftdi_gpio_init(struct usb_serial_port *port) int result; switch (priv->chip_type) { + case FT232H: + result = ftdi_gpio_init_ft232h(port); + break; case FT232RL: result = ftdi_gpio_init_ft232r(port); break; From e74e8bbff6e3654db55a56d3000ef581a6166944 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 23 Aug 2019 14:40:24 +0800 Subject: [PATCH 108/159] dt-bindings: usb: mtk-xhci: add an optional xhci_ck clock Add a new optional clock xhci_ck Signed-off-by: Chunfeng Yun Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1566542425-20082-1-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt index 266c2d917a28..f3e4acecabe8 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt @@ -30,7 +30,8 @@ Required properties: the following ones are optional: "ref_ck": reference clock used by low power mode etc, "mcu_ck": mcu_bus clock for register access, - "dma_ck": dma_bus clock for data transfer by DMA + "dma_ck": dma_bus clock for data transfer by DMA, + "xhci_ck": controller clock - phys : see usb-hcd.txt in the current directory @@ -100,7 +101,7 @@ Required properties: - clocks : a list of phandle + clock-specifier pairs, one for each entry in clock-names - clock-names : must contain "sys_ck", and the following ones are optional: - "ref_ck", "mcu_ck" and "dma_ck" + "ref_ck", "mcu_ck" and "dma_ck", "xhci_ck" Optional properties: - vbus-supply : reference to the VBUS regulator; From b2fcb285ade36c2339ee6638223a65829ee9907f Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 23 Aug 2019 14:40:25 +0800 Subject: [PATCH 109/159] usb: xhci-mtk: add an optional xhci_ck clock Some SoCs may have an optional clock xhci_ck (125M or 200M), it usually uses the same PLL as sys_ck, so support it. Signed-off-by: Chunfeng Yun Acked-by: Mathias Nyman Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/r/1566542425-20082-2-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mtk.c | 13 +++++++++++++ drivers/usb/host/xhci-mtk.h | 1 + 2 files changed, 14 insertions(+) diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 026fe18972d3..b18a6baef204 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -216,6 +216,10 @@ static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk) return PTR_ERR(mtk->sys_clk); } + mtk->xhci_clk = devm_clk_get_optional(dev, "xhci_ck"); + if (IS_ERR(mtk->xhci_clk)) + return PTR_ERR(mtk->xhci_clk); + mtk->ref_clk = devm_clk_get_optional(dev, "ref_ck"); if (IS_ERR(mtk->ref_clk)) return PTR_ERR(mtk->ref_clk); @@ -244,6 +248,12 @@ static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk) goto sys_clk_err; } + ret = clk_prepare_enable(mtk->xhci_clk); + if (ret) { + dev_err(mtk->dev, "failed to enable xhci_clk\n"); + goto xhci_clk_err; + } + ret = clk_prepare_enable(mtk->mcu_clk); if (ret) { dev_err(mtk->dev, "failed to enable mcu_clk\n"); @@ -261,6 +271,8 @@ static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk) dma_clk_err: clk_disable_unprepare(mtk->mcu_clk); mcu_clk_err: + clk_disable_unprepare(mtk->xhci_clk); +xhci_clk_err: clk_disable_unprepare(mtk->sys_clk); sys_clk_err: clk_disable_unprepare(mtk->ref_clk); @@ -272,6 +284,7 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk) { clk_disable_unprepare(mtk->dma_clk); clk_disable_unprepare(mtk->mcu_clk); + clk_disable_unprepare(mtk->xhci_clk); clk_disable_unprepare(mtk->sys_clk); clk_disable_unprepare(mtk->ref_clk); } diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 8be8c5f7ff62..5ac458b7d2e0 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -139,6 +139,7 @@ struct xhci_hcd_mtk { struct regulator *vusb33; struct regulator *vbus; struct clk *sys_clk; /* sys and mac clock */ + struct clk *xhci_clk; struct clk *ref_clk; struct clk *mcu_clk; struct clk *dma_clk; From a7a3a6a941cf53d49c5f40529b669304d328caf4 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 26 Aug 2019 12:19:26 +0100 Subject: [PATCH 110/159] dt-bindings: add binding for USBSS-DRD controller. This patch aim at documenting USB related dt-bindings for the Cadence USBSS-DRD controller. Signed-off-by: Pawel Laszczak Reviewed-by: Rob Herring Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/cdns-usb3.txt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/cdns-usb3.txt diff --git a/Documentation/devicetree/bindings/usb/cdns-usb3.txt b/Documentation/devicetree/bindings/usb/cdns-usb3.txt new file mode 100644 index 000000000000..b7dc606d37b5 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/cdns-usb3.txt @@ -0,0 +1,45 @@ +Binding for the Cadence USBSS-DRD controller + +Required properties: + - reg: Physical base address and size of the controller's register areas. + Controller has 3 different regions: + - HOST registers area + - DEVICE registers area + - OTG/DRD registers area + - reg-names - register memory area names: + "xhci" - for HOST registers space + "dev" - for DEVICE registers space + "otg" - for OTG/DRD registers space + - compatible: Should contain: "cdns,usb3" + - interrupts: Interrupts used by cdns3 controller: + "host" - interrupt used by XHCI driver. + "peripheral" - interrupt used by device driver + "otg" - interrupt used by DRD/OTG part of driver + +Optional properties: + - maximum-speed : valid arguments are "super-speed", "high-speed" and + "full-speed"; refer to usb/generic.txt + - dr_mode: Should be one of "host", "peripheral" or "otg". + - phys: reference to the USB PHY + - phy-names: from the *Generic PHY* bindings; + Supported names are: + - cdns3,usb2-phy + - cdns3,usb3-phy + + - cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints + buffers expressed in KB + +Example: + usb@f3000000 { + compatible = "cdns,usb3"; + interrupts = , + , + ; + interrupt-names = "host", "peripheral", "otg"; + reg = <0xf3000000 0x10000>, /* memory area for HOST registers */ + <0xf3010000 0x10000>, /* memory area for DEVICE registers */ + <0xf3020000 0x10000>; /* memory area for OTG/DRD registers */ + reg-names = "xhci", "dev", "otg"; + phys = <&usb2_phy>, <&usb3_phy>; + phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy"; + }; From 91f255a26bce80d27b5f3dcbb0333b852b5dbe2c Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 26 Aug 2019 12:19:27 +0100 Subject: [PATCH 111/159] usb: common: Separated decoding functions from dwc3 driver. Patch moves some decoding functions from driver/usb/dwc3/debug.h driver to driver/usb/common/debug.c file. These moved functions include: dwc3_decode_get_status dwc3_decode_set_clear_feature dwc3_decode_set_address dwc3_decode_get_set_descriptor dwc3_decode_get_configuration dwc3_decode_set_configuration dwc3_decode_get_intf dwc3_decode_set_intf dwc3_decode_synch_frame dwc3_decode_set_sel dwc3_decode_set_isoch_delay dwc3_decode_ctrl These functions are used also in inroduced cdns3 driver. All functions prefixes were changed from dwc3 to usb. Also, function's parameters has been extended according to the name of fields in standard SETUP packet. Additionally, patch adds usb_decode_ctrl function to include/linux/usb/ch9.h file. Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/common/Makefile | 1 + drivers/usb/common/debug.c | 268 ++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/debug.h | 252 --------------------------------- drivers/usb/dwc3/trace.h | 2 +- include/linux/usb/ch9.h | 27 ++++ 5 files changed, 297 insertions(+), 253 deletions(-) create mode 100644 drivers/usb/common/debug.c diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 0a7c45e85481..76f3e80623db 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_USB_COMMON) += usb-common.o usb-common-y += common.o +usb-common-$(CONFIG_TRACING) += debug.o usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c new file mode 100644 index 000000000000..d5a469bc67a3 --- /dev/null +++ b/drivers/usb/common/debug.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Common USB debugging functions + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#include + +static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex, + __u16 wLength, char *str, size_t size) +{ + switch (bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + snprintf(str, size, "Get Device Status(Length = %d)", wLength); + break; + case USB_RECIP_INTERFACE: + snprintf(str, size, + "Get Interface Status(Intf = %d, Length = %d)", + wIndex, wLength); + break; + case USB_RECIP_ENDPOINT: + snprintf(str, size, "Get Endpoint Status(ep%d%s)", + wIndex & ~USB_DIR_IN, + wIndex & USB_DIR_IN ? "in" : "out"); + break; + } +} + +static void usb_decode_set_clear_feature(__u8 bRequestType, __u8 bRequest, + __u16 wValue, __u16 wIndex, + char *str, size_t size) +{ + switch (bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + snprintf(str, size, "%s Device Feature(%s%s)", + bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + ({char *s; + switch (wValue) { + case USB_DEVICE_SELF_POWERED: + s = "Self Powered"; + break; + case USB_DEVICE_REMOTE_WAKEUP: + s = "Remote Wakeup"; + break; + case USB_DEVICE_TEST_MODE: + s = "Test Mode"; + break; + case USB_DEVICE_U1_ENABLE: + s = "U1 Enable"; + break; + case USB_DEVICE_U2_ENABLE: + s = "U2 Enable"; + break; + case USB_DEVICE_LTM_ENABLE: + s = "LTM Enable"; + break; + default: + s = "UNKNOWN"; + } s; }), + wValue == USB_DEVICE_TEST_MODE ? + ({ char *s; + switch (wIndex) { + case TEST_J: + s = ": TEST_J"; + break; + case TEST_K: + s = ": TEST_K"; + break; + case TEST_SE0_NAK: + s = ": TEST_SE0_NAK"; + break; + case TEST_PACKET: + s = ": TEST_PACKET"; + break; + case TEST_FORCE_EN: + s = ": TEST_FORCE_EN"; + break; + default: + s = ": UNKNOWN"; + } s; }) : ""); + break; + case USB_RECIP_INTERFACE: + snprintf(str, size, "%s Interface Feature(%s)", + bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + wValue == USB_INTRF_FUNC_SUSPEND ? + "Function Suspend" : "UNKNOWN"); + break; + case USB_RECIP_ENDPOINT: + snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)", + bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN", + wIndex & ~USB_DIR_IN, + wIndex & USB_DIR_IN ? "in" : "out"); + break; + } +} + +static void usb_decode_set_address(__u16 wValue, char *str, size_t size) +{ + snprintf(str, size, "Set Address(Addr = %02x)", wValue); +} + +static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest, + __u16 wValue, __u16 wIndex, + __u16 wLength, char *str, size_t size) +{ + snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)", + bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", + ({ char *s; + switch (wValue >> 8) { + case USB_DT_DEVICE: + s = "Device"; + break; + case USB_DT_CONFIG: + s = "Configuration"; + break; + case USB_DT_STRING: + s = "String"; + break; + case USB_DT_INTERFACE: + s = "Interface"; + break; + case USB_DT_ENDPOINT: + s = "Endpoint"; + break; + case USB_DT_DEVICE_QUALIFIER: + s = "Device Qualifier"; + break; + case USB_DT_OTHER_SPEED_CONFIG: + s = "Other Speed Config"; + break; + case USB_DT_INTERFACE_POWER: + s = "Interface Power"; + break; + case USB_DT_OTG: + s = "OTG"; + break; + case USB_DT_DEBUG: + s = "Debug"; + break; + case USB_DT_INTERFACE_ASSOCIATION: + s = "Interface Association"; + break; + case USB_DT_BOS: + s = "BOS"; + break; + case USB_DT_DEVICE_CAPABILITY: + s = "Device Capability"; + break; + case USB_DT_PIPE_USAGE: + s = "Pipe Usage"; + break; + case USB_DT_SS_ENDPOINT_COMP: + s = "SS Endpoint Companion"; + break; + case USB_DT_SSP_ISOC_ENDPOINT_COMP: + s = "SSP Isochronous Endpoint Companion"; + break; + default: + s = "UNKNOWN"; + break; + } s; }), wValue & 0xff, wLength); +} + +static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size) +{ + snprintf(str, size, "Get Configuration(Length = %d)", wLength); +} + +static void usb_decode_set_configuration(__u8 wValue, char *str, size_t size) +{ + snprintf(str, size, "Set Configuration(Config = %d)", wValue); +} + +static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str, + size_t size) +{ + snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", + wIndex, wLength); +} + +static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str, + size_t size) +{ + snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", + wIndex, wValue); +} + +static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength, + char *str, size_t size) +{ + snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", + wIndex, wLength); +} + +static void usb_decode_set_sel(__u16 wLength, char *str, size_t size) +{ + snprintf(str, size, "Set SEL(Length = %d)", wLength); +} + +static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size) +{ + snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue); +} + +/** + * usb_decode_ctrl - returns a string representation of ctrl request + */ +const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType, + __u8 bRequest, __u16 wValue, __u16 wIndex, + __u16 wLength) +{ + switch (bRequest) { + case USB_REQ_GET_STATUS: + usb_decode_get_status(bRequestType, wIndex, wLength, str, size); + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + usb_decode_set_clear_feature(bRequestType, bRequest, wValue, + wIndex, str, size); + break; + case USB_REQ_SET_ADDRESS: + usb_decode_set_address(wValue, str, size); + break; + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_SET_DESCRIPTOR: + usb_decode_get_set_descriptor(bRequestType, bRequest, wValue, + wIndex, wLength, str, size); + break; + case USB_REQ_GET_CONFIGURATION: + usb_decode_get_configuration(wLength, str, size); + break; + case USB_REQ_SET_CONFIGURATION: + usb_decode_set_configuration(wValue, str, size); + break; + case USB_REQ_GET_INTERFACE: + usb_decode_get_intf(wIndex, wLength, str, size); + break; + case USB_REQ_SET_INTERFACE: + usb_decode_set_intf(wValue, wIndex, str, size); + break; + case USB_REQ_SYNCH_FRAME: + usb_decode_synch_frame(wIndex, wLength, str, size); + break; + case USB_REQ_SET_SEL: + usb_decode_set_sel(wLength, str, size); + break; + case USB_REQ_SET_ISOCH_DELAY: + usb_decode_set_isoch_delay(wValue, str, size); + break; + default: + snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x", + bRequestType, bRequest, + (u8)(cpu_to_le16(wValue) & 0xff), + (u8)(cpu_to_le16(wValue) >> 8), + (u8)(cpu_to_le16(wIndex) & 0xff), + (u8)(cpu_to_le16(wIndex) >> 8), + (u8)(cpu_to_le16(wLength) & 0xff), + (u8)(cpu_to_le16(wLength) >> 8)); + } + + return str; +} +EXPORT_SYMBOL_GPL(usb_decode_ctrl); diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 068259fdfb0c..9baabed87d61 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -246,258 +246,6 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size, return str; } -static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str, - size_t size) -{ - switch (t & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - snprintf(str, size, "Get Device Status(Length = %d)", l); - break; - case USB_RECIP_INTERFACE: - snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)", - i, l); - break; - case USB_RECIP_ENDPOINT: - snprintf(str, size, "Get Endpoint Status(ep%d%s)", - i & ~USB_DIR_IN, - i & USB_DIR_IN ? "in" : "out"); - break; - } -} - -static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, - __u16 i, char *str, size_t size) -{ - switch (t & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - snprintf(str, size, "%s Device Feature(%s%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - ({char *s; - switch (v) { - case USB_DEVICE_SELF_POWERED: - s = "Self Powered"; - break; - case USB_DEVICE_REMOTE_WAKEUP: - s = "Remote Wakeup"; - break; - case USB_DEVICE_TEST_MODE: - s = "Test Mode"; - break; - case USB_DEVICE_U1_ENABLE: - s = "U1 Enable"; - break; - case USB_DEVICE_U2_ENABLE: - s = "U2 Enable"; - break; - case USB_DEVICE_LTM_ENABLE: - s = "LTM Enable"; - break; - default: - s = "UNKNOWN"; - } s; }), - v == USB_DEVICE_TEST_MODE ? - ({ char *s; - switch (i) { - case TEST_J: - s = ": TEST_J"; - break; - case TEST_K: - s = ": TEST_K"; - break; - case TEST_SE0_NAK: - s = ": TEST_SE0_NAK"; - break; - case TEST_PACKET: - s = ": TEST_PACKET"; - break; - case TEST_FORCE_EN: - s = ": TEST_FORCE_EN"; - break; - default: - s = ": UNKNOWN"; - } s; }) : ""); - break; - case USB_RECIP_INTERFACE: - snprintf(str, size, "%s Interface Feature(%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - v == USB_INTRF_FUNC_SUSPEND ? - "Function Suspend" : "UNKNOWN"); - break; - case USB_RECIP_ENDPOINT: - snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN", - i & ~USB_DIR_IN, - i & USB_DIR_IN ? "in" : "out"); - break; - } -} - -static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size) -{ - snprintf(str, size, "Set Address(Addr = %02x)", v); -} - -static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v, - __u16 i, __u16 l, char *str, size_t size) -{ - snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)", - b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", - ({ char *s; - switch (v >> 8) { - case USB_DT_DEVICE: - s = "Device"; - break; - case USB_DT_CONFIG: - s = "Configuration"; - break; - case USB_DT_STRING: - s = "String"; - break; - case USB_DT_INTERFACE: - s = "Interface"; - break; - case USB_DT_ENDPOINT: - s = "Endpoint"; - break; - case USB_DT_DEVICE_QUALIFIER: - s = "Device Qualifier"; - break; - case USB_DT_OTHER_SPEED_CONFIG: - s = "Other Speed Config"; - break; - case USB_DT_INTERFACE_POWER: - s = "Interface Power"; - break; - case USB_DT_OTG: - s = "OTG"; - break; - case USB_DT_DEBUG: - s = "Debug"; - break; - case USB_DT_INTERFACE_ASSOCIATION: - s = "Interface Association"; - break; - case USB_DT_BOS: - s = "BOS"; - break; - case USB_DT_DEVICE_CAPABILITY: - s = "Device Capability"; - break; - case USB_DT_PIPE_USAGE: - s = "Pipe Usage"; - break; - case USB_DT_SS_ENDPOINT_COMP: - s = "SS Endpoint Companion"; - break; - case USB_DT_SSP_ISOC_ENDPOINT_COMP: - s = "SSP Isochronous Endpoint Companion"; - break; - default: - s = "UNKNOWN"; - break; - } s; }), v & 0xff, l); -} - - -static inline void dwc3_decode_get_configuration(__u16 l, char *str, - size_t size) -{ - snprintf(str, size, "Get Configuration(Length = %d)", l); -} - -static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size) -{ - snprintf(str, size, "Set Configuration(Config = %d)", v); -} - -static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str, - size_t size) -{ - snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l); -} - -static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size) -{ - snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v); -} - -static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str, - size_t size) -{ - snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l); -} - -static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size) -{ - snprintf(str, size, "Set SEL(Length = %d)", l); -} - -static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size) -{ - snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v); -} - -/** - * dwc3_decode_ctrl - returns a string represetion of ctrl request - */ -static inline const char *dwc3_decode_ctrl(char *str, size_t size, - __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex, - __u16 wLength) -{ - switch (bRequest) { - case USB_REQ_GET_STATUS: - dwc3_decode_get_status(bRequestType, wIndex, wLength, str, - size); - break; - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue, - wIndex, str, size); - break; - case USB_REQ_SET_ADDRESS: - dwc3_decode_set_address(wValue, str, size); - break; - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_SET_DESCRIPTOR: - dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue, - wIndex, wLength, str, size); - break; - case USB_REQ_GET_CONFIGURATION: - dwc3_decode_get_configuration(wLength, str, size); - break; - case USB_REQ_SET_CONFIGURATION: - dwc3_decode_set_configuration(wValue, str, size); - break; - case USB_REQ_GET_INTERFACE: - dwc3_decode_get_intf(wIndex, wLength, str, size); - break; - case USB_REQ_SET_INTERFACE: - dwc3_decode_set_intf(wValue, wIndex, str, size); - break; - case USB_REQ_SYNCH_FRAME: - dwc3_decode_synch_frame(wIndex, wLength, str, size); - break; - case USB_REQ_SET_SEL: - dwc3_decode_set_sel(wLength, str, size); - break; - case USB_REQ_SET_ISOCH_DELAY: - dwc3_decode_set_isoch_delay(wValue, str, size); - break; - default: - snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x", - bRequestType, bRequest, - cpu_to_le16(wValue) & 0xff, - cpu_to_le16(wValue) >> 8, - cpu_to_le16(wIndex) & 0xff, - cpu_to_le16(wIndex) >> 8, - cpu_to_le16(wLength) & 0xff, - cpu_to_le16(wLength) >> 8); - } - - return str; -} - /** * dwc3_ep_event_string - returns event name * @event: then event code diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 818a63da1a44..9edff17111f7 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl, __entry->wIndex = le16_to_cpu(ctrl->wIndex); __entry->wLength = le16_to_cpu(ctrl->wLength); ), - TP_printk("%s", dwc3_decode_ctrl(__get_str(str), DWC3_MSG_MAX, + TP_printk("%s", usb_decode_ctrl(__get_str(str), DWC3_MSG_MAX, __entry->bRequestType, __entry->bRequest, __entry->wValue, __entry->wIndex, __entry->wLength) diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index da82606be605..58b83066bea4 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -70,4 +70,31 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev); */ extern const char *usb_state_string(enum usb_device_state state); +#ifdef CONFIG_TRACING +/** + * usb_decode_ctrl - Returns human readable representation of control request. + * @str: buffer to return a human-readable representation of control request. + * This buffer should have about 200 bytes. + * @size: size of str buffer. + * @bRequestType: matches the USB bmRequestType field + * @bRequest: matches the USB bRequest field + * @wValue: matches the USB wValue field (CPU byte order) + * @wIndex: matches the USB wIndex field (CPU byte order) + * @wLength: matches the USB wLength field (CPU byte order) + * + * Function returns decoded, formatted and human-readable description of + * control request packet. + * + * The usage scenario for this is for tracepoints, so function as a return + * use the same value as in parameters. This approach allows to use this + * function in TP_printk + * + * Important: wValue, wIndex, wLength parameters before invoking this function + * should be processed by le16_to_cpu macro. + */ +extern const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType, + __u8 bRequest, __u16 wValue, __u16 wIndex, + __u16 wLength); +#endif + #endif /* __LINUX_USB_CH9_H */ From 010665b44d596d1b2aa0249b0e093fd3df774f6e Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 26 Aug 2019 12:19:28 +0100 Subject: [PATCH 112/159] usb: common: Patch simplify usb_decode_set_clear_feature function. Patch adds usb_decode_test_mode and usb_decode_device_feature functions, which allow to make more readable and simplify the usb_decode_set_clear_feature function. Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/common/debug.c | 89 ++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c index d5a469bc67a3..60a9f70a0904 100644 --- a/drivers/usb/common/debug.c +++ b/drivers/usb/common/debug.c @@ -30,58 +30,55 @@ static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex, } } -static void usb_decode_set_clear_feature(__u8 bRequestType, __u8 bRequest, - __u16 wValue, __u16 wIndex, - char *str, size_t size) +static const char *usb_decode_device_feature(u16 wValue) +{ + switch (wValue) { + case USB_DEVICE_SELF_POWERED: + return "Self Powered"; + case USB_DEVICE_REMOTE_WAKEUP: + return "Remote Wakeup"; + case USB_DEVICE_TEST_MODE: + return "Test Mode"; + case USB_DEVICE_U1_ENABLE: + return "U1 Enable"; + case USB_DEVICE_U2_ENABLE: + return "U2 Enable"; + case USB_DEVICE_LTM_ENABLE: + return "LTM Enable"; + default: + return "UNKNOWN"; + } +} + +static const char *usb_decode_test_mode(u16 wIndex) +{ + switch (wIndex) { + case TEST_J: + return ": TEST_J"; + case TEST_K: + return ": TEST_K"; + case TEST_SE0_NAK: + return ": TEST_SE0_NAK"; + case TEST_PACKET: + return ": TEST_PACKET"; + case TEST_FORCE_EN: + return ": TEST_FORCE_EN"; + default: + return ": UNKNOWN"; + } +} + +static void usb_decode_set_clear_feature(__u8 bRequestType, + __u8 bRequest, __u16 wValue, + __u16 wIndex, char *str, size_t size) { switch (bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: snprintf(str, size, "%s Device Feature(%s%s)", bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - ({char *s; - switch (wValue) { - case USB_DEVICE_SELF_POWERED: - s = "Self Powered"; - break; - case USB_DEVICE_REMOTE_WAKEUP: - s = "Remote Wakeup"; - break; - case USB_DEVICE_TEST_MODE: - s = "Test Mode"; - break; - case USB_DEVICE_U1_ENABLE: - s = "U1 Enable"; - break; - case USB_DEVICE_U2_ENABLE: - s = "U2 Enable"; - break; - case USB_DEVICE_LTM_ENABLE: - s = "LTM Enable"; - break; - default: - s = "UNKNOWN"; - } s; }), + usb_decode_device_feature(wValue), wValue == USB_DEVICE_TEST_MODE ? - ({ char *s; - switch (wIndex) { - case TEST_J: - s = ": TEST_J"; - break; - case TEST_K: - s = ": TEST_K"; - break; - case TEST_SE0_NAK: - s = ": TEST_SE0_NAK"; - break; - case TEST_PACKET: - s = ": TEST_PACKET"; - break; - case TEST_FORCE_EN: - s = ": TEST_FORCE_EN"; - break; - default: - s = ": UNKNOWN"; - } s; }) : ""); + usb_decode_test_mode(wIndex) : ""); break; case USB_RECIP_INTERFACE: snprintf(str, size, "%s Interface Feature(%s)", From f72429fc88b5f48ee9ff2617224bbe098e40e86f Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 26 Aug 2019 12:19:29 +0100 Subject: [PATCH 113/159] usb: common: Simplify usb_decode_get_set_descriptor function. Patch moves switch responsible for decoding descriptor type outside snprintf. It improves code readability a little. Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/common/debug.c | 113 +++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c index 60a9f70a0904..92a986aeaa5d 100644 --- a/drivers/usb/common/debug.c +++ b/drivers/usb/common/debug.c @@ -105,62 +105,65 @@ static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength, char *str, size_t size) { + char *s; + + switch (wValue >> 8) { + case USB_DT_DEVICE: + s = "Device"; + break; + case USB_DT_CONFIG: + s = "Configuration"; + break; + case USB_DT_STRING: + s = "String"; + break; + case USB_DT_INTERFACE: + s = "Interface"; + break; + case USB_DT_ENDPOINT: + s = "Endpoint"; + break; + case USB_DT_DEVICE_QUALIFIER: + s = "Device Qualifier"; + break; + case USB_DT_OTHER_SPEED_CONFIG: + s = "Other Speed Config"; + break; + case USB_DT_INTERFACE_POWER: + s = "Interface Power"; + break; + case USB_DT_OTG: + s = "OTG"; + break; + case USB_DT_DEBUG: + s = "Debug"; + break; + case USB_DT_INTERFACE_ASSOCIATION: + s = "Interface Association"; + break; + case USB_DT_BOS: + s = "BOS"; + break; + case USB_DT_DEVICE_CAPABILITY: + s = "Device Capability"; + break; + case USB_DT_PIPE_USAGE: + s = "Pipe Usage"; + break; + case USB_DT_SS_ENDPOINT_COMP: + s = "SS Endpoint Companion"; + break; + case USB_DT_SSP_ISOC_ENDPOINT_COMP: + s = "SSP Isochronous Endpoint Companion"; + break; + default: + s = "UNKNOWN"; + break; + } + snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)", - bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", - ({ char *s; - switch (wValue >> 8) { - case USB_DT_DEVICE: - s = "Device"; - break; - case USB_DT_CONFIG: - s = "Configuration"; - break; - case USB_DT_STRING: - s = "String"; - break; - case USB_DT_INTERFACE: - s = "Interface"; - break; - case USB_DT_ENDPOINT: - s = "Endpoint"; - break; - case USB_DT_DEVICE_QUALIFIER: - s = "Device Qualifier"; - break; - case USB_DT_OTHER_SPEED_CONFIG: - s = "Other Speed Config"; - break; - case USB_DT_INTERFACE_POWER: - s = "Interface Power"; - break; - case USB_DT_OTG: - s = "OTG"; - break; - case USB_DT_DEBUG: - s = "Debug"; - break; - case USB_DT_INTERFACE_ASSOCIATION: - s = "Interface Association"; - break; - case USB_DT_BOS: - s = "BOS"; - break; - case USB_DT_DEVICE_CAPABILITY: - s = "Device Capability"; - break; - case USB_DT_PIPE_USAGE: - s = "Pipe Usage"; - break; - case USB_DT_SS_ENDPOINT_COMP: - s = "SS Endpoint Companion"; - break; - case USB_DT_SSP_ISOC_ENDPOINT_COMP: - s = "SSP Isochronous Endpoint Companion"; - break; - default: - s = "UNKNOWN"; - break; - } s; }), wValue & 0xff, wLength); + bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", + s, wValue & 0xff, wLength); } static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size) From 7733f6c32e36ff9d7adadf40001039bf219b1cbe Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 26 Aug 2019 12:19:30 +0100 Subject: [PATCH 114/159] usb: cdns3: Add Cadence USB3 DRD Driver This patch introduce new Cadence USBSS DRD driver to Linux kernel. The Cadence USBSS DRD Controller is a highly configurable IP Core which can be instantiated as Dual-Role Device (DRD), Peripheral Only and Host Only (XHCI)configurations. The current driver has been validated with FPGA platform. We have support for PCIe bus, which is used on FPGA prototyping. The host side of USBSS-DRD controller is compliant with XHCI specification, so it works with standard XHCI Linux driver. Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 2 + drivers/usb/cdns3/Kconfig | 46 + drivers/usb/cdns3/Makefile | 16 + drivers/usb/cdns3/cdns3-pci-wrap.c | 203 +++ drivers/usb/cdns3/core.c | 653 ++++++++ drivers/usb/cdns3/core.h | 98 ++ drivers/usb/cdns3/debug.h | 161 ++ drivers/usb/cdns3/drd.c | 381 +++++ drivers/usb/cdns3/drd.h | 167 ++ drivers/usb/cdns3/ep0.c | 888 ++++++++++ drivers/usb/cdns3/gadget-export.h | 28 + drivers/usb/cdns3/gadget.c | 2416 ++++++++++++++++++++++++++++ drivers/usb/cdns3/gadget.h | 1325 +++++++++++++++ drivers/usb/cdns3/host-export.h | 28 + drivers/usb/cdns3/host.c | 74 + drivers/usb/cdns3/trace.c | 11 + drivers/usb/cdns3/trace.h | 493 ++++++ 18 files changed, 6992 insertions(+) create mode 100644 drivers/usb/cdns3/Kconfig create mode 100644 drivers/usb/cdns3/Makefile create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c create mode 100644 drivers/usb/cdns3/core.c create mode 100644 drivers/usb/cdns3/core.h create mode 100644 drivers/usb/cdns3/debug.h create mode 100644 drivers/usb/cdns3/drd.c create mode 100644 drivers/usb/cdns3/drd.h create mode 100644 drivers/usb/cdns3/ep0.c create mode 100644 drivers/usb/cdns3/gadget-export.h create mode 100644 drivers/usb/cdns3/gadget.c create mode 100644 drivers/usb/cdns3/gadget.h create mode 100644 drivers/usb/cdns3/host-export.h create mode 100644 drivers/usb/cdns3/host.c create mode 100644 drivers/usb/cdns3/trace.c create mode 100644 drivers/usb/cdns3/trace.h diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 6e59d370ef81..2f4a70b5a225 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -114,6 +114,8 @@ source "drivers/usb/usbip/Kconfig" endif +source "drivers/usb/cdns3/Kconfig" + source "drivers/usb/mtu3/Kconfig" source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ecc2de1ffaae..09fc9f2448ce 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_ISP1760) += isp1760/ +obj-$(CONFIG_USB_CDNS3) += cdns3/ + obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_USB_MTU3) += mtu3/ diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig new file mode 100644 index 000000000000..d0331613a355 --- /dev/null +++ b/drivers/usb/cdns3/Kconfig @@ -0,0 +1,46 @@ +config USB_CDNS3 + tristate "Cadence USB3 Dual-Role Controller" + depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA + select USB_XHCI_PLATFORM if USB_XHCI_HCD + select USB_ROLE_SWITCH + help + Say Y here if your system has a Cadence USB3 dual-role controller. + It supports: dual-role switch, Host-only, and Peripheral-only. + + If you choose to build this driver is a dynamically linked + as module, the module will be called cdns3.ko. + +if USB_CDNS3 + +config USB_CDNS3_GADGET + bool "Cadence USB3 device controller" + depends on USB_GADGET=y || USB_GADGET=USB_CDNS3 + help + Say Y here to enable device controller functionality of the + Cadence USBSS-DEV driver. + + This controller supports FF, HS and SS mode. It doesn't support + LS and SSP mode. + +config USB_CDNS3_HOST + bool "Cadence USB3 host controller" + depends on USB=y || USB=USB_CDNS3 + help + Say Y here to enable host controller functionality of the + Cadence driver. + + Host controller is compliant with XHCI so it will use + standard XHCI driver. + +config USB_CDNS3_PCI_WRAP + tristate "Cadence USB3 support on PCIe-based platforms" + depends on USB_PCI && ACPI + default USB_CDNS3 + help + If you're using the USBSS Core IP with a PCIe, please say + 'Y' or 'M' here. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdns3-pci.ko + +endif diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile new file mode 100644 index 000000000000..a703547350bb --- /dev/null +++ b/drivers/usb/cdns3/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + +cdns3-y := core.o drd.o + +obj-$(CONFIG_USB_CDNS3) += cdns3.o +cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o + +ifneq ($(CONFIG_USB_CDNS3_GADGET),) +cdns3-$(CONFIG_TRACING) += trace.o +endif + +cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o + +obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c new file mode 100644 index 000000000000..c41ddb61b857 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBSS PCI Glue driver + * + * Copyright (C) 2018-2019 Cadence. + * + * Author: Pawel Laszczak + */ + +#include +#include +#include +#include +#include +#include + +struct cdns3_wrap { + struct platform_device *plat_dev; + struct resource dev_res[6]; + int devfn; +}; + +#define RES_IRQ_HOST_ID 0 +#define RES_IRQ_PERIPHERAL_ID 1 +#define RES_IRQ_OTG_ID 2 +#define RES_HOST_ID 3 +#define RES_DEV_ID 4 +#define RES_DRD_ID 5 + +#define PCI_BAR_HOST 0 +#define PCI_BAR_DEV 2 +#define PCI_BAR_OTG 0 + +#define PCI_DEV_FN_HOST_DEVICE 0 +#define PCI_DEV_FN_OTG 1 + +#define PCI_DRIVER_NAME "cdns3-pci-usbss" +#define PLAT_DRIVER_NAME "cdns-usb3" + +#define CDNS_VENDOR_ID 0x17cd +#define CDNS_DEVICE_ID 0x0100 + +static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) +{ + struct pci_dev *func; + + /* + * Gets the second function. + * It's little tricky, but this platform has two function. + * The fist keeps resources for Host/Device while the second + * keeps resources for DRD/OTG. + */ + func = pci_get_device(pdev->vendor, pdev->device, NULL); + if (unlikely(!func)) + return NULL; + + if (func->devfn == pdev->devfn) { + func = pci_get_device(pdev->vendor, pdev->device, func); + if (unlikely(!func)) + return NULL; + } + + return func; +} + +static int cdns3_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct platform_device_info plat_info; + struct cdns3_wrap *wrap; + struct resource *res; + struct pci_dev *func; + int err; + + /* + * for GADGET/HOST PCI (devfn) function number is 0, + * for OTG PCI (devfn) function number is 1 + */ + if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE && + pdev->devfn != PCI_DEV_FN_OTG)) + return -EINVAL; + + func = cdns3_get_second_fun(pdev); + if (unlikely(!func)) + return -EINVAL; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err); + return err; + } + + pci_set_master(pdev); + + if (pci_is_enabled(func)) { + wrap = pci_get_drvdata(func); + } else { + wrap = kzalloc(sizeof(*wrap), GFP_KERNEL); + if (!wrap) { + pci_disable_device(pdev); + return -ENOMEM; + } + } + + res = wrap->dev_res; + + if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) { + /* function 0: host(BAR_0) + device(BAR_1).*/ + dev_dbg(&pdev->dev, "Initialize Device resources\n"); + res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV); + res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV); + res[RES_DEV_ID].name = "dev"; + res[RES_DEV_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n", + &res[RES_DEV_ID].start); + + res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST); + res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST); + res[RES_HOST_ID].name = "xhci"; + res[RES_HOST_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n", + &res[RES_HOST_ID].start); + + /* Interrupt for XHCI */ + wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_HOST_ID].name = "host"; + wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ; + + /* Interrupt device. It's the same as for HOST. */ + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral"; + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ; + } else { + res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG); + res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG); + res[RES_DRD_ID].name = "otg"; + res[RES_DRD_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n", + &res[RES_DRD_ID].start); + + /* Interrupt for OTG/DRD. */ + wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_OTG_ID].name = "otg"; + wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ; + } + + if (pci_is_enabled(func)) { + /* set up platform device info */ + memset(&plat_info, 0, sizeof(plat_info)); + plat_info.parent = &pdev->dev; + plat_info.fwnode = pdev->dev.fwnode; + plat_info.name = PLAT_DRIVER_NAME; + plat_info.id = pdev->devfn; + wrap->devfn = pdev->devfn; + plat_info.res = wrap->dev_res; + plat_info.num_res = ARRAY_SIZE(wrap->dev_res); + plat_info.dma_mask = pdev->dma_mask; + /* register platform device */ + wrap->plat_dev = platform_device_register_full(&plat_info); + if (IS_ERR(wrap->plat_dev)) { + pci_disable_device(pdev); + kfree(wrap); + return PTR_ERR(wrap->plat_dev); + } + } + + pci_set_drvdata(pdev, wrap); + return err; +} + +static void cdns3_pci_remove(struct pci_dev *pdev) +{ + struct cdns3_wrap *wrap; + struct pci_dev *func; + + func = cdns3_get_second_fun(pdev); + + wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev); + if (wrap->devfn == pdev->devfn) + platform_device_unregister(wrap->plat_dev); + + if (!pci_is_enabled(func)) + kfree(wrap); +} + +static const struct pci_device_id cdns3_pci_ids[] = { + { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, + { 0, } +}; + +static struct pci_driver cdns3_pci_driver = { + .name = PCI_DRIVER_NAME, + .id_table = cdns3_pci_ids, + .probe = cdns3_pci_probe, + .remove = cdns3_pci_remove, +}; + +module_pci_driver(cdns3_pci_driver); +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids); + +MODULE_AUTHOR("Pawel Laszczak "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr"); diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c new file mode 100644 index 000000000000..300df58a190b --- /dev/null +++ b/drivers/usb/cdns3/core.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBSS DRD Driver. + * + * Copyright (C) 2018-2019 Cadence. + * Copyright (C) 2017-2018 NXP + * Copyright (C) 2019 Texas Instruments + * + * Author: Peter Chen + * Pawel Laszczak + * Roger Quadros + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gadget.h" +#include "core.h" +#include "host-export.h" +#include "gadget-export.h" +#include "drd.h" + +static int cdns3_idle_init(struct cdns3 *cdns); + +static inline +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns) +{ + WARN_ON(!cdns->roles[cdns->role]); + return cdns->roles[cdns->role]; +} + +static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role) +{ + int ret; + + if (WARN_ON(role > USB_ROLE_DEVICE)) + return 0; + + mutex_lock(&cdns->mutex); + cdns->role = role; + mutex_unlock(&cdns->mutex); + + if (!cdns->roles[role]) + return -ENXIO; + + if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE) + return 0; + + mutex_lock(&cdns->mutex); + ret = cdns->roles[role]->start(cdns); + if (!ret) + cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE; + mutex_unlock(&cdns->mutex); + + return ret; +} + +static void cdns3_role_stop(struct cdns3 *cdns) +{ + enum usb_role role = cdns->role; + + if (WARN_ON(role > USB_ROLE_DEVICE)) + return; + + if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE) + return; + + mutex_lock(&cdns->mutex); + cdns->roles[role]->stop(cdns); + cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE; + mutex_unlock(&cdns->mutex); +} + +static void cdns3_exit_roles(struct cdns3 *cdns) +{ + cdns3_role_stop(cdns); + cdns3_drd_exit(cdns); +} + +static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns); + +/** + * cdns3_core_init_role - initialize role of operation + * @cdns: Pointer to cdns3 structure + * + * Returns 0 on success otherwise negative errno + */ +static int cdns3_core_init_role(struct cdns3 *cdns) +{ + struct device *dev = cdns->dev; + enum usb_dr_mode best_dr_mode; + enum usb_dr_mode dr_mode; + int ret = 0; + + dr_mode = usb_get_dr_mode(dev); + cdns->role = USB_ROLE_NONE; + + /* + * If driver can't read mode by means of usb_get_dr_mode function then + * chooses mode according with Kernel configuration. This setting + * can be restricted later depending on strap pin configuration. + */ + if (dr_mode == USB_DR_MODE_UNKNOWN) { + if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) && + IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) + dr_mode = USB_DR_MODE_OTG; + else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST)) + dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) + dr_mode = USB_DR_MODE_PERIPHERAL; + } + + /* + * At this point cdns->dr_mode contains strap configuration. + * Driver try update this setting considering kernel configuration + */ + best_dr_mode = cdns->dr_mode; + + ret = cdns3_idle_init(cdns); + if (ret) + return ret; + + if (dr_mode == USB_DR_MODE_OTG) { + best_dr_mode = cdns->dr_mode; + } else if (cdns->dr_mode == USB_DR_MODE_OTG) { + best_dr_mode = dr_mode; + } else if (cdns->dr_mode != dr_mode) { + dev_err(dev, "Incorrect DRD configuration\n"); + return -EINVAL; + } + + dr_mode = best_dr_mode; + + if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { + ret = cdns3_host_init(cdns); + if (ret) { + dev_err(dev, "Host initialization failed with %d\n", + ret); + goto err; + } + } + + if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { + ret = cdns3_gadget_init(cdns); + if (ret) { + dev_err(dev, "Device initialization failed with %d\n", + ret); + goto err; + } + } + + cdns->dr_mode = dr_mode; + + ret = cdns3_drd_update_mode(cdns); + if (ret) + goto err; + + if (cdns->dr_mode != USB_DR_MODE_OTG) { + ret = cdns3_hw_role_switch(cdns); + if (ret) + goto err; + } + + return ret; +err: + cdns3_exit_roles(cdns); + return ret; +} + +/** + * cdsn3_hw_role_state_machine - role switch state machine based on hw events. + * @cdns: Pointer to controller structure. + * + * Returns next role to be entered based on hw events. + */ +static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns) +{ + enum usb_role role; + int id, vbus; + + if (cdns->dr_mode != USB_DR_MODE_OTG) + goto not_otg; + + id = cdns3_get_id(cdns); + vbus = cdns3_get_vbus(cdns); + + /* + * Role change state machine + * Inputs: ID, VBUS + * Previous state: cdns->role + * Next state: role + */ + role = cdns->role; + + switch (role) { + case USB_ROLE_NONE: + /* + * Driver treats USB_ROLE_NONE synonymous to IDLE state from + * controller specification. + */ + if (!id) + role = USB_ROLE_HOST; + else if (vbus) + role = USB_ROLE_DEVICE; + break; + case USB_ROLE_HOST: /* from HOST, we can only change to NONE */ + if (id) + role = USB_ROLE_NONE; + break; + case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/ + if (!vbus) + role = USB_ROLE_NONE; + break; + } + + dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); + + return role; + +not_otg: + if (cdns3_is_host(cdns)) + role = USB_ROLE_HOST; + if (cdns3_is_device(cdns)) + role = USB_ROLE_DEVICE; + + return role; +} + +static int cdns3_idle_role_start(struct cdns3 *cdns) +{ + return 0; +} + +static void cdns3_idle_role_stop(struct cdns3 *cdns) +{ + /* Program Lane swap and bring PHY out of RESET */ + phy_reset(cdns->usb3_phy); +} + +static int cdns3_idle_init(struct cdns3 *cdns) +{ + struct cdns3_role_driver *rdrv; + + rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); + if (!rdrv) + return -ENOMEM; + + rdrv->start = cdns3_idle_role_start; + rdrv->stop = cdns3_idle_role_stop; + rdrv->state = CDNS3_ROLE_STATE_INACTIVE; + rdrv->suspend = NULL; + rdrv->resume = NULL; + rdrv->name = "idle"; + + cdns->roles[USB_ROLE_NONE] = rdrv; + + return 0; +} + +/** + * cdns3_hw_role_switch - switch roles based on HW state + * @cdns3: controller + */ +int cdns3_hw_role_switch(struct cdns3 *cdns) +{ + enum usb_role real_role, current_role; + int ret = 0; + + /* Do nothing if role based on syfs. */ + if (cdns->role_override) + return 0; + + pm_runtime_get_sync(cdns->dev); + + current_role = cdns->role; + real_role = cdsn3_hw_role_state_machine(cdns); + + /* Do nothing if nothing changed */ + if (current_role == real_role) + goto exit; + + cdns3_role_stop(cdns); + + dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role); + + ret = cdns3_role_start(cdns, real_role); + if (ret) { + /* Back to current role */ + dev_err(cdns->dev, "set %d has failed, back to %d\n", + real_role, current_role); + ret = cdns3_role_start(cdns, current_role); + if (ret) + dev_err(cdns->dev, "back to %d failed too\n", + current_role); + } +exit: + pm_runtime_put_sync(cdns->dev); + return ret; +} + +/** + * cdsn3_role_get - get current role of controller. + * + * @dev: Pointer to device structure + * + * Returns role + */ +static enum usb_role cdns3_role_get(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + + return cdns->role; +} + +/** + * cdns3_role_set - set current role of controller. + * + * @dev: pointer to device object + * @role - the previous role + * Handles below events: + * - Role switch for dual-role devices + * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices + */ +static int cdns3_role_set(struct device *dev, enum usb_role role) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + int ret = 0; + + pm_runtime_get_sync(cdns->dev); + + /* + * FIXME: switch role framework should be extended to meet + * requirements. Driver assumes that role can be controlled + * by SW or HW. Temporary workaround is to use USB_ROLE_NONE to + * switch from SW to HW control. + * + * For dr_mode == USB_DR_MODE_OTG: + * if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver + * sets role_override flag and forces that role. + * if user sets USB_ROLE_NONE, driver clears role_override and lets + * HW state machine take over. + * + * For dr_mode != USB_DR_MODE_OTG: + * Assumptions: + * 1. Restricted user control between NONE and dr_mode. + * 2. Driver doesn't need to rely on role_override flag. + * 3. Driver needs to ensure that HW state machine is never called + * if dr_mode != USB_DR_MODE_OTG. + */ + if (role == USB_ROLE_NONE) + cdns->role_override = 0; + else + cdns->role_override = 1; + + /* + * HW state might have changed so driver need to trigger + * HW state machine if dr_mode == USB_DR_MODE_OTG. + */ + if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) { + cdns3_hw_role_switch(cdns); + goto pm_put; + } + + if (cdns->role == role) + goto pm_put; + + if (cdns->dr_mode == USB_DR_MODE_HOST) { + switch (role) { + case USB_ROLE_NONE: + case USB_ROLE_HOST: + break; + default: + ret = -EPERM; + goto pm_put; + } + } + + if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) { + switch (role) { + case USB_ROLE_NONE: + case USB_ROLE_DEVICE: + break; + default: + ret = -EPERM; + goto pm_put; + } + } + + cdns3_role_stop(cdns); + ret = cdns3_role_start(cdns, role); + if (ret) { + dev_err(cdns->dev, "set role %d has failed\n", role); + ret = -EPERM; + } + +pm_put: + pm_runtime_put_sync(cdns->dev); + return ret; +} + +static const struct usb_role_switch_desc cdns3_switch_desc = { + .set = cdns3_role_set, + .get = cdns3_role_get, + .allow_userspace_control = true, +}; + +/** + * cdns3_probe - probe for cdns3 core device + * @pdev: Pointer to cdns3 core platform device + * + * Returns 0 on success otherwise negative errno + */ +static int cdns3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct cdns3 *cdns; + void __iomem *regs; + int ret; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "error setting dma mask: %d\n", ret); + return -ENODEV; + } + + cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL); + if (!cdns) + return -ENOMEM; + + cdns->dev = dev; + + platform_set_drvdata(pdev, cdns); + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host"); + if (!res) { + dev_err(dev, "missing host IRQ\n"); + return -ENODEV; + } + + cdns->xhci_res[0] = *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci"); + if (!res) { + dev_err(dev, "couldn't get xhci resource\n"); + return -ENXIO; + } + + cdns->xhci_res[1] = *res; + + cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral"); + if (cdns->dev_irq == -EPROBE_DEFER) + return cdns->dev_irq; + + if (cdns->dev_irq < 0) + dev_err(dev, "couldn't get peripheral irq\n"); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev"); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) { + dev_err(dev, "couldn't iomap dev resource\n"); + return PTR_ERR(regs); + } + cdns->dev_regs = regs; + + cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); + if (cdns->otg_irq == -EPROBE_DEFER) + return cdns->otg_irq; + + if (cdns->otg_irq < 0) { + dev_err(dev, "couldn't get otg irq\n"); + return cdns->otg_irq; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); + if (!res) { + dev_err(dev, "couldn't get otg resource\n"); + return -ENXIO; + } + + cdns->otg_res = *res; + + mutex_init(&cdns->mutex); + + cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); + if (IS_ERR(cdns->usb2_phy)) + return PTR_ERR(cdns->usb2_phy); + + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + + cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); + if (IS_ERR(cdns->usb3_phy)) + return PTR_ERR(cdns->usb3_phy); + + ret = phy_init(cdns->usb3_phy); + if (ret) + goto err1; + + ret = phy_power_on(cdns->usb2_phy); + if (ret) + goto err2; + + ret = phy_power_on(cdns->usb3_phy); + if (ret) + goto err3; + + cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc); + if (IS_ERR(cdns->role_sw)) { + ret = PTR_ERR(cdns->role_sw); + dev_warn(dev, "Unable to register Role Switch\n"); + goto err4; + } + + ret = cdns3_drd_init(cdns); + if (ret) + goto err5; + + ret = cdns3_core_init_role(cdns); + if (ret) + goto err5; + + device_set_wakeup_capable(dev, true); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* + * The controller needs less time between bus and controller suspend, + * and we also needs a small delay to avoid frequently entering low + * power mode. + */ + pm_runtime_set_autosuspend_delay(dev, 20); + pm_runtime_mark_last_busy(dev); + pm_runtime_use_autosuspend(dev); + dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); + + return 0; +err5: + cdns3_drd_exit(cdns); + usb_role_switch_unregister(cdns->role_sw); +err4: + phy_power_off(cdns->usb3_phy); + +err3: + phy_power_off(cdns->usb2_phy); +err2: + phy_exit(cdns->usb3_phy); +err1: + phy_exit(cdns->usb2_phy); + + return ret; +} + +/** + * cdns3_remove - unbind drd driver and clean up + * @pdev: Pointer to Linux platform device + * + * Returns 0 on success otherwise negative errno + */ +static int cdns3_remove(struct platform_device *pdev) +{ + struct cdns3 *cdns = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + cdns3_exit_roles(cdns); + usb_role_switch_unregister(cdns->role_sw); + phy_power_off(cdns->usb2_phy); + phy_power_off(cdns->usb3_phy); + phy_exit(cdns->usb2_phy); + phy_exit(cdns->usb3_phy); + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int cdns3_suspend(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + unsigned long flags; + + if (cdns->role == USB_ROLE_HOST) + return 0; + + if (pm_runtime_status_suspended(dev)) + pm_runtime_resume(dev); + + if (cdns->roles[cdns->role]->suspend) { + spin_lock_irqsave(&cdns->gadget_dev->lock, flags); + cdns->roles[cdns->role]->suspend(cdns, false); + spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags); + } + + return 0; +} + +static int cdns3_resume(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + unsigned long flags; + + if (cdns->role == USB_ROLE_HOST) + return 0; + + if (cdns->roles[cdns->role]->resume) { + spin_lock_irqsave(&cdns->gadget_dev->lock, flags); + cdns->roles[cdns->role]->resume(cdns, false); + spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags); + } + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} +#endif + +static const struct dev_pm_ops cdns3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id of_cdns3_match[] = { + { .compatible = "cdns,usb3" }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_cdns3_match); +#endif + +static struct platform_driver cdns3_driver = { + .probe = cdns3_probe, + .remove = cdns3_remove, + .driver = { + .name = "cdns-usb3", + .of_match_table = of_match_ptr(of_cdns3_match), + .pm = &cdns3_pm_ops, + }, +}; + +module_platform_driver(cdns3_driver); + +MODULE_ALIAS("platform:cdns3"); +MODULE_AUTHOR("Pawel Laszczak "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver"); diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h new file mode 100644 index 000000000000..969eb94de204 --- /dev/null +++ b/drivers/usb/cdns3/core.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cadence USBSS DRD Header File. + * + * Copyright (C) 2017-2018 NXP + * Copyright (C) 2018-2019 Cadence. + * + * Authors: Peter Chen + * Pawel Laszczak + */ +#include +#include + +#ifndef __LINUX_CDNS3_CORE_H +#define __LINUX_CDNS3_CORE_H + +struct cdns3; + +/** + * struct cdns3_role_driver - host/gadget role driver + * @start: start this role + * @stop: stop this role + * @suspend: suspend callback for this role + * @resume: resume callback for this role + * @irq: irq handler for this role + * @name: role name string (host/gadget) + * @state: current state + */ +struct cdns3_role_driver { + int (*start)(struct cdns3 *cdns); + void (*stop)(struct cdns3 *cdns); + int (*suspend)(struct cdns3 *cdns, bool do_wakeup); + int (*resume)(struct cdns3 *cdns, bool hibernated); + const char *name; +#define CDNS3_ROLE_STATE_INACTIVE 0 +#define CDNS3_ROLE_STATE_ACTIVE 1 + int state; +}; + +#define CDNS3_XHCI_RESOURCES_NUM 2 +/** + * struct cdns3 - Representation of Cadence USB3 DRD controller. + * @dev: pointer to Cadence device struct + * @xhci_regs: pointer to base of xhci registers + * @xhci_res: the resource for xhci + * @dev_regs: pointer to base of dev registers + * @otg_res: the resource for otg + * @otg_v0_regs: pointer to base of v0 otg registers + * @otg_v1_regs: pointer to base of v1 otg registers + * @otg_regs: pointer to base of otg registers + * @otg_irq: irq number for otg controller + * @dev_irq: irq number for device controller + * @roles: array of supported roles for this controller + * @role: current role + * @host_dev: the child host device pointer for cdns3 core + * @gadget_dev: the child gadget device pointer for cdns3 core + * @usb2_phy: pointer to USB2 PHY + * @usb3_phy: pointer to USB3 PHY + * @mutex: the mutex for concurrent code at driver + * @dr_mode: supported mode of operation it can be only Host, only Device + * or OTG mode that allow to switch between Device and Host mode. + * This field based on firmware setting, kernel configuration + * and hardware configuration. + * @role_sw: pointer to role switch object. + * @role_override: set 1 if role rely on SW. + */ +struct cdns3 { + struct device *dev; + void __iomem *xhci_regs; + struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM]; + struct cdns3_usb_regs __iomem *dev_regs; + + struct resource otg_res; + struct cdns3_otg_legacy_regs *otg_v0_regs; + struct cdns3_otg_regs *otg_v1_regs; + struct cdns3_otg_common_regs *otg_regs; +#define CDNS3_CONTROLLER_V0 0 +#define CDNS3_CONTROLLER_V1 1 + u32 version; + + int otg_irq; + int dev_irq; + struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1]; + enum usb_role role; + struct platform_device *host_dev; + struct cdns3_device *gadget_dev; + struct phy *usb2_phy; + struct phy *usb3_phy; + /* mutext used in workqueue*/ + struct mutex mutex; + enum usb_dr_mode dr_mode; + struct usb_role_switch *role_sw; + int role_override; +}; + +int cdns3_hw_role_switch(struct cdns3 *cdns); + +#endif /* __LINUX_CDNS3_CORE_H */ diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h new file mode 100644 index 000000000000..2c9afbfe988b --- /dev/null +++ b/drivers/usb/cdns3/debug.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cadence USBSS DRD Driver. + * Debug header file. + * + * Copyright (C) 2018-2019 Cadence. + * + * Author: Pawel Laszczak + */ +#ifndef __LINUX_CDNS3_DEBUG +#define __LINUX_CDNS3_DEBUG + +#include "core.h" + +static inline char *cdns3_decode_usb_irq(char *str, + enum usb_device_speed speed, + u32 usb_ists) +{ + int ret; + + ret = sprintf(str, "IRQ %08x = ", usb_ists); + + if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) { + ret += sprintf(str + ret, "Connection %s\n", + usb_speed_string(speed)); + } + if (usb_ists & USB_ISTS_DIS2I || usb_ists & USB_ISTS_DISI) + ret += sprintf(str + ret, "Disconnection "); + if (usb_ists & USB_ISTS_L2ENTI) + ret += sprintf(str + ret, "suspended "); + if (usb_ists & USB_ISTS_L1ENTI) + ret += sprintf(str + ret, "L1 enter "); + if (usb_ists & USB_ISTS_L1EXTI) + ret += sprintf(str + ret, "L1 exit "); + if (usb_ists & USB_ISTS_L2ENTI) + ret += sprintf(str + ret, "L2 enter "); + if (usb_ists & USB_ISTS_L2EXTI) + ret += sprintf(str + ret, "L2 exit "); + if (usb_ists & USB_ISTS_U3EXTI) + ret += sprintf(str + ret, "U3 exit "); + if (usb_ists & USB_ISTS_UWRESI) + ret += sprintf(str + ret, "Warm Reset "); + if (usb_ists & USB_ISTS_UHRESI) + ret += sprintf(str + ret, "Hot Reset "); + if (usb_ists & USB_ISTS_U2RESI) + ret += sprintf(str + ret, "Reset"); + + return str; +} + +static inline char *cdns3_decode_ep_irq(char *str, + u32 ep_sts, + const char *ep_name) +{ + int ret; + + ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts); + + if (ep_sts & EP_STS_SETUP) + ret += sprintf(str + ret, "SETUP "); + if (ep_sts & EP_STS_IOC) + ret += sprintf(str + ret, "IOC "); + if (ep_sts & EP_STS_ISP) + ret += sprintf(str + ret, "ISP "); + if (ep_sts & EP_STS_DESCMIS) + ret += sprintf(str + ret, "DESCMIS "); + if (ep_sts & EP_STS_STREAMR) + ret += sprintf(str + ret, "STREAMR "); + if (ep_sts & EP_STS_MD_EXIT) + ret += sprintf(str + ret, "MD_EXIT "); + if (ep_sts & EP_STS_TRBERR) + ret += sprintf(str + ret, "TRBERR "); + if (ep_sts & EP_STS_NRDY) + ret += sprintf(str + ret, "NRDY "); + if (ep_sts & EP_STS_PRIME) + ret += sprintf(str + ret, "PRIME "); + if (ep_sts & EP_STS_SIDERR) + ret += sprintf(str + ret, "SIDERRT "); + if (ep_sts & EP_STS_OUTSMM) + ret += sprintf(str + ret, "OUTSMM "); + if (ep_sts & EP_STS_ISOERR) + ret += sprintf(str + ret, "ISOERR "); + if (ep_sts & EP_STS_IOT) + ret += sprintf(str + ret, "IOT "); + + return str; +} + +static inline char *cdns3_decode_epx_irq(char *str, + char *ep_name, + u32 ep_sts) +{ + return cdns3_decode_ep_irq(str, ep_sts, ep_name); +} + +static inline char *cdns3_decode_ep0_irq(char *str, + int dir, + u32 ep_sts) +{ + return cdns3_decode_ep_irq(str, ep_sts, + dir ? "ep0IN" : "ep0OUT"); +} + +/** + * Debug a transfer ring. + * + * Prints out all TRBs in the endpoint ring, even those after the Link TRB. + *. + */ +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep, + struct cdns3_trb *ring, char *str) +{ + dma_addr_t addr = priv_ep->trb_pool_dma; + struct cdns3_trb *trb; + int trb_per_sector; + int ret = 0; + int i; + + trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type); + + trb = &priv_ep->trb_pool[priv_ep->dequeue]; + ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name); + + ret += sprintf(str + ret, + "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n", + priv_ep->dequeue, trb, + (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb)); + + trb = &priv_ep->trb_pool[priv_ep->enqueue]; + ret += sprintf(str + ret, + "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n", + priv_ep->enqueue, trb, + (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb)); + + ret += sprintf(str + ret, + "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", + priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs); + + if (trb_per_sector > TRBS_PER_SEGMENT) + trb_per_sector = TRBS_PER_SEGMENT; + + if (trb_per_sector > TRBS_PER_SEGMENT) { + sprintf(str + ret, "\t\tTo big transfer ring %d\n", + trb_per_sector); + return str; + } + + for (i = 0; i < trb_per_sector; ++i) { + trb = &ring[i]; + ret += sprintf(str + ret, + "\t\t@%pad %08x %08x %08x\n", &addr, + le32_to_cpu(trb->buffer), + le32_to_cpu(trb->length), + le32_to_cpu(trb->control)); + addr += sizeof(*trb); + } + + return str; +} + +#endif /*__LINUX_CDNS3_DEBUG*/ diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c new file mode 100644 index 000000000000..16ad485f0b69 --- /dev/null +++ b/drivers/usb/cdns3/drd.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBSS DRD Driver. + * + * Copyright (C) 2018-2019 Cadence. + * Copyright (C) 2019 Texas Instruments + * + * Author: Pawel Laszczak + * Roger Quadros + * + * + */ +#include +#include +#include +#include +#include + +#include "gadget.h" +#include "drd.h" +#include "core.h" + +/** + * cdns3_set_mode - change mode of OTG Core + * @cdns: pointer to context structure + * @mode: selected mode from cdns_role + * + * Returns 0 on success otherwise negative errno + */ +int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode) +{ + int ret = 0; + u32 reg; + + switch (mode) { + case USB_DR_MODE_PERIPHERAL: + break; + case USB_DR_MODE_HOST: + break; + case USB_DR_MODE_OTG: + dev_dbg(cdns->dev, "Set controller to OTG mode\n"); + if (cdns->version == CDNS3_CONTROLLER_V1) { + reg = readl(&cdns->otg_v1_regs->override); + reg |= OVERRIDE_IDPULLUP; + writel(reg, &cdns->otg_v1_regs->override); + } else { + reg = readl(&cdns->otg_v0_regs->ctrl1); + reg |= OVERRIDE_IDPULLUP_V0; + writel(reg, &cdns->otg_v0_regs->ctrl1); + } + + /* + * Hardware specification says: "ID_VALUE must be valid within + * 50ms after idpullup is set to '1" so driver must wait + * 50ms before reading this pin. + */ + usleep_range(50000, 60000); + break; + default: + dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode); + return -EINVAL; + } + + return ret; +} + +int cdns3_get_id(struct cdns3 *cdns) +{ + int id; + + id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE; + dev_dbg(cdns->dev, "OTG ID: %d", id); + + return id; +} + +int cdns3_get_vbus(struct cdns3 *cdns) +{ + int vbus; + + vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID); + dev_dbg(cdns->dev, "OTG VBUS: %d", vbus); + + return vbus; +} + +int cdns3_is_host(struct cdns3 *cdns) +{ + if (cdns->dr_mode == USB_DR_MODE_HOST) + return 1; + else if (!cdns3_get_id(cdns)) + return 1; + + return 0; +} + +int cdns3_is_device(struct cdns3 *cdns) +{ + if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) + return 1; + else if (cdns->dr_mode == USB_DR_MODE_OTG) + if (cdns3_get_id(cdns)) + return 1; + + return 0; +} + +/** + * cdns3_otg_disable_irq - Disable all OTG interrupts + * @cdns: Pointer to controller context structure + */ +static void cdns3_otg_disable_irq(struct cdns3 *cdns) +{ + writel(0, &cdns->otg_regs->ien); +} + +/** + * cdns3_otg_enable_irq - enable id and sess_valid interrupts + * @cdns: Pointer to controller context structure + */ +static void cdns3_otg_enable_irq(struct cdns3 *cdns) +{ + writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT | + OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien); +} + +/** + * cdns3_drd_switch_host - start/stop host + * @cdns: Pointer to controller context structure + * @on: 1 for start, 0 for stop + * + * Returns 0 on success otherwise negative errno + */ +int cdns3_drd_switch_host(struct cdns3 *cdns, int on) +{ + int ret, val; + u32 reg = OTGCMD_OTG_DIS; + + /* switch OTG core */ + if (on) { + writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd); + + dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n"); + ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, + val & OTGSTS_XHCI_READY, + 1, 100000); + if (ret) { + dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); + return ret; + } + } else { + writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | + OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, + &cdns->otg_regs->cmd); + /* Waiting till H_IDLE state.*/ + readl_poll_timeout_atomic(&cdns->otg_regs->state, val, + !(val & OTGSTATE_HOST_STATE_MASK), + 1, 2000000); + } + + return 0; +} + +/** + * cdns3_drd_switch_gadget - start/stop gadget + * @cdns: Pointer to controller context structure + * @on: 1 for start, 0 for stop + * + * Returns 0 on success otherwise negative errno + */ +int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on) +{ + int ret, val; + u32 reg = OTGCMD_OTG_DIS; + + /* switch OTG core */ + if (on) { + writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); + + dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n"); + + ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, + val & OTGSTS_DEV_READY, + 1, 100000); + if (ret) { + dev_err(cdns->dev, "timeout waiting for dev_ready\n"); + return ret; + } + } else { + /* + * driver should wait at least 10us after disabling Device + * before turning-off Device (DEV_BUS_DROP) + */ + usleep_range(20, 30); + writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | + OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, + &cdns->otg_regs->cmd); + /* Waiting till DEV_IDLE state.*/ + readl_poll_timeout_atomic(&cdns->otg_regs->state, val, + !(val & OTGSTATE_DEV_STATE_MASK), + 1, 2000000); + } + + return 0; +} + +/** + * cdns3_init_otg_mode - initialize drd controller + * @cdns: Pointer to controller context structure + * + * Returns 0 on success otherwise negative errno + */ +static int cdns3_init_otg_mode(struct cdns3 *cdns) +{ + int ret = 0; + + cdns3_otg_disable_irq(cdns); + /* clear all interrupts */ + writel(~0, &cdns->otg_regs->ivect); + + ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG); + if (ret) + return ret; + + cdns3_otg_enable_irq(cdns); + return ret; +} + +/** + * cdns3_drd_update_mode - initialize mode of operation + * @cdns: Pointer to controller context structure + * + * Returns 0 on success otherwise negative errno + */ +int cdns3_drd_update_mode(struct cdns3 *cdns) +{ + int ret = 0; + + switch (cdns->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL); + break; + case USB_DR_MODE_HOST: + ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST); + break; + case USB_DR_MODE_OTG: + ret = cdns3_init_otg_mode(cdns); + break; + default: + dev_err(cdns->dev, "Unsupported mode of operation %d\n", + cdns->dr_mode); + return -EINVAL; + } + + return ret; +} + +static irqreturn_t cdns3_drd_thread_irq(int irq, void *data) +{ + struct cdns3 *cdns = data; + + cdns3_hw_role_switch(cdns); + + return IRQ_HANDLED; +} + +/** + * cdns3_drd_irq - interrupt handler for OTG events + * + * @irq: irq number for cdns3 core device + * @data: structure of cdns3 + * + * Returns IRQ_HANDLED or IRQ_NONE + */ +static irqreturn_t cdns3_drd_irq(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + struct cdns3 *cdns = data; + u32 reg; + + if (cdns->dr_mode != USB_DR_MODE_OTG) + return ret; + + reg = readl(&cdns->otg_regs->ivect); + + if (!reg) + return ret; + + if (reg & OTGIEN_ID_CHANGE_INT) { + dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n", + cdns3_get_id(cdns)); + + ret = IRQ_WAKE_THREAD; + } + + if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) { + dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n", + cdns3_get_vbus(cdns)); + + ret = IRQ_WAKE_THREAD; + } + + writel(~0, &cdns->otg_regs->ivect); + return ret; +} + +int cdns3_drd_init(struct cdns3 *cdns) +{ + void __iomem *regs; + int ret = 0; + u32 state; + + regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* Detection of DRD version. Controller has been released + * in two versions. Both are similar, but they have same changes + * in register maps. + * The first register in old version is command register and it's read + * only, so driver should read 0 from it. On the other hand, in v1 + * the first register contains device ID number which is not set to 0. + * Driver uses this fact to detect the proper version of + * controller. + */ + cdns->otg_v0_regs = regs; + if (!readl(&cdns->otg_v0_regs->cmd)) { + cdns->version = CDNS3_CONTROLLER_V0; + cdns->otg_v1_regs = NULL; + cdns->otg_regs = regs; + writel(1, &cdns->otg_v0_regs->simulate); + dev_info(cdns->dev, "DRD version v0 (%08x)\n", + readl(&cdns->otg_v0_regs->version)); + } else { + cdns->otg_v0_regs = NULL; + cdns->otg_v1_regs = regs; + cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd; + cdns->version = CDNS3_CONTROLLER_V1; + writel(1, &cdns->otg_v1_regs->simulate); + dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n", + readl(&cdns->otg_v1_regs->did), + readl(&cdns->otg_v1_regs->rid)); + } + + state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts)); + + /* Update dr_mode according to STRAP configuration. */ + cdns->dr_mode = USB_DR_MODE_OTG; + if (state == OTGSTS_STRAP_HOST) { + dev_dbg(cdns->dev, "Controller strapped to HOST\n"); + cdns->dr_mode = USB_DR_MODE_HOST; + } else if (state == OTGSTS_STRAP_GADGET) { + dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n"); + cdns->dr_mode = USB_DR_MODE_PERIPHERAL; + } + + ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq, + cdns3_drd_irq, + cdns3_drd_thread_irq, + IRQF_SHARED, + dev_name(cdns->dev), cdns); + + if (ret) { + dev_err(cdns->dev, "couldn't get otg_irq\n"); + return ret; + } + + state = readl(&cdns->otg_regs->sts); + if (OTGSTS_OTG_NRDY(state) != 0) { + dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n"); + return -ENODEV; + } + + return ret; +} + +int cdns3_drd_exit(struct cdns3 *cdns) +{ + cdns3_otg_disable_irq(cdns); + return 0; +} diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h new file mode 100644 index 000000000000..04e01c4d2377 --- /dev/null +++ b/drivers/usb/cdns3/drd.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cadence USB3 DRD header file. + * + * Copyright (C) 2018-2019 Cadence. + * + * Author: Pawel Laszczak + */ +#ifndef __LINUX_CDNS3_DRD +#define __LINUX_CDNS3_DRD + +#include +#include +#include "core.h" + +/* DRD register interface for version v1. */ +struct cdns3_otg_regs { + __le32 did; + __le32 rid; + __le32 capabilities; + __le32 reserved1; + __le32 cmd; + __le32 sts; + __le32 state; + __le32 reserved2; + __le32 ien; + __le32 ivect; + __le32 refclk; + __le32 tmr; + __le32 reserved3[4]; + __le32 simulate; + __le32 override; + __le32 susp_ctrl; + __le32 reserved4; + __le32 anasts; + __le32 adp_ramp_time; + __le32 ctrl1; + __le32 ctrl2; +}; + +/* DRD register interface for version v0. */ +struct cdns3_otg_legacy_regs { + __le32 cmd; + __le32 sts; + __le32 state; + __le32 refclk; + __le32 ien; + __le32 ivect; + __le32 reserved1[3]; + __le32 tmr; + __le32 reserved2[2]; + __le32 version; + __le32 capabilities; + __le32 reserved3[2]; + __le32 simulate; + __le32 reserved4[5]; + __le32 ctrl1; +}; + +/* + * Common registers interface for both version of DRD. + */ +struct cdns3_otg_common_regs { + __le32 cmd; + __le32 sts; + __le32 state; + __le32 different1; + __le32 ien; + __le32 ivect; +}; + +/* CDNS_RID - bitmasks */ +#define CDNS_RID(p) ((p) & GENMASK(15, 0)) + +/* CDNS_VID - bitmasks */ +#define CDNS_DID(p) ((p) & GENMASK(31, 0)) + +/* OTGCMD - bitmasks */ +/* "Request the bus for Device mode. */ +#define OTGCMD_DEV_BUS_REQ BIT(0) +/* Request the bus for Host mode */ +#define OTGCMD_HOST_BUS_REQ BIT(1) +/* Enable OTG mode. */ +#define OTGCMD_OTG_EN BIT(2) +/* Disable OTG mode */ +#define OTGCMD_OTG_DIS BIT(3) +/*"Configure OTG as A-Device. */ +#define OTGCMD_A_DEV_EN BIT(4) +/*"Configure OTG as A-Device. */ +#define OTGCMD_A_DEV_DIS BIT(5) +/* Drop the bus for Device mod e. */ +#define OTGCMD_DEV_BUS_DROP BIT(8) +/* Drop the bus for Host mode*/ +#define OTGCMD_HOST_BUS_DROP BIT(9) +/* Power Down USBSS-DEV. */ +#define OTGCMD_DEV_POWER_OFF BIT(11) +/* Power Down CDNSXHCI. */ +#define OTGCMD_HOST_POWER_OFF BIT(12) + +/* OTGIEN - bitmasks */ +/* ID change interrupt enable */ +#define OTGIEN_ID_CHANGE_INT BIT(0) +/* Vbusvalid fall detected interrupt enable.*/ +#define OTGIEN_VBUSVALID_RISE_INT BIT(4) +/* Vbusvalid fall detected interrupt enable */ +#define OTGIEN_VBUSVALID_FALL_INT BIT(5) + +/* OTGSTS - bitmasks */ +/* + * Current value of the ID pin. It is only valid when idpullup in + * OTGCTRL1_TYPE register is set to '1'. + */ +#define OTGSTS_ID_VALUE BIT(0) +/* Current value of the vbus_valid */ +#define OTGSTS_VBUS_VALID BIT(1) +/* Current value of the b_sess_vld */ +#define OTGSTS_SESSION_VALID BIT(2) +/*Device mode is active*/ +#define OTGSTS_DEV_ACTIVE BIT(3) +/* Host mode is active. */ +#define OTGSTS_HOST_ACTIVE BIT(4) +/* OTG Controller not ready. */ +#define OTGSTS_OTG_NRDY_MASK BIT(11) +#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK) +/* + * Value of the strap pins. + * 000 - no default configuration + * 010 - Controller initiall configured as Host + * 100 - Controller initially configured as Device + */ +#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12) +#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00 +#define OTGSTS_STRAP_HOST_OTG 0x01 +#define OTGSTS_STRAP_HOST 0x02 +#define OTGSTS_STRAP_GADGET 0x04 +/* Host mode is turned on. */ +#define OTGSTS_XHCI_READY BIT(26) +/* "Device mode is turned on .*/ +#define OTGSTS_DEV_READY BIT(27) + +/* OTGSTATE- bitmasks */ +#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0) +#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3) +#define OTGSTATE_HOST_STATE_IDLE 0x0 +#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7 +#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3) + +/* OTGREFCLK - bitmasks */ +#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31) + +/* OVERRIDE - bitmasks */ +#define OVERRIDE_IDPULLUP BIT(0) +/* Only for CDNS3_CONTROLLER_V0 version */ +#define OVERRIDE_IDPULLUP_V0 BIT(24) + +int cdns3_is_host(struct cdns3 *cdns); +int cdns3_is_device(struct cdns3 *cdns); +int cdns3_get_id(struct cdns3 *cdns); +int cdns3_get_vbus(struct cdns3 *cdns); +int cdns3_drd_init(struct cdns3 *cdns); +int cdns3_drd_exit(struct cdns3 *cdns); +int cdns3_drd_update_mode(struct cdns3 *cdns); +int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on); +int cdns3_drd_switch_host(struct cdns3 *cdns, int on); +int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode); + +#endif /* __LINUX_CDNS3_DRD */ diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c new file mode 100644 index 000000000000..e94f55d4729c --- /dev/null +++ b/drivers/usb/cdns3/ep0.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBSS DRD Driver - gadget side. + * + * Copyright (C) 2018 Cadence Design Systems. + * Copyright (C) 2017-2018 NXP + * + * Authors: Pawel Jez , + * Pawel Laszczak + * Peter Chen + */ + +#include +#include + +#include "gadget.h" +#include "trace.h" + +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +/** + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware + * @priv_dev: extended gadget object + * @dma_addr: physical address where data is/will be stored + * @length: data length + * @erdy: set it to 1 when ERDY packet should be sent - + * exit from flow control state + */ +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev, + dma_addr_t dma_addr, + unsigned int length, int erdy, int zlp) +{ + struct cdns3_usb_regs __iomem *regs = priv_dev->regs; + struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; + + priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr); + priv_ep->trb_pool[0].length = TRB_LEN(length); + + if (zlp) { + priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL); + priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr); + priv_ep->trb_pool[1].length = TRB_LEN(0); + priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC | + TRB_TYPE(TRB_NORMAL); + } else { + priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC | + TRB_TYPE(TRB_NORMAL); + priv_ep->trb_pool[1].control = 0; + } + + trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool); + + cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir); + + writel(EP_STS_TRBERR, ®s->ep_sts); + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), ®s->ep_traddr); + trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out", + readl(®s->ep_traddr)); + + /* TRB should be prepared before starting transfer. */ + writel(EP_CMD_DRDY, ®s->ep_cmd); + + /* Resume controller before arming transfer. */ + __cdns3_gadget_wakeup(priv_dev); + + if (erdy) + writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd); +} + +/** + * cdns3_ep0_delegate_req - Returns status of handling setup packet + * Setup is handled by gadget driver + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns zero on success or negative value on failure + */ +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + int ret; + + spin_unlock(&priv_dev->lock); + priv_dev->setup_pending = 1; + ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req); + priv_dev->setup_pending = 0; + spin_lock(&priv_dev->lock); + return ret; +} + +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev) +{ + priv_dev->ep0_data_dir = 0; + priv_dev->ep0_stage = CDNS3_SETUP_STAGE; + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, + sizeof(struct usb_ctrlrequest), 0, 0); +} + +static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev, + u8 send_stall, u8 send_erdy) +{ + struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; + struct usb_request *request; + + request = cdns3_next_request(&priv_ep->pending_req_list); + if (request) + list_del_init(&request->list); + + if (send_stall) { + trace_cdns3_halt(priv_ep, send_stall, 0); + /* set_stall on ep0 */ + cdns3_select_ep(priv_dev, 0x00); + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); + } else { + cdns3_prepare_setup_packet(priv_dev); + } + + priv_dev->ep0_stage = CDNS3_SETUP_STAGE; + writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL, + &priv_dev->regs->ep_cmd); + + cdns3_allow_enable_l1(priv_dev, 1); +} + +/** + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage, + * error code on error + */ +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state device_state = priv_dev->gadget.state; + struct cdns3_endpoint *priv_ep; + u32 config = le16_to_cpu(ctrl_req->wValue); + int result = 0; + int i; + + switch (device_state) { + case USB_STATE_ADDRESS: + /* Configure non-control EPs */ + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { + priv_ep = priv_dev->eps[i]; + if (!priv_ep) + continue; + + if (priv_ep->flags & EP_CLAIMED) + cdns3_ep_config(priv_ep); + } + + result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + + if (result) + return result; + + if (config) { + cdns3_set_hw_configuration(priv_dev); + } else { + cdns3_hw_reset_eps_config(priv_dev); + usb_gadget_set_state(&priv_dev->gadget, + USB_STATE_ADDRESS); + } + break; + case USB_STATE_CONFIGURED: + result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + + if (!config && !result) { + cdns3_hw_reset_eps_config(priv_dev); + usb_gadget_set_state(&priv_dev->gadget, + USB_STATE_ADDRESS); + } + break; + default: + result = -EINVAL; + } + + return result; +} + +/** + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state device_state = priv_dev->gadget.state; + u32 reg; + u32 addr; + + addr = le16_to_cpu(ctrl_req->wValue); + + if (addr > USB_DEVICE_MAX_ADDRESS) { + dev_err(priv_dev->dev, + "Device address (%d) cannot be greater than %d\n", + addr, USB_DEVICE_MAX_ADDRESS); + return -EINVAL; + } + + if (device_state == USB_STATE_CONFIGURED) { + dev_err(priv_dev->dev, + "can't set_address from configured state\n"); + return -EINVAL; + } + + reg = readl(&priv_dev->regs->usb_cmd); + + writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR, + &priv_dev->regs->usb_cmd); + + usb_gadget_set_state(&priv_dev->gadget, + (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); + + return 0; +} + +/** + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl) +{ + __le16 *response_pkt; + u16 usb_status = 0; + u32 recip; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + /* self powered */ + if (priv_dev->is_selfpowered) + usb_status = BIT(USB_DEVICE_SELF_POWERED); + + if (priv_dev->wake_up_flag) + usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP); + + if (priv_dev->gadget.speed != USB_SPEED_SUPER) + break; + + if (priv_dev->u1_allowed) + usb_status |= BIT(USB_DEV_STAT_U1_ENABLED); + + if (priv_dev->u2_allowed) + usb_status |= BIT(USB_DEV_STAT_U2_ENABLED); + + break; + case USB_RECIP_INTERFACE: + return cdns3_ep0_delegate_req(priv_dev, ctrl); + case USB_RECIP_ENDPOINT: + /* check if endpoint is stalled */ + cdns3_select_ep(priv_dev, ctrl->wIndex); + if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts))) + usb_status = BIT(USB_ENDPOINT_HALT); + break; + default: + return -EINVAL; + } + + response_pkt = (__le16 *)priv_dev->setup_buf; + *response_pkt = cpu_to_le16(usb_status); + + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, + sizeof(*response_pkt), 1, 0); + return 0; +} + +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + enum usb_device_state state; + enum usb_device_speed speed; + int ret = 0; + u32 wValue; + u32 wIndex; + u16 tmode; + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + state = priv_dev->gadget.state; + speed = priv_dev->gadget.speed; + + switch (ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + priv_dev->wake_up_flag = !!set; + break; + case USB_DEVICE_U1_ENABLE: + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) + return -EINVAL; + + priv_dev->u1_allowed = !!set; + break; + case USB_DEVICE_U2_ENABLE: + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) + return -EINVAL; + + priv_dev->u2_allowed = !!set; + break; + case USB_DEVICE_LTM_ENABLE: + ret = -EINVAL; + break; + case USB_DEVICE_TEST_MODE: + if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) + return -EINVAL; + + tmode = le16_to_cpu(ctrl->wIndex); + + if (!set || (tmode & 0xff) != 0) + return -EINVAL; + + switch (tmode >> 8) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + cdns3_ep0_complete_setup(priv_dev, 0, 1); + /** + * Little delay to give the controller some time + * for sending status stage. + * This time should be less then 3ms. + */ + usleep_range(1000, 2000); + cdns3_set_register_bit(&priv_dev->regs->usb_cmd, + USB_CMD_STMODE | + USB_STS_TMODE_SEL(tmode - 1)); + break; + default: + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + u32 wValue; + int ret = 0; + + wValue = le16_to_cpu(ctrl->wValue); + + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + struct cdns3_endpoint *priv_ep; + int ret = 0; + u8 index; + + if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) + return -EINVAL; + + if (!(ctrl->wIndex & ~USB_DIR_IN)) + return 0; + + index = cdns3_ep_addr_to_index(ctrl->wIndex); + priv_ep = priv_dev->eps[index]; + + cdns3_select_ep(priv_dev, ctrl->wIndex); + + if (set) + __cdns3_gadget_ep_set_halt(priv_ep); + else if (!(priv_ep->flags & EP_WEDGE)) + ret = __cdns3_gadget_ep_clear_halt(priv_ep); + + cdns3_select_ep(priv_dev, 0x00); + + return ret; +} + +/** + * cdns3_req_ep0_handle_feature - + * Handling of GET/SET_FEATURE standard USB request + * + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * @set: must be set to 1 for SET_FEATURE request + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + int ret = 0; + u32 recip; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set); + break; + case USB_RECIP_INTERFACE: + ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set); + break; + case USB_RECIP_ENDPOINT: + ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set); + break; + default: + return -EINVAL; + } + + return ret; +} + +/** + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + if (priv_dev->gadget.state < USB_STATE_ADDRESS) + return -EINVAL; + + if (ctrl_req->wLength != 6) { + dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n", + ctrl_req->wLength); + return -EINVAL; + } + + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0); + return 0; +} + +/** + * cdns3_req_ep0_set_isoch_delay - + * Handling of GET_ISOCH_DELAY standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + if (ctrl_req->wIndex || ctrl_req->wLength) + return -EINVAL; + + priv_dev->isoch_delay = ctrl_req->wValue; + + return 0; +} + +/** + * cdns3_ep0_standard_request - Handling standard USB requests + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + int ret; + + switch (ctrl_req->bRequest) { + case USB_REQ_SET_ADDRESS: + ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req); + break; + case USB_REQ_SET_CONFIGURATION: + ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req); + break; + case USB_REQ_GET_STATUS: + ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req); + break; + case USB_REQ_CLEAR_FEATURE: + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0); + break; + case USB_REQ_SET_FEATURE: + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1); + break; + case USB_REQ_SET_SEL: + ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req); + break; + case USB_REQ_SET_ISOCH_DELAY: + ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req); + break; + default: + ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + break; + } + + return ret; +} + +static void __pending_setup_status_handler(struct cdns3_device *priv_dev) +{ + struct usb_request *request = priv_dev->pending_status_request; + + if (priv_dev->status_completion_no_call && request && + request->complete) { + request->complete(&priv_dev->eps[0]->endpoint, request); + priv_dev->status_completion_no_call = 0; + } +} + +void cdns3_pending_setup_status_handler(struct work_struct *work) +{ + struct cdns3_device *priv_dev = container_of(work, struct cdns3_device, + pending_status_wq); + unsigned long flags; + + spin_lock_irqsave(&priv_dev->lock, flags); + __pending_setup_status_handler(priv_dev); + spin_unlock_irqrestore(&priv_dev->lock, flags); +} + +/** + * cdns3_ep0_setup_phase - Handling setup USB requests + * @priv_dev: extended gadget object + */ +static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev) +{ + struct usb_ctrlrequest *ctrl = priv_dev->setup_buf; + struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; + int result; + + priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN; + + trace_cdns3_ctrl_req(ctrl); + + if (!list_empty(&priv_ep->pending_req_list)) { + struct usb_request *request; + + request = cdns3_next_request(&priv_ep->pending_req_list); + priv_ep->dir = priv_dev->ep0_data_dir; + cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), + -ECONNRESET); + } + + if (le16_to_cpu(ctrl->wLength)) + priv_dev->ep0_stage = CDNS3_DATA_STAGE; + else + priv_dev->ep0_stage = CDNS3_STATUS_STAGE; + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + result = cdns3_ep0_standard_request(priv_dev, ctrl); + else + result = cdns3_ep0_delegate_req(priv_dev, ctrl); + + if (result == USB_GADGET_DELAYED_STATUS) + return; + + if (result < 0) + cdns3_ep0_complete_setup(priv_dev, 1, 1); + else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) + cdns3_ep0_complete_setup(priv_dev, 0, 1); +} + +static void cdns3_transfer_completed(struct cdns3_device *priv_dev) +{ + struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; + + if (!list_empty(&priv_ep->pending_req_list)) { + struct usb_request *request; + + trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool); + request = cdns3_next_request(&priv_ep->pending_req_list); + + request->actual = + TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length)); + + priv_ep->dir = priv_dev->ep0_data_dir; + cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0); + } + + cdns3_ep0_complete_setup(priv_dev, 0, 0); +} + +/** + * cdns3_check_new_setup - Check if controller receive new SETUP packet. + * @priv_dev: extended gadget object + * + * The SETUP packet can be kept in on-chip memory or in system memory. + */ +static bool cdns3_check_new_setup(struct cdns3_device *priv_dev) +{ + u32 ep_sts_reg; + + cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT); + ep_sts_reg = readl(&priv_dev->regs->ep_sts); + + return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT)); +} + +/** + * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0 + * @priv_dev: extended gadget object + * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction + */ +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir) +{ + u32 ep_sts_reg; + + cdns3_select_ep(priv_dev, dir); + + ep_sts_reg = readl(&priv_dev->regs->ep_sts); + writel(ep_sts_reg, &priv_dev->regs->ep_sts); + + trace_cdns3_ep0_irq(priv_dev, ep_sts_reg); + + __pending_setup_status_handler(priv_dev); + + if (ep_sts_reg & EP_STS_SETUP) + priv_dev->wait_for_setup = 1; + + if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) { + priv_dev->wait_for_setup = 0; + cdns3_allow_enable_l1(priv_dev, 0); + cdns3_ep0_setup_phase(priv_dev); + } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) { + priv_dev->ep0_data_dir = dir; + cdns3_transfer_completed(priv_dev); + } + + if (ep_sts_reg & EP_STS_DESCMIS) { + if (dir == 0 && !priv_dev->setup_pending) + cdns3_prepare_setup_packet(priv_dev); + } +} + +/** + * cdns3_gadget_ep0_enable + * Function shouldn't be called by gadget driver, + * endpoint 0 is allways active + */ +static int cdns3_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +/** + * cdns3_gadget_ep0_disable + * Function shouldn't be called by gadget driver, + * endpoint 0 is allways active + */ +static int cdns3_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/** + * cdns3_gadget_ep0_set_halt + * @ep: pointer to endpoint zero object + * @value: 1 for set stall, 0 for clear stall + * + * Returns 0 + */ +static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + /* TODO */ + return 0; +} + +/** + * cdns3_gadget_ep0_queue Transfer data on endpoint zero + * @ep: pointer to endpoint zero object + * @request: pointer to request object + * @gfp_flags: gfp flags + * + * Returns 0 on success, error code elsewhere + */ +static int cdns3_gadget_ep0_queue(struct usb_ep *ep, + struct usb_request *request, + gfp_t gfp_flags) +{ + struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + unsigned long flags; + int erdy_sent = 0; + int ret = 0; + u8 zlp = 0; + + trace_cdns3_ep0_queue(priv_dev, request); + + /* cancel the request if controller receive new SETUP packet. */ + if (cdns3_check_new_setup(priv_dev)) + return -ECONNRESET; + + /* send STATUS stage. Should be called only for SET_CONFIGURATION */ + if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) { + spin_lock_irqsave(&priv_dev->lock, flags); + cdns3_select_ep(priv_dev, 0x00); + + erdy_sent = !priv_dev->hw_configured_flag; + cdns3_set_hw_configuration(priv_dev); + + if (!erdy_sent) + cdns3_ep0_complete_setup(priv_dev, 0, 1); + + cdns3_allow_enable_l1(priv_dev, 1); + + request->actual = 0; + priv_dev->status_completion_no_call = true; + priv_dev->pending_status_request = request; + spin_unlock_irqrestore(&priv_dev->lock, flags); + + /* + * Since there is no completion interrupt for status stage, + * it needs to call ->completion in software after + * ep0_queue is back. + */ + queue_work(system_freezable_wq, &priv_dev->pending_status_wq); + return 0; + } + + spin_lock_irqsave(&priv_dev->lock, flags); + if (!list_empty(&priv_ep->pending_req_list)) { + dev_err(priv_dev->dev, + "can't handle multiple requests for ep0\n"); + spin_unlock_irqrestore(&priv_dev->lock, flags); + return -EBUSY; + } + + ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, + priv_dev->ep0_data_dir); + if (ret) { + spin_unlock_irqrestore(&priv_dev->lock, flags); + dev_err(priv_dev->dev, "failed to map request\n"); + return -EINVAL; + } + + request->status = -EINPROGRESS; + list_add_tail(&request->list, &priv_ep->pending_req_list); + + if (request->zero && request->length && + (request->length % ep->maxpacket == 0)) + zlp = 1; + + cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp); + + spin_unlock_irqrestore(&priv_dev->lock, flags); + + return ret; +} + +/** + * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint + * @ep: endpoint object + * + * Returns 0 + */ +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name); + cdns3_gadget_ep_set_halt(ep, 1); + priv_ep->flags |= EP_WEDGE; + + return 0; +} + +const struct usb_ep_ops cdns3_gadget_ep0_ops = { + .enable = cdns3_gadget_ep0_enable, + .disable = cdns3_gadget_ep0_disable, + .alloc_request = cdns3_gadget_ep_alloc_request, + .free_request = cdns3_gadget_ep_free_request, + .queue = cdns3_gadget_ep0_queue, + .dequeue = cdns3_gadget_ep_dequeue, + .set_halt = cdns3_gadget_ep0_set_halt, + .set_wedge = cdns3_gadget_ep_set_wedge, +}; + +/** + * cdns3_ep0_config - Configures default endpoint + * @priv_dev: extended gadget object + * + * Functions sets parameters: maximal packet size and enables interrupts + */ +void cdns3_ep0_config(struct cdns3_device *priv_dev) +{ + struct cdns3_usb_regs __iomem *regs; + struct cdns3_endpoint *priv_ep; + u32 max_packet_size = 64; + + regs = priv_dev->regs; + + if (priv_dev->gadget.speed == USB_SPEED_SUPER) + max_packet_size = 512; + + priv_ep = priv_dev->eps[0]; + + if (!list_empty(&priv_ep->pending_req_list)) { + struct usb_request *request; + + request = cdns3_next_request(&priv_ep->pending_req_list); + list_del_init(&request->list); + } + + priv_dev->u1_allowed = 0; + priv_dev->u2_allowed = 0; + + priv_dev->gadget.ep0->maxpacket = max_packet_size; + cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size); + + /* init ep out */ + cdns3_select_ep(priv_dev, USB_DIR_OUT); + + if (priv_dev->dev_ver >= DEV_VER_V3) { + cdns3_set_register_bit(&priv_dev->regs->dtrans, + BIT(0) | BIT(16)); + cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb, + BIT(0) | BIT(16)); + } + + writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), + ®s->ep_cfg); + + writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN, + ®s->ep_sts_en); + + /* init ep in */ + cdns3_select_ep(priv_dev, USB_DIR_IN); + + writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), + ®s->ep_cfg); + + writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); + + cdns3_set_register_bit(®s->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS); +} + +/** + * cdns3_init_ep0 Initializes software endpoint 0 of gadget + * @priv_dev: extended gadget object + * @ep_priv: extended endpoint object + * + * Returns 0 on success else error code. + */ +int cdns3_init_ep0(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + sprintf(priv_ep->name, "ep0"); + + /* fill linux fields */ + priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops; + priv_ep->endpoint.maxburst = 1; + usb_ep_set_maxpacket_limit(&priv_ep->endpoint, + CDNS3_EP0_MAX_PACKET_LIMIT); + priv_ep->endpoint.address = 0; + priv_ep->endpoint.caps.type_control = 1; + priv_ep->endpoint.caps.dir_in = 1; + priv_ep->endpoint.caps.dir_out = 1; + priv_ep->endpoint.name = priv_ep->name; + priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc; + priv_dev->gadget.ep0 = &priv_ep->endpoint; + priv_ep->type = USB_ENDPOINT_XFER_CONTROL; + + return cdns3_allocate_trb_pool(priv_ep); +} diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h new file mode 100644 index 000000000000..577469eee961 --- /dev/null +++ b/drivers/usb/cdns3/gadget-export.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cadence USBSS DRD Driver - Gadget Export APIs. + * + * Copyright (C) 2017 NXP + * Copyright (C) 2017-2018 NXP + * + * Authors: Peter Chen + */ +#ifndef __LINUX_CDNS3_GADGET_EXPORT +#define __LINUX_CDNS3_GADGET_EXPORT + +#ifdef CONFIG_USB_CDNS3_GADGET + +int cdns3_gadget_init(struct cdns3 *cdns); +void cdns3_gadget_exit(struct cdns3 *cdns); +#else + +static inline int cdns3_gadget_init(struct cdns3 *cdns) +{ + return -ENXIO; +} + +static inline void cdns3_gadget_exit(struct cdns3 *cdns) { } + +#endif + +#endif /* __LINUX_CDNS3_GADGET_EXPORT */ diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c new file mode 100644 index 000000000000..49ba253d81be --- /dev/null +++ b/drivers/usb/cdns3/gadget.c @@ -0,0 +1,2416 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBSS DRD Driver - gadget side. + * + * Copyright (C) 2018-2019 Cadence Design Systems. + * Copyright (C) 2017-2018 NXP + * + * Authors: Pawel Jez , + * Pawel Laszczak + * Peter Chen + */ + +/* + * Work around 1: + * At some situations, the controller may get stale data address in TRB + * at below sequences: + * 1. Controller read TRB includes data address + * 2. Software updates TRBs includes data address and Cycle bit + * 3. Controller read TRB which includes Cycle bit + * 4. DMA run with stale data address + * + * To fix this problem, driver needs to make the first TRB in TD as invalid. + * After preparing all TRBs driver needs to check the position of DMA and + * if the DMA point to the first just added TRB and doorbell is 1, + * then driver must defer making this TRB as valid. This TRB will be make + * as valid during adding next TRB only if DMA is stopped or at TRBERR + * interrupt. + * + * Issue has been fixed in DEV_VER_V3 version of controller. + * + */ + +#include +#include +#include +#include + +#include "core.h" +#include "gadget-export.h" +#include "gadget.h" +#include "trace.h" +#include "drd.h" + +static int __cdns3_gadget_ep_queue(struct usb_ep *ep, + struct usb_request *request, + gfp_t gfp_flags); + +/** + * cdns3_set_register_bit - set bit in given register. + * @ptr: address of device controller register to be read and changed + * @mask: bits requested to set + */ +void cdns3_set_register_bit(void __iomem *ptr, u32 mask) +{ + mask = readl(ptr) | mask; + writel(mask, ptr); +} + +/** + * cdns3_ep_addr_to_index - Macro converts endpoint address to + * index of endpoint object in cdns3_device.eps[] container + * @ep_addr: endpoint address for which endpoint object is required + * + */ +u8 cdns3_ep_addr_to_index(u8 ep_addr) +{ + return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0)); +} + +static int cdns3_get_dma_pos(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + int dma_index; + + dma_index = readl(&priv_dev->regs->ep_traddr) - priv_ep->trb_pool_dma; + + return dma_index / TRB_SIZE; +} + +/** + * cdns3_next_request - returns next request from list + * @list: list containing requests + * + * Returns request or NULL if no requests in list + */ +struct usb_request *cdns3_next_request(struct list_head *list) +{ + return list_first_entry_or_null(list, struct usb_request, list); +} + +/** + * cdns3_next_align_buf - returns next buffer from list + * @list: list containing buffers + * + * Returns buffer or NULL if no buffers in list + */ +struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list) +{ + return list_first_entry_or_null(list, struct cdns3_aligned_buf, list); +} + +/** + * select_ep - selects endpoint + * @priv_dev: extended gadget object + * @ep: endpoint address + */ +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep) +{ + if (priv_dev->selected_ep == ep) + return; + + priv_dev->selected_ep = ep; + writel(ep, &priv_dev->regs->ep_sel); +} + +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep, + struct cdns3_trb *trb) +{ + u32 offset = (char *)trb - (char *)priv_ep->trb_pool; + + return priv_ep->trb_pool_dma + offset; +} + +int cdns3_ring_size(struct cdns3_endpoint *priv_ep) +{ + switch (priv_ep->type) { + case USB_ENDPOINT_XFER_ISOC: + return TRB_ISO_RING_SIZE; + case USB_ENDPOINT_XFER_CONTROL: + return TRB_CTRL_RING_SIZE; + default: + return TRB_RING_SIZE; + } +} + +/** + * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint + * @priv_ep: endpoint object + * + * Function will return 0 on success or -ENOMEM on allocation error + */ +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + int ring_size = cdns3_ring_size(priv_ep); + struct cdns3_trb *link_trb; + + if (!priv_ep->trb_pool) { + priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev, + ring_size, + &priv_ep->trb_pool_dma, + GFP_DMA32 | GFP_ATOMIC); + if (!priv_ep->trb_pool) + return -ENOMEM; + } else { + memset(priv_ep->trb_pool, 0, ring_size); + } + + if (!priv_ep->num) + return 0; + + priv_ep->num_trbs = ring_size / TRB_SIZE; + /* Initialize the last TRB as Link TRB. */ + link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1)); + link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma); + link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE; + + return 0; +} + +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + if (priv_ep->trb_pool) { + dma_free_coherent(priv_dev->sysdev, + cdns3_ring_size(priv_ep), + priv_ep->trb_pool, priv_ep->trb_pool_dma); + priv_ep->trb_pool = NULL; + } +} + +/** + * cdns3_ep_stall_flush - Stalls and flushes selected endpoint + * @priv_ep: endpoint object + * + * Endpoint must be selected before call to this function + */ +static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + int val; + + trace_cdns3_halt(priv_ep, 1, 1); + + writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL, + &priv_dev->regs->ep_cmd); + + /* wait for DFLUSH cleared */ + readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & EP_CMD_DFLUSH), 1, 1000); + priv_ep->flags |= EP_STALLED; + priv_ep->flags &= ~EP_STALL_PENDING; +} + +/** + * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller. + * @priv_dev: extended gadget object + */ +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) +{ + writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf); + + cdns3_allow_enable_l1(priv_dev, 0); + priv_dev->hw_configured_flag = 0; + priv_dev->onchip_used_size = 0; + priv_dev->out_mem_is_allocated = 0; + priv_dev->wait_for_setup = 0; +} + +/** + * cdns3_ep_inc_trb - increment a trb index. + * @index: Pointer to the TRB index to increment. + * @cs: Cycle state + * @trb_in_seg: number of TRBs in segment + * + * The index should never point to the link TRB. After incrementing, + * if it is point to the link TRB, wrap around to the beginning and revert + * cycle state bit The + * link TRB is always at the last TRB entry. + */ +static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg) +{ + (*index)++; + if (*index == (trb_in_seg - 1)) { + *index = 0; + *cs ^= 1; + } +} + +/** + * cdns3_ep_inc_enq - increment endpoint's enqueue pointer + * @priv_ep: The endpoint whose enqueue pointer we're incrementing + */ +static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep) +{ + priv_ep->free_trbs--; + cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs); +} + +/** + * cdns3_ep_inc_deq - increment endpoint's dequeue pointer + * @priv_ep: The endpoint whose dequeue pointer we're incrementing + */ +static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep) +{ + priv_ep->free_trbs++; + cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs); +} + +void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req) +{ + struct cdns3_endpoint *priv_ep = priv_req->priv_ep; + int current_trb = priv_req->start_trb; + + while (current_trb != priv_req->end_trb) { + cdns3_ep_inc_deq(priv_ep); + current_trb = priv_ep->dequeue; + } + + cdns3_ep_inc_deq(priv_ep); +} + +/** + * cdns3_allow_enable_l1 - enable/disable permits to transition to L1. + * @priv_dev: Extended gadget object + * @enable: Enable/disable permit to transition to L1. + * + * If bit USB_CONF_L1EN is set and device receive Extended Token packet, + * then controller answer with ACK handshake. + * If bit USB_CONF_L1DS is set and device receive Extended Token packet, + * then controller answer with NYET handshake. + */ +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable) +{ + if (enable) + writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf); + else + writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf); +} + +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev) +{ + u32 reg; + + reg = readl(&priv_dev->regs->usb_sts); + + if (DEV_SUPERSPEED(reg)) + return USB_SPEED_SUPER; + else if (DEV_HIGHSPEED(reg)) + return USB_SPEED_HIGH; + else if (DEV_FULLSPEED(reg)) + return USB_SPEED_FULL; + else if (DEV_LOWSPEED(reg)) + return USB_SPEED_LOW; + return USB_SPEED_UNKNOWN; +} + +/** + * cdns3_start_all_request - add to ring all request not started + * @priv_dev: Extended gadget object + * @priv_ep: The endpoint for whom request will be started. + * + * Returns return ENOMEM if transfer ring i not enough TRBs to start + * all requests. + */ +static int cdns3_start_all_request(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + struct cdns3_request *priv_req; + struct usb_request *request; + int ret = 0; + + while (!list_empty(&priv_ep->deferred_req_list)) { + request = cdns3_next_request(&priv_ep->deferred_req_list); + priv_req = to_cdns3_request(request); + + ret = cdns3_ep_run_transfer(priv_ep, request); + if (ret) + return ret; + + list_del(&request->list); + list_add_tail(&request->list, + &priv_ep->pending_req_list); + } + + priv_ep->flags &= ~EP_RING_FULL; + return ret; +} + +/** + * cdns3_gadget_giveback - call struct usb_request's ->complete callback + * @priv_ep: The endpoint to whom the request belongs to + * @priv_req: The request we're giving back + * @status: completion code for the request + * + * Must be called with controller's lock held and interrupts disabled. This + * function will unmap @req and call its ->complete() callback to notify upper + * layers that it has completed. + */ +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, + struct cdns3_request *priv_req, + int status) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct usb_request *request = &priv_req->request; + + list_del_init(&request->list); + + if (request->status == -EINPROGRESS) + request->status = status; + + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request, + priv_ep->dir); + + if ((priv_req->flags & REQUEST_UNALIGNED) && + priv_ep->dir == USB_DIR_OUT && !request->status) + memcpy(request->buf, priv_req->aligned_buf->buf, + request->length); + + priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); + trace_cdns3_gadget_giveback(priv_req); + + if (request->complete) { + spin_unlock(&priv_dev->lock); + usb_gadget_giveback_request(&priv_ep->endpoint, + request); + spin_lock(&priv_dev->lock); + } + + if (request->buf == priv_dev->zlp_buf) + cdns3_gadget_ep_free_request(&priv_ep->endpoint, request); +} + +void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep) +{ + /* Work around for stale data address in TRB*/ + if (priv_ep->wa1_set) { + trace_cdns3_wa1(priv_ep, "restore cycle bit"); + + priv_ep->wa1_set = 0; + priv_ep->wa1_trb_index = 0xFFFF; + if (priv_ep->wa1_cycle_bit) { + priv_ep->wa1_trb->control = + priv_ep->wa1_trb->control | 0x1; + } else { + priv_ep->wa1_trb->control = + priv_ep->wa1_trb->control & ~0x1; + } + } +} + +static void cdns3_free_aligned_request_buf(struct work_struct *work) +{ + struct cdns3_device *priv_dev = container_of(work, struct cdns3_device, + aligned_buf_wq); + struct cdns3_aligned_buf *buf, *tmp; + unsigned long flags; + + spin_lock_irqsave(&priv_dev->lock, flags); + + list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list, list) { + if (!buf->in_use) { + list_del(&buf->list); + + /* + * Re-enable interrupts to free DMA capable memory. + * Driver can't free this memory with disabled + * interrupts. + */ + spin_unlock_irqrestore(&priv_dev->lock, flags); + dma_free_coherent(priv_dev->sysdev, buf->size, + buf->buf, buf->dma); + kfree(buf); + spin_lock_irqsave(&priv_dev->lock, flags); + } + } + + spin_unlock_irqrestore(&priv_dev->lock, flags); +} + +static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) +{ + struct cdns3_endpoint *priv_ep = priv_req->priv_ep; + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct cdns3_aligned_buf *buf; + + /* check if buffer is aligned to 8. */ + if (!((uintptr_t)priv_req->request.buf & 0x7)) + return 0; + + buf = priv_req->aligned_buf; + + if (!buf || priv_req->request.length > buf->size) { + buf = kzalloc(sizeof(*buf), GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + buf->size = priv_req->request.length; + + buf->buf = dma_alloc_coherent(priv_dev->sysdev, + buf->size, + &buf->dma, + GFP_ATOMIC); + if (!buf->buf) { + kfree(buf); + return -ENOMEM; + } + + if (priv_req->aligned_buf) { + trace_cdns3_free_aligned_request(priv_req); + priv_req->aligned_buf->in_use = 0; + queue_work(system_freezable_wq, + &priv_dev->aligned_buf_wq); + } + + buf->in_use = 1; + priv_req->aligned_buf = buf; + + list_add_tail(&buf->list, + &priv_dev->aligned_buf_list); + } + + if (priv_ep->dir == USB_DIR_IN) { + memcpy(buf->buf, priv_req->request.buf, + priv_req->request.length); + } + + priv_req->flags |= REQUEST_UNALIGNED; + trace_cdns3_prepare_aligned_request(priv_req); + + return 0; +} + +static int cdns3_wa1_update_guard(struct cdns3_endpoint *priv_ep, + struct cdns3_trb *trb) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + if (!priv_ep->wa1_set) { + u32 doorbell; + + doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); + + if (doorbell) { + priv_ep->wa1_cycle_bit = priv_ep->pcs ? TRB_CYCLE : 0; + priv_ep->wa1_set = 1; + priv_ep->wa1_trb = trb; + priv_ep->wa1_trb_index = priv_ep->enqueue; + trace_cdns3_wa1(priv_ep, "set guard"); + return 0; + } + } + return 1; +} + +static void cdns3_wa1_tray_restore_cycle_bit(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + int dma_index; + u32 doorbell; + + doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); + dma_index = cdns3_get_dma_pos(priv_dev, priv_ep); + + if (!doorbell || dma_index != priv_ep->wa1_trb_index) + cdns3_wa1_restore_cycle_bit(priv_ep); +} + +/** + * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware + * @priv_ep: endpoint object + * + * Returns zero on success or negative value on failure + */ +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, + struct usb_request *request) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct cdns3_request *priv_req; + struct cdns3_trb *trb; + dma_addr_t trb_dma; + u32 togle_pcs = 1; + int sg_iter = 0; + int num_trb; + int address; + u32 control; + int pcs; + + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) + num_trb = priv_ep->interval; + else + num_trb = request->num_sgs ? request->num_sgs : 1; + + if (num_trb > priv_ep->free_trbs) { + priv_ep->flags |= EP_RING_FULL; + return -ENOBUFS; + } + + priv_req = to_cdns3_request(request); + address = priv_ep->endpoint.desc->bEndpointAddress; + + priv_ep->flags |= EP_PENDING_REQUEST; + + /* must allocate buffer aligned to 8 */ + if (priv_req->flags & REQUEST_UNALIGNED) + trb_dma = priv_req->aligned_buf->dma; + else + trb_dma = request->dma; + + trb = priv_ep->trb_pool + priv_ep->enqueue; + priv_req->start_trb = priv_ep->enqueue; + priv_req->trb = trb; + + cdns3_select_ep(priv_ep->cdns3_dev, address); + + /* prepare ring */ + if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) { + struct cdns3_trb *link_trb; + int doorbell, dma_index; + u32 ch_bit = 0; + + doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); + dma_index = cdns3_get_dma_pos(priv_dev, priv_ep); + + /* Driver can't update LINK TRB if it is current processed. */ + if (doorbell && dma_index == priv_ep->num_trbs - 1) { + priv_ep->flags |= EP_DEFERRED_DRDY; + return -ENOBUFS; + } + + /*updating C bt in Link TRB before starting DMA*/ + link_trb = priv_ep->trb_pool + (priv_ep->num_trbs - 1); + /* + * For TRs size equal 2 enabling TRB_CHAIN for epXin causes + * that DMA stuck at the LINK TRB. + * On the other hand, removing TRB_CHAIN for longer TRs for + * epXout cause that DMA stuck after handling LINK TRB. + * To eliminate this strange behavioral driver set TRB_CHAIN + * bit only for TR size > 2. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC || + TRBS_PER_SEGMENT > 2) + ch_bit = TRB_CHAIN; + + link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) | + TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit; + } + + if (priv_dev->dev_ver <= DEV_VER_V2) + togle_pcs = cdns3_wa1_update_guard(priv_ep, trb); + + /* set incorrect Cycle Bit for first trb*/ + control = priv_ep->pcs ? 0 : TRB_CYCLE; + + do { + u32 length; + u16 td_size = 0; + + /* fill TRB */ + control |= TRB_TYPE(TRB_NORMAL); + trb->buffer = TRB_BUFFER(request->num_sgs == 0 + ? trb_dma : request->sg[sg_iter].dma_address); + + if (likely(!request->num_sgs)) + length = request->length; + else + length = request->sg[sg_iter].length; + + if (likely(priv_dev->dev_ver >= DEV_VER_V2)) + td_size = DIV_ROUND_UP(length, + priv_ep->endpoint.maxpacket); + + trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) | + TRB_LEN(length); + if (priv_dev->gadget.speed == USB_SPEED_SUPER) + trb->length |= TRB_TDL_SS_SIZE(td_size); + else + control |= TRB_TDL_HS_SIZE(td_size); + + pcs = priv_ep->pcs ? TRB_CYCLE : 0; + + /* + * first trb should be prepared as last to avoid processing + * transfer to early + */ + if (sg_iter != 0) + control |= pcs; + + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) { + control |= TRB_IOC | TRB_ISP; + } else { + /* for last element in TD or in SG list */ + if (sg_iter == (num_trb - 1) && sg_iter != 0) + control |= pcs | TRB_IOC | TRB_ISP; + } + + if (sg_iter) + trb->control = control; + else + priv_req->trb->control = control; + + control = 0; + ++sg_iter; + priv_req->end_trb = priv_ep->enqueue; + cdns3_ep_inc_enq(priv_ep); + trb = priv_ep->trb_pool + priv_ep->enqueue; + } while (sg_iter < num_trb); + + trb = priv_req->trb; + + priv_req->flags |= REQUEST_PENDING; + + if (sg_iter == 1) + trb->control |= TRB_IOC | TRB_ISP; + + /* + * Memory barrier - cycle bit must be set before other filds in trb. + */ + wmb(); + + /* give the TD to the consumer*/ + if (togle_pcs) + trb->control = trb->control ^ 1; + + if (priv_dev->dev_ver <= DEV_VER_V2) + cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep); + + trace_cdns3_prepare_trb(priv_ep, priv_req->trb); + + /* + * Memory barrier - Cycle Bit must be set before trb->length and + * trb->buffer fields. + */ + wmb(); + + /* + * For DMULT mode we can set address to transfer ring only once after + * enabling endpoint. + */ + if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) { + /* + * Until SW is not ready to handle the OUT transfer the ISO OUT + * Endpoint should be disabled (EP_CFG.ENABLE = 0). + * EP_CFG_ENABLE must be set before updating ep_traddr. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir && + !(priv_ep->flags & EP_QUIRK_ISO_OUT_EN)) { + priv_ep->flags |= EP_QUIRK_ISO_OUT_EN; + cdns3_set_register_bit(&priv_dev->regs->ep_cfg, + EP_CFG_ENABLE); + } + + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma + + priv_req->start_trb * TRB_SIZE), + &priv_dev->regs->ep_traddr); + + priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR; + } + + if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALLED)) { + trace_cdns3_ring(priv_ep); + /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/ + writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts); + writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd); + trace_cdns3_doorbell_epx(priv_ep->name, + readl(&priv_dev->regs->ep_traddr)); + } + + /* WORKAROUND for transition to L0 */ + __cdns3_gadget_wakeup(priv_dev); + + return 0; +} + +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) +{ + struct cdns3_endpoint *priv_ep; + struct usb_ep *ep; + int val; + + if (priv_dev->hw_configured_flag) + return; + + writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf); + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); + + cdns3_set_register_bit(&priv_dev->regs->usb_conf, + USB_CONF_U1EN | USB_CONF_U2EN); + + /* wait until configuration set */ + readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val, + val & USB_STS_CFGSTS_MASK, 1, 100); + + priv_dev->hw_configured_flag = 1; + + list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) { + if (ep->enabled) { + priv_ep = ep_to_cdns3_ep(ep); + cdns3_start_all_request(priv_dev, priv_ep); + } + } +} + +/** + * cdns3_request_handled - check whether request has been handled by DMA + * + * @priv_ep: extended endpoint object. + * @priv_req: request object for checking + * + * Endpoint must be selected before invoking this function. + * + * Returns false if request has not been handled by DMA, else returns true. + * + * SR - start ring + * ER - end ring + * DQ = priv_ep->dequeue - dequeue position + * EQ = priv_ep->enqueue - enqueue position + * ST = priv_req->start_trb - index of first TRB in transfer ring + * ET = priv_req->end_trb - index of last TRB in transfer ring + * CI = current_index - index of processed TRB by DMA. + * + * As first step, function checks if cycle bit for priv_req->start_trb is + * correct. + * + * some rules: + * 1. priv_ep->dequeue never exceed current_index. + * 2 priv_ep->enqueue never exceed priv_ep->dequeue + * 3. exception: priv_ep->enqueue == priv_ep->dequeue + * and priv_ep->free_trbs is zero. + * This case indicate that TR is full. + * + * Then We can split recognition into two parts: + * Case 1 - priv_ep->dequeue < current_index + * SR ... EQ ... DQ ... CI ... ER + * SR ... DQ ... CI ... EQ ... ER + * + * Request has been handled by DMA if ST and ET is between DQ and CI. + * + * Case 2 - priv_ep->dequeue > current_index + * This situation take place when CI go through the LINK TRB at the end of + * transfer ring. + * SR ... CI ... EQ ... DQ ... ER + * + * Request has been handled by DMA if ET is less then CI or + * ET is greater or equal DQ. + */ +static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, + struct cdns3_request *priv_req) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct cdns3_trb *trb = priv_req->trb; + int current_index = 0; + int handled = 0; + int doorbell; + + current_index = cdns3_get_dma_pos(priv_dev, priv_ep); + doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); + + trb = &priv_ep->trb_pool[priv_req->start_trb]; + + if ((trb->control & TRB_CYCLE) != priv_ep->ccs) + goto finish; + + if (doorbell == 1 && current_index == priv_ep->dequeue) + goto finish; + + /* The corner case for TRBS_PER_SEGMENT equal 2). */ + if (TRBS_PER_SEGMENT == 2 && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { + handled = 1; + goto finish; + } + + if (priv_ep->enqueue == priv_ep->dequeue && + priv_ep->free_trbs == 0) { + handled = 1; + } else if (priv_ep->dequeue < current_index) { + if ((current_index == (priv_ep->num_trbs - 1)) && + !priv_ep->dequeue) + goto finish; + + if (priv_req->end_trb >= priv_ep->dequeue && + priv_req->end_trb < current_index) + handled = 1; + } else if (priv_ep->dequeue > current_index) { + if (priv_req->end_trb < current_index || + priv_req->end_trb >= priv_ep->dequeue) + handled = 1; + } + +finish: + trace_cdns3_request_handled(priv_req, current_index, handled); + + return handled; +} + +static void cdns3_transfer_completed(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + struct cdns3_request *priv_req; + struct usb_request *request; + struct cdns3_trb *trb; + + while (!list_empty(&priv_ep->pending_req_list)) { + request = cdns3_next_request(&priv_ep->pending_req_list); + priv_req = to_cdns3_request(request); + + /* Re-select endpoint. It could be changed by other CPU during + * handling usb_gadget_giveback_request. + */ + cdns3_select_ep(priv_dev, priv_ep->endpoint.address); + + if (!cdns3_request_handled(priv_ep, priv_req)) + goto prepare_next_td; + + trb = priv_ep->trb_pool + priv_ep->dequeue; + trace_cdns3_complete_trb(priv_ep, trb); + + if (trb != priv_req->trb) + dev_warn(priv_dev->dev, + "request_trb=0x%p, queue_trb=0x%p\n", + priv_req->trb, trb); + + request->actual = TRB_LEN(le32_to_cpu(trb->length)); + cdns3_move_deq_to_next_trb(priv_req); + cdns3_gadget_giveback(priv_ep, priv_req, 0); + + if (priv_ep->type != USB_ENDPOINT_XFER_ISOC && + TRBS_PER_SEGMENT == 2) + break; + } + priv_ep->flags &= ~EP_PENDING_REQUEST; + +prepare_next_td: + if (!(priv_ep->flags & EP_STALLED) && + !(priv_ep->flags & EP_STALL_PENDING)) + cdns3_start_all_request(priv_dev, priv_ep); +} + +void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + cdns3_wa1_restore_cycle_bit(priv_ep); + + if (rearm) { + trace_cdns3_ring(priv_ep); + + /* Cycle Bit must be updated before arming DMA. */ + wmb(); + writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd); + + __cdns3_gadget_wakeup(priv_dev); + + trace_cdns3_doorbell_epx(priv_ep->name, + readl(&priv_dev->regs->ep_traddr)); + } +} + +/** + * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint + * @priv_ep: endpoint object + * + * Returns 0 + */ +static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + u32 ep_sts_reg; + + cdns3_select_ep(priv_dev, priv_ep->endpoint.address); + + trace_cdns3_epx_irq(priv_dev, priv_ep); + + ep_sts_reg = readl(&priv_dev->regs->ep_sts); + writel(ep_sts_reg, &priv_dev->regs->ep_sts); + + if (ep_sts_reg & EP_STS_TRBERR) { + if (priv_ep->flags & EP_STALL_PENDING && + !(ep_sts_reg & EP_STS_DESCMIS && + priv_dev->dev_ver < DEV_VER_V2)) { + cdns3_ep_stall_flush(priv_ep); + } + + /* + * For isochronous transfer driver completes request on + * IOC or on TRBERR. IOC appears only when device receive + * OUT data packet. If host disable stream or lost some packet + * then the only way to finish all queued transfer is to do it + * on TRBERR event. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && + !priv_ep->wa1_set) { + if (!priv_ep->dir) { + u32 ep_cfg = readl(&priv_dev->regs->ep_cfg); + + ep_cfg &= ~EP_CFG_ENABLE; + writel(ep_cfg, &priv_dev->regs->ep_cfg); + priv_ep->flags &= ~EP_QUIRK_ISO_OUT_EN; + } + cdns3_transfer_completed(priv_dev, priv_ep); + } else if (!(priv_ep->flags & EP_STALLED) && + !(priv_ep->flags & EP_STALL_PENDING)) { + if (priv_ep->flags & EP_DEFERRED_DRDY) { + priv_ep->flags &= ~EP_DEFERRED_DRDY; + cdns3_start_all_request(priv_dev, priv_ep); + } else { + cdns3_rearm_transfer(priv_ep, + priv_ep->wa1_set); + } + } + } + + if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) + cdns3_transfer_completed(priv_dev, priv_ep); + + return 0; +} + +static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) +{ + if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) { + spin_unlock(&priv_dev->lock); + priv_dev->gadget_driver->disconnect(&priv_dev->gadget); + spin_lock(&priv_dev->lock); + } +} + +/** + * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device + * @priv_dev: extended gadget object + * @usb_ists: bitmap representation of device's reported interrupts + * (usb_ists register value) + */ +static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, + u32 usb_ists) +{ + int speed = 0; + + trace_cdns3_usb_irq(priv_dev, usb_ists); + if (usb_ists & USB_ISTS_L1ENTI) { + /* + * WORKAROUND: CDNS3 controller has issue with hardware resuming + * from L1. To fix it, if any DMA transfer is pending driver + * must starts driving resume signal immediately. + */ + if (readl(&priv_dev->regs->drbl)) + __cdns3_gadget_wakeup(priv_dev); + } + + /* Connection detected */ + if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) { + speed = cdns3_get_speed(priv_dev); + priv_dev->gadget.speed = speed; + usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED); + cdns3_ep0_config(priv_dev); + } + + /* Disconnection detected */ + if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) { + cdns3_disconnect_gadget(priv_dev); + priv_dev->gadget.speed = USB_SPEED_UNKNOWN; + usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); + cdns3_hw_reset_eps_config(priv_dev); + } + + if (usb_ists & (USB_ISTS_L2ENTI | USB_ISTS_U3ENTI)) { + if (priv_dev->gadget_driver && + priv_dev->gadget_driver->suspend) { + spin_unlock(&priv_dev->lock); + priv_dev->gadget_driver->suspend(&priv_dev->gadget); + spin_lock(&priv_dev->lock); + } + } + + if (usb_ists & (USB_ISTS_L2EXTI | USB_ISTS_U3EXTI)) { + if (priv_dev->gadget_driver && + priv_dev->gadget_driver->resume) { + spin_unlock(&priv_dev->lock); + priv_dev->gadget_driver->resume(&priv_dev->gadget); + spin_lock(&priv_dev->lock); + } + } + + /* reset*/ + if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) { + if (priv_dev->gadget_driver) { + spin_unlock(&priv_dev->lock); + usb_gadget_udc_reset(&priv_dev->gadget, + priv_dev->gadget_driver); + spin_lock(&priv_dev->lock); + + /*read again to check the actual speed*/ + speed = cdns3_get_speed(priv_dev); + priv_dev->gadget.speed = speed; + cdns3_hw_reset_eps_config(priv_dev); + cdns3_ep0_config(priv_dev); + } + } +} + +/** + * cdns3_device_irq_handler- interrupt handler for device part of controller + * + * @irq: irq number for cdns3 core device + * @data: structure of cdns3 + * + * Returns IRQ_HANDLED or IRQ_NONE + */ +static irqreturn_t cdns3_device_irq_handler(int irq, void *data) +{ + struct cdns3_device *priv_dev; + struct cdns3 *cdns = data; + irqreturn_t ret = IRQ_NONE; + u32 reg; + + priv_dev = cdns->gadget_dev; + + /* check USB device interrupt */ + reg = readl(&priv_dev->regs->usb_ists); + if (reg) { + /* After masking interrupts the new interrupts won't be + * reported in usb_ists/ep_ists. In order to not lose some + * of them driver disables only detected interrupts. + * They will be enabled ASAP after clearing source of + * interrupt. This an unusual behavior only applies to + * usb_ists register. + */ + reg = ~reg & readl(&priv_dev->regs->usb_ien); + /* mask deferred interrupt. */ + writel(reg, &priv_dev->regs->usb_ien); + ret = IRQ_WAKE_THREAD; + } + + /* check endpoint interrupt */ + reg = readl(&priv_dev->regs->ep_ists); + if (reg) { + writel(0, &priv_dev->regs->ep_ien); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +/** + * cdns3_device_thread_irq_handler- interrupt handler for device part + * of controller + * + * @irq: irq number for cdns3 core device + * @data: structure of cdns3 + * + * Returns IRQ_HANDLED or IRQ_NONE + */ +static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data) +{ + struct cdns3_device *priv_dev; + struct cdns3 *cdns = data; + irqreturn_t ret = IRQ_NONE; + unsigned long flags; + int bit; + u32 reg; + + priv_dev = cdns->gadget_dev; + spin_lock_irqsave(&priv_dev->lock, flags); + + reg = readl(&priv_dev->regs->usb_ists); + if (reg) { + writel(reg, &priv_dev->regs->usb_ists); + writel(USB_IEN_INIT, &priv_dev->regs->usb_ien); + cdns3_check_usb_interrupt_proceed(priv_dev, reg); + ret = IRQ_HANDLED; + } + + reg = readl(&priv_dev->regs->ep_ists); + + /* handle default endpoint OUT */ + if (reg & EP_ISTS_EP_OUT0) { + cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT); + ret = IRQ_HANDLED; + } + + /* handle default endpoint IN */ + if (reg & EP_ISTS_EP_IN0) { + cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN); + ret = IRQ_HANDLED; + } + + /* check if interrupt from non default endpoint, if no exit */ + reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0); + if (!reg) + goto irqend; + + for_each_set_bit(bit, (unsigned long *)®, + sizeof(u32) * BITS_PER_BYTE) { + cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]); + ret = IRQ_HANDLED; + } + +irqend: + writel(~0, &priv_dev->regs->ep_ien); + spin_unlock_irqrestore(&priv_dev->lock, flags); + + return ret; +} + +/** + * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP + * + * The real reservation will occur during write to EP_CFG register, + * this function is used to check if the 'size' reservation is allowed. + * + * @priv_dev: extended gadget object + * @size: the size (KB) for EP would like to allocate + * @is_in: endpoint direction + * + * Return 0 if the required size can met or negative value on failure + */ +static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev, + int size, int is_in) +{ + int remained; + + /* 2KB are reserved for EP0*/ + remained = priv_dev->onchip_buffers - priv_dev->onchip_used_size - 2; + + if (is_in) { + if (remained < size) + return -EPERM; + + priv_dev->onchip_used_size += size; + } else { + int required; + + /** + * ALL OUT EPs are shared the same chunk onchip memory, so + * driver checks if it already has assigned enough buffers + */ + if (priv_dev->out_mem_is_allocated >= size) + return 0; + + required = size - priv_dev->out_mem_is_allocated; + + if (required > remained) + return -EPERM; + + priv_dev->out_mem_is_allocated += required; + priv_dev->onchip_used_size += required; + } + + return 0; +} + +void cdns3_configure_dmult(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + struct cdns3_usb_regs __iomem *regs = priv_dev->regs; + + /* For dev_ver > DEV_VER_V2 DMULT is configured per endpoint */ + if (priv_dev->dev_ver <= DEV_VER_V2) + writel(USB_CONF_DMULT, ®s->usb_conf); + + if (priv_dev->dev_ver == DEV_VER_V2) + writel(USB_CONF2_EN_TDL_TRB, ®s->usb_conf2); + + if (priv_dev->dev_ver >= DEV_VER_V3 && priv_ep) { + u32 mask; + + if (priv_ep->dir) + mask = BIT(priv_ep->num + 16); + else + mask = BIT(priv_ep->num); + + if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) { + cdns3_set_register_bit(®s->tdl_from_trb, mask); + cdns3_set_register_bit(®s->tdl_beh, mask); + cdns3_set_register_bit(®s->tdl_beh2, mask); + cdns3_set_register_bit(®s->dma_adv_td, mask); + } + + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) + cdns3_set_register_bit(®s->tdl_from_trb, mask); + + cdns3_set_register_bit(®s->dtrans, mask); + } +} + +/** + * cdns3_ep_config Configure hardware endpoint + * @priv_ep: extended endpoint object + */ +void cdns3_ep_config(struct cdns3_endpoint *priv_ep) +{ + bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + u32 bEndpointAddress = priv_ep->num | priv_ep->dir; + u32 max_packet_size = 0; + u8 maxburst = 0; + u32 ep_cfg = 0; + u8 buffering; + u8 mult = 0; + int ret; + + buffering = CDNS3_EP_BUF_SIZE - 1; + + cdns3_configure_dmult(priv_dev, priv_ep); + + switch (priv_ep->type) { + case USB_ENDPOINT_XFER_INT: + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT); + + if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) || + priv_dev->dev_ver > DEV_VER_V2) + ep_cfg |= EP_CFG_TDL_CHK; + break; + case USB_ENDPOINT_XFER_BULK: + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK); + + if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) || + priv_dev->dev_ver > DEV_VER_V2) + ep_cfg |= EP_CFG_TDL_CHK; + break; + default: + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); + mult = CDNS3_EP_ISO_HS_MULT - 1; + buffering = mult + 1; + } + + switch (priv_dev->gadget.speed) { + case USB_SPEED_FULL: + max_packet_size = is_iso_ep ? 1023 : 64; + break; + case USB_SPEED_HIGH: + max_packet_size = is_iso_ep ? 1024 : 512; + break; + case USB_SPEED_SUPER: + /* It's limitation that driver assumes in driver. */ + mult = 0; + max_packet_size = 1024; + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) { + maxburst = CDNS3_EP_ISO_SS_BURST - 1; + buffering = (mult + 1) * + (maxburst + 1); + + if (priv_ep->interval > 1) + buffering++; + } else { + maxburst = CDNS3_EP_BUF_SIZE - 1; + } + break; + default: + /* all other speed are not supported */ + return; + } + + if (max_packet_size == 1024) + priv_ep->trb_burst_size = 128; + else if (max_packet_size >= 512) + priv_ep->trb_burst_size = 64; + else + priv_ep->trb_burst_size = 16; + + ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, + !!priv_ep->dir); + if (ret) { + dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); + return; + } + + ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | + EP_CFG_MULT(mult) | + EP_CFG_BUFFERING(buffering) | + EP_CFG_MAXBURST(maxburst); + + cdns3_select_ep(priv_dev, bEndpointAddress); + writel(ep_cfg, &priv_dev->regs->ep_cfg); + + dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n", + priv_ep->name, ep_cfg); +} + +/* Find correct direction for HW endpoint according to description */ +static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc, + struct cdns3_endpoint *priv_ep) +{ + return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) || + (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc)); +} + +static struct +cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev, + struct usb_endpoint_descriptor *desc) +{ + struct usb_ep *ep; + struct cdns3_endpoint *priv_ep; + + list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) { + unsigned long num; + int ret; + /* ep name pattern likes epXin or epXout */ + char c[2] = {ep->name[2], '\0'}; + + ret = kstrtoul(c, 10, &num); + if (ret) + return ERR_PTR(ret); + + priv_ep = ep_to_cdns3_ep(ep); + if (cdns3_ep_dir_is_correct(desc, priv_ep)) { + if (!(priv_ep->flags & EP_CLAIMED)) { + priv_ep->num = num; + return priv_ep; + } + } + } + + return ERR_PTR(-ENOENT); +} + +/* + * Cadence IP has one limitation that all endpoints must be configured + * (Type & MaxPacketSize) before setting configuration through hardware + * register, it means we can't change endpoints configuration after + * set_configuration. + * + * This function set EP_CLAIMED flag which is added when the gadget driver + * uses usb_ep_autoconfig to configure specific endpoint; + * When the udc driver receives set_configurion request, + * it goes through all claimed endpoints, and configure all endpoints + * accordingly. + * + * At usb_ep_ops.enable/disable, we only enable and disable endpoint through + * ep_cfg register which can be changed after set_configuration, and do + * some software operation accordingly. + */ +static struct +usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *comp_desc) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + struct cdns3_endpoint *priv_ep; + unsigned long flags; + + priv_ep = cdns3_find_available_ep(priv_dev, desc); + if (IS_ERR(priv_ep)) { + dev_err(priv_dev->dev, "no available ep\n"); + return NULL; + } + + dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name); + + spin_lock_irqsave(&priv_dev->lock, flags); + priv_ep->endpoint.desc = desc; + priv_ep->dir = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT; + priv_ep->type = usb_endpoint_type(desc); + priv_ep->flags |= EP_CLAIMED; + priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0; + + spin_unlock_irqrestore(&priv_dev->lock, flags); + return &priv_ep->endpoint; +} + +/** + * cdns3_gadget_ep_alloc_request Allocates request + * @ep: endpoint object associated with request + * @gfp_flags: gfp flags + * + * Returns allocated request address, NULL on allocation error + */ +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); + struct cdns3_request *priv_req; + + priv_req = kzalloc(sizeof(*priv_req), gfp_flags); + if (!priv_req) + return NULL; + + priv_req->priv_ep = priv_ep; + + trace_cdns3_alloc_request(priv_req); + return &priv_req->request; +} + +/** + * cdns3_gadget_ep_free_request Free memory occupied by request + * @ep: endpoint object associated with request + * @request: request to free memory + */ +void cdns3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct cdns3_request *priv_req = to_cdns3_request(request); + + if (priv_req->aligned_buf) + priv_req->aligned_buf->in_use = 0; + + trace_cdns3_free_request(priv_req); + kfree(priv_req); +} + +/** + * cdns3_gadget_ep_enable Enable endpoint + * @ep: endpoint object + * @desc: endpoint descriptor + * + * Returns 0 on success, error code elsewhere + */ +static int cdns3_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct cdns3_endpoint *priv_ep; + struct cdns3_device *priv_dev; + u32 reg = EP_STS_EN_TRBERREN; + u32 bEndpointAddress; + unsigned long flags; + int enable = 1; + int ret; + int val; + + priv_ep = ep_to_cdns3_ep(ep); + priv_dev = priv_ep->cdns3_dev; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + dev_dbg(priv_dev->dev, "usbss: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n"); + return -EINVAL; + } + + if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED, + "%s is already enabled\n", priv_ep->name)) + return 0; + + spin_lock_irqsave(&priv_dev->lock, flags); + + priv_ep->endpoint.desc = desc; + priv_ep->type = usb_endpoint_type(desc); + priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0; + + if (priv_ep->interval > ISO_MAX_INTERVAL && + priv_ep->type == USB_ENDPOINT_XFER_ISOC) { + dev_err(priv_dev->dev, "Driver is limited to %d period\n", + ISO_MAX_INTERVAL); + + ret = -EINVAL; + goto exit; + } + + ret = cdns3_allocate_trb_pool(priv_ep); + + if (ret) + goto exit; + + bEndpointAddress = priv_ep->num | priv_ep->dir; + cdns3_select_ep(priv_dev, bEndpointAddress); + + trace_cdns3_gadget_ep_enable(priv_ep); + + writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd); + + ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)), + 1, 1000); + + if (unlikely(ret)) { + cdns3_free_trb_pool(priv_ep); + ret = -EINVAL; + goto exit; + } + + /* enable interrupt for selected endpoint */ + cdns3_set_register_bit(&priv_dev->regs->ep_ien, + BIT(cdns3_ep_addr_to_index(bEndpointAddress))); + + writel(reg, &priv_dev->regs->ep_sts_en); + + /* + * For some versions of controller at some point during ISO OUT traffic + * DMA reads Transfer Ring for the EP which has never got doorbell. + * This issue was detected only on simulation, but to avoid this issue + * driver add protection against it. To fix it driver enable ISO OUT + * endpoint before setting DRBL. This special treatment of ISO OUT + * endpoints are recommended by controller specification. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) + enable = 0; + + if (enable) + cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE); + + ep->desc = desc; + priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING | + EP_QUIRK_ISO_OUT_EN); + priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR; + priv_ep->wa1_set = 0; + priv_ep->enqueue = 0; + priv_ep->dequeue = 0; + reg = readl(&priv_dev->regs->ep_sts); + priv_ep->pcs = !!EP_STS_CCS(reg); + priv_ep->ccs = !!EP_STS_CCS(reg); + /* one TRB is reserved for link TRB used in DMULT mode*/ + priv_ep->free_trbs = priv_ep->num_trbs - 1; +exit: + spin_unlock_irqrestore(&priv_dev->lock, flags); + + return ret; +} + +/** + * cdns3_gadget_ep_disable Disable endpoint + * @ep: endpoint object + * + * Returns 0 on success, error code elsewhere + */ +static int cdns3_gadget_ep_disable(struct usb_ep *ep) +{ + struct cdns3_endpoint *priv_ep; + struct cdns3_device *priv_dev; + struct usb_request *request; + unsigned long flags; + int ret = 0; + u32 ep_cfg; + int val; + + if (!ep) { + pr_err("usbss: invalid parameters\n"); + return -EINVAL; + } + + priv_ep = ep_to_cdns3_ep(ep); + priv_dev = priv_ep->cdns3_dev; + + if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED), + "%s is already disabled\n", priv_ep->name)) + return 0; + + spin_lock_irqsave(&priv_dev->lock, flags); + + trace_cdns3_gadget_ep_disable(priv_ep); + + cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress); + + ep_cfg = readl(&priv_dev->regs->ep_cfg); + ep_cfg &= ~EP_CFG_ENABLE; + writel(ep_cfg, &priv_dev->regs->ep_cfg); + + /** + * Driver needs some time before resetting endpoint. + * It need waits for clearing DBUSY bit or for timeout expired. + * 10us is enough time for controller to stop transfer. + */ + readl_poll_timeout_atomic(&priv_dev->regs->ep_sts, val, + !(val & EP_STS_DBUSY), 1, 10); + writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd); + + readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)), + 1, 1000); + if (unlikely(ret)) + dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n", + priv_ep->name); + + while (!list_empty(&priv_ep->pending_req_list)) { + request = cdns3_next_request(&priv_ep->pending_req_list); + + cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), + -ESHUTDOWN); + } + + while (!list_empty(&priv_ep->deferred_req_list)) { + request = cdns3_next_request(&priv_ep->deferred_req_list); + + cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), + -ESHUTDOWN); + } + + ep->desc = NULL; + priv_ep->flags &= ~EP_ENABLED; + + spin_unlock_irqrestore(&priv_dev->lock, flags); + + return ret; +} + +/** + * cdns3_gadget_ep_queue Transfer data on endpoint + * @ep: endpoint object + * @request: request object + * @gfp_flags: gfp flags + * + * Returns 0 on success, error code elsewhere + */ +static int __cdns3_gadget_ep_queue(struct usb_ep *ep, + struct usb_request *request, + gfp_t gfp_flags) +{ + struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct cdns3_request *priv_req; + int ret = 0; + + request->actual = 0; + request->status = -EINPROGRESS; + priv_req = to_cdns3_request(request); + trace_cdns3_ep_queue(priv_req); + + ret = cdns3_prepare_aligned_request_buf(priv_req); + if (ret < 0) + return ret; + + ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, + usb_endpoint_dir_in(ep->desc)); + if (ret) + return ret; + + list_add_tail(&request->list, &priv_ep->deferred_req_list); + + /* + * If hardware endpoint configuration has not been set yet then + * just queue request in deferred list. Transfer will be started in + * cdns3_set_hw_configuration. + */ + if (priv_dev->hw_configured_flag && !(priv_ep->flags & EP_STALLED) && + !(priv_ep->flags & EP_STALL_PENDING)) + cdns3_start_all_request(priv_dev, priv_ep); + + return 0; +} + +static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct usb_request *zlp_request; + struct cdns3_endpoint *priv_ep; + struct cdns3_device *priv_dev; + unsigned long flags; + int ret; + + if (!request || !ep) + return -EINVAL; + + priv_ep = ep_to_cdns3_ep(ep); + priv_dev = priv_ep->cdns3_dev; + + spin_lock_irqsave(&priv_dev->lock, flags); + + ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags); + + if (ret == 0 && request->zero && request->length && + (request->length % ep->maxpacket == 0)) { + struct cdns3_request *priv_req; + + zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC); + zlp_request->buf = priv_dev->zlp_buf; + zlp_request->length = 0; + + priv_req = to_cdns3_request(zlp_request); + priv_req->flags |= REQUEST_ZLP; + + dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n", + priv_ep->name); + ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags); + } + + spin_unlock_irqrestore(&priv_dev->lock, flags); + return ret; +} + +/** + * cdns3_gadget_ep_dequeue Remove request from transfer queue + * @ep: endpoint object associated with request + * @request: request object + * + * Returns 0 on success, error code elsewhere + */ +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct usb_request *req, *req_temp; + struct cdns3_request *priv_req; + struct cdns3_trb *link_trb; + unsigned long flags; + int ret = 0; + + if (!ep || !request || !ep->desc) + return -EINVAL; + + spin_lock_irqsave(&priv_dev->lock, flags); + + priv_req = to_cdns3_request(request); + + trace_cdns3_ep_dequeue(priv_req); + + cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress); + + list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list, + list) { + if (request == req) + goto found; + } + + list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list, + list) { + if (request == req) + goto found; + } + + goto not_found; + +found: + + if (priv_ep->wa1_trb == priv_req->trb) + cdns3_wa1_restore_cycle_bit(priv_ep); + + link_trb = priv_req->trb; + cdns3_move_deq_to_next_trb(priv_req); + cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET); + + /* Update ring */ + request = cdns3_next_request(&priv_ep->deferred_req_list); + if (request) { + priv_req = to_cdns3_request(request); + + link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma + + (priv_req->start_trb * TRB_SIZE)); + link_trb->control = (link_trb->control & TRB_CYCLE) | + TRB_TYPE(TRB_LINK) | TRB_CHAIN | TRB_TOGGLE; + } else { + priv_ep->flags |= EP_UPDATE_EP_TRBADDR; + } + +not_found: + spin_unlock_irqrestore(&priv_dev->lock, flags); + return ret; +} + +/** + * __cdns3_gadget_ep_set_halt Sets stall on selected endpoint + * Should be called after acquiring spin_lock and selecting ep + * @ep: endpoint object to set stall on. + */ +void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + trace_cdns3_halt(priv_ep, 1, 0); + + if (!(priv_ep->flags & EP_STALLED)) { + u32 ep_sts_reg = readl(&priv_dev->regs->ep_sts); + + if (!(ep_sts_reg & EP_STS_DBUSY)) + cdns3_ep_stall_flush(priv_ep); + else + priv_ep->flags |= EP_STALL_PENDING; + } +} + +/** + * __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint + * Should be called after acquiring spin_lock and selecting ep + * @ep: endpoint object to clear stall on + */ +int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct usb_request *request; + int ret = 0; + int val; + + trace_cdns3_halt(priv_ep, 0, 0); + + writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd); + + /* wait for EPRST cleared */ + readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & EP_CMD_EPRST), 1, 100); + if (ret) + return -EINVAL; + + priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING); + + request = cdns3_next_request(&priv_ep->pending_req_list); + + if (request) + cdns3_rearm_transfer(priv_ep, 1); + + cdns3_start_all_request(priv_dev, priv_ep); + return ret; +} + +/** + * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint + * @ep: endpoint object to set/clear stall on + * @value: 1 for set stall, 0 for clear stall + * + * Returns 0 on success, error code elsewhere + */ +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + unsigned long flags; + int ret = 0; + + if (!(priv_ep->flags & EP_ENABLED)) + return -EPERM; + + spin_lock_irqsave(&priv_dev->lock, flags); + + cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress); + + if (!value) { + priv_ep->flags &= ~EP_WEDGE; + ret = __cdns3_gadget_ep_clear_halt(priv_ep); + } else { + __cdns3_gadget_ep_set_halt(priv_ep); + } + + spin_unlock_irqrestore(&priv_dev->lock, flags); + + return ret; +} + +extern const struct usb_ep_ops cdns3_gadget_ep0_ops; + +static const struct usb_ep_ops cdns3_gadget_ep_ops = { + .enable = cdns3_gadget_ep_enable, + .disable = cdns3_gadget_ep_disable, + .alloc_request = cdns3_gadget_ep_alloc_request, + .free_request = cdns3_gadget_ep_free_request, + .queue = cdns3_gadget_ep_queue, + .dequeue = cdns3_gadget_ep_dequeue, + .set_halt = cdns3_gadget_ep_set_halt, + .set_wedge = cdns3_gadget_ep_set_wedge, +}; + +/** + * cdns3_gadget_get_frame Returns number of actual ITP frame + * @gadget: gadget object + * + * Returns number of actual ITP frame + */ +static int cdns3_gadget_get_frame(struct usb_gadget *gadget) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + + return readl(&priv_dev->regs->usb_itpn); +} + +int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev) +{ + enum usb_device_speed speed; + + speed = cdns3_get_speed(priv_dev); + + if (speed >= USB_SPEED_SUPER) + return 0; + + /* Start driving resume signaling to indicate remote wakeup. */ + writel(USB_CONF_LGO_L0, &priv_dev->regs->usb_conf); + + return 0; +} + +static int cdns3_gadget_wakeup(struct usb_gadget *gadget) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&priv_dev->lock, flags); + ret = __cdns3_gadget_wakeup(priv_dev); + spin_unlock_irqrestore(&priv_dev->lock, flags); + return ret; +} + +static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget, + int is_selfpowered) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + unsigned long flags; + + spin_lock_irqsave(&priv_dev->lock, flags); + priv_dev->is_selfpowered = !!is_selfpowered; + spin_unlock_irqrestore(&priv_dev->lock, flags); + return 0; +} + +static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + + if (is_on) + writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); + else + writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); + + return 0; +} + +static void cdns3_gadget_config(struct cdns3_device *priv_dev) +{ + struct cdns3_usb_regs __iomem *regs = priv_dev->regs; + u32 reg; + + cdns3_ep0_config(priv_dev); + + /* enable interrupts for endpoint 0 (in and out) */ + writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, ®s->ep_ien); + + /* + * Driver needs to modify LFPS minimal U1 Exit time for DEV_VER_TI_V1 + * revision of controller. + */ + if (priv_dev->dev_ver == DEV_VER_TI_V1) { + reg = readl(®s->dbg_link1); + + reg &= ~DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK; + reg |= DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x55) | + DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET; + writel(reg, ®s->dbg_link1); + } + + /* + * By default some platforms has set protected access to memory. + * This cause problem with cache, so driver restore non-secure + * access to memory. + */ + reg = readl(®s->dma_axi_ctrl); + reg |= DMA_AXI_CTRL_MARPROT(DMA_AXI_CTRL_NON_SECURE) | + DMA_AXI_CTRL_MAWPROT(DMA_AXI_CTRL_NON_SECURE); + writel(reg, ®s->dma_axi_ctrl); + + /* enable generic interrupt*/ + writel(USB_IEN_INIT, ®s->usb_ien); + writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, ®s->usb_conf); + + cdns3_configure_dmult(priv_dev, NULL); + + cdns3_gadget_pullup(&priv_dev->gadget, 1); +} + +/** + * cdns3_gadget_udc_start Gadget start + * @gadget: gadget object + * @driver: driver which operates on this gadget + * + * Returns 0 on success, error code elsewhere + */ +static int cdns3_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + unsigned long flags; + + spin_lock_irqsave(&priv_dev->lock, flags); + priv_dev->gadget_driver = driver; + cdns3_gadget_config(priv_dev); + spin_unlock_irqrestore(&priv_dev->lock, flags); + return 0; +} + +/** + * cdns3_gadget_udc_stop Stops gadget + * @gadget: gadget object + * + * Returns 0 + */ +static int cdns3_gadget_udc_stop(struct usb_gadget *gadget) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + struct cdns3_endpoint *priv_ep; + u32 bEndpointAddress; + struct usb_ep *ep; + int ret = 0; + int val; + + priv_dev->gadget_driver = NULL; + + priv_dev->onchip_used_size = 0; + priv_dev->out_mem_is_allocated = 0; + priv_dev->gadget.speed = USB_SPEED_UNKNOWN; + + list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) { + priv_ep = ep_to_cdns3_ep(ep); + bEndpointAddress = priv_ep->num | priv_ep->dir; + cdns3_select_ep(priv_dev, bEndpointAddress); + writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd); + readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & EP_CMD_EPRST), 1, 100); + } + + /* disable interrupt for device */ + writel(0, &priv_dev->regs->usb_ien); + writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); + + return ret; +} + +static const struct usb_gadget_ops cdns3_gadget_ops = { + .get_frame = cdns3_gadget_get_frame, + .wakeup = cdns3_gadget_wakeup, + .set_selfpowered = cdns3_gadget_set_selfpowered, + .pullup = cdns3_gadget_pullup, + .udc_start = cdns3_gadget_udc_start, + .udc_stop = cdns3_gadget_udc_stop, + .match_ep = cdns3_gadget_match_ep, +}; + +static void cdns3_free_all_eps(struct cdns3_device *priv_dev) +{ + int i; + + /* ep0 OUT point to ep0 IN. */ + priv_dev->eps[16] = NULL; + + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) + if (priv_dev->eps[i]) { + cdns3_free_trb_pool(priv_dev->eps[i]); + devm_kfree(priv_dev->dev, priv_dev->eps[i]); + } +} + +/** + * cdns3_init_eps Initializes software endpoints of gadget + * @cdns3: extended gadget object + * + * Returns 0 on success, error code elsewhere + */ +static int cdns3_init_eps(struct cdns3_device *priv_dev) +{ + u32 ep_enabled_reg, iso_ep_reg; + struct cdns3_endpoint *priv_ep; + int ep_dir, ep_number; + u32 ep_mask; + int ret = 0; + int i; + + /* Read it from USB_CAP3 to USB_CAP5 */ + ep_enabled_reg = readl(&priv_dev->regs->usb_cap3); + iso_ep_reg = readl(&priv_dev->regs->usb_cap4); + + dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n"); + + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { + ep_dir = i >> 4; /* i div 16 */ + ep_number = i & 0xF; /* i % 16 */ + ep_mask = BIT(i); + + if (!(ep_enabled_reg & ep_mask)) + continue; + + if (ep_dir && !ep_number) { + priv_dev->eps[i] = priv_dev->eps[0]; + continue; + } + + priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep), + GFP_KERNEL); + if (!priv_ep) { + ret = -ENOMEM; + goto err; + } + + /* set parent of endpoint object */ + priv_ep->cdns3_dev = priv_dev; + priv_dev->eps[i] = priv_ep; + priv_ep->num = ep_number; + priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT; + + if (!ep_number) { + ret = cdns3_init_ep0(priv_dev, priv_ep); + if (ret) { + dev_err(priv_dev->dev, "Failed to init ep0\n"); + goto err; + } + } else { + snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s", + ep_number, !!ep_dir ? "in" : "out"); + priv_ep->endpoint.name = priv_ep->name; + + usb_ep_set_maxpacket_limit(&priv_ep->endpoint, + CDNS3_EP_MAX_PACKET_LIMIT); + priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS; + priv_ep->endpoint.ops = &cdns3_gadget_ep_ops; + if (ep_dir) + priv_ep->endpoint.caps.dir_in = 1; + else + priv_ep->endpoint.caps.dir_out = 1; + + if (iso_ep_reg & ep_mask) + priv_ep->endpoint.caps.type_iso = 1; + + priv_ep->endpoint.caps.type_bulk = 1; + priv_ep->endpoint.caps.type_int = 1; + + list_add_tail(&priv_ep->endpoint.ep_list, + &priv_dev->gadget.ep_list); + } + + priv_ep->flags = 0; + + dev_info(priv_dev->dev, "Initialized %s support: %s %s\n", + priv_ep->name, + priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "", + priv_ep->endpoint.caps.type_iso ? "ISO" : ""); + + INIT_LIST_HEAD(&priv_ep->pending_req_list); + INIT_LIST_HEAD(&priv_ep->deferred_req_list); + } + + return 0; +err: + cdns3_free_all_eps(priv_dev); + return -ENOMEM; +} + +void cdns3_gadget_exit(struct cdns3 *cdns) +{ + struct cdns3_device *priv_dev; + + priv_dev = cdns->gadget_dev; + + devm_free_irq(cdns->dev, cdns->dev_irq, cdns); + + pm_runtime_mark_last_busy(cdns->dev); + pm_runtime_put_autosuspend(cdns->dev); + + usb_del_gadget_udc(&priv_dev->gadget); + + cdns3_free_all_eps(priv_dev); + + while (!list_empty(&priv_dev->aligned_buf_list)) { + struct cdns3_aligned_buf *buf; + + buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list); + dma_free_coherent(priv_dev->sysdev, buf->size, + buf->buf, + buf->dma); + + list_del(&buf->list); + kfree(buf); + } + + dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf, + priv_dev->setup_dma); + + kfree(priv_dev->zlp_buf); + kfree(priv_dev); + cdns->gadget_dev = NULL; + cdns3_drd_switch_gadget(cdns, 0); +} + +static int cdns3_gadget_start(struct cdns3 *cdns) +{ + struct cdns3_device *priv_dev; + u32 max_speed; + int ret; + + priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL); + if (!priv_dev) + return -ENOMEM; + + cdns->gadget_dev = priv_dev; + priv_dev->sysdev = cdns->dev; + priv_dev->dev = cdns->dev; + priv_dev->regs = cdns->dev_regs; + + device_property_read_u16(priv_dev->dev, "cdns,on-chip-buff-size", + &priv_dev->onchip_buffers); + + if (priv_dev->onchip_buffers <= 0) { + u32 reg = readl(&priv_dev->regs->usb_cap2); + + priv_dev->onchip_buffers = USB_CAP2_ACTUAL_MEM_SIZE(reg); + } + + if (!priv_dev->onchip_buffers) + priv_dev->onchip_buffers = 256; + + max_speed = usb_get_maximum_speed(cdns->dev); + + /* Check the maximum_speed parameter */ + switch (max_speed) { + case USB_SPEED_FULL: + writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf); + break; + case USB_SPEED_HIGH: + writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf); + break; + case USB_SPEED_SUPER: + break; + default: + dev_err(cdns->dev, "invalid maximum_speed parameter %d\n", + max_speed); + /* fall through */ + case USB_SPEED_UNKNOWN: + /* default to superspeed */ + max_speed = USB_SPEED_SUPER; + break; + } + + /* fill gadget fields */ + priv_dev->gadget.max_speed = max_speed; + priv_dev->gadget.speed = USB_SPEED_UNKNOWN; + priv_dev->gadget.ops = &cdns3_gadget_ops; + priv_dev->gadget.name = "usb-ss-gadget"; + priv_dev->gadget.sg_supported = 1; + priv_dev->gadget.quirk_avoids_skb_reserve = 1; + + spin_lock_init(&priv_dev->lock); + INIT_WORK(&priv_dev->pending_status_wq, + cdns3_pending_setup_status_handler); + + INIT_WORK(&priv_dev->aligned_buf_wq, + cdns3_free_aligned_request_buf); + + /* initialize endpoint container */ + INIT_LIST_HEAD(&priv_dev->gadget.ep_list); + INIT_LIST_HEAD(&priv_dev->aligned_buf_list); + + ret = cdns3_init_eps(priv_dev); + if (ret) { + dev_err(priv_dev->dev, "Failed to create endpoints\n"); + goto err1; + } + + /* allocate memory for setup packet buffer */ + priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8, + &priv_dev->setup_dma, GFP_DMA); + if (!priv_dev->setup_buf) { + ret = -ENOMEM; + goto err2; + } + + priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6); + + dev_dbg(priv_dev->dev, "Device Controller version: %08x\n", + readl(&priv_dev->regs->usb_cap6)); + dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n", + readl(&priv_dev->regs->usb_cap1)); + dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n", + readl(&priv_dev->regs->usb_cap2)); + + priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver); + + priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL); + if (!priv_dev->zlp_buf) { + ret = -ENOMEM; + goto err3; + } + + /* add USB gadget device */ + ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget); + if (ret < 0) { + dev_err(priv_dev->dev, + "Failed to register USB device controller\n"); + goto err4; + } + + return 0; +err4: + kfree(priv_dev->zlp_buf); +err3: + dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf, + priv_dev->setup_dma); +err2: + cdns3_free_all_eps(priv_dev); +err1: + cdns->gadget_dev = NULL; + return ret; +} + +static int __cdns3_gadget_init(struct cdns3 *cdns) +{ + struct cdns3_device *priv_dev; + int ret = 0; + + cdns3_drd_switch_gadget(cdns, 1); + pm_runtime_get_sync(cdns->dev); + + ret = cdns3_gadget_start(cdns); + if (ret) + return ret; + + priv_dev = cdns->gadget_dev; + + /* + * Because interrupt line can be shared with other components in + * driver it can't use IRQF_ONESHOT flag here. + */ + ret = devm_request_threaded_irq(cdns->dev, cdns->dev_irq, + cdns3_device_irq_handler, + cdns3_device_thread_irq_handler, + IRQF_SHARED, dev_name(cdns->dev), cdns); + + if (ret) + goto err0; + + return 0; +err0: + cdns3_gadget_exit(cdns); + return ret; +} + +static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup) +{ + struct cdns3_device *priv_dev = cdns->gadget_dev; + + cdns3_disconnect_gadget(priv_dev); + + priv_dev->gadget.speed = USB_SPEED_UNKNOWN; + usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); + cdns3_hw_reset_eps_config(priv_dev); + + /* disable interrupt for device */ + writel(0, &priv_dev->regs->usb_ien); + + cdns3_gadget_pullup(&priv_dev->gadget, 0); + + return 0; +} + +static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated) +{ + struct cdns3_device *priv_dev = cdns->gadget_dev; + + if (!priv_dev->gadget_driver) + return 0; + + cdns3_gadget_config(priv_dev); + + return 0; +} + +/** + * cdns3_gadget_init - initialize device structure + * + * cdns: cdns3 instance + * + * This function initializes the gadget. + */ +int cdns3_gadget_init(struct cdns3 *cdns) +{ + struct cdns3_role_driver *rdrv; + + rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); + if (!rdrv) + return -ENOMEM; + + rdrv->start = __cdns3_gadget_init; + rdrv->stop = cdns3_gadget_exit; + rdrv->suspend = cdns3_gadget_suspend; + rdrv->resume = cdns3_gadget_resume; + rdrv->state = CDNS3_ROLE_STATE_INACTIVE; + rdrv->name = "gadget"; + cdns->roles[USB_ROLE_DEVICE] = rdrv; + + return 0; +} diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h new file mode 100644 index 000000000000..a4652d2e9bf9 --- /dev/null +++ b/drivers/usb/cdns3/gadget.h @@ -0,0 +1,1325 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * USBSS device controller driver header file + * + * Copyright (C) 2018-2019 Cadence. + * Copyright (C) 2017-2018 NXP + * + * Author: Pawel Laszczak + * Pawel Jez + * Peter Chen + */ +#ifndef __LINUX_CDNS3_GADGET +#define __LINUX_CDNS3_GADGET +#include + +/* + * USBSS-DEV register interface. + * This corresponds to the USBSS Device Controller Interface + */ + +/** + * struct cdns3_usb_regs - device controller registers. + * @usb_conf: Global Configuration. + * @usb_sts: Global Status. + * @usb_cmd: Global Command. + * @usb_itpn: ITP/SOF number. + * @usb_lpm: Global Command. + * @usb_ien: USB Interrupt Enable. + * @usb_ists: USB Interrupt Status. + * @ep_sel: Endpoint Select. + * @ep_traddr: Endpoint Transfer Ring Address. + * @ep_cfg: Endpoint Configuration. + * @ep_cmd: Endpoint Command. + * @ep_sts: Endpoint Status. + * @ep_sts_sid: Endpoint Status. + * @ep_sts_en: Endpoint Status Enable. + * @drbl: Doorbell. + * @ep_ien: EP Interrupt Enable. + * @ep_ists: EP Interrupt Status. + * @usb_pwr: Global Power Configuration. + * @usb_conf2: Global Configuration 2. + * @usb_cap1: Capability 1. + * @usb_cap2: Capability 2. + * @usb_cap3: Capability 3. + * @usb_cap4: Capability 4. + * @usb_cap5: Capability 5. + * @usb_cap6: Capability 6. + * @usb_cpkt1: Custom Packet 1. + * @usb_cpkt2: Custom Packet 2. + * @usb_cpkt3: Custom Packet 3. + * @ep_dma_ext_addr: Upper address for DMA operations. + * @buf_addr: Address for On-chip Buffer operations. + * @buf_data: Data for On-chip Buffer operations. + * @buf_ctrl: On-chip Buffer Access Control. + * @dtrans: DMA Transfer Mode. + * @tdl_from_trb: Source of TD Configuration. + * @tdl_beh: TDL Behavior Configuration. + * @ep_tdl: Endpoint TDL. + * @tdl_beh2: TDL Behavior 2 Configuration. + * @dma_adv_td: DMA Advance TD Configuration. + * @reserved1: Reserved. + * @cfg_regs: Configuration. + * @reserved2: Reserved. + * @dma_axi_ctrl: AXI Control. + * @dma_axi_id: AXI ID register. + * @dma_axi_cap: AXI Capability. + * @dma_axi_ctrl0: AXI Control 0. + * @dma_axi_ctrl1: AXI Control 1. + */ +struct cdns3_usb_regs { + __le32 usb_conf; + __le32 usb_sts; + __le32 usb_cmd; + __le32 usb_itpn; + __le32 usb_lpm; + __le32 usb_ien; + __le32 usb_ists; + __le32 ep_sel; + __le32 ep_traddr; + __le32 ep_cfg; + __le32 ep_cmd; + __le32 ep_sts; + __le32 ep_sts_sid; + __le32 ep_sts_en; + __le32 drbl; + __le32 ep_ien; + __le32 ep_ists; + __le32 usb_pwr; + __le32 usb_conf2; + __le32 usb_cap1; + __le32 usb_cap2; + __le32 usb_cap3; + __le32 usb_cap4; + __le32 usb_cap5; + __le32 usb_cap6; + __le32 usb_cpkt1; + __le32 usb_cpkt2; + __le32 usb_cpkt3; + __le32 ep_dma_ext_addr; + __le32 buf_addr; + __le32 buf_data; + __le32 buf_ctrl; + __le32 dtrans; + __le32 tdl_from_trb; + __le32 tdl_beh; + __le32 ep_tdl; + __le32 tdl_beh2; + __le32 dma_adv_td; + __le32 reserved1[26]; + __le32 cfg_reg1; + __le32 dbg_link1; + __le32 dbg_link2; + __le32 cfg_regs[74]; + __le32 reserved2[51]; + __le32 dma_axi_ctrl; + __le32 dma_axi_id; + __le32 dma_axi_cap; + __le32 dma_axi_ctrl0; + __le32 dma_axi_ctrl1; +}; + +/* USB_CONF - bitmasks */ +/* Reset USB device configuration. */ +#define USB_CONF_CFGRST BIT(0) +/* Set Configuration. */ +#define USB_CONF_CFGSET BIT(1) +/* Disconnect USB device in SuperSpeed. */ +#define USB_CONF_USB3DIS BIT(3) +/* Disconnect USB device in HS/FS */ +#define USB_CONF_USB2DIS BIT(4) +/* Little Endian access - default */ +#define USB_CONF_LENDIAN BIT(5) +/* + * Big Endian access. Driver assume that byte order for + * SFRs access always is as Little Endian so this bit + * is not used. + */ +#define USB_CONF_BENDIAN BIT(6) +/* Device software reset. */ +#define USB_CONF_SWRST BIT(7) +/* Singular DMA transfer mode. Only for VER < DEV_VER_V3*/ +#define USB_CONF_DSING BIT(8) +/* Multiple DMA transfers mode. Only for VER < DEV_VER_V3 */ +#define USB_CONF_DMULT BIT(9) +/* DMA clock turn-off enable. */ +#define USB_CONF_DMAOFFEN BIT(10) +/* DMA clock turn-off disable. */ +#define USB_CONF_DMAOFFDS BIT(11) +/* Clear Force Full Speed. */ +#define USB_CONF_CFORCE_FS BIT(12) +/* Set Force Full Speed. */ +#define USB_CONF_SFORCE_FS BIT(13) +/* Device enable. */ +#define USB_CONF_DEVEN BIT(14) +/* Device disable. */ +#define USB_CONF_DEVDS BIT(15) +/* L1 LPM state entry enable (used in HS/FS mode). */ +#define USB_CONF_L1EN BIT(16) +/* L1 LPM state entry disable (used in HS/FS mode). */ +#define USB_CONF_L1DS BIT(17) +/* USB 2.0 clock gate disable. */ +#define USB_CONF_CLK2OFFEN BIT(18) +/* USB 2.0 clock gate enable. */ +#define USB_CONF_CLK2OFFDS BIT(19) +/* L0 LPM state entry request (used in HS/FS mode). */ +#define USB_CONF_LGO_L0 BIT(20) +/* USB 3.0 clock gate disable. */ +#define USB_CONF_CLK3OFFEN BIT(21) +/* USB 3.0 clock gate enable. */ +#define USB_CONF_CLK3OFFDS BIT(22) +/* Bit 23 is reserved*/ +/* U1 state entry enable (used in SS mode). */ +#define USB_CONF_U1EN BIT(24) +/* U1 state entry disable (used in SS mode). */ +#define USB_CONF_U1DS BIT(25) +/* U2 state entry enable (used in SS mode). */ +#define USB_CONF_U2EN BIT(26) +/* U2 state entry disable (used in SS mode). */ +#define USB_CONF_U2DS BIT(27) +/* U0 state entry request (used in SS mode). */ +#define USB_CONF_LGO_U0 BIT(28) +/* U1 state entry request (used in SS mode). */ +#define USB_CONF_LGO_U1 BIT(29) +/* U2 state entry request (used in SS mode). */ +#define USB_CONF_LGO_U2 BIT(30) +/* SS.Inactive state entry request (used in SS mode) */ +#define USB_CONF_LGO_SSINACT BIT(31) + +/* USB_STS - bitmasks */ +/* + * Configuration status. + * 1 - device is in the configured state. + * 0 - device is not configured. + */ +#define USB_STS_CFGSTS_MASK BIT(0) +#define USB_STS_CFGSTS(p) ((p) & USB_STS_CFGSTS_MASK) +/* + * On-chip memory overflow. + * 0 - On-chip memory status OK. + * 1 - On-chip memory overflow. + */ +#define USB_STS_OV_MASK BIT(1) +#define USB_STS_OV(p) ((p) & USB_STS_OV_MASK) +/* + * SuperSpeed connection status. + * 0 - USB in SuperSpeed mode disconnected. + * 1 - USB in SuperSpeed mode connected. + */ +#define USB_STS_USB3CONS_MASK BIT(2) +#define USB_STS_USB3CONS(p) ((p) & USB_STS_USB3CONS_MASK) +/* + * DMA transfer configuration status. + * 0 - single request. + * 1 - multiple TRB chain + * Supported only for controller version < DEV_VER_V3 + */ +#define USB_STS_DTRANS_MASK BIT(3) +#define USB_STS_DTRANS(p) ((p) & USB_STS_DTRANS_MASK) +/* + * Device speed. + * 0 - Undefined (value after reset). + * 1 - Low speed + * 2 - Full speed + * 3 - High speed + * 4 - Super speed + */ +#define USB_STS_USBSPEED_MASK GENMASK(6, 4) +#define USB_STS_USBSPEED(p) (((p) & USB_STS_USBSPEED_MASK) >> 4) +#define USB_STS_LS (0x1 << 4) +#define USB_STS_FS (0x2 << 4) +#define USB_STS_HS (0x3 << 4) +#define USB_STS_SS (0x4 << 4) +#define DEV_UNDEFSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4)) +#define DEV_LOWSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS) +#define DEV_FULLSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS) +#define DEV_HIGHSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS) +#define DEV_SUPERSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS) +/* + * Endianness for SFR access. + * 0 - Little Endian order (default after hardware reset). + * 1 - Big Endian order + */ +#define USB_STS_ENDIAN_MASK BIT(7) +#define USB_STS_ENDIAN(p) ((p) & USB_STS_ENDIAN_MASK) +/* + * HS/FS clock turn-off status. + * 0 - hsfs clock is always on. + * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled + * (default after hardware reset). + */ +#define USB_STS_CLK2OFF_MASK BIT(8) +#define USB_STS_CLK2OFF(p) ((p) & USB_STS_CLK2OFF_MASK) +/* + * PCLK clock turn-off status. + * 0 - pclk clock is always on. + * 1 - pclk clock turn-off in U3 (SS mode) is enabled + * (default after hardware reset). + */ +#define USB_STS_CLK3OFF_MASK BIT(9) +#define USB_STS_CLK3OFF(p) ((p) & USB_STS_CLK3OFF_MASK) +/* + * Controller in reset state. + * 0 - Internal reset is active. + * 1 - Internal reset is not active and controller is fully operational. + */ +#define USB_STS_IN_RST_MASK BIT(10) +#define USB_STS_IN_RST(p) ((p) & USB_STS_IN_RST_MASK) +/* + * Status of the "TDL calculation basing on TRB" feature. + * 0 - disabled + * 1 - enabled + * Supported only for DEV_VER_V2 controller version. + */ +#define USB_STS_TDL_TRB_ENABLED BIT(11) +/* + * Device enable Status. + * 0 - USB device is disabled (VBUS input is disconnected from internal logic). + * 1 - USB device is enabled (VBUS input is connected to the internal logic). + */ +#define USB_STS_DEVS_MASK BIT(14) +#define USB_STS_DEVS(p) ((p) & USB_STS_DEVS_MASK) +/* + * Address status. + * 0 - USB device is default state. + * 1 - USB device is at least in address state. + */ +#define USB_STS_ADDRESSED_MASK BIT(15) +#define USB_STS_ADDRESSED(p) ((p) & USB_STS_ADDRESSED_MASK) +/* + * L1 LPM state enable status (used in HS/FS mode). + * 0 - Entering to L1 LPM state disabled. + * 1 - Entering to L1 LPM state enabled. + */ +#define USB_STS_L1ENS_MASK BIT(16) +#define USB_STS_L1ENS(p) ((p) & USB_STS_L1ENS_MASK) +/* + * Internal VBUS connection status (used both in HS/FS and SS mode). + * 0 - internal VBUS is not detected. + * 1 - internal VBUS is detected. + */ +#define USB_STS_VBUSS_MASK BIT(17) +#define USB_STS_VBUSS(p) ((p) & USB_STS_VBUSS_MASK) +/* + * HS/FS LPM state (used in FS/HS mode). + * 0 - L0 State + * 1 - L1 State + * 2 - L2 State + * 3 - L3 State + */ +#define USB_STS_LPMST_MASK GENMASK(19, 18) +#define DEV_L0_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x0 << 18)) +#define DEV_L1_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x1 << 18)) +#define DEV_L2_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x2 << 18)) +#define DEV_L3_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x3 << 18)) +/* + * Disable HS status (used in FS/HS mode). + * 0 - the disconnect bit for HS/FS mode is set . + * 1 - the disconnect bit for HS/FS mode is not set. + */ +#define USB_STS_USB2CONS_MASK BIT(20) +#define USB_STS_USB2CONS(p) ((p) & USB_STS_USB2CONS_MASK) +/* + * HS/FS mode connection status (used in FS/HS mode). + * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled. + * 1 - High Speed operations in USB2.0 (FS/HS). + */ +#define USB_STS_DISABLE_HS_MASK BIT(21) +#define USB_STS_DISABLE_HS(p) ((p) & USB_STS_DISABLE_HS_MASK) +/* + * U1 state enable status (used in SS mode). + * 0 - Entering to U1 state disabled. + * 1 - Entering to U1 state enabled. + */ +#define USB_STS_U1ENS_MASK BIT(24) +#define USB_STS_U1ENS(p) ((p) & USB_STS_U1ENS_MASK) +/* + * U2 state enable status (used in SS mode). + * 0 - Entering to U2 state disabled. + * 1 - Entering to U2 state enabled. + */ +#define USB_STS_U2ENS_MASK BIT(25) +#define USB_STS_U2ENS(p) ((p) & USB_STS_U2ENS_MASK) +/* + * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current + * SuperSpeed link state + */ +#define USB_STS_LST_MASK GENMASK(29, 26) +#define DEV_LST_U0 (((p) & USB_STS_LST_MASK) == (0x0 << 26)) +#define DEV_LST_U1 (((p) & USB_STS_LST_MASK) == (0x1 << 26)) +#define DEV_LST_U2 (((p) & USB_STS_LST_MASK) == (0x2 << 26)) +#define DEV_LST_U3 (((p) & USB_STS_LST_MASK) == (0x3 << 26)) +#define DEV_LST_DISABLED (((p) & USB_STS_LST_MASK) == (0x4 << 26)) +#define DEV_LST_RXDETECT (((p) & USB_STS_LST_MASK) == (0x5 << 26)) +#define DEV_LST_INACTIVE (((p) & USB_STS_LST_MASK) == (0x6 << 26)) +#define DEV_LST_POLLING (((p) & USB_STS_LST_MASK) == (0x7 << 26)) +#define DEV_LST_RECOVERY (((p) & USB_STS_LST_MASK) == (0x8 << 26)) +#define DEV_LST_HOT_RESET (((p) & USB_STS_LST_MASK) == (0x9 << 26)) +#define DEV_LST_COMP_MODE (((p) & USB_STS_LST_MASK) == (0xa << 26)) +#define DEV_LST_LB_STATE (((p) & USB_STS_LST_MASK) == (0xb << 26)) +/* + * DMA clock turn-off status. + * 0 - DMA clock is always on (default after hardware reset). + * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled. + */ +#define USB_STS_DMAOFF_MASK BIT(30) +#define USB_STS_DMAOFF(p) ((p) & USB_STS_DMAOFF_MASK) +/* + * SFR Endian status. + * 0 - Little Endian order (default after hardware reset). + * 1 - Big Endian order. + */ +#define USB_STS_ENDIAN2_MASK BIT(31) +#define USB_STS_ENDIAN2(p) ((p) & USB_STS_ENDIAN2_MASK) + +/* USB_CMD - bitmasks */ +/* Set Function Address */ +#define USB_CMD_SET_ADDR BIT(0) +/* + * Function Address This field is saved to the device only when the field + * SET_ADDR is set '1 ' during write to USB_CMD register. + * Software is responsible for entering the address of the device during + * SET_ADDRESS request service. This field should be set immediately after + * the SETUP packet is decoded, and prior to confirmation of the status phase + */ +#define USB_CMD_FADDR_MASK GENMASK(7, 1) +#define USB_CMD_FADDR(p) (((p) << 1) & USB_CMD_FADDR_MASK) +/* Send Function Wake Device Notification TP (used only in SS mode). */ +#define USB_CMD_SDNFW BIT(8) +/* Set Test Mode (used only in HS/FS mode). */ +#define USB_CMD_STMODE BIT(9) +/* Test mode selector (used only in HS/FS mode) */ +#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10) +#define USB_STS_TMODE_SEL(p) (((p) << 10) & USB_STS_TMODE_SEL_MASK) +/* + * Send Latency Tolerance Message Device Notification TP (used only + * in SS mode). + */ +#define USB_CMD_SDNLTM BIT(12) +/* Send Custom Transaction Packet (used only in SS mode) */ +#define USB_CMD_SPKT BIT(13) +/*Device Notification 'Function Wake' - Interface value (only in SS mode. */ +#define USB_CMD_DNFW_INT_MASK GENMASK(23, 16) +#define USB_STS_DNFW_INT(p) (((p) << 16) & USB_CMD_DNFW_INT_MASK) +/* + * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0] + * (used only in SS mode). + */ +#define USB_CMD_DNLTM_BELT_MASK GENMASK(27, 16) +#define USB_STS_DNLTM_BELT(p) (((p) << 16) & USB_CMD_DNLTM_BELT_MASK) + +/* USB_ITPN - bitmasks */ +/* + * ITP(SS) / SOF (HS/FS) number + * In SS mode this field represent number of last ITP received from host. + * In HS/FS mode this field represent number of last SOF received from host. + */ +#define USB_ITPN_MASK GENMASK(13, 0) +#define USB_ITPN(p) ((p) & USB_ITPN_MASK) + +/* USB_LPM - bitmasks */ +/* Host Initiated Resume Duration. */ +#define USB_LPM_HIRD_MASK GENMASK(3, 0) +#define USB_LPM_HIRD(p) ((p) & USB_LPM_HIRD_MASK) +/* Remote Wakeup Enable (bRemoteWake). */ +#define USB_LPM_BRW BIT(4) + +/* USB_IEN - bitmasks */ +/* SS connection interrupt enable */ +#define USB_IEN_CONIEN BIT(0) +/* SS disconnection interrupt enable. */ +#define USB_IEN_DISIEN BIT(1) +/* USB SS warm reset interrupt enable. */ +#define USB_IEN_UWRESIEN BIT(2) +/* USB SS hot reset interrupt enable */ +#define USB_IEN_UHRESIEN BIT(3) +/* SS link U3 state enter interrupt enable (suspend).*/ +#define USB_IEN_U3ENTIEN BIT(4) +/* SS link U3 state exit interrupt enable (wakeup). */ +#define USB_IEN_U3EXTIEN BIT(5) +/* SS link U2 state enter interrupt enable.*/ +#define USB_IEN_U2ENTIEN BIT(6) +/* SS link U2 state exit interrupt enable.*/ +#define USB_IEN_U2EXTIEN BIT(7) +/* SS link U1 state enter interrupt enable.*/ +#define USB_IEN_U1ENTIEN BIT(8) +/* SS link U1 state exit interrupt enable.*/ +#define USB_IEN_U1EXTIEN BIT(9) +/* ITP/SOF packet detected interrupt enable.*/ +#define USB_IEN_ITPIEN BIT(10) +/* Wakeup interrupt enable.*/ +#define USB_IEN_WAKEIEN BIT(11) +/* Send Custom Packet interrupt enable.*/ +#define USB_IEN_SPKTIEN BIT(12) +/* HS/FS mode connection interrupt enable.*/ +#define USB_IEN_CON2IEN BIT(16) +/* HS/FS mode disconnection interrupt enable.*/ +#define USB_IEN_DIS2IEN BIT(17) +/* USB reset (HS/FS mode) interrupt enable.*/ +#define USB_IEN_U2RESIEN BIT(18) +/* LPM L2 state enter interrupt enable.*/ +#define USB_IEN_L2ENTIEN BIT(20) +/* LPM L2 state exit interrupt enable.*/ +#define USB_IEN_L2EXTIEN BIT(21) +/* LPM L1 state enter interrupt enable.*/ +#define USB_IEN_L1ENTIEN BIT(24) +/* LPM L1 state exit interrupt enable.*/ +#define USB_IEN_L1EXTIEN BIT(25) +/* Configuration reset interrupt enable.*/ +#define USB_IEN_CFGRESIEN BIT(26) +/* Start of the USB SS warm reset interrupt enable.*/ +#define USB_IEN_UWRESSIEN BIT(28) +/* End of the USB SS warm reset interrupt enable.*/ +#define USB_IEN_UWRESEIEN BIT(29) + +#define USB_IEN_INIT (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \ + | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \ + | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \ + | USB_IEN_L2EXTIEN | USB_IEN_L1ENTIEN | USB_IEN_U3ENTIEN) + +/* USB_ISTS - bitmasks */ +/* SS Connection detected. */ +#define USB_ISTS_CONI BIT(0) +/* SS Disconnection detected. */ +#define USB_ISTS_DISI BIT(1) +/* UUSB warm reset detectede. */ +#define USB_ISTS_UWRESI BIT(2) +/* USB hot reset detected. */ +#define USB_ISTS_UHRESI BIT(3) +/* U3 link state enter detected (suspend).*/ +#define USB_ISTS_U3ENTI BIT(4) +/* U3 link state exit detected (wakeup). */ +#define USB_ISTS_U3EXTI BIT(5) +/* U2 link state enter detected.*/ +#define USB_ISTS_U2ENTI BIT(6) +/* U2 link state exit detected.*/ +#define USB_ISTS_U2EXTI BIT(7) +/* U1 link state enter detected.*/ +#define USB_ISTS_U1ENTI BIT(8) +/* U1 link state exit detected.*/ +#define USB_ISTS_U1EXTI BIT(9) +/* ITP/SOF packet detected.*/ +#define USB_ISTS_ITPI BIT(10) +/* Wakeup detected.*/ +#define USB_ISTS_WAKEI BIT(11) +/* Send Custom Packet detected.*/ +#define USB_ISTS_SPKTI BIT(12) +/* HS/FS mode connection detected.*/ +#define USB_ISTS_CON2I BIT(16) +/* HS/FS mode disconnection detected.*/ +#define USB_ISTS_DIS2I BIT(17) +/* USB reset (HS/FS mode) detected.*/ +#define USB_ISTS_U2RESI BIT(18) +/* LPM L2 state enter detected.*/ +#define USB_ISTS_L2ENTI BIT(20) +/* LPM L2 state exit detected.*/ +#define USB_ISTS_L2EXTI BIT(21) +/* LPM L1 state enter detected.*/ +#define USB_ISTS_L1ENTI BIT(24) +/* LPM L1 state exit detected.*/ +#define USB_ISTS_L1EXTI BIT(25) +/* USB configuration reset detected.*/ +#define USB_ISTS_CFGRESI BIT(26) +/* Start of the USB warm reset detected.*/ +#define USB_ISTS_UWRESSI BIT(28) +/* End of the USB warm reset detected.*/ +#define USB_ISTS_UWRESEI BIT(29) + +/* USB_SEL - bitmasks */ +#define EP_SEL_EPNO_MASK GENMASK(3, 0) +/* Endpoint number. */ +#define EP_SEL_EPNO(p) ((p) & EP_SEL_EPNO_MASK) +/* Endpoint direction bit - 0 - OUT, 1 - IN. */ +#define EP_SEL_DIR BIT(7) + +#define select_ep_in(nr) (EP_SEL_EPNO(p) | EP_SEL_DIR) +#define select_ep_out (EP_SEL_EPNO(p)) + +/* EP_TRADDR - bitmasks */ +/* Transfer Ring address. */ +#define EP_TRADDR_TRADDR(p) ((p)) + +/* EP_CFG - bitmasks */ +/* Endpoint enable */ +#define EP_CFG_ENABLE BIT(0) +/* + * Endpoint type. + * 1 - isochronous + * 2 - bulk + * 3 - interrupt + */ +#define EP_CFG_EPTYPE_MASK GENMASK(2, 1) +#define EP_CFG_EPTYPE(p) (((p) << 1) & EP_CFG_EPTYPE_MASK) +/* Stream support enable (only in SS mode). */ +#define EP_CFG_STREAM_EN BIT(3) +/* TDL check (only in SS mode for BULK EP). */ +#define EP_CFG_TDL_CHK BIT(4) +/* SID check (only in SS mode for BULK OUT EP). */ +#define EP_CFG_SID_CHK BIT(5) +/* DMA transfer endianness. */ +#define EP_CFG_EPENDIAN BIT(7) +/* Max burst size (used only in SS mode). */ +#define EP_CFG_MAXBURST_MASK GENMASK(11, 8) +#define EP_CFG_MAXBURST(p) (((p) << 8) & EP_CFG_MAXBURST_MASK) +/* ISO max burst. */ +#define EP_CFG_MULT_MASK GENMASK(15, 14) +#define EP_CFG_MULT(p) (((p) << 14) & EP_CFG_MULT_MASK) +/* ISO max burst. */ +#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16) +#define EP_CFG_MAXPKTSIZE(p) (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK) +/* Max number of buffered packets. */ +#define EP_CFG_BUFFERING_MASK GENMASK(31, 27) +#define EP_CFG_BUFFERING(p) (((p) << 27) & EP_CFG_BUFFERING_MASK) + +/* EP_CMD - bitmasks */ +/* Endpoint reset. */ +#define EP_CMD_EPRST BIT(0) +/* Endpoint STALL set. */ +#define EP_CMD_SSTALL BIT(1) +/* Endpoint STALL clear. */ +#define EP_CMD_CSTALL BIT(2) +/* Send ERDY TP. */ +#define EP_CMD_ERDY BIT(3) +/* Request complete. */ +#define EP_CMD_REQ_CMPL BIT(5) +/* Transfer descriptor ready. */ +#define EP_CMD_DRDY BIT(6) +/* Data flush. */ +#define EP_CMD_DFLUSH BIT(7) +/* + * Transfer Descriptor Length write (used only for Bulk Stream capable + * endpoints in SS mode). + * Bit Removed from DEV_VER_V3 controller version. + */ +#define EP_CMD_STDL BIT(8) +/* + * Transfer Descriptor Length (used only in SS mode for bulk endpoints). + * Bits Removed from DEV_VER_V3 controller version. + */ +#define EP_CMD_TDL_MASK GENMASK(15, 9) +#define EP_CMD_TDL_SET(p) (((p) << 9) & EP_CMD_TDL_MASK) +#define EP_CMD_TDL_GET(p) (((p) & EP_CMD_TDL_MASK) >> 9) + +/* ERDY Stream ID value (used in SS mode). */ +#define EP_CMD_ERDY_SID_MASK GENMASK(31, 16) +#define EP_CMD_ERDY_SID(p) (((p) << 16) & EP_CMD_ERDY_SID_MASK) + +/* EP_STS - bitmasks */ +/* Setup transfer complete. */ +#define EP_STS_SETUP BIT(0) +/* Endpoint STALL status. */ +#define EP_STS_STALL(p) ((p) & BIT(1)) +/* Interrupt On Complete. */ +#define EP_STS_IOC BIT(2) +/* Interrupt on Short Packet. */ +#define EP_STS_ISP BIT(3) +/* Transfer descriptor missing. */ +#define EP_STS_DESCMIS BIT(4) +/* Stream Rejected (used only in SS mode) */ +#define EP_STS_STREAMR BIT(5) +/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */ +#define EP_STS_MD_EXIT BIT(6) +/* TRB error. */ +#define EP_STS_TRBERR BIT(7) +/* Not ready (used only in SS mode). */ +#define EP_STS_NRDY BIT(8) +/* DMA busy bit. */ +#define EP_STS_DBUSY BIT(9) +/* Endpoint Buffer Empty */ +#define EP_STS_BUFFEMPTY(p) ((p) & BIT(10)) +/* Current Cycle Status */ +#define EP_STS_CCS(p) ((p) & BIT(11)) +/* Prime (used only in SS mode. */ +#define EP_STS_PRIME BIT(12) +/* Stream error (used only in SS mode). */ +#define EP_STS_SIDERR BIT(13) +/* OUT size mismatch. */ +#define EP_STS_OUTSMM BIT(14) +/* ISO transmission error. */ +#define EP_STS_ISOERR BIT(15) +/* Host Packet Pending (only for SS mode). */ +#define EP_STS_HOSTPP(p) ((p) & BIT(16)) +/* Stream Protocol State Machine State (only for Bulk stream endpoints). */ +#define EP_STS_SPSMST_MASK GENMASK(18, 17) +#define EP_STS_SPSMST_DISABLED(p) (((p) & EP_STS_SPSMST_MASK) >> 17) +#define EP_STS_SPSMST_IDLE(p) (((p) & EP_STS_SPSMST_MASK) >> 17) +#define EP_STS_SPSMST_START_STREAM(p) (((p) & EP_STS_SPSMST_MASK) >> 17) +#define EP_STS_SPSMST_MOVE_DATA(p) (((p) & EP_STS_SPSMST_MASK) >> 17) +/* Interrupt On Transfer complete. */ +#define EP_STS_IOT BIT(19) +/* OUT queue endpoint number. */ +#define EP_STS_OUTQ_NO_MASK GENMASK(27, 24) +#define EP_STS_OUTQ_NO(p) (((p) & EP_STS_OUTQ_NO_MASK) >> 24) +/* OUT queue valid flag. */ +#define EP_STS_OUTQ_VAL_MASK BIT(28) +#define EP_STS_OUTQ_VAL(p) ((p) & EP_STS_OUTQ_VAL_MASK) +/* SETUP WAIT. */ +#define EP_STS_STPWAIT BIT(31) + +/* EP_STS_SID - bitmasks */ +/* Stream ID (used only in SS mode). */ +#define EP_STS_SID_MASK GENMASK(15, 0) +#define EP_STS_SID(p) ((p) & EP_STS_SID_MASK) + +/* EP_STS_EN - bitmasks */ +/* SETUP interrupt enable. */ +#define EP_STS_EN_SETUPEN BIT(0) +/* OUT transfer missing descriptor enable. */ +#define EP_STS_EN_DESCMISEN BIT(4) +/* Stream Rejected enable. */ +#define EP_STS_EN_STREAMREN BIT(5) +/* Move Data Exit enable.*/ +#define EP_STS_EN_MD_EXITEN BIT(6) +/* TRB enable. */ +#define EP_STS_EN_TRBERREN BIT(7) +/* NRDY enable. */ +#define EP_STS_EN_NRDYEN BIT(8) +/* Prime enable. */ +#define EP_STS_EN_PRIMEEEN BIT(12) +/* Stream error enable. */ +#define EP_STS_EN_SIDERREN BIT(13) +/* OUT size mismatch enable. */ +#define EP_STS_EN_OUTSMMEN BIT(14) +/* ISO transmission error enable. */ +#define EP_STS_EN_ISOERREN BIT(15) +/* Interrupt on Transmission complete enable. */ +#define EP_STS_EN_IOTEN BIT(19) +/* Setup Wait interrupt enable. */ +#define EP_STS_EN_STPWAITEN BIT(31) + +/* DRBL- bitmasks */ +#define DB_VALUE_BY_INDEX(index) (1 << (index)) +#define DB_VALUE_EP0_OUT BIT(0) +#define DB_VALUE_EP0_IN BIT(16) + +/* EP_IEN - bitmasks */ +#define EP_IEN(index) (1 << (index)) +#define EP_IEN_EP_OUT0 BIT(0) +#define EP_IEN_EP_IN0 BIT(16) + +/* EP_ISTS - bitmasks */ +#define EP_ISTS(index) (1 << (index)) +#define EP_ISTS_EP_OUT0 BIT(0) +#define EP_ISTS_EP_IN0 BIT(16) + +/* USB_PWR- bitmasks */ +/*Power Shut Off capability enable*/ +#define PUSB_PWR_PSO_EN BIT(0) +/*Power Shut Off capability disable*/ +#define PUSB_PWR_PSO_DS BIT(1) +/* + * Enables turning-off Reference Clock. + * This bit is optional and implemented only when support for OTG is + * implemented (indicated by OTG_READY bit set to '1'). + */ +#define PUSB_PWR_STB_CLK_SWITCH_EN BIT(8) +/* + * Status bit indicating that operation required by STB_CLK_SWITCH_EN write + * is completed + */ +#define PUSB_PWR_STB_CLK_SWITCH_DONE BIT(9) +/* This bit informs if Fast Registers Access is enabled. */ +#define PUSB_PWR_FST_REG_ACCESS_STAT BIT(30) +/* Fast Registers Access Enable. */ +#define PUSB_PWR_FST_REG_ACCESS BIT(31) + +/* USB_CONF2- bitmasks */ +/* + * Writing 1 disables TDL calculation basing on TRB feature in controller + * for DMULT mode. + * Bit supported only for DEV_VER_V2 version. + */ +#define USB_CONF2_DIS_TDL_TRB BIT(1) +/* + * Writing 1 enables TDL calculation basing on TRB feature in controller + * for DMULT mode. + * Bit supported only for DEV_VER_V2 version. + */ +#define USB_CONF2_EN_TDL_TRB BIT(2) + +/* USB_CAP1- bitmasks */ +/* + * SFR Interface type + * These field reflects type of SFR interface implemented: + * 0x0 - OCP + * 0x1 - AHB, + * 0x2 - PLB + * 0x3 - AXI + * 0x4-0xF - reserved + */ +#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0) +#define DEV_SFR_TYPE_OCP(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0) +#define DEV_SFR_TYPE_AHB(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1) +#define DEV_SFR_TYPE_PLB(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2) +#define DEV_SFR_TYPE_AXI(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3) +/* + * SFR Interface width + * These field reflects width of SFR interface implemented: + * 0x0 - 8 bit interface, + * 0x1 - 16 bit interface, + * 0x2 - 32 bit interface + * 0x3 - 64 bit interface + * 0x4-0xF - reserved + */ +#define USB_CAP1_SFR_WIDTH_MASK GENMASK(7, 4) +#define DEV_SFR_WIDTH_8(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4)) +#define DEV_SFR_WIDTH_16(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4)) +#define DEV_SFR_WIDTH_32(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4)) +#define DEV_SFR_WIDTH_64(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4)) +/* + * DMA Interface type + * These field reflects type of DMA interface implemented: + * 0x0 - OCP + * 0x1 - AHB, + * 0x2 - PLB + * 0x3 - AXI + * 0x4-0xF - reserved + */ +#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8) +#define DEV_DMA_TYPE_OCP(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8)) +#define DEV_DMA_TYPE_AHB(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8)) +#define DEV_DMA_TYPE_PLB(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8)) +#define DEV_DMA_TYPE_AXI(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8)) +/* + * DMA Interface width + * These field reflects width of DMA interface implemented: + * 0x0 - reserved, + * 0x1 - reserved, + * 0x2 - 32 bit interface + * 0x3 - 64 bit interface + * 0x4-0xF - reserved + */ +#define USB_CAP1_DMA_WIDTH_MASK GENMASK(15, 12) +#define DEV_DMA_WIDTH_32(p) (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12)) +#define DEV_DMA_WIDTH_64(p) (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12)) +/* + * USB3 PHY Interface type + * These field reflects type of USB3 PHY interface implemented: + * 0x0 - USB PIPE, + * 0x1 - RMMI, + * 0x2-0xF - reserved + */ +#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16) +#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16)) +#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16)) +/* + * USB3 PHY Interface width + * These field reflects width of USB3 PHY interface implemented: + * 0x0 - 8 bit PIPE interface, + * 0x1 - 16 bit PIPE interface, + * 0x2 - 32 bit PIPE interface, + * 0x3 - 64 bit PIPE interface + * 0x4-0xF - reserved + * Note: When SSIC interface is implemented this field shows the width of + * internal PIPE interface. The RMMI interface is always 20bit wide. + */ +#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20) +#define DEV_U3PHY_WIDTH_8(p) \ + (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20)) +#define DEV_U3PHY_WIDTH_16(p) \ + (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16)) +#define DEV_U3PHY_WIDTH_32(p) \ + (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20)) +#define DEV_U3PHY_WIDTH_64(p) \ + (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16)) + +/* + * USB2 PHY Interface enable + * These field informs if USB2 PHY interface is implemented: + * 0x0 - interface NOT implemented, + * 0x1 - interface implemented + */ +#define USB_CAP1_U2PHY_EN(p) ((p) & BIT(24)) +/* + * USB2 PHY Interface type + * These field reflects type of USB2 PHY interface implemented: + * 0x0 - UTMI, + * 0x1 - ULPI + */ +#define DEV_U2PHY_ULPI(p) ((p) & BIT(25)) +/* + * USB2 PHY Interface width + * These field reflects width of USB2 PHY interface implemented: + * 0x0 - 8 bit interface, + * 0x1 - 16 bit interface, + * Note: The ULPI interface is always 8bit wide. + */ +#define DEV_U2PHY_WIDTH_16(p) ((p) & BIT(26)) +/* + * OTG Ready + * 0x0 - pure device mode + * 0x1 - some features and ports for CDNS USB OTG controller are implemented. + */ +#define USB_CAP1_OTG_READY(p) ((p) & BIT(27)) + +/* + * When set, indicates that controller supports automatic internal TDL + * calculation basing on the size provided in TRB (TRB[22:17]) for DMULT mode + * Supported only for DEV_VER_V2 controller version. + */ +#define USB_CAP1_TDL_FROM_TRB(p) ((p) & BIT(28)) + +/* USB_CAP2- bitmasks */ +/* + * The actual size of the connected On-chip RAM memory in kB: + * - 0 means 256 kB (max supported mem size) + * - value other than 0 reflects the mem size in kB + */ +#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0)) +/* + * Max supported mem size + * These field reflects width of on-chip RAM address bus width, + * which determines max supported mem size: + * 0x0-0x7 - reserved, + * 0x8 - support for 4kB mem, + * 0x9 - support for 8kB mem, + * 0xA - support for 16kB mem, + * 0xB - support for 32kB mem, + * 0xC - support for 64kB mem, + * 0xD - support for 128kB mem, + * 0xE - support for 256kB mem, + * 0xF - reserved + */ +#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8)) + +/* USB_CAP3- bitmasks */ +#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index))) + +/* USB_CAP4- bitmasks */ +#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index))) + +/* USB_CAP5- bitmasks */ +#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index))) + +/* USB_CAP6- bitmasks */ +/* The USBSS-DEV Controller Internal build number. */ +#define GET_DEV_BASE_VERSION(p) ((p) & GENMASK(23, 0)) +/* The USBSS-DEV Controller version number. */ +#define GET_DEV_CUSTOM_VERSION(p) ((p) & GENMASK(31, 24)) + +#define DEV_VER_NXP_V1 0x00024502 +#define DEV_VER_TI_V1 0x00024509 +#define DEV_VER_V2 0x0002450C +#define DEV_VER_V3 0x0002450d + +/* DBG_LINK1- bitmasks */ +/* + * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum + * time required for decoding the received LFPS as an LFPS.U1_Exit. + */ +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p) ((p) & GENMASK(7, 0)) +/* + * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for + * phytxelecidle deassertion when LFPS.U1_Exit + */ +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK GENMASK(15, 8) +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p) (((p) << 8) & GENMASK(15, 8)) +/* + * RXDET_BREAK_DIS value This parameter configures terminating the Far-end + * Receiver termination detection sequence: + * 0: it is possible that USBSS_DEV will terminate Farend receiver + * termination detection sequence + * 1: USBSS_DEV will not terminate Far-end receiver termination + * detection sequence + */ +#define DBG_LINK1_RXDET_BREAK_DIS BIT(16) +/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */ +#define DBG_LINK1_LFPS_GEN_PING(p) (((p) << 17) & GENMASK(21, 17)) +/* + * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the + * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically + * cleared. Writing '0' has no effect + */ +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET BIT(24) +/* + * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the + * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically + * cleared. Writing '0' has no effect + */ +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET BIT(25) +/* + * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes + * the RXDET_BREAK_DIS field value to the device. This bit is automatically + * cleared. Writing '0' has no effect + */ +#define DBG_LINK1_RXDET_BREAK_DIS_SET BIT(26) +/* + * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes + * the LFPS_GEN_PING field value to the device. This bit is automatically + * cleared. Writing '0' has no effect." + */ +#define DBG_LINK1_LFPS_GEN_PING_SET BIT(27) + +/* DMA_AXI_CTRL- bitmasks */ +/* The mawprot pin configuration. */ +#define DMA_AXI_CTRL_MARPROT(p) ((p) & GENMASK(2, 0)) +/* The marprot pin configuration. */ +#define DMA_AXI_CTRL_MAWPROT(p) (((p) & GENMASK(2, 0)) << 16) +#define DMA_AXI_CTRL_NON_SECURE 0x02 + +#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget)) + +#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint)) + +/*-------------------------------------------------------------------------*/ +/* + * USBSS-DEV DMA interface. + */ +#define TRBS_PER_SEGMENT 40 + +#define ISO_MAX_INTERVAL 10 + +#if TRBS_PER_SEGMENT < 2 +#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2." +#endif + +/* + *Only for ISOC endpoints - maximum number of TRBs is calculated as + * pow(2, bInterval-1) * number of usb requests. It is limitation made by + * driver to save memory. Controller must prepare TRB for each ITP even + * if bInterval > 1. It's the reason why driver needs so many TRBs for + * isochronous endpoints. + */ +#define TRBS_PER_ISOC_SEGMENT (ISO_MAX_INTERVAL * 8) + +#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \ + TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT) +/** + * struct cdns3_trb - represent Transfer Descriptor block. + * @buffer: pointer to buffer data + * @length: length of data + * @control: control flags. + * + * This structure describes transfer block serviced by DMA module. + */ +struct cdns3_trb { + __le32 buffer; + __le32 length; + __le32 control; +}; + +#define TRB_SIZE (sizeof(struct cdns3_trb)) +#define TRB_RING_SIZE (TRB_SIZE * TRBS_PER_SEGMENT) +#define TRB_ISO_RING_SIZE (TRB_SIZE * TRBS_PER_ISOC_SEGMENT) +#define TRB_CTRL_RING_SIZE (TRB_SIZE * 2) + +/* TRB bit mask */ +#define TRB_TYPE_BITMASK GENMASK(15, 10) +#define TRB_TYPE(p) ((p) << 10) +#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10) + +/* TRB type IDs */ +/* bulk, interrupt, isoc , and control data stage */ +#define TRB_NORMAL 1 +/* TRB for linking ring segments */ +#define TRB_LINK 6 + +/* Cycle bit - indicates TRB ownership by driver or hw*/ +#define TRB_CYCLE BIT(0) +/* + * When set to '1', the device will toggle its interpretation of the Cycle bit + */ +#define TRB_TOGGLE BIT(1) + +/* + * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was + * processed while USB short packet was received. No more buffers defined by + * the TD will be used. DMA will automatically advance to next TD. + * - Shall be set to 0 by Software when putting TRB on the Transfer Ring + * - Shall be set to 1 by Controller when Short Packet condition for this TRB + * is detected independent if ISP is set or not. + */ +#define TRB_SP BIT(1) + +/* Interrupt on short packet*/ +#define TRB_ISP BIT(2) +/*Setting this bit enables FIFO DMA operation mode*/ +#define TRB_FIFO_MODE BIT(3) +/* Set PCIe no snoop attribute */ +#define TRB_CHAIN BIT(4) +/* Interrupt on completion */ +#define TRB_IOC BIT(5) + +/* stream ID bitmasks. */ +#define TRB_STREAM_ID_BITMASK GENMASK(31, 16) +#define TRB_STREAM_ID(p) ((p) << 16) +#define TRB_FIELD_TO_STREAMID(p) (((p) & TRB_STREAM_ID_BITMASK) >> 16) + +/* Size of TD expressed in USB packets for HS/FS mode. */ +#define TRB_TDL_HS_SIZE(p) (((p) << 16) & GENMASK(31, 16)) +#define TRB_TDL_HS_SIZE_GET(p) (((p) & GENMASK(31, 16)) >> 16) + +/* transfer_len bitmasks. */ +#define TRB_LEN(p) ((p) & GENMASK(16, 0)) + +/* Size of TD expressed in USB packets for SS mode. */ +#define TRB_TDL_SS_SIZE(p) (((p) << 17) & GENMASK(23, 17)) +#define TRB_TDL_SS_SIZE_GET(p) (((p) & GENMASK(23, 17)) >> 17) + +/* transfer_len bitmasks - bits 31:24 */ +#define TRB_BURST_LEN(p) (((p) << 24) & GENMASK(31, 24)) +#define TRB_BURST_LEN_GET(p) (((p) & GENMASK(31, 24)) >> 24) + +/* Data buffer pointer bitmasks*/ +#define TRB_BUFFER(p) ((p) & GENMASK(31, 0)) + +/*-------------------------------------------------------------------------*/ +/* Driver numeric constants */ + +/* Such declaration should be added to ch9.h */ +#define USB_DEVICE_MAX_ADDRESS 127 + +/* Endpoint init values */ +#define CDNS3_EP_MAX_PACKET_LIMIT 1024 +#define CDNS3_EP_MAX_STREAMS 15 +#define CDNS3_EP0_MAX_PACKET_LIMIT 512 + +/* All endpoints including EP0 */ +#define CDNS3_ENDPOINTS_MAX_COUNT 32 +#define CDNS3_EP_ZLP_BUF_SIZE 1024 + +#define CDNS3_EP_BUF_SIZE 2 /* KB */ +#define CDNS3_EP_ISO_HS_MULT 3 +#define CDNS3_EP_ISO_SS_BURST 3 +#define CDNS3_MAX_NUM_DESCMISS_BUF 32 +#define CDNS3_DESCMIS_BUF_SIZE 2048 /* Bytes */ +/*-------------------------------------------------------------------------*/ +/* Used structs */ + +struct cdns3_device; + +/** + * struct cdns3_endpoint - extended device side representation of USB endpoint. + * @endpoint: usb endpoint + * @pending_req_list: list of requests queuing on transfer ring. + * @deferred_req_list: list of requests waiting for queuing on transfer ring. + * @trb_pool: transfer ring - array of transaction buffers + * @trb_pool_dma: dma address of transfer ring + * @cdns3_dev: device associated with this endpoint + * @name: a human readable name e.g. ep1out + * @flags: specify the current state of endpoint + * @dir: endpoint direction + * @num: endpoint number (1 - 15) + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK + * @interval: interval between packets used for ISOC endpoint. + * @free_trbs: number of free TRBs in transfer ring + * @num_trbs: number of all TRBs in transfer ring + * @pcs: producer cycle state + * @ccs: consumer cycle state + * @enqueue: enqueue index in transfer ring + * @dequeue: dequeue index in transfer ring + * @trb_burst_size: number of burst used in trb. + */ +struct cdns3_endpoint { + struct usb_ep endpoint; + struct list_head pending_req_list; + struct list_head deferred_req_list; + + struct cdns3_trb *trb_pool; + dma_addr_t trb_pool_dma; + + struct cdns3_device *cdns3_dev; + char name[20]; + +#define EP_ENABLED BIT(0) +#define EP_STALLED BIT(1) +#define EP_STALL_PENDING BIT(2) +#define EP_WEDGE BIT(3) +#define EP_TRANSFER_STARTED BIT(4) +#define EP_UPDATE_EP_TRBADDR BIT(5) +#define EP_PENDING_REQUEST BIT(6) +#define EP_RING_FULL BIT(7) +#define EP_CLAIMED BIT(8) +#define EP_DEFERRED_DRDY BIT(9) +#define EP_QUIRK_ISO_OUT_EN BIT(10) + u32 flags; + + u8 dir; + u8 num; + u8 type; + int interval; + + int free_trbs; + int num_trbs; + u8 pcs; + u8 ccs; + int enqueue; + int dequeue; + u8 trb_burst_size; + + unsigned int wa1_set:1; + struct cdns3_trb *wa1_trb; + unsigned int wa1_trb_index; + unsigned int wa1_cycle_bit:1; +}; + +/** + * struct cdns3_aligned_buf - represent aligned buffer used for DMA transfer + * @buf: aligned to 8 bytes data buffer. Buffer address used in + * TRB shall be aligned to 8. + * @dma: dma address + * @size: size of buffer + * @in_use: inform if this buffer is associated with usb_request + * @list: used to adding instance of this object to list + */ +struct cdns3_aligned_buf { + void *buf; + dma_addr_t dma; + u32 size; + int in_use:1; + struct list_head list; +}; + +/** + * struct cdns3_request - extended device side representation of usb_request + * object . + * @request: generic usb_request object describing single I/O request. + * @priv_ep: extended representation of usb_ep object + * @trb: the first TRB association with this request + * @start_trb: number of the first TRB in transfer ring + * @end_trb: number of the last TRB in transfer ring + * @aligned_buf: object holds information about aligned buffer associated whit + * this endpoint + * @flags: flag specifying special usage of request + */ +struct cdns3_request { + struct usb_request request; + struct cdns3_endpoint *priv_ep; + struct cdns3_trb *trb; + int start_trb; + int end_trb; + struct cdns3_aligned_buf *aligned_buf; +#define REQUEST_PENDING BIT(0) +#define REQUEST_INTERNAL BIT(1) +#define REQUEST_INTERNAL_CH BIT(2) +#define REQUEST_ZLP BIT(3) +#define REQUEST_UNALIGNED BIT(4) + u32 flags; + struct list_head list; +}; + +#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request)) + +/*Stages used during enumeration process.*/ +#define CDNS3_SETUP_STAGE 0x0 +#define CDNS3_DATA_STAGE 0x1 +#define CDNS3_STATUS_STAGE 0x2 + +/** + * struct cdns3_device - represent USB device. + * @dev: pointer to device structure associated whit this controller + * @sysdev: pointer to the DMA capable device + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @dev_ver: device controller version. + * @lock: for synchronizing + * @regs: base address for device side registers + * @setup_buf: used while processing usb control requests + * @setup_dma: dma address for setup_buf + * @zlp_buf - zlp buffer + * @ep0_stage: ep0 stage during enumeration process. + * @ep0_data_dir: direction for control transfer + * @eps: array of pointers to all endpoints with exclusion ep0 + * @aligned_buf_list: list of aligned buffers internally allocated by driver + * @aligned_buf_wq: workqueue freeing no longer used aligned buf. + * @selected_ep: actually selected endpoint. It's used only to improve + * performance. + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP. + * @u1_allowed: allow device transition to u1 state + * @u2_allowed: allow device transition to u2 state + * @is_selfpowered: device is self powered + * @setup_pending: setup packet is processing by gadget driver + * @hw_configured_flag: hardware endpoint configuration was set. + * @wake_up_flag: allow device to remote up the host + * @status_completion_no_call: indicate that driver is waiting for status s + * stage completion. It's used in deferred SET_CONFIGURATION request. + * @onchip_buffers: number of available on-chip buffers. + * @onchip_used_size: actual size of on-chip memory assigned to endpoints. + * @pending_status_wq: workqueue handling status stage for deferred requests. + * @pending_status_request: request for which status stage was deferred + */ +struct cdns3_device { + struct device *dev; + struct device *sysdev; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + +#define CDNS_REVISION_V0 0x00024501 +#define CDNS_REVISION_V1 0x00024509 + u32 dev_ver; + + /* generic spin-lock for drivers */ + spinlock_t lock; + + struct cdns3_usb_regs __iomem *regs; + + struct usb_ctrlrequest *setup_buf; + dma_addr_t setup_dma; + void *zlp_buf; + + u8 ep0_stage; + int ep0_data_dir; + + struct cdns3_endpoint *eps[CDNS3_ENDPOINTS_MAX_COUNT]; + + struct list_head aligned_buf_list; + struct work_struct aligned_buf_wq; + + u32 selected_ep; + u16 isoch_delay; + + unsigned wait_for_setup:1; + unsigned u1_allowed:1; + unsigned u2_allowed:1; + unsigned is_selfpowered:1; + unsigned setup_pending:1; + int hw_configured_flag:1; + int wake_up_flag:1; + unsigned status_completion_no_call:1; + int out_mem_is_allocated; + + struct work_struct pending_status_wq; + struct usb_request *pending_status_request; + + /*in KB */ + u16 onchip_buffers; + u16 onchip_used_size; +}; + +void cdns3_set_register_bit(void __iomem *ptr, u32 mask); +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep, + struct cdns3_trb *trb); +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev); +void cdns3_pending_setup_status_handler(struct work_struct *work); +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev); +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev); +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep); +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable); +struct usb_request *cdns3_next_request(struct list_head *list); +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, + struct usb_request *request); +void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm); +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep); +u8 cdns3_ep_addr_to_index(u8 ep_addr); +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep); +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value); +void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep); +int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep); +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags); +void cdns3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request); +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request); +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, + struct cdns3_request *priv_req, + int status); + +int cdns3_init_ep0(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep); +void cdns3_ep0_config(struct cdns3_device *priv_dev); +void cdns3_ep_config(struct cdns3_endpoint *priv_ep); +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); +int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev); + +#endif /* __LINUX_CDNS3_GADGET */ diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h new file mode 100644 index 000000000000..b498a170b7e8 --- /dev/null +++ b/drivers/usb/cdns3/host-export.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cadence USBSS DRD Driver - Host Export APIs + * + * Copyright (C) 2017-2018 NXP + * + * Authors: Peter Chen + */ +#ifndef __LINUX_CDNS3_HOST_EXPORT +#define __LINUX_CDNS3_HOST_EXPORT + +#ifdef CONFIG_USB_CDNS3_HOST + +int cdns3_host_init(struct cdns3 *cdns); +void cdns3_host_exit(struct cdns3 *cdns); + +#else + +static inline int cdns3_host_init(struct cdns3 *cdns) +{ + return -ENXIO; +} + +static inline void cdns3_host_exit(struct cdns3 *cdns) { } + +#endif /* CONFIG_USB_CDNS3_HOST */ + +#endif /* __LINUX_CDNS3_HOST_EXPORT */ diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c new file mode 100644 index 000000000000..2733a8f71fcd --- /dev/null +++ b/drivers/usb/cdns3/host.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBSS DRD Driver - host side + * + * Copyright (C) 2018-2019 Cadence Design Systems. + * Copyright (C) 2017-2018 NXP + * + * Authors: Peter Chen + * Pawel Laszczak + */ + +#include +#include "core.h" +#include "drd.h" + +static int __cdns3_host_init(struct cdns3 *cdns) +{ + struct platform_device *xhci; + int ret; + + cdns3_drd_switch_host(cdns, 1); + + xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); + if (!xhci) { + dev_err(cdns->dev, "couldn't allocate xHCI device\n"); + return -ENOMEM; + } + + xhci->dev.parent = cdns->dev; + cdns->host_dev = xhci; + + ret = platform_device_add_resources(xhci, cdns->xhci_res, + CDNS3_XHCI_RESOURCES_NUM); + if (ret) { + dev_err(cdns->dev, "couldn't add resources to xHCI device\n"); + goto err1; + } + + ret = platform_device_add(xhci); + if (ret) { + dev_err(cdns->dev, "failed to register xHCI device\n"); + goto err1; + } + + return 0; +err1: + platform_device_put(xhci); + return ret; +} + +static void cdns3_host_exit(struct cdns3 *cdns) +{ + platform_device_unregister(cdns->host_dev); + cdns->host_dev = NULL; + cdns3_drd_switch_host(cdns, 0); +} + +int cdns3_host_init(struct cdns3 *cdns) +{ + struct cdns3_role_driver *rdrv; + + rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); + if (!rdrv) + return -ENOMEM; + + rdrv->start = __cdns3_host_init; + rdrv->stop = cdns3_host_exit; + rdrv->state = CDNS3_ROLE_STATE_INACTIVE; + rdrv->name = "host"; + + cdns->roles[USB_ROLE_HOST] = rdrv; + + return 0; +} diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c new file mode 100644 index 000000000000..459fa72d9c74 --- /dev/null +++ b/drivers/usb/cdns3/trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USBSS device controller driver Trace Support + * + * Copyright (C) 2018-2019 Cadence. + * + * Author: Pawel Laszczak + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h new file mode 100644 index 000000000000..e92348c9b4d7 --- /dev/null +++ b/drivers/usb/cdns3/trace.h @@ -0,0 +1,493 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * USBSS device controller driver. + * Trace support header file. + * + * Copyright (C) 2018-2019 Cadence. + * + * Author: Pawel Laszczak + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cdns3 + +#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __LINUX_CDNS3_TRACE + +#include +#include +#include +#include +#include "core.h" +#include "gadget.h" +#include "debug.h" + +#define CDNS3_MSG_MAX 500 + +TRACE_EVENT(cdns3_halt, + TP_PROTO(struct cdns3_endpoint *ep_priv, u8 halt, u8 flush), + TP_ARGS(ep_priv, halt, flush), + TP_STRUCT__entry( + __string(name, ep_priv->name) + __field(u8, halt) + __field(u8, flush) + ), + TP_fast_assign( + __assign_str(name, ep_priv->name); + __entry->halt = halt; + __entry->flush = flush; + ), + TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "", + __get_str(name), __entry->halt ? "set" : "cleared") +); + +TRACE_EVENT(cdns3_wa1, + TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg), + TP_ARGS(ep_priv, msg), + TP_STRUCT__entry( + __string(ep_name, ep_priv->name) + __string(msg, msg) + ), + TP_fast_assign( + __assign_str(ep_name, ep_priv->name); + __assign_str(msg, msg); + ), + TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg)) +); + +TRACE_EVENT(cdns3_wa2, + TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg), + TP_ARGS(ep_priv, msg), + TP_STRUCT__entry( + __string(ep_name, ep_priv->name) + __string(msg, msg) + ), + TP_fast_assign( + __assign_str(ep_name, ep_priv->name); + __assign_str(msg, msg); + ), + TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg)) +); + +DECLARE_EVENT_CLASS(cdns3_log_doorbell, + TP_PROTO(const char *ep_name, u32 ep_trbaddr), + TP_ARGS(ep_name, ep_trbaddr), + TP_STRUCT__entry( + __string(name, ep_name) + __field(u32, ep_trbaddr) + ), + TP_fast_assign( + __assign_str(name, ep_name); + __entry->ep_trbaddr = ep_trbaddr; + ), + TP_printk("%s, ep_trbaddr %08x", __get_str(name), + __entry->ep_trbaddr) +); + +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0, + TP_PROTO(const char *ep_name, u32 ep_trbaddr), + TP_ARGS(ep_name, ep_trbaddr) +); + +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx, + TP_PROTO(const char *ep_name, u32 ep_trbaddr), + TP_ARGS(ep_name, ep_trbaddr) +); + +DECLARE_EVENT_CLASS(cdns3_log_usb_irq, + TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists), + TP_ARGS(priv_dev, usb_ists), + TP_STRUCT__entry( + __field(enum usb_device_speed, speed) + __field(u32, usb_ists) + __dynamic_array(char, str, CDNS3_MSG_MAX) + ), + TP_fast_assign( + __entry->speed = cdns3_get_speed(priv_dev); + __entry->usb_ists = usb_ists; + ), + TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed, + __entry->usb_ists)) +); + +DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq, + TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists), + TP_ARGS(priv_dev, usb_ists) +); + +DECLARE_EVENT_CLASS(cdns3_log_epx_irq, + TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_dev, priv_ep), + TP_STRUCT__entry( + __string(ep_name, priv_ep->name) + __field(u32, ep_sts) + __field(u32, ep_traddr) + __dynamic_array(char, str, CDNS3_MSG_MAX) + ), + TP_fast_assign( + __assign_str(ep_name, priv_ep->name); + __entry->ep_sts = readl(&priv_dev->regs->ep_sts); + __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr); + ), + TP_printk("%s, ep_traddr: %08x", + cdns3_decode_epx_irq(__get_str(str), + __get_str(ep_name), + __entry->ep_sts), + __entry->ep_traddr) +); + +DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq, + TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_dev, priv_ep) +); + +DECLARE_EVENT_CLASS(cdns3_log_ep0_irq, + TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts), + TP_ARGS(priv_dev, ep_sts), + TP_STRUCT__entry( + __field(int, ep_dir) + __field(u32, ep_sts) + __dynamic_array(char, str, CDNS3_MSG_MAX) + ), + TP_fast_assign( + __entry->ep_dir = priv_dev->ep0_data_dir; + __entry->ep_sts = ep_sts; + ), + TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str), + __entry->ep_dir, + __entry->ep_sts)) +); + +DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq, + TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts), + TP_ARGS(priv_dev, ep_sts) +); + +DECLARE_EVENT_CLASS(cdns3_log_ctrl, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl), + TP_STRUCT__entry( + __field(u8, bRequestType) + __field(u8, bRequest) + __field(u16, wValue) + __field(u16, wIndex) + __field(u16, wLength) + __dynamic_array(char, str, CDNS3_MSG_MAX) + ), + TP_fast_assign( + __entry->bRequestType = ctrl->bRequestType; + __entry->bRequest = ctrl->bRequest; + __entry->wValue = le16_to_cpu(ctrl->wValue); + __entry->wIndex = le16_to_cpu(ctrl->wIndex); + __entry->wLength = le16_to_cpu(ctrl->wLength); + ), + TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNS3_MSG_MAX, + __entry->bRequestType, + __entry->bRequest, __entry->wValue, + __entry->wIndex, __entry->wLength) + ) +); + +DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl) +); + +DECLARE_EVENT_CLASS(cdns3_log_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req), + TP_STRUCT__entry( + __string(name, req->priv_ep->name) + __field(struct cdns3_request *, req) + __field(void *, buf) + __field(unsigned int, actual) + __field(unsigned int, length) + __field(int, status) + __field(int, zero) + __field(int, short_not_ok) + __field(int, no_interrupt) + __field(int, start_trb) + __field(int, end_trb) + __field(struct cdns3_trb *, start_trb_addr) + __field(int, flags) + ), + TP_fast_assign( + __assign_str(name, req->priv_ep->name); + __entry->req = req; + __entry->buf = req->request.buf; + __entry->actual = req->request.actual; + __entry->length = req->request.length; + __entry->status = req->request.status; + __entry->zero = req->request.zero; + __entry->short_not_ok = req->request.short_not_ok; + __entry->no_interrupt = req->request.no_interrupt; + __entry->start_trb = req->start_trb; + __entry->end_trb = req->end_trb; + __entry->start_trb_addr = req->trb; + __entry->flags = req->flags; + ), + TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d," + " trb: [start:%d, end:%d: virt addr %pa], flags:%x ", + __get_str(name), __entry->req, __entry->buf, __entry->actual, + __entry->length, + __entry->zero ? "Z" : "z", + __entry->short_not_ok ? "S" : "s", + __entry->no_interrupt ? "I" : "i", + __entry->status, + __entry->start_trb, + __entry->end_trb, + __entry->start_trb_addr, + __entry->flags + ) +); + +DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(cdns3_log_request, cdns3_free_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +TRACE_EVENT(cdns3_ep0_queue, + TP_PROTO(struct cdns3_device *dev_priv, struct usb_request *request), + TP_ARGS(dev_priv, request), + TP_STRUCT__entry( + __field(int, dir) + __field(int, length) + ), + TP_fast_assign( + __entry->dir = dev_priv->ep0_data_dir; + __entry->length = request->length; + ), + TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out", + __entry->length) +); + +DECLARE_EVENT_CLASS(cdns3_log_aligned_request, + TP_PROTO(struct cdns3_request *priv_req), + TP_ARGS(priv_req), + TP_STRUCT__entry( + __string(name, priv_req->priv_ep->name) + __field(struct usb_request *, req) + __field(void *, buf) + __field(dma_addr_t, dma) + __field(void *, aligned_buf) + __field(dma_addr_t, aligned_dma) + __field(u32, aligned_buf_size) + ), + TP_fast_assign( + __assign_str(name, priv_req->priv_ep->name); + __entry->req = &priv_req->request; + __entry->buf = priv_req->request.buf; + __entry->dma = priv_req->request.dma; + __entry->aligned_buf = priv_req->aligned_buf->buf; + __entry->aligned_dma = priv_req->aligned_buf->dma; + __entry->aligned_buf_size = priv_req->aligned_buf->size; + ), + TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d", + __get_str(name), __entry->req, __entry->buf, &__entry->dma, + __entry->aligned_buf, &__entry->aligned_dma, + __entry->aligned_buf_size + ) +); + +DEFINE_EVENT(cdns3_log_aligned_request, cdns3_free_aligned_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DECLARE_EVENT_CLASS(cdns3_log_trb, + TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb), + TP_ARGS(priv_ep, trb), + TP_STRUCT__entry( + __string(name, priv_ep->name) + __field(struct cdns3_trb *, trb) + __field(u32, buffer) + __field(u32, length) + __field(u32, control) + __field(u32, type) + ), + TP_fast_assign( + __assign_str(name, priv_ep->name); + __entry->trb = trb; + __entry->buffer = trb->buffer; + __entry->length = trb->length; + __entry->control = trb->control; + __entry->type = usb_endpoint_type(priv_ep->endpoint.desc); + ), + TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)", + __get_str(name), __entry->trb, __entry->buffer, + TRB_LEN(__entry->length), + (u8)TRB_BURST_LEN_GET(__entry->length), + __entry->control, + __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ", + __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ", + __entry->control & TRB_ISP ? "ISP, " : "", + __entry->control & TRB_FIFO_MODE ? "FIFO, " : "", + __entry->control & TRB_CHAIN ? "CHAIN, " : "", + __entry->control & TRB_IOC ? "IOC, " : "", + TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK" + ) +); + +DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb, + TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb), + TP_ARGS(priv_ep, trb) +); + +DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb, + TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb), + TP_ARGS(priv_ep, trb) +); + +DECLARE_EVENT_CLASS(cdns3_log_ring, + TP_PROTO(struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_ep), + TP_STRUCT__entry( + __dynamic_array(u8, ring, TRB_RING_SIZE) + __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint)) + __dynamic_array(char, buffer, + (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX) + ), + TP_fast_assign( + memcpy(__get_dynamic_array(priv_ep), priv_ep, + sizeof(struct cdns3_endpoint)); + memcpy(__get_dynamic_array(ring), priv_ep->trb_pool, + TRB_RING_SIZE); + ), + + TP_printk("%s", + cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep), + (struct cdns3_trb *)__get_str(ring), + __get_str(buffer))) +); + +DEFINE_EVENT(cdns3_log_ring, cdns3_ring, + TP_PROTO(struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_ep) +); + +DECLARE_EVENT_CLASS(cdns3_log_ep, + TP_PROTO(struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_ep), + TP_STRUCT__entry( + __string(name, priv_ep->name) + __field(unsigned int, maxpacket) + __field(unsigned int, maxpacket_limit) + __field(unsigned int, max_streams) + __field(unsigned int, maxburst) + __field(unsigned int, flags) + __field(unsigned int, dir) + __field(u8, enqueue) + __field(u8, dequeue) + ), + TP_fast_assign( + __assign_str(name, priv_ep->name); + __entry->maxpacket = priv_ep->endpoint.maxpacket; + __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit; + __entry->max_streams = priv_ep->endpoint.max_streams; + __entry->maxburst = priv_ep->endpoint.maxburst; + __entry->flags = priv_ep->flags; + __entry->dir = priv_ep->dir; + __entry->enqueue = priv_ep->enqueue; + __entry->dequeue = priv_ep->dequeue; + ), + TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, " + "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s", + __get_str(name), __entry->maxpacket, + __entry->maxpacket_limit, __entry->max_streams, + __entry->maxburst, __entry->enqueue, + __entry->dequeue, + __entry->flags & EP_ENABLED ? "EN | " : "", + __entry->flags & EP_STALLED ? "STALLED | " : "", + __entry->flags & EP_WEDGE ? "WEDGE | " : "", + __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "", + __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "", + __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "", + __entry->flags & EP_RING_FULL ? "RING FULL |" : "", + __entry->flags & EP_CLAIMED ? "CLAIMED " : "", + __entry->dir ? "IN" : "OUT" + ) +); + +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable, + TP_PROTO(struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_ep) +); + +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable, + TP_PROTO(struct cdns3_endpoint *priv_ep), + TP_ARGS(priv_ep) +); + +DECLARE_EVENT_CLASS(cdns3_log_request_handled, + TP_PROTO(struct cdns3_request *priv_req, int current_index, + int handled), + TP_ARGS(priv_req, current_index, handled), + TP_STRUCT__entry( + __field(struct cdns3_request *, priv_req) + __field(unsigned int, dma_position) + __field(unsigned int, handled) + __field(unsigned int, dequeue_idx) + __field(unsigned int, enqueue_idx) + __field(unsigned int, start_trb) + __field(unsigned int, end_trb) + ), + TP_fast_assign( + __entry->priv_req = priv_req; + __entry->dma_position = current_index; + __entry->handled = handled; + __entry->dequeue_idx = priv_req->priv_ep->dequeue; + __entry->enqueue_idx = priv_req->priv_ep->enqueue; + __entry->start_trb = priv_req->start_trb; + __entry->end_trb = priv_req->end_trb; + ), + TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d," + " start trb: %d, end trb: %d", + __entry->priv_req, + __entry->handled ? "handled" : "not handled", + __entry->dma_position, __entry->dequeue_idx, + __entry->enqueue_idx, __entry->start_trb, + __entry->end_trb + ) +); + +DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled, + TP_PROTO(struct cdns3_request *priv_req, int current_index, + int handled), + TP_ARGS(priv_req, current_index, handled) +); +#endif /* __LINUX_CDNS3_TRACE */ + +/* this part must be outside header guard */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include From 6bbf87a131edd39040974f3843ff97e89310ebde Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 26 Aug 2019 12:19:31 +0100 Subject: [PATCH 115/159] usb:cdns3 Fix for stuck packets in on-chip OUT buffer. Controller for OUT endpoints has shared on-chip buffers for all incoming packets, including ep0out. It's FIFO buffer, so packets must be handled by DMA in correct order. If the first packet in the buffer will not be handled, then the following packets directed for other endpoints and functions will be blocked. Additionally the packets directed to one endpoint can block entire on-chip buffers. In this case transfer to other endpoints also will blocked. To resolve this issue after raising the descriptor missing interrupt driver prepares internal usb_request object and use it to arm DMA transfer. The problematic situation was observed in case when endpoint has been enabled but no usb_request were queued. Driver try detects such endpoints and will use this workaround only for these endpoint. Driver use limited number of buffer. This number can be set by macro CDNS_WA2_NUM_BUFFERS. Such blocking situation was observed on ACM gadget. For this function host send OUT data packet but ACM function is not prepared for this packet. It's cause that buffer placed in on chip memory block transfer to other endpoints. Issue has been fixed for DEV_VER_V2 version of controller. Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 339 ++++++++++++++++++++++++++++++++++++- drivers/usb/cdns3/gadget.h | 13 ++ 2 files changed, 350 insertions(+), 2 deletions(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 49ba253d81be..3094ad65ffc9 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -28,6 +28,32 @@ * * Issue has been fixed in DEV_VER_V3 version of controller. * + * Work around 2: + * Controller for OUT endpoints has shared on-chip buffers for all incoming + * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA + * in correct order. If the first packet in the buffer will not be handled, + * then the following packets directed for other endpoints and functions + * will be blocked. + * Additionally the packets directed to one endpoint can block entire on-chip + * buffers. In this case transfer to other endpoints also will blocked. + * + * To resolve this issue after raising the descriptor missing interrupt + * driver prepares internal usb_request object and use it to arm DMA transfer. + * + * The problematic situation was observed in case when endpoint has been enabled + * but no usb_request were queued. Driver try detects such endpoints and will + * use this workaround only for these endpoint. + * + * Driver use limited number of buffer. This number can be set by macro + * CDNS3_WA2_NUM_BUFFERS. + * + * Such blocking situation was observed on ACM gadget. For this function + * host send OUT data packet but ACM function is not prepared for this packet. + * It's cause that buffer placed in on chip memory block transfer to other + * endpoints. + * + * Issue has been fixed in DEV_VER_V2 version of controller. + * */ #include @@ -99,6 +125,17 @@ struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list) return list_first_entry_or_null(list, struct cdns3_aligned_buf, list); } +/** + * cdns3_next_priv_request - returns next request from list + * @list: list containing requests + * + * Returns request or NULL if no requests in list + */ +struct cdns3_request *cdns3_next_priv_request(struct list_head *list) +{ + return list_first_entry_or_null(list, struct cdns3_request, list); +} + /** * select_ep - selects endpoint * @priv_dev: extended gadget object @@ -338,6 +375,254 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev, return ret; } +/* + * WA2: Set flag for all not ISOC OUT endpoints. If this flag is set + * driver try to detect whether endpoint need additional internal + * buffer for unblocking on-chip FIFO buffer. This flag will be cleared + * if before first DESCMISS interrupt the DMA will be armed. + */ +#define cdns3_wa2_enable_detection(priv_dev, ep_priv, reg) do { \ + if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { \ + priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET; \ + (reg) |= EP_STS_EN_DESCMISEN; \ + } } while (0) + +/** + * cdns3_wa2_descmiss_copy_data copy data from internal requests to + * request queued by class driver. + * @priv_ep: extended endpoint object + * @request: request object + */ +static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, + struct usb_request *request) +{ + struct usb_request *descmiss_req; + struct cdns3_request *descmiss_priv_req; + + while (!list_empty(&priv_ep->wa2_descmiss_req_list)) { + int chunk_end; + int length; + + descmiss_priv_req = + cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list); + descmiss_req = &descmiss_priv_req->request; + + /* driver can't touch pending request */ + if (descmiss_priv_req->flags & REQUEST_PENDING) + break; + + chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH; + length = request->actual + descmiss_req->actual; + + request->status = descmiss_req->status; + + if (length <= request->length) { + memcpy(&((u8 *)request->buf)[request->actual], + descmiss_req->buf, + descmiss_req->actual); + request->actual = length; + } else { + /* It should never occures */ + request->status = -ENOMEM; + } + + list_del_init(&descmiss_priv_req->list); + + kfree(descmiss_req->buf); + cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req); + --priv_ep->wa2_counter; + + if (!chunk_end) + break; + } +} + +struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep, + struct cdns3_request *priv_req) +{ + if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN && + priv_req->flags & REQUEST_INTERNAL) { + struct usb_request *req; + + req = cdns3_next_request(&priv_ep->deferred_req_list); + + priv_ep->descmis_req = NULL; + + if (!req) + return NULL; + + cdns3_wa2_descmiss_copy_data(priv_ep, req); + if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) && + req->length != req->actual) { + /* wait for next part of transfer */ + return NULL; + } + + if (req->status == -EINPROGRESS) + req->status = 0; + + list_del_init(&req->list); + cdns3_start_all_request(priv_dev, priv_ep); + return req; + } + + return &priv_req->request; +} + +int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep, + struct cdns3_request *priv_req) +{ + int deferred = 0; + + /* + * If transfer was queued before DESCMISS appear than we + * can disable handling of DESCMISS interrupt. Driver assumes that it + * can disable special treatment for this endpoint. + */ + if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) { + u32 reg; + + cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir); + priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET; + reg = readl(&priv_dev->regs->ep_sts_en); + reg &= ~EP_STS_EN_DESCMISEN; + trace_cdns3_wa2(priv_ep, "workaround disabled\n"); + writel(reg, &priv_dev->regs->ep_sts_en); + } + + if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) { + u8 pending_empty = list_empty(&priv_ep->pending_req_list); + u8 descmiss_empty = list_empty(&priv_ep->wa2_descmiss_req_list); + + /* + * DESCMISS transfer has been finished, so data will be + * directly copied from internal allocated usb_request + * objects. + */ + if (pending_empty && !descmiss_empty && + !(priv_req->flags & REQUEST_INTERNAL)) { + cdns3_wa2_descmiss_copy_data(priv_ep, + &priv_req->request); + + trace_cdns3_wa2(priv_ep, "get internal stored data"); + + list_add_tail(&priv_req->request.list, + &priv_ep->pending_req_list); + cdns3_gadget_giveback(priv_ep, priv_req, + priv_req->request.status); + + /* + * Intentionally driver returns positive value as + * correct value. It informs that transfer has + * been finished. + */ + return EINPROGRESS; + } + + /* + * Driver will wait for completion DESCMISS transfer, + * before starts new, not DESCMISS transfer. + */ + if (!pending_empty && !descmiss_empty) { + trace_cdns3_wa2(priv_ep, "wait for pending transfer\n"); + deferred = 1; + } + + if (priv_req->flags & REQUEST_INTERNAL) + list_add_tail(&priv_req->list, + &priv_ep->wa2_descmiss_req_list); + } + + return deferred; +} + +static void cdns3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_request *priv_req; + + while (!list_empty(&priv_ep->wa2_descmiss_req_list)) { + u8 chain; + + priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list); + chain = !!(priv_req->flags & REQUEST_INTERNAL_CH); + + trace_cdns3_wa2(priv_ep, "removes eldest request"); + + kfree(priv_req->request.buf); + cdns3_gadget_ep_free_request(&priv_ep->endpoint, + &priv_req->request); + list_del_init(&priv_req->list); + --priv_ep->wa2_counter; + + if (!chain) + break; + } +} + +/** + * cdns3_wa2_descmissing_packet - handles descriptor missing event. + * @priv_dev: extended gadget object + * + * This function is used only for WA2. For more information see Work around 2 + * description. + */ +static void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_request *priv_req; + struct usb_request *request; + + if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) { + priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET; + priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN; + } + + trace_cdns3_wa2(priv_ep, "Description Missing detected\n"); + + if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS) + cdns3_wa2_remove_old_request(priv_ep); + + request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint, + GFP_ATOMIC); + if (!request) + goto err; + + priv_req = to_cdns3_request(request); + priv_req->flags |= REQUEST_INTERNAL; + + /* if this field is still assigned it indicate that transfer related + * with this request has not been finished yet. Driver in this + * case simply allocate next request and assign flag REQUEST_INTERNAL_CH + * flag to previous one. It will indicate that current request is + * part of the previous one. + */ + if (priv_ep->descmis_req) + priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH; + + priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE, + GFP_ATOMIC); + priv_ep->wa2_counter++; + + if (!priv_req->request.buf) { + cdns3_gadget_ep_free_request(&priv_ep->endpoint, request); + goto err; + } + + priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE; + priv_ep->descmis_req = priv_req; + + __cdns3_gadget_ep_queue(&priv_ep->endpoint, + &priv_ep->descmis_req->request, + GFP_ATOMIC); + + return; + +err: + dev_err(priv_ep->cdns3_dev->dev, + "Failed: No sufficient memory for DESCMIS\n"); +} + /** * cdns3_gadget_giveback - call struct usb_request's ->complete callback * @priv_ep: The endpoint to whom the request belongs to @@ -371,6 +656,13 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); trace_cdns3_gadget_giveback(priv_req); + if (priv_dev->dev_ver < DEV_VER_V2) { + request = cdns3_wa2_gadget_giveback(priv_dev, priv_ep, + priv_req); + if (!request) + return; + } + if (request->complete) { spin_unlock(&priv_dev->lock); usb_gadget_giveback_request(&priv_ep->endpoint, @@ -961,8 +1253,26 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) } } - if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) + if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) { + if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) { + if (ep_sts_reg & EP_STS_ISP) + priv_ep->flags |= EP_QUIRK_END_TRANSFER; + else + priv_ep->flags &= ~EP_QUIRK_END_TRANSFER; + } + cdns3_transfer_completed(priv_dev, priv_ep); + } + + /* + * WA2: this condition should only be meet when + * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or + * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN. + * In other cases this interrupt will be disabled/ + */ + if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 && + !(priv_ep->flags & EP_STALLED)) + cdns3_wa2_descmissing_packet(priv_ep); return 0; } @@ -1526,6 +1836,9 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, cdns3_set_register_bit(&priv_dev->regs->ep_ien, BIT(cdns3_ep_addr_to_index(bEndpointAddress))); + if (priv_dev->dev_ver < DEV_VER_V2) + cdns3_wa2_enable_detection(priv_dev, priv_ep, reg); + writel(reg, &priv_dev->regs->ep_sts_en); /* @@ -1544,7 +1857,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, ep->desc = desc; priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING | - EP_QUIRK_ISO_OUT_EN); + EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN); priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR; priv_ep->wa1_set = 0; priv_ep->enqueue = 0; @@ -1569,6 +1882,7 @@ exit: static int cdns3_gadget_ep_disable(struct usb_ep *ep) { struct cdns3_endpoint *priv_ep; + struct cdns3_request *priv_req; struct cdns3_device *priv_dev; struct usb_request *request; unsigned long flags; @@ -1621,6 +1935,16 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep) -ESHUTDOWN); } + while (!list_empty(&priv_ep->wa2_descmiss_req_list)) { + priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list); + + kfree(priv_req->request.buf); + cdns3_gadget_ep_free_request(&priv_ep->endpoint, + &priv_req->request); + list_del_init(&priv_req->list); + --priv_ep->wa2_counter; + } + while (!list_empty(&priv_ep->deferred_req_list)) { request = cdns3_next_request(&priv_ep->deferred_req_list); @@ -1628,6 +1952,8 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep) -ESHUTDOWN); } + priv_ep->descmis_req = NULL; + ep->desc = NULL; priv_ep->flags &= ~EP_ENABLED; @@ -1658,6 +1984,14 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, priv_req = to_cdns3_request(request); trace_cdns3_ep_queue(priv_req); + if (priv_dev->dev_ver < DEV_VER_V2) { + ret = cdns3_wa2_gadget_ep_queue(priv_dev, priv_ep, + priv_req); + + if (ret == EINPROGRESS) + return 0; + } + ret = cdns3_prepare_aligned_request_buf(priv_req); if (ret < 0) return ret; @@ -2163,6 +2497,7 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev) INIT_LIST_HEAD(&priv_ep->pending_req_list); INIT_LIST_HEAD(&priv_ep->deferred_req_list); + INIT_LIST_HEAD(&priv_ep->wa2_descmiss_req_list); } return 0; diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index a4652d2e9bf9..bc4024041ef2 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1083,6 +1083,7 @@ struct cdns3_trb { #define CDNS3_EP_ISO_SS_BURST 3 #define CDNS3_MAX_NUM_DESCMISS_BUF 32 #define CDNS3_DESCMIS_BUF_SIZE 2048 /* Bytes */ +#define CDNS3_WA2_NUM_BUFFERS 128 /*-------------------------------------------------------------------------*/ /* Used structs */ @@ -1093,11 +1094,15 @@ struct cdns3_device; * @endpoint: usb endpoint * @pending_req_list: list of requests queuing on transfer ring. * @deferred_req_list: list of requests waiting for queuing on transfer ring. + * @wa2_descmiss_req_list: list of requests internally allocated by driver. * @trb_pool: transfer ring - array of transaction buffers * @trb_pool_dma: dma address of transfer ring * @cdns3_dev: device associated with this endpoint * @name: a human readable name e.g. ep1out * @flags: specify the current state of endpoint + * @descmis_req: internal transfer object used for getting data from on-chip + * buffer. It can happen only if function driver doesn't send usb_request + * object on time. * @dir: endpoint direction * @num: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK @@ -1114,6 +1119,8 @@ struct cdns3_endpoint { struct usb_ep endpoint; struct list_head pending_req_list; struct list_head deferred_req_list; + struct list_head wa2_descmiss_req_list; + int wa2_counter; struct cdns3_trb *trb_pool; dma_addr_t trb_pool_dma; @@ -1132,8 +1139,13 @@ struct cdns3_endpoint { #define EP_CLAIMED BIT(8) #define EP_DEFERRED_DRDY BIT(9) #define EP_QUIRK_ISO_OUT_EN BIT(10) +#define EP_QUIRK_END_TRANSFER BIT(11) +#define EP_QUIRK_EXTRA_BUF_DET BIT(12) +#define EP_QUIRK_EXTRA_BUF_EN BIT(13) u32 flags; + struct cdns3_request *descmis_req; + u8 dir; u8 num; u8 type; @@ -1181,6 +1193,7 @@ struct cdns3_aligned_buf { * @aligned_buf: object holds information about aligned buffer associated whit * this endpoint * @flags: flag specifying special usage of request + * @list: used by internally allocated request to add to wa2_descmiss_req_list. */ struct cdns3_request { struct usb_request request; From 17b63704ec7cd4a37c513d964bc09c7e3fde8988 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 29 Aug 2019 18:00:16 -0700 Subject: [PATCH 116/159] usb: dwc3: gadget: Workaround Mirosoft's BESL check While testing our host system using Microsoft's usb stack against our gadget for various BESL values, we found an issue with their usb stack when the recommended baseline BESL value is 0 (125us) or when the deep BESL is 1 or less. The Windows host will issue a usb reset immediately after it receives the extended BOS descriptor and the enumeration will fail after a few attempts. To keep compatibility with Microsoft's host usb stack, let's workaround this issue by using the recommended baseline BESL of 1 (or 150us) and clamp the deep BESL value within 2 to 15. This was tested against Windows 10 build 18956. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7b58e0e1e438..8adb59f8e4f1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2083,9 +2083,19 @@ static void dwc3_gadget_config_params(struct usb_gadget *g, /* Recommended BESL */ if (!dwc->dis_enblslpm_quirk) { - params->besl_baseline = 0; + /* + * If the recommended BESL baseline is 0 or if the BESL deep is + * less than 2, Microsoft's Windows 10 host usb stack will issue + * a usb reset immediately after it receives the extended BOS + * descriptor and the enumeration will fail. To maintain + * compatibility with the Windows' usb stack, let's set the + * recommended BESL baseline to 1 and clamp the BESL deep to be + * within 2 to 15. + */ + params->besl_baseline = 1; if (dwc->is_utmi_l1_suspend) - params->besl_deep = min_t(u8, dwc->hird_threshold, 15); + params->besl_deep = + clamp_t(u8, dwc->hird_threshold, 2, 15); } /* U1 Device exit Latency */ From ec8ca8a3489cc87ca43a865af7e7393690841810 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 30 Aug 2019 13:56:10 +1000 Subject: [PATCH 117/159] usb: gadget: net2280: Move all "ll" registers in one structure The split into multiple structures of the "ll" register bank is impractical. It makes it hard to add ll_lfps_timers_2 which is at offset 0x794, which is outside of the existing "lfps" structure and would require us to add yet another one. Instead, move all the "ll" registers into a single usb338x_ll_regs structure, and add ll_lfps_timers_2 while at it. It will be used in a subsequent patch. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 28 ++++++++++--------------- drivers/usb/gadget/udc/net2280.h | 3 --- include/linux/usb/usb338x.h | 35 +++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index b6bbe2e448ba..e0191146ba22 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2244,30 +2244,30 @@ static void usb_reinit_338x(struct net2280 *dev) } /* Hardware Defect and Workaround */ - val = readl(&dev->ll_lfps_regs->ll_lfps_5); + val = readl(&dev->llregs->ll_lfps_5); val &= ~(0xf << TIMER_LFPS_6US); val |= 0x5 << TIMER_LFPS_6US; - writel(val, &dev->ll_lfps_regs->ll_lfps_5); + writel(val, &dev->llregs->ll_lfps_5); - val = readl(&dev->ll_lfps_regs->ll_lfps_6); + val = readl(&dev->llregs->ll_lfps_6); val &= ~(0xffff << TIMER_LFPS_80US); val |= 0x0100 << TIMER_LFPS_80US; - writel(val, &dev->ll_lfps_regs->ll_lfps_6); + writel(val, &dev->llregs->ll_lfps_6); /* * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB * Hot Reset Exit Handshake may Fail in Specific Case using * Default Register Settings. Workaround for Enumeration test. */ - val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); + val = readl(&dev->llregs->ll_tsn_counters_2); val &= ~(0x1f << HOT_TX_NORESET_TS2); val |= 0x10 << HOT_TX_NORESET_TS2; - writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); + writel(val, &dev->llregs->ll_tsn_counters_2); - val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); + val = readl(&dev->llregs->ll_tsn_counters_3); val &= ~(0x1f << HOT_RX_RESET_TS2); val |= 0x3 << HOT_RX_RESET_TS2; - writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); + writel(val, &dev->llregs->ll_tsn_counters_3); /* * Set Recovery Idle to Recover bit: @@ -2276,10 +2276,10 @@ static void usb_reinit_338x(struct net2280 *dev) * - It is safe to set for all connection speeds; all chip revisions. * - R-M-W to leave other bits undisturbed. * - Reference PLX TT-7372 - */ - val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); + */ + val = readl(&dev->llregs->ll_tsn_chicken_bit); val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); - writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); + writel(val, &dev->llregs->ll_tsn_chicken_bit); INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); @@ -3669,12 +3669,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) (base + 0x00b4); dev->llregs = (struct usb338x_ll_regs __iomem *) (base + 0x0700); - dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) - (base + 0x0748); - dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) - (base + 0x077c); - dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) - (base + 0x079c); dev->plregs = (struct usb338x_pl_regs __iomem *) (base + 0x0800); usbstat = readl(&dev->usb->usbstat); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index b65a797544d7..85d3ca1698ba 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -178,9 +178,6 @@ struct net2280 { struct net2280_dep_regs __iomem *dep; struct net2280_ep_regs __iomem *epregs; struct usb338x_ll_regs __iomem *llregs; - struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; - struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; - struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; struct usb338x_pl_regs __iomem *plregs; struct dma_pool *requests; diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h index 7189e3387bf9..20020c1336d5 100644 --- a/include/linux/usb/usb338x.h +++ b/include/linux/usb/usb338x.h @@ -113,7 +113,10 @@ struct usb338x_ll_regs { u32 ll_ltssm_ctrl1; u32 ll_ltssm_ctrl2; u32 ll_ltssm_ctrl3; - u32 unused[2]; + u32 unused1; + + /* 0x710 */ + u32 unused2; u32 ll_general_ctrl0; u32 ll_general_ctrl1; #define PM_U3_AUTO_EXIT 29 @@ -136,29 +139,41 @@ struct usb338x_ll_regs { u32 ll_general_ctrl2; #define SELECT_INVERT_LANE_POLARITY 7 #define FORCE_INVERT_LANE_POLARITY 6 + + /* 0x720 */ u32 ll_general_ctrl3; u32 ll_general_ctrl4; u32 ll_error_gen; -} __packed; + u32 unused3; -struct usb338x_ll_lfps_regs { - /* offset 0x748 */ + /* 0x730 */ + u32 unused4[4]; + + /* 0x740 */ + u32 unused5[2]; u32 ll_lfps_5; #define TIMER_LFPS_6US 16 u32 ll_lfps_6; #define TIMER_LFPS_80US 0 -} __packed; -struct usb338x_ll_tsn_regs { - /* offset 0x77C */ + /* 0x750 */ + u32 unused6[8]; + + /* 0x770 */ + u32 unused7[3]; u32 ll_tsn_counters_2; #define HOT_TX_NORESET_TS2 24 + + /* 0x780 */ u32 ll_tsn_counters_3; #define HOT_RX_RESET_TS2 0 -} __packed; + u32 unused8[3]; -struct usb338x_ll_chi_regs { - /* offset 0x79C */ + /* 0x790 */ + u32 unused9; + u32 ll_lfps_timers_2; +#define LFPS_TIMERS_2_WORKAROUND_VALUE 0x084d + u32 unused10; u32 ll_tsn_chicken_bit; #define RECOVERY_IDLE_TO_RECOVER_FMW 3 } __packed; From 18a93cd38be3e69ac5b067c570a78a369b79e31d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 30 Aug 2019 13:56:14 +1000 Subject: [PATCH 118/159] usb: gadget: net2280: Add workaround for AB chip Errata 11 The errata description is: Workaround for Default Duration of LFPS Handshake Signaling for Device-Initiated U1 Exit is too short. The default duration of the LFPS handshake generated by USB3380 for a device-initiated U1-exit may not be long enough for certain SuperSpeed downstream ports (SuperSpeed hubs/hosts) to recognize. This could lead to USB3380 entering the recovery state pre-maturely and ending up in the SS.Inactive state. I have observed various enumeration failures, seemingly related to lost transactions or SETUP status phases on modern hosts (typically thunderbolt capable systems) without this workaround. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index e0191146ba22..51efee21915f 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2269,6 +2269,16 @@ static void usb_reinit_338x(struct net2280 *dev) val |= 0x3 << HOT_RX_RESET_TS2; writel(val, &dev->llregs->ll_tsn_counters_3); + /* + * AB errata. Errata 11. Workaround for Default Duration of LFPS + * Handshake Signaling for Device-Initiated U1 Exit is too short. + * Without this, various enumeration failures observed with + * modern superspeed hosts. + */ + val = readl(&dev->llregs->ll_lfps_timers_2); + writel((val & 0xffff0000) | LFPS_TIMERS_2_WORKAROUND_VALUE, + &dev->llregs->ll_lfps_timers_2); + /* * Set Recovery Idle to Recover bit: * - On SS connections, setting Recovery Idle to Recover Fmw improves From ecd55e367f3d706788632e176ec6b94e1a72a07c Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Mon, 2 Sep 2019 09:43:23 +0800 Subject: [PATCH 119/159] usb: chipidea: msm: Use device-managed registration API Use devm_reset_controller_register to get rid of manual unregistration. Signed-off-by: Chuhong Yuan Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_msm.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index bb4645a8ca46..af648ba6544d 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -216,13 +216,13 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) ci->rcdev.ops = &ci_hdrc_msm_reset_ops; ci->rcdev.of_node = pdev->dev.of_node; ci->rcdev.nr_resets = 2; - ret = reset_controller_register(&ci->rcdev); + ret = devm_reset_controller_register(&pdev->dev, &ci->rcdev); if (ret) return ret; ret = clk_prepare_enable(ci->fs_clk); if (ret) - goto err_fs; + return ret; reset_control_assert(reset); usleep_range(10000, 12000); @@ -232,7 +232,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) ret = clk_prepare_enable(ci->core_clk); if (ret) - goto err_fs; + return ret; ret = clk_prepare_enable(ci->iface_clk); if (ret) @@ -271,8 +271,6 @@ err_mux: clk_disable_unprepare(ci->iface_clk); err_iface: clk_disable_unprepare(ci->core_clk); -err_fs: - reset_controller_unregister(&ci->rcdev); return ret; } @@ -284,7 +282,6 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev) ci_hdrc_remove_device(ci->ci); clk_disable_unprepare(ci->iface_clk); clk_disable_unprepare(ci->core_clk); - reset_controller_unregister(&ci->rcdev); return 0; } From a1747eb43f3546038dad8cb0c4fec5416b83ec59 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 3 Sep 2019 10:46:10 +0200 Subject: [PATCH 120/159] usb/ohci-sm501: remove the HCD_DMA flag This driver doesn't support normal DMA, only direct access to its local memory. Remove the HCD_DMA flag to properly express that fact. Fixes: 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of guestimating DMA capabilities") Reported-by: Guenter Roeck Signed-off-by: Christoph Hellwig Tested-by: Guenter Roeck Link: https://lore.kernel.org/r/20190903084615.19161-2-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-sm501.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 0b2aea6e28d4..c158cda9e4b9 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -49,7 +49,7 @@ static const struct hc_driver ohci_sm501_hc_driver = { * generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, + .flags = HCD_USB11 | HCD_MEMORY, /* * basic lifecycle operations From 8838d9224f84cbe04979edb005365af236d39410 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 3 Sep 2019 10:46:11 +0200 Subject: [PATCH 121/159] usb/ohci-tmio: remove the HCD_DMA flag This driver doesn't support normal DMA, only direct access to its local memory. Remove the HCD_DMA flag to properly express that fact. Fixes: 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of guestimating DMA capabilities") Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190903084615.19161-3-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-tmio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 221593d75556..fb6f5e9ae5c6 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -156,7 +156,7 @@ static const struct hc_driver ohci_tmio_hc_driver = { /* generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, + .flags = HCD_USB11 | HCD_MEMORY, /* basic lifecycle operations */ .start = ohci_tmio_start, From bf89015a54131253f963ee68b95461e6a3a4c462 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 3 Sep 2019 10:46:12 +0200 Subject: [PATCH 122/159] usb-storage: use hcd_uses_dma to check for DMA capabilities The dma_mask on its own doesn't mean much. Instead check for the actual flag. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190903084615.19161-4-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index df4de8323eff..6737fab94959 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -40,6 +40,7 @@ #include #include "usb.h" +#include #include "scsiglue.h" #include "debug.h" #include "transport.h" @@ -141,11 +142,10 @@ static int slave_configure(struct scsi_device *sdev) /* * Some USB host controllers can't do DMA; they have to use PIO. - * They indicate this by setting their dma_mask to NULL. For - * such controllers we need to make sure the block layer sets + * For such controllers we need to make sure the block layer sets * up bounce buffers in addressable memory. */ - if (!us->pusb_dev->bus->controller->dma_mask) + if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus))) blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH); /* From ac2658e0d9f0461e730cbc343231dad304fb2f4f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 3 Sep 2019 10:46:13 +0200 Subject: [PATCH 123/159] usb: remove a stale comment in hcd_alloc_coherent Now that we have the local memory pool implemented there is no need to use dma_declare_coherent_memory. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190903084615.19161-5-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index add2af4af766..4ccfc8e59604 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1249,9 +1249,6 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); * To support host controllers with limited dma capabilities we provide dma * bounce buffers. This feature can be enabled by initializing * hcd->localmem_pool using usb_hcd_setup_local_mem(). - * For this to work properly the host controller code must first use the - * function dma_declare_coherent_memory() to point out which memory area - * that should be used for dma allocations. * * The initialized hcd->localmem_pool then tells the usb code to allocate all * data for dma using the genalloc API. From 7b65fe12556f0b57721f75d5ee61b67d03379b79 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 3 Sep 2019 10:46:14 +0200 Subject: [PATCH 124/159] usb: remove commented out dma wrappers These wrappers have never seen use and have been commented out for a long time. Remove them for good. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20190903084615.19161-6-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 222 ----------------------------------------- 1 file changed, 222 deletions(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0ab8738047da..f16c26dc079d 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -933,228 +933,6 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, } EXPORT_SYMBOL_GPL(usb_free_coherent); -/** - * usb_buffer_map - create DMA mapping(s) for an urb - * @urb: urb whose transfer_buffer/setup_packet will be mapped - * - * URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation - * succeeds. If the device is connected to this system through a non-DMA - * controller, this operation always succeeds. - * - * This call would normally be used for an urb which is reused, perhaps - * as the target of a large periodic transfer, with usb_buffer_dmasync() - * calls to synchronize memory and dma state. - * - * Reverse the effect of this call with usb_buffer_unmap(). - * - * Return: Either %NULL (indicating no buffer could be mapped), or @urb. - * - */ -#if 0 -struct urb *usb_buffer_map(struct urb *urb) -{ - struct usb_bus *bus; - struct device *controller; - - if (!urb - || !urb->dev - || !(bus = urb->dev->bus) - || !(controller = bus->sysdev)) - return NULL; - - if (controller->dma_mask) { - urb->transfer_dma = dma_map_single(controller, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein(urb->pipe) - ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - /* FIXME generic api broken like pci, can't report errors */ - /* if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; */ - } else - urb->transfer_dma = ~0; - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - return urb; -} -EXPORT_SYMBOL_GPL(usb_buffer_map); -#endif /* 0 */ - -/* XXX DISABLED, no users currently. If you wish to re-enable this - * XXX please determine whether the sync is to transfer ownership of - * XXX the buffer from device to cpu or vice verse, and thusly use the - * XXX appropriate _for_{cpu,device}() method. -DaveM - */ -#if 0 - -/** - * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) - * @urb: urb whose transfer_buffer/setup_packet will be synchronized - */ -void usb_buffer_dmasync(struct urb *urb) -{ - struct usb_bus *bus; - struct device *controller; - - if (!urb - || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) - || !urb->dev - || !(bus = urb->dev->bus) - || !(controller = bus->sysdev)) - return; - - if (controller->dma_mask) { - dma_sync_single_for_cpu(controller, - urb->transfer_dma, urb->transfer_buffer_length, - usb_pipein(urb->pipe) - ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (usb_pipecontrol(urb->pipe)) - dma_sync_single_for_cpu(controller, - urb->setup_dma, - sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); - } -} -EXPORT_SYMBOL_GPL(usb_buffer_dmasync); -#endif - -/** - * usb_buffer_unmap - free DMA mapping(s) for an urb - * @urb: urb whose transfer_buffer will be unmapped - * - * Reverses the effect of usb_buffer_map(). - */ -#if 0 -void usb_buffer_unmap(struct urb *urb) -{ - struct usb_bus *bus; - struct device *controller; - - if (!urb - || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) - || !urb->dev - || !(bus = urb->dev->bus) - || !(controller = bus->sysdev)) - return; - - if (controller->dma_mask) { - dma_unmap_single(controller, - urb->transfer_dma, urb->transfer_buffer_length, - usb_pipein(urb->pipe) - ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - } - urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; -} -EXPORT_SYMBOL_GPL(usb_buffer_unmap); -#endif /* 0 */ - -#if 0 -/** - * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint - * @dev: device to which the scatterlist will be mapped - * @is_in: mapping transfer direction - * @sg: the scatterlist to map - * @nents: the number of entries in the scatterlist - * - * Return: Either < 0 (indicating no buffers could be mapped), or the - * number of DMA mapping array entries in the scatterlist. - * - * Note: - * The caller is responsible for placing the resulting DMA addresses from - * the scatterlist into URB transfer buffer pointers, and for setting the - * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs. - * - * Top I/O rates come from queuing URBs, instead of waiting for each one - * to complete before starting the next I/O. This is particularly easy - * to do with scatterlists. Just allocate and submit one URB for each DMA - * mapping entry returned, stopping on the first error or when all succeed. - * Better yet, use the usb_sg_*() calls, which do that (and more) for you. - * - * This call would normally be used when translating scatterlist requests, - * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it - * may be able to coalesce mappings for improved I/O efficiency. - * - * Reverse the effect of this call with usb_buffer_unmap_sg(). - */ -int usb_buffer_map_sg(const struct usb_device *dev, int is_in, - struct scatterlist *sg, int nents) -{ - struct usb_bus *bus; - struct device *controller; - - if (!dev - || !(bus = dev->bus) - || !(controller = bus->sysdev) - || !controller->dma_mask) - return -EINVAL; - - /* FIXME generic api broken like pci, can't report errors */ - return dma_map_sg(controller, sg, nents, - is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM; -} -EXPORT_SYMBOL_GPL(usb_buffer_map_sg); -#endif - -/* XXX DISABLED, no users currently. If you wish to re-enable this - * XXX please determine whether the sync is to transfer ownership of - * XXX the buffer from device to cpu or vice verse, and thusly use the - * XXX appropriate _for_{cpu,device}() method. -DaveM - */ -#if 0 - -/** - * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s) - * @dev: device to which the scatterlist will be mapped - * @is_in: mapping transfer direction - * @sg: the scatterlist to synchronize - * @n_hw_ents: the positive return value from usb_buffer_map_sg - * - * Use this when you are re-using a scatterlist's data buffers for - * another USB request. - */ -void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in, - struct scatterlist *sg, int n_hw_ents) -{ - struct usb_bus *bus; - struct device *controller; - - if (!dev - || !(bus = dev->bus) - || !(controller = bus->sysdev) - || !controller->dma_mask) - return; - - dma_sync_sg_for_cpu(controller, sg, n_hw_ents, - is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); -} -EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg); -#endif - -#if 0 -/** - * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist - * @dev: device to which the scatterlist will be mapped - * @is_in: mapping transfer direction - * @sg: the scatterlist to unmap - * @n_hw_ents: the positive return value from usb_buffer_map_sg - * - * Reverses the effect of usb_buffer_map_sg(). - */ -void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in, - struct scatterlist *sg, int n_hw_ents) -{ - struct usb_bus *bus; - struct device *controller; - - if (!dev - || !(bus = dev->bus) - || !(controller = bus->sysdev) - || !controller->dma_mask) - return; - - dma_unmap_sg(controller, sg, n_hw_ents, - is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); -} -EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg); -#endif - /* * Notifications of device and interface registration */ From 8ceb1417f3ca3fdc3f093113dcf416b7fa9690eb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 3 Sep 2019 10:46:15 +0200 Subject: [PATCH 125/159] mfd: don't select DMA_DECLARE_COHERENT for the sm501 and tc6393xb drivers Now that these drivers use the usb localmem pool there is no need to select DMA_DECLARE_COHERENT. Signed-off-by: Christoph Hellwig Tested-by: Guenter Roeck Link: https://lore.kernel.org/r/20190903084615.19161-7-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f129f9678940..c8cbde59bbf6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1105,7 +1105,6 @@ config MFD_SI476X_CORE config MFD_SM501 tristate "Silicon Motion SM501" depends on HAS_DMA - select DMA_DECLARE_COHERENT ---help--- This is the core driver for the Silicon Motion SM501 multimedia companion chip. This device is a multifunction device which may @@ -1714,7 +1713,6 @@ config MFD_TC6393XB select GPIOLIB select MFD_CORE select MFD_TMIO - select DMA_DECLARE_COHERENT help Support for Toshiba Mobile IO Controller TC6393XB From b3d72616b428e28200615dcc7fc5fde04f777d07 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 3 Sep 2019 20:06:15 +0800 Subject: [PATCH 126/159] usb: cdns3: Fix Wunused-but-set-variable warning drivers/usb/cdns3/ep0.c: In function cdns3_ep0_feature_handle_device: drivers/usb/cdns3/ep0.c:290:6: warning: variable wIndex set but not used [-Wunused-but-set-variable] drivers/usb/cdns3/ep0.c:289:6: warning: variable wValue set but not used [-Wunused-but-set-variable] wIndex is never used, so remove it. wValue should be use in the switch statement. Reported-by: Hulk Robot Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190903120615.19504-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/ep0.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index e94f55d4729c..44f652e8b5a2 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -287,15 +287,13 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, enum usb_device_speed speed; int ret = 0; u32 wValue; - u32 wIndex; u16 tmode; wValue = le16_to_cpu(ctrl->wValue); - wIndex = le16_to_cpu(ctrl->wIndex); state = priv_dev->gadget.state; speed = priv_dev->gadget.speed; - switch (ctrl->wValue) { + switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: priv_dev->wake_up_flag = !!set; break; From f271b966bff19363b621204c035dce028c534cdd Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 3 Sep 2019 20:04:45 +0800 Subject: [PATCH 127/159] usb: cdns3: remove set but not used variable 'priv_dev' Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/cdns3/gadget.c: In function '__cdns3_gadget_init': drivers/usb/cdns3/gadget.c:2665:23: warning: variable 'priv_dev' set but not used [-Wunused-but-set-variable] drivers/usb/cdns3/gadget.c: In function cdns3_start_all_request: drivers/usb/cdns3/gadget.c:357:24: warning: variable priv_req set but not used [-Wunused-but-set-variable] They are never used, so can be removed. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190903120445.22204-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 3094ad65ffc9..ddac638f0409 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -354,13 +354,11 @@ enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev) static int cdns3_start_all_request(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep) { - struct cdns3_request *priv_req; struct usb_request *request; int ret = 0; while (!list_empty(&priv_ep->deferred_req_list)) { request = cdns3_next_request(&priv_ep->deferred_req_list); - priv_req = to_cdns3_request(request); ret = cdns3_ep_run_transfer(priv_ep, request); if (ret) @@ -2664,7 +2662,6 @@ err1: static int __cdns3_gadget_init(struct cdns3 *cdns) { - struct cdns3_device *priv_dev; int ret = 0; cdns3_drd_switch_gadget(cdns, 1); @@ -2674,8 +2671,6 @@ static int __cdns3_gadget_init(struct cdns3 *cdns) if (ret) return ret; - priv_dev = cdns->gadget_dev; - /* * Because interrupt line can be shared with other components in * driver it can't use IRQF_ONESHOT flag here. From 35af2445dc306403254a181507b390ec9eb725d5 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 3 Sep 2019 20:10:26 +0800 Subject: [PATCH 128/159] usb: typec: tps6598x: Fix build error without CONFIG_REGMAP_I2C If CONFIG_REGMAP_I2C is not set, building fails: drivers/usb/typec/tps6598x.o: In function `tps6598x_probe': tps6598x.c:(.text+0x5f0): undefined reference to `__devm_regmap_init_i2c' Select REGMAP_I2C to fix this. Reported-by: Hulk Robot Fixes: 0a4c005bd171 ("usb: typec: driver for TI TPS6598x USB Power Delivery controllers") Signed-off-by: YueHaibing Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190903121026.22148-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 89d9193bd1cf..895e2418de53 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -53,6 +53,7 @@ source "drivers/usb/typec/ucsi/Kconfig" config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C + select REGMAP_I2C help Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power Delivery controller. From 4d2233ecec8e5e2ac3e2efd5bf8fdfaf1dab30d9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 2 Sep 2019 19:43:34 +0100 Subject: [PATCH 129/159] usb: cdns3: remove redundant assignment to ret on error exit path In the case where an allocation fails for priv_ep ret is assigned -ENOMEM and the code exits via the exit path 'err'. The exit path returns -ENOMEM without using variable ret, so the assingment is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190902184334.27406-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index ddac638f0409..9536908aabb4 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2445,10 +2445,8 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev) priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep), GFP_KERNEL); - if (!priv_ep) { - ret = -ENOMEM; + if (!priv_ep) goto err; - } /* set parent of endpoint object */ priv_ep->cdns3_dev = priv_dev; From 04db1d2057c8799ab4e732246ed45ca0b283aa6f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 2 Sep 2019 15:50:35 +0100 Subject: [PATCH 130/159] usb: cdns3: fix missing assignment of ret before error check on ret Currently the check on a non-zero return code in ret is false because ret has been initialized to zero. I believe that ret should be assigned to the return from the call to readl_poll_timeout_atomic before the check on ret. Since ret is being re-assinged the original initialization of ret to zero can be removed. Addresses-Coverity: ("'Constant' variable guards dead code") Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190902145035.18200-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 9536908aabb4..3a9349d14788 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2152,7 +2152,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; struct usb_request *request; - int ret = 0; + int ret; int val; trace_cdns3_halt(priv_ep, 0, 0); @@ -2160,8 +2160,8 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd); /* wait for EPRST cleared */ - readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, - !(val & EP_CMD_EPRST), 1, 100); + ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & EP_CMD_EPRST), 1, 100); if (ret) return -EINVAL; From 5d04111d76e2ef7b54ce364ff72076c676e684c0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 3 Sep 2019 13:07:10 +0100 Subject: [PATCH 131/159] usb: cdns3: fix spelling mistake: "cnfiguration" -> "configuration" There is a spelling mistake in a dev_dbg message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190903120710.30128-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 3a9349d14788..228cdc4ab886 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2626,7 +2626,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns) readl(&priv_dev->regs->usb_cap6)); dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n", readl(&priv_dev->regs->usb_cap1)); - dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n", + dev_dbg(priv_dev->dev, "On-Chip memory configuration: %08x\n", readl(&priv_dev->regs->usb_cap2)); priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver); From 5a030e618712fcdae0bc61b8c2303bac1c2b805e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 30 Aug 2019 16:39:13 +0300 Subject: [PATCH 132/159] usb: xhci: dbc: Simplify error handling in 'xhci_dbc_alloc_requests()' If the 'kmalloc()' fails, we need to undo the previous 'dbc_alloc_request()' call. Because of the more similar function name, it is more logical to use 'dbc_free_request()' instead of 'xhci_dbc_free_req()'. Both are equivalent here because: static void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req) { kfree(req->buf); dbc_free_request(dep, req); } and 'req->buf' is known to be NULL at this point Signed-off-by: Christophe JAILLET Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1567172356-12915-2-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index aff79ff5aba4..845939f8a0b8 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -146,7 +146,7 @@ xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head, req->length = DBC_MAX_PACKET; req->buf = kmalloc(req->length, GFP_KERNEL); if (!req->buf) { - xhci_dbc_free_req(dep, req); + dbc_free_request(dep, req); break; } From b62a31b04e9315517bcd59d6e9aac29ab0d9d582 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 30 Aug 2019 16:39:14 +0300 Subject: [PATCH 133/159] usb: xhci: dbc: Use GFP_KERNEL instead of GFP_ATOMIC in 'xhci_dbc_alloc_requests()' There is no need to use GFP_ATOMIC to allocate 'req'. GFP_KERNEL should be enough and is already used for another allocation juste a few lines below. Signed-off-by: Christophe JAILLET Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1567172356-12915-3-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index 845939f8a0b8..be726c791323 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -139,7 +139,7 @@ xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head, struct dbc_request *req; for (i = 0; i < DBC_QUEUE_SIZE; i++) { - req = dbc_alloc_request(dep, GFP_ATOMIC); + req = dbc_alloc_request(dep, GFP_KERNEL); if (!req) break; From 8a62dff2c0734a67fefd96742839a6d4bbb2587b Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 30 Aug 2019 16:39:15 +0300 Subject: [PATCH 134/159] xhci: add TSP bitflag to TRB tracing Software can set a Transfer State Preserve (TSP) flag to maintain data toggle and sequence number when issuing a reset endpoint command. xhci driver is using TSP for soft retry, we want to show TSP usage in tracing as well Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1567172356-12915-4-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f5c41448d067..f9f88626a57a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2337,12 +2337,13 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, break; case TRB_RESET_EP: sprintf(str, - "%s: ctx %08x%08x slot %d ep %d flags %c", + "%s: ctx %08x%08x slot %d ep %d flags %c:%c", xhci_trb_type_string(type), field1, field0, TRB_TO_SLOT_ID(field3), /* Macro decrements 1, maybe it shouldn't?!? */ TRB_TO_EP_INDEX(field3) + 1, + field3 & TRB_TSP ? 'T' : 't', field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STOP_RING: From 9334367cda850fadc3b0ed2ff26b85e29226c1c4 Mon Sep 17 00:00:00 2001 From: Ikjoon Jang Date: Fri, 30 Aug 2019 16:39:16 +0300 Subject: [PATCH 135/159] xhci: fix possible memleak on setup address fails. Xhci re-enables a slot on transaction error in set_address using xhci_disable_slot() + xhci_alloc_dev(). But in this case, xhci_alloc_dev() creates debugfs entries upon an existing device without cleaning up old entries, thus memory leaks. So this patch simply moves calling xhci_debugfs_free_dev() from xhci_free_dev() to xhci_disable_slot(). [added "possible" to header as this is about failure codepath -Mathias] Signed-off-by: Ikjoon Jang Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1567172356-12915-5-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e315c0158e90..500865975687 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3814,7 +3814,6 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING; del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } - xhci_debugfs_remove_slot(xhci, udev->slot_id); virt_dev->udev = NULL; ret = xhci_disable_slot(xhci, udev->slot_id); if (ret) @@ -3832,6 +3831,8 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) if (!command) return -ENOMEM; + xhci_debugfs_remove_slot(xhci, slot_id); + spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = readl(&xhci->op_regs->status); From 77d8f110acb77657fefb18f9563c2f911512759b Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 2 Sep 2019 21:01:36 +0900 Subject: [PATCH 136/159] usb: host: xhci-plat: add quirks member into struct xhci_plat_priv To simplify adding xhci->quirks instead of the .init_quirk() function, this patch adds a new parameter "quirks" into the struct xhci_plat_priv. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1567425698-27560-2-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 4 +++- drivers/usb/host/xhci-plat.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index a1e5ce484bf8..1843b698b78a 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -66,12 +66,14 @@ static int xhci_priv_resume_quirk(struct usb_hcd *hcd) static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { + struct xhci_plat_priv *priv = xhci_to_priv(xhci); + /* * As of now platform drivers don't provide MSI support so we ensure * here that the generic code does not try to make a pci_dev from our * dev struct in order to setup MSI */ - xhci->quirks |= XHCI_PLAT; + xhci->quirks |= XHCI_PLAT | priv->quirks; } /* called during probe() after chip reset completes */ diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index ae29f22ff5bd..5681723fc9cd 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -12,10 +12,12 @@ struct xhci_plat_priv { const char *firmware_name; + unsigned long long quirks; void (*plat_start)(struct usb_hcd *); int (*init_quirk)(struct usb_hcd *); int (*resume_quirk)(struct usb_hcd *); }; #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) +#define xhci_to_priv(x) ((struct xhci_plat_priv *)(x)->priv) #endif /* _XHCI_PLAT_H */ From ed056203fa7c53bb180970d28e31e3635ad82c7a Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 2 Sep 2019 21:01:37 +0900 Subject: [PATCH 137/159] usb: host: xhci-rcar: Add a helper macro to set xhci_plat_priv To avoid copy-and-paste setting of xhci_plat_priv for R-Car SoCs, this patch add a helper macro SET_XHCI_PLAT_PRIV_FOR_RCAR. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1567425698-27560-3-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 10 ++-------- drivers/usb/host/xhci-rcar.h | 7 +++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1843b698b78a..d90cd5ec09cf 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -105,17 +105,11 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { }; static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { - .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1, - .init_quirk = xhci_rcar_init_quirk, - .plat_start = xhci_rcar_start, - .resume_quirk = xhci_rcar_resume_quirk, + SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) }; static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { - .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3, - .init_quirk = xhci_rcar_init_quirk, - .plat_start = xhci_rcar_start, - .resume_quirk = xhci_rcar_resume_quirk, + SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) }; static const struct of_device_id usb_xhci_of_match[] = { diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h index 804b6ab4246f..1f8c22506a92 100644 --- a/drivers/usb/host/xhci-rcar.h +++ b/drivers/usb/host/xhci-rcar.h @@ -31,4 +31,11 @@ static inline int xhci_rcar_resume_quirk(struct usb_hcd *hcd) return 0; } #endif + +#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ + .firmware_name = firmware, \ + .init_quirk = xhci_rcar_init_quirk, \ + .plat_start = xhci_rcar_start, \ + .resume_quirk = xhci_rcar_resume_quirk, + #endif /* _XHCI_RCAR_H */ From ee6754ace1ec6b755b9b505b1e147add70e39678 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 2 Sep 2019 21:01:38 +0900 Subject: [PATCH 138/159] usb: host: xhci-rcar: Use xhci_plat_priv.quirks instead of code settings This patch uses xhci_plat_priv.quirks to simplify. The previous code had conditions to set some quirks in xhci_rcar_init_quirk(). But, the xhci_rcar_init_quirk() is called at the same conditions. So, no behavior change. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1567425698-27560-4-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-rcar.c | 28 ---------------------------- drivers/usb/host/xhci-rcar.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 2b0ccd150209..c1025d321a41 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -107,15 +107,6 @@ static int xhci_rcar_is_gen2(struct device *dev) of_device_is_compatible(node, "renesas,rcar-gen2-xhci"); } -static int xhci_rcar_is_gen3(struct device *dev) -{ - struct device_node *node = dev->of_node; - - return of_device_is_compatible(node, "renesas,xhci-r8a7795") || - of_device_is_compatible(node, "renesas,xhci-r8a7796") || - of_device_is_compatible(node, "renesas,rcar-gen3-xhci"); -} - void xhci_rcar_start(struct usb_hcd *hcd) { u32 temp; @@ -226,32 +217,13 @@ static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd) /* This function needs to initialize a "phy" of usb before */ int xhci_rcar_init_quirk(struct usb_hcd *hcd) { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - /* If hcd->regs is NULL, we don't just call the following function */ if (!hcd->regs) return 0; - /* - * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set - * to 1. However, these SoCs don't support 64-bit address memory - * pointers. So, this driver clears the AC64 bit of xhci->hcc_params - * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in - * xhci_gen_setup(). - * - * And, since the firmware/internal CPU control the USBSTS.STS_HALT - * and the process speed is down when the roothub port enters U3, - * long delay for the handshake of STS_HALT is neeed in xhci_suspend(). - */ - if (xhci_rcar_is_gen2(hcd->self.controller) || - xhci_rcar_is_gen3(hcd->self.controller)) { - xhci->quirks |= XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND; - } - if (!xhci_rcar_wait_for_pll_active(hcd)) return -ETIMEDOUT; - xhci->quirks |= XHCI_TRUST_TX_LENGTH; return xhci_rcar_download_firmware(hcd); } diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h index 1f8c22506a92..012744a63a49 100644 --- a/drivers/usb/host/xhci-rcar.h +++ b/drivers/usb/host/xhci-rcar.h @@ -32,8 +32,22 @@ static inline int xhci_rcar_resume_quirk(struct usb_hcd *hcd) } #endif +/* + * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set + * to 1. However, these SoCs don't support 64-bit address memory + * pointers. So, this driver clears the AC64 bit of xhci->hcc_params + * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in + * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk. + * + * And, since the firmware/internal CPU control the USBSTS.STS_HALT + * and the process speed is down when the roothub port enters U3, + * long delay for the handshake of STS_HALT is neeed in xhci_suspend() + * by using the XHCI_SLOW_SUSPEND quirk. + */ #define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ .firmware_name = firmware, \ + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \ + XHCI_SLOW_SUSPEND, \ .init_quirk = xhci_rcar_init_quirk, \ .plat_start = xhci_rcar_start, \ .resume_quirk = xhci_rcar_resume_quirk, From d7290cd04b4ede9cccc0cf1b72ee856fdf46cf8b Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Mon, 2 Sep 2019 09:43:23 +0800 Subject: [PATCH 139/159] usb: chipidea: msm: Use device-managed registration API Use devm_reset_controller_register to get rid of manual unregistration. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20190902014323.27588-1-hslester96@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_msm.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index bb4645a8ca46..af648ba6544d 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -216,13 +216,13 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) ci->rcdev.ops = &ci_hdrc_msm_reset_ops; ci->rcdev.of_node = pdev->dev.of_node; ci->rcdev.nr_resets = 2; - ret = reset_controller_register(&ci->rcdev); + ret = devm_reset_controller_register(&pdev->dev, &ci->rcdev); if (ret) return ret; ret = clk_prepare_enable(ci->fs_clk); if (ret) - goto err_fs; + return ret; reset_control_assert(reset); usleep_range(10000, 12000); @@ -232,7 +232,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) ret = clk_prepare_enable(ci->core_clk); if (ret) - goto err_fs; + return ret; ret = clk_prepare_enable(ci->iface_clk); if (ret) @@ -271,8 +271,6 @@ err_mux: clk_disable_unprepare(ci->iface_clk); err_iface: clk_disable_unprepare(ci->core_clk); -err_fs: - reset_controller_unregister(&ci->rcdev); return ret; } @@ -284,7 +282,6 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev) ci_hdrc_remove_device(ci->ci); clk_disable_unprepare(ci->iface_clk); clk_disable_unprepare(ci->core_clk); - reset_controller_unregister(&ci->rcdev); return 0; } From 34c7ed72f4f04e73901ca6ec2e1db6c040495935 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 29 Aug 2019 07:30:27 +0200 Subject: [PATCH 140/159] usb: core: phy: add support for PHY calibration Some PHYs (for example Exynos5 USB3.0 DRD PHY) require calibration to be done after every USB HCD reset. Generic PHY framework has been already extended with phy_calibrate() function in commit 36914111e682 ("drivers: phy: add calibrate method"). This patch adds support for it to generic PHY handling code in USB HCD core. Signed-off-by: Marek Szyprowski Tested-by: Anand Moon Tested-by: Jochen Sprickerhof Link: https://lore.kernel.org/r/20190829053028.32438-2-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 7 +++++++ drivers/usb/core/phy.c | 21 +++++++++++++++++++++ drivers/usb/core/phy.h | 1 + 3 files changed, 29 insertions(+) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4ccfc8e59604..f225eaa98ff8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2188,6 +2188,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) hcd->state = HC_STATE_RESUMING; status = hcd->driver->bus_resume(hcd); clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); + if (status == 0) + status = usb_phy_roothub_calibrate(hcd->phy_roothub); + if (status == 0) { struct usb_device *udev; int port1; @@ -2760,6 +2763,10 @@ int usb_add_hcd(struct usb_hcd *hcd, } hcd->rh_pollable = 1; + retval = usb_phy_roothub_calibrate(hcd->phy_roothub); + if (retval) + goto err_hcd_driver_setup; + /* NOTE: root hub and controller capabilities may not be the same */ if (device_can_wakeup(hcd->self.controller) && device_can_wakeup(&hcd->self.root_hub->dev)) diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c index 7580493b867a..fb1588e7c282 100644 --- a/drivers/usb/core/phy.c +++ b/drivers/usb/core/phy.c @@ -151,6 +151,27 @@ err_out: } EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode); +int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub) +{ + struct usb_phy_roothub *roothub_entry; + struct list_head *head; + int err; + + if (!phy_roothub) + return 0; + + head = &phy_roothub->list; + + list_for_each_entry(roothub_entry, head, list) { + err = phy_calibrate(roothub_entry->phy); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate); + int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub) { struct usb_phy_roothub *roothub_entry; diff --git a/drivers/usb/core/phy.h b/drivers/usb/core/phy.h index dad564e2d2d4..20a267cd986b 100644 --- a/drivers/usb/core/phy.h +++ b/drivers/usb/core/phy.h @@ -18,6 +18,7 @@ int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub); int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub, enum phy_mode mode); +int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub); int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub); void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub); From a0a465569b45e3690be155c96fb54603d6904f41 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 29 Aug 2019 07:30:28 +0200 Subject: [PATCH 141/159] usb: dwc3: remove generic PHY calibrate() calls Calls to USB2 generic PHY calibrate() method has been moved to HCD core, which now successfully handles generic PHYs and their calibration after every HCD reset. This fixes all the timing issues related to PHY calibration done directly from DWC3 driver: incorrect operation after system suspend/resume or USB3.0 detection failure when XHCI-plat driver compiled as separate module. Signed-off-by: Marek Szyprowski Tested-by: Anand Moon Tested-by: Jochen Sprickerhof Acked-by: Felipe Balbi Link: https://lore.kernel.org/r/20190829053028.32438-3-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f56d5058a66c..999ce5e84d3c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -168,7 +168,6 @@ static void __dwc3_set_mode(struct work_struct *work) otg_set_vbus(dwc->usb2_phy->otg, true); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); - phy_calibrate(dwc->usb2_generic_phy); } break; case DWC3_GCTL_PRTCAP_DEVICE: @@ -1177,7 +1176,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dev_err(dev, "failed to initialize host\n"); return ret; } - phy_calibrate(dwc->usb2_generic_phy); break; case USB_DR_MODE_OTG: INIT_WORK(&dwc->drd_work, __dwc3_set_mode); From 6ed151f264841d5d3453e2c68c7e711b639ccde5 Mon Sep 17 00:00:00 2001 From: Saranya Gopal Date: Thu, 29 Aug 2019 17:25:59 +0530 Subject: [PATCH 142/159] xhci-ext-caps.c: Add property to disable Intel SW switch In platforms like Cherrytrail, 'SW switch enable' bit should not be enabled for role switch. This patch adds a property to Intel USB Role Switch platform driver to denote that SW switch should be disabled in Cherrytrail devices. Signed-off-by: Saranya Gopal Signed-off-by: Balaji Manoharan Suggested-by: Heikki Krogerus Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/1567079760-24822-1-git-send-email-saranya.gopal@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ext-caps.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c index 399113f9fc5c..f498160df969 100644 --- a/drivers/usb/host/xhci-ext-caps.c +++ b/drivers/usb/host/xhci-ext-caps.c @@ -6,11 +6,20 @@ */ #include +#include +#include #include "xhci.h" #define USB_SW_DRV_NAME "intel_xhci_usb_sw" #define USB_SW_RESOURCE_SIZE 0x400 +#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 + +static const struct property_entry role_switch_props[] = { + PROPERTY_ENTRY_BOOL("sw_switch_disable"), + {}, +}; + static void xhci_intel_unregister_pdev(void *arg) { platform_device_unregister(arg); @@ -21,6 +30,7 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset) struct usb_hcd *hcd = xhci_to_hcd(xhci); struct device *dev = hcd->self.controller; struct platform_device *pdev; + struct pci_dev *pci = to_pci_dev(dev); struct resource res = { 0, }; int ret; @@ -43,6 +53,14 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset) return ret; } + if (pci->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { + ret = platform_device_add_properties(pdev, role_switch_props); + if (ret) { + dev_err(dev, "failed to register device properties\n"); + return ret; + } + } + pdev->dev.parent = dev; ret = platform_device_add(pdev); From 2be1fb64dfeb2b1753328a5c4ac2259e16e53efd Mon Sep 17 00:00:00 2001 From: Saranya Gopal Date: Thu, 29 Aug 2019 17:26:00 +0530 Subject: [PATCH 143/159] usb: roles: intel: Enable static DRD mode for role switch Enable static DRD mode in Intel platforms which guarantees successful role switch all the time. This fixes issues like software role switch failure after cold boot and issue with role switch when USB 3.0 cable is used. But, do not enable static DRD mode for Cherrytrail devices which rely on firmware for role switch. Signed-off-by: Saranya Gopal Signed-off-by: Balaji Manoharan Reviewed-by: Heikki Krogerus Reviewed-by: Hans de Goede Tested-by: Hans de Goede Link: https://lore.kernel.org/r/1567079760-24822-2-git-send-email-saranya.gopal@intel.com Signed-off-by: Greg Kroah-Hartman --- .../usb/roles/intel-xhci-usb-role-switch.c | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 277de96181f9..88d041601c51 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* register definition */ @@ -26,6 +27,12 @@ #define SW_VBUS_VALID BIT(24) #define SW_IDPIN_EN BIT(21) #define SW_IDPIN BIT(20) +#define SW_SWITCH_EN BIT(16) + +#define DRD_CONFIG_DYNAMIC 0 +#define DRD_CONFIG_STATIC_HOST 1 +#define DRD_CONFIG_STATIC_DEVICE 2 +#define DRD_CONFIG_MASK 3 #define DUAL_ROLE_CFG1 0x6c #define HOST_MODE BIT(29) @@ -37,6 +44,7 @@ struct intel_xhci_usb_data { struct usb_role_switch *role_sw; void __iomem *base; + bool enable_sw_switch; }; static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) @@ -45,6 +53,7 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) unsigned long timeout; acpi_status status; u32 glk, val; + u32 drd_config = DRD_CONFIG_DYNAMIC; /* * On many CHT devices ACPI event (_AEI) handlers read / modify / @@ -59,24 +68,35 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) pm_runtime_get_sync(dev); - /* Set idpin value as requested */ + /* + * Set idpin value as requested. + * Since some devices rely on firmware setting DRD_CONFIG and + * SW_SWITCH_EN bits to be zero for role switch, + * do not set these bits for those devices. + */ val = readl(data->base + DUAL_ROLE_CFG0); switch (role) { case USB_ROLE_NONE: val |= SW_IDPIN; val &= ~SW_VBUS_VALID; + drd_config = DRD_CONFIG_DYNAMIC; break; case USB_ROLE_HOST: val &= ~SW_IDPIN; val &= ~SW_VBUS_VALID; + drd_config = DRD_CONFIG_STATIC_HOST; break; case USB_ROLE_DEVICE: val |= SW_IDPIN; val |= SW_VBUS_VALID; + drd_config = DRD_CONFIG_STATIC_DEVICE; break; } val |= SW_IDPIN_EN; - + if (data->enable_sw_switch) { + val &= ~DRD_CONFIG_MASK; + val |= SW_SWITCH_EN | drd_config; + } writel(val, data->base + DUAL_ROLE_CFG0); acpi_release_global_lock(glk); @@ -147,6 +167,9 @@ static int intel_xhci_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + data->enable_sw_switch = !device_property_read_bool(dev, + "sw_switch_disable"); + data->role_sw = usb_role_switch_register(dev, &sw_desc); if (IS_ERR(data->role_sw)) return PTR_ERR(data->role_sw); From ea44d190764b4422af4d1c29eaeb9e69e353b406 Mon Sep 17 00:00:00 2001 From: Suwan Kim Date: Wed, 28 Aug 2019 12:27:41 +0900 Subject: [PATCH 144/159] usbip: Implement SG support to vhci-hcd and stub driver There are bugs on vhci with usb 3.0 storage device. In USB, each SG list entry buffer should be divisible by the bulk max packet size. But with native SG support, this problem doesn't matter because the SG buffer is treated as contiguous buffer. But without native SG support, USB storage driver breaks SG list into several URBs and the error occurs because of a buffer size of URB that cannot be divided by the bulk max packet size. The error situation is as follows. When USB Storage driver requests 31.5 KB data and has SG list which has 3584 bytes buffer followed by 7 4096 bytes buffer for some reason. USB Storage driver splits this SG list into several URBs because VHCI doesn't support SG and sends them separately. So the first URB buffer size is 3584 bytes. When receiving data from device, USB 3.0 device sends data packet of 1024 bytes size because the max packet size of BULK pipe is 1024 bytes. So device sends 4096 bytes. But the first URB buffer has only 3584 bytes buffer size. So host controller terminates the transfer even though there is more data to receive. So, vhci needs to support SG transfer to prevent this error. In this patch, vhci supports SG regardless of whether the server's host controller supports SG or not, because stub driver splits SG list into several URBs if the server's host controller doesn't support SG. To support SG, vhci sets URB_DMA_MAP_SG flag in urb->transfer_flags if URB has SG list and this flag will tell stub driver to use SG list. After receiving urb from stub driver, vhci clear URB_DMA_MAP_SG flag to avoid unnecessary DMA unmapping in HCD. vhci sends each SG list entry to stub driver. Then, stub driver sees the total length of the buffer and allocates SG table and pages according to the total buffer length calling sgl_alloc(). After stub driver receives completed URB, it again sends each SG list entry to vhci. If the server's host controller doesn't support SG, stub driver breaks a single SG request into several URBs and submits them to the server's host controller. When all the split URBs are completed, stub driver reassembles the URBs into a single return command and sends it to vhci. Moreover, in the situation where vhci supports SG, but stub driver does not, or vice versa, usbip works normally. Because there is no protocol modification, there is no problem in communication between server and client even if the one has a kernel without SG support. In the case of vhci supports SG and stub driver doesn't, because vhci sends only the total length of the buffer to stub driver as it did before the patch applied, stub driver only needs to allocate the required length of buffers using only kmalloc() regardless of whether vhci supports SG or not. But stub driver has to allocate buffer with kmalloc() as much as the total length of SG buffer which is quite huge when vhci sends SG request, so it has overhead in buffer allocation in this situation. If stub driver needs to send data buffer to vhci because of IN pipe, stub driver also sends only total length of buffer as metadata and then sends real data as vhci does. Then vhci receive data from stub driver and store it to the corresponding buffer of SG list entry. And for the case of stub driver supports SG and vhci doesn't, since the USB storage driver checks that vhci doesn't support SG and sends the request to stub driver by splitting the SG list into multiple URBs, stub driver allocates a buffer for each URB with kmalloc() as it did before this patch. * Test environment Test uses two difference machines and two different kernel version to make mismatch situation between the client and the server where vhci supports SG, but stub driver does not, or vice versa. All tests are conducted in both full SG support that both vhci and stub support SG and half SG support that is the mismatch situation. Test kernel version is 5.3-rc6 with commit "usb: add a HCD_DMA flag instead of guestimating DMA capabilities" to avoid unnecessary DMA mapping and unmapping. - Test kernel version - 5.3-rc6 with SG support - 5.1.20-200.fc29.x86_64 without SG support * SG support test - Test devices - Super-speed storage device - SanDisk Ultra USB 3.0 - High-speed storage device - SMI corporation USB 2.0 flash drive - Test description Test read and write operation of mass storage device that uses the BULK transfer. In test, the client reads and writes files whose size is over 1G and it works normally. * Regression test - Test devices - Super-speed device - Logitech Brio webcam - High-speed device - Logitech C920 HD Pro webcam - Full-speed device - Logitech bluetooth mouse - Britz BR-Orion speaker - Low-speed device - Logitech wired mouse - Test description Moving and click test for mouse. To test the webcam, use gnome-cheese. To test the speaker, play music and video on the client. All works normally. * VUDC compatibility test VUDC also works well with this patch. Tests are done with two USB gadget created by CONFIGFS USB gadget. Both use the BULK pipe. 1. Serial gadget 2. Mass storage gadget - Serial gadget test Serial gadget on the host sends and receives data using cat command on the /dev/ttyGS. The client uses minicom to communicate with the serial gadget. - Mass storage gadget test After connecting the gadget with vhci, use "dd" to test read and write operation on the client side. Read - dd if=/dev/sd iflag=direct of=/dev/null bs=1G count=1 Write - dd if= iflag=direct of=/dev/sd bs=1G count=1 Signed-off-by: Suwan Kim Acked-by: Shuah khan Link: https://lore.kernel.org/r/20190828032741.12234-1-suwan.kim027@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub.h | 7 +- drivers/usb/usbip/stub_main.c | 57 ++++++--- drivers/usb/usbip/stub_rx.c | 204 ++++++++++++++++++++++--------- drivers/usb/usbip/stub_tx.c | 99 +++++++++++---- drivers/usb/usbip/usbip_common.c | 59 ++++++--- drivers/usb/usbip/vhci_hcd.c | 12 +- drivers/usb/usbip/vhci_rx.c | 3 + drivers/usb/usbip/vhci_tx.c | 66 ++++++++-- 8 files changed, 380 insertions(+), 127 deletions(-) diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h index 35618ceb2791..d11270560c24 100644 --- a/drivers/usb/usbip/stub.h +++ b/drivers/usb/usbip/stub.h @@ -52,7 +52,11 @@ struct stub_priv { unsigned long seqnum; struct list_head list; struct stub_device *sdev; - struct urb *urb; + struct urb **urbs; + struct scatterlist *sgl; + int num_urbs; + int completed_urbs; + int urb_status; int unlinking; }; @@ -86,6 +90,7 @@ extern struct usb_device_driver stub_driver; struct bus_id_priv *get_busid_priv(const char *busid); void put_busid_priv(struct bus_id_priv *bid); int del_match_busid(char *busid); +void stub_free_priv_and_urb(struct stub_priv *priv); void stub_device_cleanup_urbs(struct stub_device *sdev); /* stub_rx.c */ diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 2e4bfccd4bfc..c1c0bbc9f8b1 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "usbip_common.h" #include "stub.h" @@ -281,13 +282,49 @@ static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) struct stub_priv *priv, *tmp; list_for_each_entry_safe(priv, tmp, listhead, list) { - list_del(&priv->list); + list_del_init(&priv->list); return priv; } return NULL; } +void stub_free_priv_and_urb(struct stub_priv *priv) +{ + struct urb *urb; + int i; + + for (i = 0; i < priv->num_urbs; i++) { + urb = priv->urbs[i]; + + if (!urb) + return; + + kfree(urb->setup_packet); + urb->setup_packet = NULL; + + + if (urb->transfer_buffer && !priv->sgl) { + kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; + } + + if (urb->num_sgs) { + sgl_free(urb->sg); + urb->sg = NULL; + urb->num_sgs = 0; + } + + usb_free_urb(urb); + } + if (!list_empty(&priv->list)) + list_del(&priv->list); + if (priv->sgl) + sgl_free(priv->sgl); + kfree(priv->urbs); + kmem_cache_free(stub_priv_cache, priv); +} + static struct stub_priv *stub_priv_pop(struct stub_device *sdev) { unsigned long flags; @@ -314,25 +351,15 @@ done: void stub_device_cleanup_urbs(struct stub_device *sdev) { struct stub_priv *priv; - struct urb *urb; + int i; dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n"); while ((priv = stub_priv_pop(sdev))) { - urb = priv->urb; - dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n", - priv->seqnum); - usb_kill_urb(urb); + for (i = 0; i < priv->num_urbs; i++) + usb_kill_urb(priv->urbs[i]); - kmem_cache_free(stub_priv_cache, priv); - - kfree(urb->transfer_buffer); - urb->transfer_buffer = NULL; - - kfree(urb->setup_packet); - urb->setup_packet = NULL; - - usb_free_urb(urb); + stub_free_priv_and_urb(priv); } } diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index b0a855acafa3..66edfeea68fe 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "usbip_common.h" #include "stub.h" @@ -201,7 +202,7 @@ static void tweak_special_requests(struct urb *urb) static int stub_recv_cmd_unlink(struct stub_device *sdev, struct usbip_header *pdu) { - int ret; + int ret, i; unsigned long flags; struct stub_priv *priv; @@ -246,12 +247,14 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev, * so a driver in a client host will know the failure * of the unlink request ? */ - ret = usb_unlink_urb(priv->urb); - if (ret != -EINPROGRESS) - dev_err(&priv->urb->dev->dev, - "failed to unlink a urb # %lu, ret %d\n", - priv->seqnum, ret); - + for (i = priv->completed_urbs; i < priv->num_urbs; i++) { + ret = usb_unlink_urb(priv->urbs[i]); + if (ret != -EINPROGRESS) + dev_err(&priv->urbs[i]->dev->dev, + "failed to unlink %d/%d urb of seqnum %lu, ret %d\n", + i + 1, priv->num_urbs, + priv->seqnum, ret); + } return 0; } @@ -433,14 +436,36 @@ static void masking_bogus_flags(struct urb *urb) urb->transfer_flags &= allowed; } +static int stub_recv_xbuff(struct usbip_device *ud, struct stub_priv *priv) +{ + int ret; + int i; + + for (i = 0; i < priv->num_urbs; i++) { + ret = usbip_recv_xbuff(ud, priv->urbs[i]); + if (ret < 0) + break; + } + + return ret; +} + static void stub_recv_cmd_submit(struct stub_device *sdev, struct usbip_header *pdu) { - int ret; struct stub_priv *priv; struct usbip_device *ud = &sdev->ud; struct usb_device *udev = sdev->udev; + struct scatterlist *sgl = NULL, *sg; + void *buffer = NULL; + unsigned long long buf_len; + int nents; + int num_urbs = 1; int pipe = get_pipe(sdev, pdu); + int use_sg = pdu->u.cmd_submit.transfer_flags & URB_DMA_MAP_SG; + int support_sg = 1; + int np = 0; + int ret, i; if (pipe == -1) return; @@ -449,76 +474,139 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, if (!priv) return; - /* setup a urb */ - if (usb_pipeisoc(pipe)) - priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, - GFP_KERNEL); - else - priv->urb = usb_alloc_urb(0, GFP_KERNEL); - - if (!priv->urb) { - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return; - } + buf_len = (unsigned long long)pdu->u.cmd_submit.transfer_buffer_length; /* allocate urb transfer buffer, if needed */ - if (pdu->u.cmd_submit.transfer_buffer_length > 0) { - priv->urb->transfer_buffer = - kzalloc(pdu->u.cmd_submit.transfer_buffer_length, - GFP_KERNEL); - if (!priv->urb->transfer_buffer) { - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return; + if (buf_len) { + if (use_sg) { + sgl = sgl_alloc(buf_len, GFP_KERNEL, &nents); + if (!sgl) + goto err_malloc; + } else { + buffer = kzalloc(buf_len, GFP_KERNEL); + if (!buffer) + goto err_malloc; } } - /* copy urb setup packet */ - priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, - GFP_KERNEL); - if (!priv->urb->setup_packet) { - dev_err(&udev->dev, "allocate setup_packet\n"); - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return; + /* Check if the server's HCD supports SG */ + if (use_sg && !udev->bus->sg_tablesize) { + /* + * If the server's HCD doesn't support SG, break a single SG + * request into several URBs and map each SG list entry to + * corresponding URB buffer. The previously allocated SG + * list is stored in priv->sgl (If the server's HCD support SG, + * SG list is stored only in urb->sg) and it is used as an + * indicator that the server split single SG request into + * several URBs. Later, priv->sgl is used by stub_complete() and + * stub_send_ret_submit() to reassemble the divied URBs. + */ + support_sg = 0; + num_urbs = nents; + priv->completed_urbs = 0; + pdu->u.cmd_submit.transfer_flags &= ~URB_DMA_MAP_SG; } - /* set other members from the base header of pdu */ - priv->urb->context = (void *) priv; - priv->urb->dev = udev; - priv->urb->pipe = pipe; - priv->urb->complete = stub_complete; + /* allocate urb array */ + priv->num_urbs = num_urbs; + priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL); + if (!priv->urbs) + goto err_urbs; - usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); + /* setup a urb */ + if (support_sg) { + if (usb_pipeisoc(pipe)) + np = pdu->u.cmd_submit.number_of_packets; + priv->urbs[0] = usb_alloc_urb(np, GFP_KERNEL); + if (!priv->urbs[0]) + goto err_urb; - if (usbip_recv_xbuff(ud, priv->urb) < 0) + if (buf_len) { + if (use_sg) { + priv->urbs[0]->sg = sgl; + priv->urbs[0]->num_sgs = nents; + priv->urbs[0]->transfer_buffer = NULL; + } else { + priv->urbs[0]->transfer_buffer = buffer; + } + } + + /* copy urb setup packet */ + priv->urbs[0]->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, + 8, GFP_KERNEL); + if (!priv->urbs[0]->setup_packet) { + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + usbip_pack_pdu(pdu, priv->urbs[0], USBIP_CMD_SUBMIT, 0); + } else { + for_each_sg(sgl, sg, nents, i) { + priv->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + /* The URBs which is previously allocated will be freed + * in stub_device_cleanup_urbs() if error occurs. + */ + if (!priv->urbs[i]) + goto err_urb; + + usbip_pack_pdu(pdu, priv->urbs[i], USBIP_CMD_SUBMIT, 0); + priv->urbs[i]->transfer_buffer = sg_virt(sg); + priv->urbs[i]->transfer_buffer_length = sg->length; + } + priv->sgl = sgl; + } + + for (i = 0; i < num_urbs; i++) { + /* set other members from the base header of pdu */ + priv->urbs[i]->context = (void *) priv; + priv->urbs[i]->dev = udev; + priv->urbs[i]->pipe = pipe; + priv->urbs[i]->complete = stub_complete; + + /* no need to submit an intercepted request, but harmless? */ + tweak_special_requests(priv->urbs[i]); + + masking_bogus_flags(priv->urbs[i]); + } + + if (stub_recv_xbuff(ud, priv) < 0) return; - if (usbip_recv_iso(ud, priv->urb) < 0) + if (usbip_recv_iso(ud, priv->urbs[0]) < 0) return; - /* no need to submit an intercepted request, but harmless? */ - tweak_special_requests(priv->urb); - - masking_bogus_flags(priv->urb); /* urb is now ready to submit */ - ret = usb_submit_urb(priv->urb, GFP_KERNEL); + for (i = 0; i < priv->num_urbs; i++) { + ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL); - if (ret == 0) - usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", - pdu->base.seqnum); - else { - dev_err(&udev->dev, "submit_urb error, %d\n", ret); - usbip_dump_header(pdu); - usbip_dump_urb(priv->urb); + if (ret == 0) + usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", + pdu->base.seqnum); + else { + dev_err(&udev->dev, "submit_urb error, %d\n", ret); + usbip_dump_header(pdu); + usbip_dump_urb(priv->urbs[i]); - /* - * Pessimistic. - * This connection will be discarded. - */ - usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + /* + * Pessimistic. + * This connection will be discarded. + */ + usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + break; + } } usbip_dbg_stub_rx("Leave\n"); + return; + +err_urb: + kfree(priv->urbs); +err_urbs: + kfree(buffer); + sgl_free(sgl); +err_malloc: + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); } /* recv a pdu */ diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index f0ec41a50cbc..36010a82b359 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -5,25 +5,11 @@ #include #include +#include #include "usbip_common.h" #include "stub.h" -static void stub_free_priv_and_urb(struct stub_priv *priv) -{ - struct urb *urb = priv->urb; - - kfree(urb->setup_packet); - urb->setup_packet = NULL; - - kfree(urb->transfer_buffer); - urb->transfer_buffer = NULL; - - list_del(&priv->list); - kmem_cache_free(stub_priv_cache, priv); - usb_free_urb(urb); -} - /* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, __u32 status) @@ -85,6 +71,22 @@ void stub_complete(struct urb *urb) break; } + /* + * If the server breaks single SG request into the several URBs, the + * URBs must be reassembled before sending completed URB to the vhci. + * Don't wake up the tx thread until all the URBs are completed. + */ + if (priv->sgl) { + priv->completed_urbs++; + + /* Only save the first error status */ + if (urb->status && !priv->urb_status) + priv->urb_status = urb->status; + + if (priv->completed_urbs < priv->num_urbs) + return; + } + /* link a urb to the queue of tx. */ spin_lock_irqsave(&sdev->priv_lock, flags); if (sdev->ud.tcp_socket == NULL) { @@ -156,18 +158,22 @@ static int stub_send_ret_submit(struct stub_device *sdev) size_t total_size = 0; while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { - int ret; - struct urb *urb = priv->urb; + struct urb *urb = priv->urbs[0]; struct usbip_header pdu_header; struct usbip_iso_packet_descriptor *iso_buffer = NULL; struct kvec *iov = NULL; + struct scatterlist *sg; + u32 actual_length = 0; int iovnum = 0; + int ret; + int i; txsize = 0; memset(&pdu_header, 0, sizeof(pdu_header)); memset(&msg, 0, sizeof(msg)); - if (urb->actual_length > 0 && !urb->transfer_buffer) { + if (urb->actual_length > 0 && !urb->transfer_buffer && + !urb->num_sgs) { dev_err(&sdev->udev->dev, "urb: actual_length %d transfer_buffer null\n", urb->actual_length); @@ -176,6 +182,11 @@ static int stub_send_ret_submit(struct stub_device *sdev) if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) iovnum = 2 + urb->number_of_packets; + else if (usb_pipein(urb->pipe) && urb->actual_length > 0 && + urb->num_sgs) + iovnum = 1 + urb->num_sgs; + else if (usb_pipein(urb->pipe) && priv->sgl) + iovnum = 1 + priv->num_urbs; else iovnum = 2; @@ -192,6 +203,15 @@ static int stub_send_ret_submit(struct stub_device *sdev) setup_ret_submit_pdu(&pdu_header, urb); usbip_dbg_stub_tx("setup txdata seqnum: %d\n", pdu_header.base.seqnum); + + if (priv->sgl) { + for (i = 0; i < priv->num_urbs; i++) + actual_length += priv->urbs[i]->actual_length; + + pdu_header.u.ret_submit.status = priv->urb_status; + pdu_header.u.ret_submit.actual_length = actual_length; + } + usbip_header_correct_endian(&pdu_header, 1); iov[iovnum].iov_base = &pdu_header; @@ -200,12 +220,47 @@ static int stub_send_ret_submit(struct stub_device *sdev) txsize += sizeof(pdu_header); /* 2. setup transfer buffer */ - if (usb_pipein(urb->pipe) && + if (usb_pipein(urb->pipe) && priv->sgl) { + /* If the server split a single SG request into several + * URBs because the server's HCD doesn't support SG, + * reassemble the split URB buffers into a single + * return command. + */ + for (i = 0; i < priv->num_urbs; i++) { + iov[iovnum].iov_base = + priv->urbs[i]->transfer_buffer; + iov[iovnum].iov_len = + priv->urbs[i]->actual_length; + iovnum++; + } + txsize += actual_length; + } else if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && urb->actual_length > 0) { - iov[iovnum].iov_base = urb->transfer_buffer; - iov[iovnum].iov_len = urb->actual_length; - iovnum++; + if (urb->num_sgs) { + unsigned int copy = urb->actual_length; + int size; + + for_each_sg(urb->sg, sg, urb->num_sgs, i) { + if (copy == 0) + break; + + if (copy < sg->length) + size = copy; + else + size = sg->length; + + iov[iovnum].iov_base = sg_virt(sg); + iov[iovnum].iov_len = size; + + iovnum++; + copy -= size; + } + } else { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; + } txsize += urb->actual_length; } else if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 45da3e01c7b0..6532d68e8808 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -680,8 +680,12 @@ EXPORT_SYMBOL_GPL(usbip_pad_iso); /* some members of urb must be substituted before. */ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) { - int ret; + struct scatterlist *sg; + int ret = 0; + int recv; int size; + int copy; + int i; if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) { /* the direction of urb must be OUT. */ @@ -701,29 +705,48 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) if (!(size > 0)) return 0; - if (size > urb->transfer_buffer_length) { + if (size > urb->transfer_buffer_length) /* should not happen, probably malicious packet */ - if (ud->side == USBIP_STUB) { - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - return 0; - } else { - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - return -EPIPE; - } - } + goto error; - ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); - if (ret != size) { - dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); - if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) { - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - } else { - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - return -EPIPE; + if (urb->num_sgs) { + copy = size; + for_each_sg(urb->sg, sg, urb->num_sgs, i) { + int recv_size; + + if (copy < sg->length) + recv_size = copy; + else + recv_size = sg->length; + + recv = usbip_recv(ud->tcp_socket, sg_virt(sg), + recv_size); + + if (recv != recv_size) + goto error; + + copy -= recv; + ret += recv; } + + if (ret != size) + goto error; + } else { + ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); + if (ret != size) + goto error; } return ret; + +error: + dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); + if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; } EXPORT_SYMBOL_GPL(usbip_recv_xbuff); diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 000ab7225717..585a84d319bd 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -697,7 +697,8 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag } vdev = &vhci_hcd->vdev[portnum-1]; - if (!urb->transfer_buffer && urb->transfer_buffer_length) { + if (!urb->transfer_buffer && !urb->num_sgs && + urb->transfer_buffer_length) { dev_dbg(dev, "Null URB transfer buffer\n"); return -EINVAL; } @@ -1143,6 +1144,15 @@ static int vhci_setup(struct usb_hcd *hcd) hcd->speed = HCD_USB3; hcd->self.root_hub->speed = USB_SPEED_SUPER; } + + /* + * Support SG. + * sg_tablesize is an arbitrary value to alleviate memory pressure + * on the host. + */ + hcd->self.sg_tablesize = 32; + hcd->self.no_sg_constraint = 1; + return 0; } diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 44cd64518925..33f8972ba842 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -90,6 +90,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); + if (urb->num_sgs) + urb->transfer_flags &= ~URB_DMA_MAP_SG; + usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum); spin_lock_irqsave(&vhci->lock, flags); diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index 2fa26d0578d7..c3803785f6ef 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -5,6 +5,7 @@ #include #include +#include #include "usbip_common.h" #include "vhci.h" @@ -50,19 +51,23 @@ static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) static int vhci_send_cmd_submit(struct vhci_device *vdev) { + struct usbip_iso_packet_descriptor *iso_buffer = NULL; struct vhci_priv *priv = NULL; + struct scatterlist *sg; struct msghdr msg; - struct kvec iov[3]; + struct kvec *iov; size_t txsize; size_t total_size = 0; + int iovnum; + int err = -ENOMEM; + int i; while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { int ret; struct urb *urb = priv->urb; struct usbip_header pdu_header; - struct usbip_iso_packet_descriptor *iso_buffer = NULL; txsize = 0; memset(&pdu_header, 0, sizeof(pdu_header)); @@ -72,18 +77,45 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n", priv->seqnum); + if (urb->num_sgs && usb_pipeout(urb->pipe)) + iovnum = 2 + urb->num_sgs; + else + iovnum = 3; + + iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL); + if (!iov) { + usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -ENOMEM; + } + + if (urb->num_sgs) + urb->transfer_flags |= URB_DMA_MAP_SG; + /* 1. setup usbip_header */ setup_cmd_submit_pdu(&pdu_header, urb); usbip_header_correct_endian(&pdu_header, 1); + iovnum = 0; - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); txsize += sizeof(pdu_header); + iovnum++; /* 2. setup transfer buffer */ if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { - iov[1].iov_base = urb->transfer_buffer; - iov[1].iov_len = urb->transfer_buffer_length; + if (urb->num_sgs && + !usb_endpoint_xfer_isoc(&urb->ep->desc)) { + for_each_sg(urb->sg, sg, urb->num_sgs, i) { + iov[iovnum].iov_base = sg_virt(sg); + iov[iovnum].iov_len = sg->length; + iovnum++; + } + } else { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = + urb->transfer_buffer_length; + iovnum++; + } txsize += urb->transfer_buffer_length; } @@ -95,23 +127,26 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) if (!iso_buffer) { usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC); - return -1; + goto err_iso_buffer; } - iov[2].iov_base = iso_buffer; - iov[2].iov_len = len; + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; + iovnum++; txsize += len; } - ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, iovnum, + txsize); if (ret != txsize) { pr_err("sendmsg failed!, ret=%d for %zd\n", ret, txsize); - kfree(iso_buffer); usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); - return -1; + err = -EPIPE; + goto err_tx; } + kfree(iov); kfree(iso_buffer); usbip_dbg_vhci_tx("send txdata\n"); @@ -119,6 +154,13 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) } return total_size; + +err_tx: + kfree(iso_buffer); +err_iso_buffer: + kfree(iov); + + return err; } static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) From 451d15a1731c7db9d4868ea084174346e8ffe0dd Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:28 +0800 Subject: [PATCH 145/159] dt-binding: usb: add usb-role-switch property Add a property usb-role-switch to tell the driver that use USB Role Switch framework to handle the role switch, it's useful when the driver has already supported other ways, such as extcon framework etc. Cc: Biju Das Cc: Yu Chen Signed-off-by: Chunfeng Yun Reviewed-by: Rob Herring Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/1567070558-29417-2-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt index 0a74ab8dfdc2..cf5a1ad456e6 100644 --- a/Documentation/devicetree/bindings/usb/generic.txt +++ b/Documentation/devicetree/bindings/usb/generic.txt @@ -30,6 +30,10 @@ Optional properties: optional for OTG device. - adp-disable: tells OTG controllers we want to disable OTG ADP, ADP is optional for OTG device. + - usb-role-switch: boolean, indicates that the device is capable of assigning + the USB data role (USB host or USB device) for a given + USB connector, such as Type-C, Type-B(micro). + see connector/usb-connector.txt. This is an attribute to a USB controller such as: From 6b68240d7c54f314da720d2dbb063d5d41e5d10c Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:29 +0800 Subject: [PATCH 146/159] dt-bindings: connector: add optional properties for Type-B Add id-gpios, vbus-gpios, vbus-supply and pinctrl properties for usb-b-connector Signed-off-by: Chunfeng Yun Reviewed-by: Rob Herring Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/1567070558-29417-3-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/connector/usb-connector.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt index cef556d4e5ee..d357987181ee 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.txt +++ b/Documentation/devicetree/bindings/connector/usb-connector.txt @@ -17,6 +17,20 @@ Optional properties: - self-powered: Set this property if the usb device that has its own power source. +Optional properties for usb-b-connector: +- id-gpios: an input gpio for USB ID pin. +- vbus-gpios: an input gpio for USB VBUS pin, used to detect presence of + VBUS 5V. + see gpio/gpio.txt. +- vbus-supply: a phandle to the regulator for USB VBUS if needed when host + mode or dual role mode is supported. + Particularly, if use an output GPIO to control a VBUS regulator, should + model it as a regulator. + see regulator/fixed-regulator.yaml +- pinctrl-names : a pinctrl state named "default" is optional +- pinctrl-0 : pin control group + see pinctrl/pinctrl-bindings.txt + Optional properties for usb-c-connector: - power-role: should be one of "source", "sink" or "dual"(DRP) if typec connector has power support. From f651c73e71f53f65e9846677d79d8e120452b59f Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:30 +0800 Subject: [PATCH 147/159] dt-bindings: usb: add binding for USB GPIO based connection detection driver It's used to support dual role switch via GPIO when use Type-B receptacle, typically the USB ID pin is connected to an input GPIO, and also used to enable/disable device when the USB Vbus pin is connected to an input GPIO. Signed-off-by: Chunfeng Yun Reviewed-by: Linus Walleij Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1567070558-29417-4-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/usb-conn-gpio.txt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/usb-conn-gpio.txt diff --git a/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt b/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt new file mode 100644 index 000000000000..3d05ae56cb0d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt @@ -0,0 +1,30 @@ +USB GPIO Based Connection Detection + +This is typically used to switch dual role mode from the USB ID pin connected +to an input GPIO, and also used to enable/disable device mode from the USB +Vbus pin connected to an input GPIO. + +Required properties: +- compatible : should include "gpio-usb-b-connector" and "usb-b-connector". +- id-gpios, vbus-gpios : input gpios, either one of them must be present, + and both can be present as well. + see connector/usb-connector.txt + +Optional properties: +- vbus-supply : can be present if needed when supports dual role mode. + see connector/usb-connector.txt + +- Sub-nodes: + - port : can be present. + see graph.txt + +Example: + +&mtu3 { + connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + type = "micro"; + id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; + vbus-supply = <&usb_p0_vbus>; + }; +}; From 3ef8685f84b316ea0e6ad00d65fa5f932d995c64 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:31 +0800 Subject: [PATCH 148/159] dt-bindings: usb: mtu3: add properties about USB Role Switch Now the USB Role Switch is supported, so add properties about it, and modify some description related. Signed-off-by: Chunfeng Yun Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1567070558-29417-5-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/mediatek,mtu3.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt index 5d740e9d4525..b9af7f5ee91d 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt @@ -28,8 +28,13 @@ Optional properties: parent's address space - extcon : external connector for vbus and idpin changes detection, needed when supports dual-role mode. + it's considered valid for compatibility reasons, not allowed for + new bindings, and use "usb-role-switch" property instead. - vbus-supply : reference to the VBUS regulator, needed when supports dual-role mode. + it's considered valid for compatibility reasons, not allowed for + new bindings, and put into a usb-connector node. + see connector/usb-connector.txt. - pinctrl-names : a pinctrl state named "default" is optional, and need be defined if auto drd switch is enabled, that means the property dr_mode is set as "otg", and meanwhile the property "mediatek,enable-manual-drd" @@ -39,6 +44,8 @@ Optional properties: - maximum-speed : valid arguments are "super-speed", "high-speed" and "full-speed"; refer to usb/generic.txt + - usb-role-switch : use USB Role Switch to support dual-role switch, but + not extcon; see usb/generic.txt. - enable-manual-drd : supports manual dual-role switch via debugfs; usually used when receptacle is TYPE-A and also wants to support dual-role mode. @@ -61,6 +68,9 @@ The xhci should be added as subnode to mtu3 as shown in the following example if host mode is enabled. The DT binding details of xhci can be found in: Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt +The port would be added as subnode if use "usb-role-switch" property. + see graph.txt + Example: ssusb: usb@11271000 { compatible = "mediatek,mt8173-mtu3"; From 977607653cdd06a2c4d1d0f179ff46cc0cbe83a5 Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Thu, 29 Aug 2019 17:22:32 +0800 Subject: [PATCH 149/159] usb: roles: Introduce stubs for the exiting functions in role.h This patch adds stubs for the exiting functions while CONFIG_USB_ROLE_SWITCH does not enabled. Cc: Greg Kroah-Hartman Cc: Heikki Krogerus Cc: Hans de Goede Cc: Andy Shevchenko Cc: John Stultz Reviewed-by: Heikki Krogerus Signed-off-by: Yu Chen Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1567070558-29417-6-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/role.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index c05ffa6abda9..da2b9641b877 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -42,6 +42,8 @@ struct usb_role_switch_desc { bool allow_userspace_control; }; + +#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role); enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw); struct usb_role_switch *usb_role_switch_get(struct device *dev); @@ -51,5 +53,33 @@ struct usb_role_switch * usb_role_switch_register(struct device *parent, const struct usb_role_switch_desc *desc); void usb_role_switch_unregister(struct usb_role_switch *sw); +#else +static inline int usb_role_switch_set_role(struct usb_role_switch *sw, + enum usb_role role) +{ + return 0; +} + +static inline enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) +{ + return USB_ROLE_NONE; +} + +static inline struct usb_role_switch *usb_role_switch_get(struct device *dev) +{ + return ERR_PTR(-ENODEV); +} + +static inline void usb_role_switch_put(struct usb_role_switch *sw) { } + +static inline struct usb_role_switch * +usb_role_switch_register(struct device *parent, + const struct usb_role_switch_desc *desc) +{ + return ERR_PTR(-ENODEV); +} + +static inline void usb_role_switch_unregister(struct usb_role_switch *sw) { } +#endif #endif /* __LINUX_USB_ROLE_H */ From 44493062abc38e5895e0cf8d22698f2ca39a1e4d Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 29 Aug 2019 17:22:33 +0800 Subject: [PATCH 150/159] device connection: Add fwnode_connection_find_match() The fwnode_connection_find_match() function is exactly the same as device_connection_find_match(), except it takes struct fwnode_handle as parameter instead of struct device. That allows locating device connections before the device entries have been created. Signed-off-by: Heikki Krogerus Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1567070558-29417-7-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/devcon.c | 43 ++++++++++++++++++++++++++++++------------ include/linux/device.h | 10 +++++++--- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/drivers/base/devcon.c b/drivers/base/devcon.c index 09f28479b243..986ad89de2f9 100644 --- a/drivers/base/devcon.c +++ b/drivers/base/devcon.c @@ -12,9 +12,6 @@ static DEFINE_MUTEX(devcon_lock); static LIST_HEAD(devcon_list); -typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep, - void *data); - static void * fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id, void *data, devcon_match_fn_t match) @@ -60,6 +57,34 @@ fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id, return NULL; } +/** + * fwnode_connection_find_match - Find connection from a device node + * @fwnode: Device node with the connection + * @con_id: Identifier for the connection + * @data: Data for the match function + * @match: Function to check and convert the connection description + * + * Find a connection with unique identifier @con_id between @fwnode and another + * device node. @match will be used to convert the connection description to + * data the caller is expecting to be returned. + */ +void *fwnode_connection_find_match(struct fwnode_handle *fwnode, + const char *con_id, void *data, + devcon_match_fn_t match) +{ + void *ret; + + if (!fwnode || !match) + return NULL; + + ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); + if (ret) + return ret; + + return fwnode_devcon_match(fwnode, con_id, data, match); +} +EXPORT_SYMBOL_GPL(fwnode_connection_find_match); + /** * device_connection_find_match - Find physical connection to a device * @dev: Device with the connection @@ -83,15 +108,9 @@ void *device_connection_find_match(struct device *dev, const char *con_id, if (!match) return NULL; - if (fwnode) { - ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); - if (ret) - return ret; - - ret = fwnode_devcon_match(fwnode, con_id, data, match); - if (ret) - return ret; - } + ret = fwnode_connection_find_match(fwnode, con_id, data, match); + if (ret) + return ret; mutex_lock(&devcon_lock); diff --git a/include/linux/device.h b/include/linux/device.h index 47ccb2029bc3..b3aa7295b4cb 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -781,10 +781,14 @@ struct device_connection { struct list_head list; }; +typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep, + void *data); + +void *fwnode_connection_find_match(struct fwnode_handle *fwnode, + const char *con_id, void *data, + devcon_match_fn_t match); void *device_connection_find_match(struct device *dev, const char *con_id, - void *data, - void *(*match)(struct device_connection *con, - int ep, void *data)); + void *data, devcon_match_fn_t match); struct device *device_connection_find(struct device *dev, const char *con_id); From a31f01777bc54a0a9d57628956d05f8d454d3418 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 29 Aug 2019 17:22:34 +0800 Subject: [PATCH 151/159] usb: roles: Add fwnode_usb_role_switch_get() function The fwnode_usb_role_switch_get() function is exactly the same as usb_role_switch_get(), except that it takes struct fwnode_handle as parameter instead of struct device. Signed-off-by: Heikki Krogerus Signed-off-by: Chunfeng Yun Tested-by: Biju Das Link: https://lore.kernel.org/r/1567070558-29417-8-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 20 ++++++++++++++++++++ include/linux/usb/role.h | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 86defca6623e..5b637aaf311f 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -135,6 +135,26 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev) } EXPORT_SYMBOL_GPL(usb_role_switch_get); +/** + * fwnode_usb_role_switch_get - Find USB role switch linked with the caller + * @fwnode: The caller device node + * + * This is similar to the usb_role_switch_get() function above, but it searches + * the switch using fwnode instead of device entry. + */ +struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode) +{ + struct usb_role_switch *sw; + + sw = fwnode_connection_find_match(fwnode, "usb-role-switch", NULL, + usb_role_switch_match); + if (!IS_ERR_OR_NULL(sw)) + WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); + + return sw; +} +EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get); + /** * usb_role_switch_put - Release handle to a switch * @sw: USB Role Switch diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index da2b9641b877..2d77f97df72d 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -47,6 +47,7 @@ struct usb_role_switch_desc { int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role); enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw); struct usb_role_switch *usb_role_switch_get(struct device *dev); +struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *node); void usb_role_switch_put(struct usb_role_switch *sw); struct usb_role_switch * @@ -70,6 +71,12 @@ static inline struct usb_role_switch *usb_role_switch_get(struct device *dev) return ERR_PTR(-ENODEV); } +static inline struct usb_role_switch * +fwnode_usb_role_switch_get(struct fwnode_handle *node) +{ + return ERR_PTR(-ENODEV); +} + static inline void usb_role_switch_put(struct usb_role_switch *sw) { } static inline struct usb_role_switch * From 6fadd72943b80ff5bef175aacc2458c8a9ee3dbc Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:35 +0800 Subject: [PATCH 152/159] usb: roles: get usb-role-switch from parent when the USB host controller is the parent of the connector, usually type-B, sometimes don't need the graph, so we should check whether it's parent registers usb-role-switch or not firstly, and get it if exists. Suggested-by: Heikki Krogerus Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1567070558-29417-9-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 2abb6fe384ca..94b4e7db2b94 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -102,6 +102,19 @@ static void *usb_role_switch_match(struct device_connection *con, int ep, return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); } +static struct usb_role_switch * +usb_role_switch_is_parent(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *parent = fwnode_get_parent(fwnode); + struct device *dev; + + if (!parent || !fwnode_property_present(parent, "usb-role-switch")) + return NULL; + + dev = class_find_device_by_fwnode(role_class, parent); + return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); +} + /** * usb_role_switch_get - Find USB role switch linked with the caller * @dev: The caller device @@ -113,8 +126,10 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev) { struct usb_role_switch *sw; - sw = device_connection_find_match(dev, "usb-role-switch", NULL, - usb_role_switch_match); + sw = usb_role_switch_is_parent(dev_fwnode(dev)); + if (!sw) + sw = device_connection_find_match(dev, "usb-role-switch", NULL, + usb_role_switch_match); if (!IS_ERR_OR_NULL(sw)) WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); @@ -134,8 +149,10 @@ struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode) { struct usb_role_switch *sw; - sw = fwnode_connection_find_match(fwnode, "usb-role-switch", NULL, - usb_role_switch_match); + sw = usb_role_switch_is_parent(fwnode); + if (!sw) + sw = fwnode_connection_find_match(fwnode, "usb-role-switch", + NULL, usb_role_switch_match); if (!IS_ERR_OR_NULL(sw)) WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); From 12b591bccc463307a21d925c19e7f61982902e08 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:36 +0800 Subject: [PATCH 153/159] usb: common: create Kconfig file Create Kconfig file for USB common core, and move USB_LED_TRIG and USB_ULPI_BUS configs into the new file from the parent Kconfig, it will help to add new configs later. Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1567070558-29417-10-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 35 +---------------------------------- drivers/usb/common/Kconfig | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 drivers/usb/common/Kconfig diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index fc1c66af6f0a..275568abc670 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -36,8 +36,7 @@ menuconfig USB_SUPPORT if USB_SUPPORT -config USB_COMMON - tristate +source "drivers/usb/common/Kconfig" config USB_ARCH_HAS_HCD def_bool y @@ -175,36 +174,4 @@ source "drivers/usb/typec/Kconfig" source "drivers/usb/roles/Kconfig" -config USB_LED_TRIG - bool "USB LED Triggers" - depends on LEDS_CLASS && LEDS_TRIGGERS - select USB_COMMON - help - This option adds LED triggers for USB host and/or gadget activity. - - Say Y here if you are working on a system with led-class supported - LEDs and you want to use them as activity indicators for USB host or - gadget. - -config USB_ULPI_BUS - tristate "USB ULPI PHY interface support" - select USB_COMMON - help - UTMI+ Low Pin Interface (ULPI) is specification for a commonly used - USB 2.0 PHY interface. The ULPI specification defines a standard set - of registers that can be used to detect the vendor and product which - allows ULPI to be handled as a bus. This module is the driver for that - bus. - - The ULPI interfaces (the buses) are registered by the drivers for USB - controllers which support ULPI register access and have ULPI PHY - attached to them. The ULPI PHY drivers themselves are normal PHY - drivers. - - ULPI PHYs provide often functions such as ADP sensing/probing (OTG - protocol) and USB charger detection. - - To compile this driver as a module, choose M here: the module will - be called ulpi. - endif # USB_SUPPORT diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig new file mode 100644 index 000000000000..848545b099cf --- /dev/null +++ b/drivers/usb/common/Kconfig @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 + +config USB_COMMON + tristate + + +config USB_LED_TRIG + bool "USB LED Triggers" + depends on LEDS_CLASS && LEDS_TRIGGERS + select USB_COMMON + help + This option adds LED triggers for USB host and/or gadget activity. + + Say Y here if you are working on a system with led-class supported + LEDs and you want to use them as activity indicators for USB host or + gadget. + +config USB_ULPI_BUS + tristate "USB ULPI PHY interface support" + select USB_COMMON + help + UTMI+ Low Pin Interface (ULPI) is specification for a commonly used + USB 2.0 PHY interface. The ULPI specification defines a standard set + of registers that can be used to detect the vendor and product which + allows ULPI to be handled as a bus. This module is the driver for that + bus. + + The ULPI interfaces (the buses) are registered by the drivers for USB + controllers which support ULPI register access and have ULPI PHY + attached to them. The ULPI PHY drivers themselves are normal PHY + drivers. + + ULPI PHYs provide often functions such as ADP sensing/probing (OTG + protocol) and USB charger detection. + + To compile this driver as a module, choose M here: the module will + be called ulpi. + From 4602f3bff2669012c1147eecfe74c121765f5c56 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:37 +0800 Subject: [PATCH 154/159] usb: common: add USB GPIO based connection detection driver Due to the requirement of usb-connector.txt binding, the old way using extcon to support USB Dual-Role switch is now deprecated when use Type-B connector. This patch introduces a USB GPIO based connection detection driver, used to support Type-B connector which typically uses an input GPIO to detect USB ID pin, and try to replace the function provided by the extcon-usb-gpio driver Signed-off-by: Chunfeng Yun Tested-by: Nagarjuna Kristam Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/1567070558-29417-11-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/Kconfig | 13 ++ drivers/usb/common/Makefile | 1 + drivers/usb/common/usb-conn-gpio.c | 284 +++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 drivers/usb/common/usb-conn-gpio.c diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig index 848545b099cf..d611477aae41 100644 --- a/drivers/usb/common/Kconfig +++ b/drivers/usb/common/Kconfig @@ -36,3 +36,16 @@ config USB_ULPI_BUS To compile this driver as a module, choose M here: the module will be called ulpi. +config USB_CONN_GPIO + tristate "USB GPIO Based Connection Detection Driver" + depends on GPIOLIB + select USB_ROLE_SWITCH + help + The driver supports USB role switch between host and device via GPIO + based USB cable detection, used typically if an input GPIO is used + to detect USB ID pin, and another input GPIO may be also used to detect + Vbus pin at the same time, it also can be used to enable/disable + device if an input GPIO is only used to detect Vbus pin. + + To compile the driver as a module, choose M here: the module will + be called usb-conn-gpio.ko diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 76f3e80623db..8ac4d21ef5c8 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -8,5 +8,6 @@ usb-common-y += common.o usb-common-$(CONFIG_TRACING) += debug.o usb-common-$(CONFIG_USB_LED_TRIG) += led.o +obj-$(CONFIG_USB_CONN_GPIO) += usb-conn-gpio.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c new file mode 100644 index 000000000000..87338f9eb5be --- /dev/null +++ b/drivers/usb/common/usb-conn-gpio.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB GPIO Based Connection Detection Driver + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun + * + * Some code borrowed from drivers/extcon/extcon-usb-gpio.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_GPIO_DEB_MS 20 /* ms */ +#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */ + +#define USB_CONN_IRQF \ + (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT) + +struct usb_conn_info { + struct device *dev; + struct usb_role_switch *role_sw; + enum usb_role last_role; + struct regulator *vbus; + struct delayed_work dw_det; + unsigned long debounce_jiffies; + + struct gpio_desc *id_gpiod; + struct gpio_desc *vbus_gpiod; + int id_irq; + int vbus_irq; +}; + +/** + * "DEVICE" = VBUS and "HOST" = !ID, so we have: + * Both "DEVICE" and "HOST" can't be set as active at the same time + * so if "HOST" is active (i.e. ID is 0) we keep "DEVICE" inactive + * even if VBUS is on. + * + * Role | ID | VBUS + * ------------------------------------ + * [1] DEVICE | H | H + * [2] NONE | H | L + * [3] HOST | L | H + * [4] HOST | L | L + * + * In case we have only one of these signals: + * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1 + * - ID only - we want to distinguish between [1] and [4], so VBUS = ID + */ +static void usb_conn_detect_cable(struct work_struct *work) +{ + struct usb_conn_info *info; + enum usb_role role; + int id, vbus, ret; + + info = container_of(to_delayed_work(work), + struct usb_conn_info, dw_det); + + /* check ID and VBUS */ + id = info->id_gpiod ? + gpiod_get_value_cansleep(info->id_gpiod) : 1; + vbus = info->vbus_gpiod ? + gpiod_get_value_cansleep(info->vbus_gpiod) : id; + + if (!id) + role = USB_ROLE_HOST; + else if (vbus) + role = USB_ROLE_DEVICE; + else + role = USB_ROLE_NONE; + + dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n", + info->last_role, role, id, vbus); + + if (info->last_role == role) { + dev_warn(info->dev, "repeated role: %d\n", role); + return; + } + + if (info->last_role == USB_ROLE_HOST) + regulator_disable(info->vbus); + + ret = usb_role_switch_set_role(info->role_sw, role); + if (ret) + dev_err(info->dev, "failed to set role: %d\n", ret); + + if (role == USB_ROLE_HOST) { + ret = regulator_enable(info->vbus); + if (ret) + dev_err(info->dev, "enable vbus regulator failed\n"); + } + + info->last_role = role; + + dev_dbg(info->dev, "vbus regulator is %s\n", + regulator_is_enabled(info->vbus) ? "enabled" : "disabled"); +} + +static void usb_conn_queue_dwork(struct usb_conn_info *info, + unsigned long delay) +{ + queue_delayed_work(system_power_efficient_wq, &info->dw_det, delay); +} + +static irqreturn_t usb_conn_isr(int irq, void *dev_id) +{ + struct usb_conn_info *info = dev_id; + + usb_conn_queue_dwork(info, info->debounce_jiffies); + + return IRQ_HANDLED; +} + +static int usb_conn_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_conn_info *info; + int ret = 0; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = dev; + info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN); + if (IS_ERR(info->id_gpiod)) + return PTR_ERR(info->id_gpiod); + + info->vbus_gpiod = devm_gpiod_get_optional(dev, "vbus", GPIOD_IN); + if (IS_ERR(info->vbus_gpiod)) + return PTR_ERR(info->vbus_gpiod); + + if (!info->id_gpiod && !info->vbus_gpiod) { + dev_err(dev, "failed to get gpios\n"); + return -ENODEV; + } + + if (info->id_gpiod) + ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEB_US); + if (!ret && info->vbus_gpiod) + ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEB_US); + if (ret < 0) + info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEB_MS); + + INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable); + + info->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(info->vbus)) { + dev_err(dev, "failed to get vbus\n"); + return PTR_ERR(info->vbus); + } + + info->role_sw = usb_role_switch_get(dev); + if (IS_ERR(info->role_sw)) { + if (PTR_ERR(info->role_sw) != -EPROBE_DEFER) + dev_err(dev, "failed to get role switch\n"); + + return PTR_ERR(info->role_sw); + } + + if (info->id_gpiod) { + info->id_irq = gpiod_to_irq(info->id_gpiod); + if (info->id_irq < 0) { + dev_err(dev, "failed to get ID IRQ\n"); + ret = info->id_irq; + goto put_role_sw; + } + + ret = devm_request_threaded_irq(dev, info->id_irq, NULL, + usb_conn_isr, USB_CONN_IRQF, + pdev->name, info); + if (ret < 0) { + dev_err(dev, "failed to request ID IRQ\n"); + goto put_role_sw; + } + } + + if (info->vbus_gpiod) { + info->vbus_irq = gpiod_to_irq(info->vbus_gpiod); + if (info->vbus_irq < 0) { + dev_err(dev, "failed to get VBUS IRQ\n"); + ret = info->vbus_irq; + goto put_role_sw; + } + + ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL, + usb_conn_isr, USB_CONN_IRQF, + pdev->name, info); + if (ret < 0) { + dev_err(dev, "failed to request VBUS IRQ\n"); + goto put_role_sw; + } + } + + platform_set_drvdata(pdev, info); + + /* Perform initial detection */ + usb_conn_queue_dwork(info, 0); + + return 0; + +put_role_sw: + usb_role_switch_put(info->role_sw); + return ret; +} + +static int usb_conn_remove(struct platform_device *pdev) +{ + struct usb_conn_info *info = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&info->dw_det); + + if (info->last_role == USB_ROLE_HOST) + regulator_disable(info->vbus); + + usb_role_switch_put(info->role_sw); + + return 0; +} + +static int __maybe_unused usb_conn_suspend(struct device *dev) +{ + struct usb_conn_info *info = dev_get_drvdata(dev); + + if (info->id_gpiod) + disable_irq(info->id_irq); + if (info->vbus_gpiod) + disable_irq(info->vbus_irq); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int __maybe_unused usb_conn_resume(struct device *dev) +{ + struct usb_conn_info *info = dev_get_drvdata(dev); + + pinctrl_pm_select_default_state(dev); + + if (info->id_gpiod) + enable_irq(info->id_irq); + if (info->vbus_gpiod) + enable_irq(info->vbus_irq); + + usb_conn_queue_dwork(info, 0); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops, + usb_conn_suspend, usb_conn_resume); + +static const struct of_device_id usb_conn_dt_match[] = { + { .compatible = "gpio-usb-b-connector", }, + { } +}; +MODULE_DEVICE_TABLE(of, usb_conn_dt_match); + +static struct platform_driver usb_conn_driver = { + .probe = usb_conn_probe, + .remove = usb_conn_remove, + .driver = { + .name = "usb-conn-gpio", + .pm = &usb_conn_pm_ops, + .of_match_table = usb_conn_dt_match, + }, +}; + +module_platform_driver(usb_conn_driver); + +MODULE_AUTHOR("Chunfeng Yun "); +MODULE_DESCRIPTION("USB GPIO based connection detection driver"); +MODULE_LICENSE("GPL v2"); From 1ac91ac5d097c1928efe6691d72c619462a9e3c5 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Aug 2019 17:22:38 +0800 Subject: [PATCH 155/159] usb: mtu3: register a USB Role Switch for dual role mode Because extcon is not allowed for new bindings, and the dual role switch is supported by USB Role Switch, especially for Type-C drivers, so register a USB Role Switch to support the new way Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1567070558-29417-12-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/Kconfig | 1 + drivers/usb/mtu3/mtu3.h | 5 ++++ drivers/usb/mtu3/mtu3_debugfs.c | 4 +-- drivers/usb/mtu3/mtu3_dr.c | 48 ++++++++++++++++++++++++++++++++- drivers/usb/mtu3/mtu3_dr.h | 6 ++--- drivers/usb/mtu3/mtu3_plat.c | 3 ++- 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig index 928c2cd6fc00..bf98fd36341d 100644 --- a/drivers/usb/mtu3/Kconfig +++ b/drivers/usb/mtu3/Kconfig @@ -44,6 +44,7 @@ config USB_MTU3_DUAL_ROLE bool "Dual Role mode" depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3)) depends on (EXTCON=y || EXTCON=USB_MTU3) + select USB_ROLE_SWITCH help This is the default mode of working of MTU3 controller where both host and gadget features are enabled. diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 76ecf12fdf62..6087be236a35 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -199,6 +199,9 @@ struct mtu3_gpd_ring { * @id_nb : notifier for iddig(idpin) detection * @id_work : work of iddig detection notifier * @id_event : event of iddig detecion notifier +* @role_sw : use USB Role Switch to support dual-role switch, can't use +* extcon at the same time, and extcon is deprecated. +* @role_sw_used : true when the USB Role Switch is used. * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not * @manual_drd_enabled: it's true when supports dual-role device by debugfs * to switch host/device modes depending on user input. @@ -212,6 +215,8 @@ struct otg_switch_mtk { struct notifier_block id_nb; struct work_struct id_work; unsigned long id_event; + struct usb_role_switch *role_sw; + bool role_sw_used; bool is_u3_drd; bool manual_drd_enabled; }; diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c index 62c57ddc554e..c96e5dab0a48 100644 --- a/drivers/usb/mtu3/mtu3_debugfs.c +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -453,9 +453,9 @@ static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf, return -EFAULT; if (!strncmp(buf, "host", 4) && !ssusb->is_host) { - ssusb_mode_manual_switch(ssusb, 1); + ssusb_mode_switch(ssusb, 1); } else if (!strncmp(buf, "device", 6) && ssusb->is_host) { - ssusb_mode_manual_switch(ssusb, 0); + ssusb_mode_switch(ssusb, 0); } else { dev_err(ssusb->dev, "wrong or duplicated setting\n"); return -EINVAL; diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 5fcb71af875a..08e18448e8b8 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -7,6 +7,8 @@ * Author: Chunfeng Yun */ +#include + #include "mtu3.h" #include "mtu3_dr.h" #include "mtu3_debug.h" @@ -280,7 +282,7 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx) * This is useful in special cases, such as uses TYPE-A receptacle but also * wants to support dual-role mode. */ -void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) +void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; @@ -318,6 +320,47 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb, mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); } +static int ssusb_role_sw_set(struct device *dev, enum usb_role role) +{ + struct ssusb_mtk *ssusb = dev_get_drvdata(dev); + bool to_host = false; + + if (role == USB_ROLE_HOST) + to_host = true; + + if (to_host ^ ssusb->is_host) + ssusb_mode_switch(ssusb, to_host); + + return 0; +} + +static enum usb_role ssusb_role_sw_get(struct device *dev) +{ + struct ssusb_mtk *ssusb = dev_get_drvdata(dev); + enum usb_role role; + + role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE; + + return role; +} + +static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx) +{ + struct usb_role_switch_desc role_sx_desc = { 0 }; + struct ssusb_mtk *ssusb = + container_of(otg_sx, struct ssusb_mtk, otg_switch); + + if (!otg_sx->role_sw_used) + return 0; + + role_sx_desc.set = ssusb_role_sw_set; + role_sx_desc.get = ssusb_role_sw_get; + role_sx_desc.fwnode = dev_fwnode(ssusb->dev); + otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc); + + return PTR_ERR_OR_ZERO(otg_sx->role_sw); +} + int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; @@ -328,6 +371,8 @@ int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) if (otg_sx->manual_drd_enabled) ssusb_dr_debugfs_init(ssusb); + else if (otg_sx->role_sw_used) + ret = ssusb_role_sw_register(otg_sx); else ret = ssusb_extcon_register(otg_sx); @@ -340,4 +385,5 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) cancel_work_sync(&otg_sx->id_work); cancel_work_sync(&otg_sx->vbus_work); + usb_role_switch_unregister(otg_sx->role_sw); } diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index ba6fe357ce29..5e58c4dbd54a 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -71,7 +71,7 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb) #if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE) int ssusb_otg_switch_init(struct ssusb_mtk *ssusb); void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb); -void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host); +void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host); int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on); void ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode); @@ -86,8 +86,8 @@ static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) {} -static inline void -ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) {} +static inline void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host) +{} static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) { diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index fd0f6c5dfbc1..9c256ea3cdf5 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -299,8 +299,9 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd"); otg_sx->manual_drd_enabled = of_property_read_bool(node, "enable-manual-drd"); + otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch"); - if (of_property_read_bool(node, "extcon")) { + if (!otg_sx->role_sw_used && of_property_read_bool(node, "extcon")) { otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0); if (IS_ERR(otg_sx->edev)) { dev_err(ssusb->dev, "couldn't get extcon device\n"); From 95e29e9bbe28e636d2f089a2fa1cba9d0a436fe3 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 9 May 2019 11:31:00 +0200 Subject: [PATCH 156/159] USB: rio500: simplify locking Admitting that there can be only one device allows us to drop any pretense about locking one device or a table of devices. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/rio500.c | 43 +++++++++++++++------------------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index a32d61a79ab8..27e9c78a791e 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -51,7 +51,6 @@ struct rio_usb_data { char *obuf, *ibuf; /* transfer buffers */ char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ wait_queue_head_t wait_q; /* for timeouts */ - struct mutex lock; /* general race avoidance */ }; static DEFINE_MUTEX(rio500_mutex); @@ -63,10 +62,8 @@ static int open_rio(struct inode *inode, struct file *file) /* against disconnect() */ mutex_lock(&rio500_mutex); - mutex_lock(&(rio->lock)); if (rio->isopen || !rio->present) { - mutex_unlock(&(rio->lock)); mutex_unlock(&rio500_mutex); return -EBUSY; } @@ -74,7 +71,6 @@ static int open_rio(struct inode *inode, struct file *file) init_waitqueue_head(&rio->wait_q); - mutex_unlock(&(rio->lock)); dev_info(&rio->rio_dev->dev, "Rio opened.\n"); mutex_unlock(&rio500_mutex); @@ -88,7 +84,6 @@ static int close_rio(struct inode *inode, struct file *file) /* against disconnect() */ mutex_lock(&rio500_mutex); - mutex_lock(&(rio->lock)); rio->isopen = 0; if (!rio->present) { @@ -100,7 +95,6 @@ static int close_rio(struct inode *inode, struct file *file) } else { dev_info(&rio->rio_dev->dev, "Rio closed.\n"); } - mutex_unlock(&(rio->lock)); mutex_unlock(&rio500_mutex); return 0; } @@ -115,7 +109,7 @@ static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) int retries; int retval=0; - mutex_lock(&(rio->lock)); + mutex_lock(&rio500_mutex); /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { retval = -ENODEV; @@ -259,7 +253,7 @@ static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) err_out: - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return retval; } @@ -279,12 +273,12 @@ write_rio(struct file *file, const char __user *buffer, int errn = 0; int intr; - intr = mutex_lock_interruptible(&(rio->lock)); + intr = mutex_lock_interruptible(&rio500_mutex); if (intr) return -EINTR; /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return -ENODEV; } @@ -307,7 +301,7 @@ write_rio(struct file *file, const char __user *buffer, goto error; } if (signal_pending(current)) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return bytes_written ? bytes_written : -EINTR; } @@ -345,12 +339,12 @@ write_rio(struct file *file, const char __user *buffer, buffer += copy_size; } while (count > 0); - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return bytes_written ? bytes_written : -EIO; error: - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return errn; } @@ -367,12 +361,12 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) char *ibuf; int intr; - intr = mutex_lock_interruptible(&(rio->lock)); + intr = mutex_lock_interruptible(&rio500_mutex); if (intr) return -EINTR; /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return -ENODEV; } @@ -383,11 +377,11 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) while (count > 0) { if (signal_pending(current)) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return read_count ? read_count : -EINTR; } if (!rio->rio_dev) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return -ENODEV; } this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; @@ -405,7 +399,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) count = this_read = partial; } else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */ if (!maxretry--) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); dev_err(&rio->rio_dev->dev, "read_rio: maxretry timeout\n"); return -ETIME; @@ -415,19 +409,19 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) finish_wait(&rio->wait_q, &wait); continue; } else if (result != -EREMOTEIO) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); dev_err(&rio->rio_dev->dev, "Read Whoops - result:%d partial:%u this_read:%u\n", result, partial, this_read); return -EIO; } else { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return (0); } if (this_read) { if (copy_to_user(buffer, ibuf, this_read)) { - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return -EFAULT; } count -= this_read; @@ -435,7 +429,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) buffer += this_read; } } - mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return read_count; } @@ -500,8 +494,6 @@ static int probe_rio(struct usb_interface *intf, } dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf); - mutex_init(&(rio->lock)); - usb_set_intfdata (intf, rio); rio->present = 1; bail_out: @@ -519,12 +511,10 @@ static void disconnect_rio(struct usb_interface *intf) if (rio) { usb_deregister_dev(intf, &usb_rio_class); - mutex_lock(&(rio->lock)); if (rio->isopen) { rio->isopen = 0; /* better let it finish - the release will do whats needed */ rio->rio_dev = NULL; - mutex_unlock(&(rio->lock)); mutex_unlock(&rio500_mutex); return; } @@ -534,7 +524,6 @@ static void disconnect_rio(struct usb_interface *intf) dev_info(&intf->dev, "USB Rio disconnected.\n"); rio->present = 0; - mutex_unlock(&(rio->lock)); } mutex_unlock(&rio500_mutex); } From 9472aff16ca0fd9351eea7773facef364743088f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 8 Aug 2019 13:34:08 -0400 Subject: [PATCH 157/159] USB: rio500: Fix lockdep violation The syzbot fuzzer found a lockdep violation in the rio500 driver: ====================================================== WARNING: possible circular locking dependency detected 5.3.0-rc2+ #23 Not tainted ------------------------------------------------------ syz-executor.2/20386 is trying to acquire lock: 00000000772249c6 (rio500_mutex){+.+.}, at: open_rio+0x16/0xc0 drivers/usb/misc/rio500.c:64 but task is already holding lock: 00000000d3e8f4b9 (minor_rwsem){++++}, at: usb_open+0x23/0x270 drivers/usb/core/file.c:39 which lock already depends on the new lock. The problem is that the driver's open_rio() routine is called while the usbcore's minor_rwsem is locked for reading, and it acquires the rio500_mutex; whereas conversely, probe_rio() and disconnect_rio() first acquire the rio500_mutex and then call usb_register_dev() or usb_deregister_dev(), which lock minor_rwsem for writing. The correct ordering of acquisition should be: minor_rwsem first, then rio500_mutex (since the locking in open_rio() cannot be changed). Thus, the probe and disconnect routines should avoid holding rio500_mutex while doing their registration and deregistration. This patch adjusts the code in those two routines to do just that. It also relies on the fact that the probe and disconnect routines are protected by the device mutex, so the initial test of rio->present needs no extra locking. Reported-by: syzbot+7bbcbe9c9ff0cd49592a@syzkaller.appspotmail.com Signed-off-by: Alan Stern Fixes: d710734b0677 ("USB: rio500: simplify locking") Acked-by: Oliver Neukum CC: Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.1908081329240.1319-100000@iolanthe.rowland.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/rio500.c | 72 +++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 27e9c78a791e..30cae5e1954d 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -454,51 +454,55 @@ static int probe_rio(struct usb_interface *intf, { struct usb_device *dev = interface_to_usbdev(intf); struct rio_usb_data *rio = &rio_instance; - int retval = 0; + int retval = -ENOMEM; + char *ibuf, *obuf; - mutex_lock(&rio500_mutex); if (rio->present) { dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum); - retval = -EBUSY; - goto bail_out; - } else { - dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum); + return -EBUSY; } + dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum); + + obuf = kmalloc(OBUF_SIZE, GFP_KERNEL); + if (!obuf) { + dev_err(&dev->dev, + "probe_rio: Not enough memory for the output buffer\n"); + goto err_obuf; + } + dev_dbg(&intf->dev, "obuf address: %p\n", obuf); + + ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL); + if (!ibuf) { + dev_err(&dev->dev, + "probe_rio: Not enough memory for the input buffer\n"); + goto err_ibuf; + } + dev_dbg(&intf->dev, "ibuf address: %p\n", ibuf); + + mutex_lock(&rio500_mutex); + rio->rio_dev = dev; + rio->ibuf = ibuf; + rio->obuf = obuf; + rio->present = 1; + mutex_unlock(&rio500_mutex); retval = usb_register_dev(intf, &usb_rio_class); if (retval) { dev_err(&dev->dev, "Not able to get a minor for this device.\n"); - retval = -ENOMEM; - goto bail_out; + goto err_register; } - rio->rio_dev = dev; + usb_set_intfdata(intf, rio); + return retval; - if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) { - dev_err(&dev->dev, - "probe_rio: Not enough memory for the output buffer\n"); - usb_deregister_dev(intf, &usb_rio_class); - retval = -ENOMEM; - goto bail_out; - } - dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf); - - if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) { - dev_err(&dev->dev, - "probe_rio: Not enough memory for the input buffer\n"); - usb_deregister_dev(intf, &usb_rio_class); - kfree(rio->obuf); - retval = -ENOMEM; - goto bail_out; - } - dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf); - - usb_set_intfdata (intf, rio); - rio->present = 1; -bail_out: + err_register: + mutex_lock(&rio500_mutex); + rio->present = 0; mutex_unlock(&rio500_mutex); - + err_ibuf: + kfree(obuf); + err_obuf: return retval; } @@ -507,10 +511,10 @@ static void disconnect_rio(struct usb_interface *intf) struct rio_usb_data *rio = usb_get_intfdata (intf); usb_set_intfdata (intf, NULL); - mutex_lock(&rio500_mutex); if (rio) { usb_deregister_dev(intf, &usb_rio_class); + mutex_lock(&rio500_mutex); if (rio->isopen) { rio->isopen = 0; /* better let it finish - the release will do whats needed */ @@ -524,8 +528,8 @@ static void disconnect_rio(struct usb_interface *intf) dev_info(&intf->dev, "USB Rio disconnected.\n"); rio->present = 0; + mutex_unlock(&rio500_mutex); } - mutex_unlock(&rio500_mutex); } static const struct usb_device_id rio_table[] = { From 815cdcc334048fcc2383d24d5122e58ba4b1fd3f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 3 Sep 2019 15:36:25 +0000 Subject: [PATCH 158/159] usb: cdns3: Remove redundant dev_err call in cdns3_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20190903153625.85691-1-weiyongjun1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 300df58a190b..06f1e105be4e 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -462,10 +462,8 @@ static int cdns3_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev"); regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) { - dev_err(dev, "couldn't iomap dev resource\n"); + if (IS_ERR(regs)) return PTR_ERR(regs); - } cdns->dev_regs = regs; cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); From 3dd550a2d36596a1b0ee7955da3b611c031d3873 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 4 Sep 2019 11:56:27 -0400 Subject: [PATCH 159/159] USB: usbcore: Fix slab-out-of-bounds bug during device reset The syzbot fuzzer provoked a slab-out-of-bounds error in the USB core: BUG: KASAN: slab-out-of-bounds in memcmp+0xa6/0xb0 lib/string.c:904 Read of size 1 at addr ffff8881d175bed6 by task kworker/0:3/2746 CPU: 0 PID: 2746 Comm: kworker/0:3 Not tainted 5.3.0-rc5+ #28 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Workqueue: usb_hub_wq hub_event Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xca/0x13e lib/dump_stack.c:113 print_address_description+0x6a/0x32c mm/kasan/report.c:351 __kasan_report.cold+0x1a/0x33 mm/kasan/report.c:482 kasan_report+0xe/0x12 mm/kasan/common.c:612 memcmp+0xa6/0xb0 lib/string.c:904 memcmp include/linux/string.h:400 [inline] descriptors_changed drivers/usb/core/hub.c:5579 [inline] usb_reset_and_verify_device+0x564/0x1300 drivers/usb/core/hub.c:5729 usb_reset_device+0x4c1/0x920 drivers/usb/core/hub.c:5898 rt2x00usb_probe+0x53/0x7af drivers/net/wireless/ralink/rt2x00/rt2x00usb.c:806 The error occurs when the descriptors_changed() routine (called during a device reset) attempts to compare the old and new BOS and capability descriptors. The length it uses for the comparison is the wTotalLength value stored in BOS descriptor, but this value is not necessarily the same as the length actually allocated for the descriptors. If it is larger the routine will call memcmp() with a length that is too big, thus reading beyond the end of the allocated region and leading to this fault. The kernel reads the BOS descriptor twice: first to get the total length of all the capability descriptors, and second to read it along with all those other descriptors. A malicious (or very faulty) device may send different values for the BOS descriptor fields each time. The memory area will be allocated using the wTotalLength value read the first time, but stored within it will be the value read the second time. To prevent this possibility from causing any errors, this patch modifies the BOS descriptor after it has been read the second time: It sets the wTotalLength field to the actual length of the descriptors that were read in and validated. Then the memcpy() call, or any other code using these descriptors, will be able to rely on wTotalLength being valid. Reported-and-tested-by: syzbot+35f4d916c623118d576e@syzkaller.appspotmail.com Signed-off-by: Alan Stern CC: Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.1909041154260.1722-100000@iolanthe.rowland.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 9d6cb709ca7b..151a74a54386 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -921,7 +921,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) struct usb_bos_descriptor *bos; struct usb_dev_cap_header *cap; struct usb_ssp_cap_descriptor *ssp_cap; - unsigned char *buffer; + unsigned char *buffer, *buffer0; int length, total_len, num, i, ssac; __u8 cap_type; int ret; @@ -966,10 +966,12 @@ int usb_get_bos_descriptor(struct usb_device *dev) ret = -ENOMSG; goto err; } + + buffer0 = buffer; total_len -= length; + buffer += length; for (i = 0; i < num; i++) { - buffer += length; cap = (struct usb_dev_cap_header *)buffer; if (total_len < sizeof(*cap) || total_len < cap->bLength) { @@ -983,8 +985,6 @@ int usb_get_bos_descriptor(struct usb_device *dev) break; } - total_len -= length; - if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { dev_warn(ddev, "descriptor type invalid, skip\n"); continue; @@ -1019,7 +1019,11 @@ int usb_get_bos_descriptor(struct usb_device *dev) default: break; } + + total_len -= length; + buffer += length; } + dev->bos->desc->wTotalLength = cpu_to_le16(buffer - buffer0); return 0;