diff options
Diffstat (limited to 'drivers/usb/mtu3')
| -rw-r--r-- | drivers/usb/mtu3/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/usb/mtu3/Makefile | 11 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3.h | 150 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_core.c | 372 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_debug.h | 51 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_debugfs.c | 515 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_dr.c | 332 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_dr.h | 36 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_gadget.c | 167 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_gadget_ep0.c | 67 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_host.c | 175 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_hw_regs.h | 67 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_plat.c | 360 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_qmu.c | 174 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_qmu.h | 3 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_trace.c | 24 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_trace.h | 277 |
17 files changed, 2074 insertions, 713 deletions
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig index 40bbf1f53337..d281da1cdbcc 100644 --- a/drivers/usb/mtu3/Kconfig +++ b/drivers/usb/mtu3/Kconfig @@ -1,9 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# # For MTK USB3.0 IP config USB_MTU3 tristate "MediaTek USB3 Dual Role controller" depends on USB || USB_GADGET depends on ARCH_MEDIATEK || COMPILE_TEST + depends on EXTCON || !EXTCON select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD help Say Y or M here if your system runs on MediaTek SoCs with @@ -18,7 +21,7 @@ config USB_MTU3 if USB_MTU3 choice - bool "MTU3 Mode Selection" + prompt "MTU3 Mode Selection" default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET) default USB_MTU3_HOST if (USB && !USB_GADGET) default USB_MTU3_GADGET if (!USB && USB_GADGET) @@ -41,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/Makefile b/drivers/usb/mtu3/Makefile index 4a9715812bf9..3bf8cbcc1add 100644 --- a/drivers/usb/mtu3/Makefile +++ b/drivers/usb/mtu3/Makefile @@ -2,10 +2,17 @@ ccflags-$(CONFIG_USB_MTU3_DEBUG) += -DDEBUG +# define_trace.h needs to know how to find our header +CFLAGS_mtu3_trace.o := -I$(src) + obj-$(CONFIG_USB_MTU3) += mtu3.o mtu3-y := mtu3_plat.o +ifneq ($(CONFIG_TRACING),) + mtu3-y += mtu3_trace.o +endif + ifneq ($(filter y,$(CONFIG_USB_MTU3_HOST) $(CONFIG_USB_MTU3_DUAL_ROLE)),) mtu3-y += mtu3_host.o endif @@ -17,3 +24,7 @@ endif ifneq ($(CONFIG_USB_MTU3_DUAL_ROLE),) mtu3-y += mtu3_dr.o endif + +ifneq ($(CONFIG_DEBUG_FS),) + mtu3-y += mtu3_debugfs.o +endif diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 87823ac0d120..ba5a63669e5f 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtu3.h - MediaTek USB3 DRD header * @@ -10,17 +10,20 @@ #ifndef __MTU3_H__ #define __MTU3_H__ +#include <linux/clk.h> #include <linux/device.h> #include <linux/dmapool.h> #include <linux/extcon.h> #include <linux/interrupt.h> #include <linux/list.h> +#include <linux/of.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h> #include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/usb/role.h> struct mtu3; struct mtu3_ep; @@ -62,9 +65,18 @@ struct mtu3_request; #define MTU3_U3_IP_SLOT_DEFAULT 2 #define MTU3_U2_IP_SLOT_DEFAULT 1 -/** +/* + * IP TRUNK version + * from 0x1003 version, USB3 Gen2 is supported, two changes affect driver: + * 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted, + * but not backward compatible + * 2. QMU extend buffer length supported + */ +#define MTU3_TRUNK_VERS_1003 0x1003 + +/* * Normally the device works on HS or SS, to simplify fifo management, - * devide fifo into some 512B parts, use bitmap to manage it; And + * divide fifo into some 512B parts, use bitmap to manage it; And * 128 bits size of bitmap is large enough, that means it can manage * up to 64KB fifo size. * NOTE: MTU3_EP_FIFO_UNIT should be power of two @@ -73,12 +85,14 @@ struct mtu3_request; #define MTU3_FIFO_BIT_SIZE 128 #define MTU3_U2_IP_EP0_FIFO_SIZE 64 -/** +/* * Maximum size of ep0 response buffer for ch9 requests, * the SET_SEL request uses 6 so far, and GET_STATUS is 2 */ #define EP0_RESPONSE_BUF 6 +#define BULK_CLKS_CNT 6 + /* device operated link and speed got from DEVICE_CONF register */ enum mtu3_speed { MTU3_SPEED_INACTIVE = 0, @@ -89,6 +103,7 @@ enum mtu3_speed { }; /** + * enum mtu3_g_ep0_state - endpoint 0 states * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP * without data stage. * @MU3D_EP0_STATE_TX: IN data stage @@ -107,11 +122,12 @@ enum mtu3_g_ep0_state { }; /** - * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode + * enum mtu3_dr_force_mode - indicates host/OTG operating mode + * @MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode * by IDPIN signal. - * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG + * @MTU3_DR_FORCE_HOST: force to enter host mode and override OTG * IDPIN signal. - * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. + * @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. */ enum mtu3_dr_force_mode { MTU3_DR_FORCE_NONE = 0, @@ -120,6 +136,7 @@ enum mtu3_dr_force_mode { }; /** + * struct mtu3_fifo_info - HW FIFO description and management data * @base: the base address of fifo * @limit: the bitmap size in bits * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -131,57 +148,47 @@ struct mtu3_fifo_info { }; /** - * General Purpose Descriptor (GPD): + * struct qmu_gpd - General Purpose Descriptor (GPD): * The format of TX GPD is a little different from RX one. * And the size of GPD is 16 bytes. * - * @flag: + * @dw0_info: * bit0: Hardware Own (HWO) * bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported * bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1 + * bit6: [EL] Zero Length Packet (ZLP), moved from @dw3_info[29] * bit7: Interrupt On Completion (IOC) - * @chksum: This is used to validate the contents of this GPD; - * If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued - * when checksum validation fails; - * Checksum value is calculated over the 16 bytes of the GPD by default; - * @data_buf_len (RX ONLY): This value indicates the length of - * the assigned data buffer - * @tx_ext_addr (TX ONLY): [3:0] are 4 extension bits of @buffer, - * [7:4] are 4 extension bits of @next_gpd + * bit[31:16]: ([EL] bit[31:12]) allow data buffer length (RX ONLY), + * the buffer length of the data to receive + * bit[23:16]: ([EL] bit[31:24]) extension address (TX ONLY), + * lower 4 bits are extension bits of @buffer, + * upper 4 bits are extension bits of @next_gpd * @next_gpd: Physical address of the next GPD * @buffer: Physical address of the data buffer - * @buf_len: - * (TX): This value indicates the length of the assigned data buffer - * (RX): The total length of data received - * @ext_len: reserved - * @rx_ext_addr(RX ONLY): [3:0] are 4 extension bits of @buffer, - * [7:4] are 4 extension bits of @next_gpd - * @ext_flag: - * bit5 (TX ONLY): Zero Length Packet (ZLP), + * @dw3_info: + * bit[15:0]: ([EL] bit[19:0]) data buffer length, + * (TX): the buffer length of the data to transmit + * (RX): The total length of data received + * bit[23:16]: ([EL] bit[31:24]) extension address (RX ONLY), + * lower 4 bits are extension bits of @buffer, + * upper 4 bits are extension bits of @next_gpd + * bit29: ([EL] abandoned) Zero Length Packet (ZLP) (TX ONLY) */ struct qmu_gpd { - __u8 flag; - __u8 chksum; - union { - __le16 data_buf_len; - __le16 tx_ext_addr; - }; + __le32 dw0_info; __le32 next_gpd; __le32 buffer; - __le16 buf_len; - union { - __u8 ext_len; - __u8 rx_ext_addr; - }; - __u8 ext_flag; + __le32 dw3_info; } __packed; /** -* dma: physical base address of GPD segment -* start: virtual base address of GPD segment -* end: the last GPD element -* enqueue: the first empty GPD to use -* dequeue: the first completed GPD serviced by ISR +* struct mtu3_gpd_ring - GPD ring descriptor +* @dma: physical base address of GPD segment +* @start: virtual base address of GPD segment +* @end: the last GPD element +* @enqueue: the first empty GPD to use +* @dequeue: the first completed GPD serviced by ISR +* * NOTE: the size of GPD ring should be >= 2 */ struct mtu3_gpd_ring { @@ -193,15 +200,16 @@ struct mtu3_gpd_ring { }; /** +* struct otg_switch_mtk - OTG/dual-role switch management * @vbus: vbus 5V used by host mode * @edev: external connector used to detect vbus and iddig changes -* @vbus_nb: notifier for vbus detection -* @vbus_work : work of vbus detection notifier, used to avoid sleep in -* notifier callback which is atomic context -* @vbus_event : event of vbus detecion notifier * @id_nb : notifier for iddig(idpin) detection -* @id_work : work of iddig detection notifier -* @id_event : event of iddig detecion notifier +* @dr_work : work for drd mode switch, used to avoid sleep in atomic context +* @desired_role : role desired to switch +* @default_role : default mode while usb role is USB_ROLE_NONE +* @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. @@ -209,28 +217,28 @@ struct mtu3_gpd_ring { struct otg_switch_mtk { struct regulator *vbus; struct extcon_dev *edev; - struct notifier_block vbus_nb; - struct work_struct vbus_work; - unsigned long vbus_event; struct notifier_block id_nb; - struct work_struct id_work; - unsigned long id_event; + struct work_struct dr_work; + enum usb_role desired_role; + enum usb_role default_role; + struct usb_role_switch *role_sw; + bool role_sw_used; bool is_u3_drd; bool manual_drd_enabled; }; /** + * struct ssusb_mtk - SuperSpeed USB descriptor (MTK) * @mac_base: register base address of device MAC, exclude xHCI's * @ippc_base: register base address of IP Power and Clock interface (IPPC) * @vusb33: usb3.3V shared by device/host IP - * @sys_clk: system clock of mtu3, shared by device/host IP - * @ref_clk: reference clock - * @mcu_clk: mcu_bus_ck clock for AHB bus etc - * @dma_clk: dma_bus_ck clock for AXI bus etc * @dr_mode: works in which mode: * host only, device only or dual-role mode * @u2_ports: number of usb2.0 host ports * @u3_ports: number of usb3.0 host ports + * @u2p_dis_msk: mask of disabling usb2 ports, e.g. bit0==1 to + * disable u2port0, bit1==1 to disable u2port1,... etc, + * but when use dual-role mode, can't disable u2port0 * @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to * disable u3port0, bit1==1 to disable u3port1,... etc * @dbgfs_root: only used when supports manual dual-role switch via debugfs @@ -246,18 +254,17 @@ struct ssusb_mtk { void __iomem *ippc_base; struct phy **phys; int num_phys; + int wakeup_irq; /* common power & clock */ struct regulator *vusb33; - struct clk *sys_clk; - struct clk *ref_clk; - struct clk *mcu_clk; - struct clk *dma_clk; + struct clk_bulk_data clks[BULK_CLKS_CNT]; /* otg */ struct otg_switch_mtk otg_switch; enum usb_dr_mode dr_mode; bool is_host; int u2_ports; int u3_ports; + int u2p_dis_msk; int u3p_dis_msk; struct dentry *dbgfs_root; /* usb wakeup for host mode */ @@ -268,6 +275,7 @@ struct ssusb_mtk { }; /** + * struct mtu3_ep - common mtu3 endpoint description * @fifo_size: it is (@slot + 1) * @fifo_seg_size * @fifo_seg_size: it is roundup_pow_of_two(@maxp) */ @@ -291,8 +299,6 @@ struct mtu3_ep { const struct usb_endpoint_descriptor *desc; int flags; - u8 wedged; - u8 busy; }; struct mtu3_request { @@ -316,9 +322,11 @@ static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev) * @may_wakeup: means device's remote wakeup is enabled * @is_self_powered: is reported in device status and the config descriptor * @delayed_status: true when function drivers ask for delayed status + * @gen2cp: compatible with USB3 Gen2 IP * @ep0_req: dummy request used while handling standard USB requests * for GET_STATUS and SET_SEL * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests + * @u3_capable: is capable of supporting USB3 */ struct mtu3 { spinlock_t lock; @@ -345,7 +353,8 @@ struct mtu3 { struct usb_gadget_driver *gadget_driver; struct mtu3_request ep0_req; u8 setup_buf[EP0_RESPONSE_BUF]; - u32 max_speed; + enum usb_device_speed max_speed; + enum usb_device_speed speed; unsigned is_active:1; unsigned may_wakeup:1; @@ -354,8 +363,12 @@ struct mtu3 { unsigned softconnect:1; unsigned u1_enable:1; unsigned u2_enable:1; - unsigned is_u3_ip:1; + unsigned u3_capable:1; unsigned delayed_status:1; + unsigned gen2cp:1; + unsigned connected:1; + unsigned async_callbacks:1; + unsigned separate_fifo:1; u8 address; u8 test_mode_nr; @@ -367,12 +380,6 @@ static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g) return container_of(g, struct mtu3, g); } -static inline int is_first_entry(const struct list_head *list, - const struct list_head *head) -{ - return list_is_last(head, list); -} - static inline struct mtu3_request *to_mtu3_request(struct usb_request *req) { return req ? container_of(req, struct mtu3_request, request) : NULL; @@ -425,7 +432,6 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, int interval, int burst, int mult); void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep); void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set); -void mtu3_ep0_setup(struct mtu3 *mtu); void mtu3_start(struct mtu3 *mtu); void mtu3_stop(struct mtu3 *mtu); void mtu3_dev_on_off(struct mtu3 *mtu, int is_on); diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 4fee200795a5..3a25ee18f144 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -16,6 +16,9 @@ #include <linux/platform_device.h> #include "mtu3.h" +#include "mtu3_dr.h" +#include "mtu3_debug.h" +#include "mtu3_trace.h" static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size) { @@ -97,7 +100,7 @@ static int mtu3_device_enable(struct mtu3 *mtu) mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); - if (mtu->is_u3_ip) { + if (mtu->u3_capable) { check_clk = SSUSB_U3_MAC_RST_B_STS; mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), (SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN | @@ -109,7 +112,7 @@ static int mtu3_device_enable(struct mtu3 *mtu) if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) { mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_setbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_DUAL_MODE); } @@ -121,19 +124,45 @@ static void mtu3_device_disable(struct mtu3 *mtu) { void __iomem *ibase = mtu->ippc_base; - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_setbits(ibase, SSUSB_U3_CTRL(0), (SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN)); mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN); - if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) + if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) { mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); + if (mtu->u3_capable) + mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), + SSUSB_U3_PORT_DUAL_MODE); + } mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } +static void mtu3_dev_power_on(struct mtu3 *mtu) +{ + void __iomem *ibase = mtu->ippc_base; + + mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); + if (mtu->u3_capable) + mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); + + mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); +} + +static void mtu3_dev_power_down(struct mtu3 *mtu) +{ + void __iomem *ibase = mtu->ippc_base; + + if (mtu->u3_capable) + mtu3_setbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); + + mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); + mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); +} + /* reset U3D's device module. */ static void mtu3_device_reset(struct mtu3 *mtu) { @@ -144,17 +173,6 @@ static void mtu3_device_reset(struct mtu3 *mtu) mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST); } -/* disable all interrupts */ -static void mtu3_intr_disable(struct mtu3 *mtu) -{ - void __iomem *mbase = mtu->mac_base; - - /* Disable level 1 interrupts */ - mtu3_writel(mbase, U3D_LV1IECR, ~0x0); - /* Disable endpoint interrupts */ - mtu3_writel(mbase, U3D_EPIECR, ~0x0); -} - static void mtu3_intr_status_clear(struct mtu3 *mtu) { void __iomem *mbase = mtu->mac_base; @@ -167,6 +185,18 @@ static void mtu3_intr_status_clear(struct mtu3 *mtu) mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0); /* Clear speed change interrupt status */ mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0); + /* Clear QMU interrupt status */ + mtu3_writel(mbase, U3D_QISAR0, ~0x0); +} + +/* disable all interrupts */ +static void mtu3_intr_disable(struct mtu3 *mtu) +{ + /* Disable level 1 interrupts */ + mtu3_writel(mtu->mac_base, U3D_LV1IECR, ~0x0); + /* Disable endpoint interrupts */ + mtu3_writel(mtu->mac_base, U3D_EPIECR, ~0x0); + mtu3_intr_status_clear(mtu); } /* enable system global interrupt */ @@ -183,7 +213,7 @@ static void mtu3_intr_enable(struct mtu3 *mtu) value = SUSPEND_INTR | RESUME_INTR | RESET_INTR; mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value); - if (mtu->is_u3_ip) { + if (mtu->u3_capable) { /* Enable U3 LTSSM interrupts */ value = HOT_RST_INTR | WARM_RST_INTR | ENTER_U3_INTR | EXIT_U3_INTR; @@ -199,6 +229,71 @@ static void mtu3_intr_enable(struct mtu3 *mtu) mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR); } +static void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed) +{ + void __iomem *mbase = mtu->mac_base; + + if (speed > mtu->max_speed) + speed = mtu->max_speed; + + switch (speed) { + case USB_SPEED_FULL: + /* disable U3 SS function */ + mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN); + /* disable HS function */ + mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + break; + case USB_SPEED_HIGH: + mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN); + /* HS/FS detected by HW */ + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + break; + case USB_SPEED_SUPER: + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), + SSUSB_U3_PORT_SSP_SPEED); + break; + case USB_SPEED_SUPER_PLUS: + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), + SSUSB_U3_PORT_SSP_SPEED); + break; + default: + dev_err(mtu->dev, "invalid speed: %s\n", + usb_speed_string(speed)); + return; + } + + mtu->speed = speed; + dev_dbg(mtu->dev, "set speed: %s\n", usb_speed_string(speed)); +} + +/* CSR registers will be reset to default value if port is disabled */ +static void mtu3_csr_init(struct mtu3 *mtu) +{ + void __iomem *mbase = mtu->mac_base; + + if (mtu->u3_capable) { + /* disable LGO_U1/U2 by default */ + mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL, + SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE); + /* enable accept LGO_U1/U2 link command from host */ + mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL, + SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE); + /* device responses to u3_exit from host automatically */ + mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN); + /* automatically build U2 link when U3 detect fail */ + mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH); + /* auto clear SOFT_CONN when clear USB3_EN if work as HS */ + mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN); + } + + /* delay about 0.1us from detecting reset to send chirp-K */ + mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); + /* enable automatic HWRW from L1 */ + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); +} + /* reset: u2 - data toggle, u3 - SeqN, flow control status etc */ static void mtu3_ep_reset(struct mtu3_ep *mep) { @@ -246,13 +341,13 @@ void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set) void mtu3_dev_on_off(struct mtu3 *mtu, int is_on) { - if (mtu->is_u3_ip && mtu->max_speed >= USB_SPEED_SUPER) + if (mtu->u3_capable && mtu->speed >= USB_SPEED_SUPER) mtu3_ss_func_set(mtu, is_on); else mtu3_hs_softconn_set(mtu, is_on); dev_info(mtu->dev, "gadget (%s) pullup D%s\n", - usb_speed_string(mtu->max_speed), is_on ? "+" : "-"); + usb_speed_string(mtu->speed), is_on ? "+" : "-"); } void mtu3_start(struct mtu3 *mtu) @@ -262,15 +357,9 @@ void mtu3_start(struct mtu3 *mtu) dev_dbg(mtu->dev, "%s devctl 0x%x\n", __func__, mtu3_readl(mbase, U3D_DEVICE_CONTROL)); - mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); - - /* - * When disable U2 port, USB2_CSR's register will be reset to - * default value after re-enable it again(HS is enabled by default). - * So if force mac to work as FS, disable HS function. - */ - if (mtu->max_speed == USB_SPEED_FULL) - mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + mtu3_dev_power_on(mtu); + mtu3_csr_init(mtu); + mtu3_set_speed(mtu, mtu->speed); /* Initialize the default interrupts */ mtu3_intr_enable(mtu); @@ -285,13 +374,30 @@ void mtu3_stop(struct mtu3 *mtu) dev_dbg(mtu->dev, "%s\n", __func__); mtu3_intr_disable(mtu); - mtu3_intr_status_clear(mtu); if (mtu->softconnect) mtu3_dev_on_off(mtu, 0); mtu->is_active = 0; - mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); + mtu3_dev_power_down(mtu); +} + +static void mtu3_dev_suspend(struct mtu3 *mtu) +{ + if (!mtu->is_active) + return; + + mtu3_intr_disable(mtu); + mtu3_dev_power_down(mtu); +} + +static void mtu3_dev_resume(struct mtu3 *mtu) +{ + if (!mtu->is_active) + return; + + mtu3_dev_power_on(mtu); + mtu3_intr_enable(mtu); } /* for non-ep0 */ @@ -299,6 +405,7 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, int interval, int burst, int mult) { void __iomem *mbase = mtu->mac_base; + bool gen2cp = mtu->gen2cp; int epnum = mep->epnum; u32 csr0, csr1, csr2; int fifo_sgsz, fifo_addr; @@ -319,7 +426,7 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, num_pkts = (burst + 1) * (mult + 1) - 1; csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot); - csr1 |= TX_MAX_PKT(num_pkts) | TX_MULT(mult); + csr1 |= TX_MAX_PKT(gen2cp, num_pkts) | TX_MULT(gen2cp, mult); csr2 = TX_FIFOADDR(fifo_addr >> 4); csr2 |= TX_FIFOSEGSIZE(fifo_sgsz); @@ -355,7 +462,7 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, num_pkts = (burst + 1) * (mult + 1) - 1; csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot); - csr1 |= RX_MAX_PKT(num_pkts) | RX_MULT(mult); + csr1 |= RX_MAX_PKT(gen2cp, num_pkts) | RX_MULT(gen2cp, mult); csr2 = RX_FIFOADDR(fifo_addr >> 4); csr2 |= RX_FIFOSEGSIZE(fifo_sgsz); @@ -437,7 +544,7 @@ static void get_ep_fifo_config(struct mtu3 *mtu) struct mtu3_fifo_info *rx_fifo; u32 fifosize; - if (mtu->is_u3_ip) { + if (mtu->separate_fifo) { fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ); tx_fifo = &mtu->tx_fifo; tx_fifo->base = 0; @@ -470,7 +577,7 @@ static void get_ep_fifo_config(struct mtu3 *mtu) rx_fifo->base, rx_fifo->limit); } -void mtu3_ep0_setup(struct mtu3 *mtu) +static void mtu3_ep0_setup(struct mtu3 *mtu) { u32 maxpacket = mtu->g.ep0->maxpacket; u32 csr; @@ -541,65 +648,22 @@ static void mtu3_mem_free(struct mtu3 *mtu) kfree(mtu->ep_array); } -static void mtu3_set_speed(struct mtu3 *mtu) -{ - void __iomem *mbase = mtu->mac_base; - - if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH)) - mtu->max_speed = USB_SPEED_HIGH; - - if (mtu->max_speed == USB_SPEED_FULL) { - /* disable U3 SS function */ - mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN); - /* disable HS function */ - mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); - } else if (mtu->max_speed == USB_SPEED_HIGH) { - mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN); - /* HS/FS detected by HW */ - mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); - } else if (mtu->max_speed == USB_SPEED_SUPER) { - mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), - SSUSB_U3_PORT_SSP_SPEED); - } - - dev_info(mtu->dev, "max_speed: %s\n", - usb_speed_string(mtu->max_speed)); -} - static void mtu3_regs_init(struct mtu3 *mtu) { - void __iomem *mbase = mtu->mac_base; /* be sure interrupts are disabled before registration of ISR */ mtu3_intr_disable(mtu); - mtu3_intr_status_clear(mtu); - - if (mtu->is_u3_ip) { - /* disable LGO_U1/U2 by default */ - mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL, - SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE); - /* enable accept LGO_U1/U2 link command from host */ - mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL, - SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE); - /* device responses to u3_exit from host automatically */ - mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN); - /* automatically build U2 link when U3 detect fail */ - mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH); - /* auto clear SOFT_CONN when clear USB3_EN if work as HS */ - mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN); - } - mtu3_set_speed(mtu); + mtu3_csr_init(mtu); - /* delay about 0.1us from detecting reset to send chirp-K */ - mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); /* U2/U3 detected by HW */ mtu3_writel(mbase, U3D_DEVICE_CONF, 0); /* vbus detected by HW */ mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON); - /* enable automatical HWRW from L1 */ - mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); + /* use new QMU format when HW version >= 0x1003 */ + if (mtu->gen2cp) + mtu3_writel(mbase, U3D_QFCR, ~0x0); } static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) @@ -650,15 +714,21 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) break; } dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed)); + mtu3_dbg_trace(mtu->dev, "link speed %s", + usb_speed_string(udev_speed)); mtu->g.speed = udev_speed; mtu->g.ep0->maxpacket = maxpkt; mtu->ep0_state = MU3D_EP0_STATE_SETUP; + mtu->connected = !!(udev_speed != USB_SPEED_UNKNOWN); - if (udev_speed == USB_SPEED_UNKNOWN) + if (udev_speed == USB_SPEED_UNKNOWN) { mtu3_gadget_disconnect(mtu); - else + pm_runtime_put(mtu->dev); + } else { + pm_runtime_get(mtu->dev); mtu3_ep0_setup(mtu); + } return IRQ_HANDLED; } @@ -672,6 +742,7 @@ static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu) ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE); mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */ dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm); + trace_mtu3_u3_ltssm_isr(ltssm); if (ltssm & (HOT_RST_INTR | WARM_RST_INTR)) mtu3_gadget_reset(mtu); @@ -702,6 +773,7 @@ static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu) u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE); mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */ dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm); + trace_mtu3_u2_common_isr(u2comm); if (u2comm & SUSPEND_INTR) mtu3_gadget_suspend(mtu); @@ -747,18 +819,56 @@ static irqreturn_t mtu3_irq(int irq, void *data) return IRQ_HANDLED; } +static void mtu3_check_params(struct mtu3 *mtu) +{ + /* device's u3 port (port0) is disabled */ + if (mtu->u3_capable && (mtu->ssusb->u3p_dis_msk & BIT(0))) + mtu->u3_capable = 0; + + /* check the max_speed parameter */ + switch (mtu->max_speed) { + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + break; + default: + dev_err(mtu->dev, "invalid max_speed: %s\n", + usb_speed_string(mtu->max_speed)); + fallthrough; + case USB_SPEED_UNKNOWN: + /* default as SSP */ + mtu->max_speed = USB_SPEED_SUPER_PLUS; + break; + } + + if (!mtu->u3_capable && (mtu->max_speed > USB_SPEED_HIGH)) + mtu->max_speed = USB_SPEED_HIGH; + + mtu->speed = mtu->max_speed; + + dev_info(mtu->dev, "max_speed: %s\n", + usb_speed_string(mtu->max_speed)); +} + static int mtu3_hw_init(struct mtu3 *mtu) { - u32 cap_dev; + u32 value; int ret; - mtu->hw_version = mtu3_readl(mtu->ippc_base, U3D_SSUSB_HW_ID); + value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_TRUNK_VERS); + mtu->hw_version = IP_TRUNK_VERS(value); + mtu->gen2cp = !!(mtu->hw_version >= MTU3_TRUNK_VERS_1003); - cap_dev = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP); - mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(cap_dev); + value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP); + mtu->u3_capable = !!SSUSB_IP_DEV_U3_PORT_NUM(value); + /* usb3 ip uses separate fifo */ + mtu->separate_fifo = mtu->u3_capable; dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version, - mtu->is_u3_ip ? "U3" : "U2"); + mtu->u3_capable ? "U3" : "U2"); + + mtu3_check_params(mtu); mtu3_device_reset(mtu); @@ -783,7 +893,7 @@ static void mtu3_hw_exit(struct mtu3 *mtu) mtu3_mem_free(mtu); } -/** +/* * we set 32-bit DMA mask by default, here check whether the controller * supports 36-bit DMA or not, if it does, set 36-bit DMA mask. */ @@ -814,22 +924,25 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) struct device *dev = ssusb->dev; struct platform_device *pdev = to_platform_device(dev); struct mtu3 *mtu = NULL; - struct resource *res; int ret = -ENOMEM; mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL); if (mtu == NULL) return -ENOMEM; - mtu->irq = platform_get_irq(pdev, 0); + mtu->irq = platform_get_irq_byname_optional(pdev, "device"); if (mtu->irq < 0) { - dev_err(dev, "fail to get irq number\n"); - return mtu->irq; + if (mtu->irq == -EPROBE_DEFER) + return mtu->irq; + + /* for backward compatibility */ + mtu->irq = platform_get_irq(pdev, 0); + if (mtu->irq < 0) + return mtu->irq; } dev_info(dev, "irq %d\n", mtu->irq); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac"); - mtu->mac_base = devm_ioremap_resource(dev, res); + mtu->mac_base = devm_platform_ioremap_resource_byname(pdev, "mac"); if (IS_ERR(mtu->mac_base)) { dev_err(dev, "error mapping memory for dev mac\n"); return PTR_ERR(mtu->mac_base); @@ -843,23 +956,6 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) mtu->ssusb = ssusb; mtu->max_speed = usb_get_maximum_speed(dev); - /* check the max_speed parameter */ - switch (mtu->max_speed) { - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - break; - default: - dev_err(dev, "invalid max_speed: %s\n", - usb_speed_string(mtu->max_speed)); - /* fall through */ - case USB_SPEED_UNKNOWN: - /* default as SSP */ - mtu->max_speed = USB_SPEED_SUPER_PLUS; - break; - } - dev_dbg(dev, "mac_base=0x%p, ippc_base=0x%p\n", mtu->mac_base, mtu->ippc_base); @@ -875,13 +971,15 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) goto dma_mask_err; } - ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu); + ret = devm_request_threaded_irq(dev, mtu->irq, NULL, mtu3_irq, + IRQF_ONESHOT, dev_name(dev), mtu); if (ret) { dev_err(dev, "request irq %d failed!\n", mtu->irq); goto irq_err; } - device_init_wakeup(dev, true); + /* power down device IP for power saving by default */ + mtu3_stop(mtu); ret = mtu3_gadget_setup(mtu); if (ret) { @@ -889,9 +987,7 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) goto gadget_err; } - /* init as host mode, power down device IP for power saving */ - if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) - mtu3_stop(mtu); + ssusb_dev_debugfs_init(ssusb); dev_dbg(dev, " %s() done...\n", __func__); @@ -917,3 +1013,47 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb) device_init_wakeup(ssusb->dev, false); mtu3_hw_exit(mtu); } + +bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb) +{ + struct mtu3 *mtu = ssusb->u3d; + + /* host only, should wait for ip sleep */ + if (!mtu) + return true; + + /* device is started and pullup D+, ip can sleep */ + if (mtu->is_active && mtu->softconnect) + return true; + + /* ip can't sleep if not pullup D+ when support device mode */ + return false; +} + +int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + struct mtu3 *mtu = ssusb->u3d; + + if (!mtu->gadget_driver) + return 0; + + if (mtu->connected) + return -EBUSY; + + mtu3_dev_suspend(mtu); + synchronize_irq(mtu->irq); + + return 0; +} + +int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + struct mtu3 *mtu = ssusb->u3d; + + if (!mtu->gadget_driver) + return 0; + + mtu3_dev_resume(mtu); + + return 0; +} diff --git a/drivers/usb/mtu3/mtu3_debug.h b/drivers/usb/mtu3/mtu3_debug.h new file mode 100644 index 000000000000..9a36134b322d --- /dev/null +++ b/drivers/usb/mtu3/mtu3_debug.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mtu3_debug.h - debug header + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#ifndef __MTU3_DEBUG_H__ +#define __MTU3_DEBUG_H__ + +#include <linux/debugfs.h> + +struct ssusb_mtk; + +#define MTU3_DEBUGFS_NAME_LEN 32 + +struct mtu3_regset { + char name[MTU3_DEBUGFS_NAME_LEN]; + struct debugfs_regset32 regset; +}; + +struct mtu3_file_map { + const char *name; + int (*show)(struct seq_file *s, void *unused); +}; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb); +void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb); +void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb); +void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb); + +#else +static inline void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb) {} +static inline void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb) {} +static inline void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb) {} +static inline void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb) {} + +#endif /* CONFIG_DEBUG_FS */ + +#if IS_ENABLED(CONFIG_TRACING) +void mtu3_dbg_trace(struct device *dev, const char *fmt, ...); + +#else +static inline void mtu3_dbg_trace(struct device *dev, const char *fmt, ...) {} + +#endif /* CONFIG_TRACING */ + +#endif /* __MTU3_DEBUG_H__ */ diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c new file mode 100644 index 000000000000..c003049bafbf --- /dev/null +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtu3_debugfs.c - debugfs interface + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#include <linux/string_choices.h> +#include <linux/uaccess.h> + +#include "mtu3.h" +#include "mtu3_dr.h" +#include "mtu3_debug.h" + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = U3D_ ##nm, \ +} + +#define dump_prb_reg(nm, os) \ +{ \ + .name = nm, \ + .offset = os, \ +} + +static const struct debugfs_reg32 mtu3_ippc_regs[] = { + dump_register(SSUSB_IP_PW_CTRL0), + dump_register(SSUSB_IP_PW_CTRL1), + dump_register(SSUSB_IP_PW_CTRL2), + dump_register(SSUSB_IP_PW_CTRL3), + dump_register(SSUSB_IP_PW_STS1), + dump_register(SSUSB_OTG_STS), + dump_register(SSUSB_IP_XHCI_CAP), + dump_register(SSUSB_IP_DEV_CAP), + dump_register(SSUSB_U3_CTRL_0P), + dump_register(SSUSB_U2_CTRL_0P), + dump_register(SSUSB_HW_ID), + dump_register(SSUSB_HW_SUB_ID), + dump_register(SSUSB_IP_SPARE0), +}; + +static const struct debugfs_reg32 mtu3_dev_regs[] = { + dump_register(LV1ISR), + dump_register(LV1IER), + dump_register(EPISR), + dump_register(EPIER), + dump_register(EP0CSR), + dump_register(RXCOUNT0), + dump_register(QISAR0), + dump_register(QIER0), + dump_register(QISAR1), + dump_register(QIER1), + dump_register(CAP_EPNTXFFSZ), + dump_register(CAP_EPNRXFFSZ), + dump_register(CAP_EPINFO), + dump_register(MISC_CTRL), +}; + +static const struct debugfs_reg32 mtu3_csr_regs[] = { + dump_register(DEVICE_CONF), + dump_register(DEV_LINK_INTR_ENABLE), + dump_register(DEV_LINK_INTR), + dump_register(LTSSM_CTRL), + dump_register(USB3_CONFIG), + dump_register(LINK_STATE_MACHINE), + dump_register(LTSSM_INTR_ENABLE), + dump_register(LTSSM_INTR), + dump_register(U3U2_SWITCH_CTRL), + dump_register(POWER_MANAGEMENT), + dump_register(DEVICE_CONTROL), + dump_register(COMMON_USB_INTR_ENABLE), + dump_register(COMMON_USB_INTR), + dump_register(USB20_MISC_CONTROL), + dump_register(USB20_OPSTATE), +}; + +static int mtu3_link_state_show(struct seq_file *sf, void *unused) +{ + struct mtu3 *mtu = sf->private; + void __iomem *mbase = mtu->mac_base; + + seq_printf(sf, "opstate: %#x, ltssm: %#x\n", + mtu3_readl(mbase, U3D_USB20_OPSTATE), + LTSSM_STATE(mtu3_readl(mbase, U3D_LINK_STATE_MACHINE))); + + return 0; +} + +static int mtu3_ep_used_show(struct seq_file *sf, void *unused) +{ + struct mtu3 *mtu = sf->private; + struct mtu3_ep *mep; + unsigned long flags; + int used = 0; + int i; + + spin_lock_irqsave(&mtu->lock, flags); + + for (i = 0; i < mtu->num_eps; i++) { + mep = mtu->in_eps + i; + if (mep->flags & MTU3_EP_ENABLED) { + seq_printf(sf, "%s - type: %s\n", mep->name, usb_ep_type_string(mep->type)); + used++; + } + + mep = mtu->out_eps + i; + if (mep->flags & MTU3_EP_ENABLED) { + seq_printf(sf, "%s - type: %s\n", mep->name, usb_ep_type_string(mep->type)); + used++; + } + } + seq_printf(sf, "total used: %d eps\n", used); + + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mtu3_link_state); +DEFINE_SHOW_ATTRIBUTE(mtu3_ep_used); + +static void mtu3_debugfs_regset(struct mtu3 *mtu, void __iomem *base, + const struct debugfs_reg32 *regs, size_t nregs, + const char *name, struct dentry *parent) +{ + struct debugfs_regset32 *regset; + struct mtu3_regset *mregs; + + mregs = devm_kzalloc(mtu->dev, sizeof(*mregs), GFP_KERNEL); + if (!mregs) + return; + + sprintf(mregs->name, "%s", name); + regset = &mregs->regset; + regset->regs = regs; + regset->nregs = nregs; + regset->base = base; + + debugfs_create_regset32(mregs->name, 0444, parent, regset); +} + +static void mtu3_debugfs_ep_regset(struct mtu3 *mtu, struct mtu3_ep *mep, + struct dentry *parent) +{ + struct debugfs_reg32 *regs; + int epnum = mep->epnum; + int in = mep->is_in; + + regs = devm_kcalloc(mtu->dev, 7, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + regs[0].name = in ? "TCR0" : "RCR0"; + regs[0].offset = in ? MU3D_EP_TXCR0(epnum) : MU3D_EP_RXCR0(epnum); + regs[1].name = in ? "TCR1" : "RCR1"; + regs[1].offset = in ? MU3D_EP_TXCR1(epnum) : MU3D_EP_RXCR1(epnum); + regs[2].name = in ? "TCR2" : "RCR2"; + regs[2].offset = in ? MU3D_EP_TXCR2(epnum) : MU3D_EP_RXCR2(epnum); + regs[3].name = in ? "TQHIAR" : "RQHIAR"; + regs[3].offset = in ? USB_QMU_TQHIAR(epnum) : USB_QMU_RQHIAR(epnum); + regs[4].name = in ? "TQCSR" : "RQCSR"; + regs[4].offset = in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum); + regs[5].name = in ? "TQSAR" : "RQSAR"; + regs[5].offset = in ? USB_QMU_TQSAR(epnum) : USB_QMU_RQSAR(epnum); + regs[6].name = in ? "TQCPR" : "RQCPR"; + regs[6].offset = in ? USB_QMU_TQCPR(epnum) : USB_QMU_RQCPR(epnum); + + mtu3_debugfs_regset(mtu, mtu->mac_base, regs, 7, "ep-regs", parent); +} + +static int mtu3_ep_info_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + unsigned long flags; + + spin_lock_irqsave(&mtu->lock, flags); + seq_printf(sf, "ep - type:%s, maxp:%d, slot:%d, flags:%x\n", + usb_ep_type_string(mep->type), mep->maxp, mep->slot, mep->flags); + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static int mtu3_fifo_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + unsigned long flags; + + spin_lock_irqsave(&mtu->lock, flags); + seq_printf(sf, "fifo - seg_size:%d, addr:%d, size:%d\n", + mep->fifo_seg_size, mep->fifo_addr, mep->fifo_size); + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static int mtu3_qmu_ring_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + struct mtu3_gpd_ring *ring; + unsigned long flags; + + ring = &mep->gpd_ring; + spin_lock_irqsave(&mtu->lock, flags); + seq_printf(sf, + "qmu-ring - dma:%pad, start:%p, end:%p, enq:%p, dep:%p\n", + &ring->dma, ring->start, ring->end, + ring->enqueue, ring->dequeue); + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static int mtu3_qmu_gpd_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + struct mtu3_gpd_ring *ring; + struct qmu_gpd *gpd; + dma_addr_t dma; + unsigned long flags; + int i; + + spin_lock_irqsave(&mtu->lock, flags); + ring = &mep->gpd_ring; + gpd = ring->start; + if (!gpd || !(mep->flags & MTU3_EP_ENABLED)) { + seq_puts(sf, "empty!\n"); + goto out; + } + + for (i = 0; i < MAX_GPD_NUM; i++, gpd++) { + dma = ring->dma + i * sizeof(*gpd); + seq_printf(sf, "gpd.%03d -> %pad, %p: %08x %08x %08x %08x\n", + i, &dma, gpd, gpd->dw0_info, gpd->next_gpd, + gpd->buffer, gpd->dw3_info); + } + +out: + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static const struct mtu3_file_map mtu3_ep_files[] = { + {"ep-info", mtu3_ep_info_show, }, + {"fifo", mtu3_fifo_show, }, + {"qmu-ring", mtu3_qmu_ring_show, }, + {"qmu-gpd", mtu3_qmu_gpd_show, }, +}; + +static int mtu3_ep_open(struct inode *inode, struct file *file) +{ + const struct mtu3_file_map *f_map = debugfs_get_aux(file); + + return single_open(file, f_map->show, inode->i_private); +} + +static const struct file_operations mtu3_ep_fops = { + .open = mtu3_ep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct debugfs_reg32 mtu3_prb_regs[] = { + dump_prb_reg("enable", U3D_SSUSB_PRB_CTRL0), + dump_prb_reg("byte-sell", U3D_SSUSB_PRB_CTRL1), + dump_prb_reg("byte-selh", U3D_SSUSB_PRB_CTRL2), + dump_prb_reg("module-sel", U3D_SSUSB_PRB_CTRL3), + dump_prb_reg("sw-out", U3D_SSUSB_PRB_CTRL4), + dump_prb_reg("data", U3D_SSUSB_PRB_CTRL5), +}; + +static int mtu3_probe_show(struct seq_file *sf, void *unused) +{ + struct mtu3 *mtu = sf->private; + const struct debugfs_reg32 *regs = debugfs_get_aux(sf->file); + + seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset, + mtu3_readl(mtu->ippc_base, (u32)regs->offset)); + + return 0; +} + +static int mtu3_probe_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtu3_probe_show, inode->i_private); +} + +static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct mtu3 *mtu = sf->private; + const struct debugfs_reg32 *regs = debugfs_get_aux(file); + char buf[32]; + u32 val; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (kstrtou32(buf, 0, &val)) + return -EINVAL; + + mtu3_writel(mtu->ippc_base, (u32)regs->offset, val); + + return count; +} + +static const struct file_operations mtu3_probe_fops = { + .open = mtu3_probe_open, + .write = mtu3_probe_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu) +{ + struct ssusb_mtk *ssusb = mtu->ssusb; + const struct debugfs_reg32 *regs; + struct dentry *dir_prb; + int i; + + dir_prb = debugfs_create_dir("probe", ssusb->dbgfs_root); + + for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { + regs = &mtu3_prb_regs[i]; + debugfs_create_file_aux(regs->name, 0644, dir_prb, + mtu, regs, &mtu3_probe_fops); + } + + mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs, + ARRAY_SIZE(mtu3_prb_regs), "regs", dir_prb); +} + +static void mtu3_debugfs_create_ep_dir(struct mtu3_ep *mep, + struct dentry *parent) +{ + const struct mtu3_file_map *files; + struct dentry *dir_ep; + int i; + + dir_ep = debugfs_create_dir(mep->name, parent); + mtu3_debugfs_ep_regset(mep->mtu, mep, dir_ep); + + for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { + files = &mtu3_ep_files[i]; + + debugfs_create_file_aux(files->name, 0444, dir_ep, + mep, files, &mtu3_ep_fops); + } +} + +static void mtu3_debugfs_create_ep_dirs(struct mtu3 *mtu) +{ + struct ssusb_mtk *ssusb = mtu->ssusb; + struct dentry *dir_eps; + int i; + + dir_eps = debugfs_create_dir("eps", ssusb->dbgfs_root); + + for (i = 1; i < mtu->num_eps; i++) { + mtu3_debugfs_create_ep_dir(mtu->in_eps + i, dir_eps); + mtu3_debugfs_create_ep_dir(mtu->out_eps + i, dir_eps); + } +} + +void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb) +{ + struct mtu3 *mtu = ssusb->u3d; + struct dentry *dir_regs; + + dir_regs = debugfs_create_dir("regs", ssusb->dbgfs_root); + + mtu3_debugfs_regset(mtu, mtu->ippc_base, + mtu3_ippc_regs, ARRAY_SIZE(mtu3_ippc_regs), + "reg-ippc", dir_regs); + + mtu3_debugfs_regset(mtu, mtu->mac_base, + mtu3_dev_regs, ARRAY_SIZE(mtu3_dev_regs), + "reg-dev", dir_regs); + + mtu3_debugfs_regset(mtu, mtu->mac_base, + mtu3_csr_regs, ARRAY_SIZE(mtu3_csr_regs), + "reg-csr", dir_regs); + + mtu3_debugfs_create_ep_dirs(mtu); + + mtu3_debugfs_create_prb_files(mtu); + + debugfs_create_file("link-state", 0444, ssusb->dbgfs_root, + mtu, &mtu3_link_state_fops); + debugfs_create_file("ep-used", 0444, ssusb->dbgfs_root, + mtu, &mtu3_ep_used_fops); +} + +static int ssusb_mode_show(struct seq_file *sf, void *unused) +{ + struct ssusb_mtk *ssusb = sf->private; + + seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n", + ssusb->is_host ? "host" : "device", + ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto"); + + return 0; +} + +static int ssusb_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, ssusb_mode_show, inode->i_private); +} + +static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct ssusb_mtk *ssusb = sf->private; + char buf[16]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "host", 4) && !ssusb->is_host) { + ssusb_mode_switch(ssusb, 1); + } else if (!strncmp(buf, "device", 6) && ssusb->is_host) { + ssusb_mode_switch(ssusb, 0); + } else { + dev_err(ssusb->dev, "wrong or duplicated setting\n"); + return -EINVAL; + } + + return count; +} + +static const struct file_operations ssusb_mode_fops = { + .open = ssusb_mode_open, + .write = ssusb_mode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ssusb_vbus_show(struct seq_file *sf, void *unused) +{ + struct ssusb_mtk *ssusb = sf->private; + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + + seq_printf(sf, "vbus state: %s\n(echo on/off)\n", + str_on_off(regulator_is_enabled(otg_sx->vbus))); + + return 0; +} + +static int ssusb_vbus_open(struct inode *inode, struct file *file) +{ + return single_open(file, ssusb_vbus_show, inode->i_private); +} + +static ssize_t ssusb_vbus_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct ssusb_mtk *ssusb = sf->private; + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + char buf[16]; + bool enable; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (kstrtobool(buf, &enable)) { + dev_err(ssusb->dev, "wrong setting\n"); + return -EINVAL; + } + + ssusb_set_vbus(otg_sx, enable); + + return count; +} + +static const struct file_operations ssusb_vbus_fops = { + .open = ssusb_vbus_open, + .write = ssusb_vbus_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb) +{ + struct dentry *root = ssusb->dbgfs_root; + + debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops); + debugfs_create_file("vbus", 0644, root, ssusb, &ssusb_vbus_fops); +} + +void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb) +{ + ssusb->dbgfs_root = + debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root); +} + +void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb) +{ + debugfs_remove_recursive(ssusb->dbgfs_root); + ssusb->dbgfs_root = NULL; +} diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index ac60e9c8564e..ffa5b9401dad 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -7,33 +7,23 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ -#include <linux/debugfs.h> -#include <linux/irq.h> -#include <linux/kernel.h> -#include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> - +#include <linux/string_choices.h> #include "mtu3.h" #include "mtu3_dr.h" +#include "mtu3_debug.h" #define USB2_PORT 2 #define USB3_PORT 3 -enum mtu3_vbus_id_state { - MTU3_ID_FLOAT = 1, - MTU3_ID_GROUND, - MTU3_VBUS_OFF, - MTU3_VBUS_VALID, -}; +static inline struct ssusb_mtk *otg_sx_to_ssusb(struct otg_switch_mtk *otg_sx) +{ + return container_of(otg_sx, struct ssusb_mtk, otg_switch); +} static void toggle_opstate(struct ssusb_mtk *ssusb) { - if (!ssusb->otg_switch.is_u3_drd) { - mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION); - mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN); - } + mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION); + mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN); } /* only port0 supports dual-role mode */ @@ -112,8 +102,7 @@ static void switch_port_to_device(struct ssusb_mtk *ssusb) int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) { - struct ssusb_mtk *ssusb = - container_of(otg_sx, struct ssusb_mtk, otg_switch); + struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx); struct regulator *vbus = otg_sx->vbus; int ret; @@ -121,7 +110,7 @@ int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) if (!vbus) return 0; - dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off"); + dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, str_on_off(is_on)); if (is_on) { ret = regulator_enable(vbus); @@ -136,98 +125,78 @@ int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) return 0; } -/* - * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND - * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID - */ -static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx, - enum mtu3_vbus_id_state status) +static void ssusb_mode_sw_work(struct work_struct *work) { - struct ssusb_mtk *ssusb = - container_of(otg_sx, struct ssusb_mtk, otg_switch); + struct otg_switch_mtk *otg_sx = + container_of(work, struct otg_switch_mtk, dr_work); + struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx); struct mtu3 *mtu = ssusb->u3d; + enum usb_role desired_role = otg_sx->desired_role; + enum usb_role current_role; - dev_dbg(ssusb->dev, "mailbox state(%d)\n", status); + current_role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE; - switch (status) { - case MTU3_ID_GROUND: + if (desired_role == USB_ROLE_NONE) { + /* the default mode is host as probe does */ + desired_role = USB_ROLE_HOST; + if (otg_sx->default_role == USB_ROLE_DEVICE) + desired_role = USB_ROLE_DEVICE; + } + + if (current_role == desired_role) + return; + + dev_dbg(ssusb->dev, "set role : %s\n", usb_role_string(desired_role)); + mtu3_dbg_trace(ssusb->dev, "set role : %s", usb_role_string(desired_role)); + pm_runtime_get_sync(ssusb->dev); + + switch (desired_role) { + case USB_ROLE_HOST: + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); + mtu3_stop(mtu); switch_port_to_host(ssusb); ssusb_set_vbus(otg_sx, 1); ssusb->is_host = true; break; - case MTU3_ID_FLOAT: + case USB_ROLE_DEVICE: + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE); ssusb->is_host = false; ssusb_set_vbus(otg_sx, 0); switch_port_to_device(ssusb); - break; - case MTU3_VBUS_OFF: - mtu3_stop(mtu); - pm_relax(ssusb->dev); - break; - case MTU3_VBUS_VALID: - /* avoid suspend when works as device */ - pm_stay_awake(ssusb->dev); mtu3_start(mtu); break; + case USB_ROLE_NONE: default: - dev_err(ssusb->dev, "invalid state\n"); + dev_err(ssusb->dev, "invalid role\n"); } + pm_runtime_put(ssusb->dev); } -static void ssusb_id_work(struct work_struct *work) +static void ssusb_set_mode(struct otg_switch_mtk *otg_sx, enum usb_role role) { - struct otg_switch_mtk *otg_sx = - container_of(work, struct otg_switch_mtk, id_work); - - if (otg_sx->id_event) - ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND); - else - ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); -} + struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx); -static void ssusb_vbus_work(struct work_struct *work) -{ - struct otg_switch_mtk *otg_sx = - container_of(work, struct otg_switch_mtk, vbus_work); + if (ssusb->dr_mode != USB_DR_MODE_OTG) + return; - if (otg_sx->vbus_event) - ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); - else - ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF); + otg_sx->desired_role = role; + queue_work(system_freezable_wq, &otg_sx->dr_work); } -/* - * @ssusb_id_notifier is called in atomic context, but @ssusb_set_mailbox - * may sleep, so use work queue here - */ static int ssusb_id_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { struct otg_switch_mtk *otg_sx = container_of(nb, struct otg_switch_mtk, id_nb); - otg_sx->id_event = event; - schedule_work(&otg_sx->id_work); - - return NOTIFY_DONE; -} - -static int ssusb_vbus_notifier(struct notifier_block *nb, - unsigned long event, void *ptr) -{ - struct otg_switch_mtk *otg_sx = - container_of(nb, struct otg_switch_mtk, vbus_nb); - - otg_sx->vbus_event = event; - schedule_work(&otg_sx->vbus_work); + ssusb_set_mode(otg_sx, event ? USB_ROLE_HOST : USB_ROLE_DEVICE); return NOTIFY_DONE; } static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx) { - struct ssusb_mtk *ssusb = - container_of(otg_sx, struct ssusb_mtk, otg_switch); + struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx); struct extcon_dev *edev = otg_sx->edev; int ret; @@ -235,27 +204,20 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx) if (!edev) return 0; - otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier; - ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB, - &otg_sx->vbus_nb); - if (ret < 0) - dev_err(ssusb->dev, "failed to register notifier for USB\n"); - otg_sx->id_nb.notifier_call = ssusb_id_notifier; ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST, &otg_sx->id_nb); - if (ret < 0) + if (ret < 0) { dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n"); + return ret; + } - dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n", - extcon_get_state(edev, EXTCON_USB), - extcon_get_state(edev, EXTCON_USB_HOST)); + ret = extcon_get_state(edev, EXTCON_USB_HOST); + dev_dbg(ssusb->dev, "EXTCON_USB_HOST: %d\n", ret); /* default as host, switch to device mode if needed */ - if (extcon_get_state(edev, EXTCON_USB_HOST) == false) - ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); - if (extcon_get_state(edev, EXTCON_USB) == true) - ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); + if (!ret) + ssusb_set_mode(otg_sx, USB_ROLE_DEVICE); return 0; } @@ -266,174 +228,104 @@ 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. */ -static 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; - if (to_host) { - ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); - ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF); - ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND); - } else { - ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE); - ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); - ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); - } -} - -static int ssusb_mode_show(struct seq_file *sf, void *unused) -{ - struct ssusb_mtk *ssusb = sf->private; - - seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n", - ssusb->is_host ? "host" : "device", - ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto"); - - return 0; -} - -static int ssusb_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, ssusb_mode_show, inode->i_private); + ssusb_set_mode(otg_sx, to_host ? USB_ROLE_HOST : USB_ROLE_DEVICE); } -static ssize_t ssusb_mode_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) +void ssusb_set_force_mode(struct ssusb_mtk *ssusb, + enum mtu3_dr_force_mode mode) { - struct seq_file *sf = file->private_data; - struct ssusb_mtk *ssusb = sf->private; - char buf[16]; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; + u32 value; - if (!strncmp(buf, "host", 4) && !ssusb->is_host) { - ssusb_mode_manual_switch(ssusb, 1); - } else if (!strncmp(buf, "device", 6) && ssusb->is_host) { - ssusb_mode_manual_switch(ssusb, 0); - } else { - dev_err(ssusb->dev, "wrong or duplicated setting\n"); - return -EINVAL; + value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0)); + switch (mode) { + case MTU3_DR_FORCE_DEVICE: + value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG; + break; + case MTU3_DR_FORCE_HOST: + value |= SSUSB_U2_PORT_FORCE_IDDIG; + value &= ~SSUSB_U2_PORT_RG_IDDIG; + break; + case MTU3_DR_FORCE_NONE: + value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG); + break; + default: + return; } - - return count; + mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); } -static const struct file_operations ssusb_mode_fops = { - .open = ssusb_mode_open, - .write = ssusb_mode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int ssusb_vbus_show(struct seq_file *sf, void *unused) +static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role) { - struct ssusb_mtk *ssusb = sf->private; + struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw); struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - seq_printf(sf, "vbus state: %s\n(echo on/off)\n", - regulator_is_enabled(otg_sx->vbus) ? "on" : "off"); + ssusb_set_mode(otg_sx, role); return 0; } -static int ssusb_vbus_open(struct inode *inode, struct file *file) +static enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw) { - return single_open(file, ssusb_vbus_show, inode->i_private); -} - -static ssize_t ssusb_vbus_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - struct seq_file *sf = file->private_data; - struct ssusb_mtk *ssusb = sf->private; - struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - char buf[16]; - bool enable; + struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw); - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (kstrtobool(buf, &enable)) { - dev_err(ssusb->dev, "wrong setting\n"); - return -EINVAL; - } - - ssusb_set_vbus(otg_sx, enable); - - return count; + return ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE; } -static const struct file_operations ssusb_vbus_fops = { - .open = ssusb_vbus_open, - .write = ssusb_vbus_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void ssusb_debugfs_init(struct ssusb_mtk *ssusb) +static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx) { - struct dentry *root; + struct usb_role_switch_desc role_sx_desc = { 0 }; + struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx); + struct device *dev = ssusb->dev; + enum usb_dr_mode mode; - root = debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root); - ssusb->dbgfs_root = root; + if (!otg_sx->role_sw_used) + return 0; - debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops); - debugfs_create_file("vbus", 0644, root, ssusb, &ssusb_vbus_fops); -} + mode = usb_get_role_switch_default_mode(dev); + if (mode == USB_DR_MODE_PERIPHERAL) + otg_sx->default_role = USB_ROLE_DEVICE; + else + otg_sx->default_role = USB_ROLE_HOST; -static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb) -{ - debugfs_remove_recursive(ssusb->dbgfs_root); -} + role_sx_desc.set = ssusb_role_sw_set; + role_sx_desc.get = ssusb_role_sw_get; + role_sx_desc.fwnode = dev_fwnode(dev); + role_sx_desc.driver_data = ssusb; + role_sx_desc.allow_userspace_control = true; + otg_sx->role_sw = usb_role_switch_register(dev, &role_sx_desc); + if (IS_ERR(otg_sx->role_sw)) + return PTR_ERR(otg_sx->role_sw); -void ssusb_set_force_mode(struct ssusb_mtk *ssusb, - enum mtu3_dr_force_mode mode) -{ - u32 value; + ssusb_set_mode(otg_sx, otg_sx->default_role); - value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0)); - switch (mode) { - case MTU3_DR_FORCE_DEVICE: - value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG; - break; - case MTU3_DR_FORCE_HOST: - value |= SSUSB_U2_PORT_FORCE_IDDIG; - value &= ~SSUSB_U2_PORT_RG_IDDIG; - break; - case MTU3_DR_FORCE_NONE: - value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG); - break; - default: - return; - } - mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); + return 0; } int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + int ret = 0; - INIT_WORK(&otg_sx->id_work, ssusb_id_work); - INIT_WORK(&otg_sx->vbus_work, ssusb_vbus_work); + INIT_WORK(&otg_sx->dr_work, ssusb_mode_sw_work); if (otg_sx->manual_drd_enabled) - ssusb_debugfs_init(ssusb); + ssusb_dr_debugfs_init(ssusb); + else if (otg_sx->role_sw_used) + ret = ssusb_role_sw_register(otg_sx); else - ssusb_extcon_register(otg_sx); + ret = ssusb_extcon_register(otg_sx); - return 0; + return ret; } void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - if (otg_sx->manual_drd_enabled) - ssusb_debugfs_exit(ssusb); - - cancel_work_sync(&otg_sx->id_work); - cancel_work_sync(&otg_sx->vbus_work); + cancel_work_sync(&otg_sx->dr_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 50702fdcde28..e325508bddf4 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtu3_dr.h - dual role switch and host glue layer header * @@ -16,8 +16,8 @@ int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn); void ssusb_host_exit(struct ssusb_mtk *ssusb); int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, struct device_node *dn); -int ssusb_host_enable(struct ssusb_mtk *ssusb); -int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend); +int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped); +int ssusb_host_suspend(struct ssusb_mtk *ssusb); void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable); #else @@ -38,12 +38,12 @@ static inline int ssusb_wakeup_of_property_parse( return 0; } -static inline int ssusb_host_enable(struct ssusb_mtk *ssusb) +static inline int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped) { return 0; } -static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) +static inline int ssusb_host_suspend(struct ssusb_mtk *ssusb) { return 0; } @@ -57,6 +57,10 @@ static inline void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) #if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE) int ssusb_gadget_init(struct ssusb_mtk *ssusb); void ssusb_gadget_exit(struct ssusb_mtk *ssusb); +int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg); +int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg); +bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb); + #else static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb) { @@ -65,12 +69,31 @@ static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb) static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb) {} + +static inline int +ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + return 0; +} + +static inline int +ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + return 0; +} + +static inline bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb) +{ + return true; +} + #endif #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_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); @@ -85,6 +108,9 @@ 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_switch(struct ssusb_mtk *ssusb, int to_host) +{} + static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) { return 0; diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index bbcd3332471d..bf73fbc29976 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -7,44 +7,40 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ +#include <linux/string_choices.h> #include "mtu3.h" +#include "mtu3_trace.h" void mtu3_req_complete(struct mtu3_ep *mep, struct usb_request *req, int status) __releases(mep->mtu->lock) __acquires(mep->mtu->lock) { - struct mtu3_request *mreq; - struct mtu3 *mtu; - int busy = mep->busy; + struct mtu3_request *mreq = to_mtu3_request(req); + struct mtu3 *mtu = mreq->mtu; - mreq = to_mtu3_request(req); list_del(&mreq->list); - if (mreq->request.status == -EINPROGRESS) - mreq->request.status = status; + if (req->status == -EINPROGRESS) + req->status = status; - mtu = mreq->mtu; - mep->busy = 1; - spin_unlock(&mtu->lock); + trace_mtu3_req_complete(mreq); /* ep0 makes use of PIO, needn't unmap it */ if (mep->epnum) usb_gadget_unmap_request(&mtu->g, req, mep->is_in); - dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", mep->name, - req, req->status, mreq->request.actual, mreq->request.length); - - usb_gadget_giveback_request(&mep->ep, &mreq->request); + dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", + mep->name, req, req->status, req->actual, req->length); + spin_unlock(&mtu->lock); + usb_gadget_giveback_request(&mep->ep, req); spin_lock(&mtu->lock); - mep->busy = busy; } static void nuke(struct mtu3_ep *mep, const int status) { struct mtu3_request *mreq = NULL; - mep->busy = 1; if (list_empty(&mep->req_list)) return; @@ -69,14 +65,12 @@ static int mtu3_ep_enable(struct mtu3_ep *mep) u32 interval = 0; u32 mult = 0; u32 burst = 0; - int max_packet; int ret; desc = mep->desc; comp_desc = mep->comp_desc; mep->type = usb_endpoint_type(desc); - max_packet = usb_endpoint_maxp(desc); - mep->maxp = max_packet & GENMASK(10, 0); + mep->maxp = usb_endpoint_maxp(desc); switch (mtu->g.speed) { case USB_SPEED_SUPER: @@ -84,7 +78,7 @@ static int mtu3_ep_enable(struct mtu3_ep *mep) if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) { interval = desc->bInterval; - interval = clamp_val(interval, 1, 16) - 1; + interval = clamp_val(interval, 1, 16); if (usb_endpoint_xfer_isoc(desc) && comp_desc) mult = comp_desc->bmAttributes; } @@ -96,10 +90,17 @@ static int mtu3_ep_enable(struct mtu3_ep *mep) if (usb_endpoint_xfer_isoc(desc) || usb_endpoint_xfer_int(desc)) { interval = desc->bInterval; - interval = clamp_val(interval, 1, 16) - 1; - burst = (max_packet & GENMASK(12, 11)) >> 11; + interval = clamp_val(interval, 1, 16); + mult = usb_endpoint_maxp_mult(desc) - 1; } break; + case USB_SPEED_FULL: + if (usb_endpoint_xfer_isoc(desc)) + interval = clamp_val(desc->bInterval, 1, 16); + else if (usb_endpoint_xfer_int(desc)) + interval = clamp_val(desc->bInterval, 1, 255); + + break; default: break; /*others are ignored */ } @@ -133,10 +134,9 @@ static int mtu3_ep_disable(struct mtu3_ep *mep) { struct mtu3 *mtu = mep->mtu; - mtu3_qmu_stop(mep); - /* abort all pending requests */ nuke(mep, -ESHUTDOWN); + mtu3_qmu_stop(mep); mtu3_deconfig_ep(mtu, mep); mtu3_gpd_ring_free(mep); @@ -192,15 +192,14 @@ static int mtu3_gadget_ep_enable(struct usb_ep *ep, if (ret) goto error; - mep->busy = 0; - mep->wedged = 0; - mep->flags |= MTU3_EP_ENABLED; + mep->flags = MTU3_EP_ENABLED; mtu->active_ep++; error: spin_unlock_irqrestore(&mtu->lock, flags); dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep); + trace_mtu3_gadget_ep_enable(mep); return ret; } @@ -212,6 +211,7 @@ static int mtu3_gadget_ep_disable(struct usb_ep *ep) unsigned long flags; dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name); + trace_mtu3_gadget_ep_disable(mep); if (!(mep->flags & MTU3_EP_ENABLED)) { dev_warn(mtu->dev, "%s is already disabled\n", mep->name); @@ -220,7 +220,7 @@ static int mtu3_gadget_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&mtu->lock, flags); mtu3_ep_disable(mep); - mep->flags &= ~MTU3_EP_ENABLED; + mep->flags = 0; mtu->active_ep--; spin_unlock_irqrestore(&(mtu->lock), flags); @@ -242,35 +242,32 @@ struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) mreq->request.dma = DMA_ADDR_INVALID; mreq->epnum = mep->epnum; mreq->mep = mep; + INIT_LIST_HEAD(&mreq->list); + trace_mtu3_alloc_request(mreq); return &mreq->request; } void mtu3_free_request(struct usb_ep *ep, struct usb_request *req) { - kfree(to_mtu3_request(req)); + struct mtu3_request *mreq = to_mtu3_request(req); + + trace_mtu3_free_request(mreq); + kfree(mreq); } static int mtu3_gadget_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { - struct mtu3_ep *mep; - struct mtu3_request *mreq; - struct mtu3 *mtu; + struct mtu3_ep *mep = to_mtu3_ep(ep); + struct mtu3_request *mreq = to_mtu3_request(req); + struct mtu3 *mtu = mep->mtu; unsigned long flags; int ret = 0; - if (!ep || !req) - return -EINVAL; - if (!req->buf) return -ENODATA; - mep = to_mtu3_ep(ep); - mtu = mep->mtu; - mreq = to_mtu3_request(req); - mreq->mtu = mtu; - if (mreq->mep != mep) return -EINVAL; @@ -278,10 +275,12 @@ static int mtu3_gadget_queue(struct usb_ep *ep, __func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name, mreq, ep->maxpacket, mreq->request.length); - if (req->length > GPD_BUF_SIZE) { + if (req->length > GPD_BUF_SIZE || + (mtu->gen2cp && req->length > GPD_BUF_SIZE_EL)) { dev_warn(mtu->dev, "req length > supported MAX:%d requested:%d\n", - GPD_BUF_SIZE, req->length); + mtu->gen2cp ? GPD_BUF_SIZE_EL : GPD_BUF_SIZE, + req->length); return -EOPNOTSUPP; } @@ -292,6 +291,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, return -ESHUTDOWN; } + mreq->mtu = mtu; mreq->request.actual = 0; mreq->request.status = -EINPROGRESS; @@ -314,6 +314,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, error: spin_unlock_irqrestore(&mtu->lock, flags); + trace_mtu3_gadget_queue(mreq); return ret; } @@ -323,14 +324,15 @@ static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req) struct mtu3_ep *mep = to_mtu3_ep(ep); struct mtu3_request *mreq = to_mtu3_request(req); struct mtu3_request *r; + struct mtu3 *mtu = mep->mtu; unsigned long flags; int ret = 0; - struct mtu3 *mtu = mep->mtu; - if (!ep || !req || mreq->mep != mep) + if (mreq->mep != mep) return -EINVAL; dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req); + trace_mtu3_gadget_dequeue(mreq); spin_lock_irqsave(&mtu->lock, flags); @@ -366,9 +368,6 @@ static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value) unsigned long flags; int ret = 0; - if (!ep) - return -EINVAL; - dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name); spin_lock_irqsave(&mtu->lock, flags); @@ -392,7 +391,7 @@ static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value) goto done; } } else { - mep->wedged = 0; + mep->flags &= ~MTU3_EP_WEDGE; } dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear"); @@ -401,6 +400,7 @@ static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value) done: spin_unlock_irqrestore(&mtu->lock, flags); + trace_mtu3_gadget_ep_set_halt(mep); return ret; } @@ -410,10 +410,7 @@ static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep) { struct mtu3_ep *mep = to_mtu3_ep(ep); - if (!ep) - return -EINVAL; - - mep->wedged = 1; + mep->flags |= MTU3_EP_WEDGE; return usb_ep_set_halt(ep); } @@ -436,6 +433,13 @@ static int mtu3_gadget_get_frame(struct usb_gadget *gadget) return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM); } +static void function_wake_notif(struct mtu3 *mtu, u8 intf) +{ + mtu3_writel(mtu->mac_base, U3D_DEV_NOTIF_0, + TYPE_FUNCTION_WAKE | DEV_NOTIF_VAL_FW(intf)); + mtu3_setbits(mtu->mac_base, U3D_DEV_NOTIF_0, SEND_DEV_NOTIF); +} + static int mtu3_gadget_wakeup(struct usb_gadget *gadget) { struct mtu3 *mtu = gadget_to_mtu3(gadget); @@ -449,7 +453,18 @@ static int mtu3_gadget_wakeup(struct usb_gadget *gadget) spin_lock_irqsave(&mtu->lock, flags); if (mtu->g.speed >= USB_SPEED_SUPER) { + /* + * class driver may do function wakeup even UFP is in U0, + * and UX_EXIT only takes effect in U1/U2/U3; + */ mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT); + /* + * Assume there's only one function on the composite device + * and enable remote wake for the first interface. + * FIXME if the IAD (interface association descriptor) shows + * there is more than one function. + */ + function_wake_notif(mtu, 0); } else { mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME); spin_unlock_irqrestore(&mtu->lock, flags); @@ -476,7 +491,9 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) unsigned long flags; dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__, - is_on ? "on" : "off", mtu->is_active ? "" : "in"); + str_on_off(is_on), mtu->is_active ? "" : "in"); + + pm_runtime_get_sync(mtu->dev); /* we'd rather not pullup unless the device is active. */ spin_lock_irqsave(&mtu->lock, flags); @@ -491,6 +508,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) } spin_unlock_irqrestore(&mtu->lock, flags); + pm_runtime_put(mtu->dev); return 0; } @@ -508,6 +526,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget, } dev_dbg(mtu->dev, "bind driver %s\n", driver->function); + pm_runtime_get_sync(mtu->dev); spin_lock_irqsave(&mtu->lock, flags); @@ -518,6 +537,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget, mtu3_start(mtu); spin_unlock_irqrestore(&mtu->lock, flags); + pm_runtime_put(mtu->dev); return 0; } @@ -573,9 +593,35 @@ static int mtu3_gadget_stop(struct usb_gadget *g) spin_unlock_irqrestore(&mtu->lock, flags); + synchronize_irq(mtu->irq); return 0; } +static void +mtu3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed) +{ + struct mtu3 *mtu = gadget_to_mtu3(g); + unsigned long flags; + + dev_dbg(mtu->dev, "%s %s\n", __func__, usb_speed_string(speed)); + + spin_lock_irqsave(&mtu->lock, flags); + mtu->speed = speed; + spin_unlock_irqrestore(&mtu->lock, flags); +} + +static void mtu3_gadget_async_callbacks(struct usb_gadget *g, bool enable) +{ + struct mtu3 *mtu = gadget_to_mtu3(g); + unsigned long flags; + + dev_dbg(mtu->dev, "%s %s\n", __func__, enable ? "en" : "dis"); + + spin_lock_irqsave(&mtu->lock, flags); + mtu->async_callbacks = enable; + spin_unlock_irqrestore(&mtu->lock, flags); +} + static const struct usb_gadget_ops mtu3_gadget_ops = { .get_frame = mtu3_gadget_get_frame, .wakeup = mtu3_gadget_wakeup, @@ -583,6 +629,8 @@ static const struct usb_gadget_ops mtu3_gadget_ops = { .pullup = mtu3_gadget_pullup, .udc_start = mtu3_gadget_start, .udc_stop = mtu3_gadget_stop, + .udc_set_speed = mtu3_gadget_set_speed, + .udc_async_callbacks = mtu3_gadget_async_callbacks, }; static void mtu3_state_reset(struct mtu3 *mtu) @@ -658,23 +706,18 @@ static void mtu3_gadget_init_eps(struct mtu3 *mtu) int mtu3_gadget_setup(struct mtu3 *mtu) { - int ret; - mtu->g.ops = &mtu3_gadget_ops; mtu->g.max_speed = mtu->max_speed; mtu->g.speed = USB_SPEED_UNKNOWN; mtu->g.sg_supported = 0; mtu->g.name = MTU3_DRIVER_NAME; + mtu->g.irq = mtu->irq; mtu->is_active = 0; mtu->delayed_status = false; mtu3_gadget_init_eps(mtu); - ret = usb_add_gadget_udc(mtu->dev, &mtu->g); - if (ret) - dev_err(mtu->dev, "failed to register udc\n"); - - return ret; + return usb_add_gadget_udc(mtu->dev, &mtu->g); } void mtu3_gadget_cleanup(struct mtu3 *mtu) @@ -685,7 +728,7 @@ void mtu3_gadget_cleanup(struct mtu3 *mtu) void mtu3_gadget_resume(struct mtu3 *mtu) { dev_dbg(mtu->dev, "gadget RESUME\n"); - if (mtu->gadget_driver && mtu->gadget_driver->resume) { + if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->resume) { spin_unlock(&mtu->lock); mtu->gadget_driver->resume(&mtu->g); spin_lock(&mtu->lock); @@ -696,7 +739,7 @@ void mtu3_gadget_resume(struct mtu3 *mtu) void mtu3_gadget_suspend(struct mtu3 *mtu) { dev_dbg(mtu->dev, "gadget SUSPEND\n"); - if (mtu->gadget_driver && mtu->gadget_driver->suspend) { + if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->suspend) { spin_unlock(&mtu->lock); mtu->gadget_driver->suspend(&mtu->g); spin_lock(&mtu->lock); @@ -707,7 +750,7 @@ void mtu3_gadget_suspend(struct mtu3 *mtu) void mtu3_gadget_disconnect(struct mtu3 *mtu) { dev_dbg(mtu->dev, "gadget DISCONNECT\n"); - if (mtu->gadget_driver && mtu->gadget_driver->disconnect) { + if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->disconnect) { spin_unlock(&mtu->lock); mtu->gadget_driver->disconnect(&mtu->g); spin_lock(&mtu->lock); diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 7cb7ac980446..e4fd1bb14a55 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -11,6 +11,8 @@ #include <linux/usb/composite.h> #include "mtu3.h" +#include "mtu3_debug.h" +#include "mtu3_trace.h" /* ep0 is always mtu3->in_eps[0] */ #define next_ep0_request(mtu) next_request((mtu)->ep0) @@ -64,7 +66,7 @@ __acquires(mtu->lock) { int ret; - if (!mtu->gadget_driver) + if (!mtu->gadget_driver || !mtu->async_callbacks) return -EOPNOTSUPP; spin_unlock(&mtu->lock); @@ -151,6 +153,15 @@ static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy) set ? "SEND" : "CLEAR", decode_ep0_state(mtu)); } +static void ep0_do_status_stage(struct mtu3 *mtu) +{ + void __iomem *mbase = mtu->mac_base; + u32 value; + + value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; + mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); +} + static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq); static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req) @@ -215,6 +226,8 @@ ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) break; case USB_RECIP_INTERFACE: + /* status of function remote wakeup, forward request */ + handled = 0; break; case USB_RECIP_ENDPOINT: epnum = (u8) le16_to_cpu(setup->wIndex); @@ -267,20 +280,20 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup) u32 value; switch (le16_to_cpu(setup->wIndex) >> 8) { - case TEST_J: - dev_dbg(mtu->dev, "TEST_J\n"); + case USB_TEST_J: + dev_dbg(mtu->dev, "USB_TEST_J\n"); mtu->test_mode_nr = TEST_J_MODE; break; - case TEST_K: - dev_dbg(mtu->dev, "TEST_K\n"); + case USB_TEST_K: + dev_dbg(mtu->dev, "USB_TEST_K\n"); mtu->test_mode_nr = TEST_K_MODE; break; - case TEST_SE0_NAK: - dev_dbg(mtu->dev, "TEST_SE0_NAK\n"); + case USB_TEST_SE0_NAK: + dev_dbg(mtu->dev, "USB_TEST_SE0_NAK\n"); mtu->test_mode_nr = TEST_SE0_NAK_MODE; break; - case TEST_PACKET: - dev_dbg(mtu->dev, "TEST_PACKET\n"); + case USB_TEST_PACKET: + dev_dbg(mtu->dev, "USB_TEST_PACKET\n"); mtu->test_mode_nr = TEST_PACKET_MODE; break; default: @@ -295,8 +308,7 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup) ep0_load_test_packet(mtu); /* send status before entering test mode. */ - value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; - mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); + ep0_do_status_stage(mtu); /* wait for ACK status sent by host */ readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value, @@ -387,10 +399,8 @@ static int ep0_handle_feature(struct mtu3 *mtu, /* superspeed only */ if (value == USB_INTRF_FUNC_SUSPEND && mtu->g.speed >= USB_SPEED_SUPER) { - /* - * forward the request because function drivers - * should handle it - */ + /* forward the request for function suspend */ + mtu->may_wakeup = !!(index & USB_INTRF_FUNC_SUSPEND_RW); handled = 0; } break; @@ -407,7 +417,7 @@ static int ep0_handle_feature(struct mtu3 *mtu, handled = 1; /* ignore request if endpoint is wedged */ - if (mep->wedged) + if (mep->flags & MTU3_EP_WEDGE) break; mtu3_ep_stall_set(mep, set); @@ -630,10 +640,10 @@ __acquires(mtu->lock) { struct usb_ctrlrequest setup; struct mtu3_request *mreq; - void __iomem *mbase = mtu->mac_base; int handled = 0; ep0_read_setup(mtu, &setup); + trace_mtu3_handle_setup(&setup); if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) handled = handle_standard_request(mtu, &setup); @@ -661,14 +671,19 @@ finish: if (mtu->test_mode) { ; /* nothing to do */ } else if (handled == USB_GADGET_DELAYED_STATUS) { - /* handle the delay STATUS phase till receive ep_queue on ep0 */ - mtu->delayed_status = true; - } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ - mtu3_writel(mbase, U3D_EP0CSR, - (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS) - | EP0_SETUPPKTRDY | EP0_DATAEND); + mreq = next_ep0_request(mtu); + if (mreq) { + /* already asked us to continue delayed status */ + ep0_do_status_stage(mtu); + ep0_req_giveback(mtu, &mreq->request); + } else { + /* do delayed STATUS stage till receive ep0_queue */ + mtu->delayed_status = true; + } + } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ + ep0_do_status_stage(mtu); /* complete zlp request directly */ mreq = next_ep0_request(mtu); if (mreq && !mreq->request.length) @@ -710,6 +725,7 @@ irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu) ret = IRQ_HANDLED; } dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu)); + mtu3_dbg_trace(mtu->dev, "ep0_state %s", decode_ep0_state(mtu)); switch (mtu->ep0_state) { case MU3D_EP0_STATE_TX: @@ -798,12 +814,9 @@ static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq) } if (mtu->delayed_status) { - u32 csr; mtu->delayed_status = false; - csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; - csr |= EP0_SETUPPKTRDY | EP0_DATAEND; - mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); + ep0_do_status_stage(mtu); /* needn't giveback the request for handling delay STATUS */ return 0; } diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index c871b94f3e6f..7c657ea2dabd 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -8,11 +8,11 @@ */ #include <linux/clk.h> -#include <linux/iopoll.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/regmap.h> #include "mtu3.h" @@ -24,6 +24,28 @@ #define WC1_IS_EN BIT(25) #define WC1_IS_P BIT(6) /* polarity for ip sleep */ +/* mt8183 */ +#define PERI_WK_CTRL0 0x0 +#define WC0_IS_C(x) ((u32)(((x) & 0xf) << 28)) /* cycle debounce */ +#define WC0_IS_P BIT(12) /* polarity */ +#define WC0_IS_EN BIT(6) + +/* mt8192 */ +#define WC0_SSUSB0_CDEN BIT(6) +#define WC0_IS_SPM_EN BIT(1) + +/* mt8195 */ +#define PERI_WK_CTRL0_8195 0x04 +#define WC0_IS_P_95 BIT(30) /* polarity */ +#define WC0_IS_C_95(x) ((u32)(((x) & 0x7) << 27)) +#define WC0_IS_EN_P3_95 BIT(26) +#define WC0_IS_EN_P2_95 BIT(25) + +#define PERI_WK_CTRL1_8195 0x20 +#define WC1_IS_C_95(x) ((u32)(((x) & 0xf) << 28)) +#define WC1_IS_P_95 BIT(12) +#define WC1_IS_EN_P0_95 BIT(6) + /* mt2712 etc */ #define PERI_SSUSB_SPM_CTRL 0x0 #define SSC_IP_SLEEP_EN BIT(4) @@ -32,6 +54,11 @@ enum ssusb_uwk_vers { SSUSB_UWK_V1 = 1, SSUSB_UWK_V2, + SSUSB_UWK_V1_1 = 101, /* specific revision 1.01 */ + SSUSB_UWK_V1_2, /* specific revision 1.02 */ + SSUSB_UWK_V1_3, /* mt8195 IP0 */ + SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */ + SSUSB_UWK_V1_6, /* mt8195 IP3 */ }; /* @@ -48,6 +75,31 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; break; + case SSUSB_UWK_V1_1: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; + msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P; + val = enable ? (WC0_IS_EN | WC0_IS_C(0x1)) : 0; + break; + case SSUSB_UWK_V1_2: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; + msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN; + val = enable ? msk : 0; + break; + case SSUSB_UWK_V1_3: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8195; + msk = WC1_IS_EN_P0_95 | WC1_IS_C_95(0xf) | WC1_IS_P_95; + val = enable ? (WC1_IS_EN_P0_95 | WC1_IS_C_95(0x1)) : 0; + break; + case SSUSB_UWK_V1_5: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8195; + msk = WC0_IS_EN_P2_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; + val = enable ? (WC0_IS_EN_P2_95 | WC0_IS_C_95(0x1)) : 0; + break; + case SSUSB_UWK_V1_6: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8195; + msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; + val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0; + break; case SSUSB_UWK_V2: reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; @@ -104,12 +156,12 @@ static void host_ports_num_get(struct ssusb_mtk *ssusb) } /* only configure ports will be used later */ -int ssusb_host_enable(struct ssusb_mtk *ssusb) +static int ssusb_host_enable(struct ssusb_mtk *ssusb) { void __iomem *ibase = ssusb->ippc_base; int num_u3p = ssusb->u3_ports; int num_u2p = ssusb->u2_ports; - int u3_ports_disabed; + int u3_ports_disabled; u32 check_clk; u32 value; int i; @@ -118,10 +170,10 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); /* power on and enable u3 ports except skipped ones */ - u3_ports_disabed = 0; + u3_ports_disabled = 0; for (i = 0; i < num_u3p; i++) { if ((0x1 << i) & ssusb->u3p_dis_msk) { - u3_ports_disabed++; + u3_ports_disabled++; continue; } @@ -133,6 +185,9 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) /* power on and enable all u2 ports */ for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & ssusb->u2p_dis_msk) + continue; + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS); value |= SSUSB_U2_PORT_HOST_SEL; @@ -140,19 +195,18 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) } check_clk = SSUSB_XHCI_RST_B_STS; - if (num_u3p > u3_ports_disabed) + if (num_u3p > u3_ports_disabled) check_clk = SSUSB_U3_MAC_RST_B_STS; return ssusb_check_clocks(ssusb, check_clk); } -int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) +static int ssusb_host_disable(struct ssusb_mtk *ssusb) { void __iomem *ibase = ssusb->ippc_base; int num_u3p = ssusb->u3_ports; int num_u2p = ssusb->u2_ports; u32 value; - int ret; int i; /* power down and disable u3 ports except skipped ones */ @@ -161,38 +215,105 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) continue; value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); - value |= SSUSB_U3_PORT_PDN; - value |= suspend ? 0 : SSUSB_U3_PORT_DIS; + value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS; mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); } - /* power down and disable all u2 ports */ + /* power down and disable u2 ports except skipped ones */ for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & ssusb->u2p_dis_msk) + continue; + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); - value |= SSUSB_U2_PORT_PDN; - value |= suspend ? 0 : SSUSB_U2_PORT_DIS; + value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS; mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); } /* power down host ip */ mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); - if (!suspend) - return 0; + return 0; +} - /* wait for host ip to sleep */ - ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value, - (value & SSUSB_IP_SLEEP_STS), 100, 100000); - if (ret) - dev_err(ssusb->dev, "ip sleep failed!!!\n"); +int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped) +{ + void __iomem *ibase = ssusb->ippc_base; + int u3p_skip_msk = ssusb->u3p_dis_msk; + int u2p_skip_msk = ssusb->u2p_dis_msk; + int num_u3p = ssusb->u3_ports; + int num_u2p = ssusb->u2_ports; + u32 value; + int i; + + if (p0_skipped) { + u2p_skip_msk |= 0x1; + if (ssusb->otg_switch.is_u3_drd) + u3p_skip_msk |= 0x1; + } + + /* power on host ip */ + mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); + + /* power on u3 ports except skipped ones */ + for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & u3p_skip_msk) + continue; - return ret; + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); + value &= ~SSUSB_U3_PORT_PDN; + mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); + } + + /* power on all u2 ports except skipped ones */ + for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & u2p_skip_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); + value &= ~SSUSB_U2_PORT_PDN; + mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); + } + + return 0; } -static void ssusb_host_setup(struct ssusb_mtk *ssusb) +/* here not skip port0 due to PDN can be set repeatedly */ +int ssusb_host_suspend(struct ssusb_mtk *ssusb) { - struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + void __iomem *ibase = ssusb->ippc_base; + int num_u3p = ssusb->u3_ports; + int num_u2p = ssusb->u2_ports; + u32 value; + int i; + + /* power down u3 ports except skipped ones */ + for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & ssusb->u3p_dis_msk) + continue; + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); + value |= SSUSB_U3_PORT_PDN; + mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); + } + + /* power down u2 ports except skipped ones */ + for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & ssusb->u2p_dis_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); + value |= SSUSB_U2_PORT_PDN; + mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); + } + + /* power down host ip */ + mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); + + return 0; +} + +static void ssusb_host_setup(struct ssusb_mtk *ssusb) +{ host_ports_num_get(ssusb); /* @@ -200,9 +321,7 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb) * if support OTG, gadget driver will switch port0 to device mode */ ssusb_host_enable(ssusb); - - if (otg_sx->manual_drd_enabled) - ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); /* if port0 supports dual-role, works as host mode by default */ ssusb_set_vbus(&ssusb->otg_switch, 1); @@ -213,7 +332,7 @@ static void ssusb_host_cleanup(struct ssusb_mtk *ssusb) if (ssusb->is_host) ssusb_set_vbus(&ssusb->otg_switch, 0); - ssusb_host_disable(ssusb, false); + ssusb_host_disable(ssusb); } /* diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 1d65b7476f23..ee30ae0a4b54 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions * @@ -49,6 +49,7 @@ #define U3D_QCR1 (SSUSB_DEV_BASE + 0x0404) #define U3D_QCR2 (SSUSB_DEV_BASE + 0x0408) #define U3D_QCR3 (SSUSB_DEV_BASE + 0x040C) +#define U3D_QFCR (SSUSB_DEV_BASE + 0x0428) #define U3D_TXQHIAR1 (SSUSB_DEV_BASE + 0x0484) #define U3D_RXQHIAR1 (SSUSB_DEV_BASE + 0x04C4) @@ -127,17 +128,30 @@ #define TX_FIFOEMPTY BIT(24) #define TX_SENTSTALL BIT(22) #define TX_SENDSTALL BIT(21) +#define TX_FLUSHFIFO BIT(20) #define TX_TXPKTRDY BIT(16) #define TX_TXMAXPKTSZ_MSK GENMASK(10, 0) #define TX_TXMAXPKTSZ(x) ((x) & TX_TXMAXPKTSZ_MSK) #define TX_W1C_BITS (~(TX_SENTSTALL)) /* U3D_TX1CSR1 */ -#define TX_MULT(x) (((x) & 0x3) << 22) -#define TX_MAX_PKT(x) (((x) & 0x3f) << 16) +#define TX_MAX_PKT_G2(x) (((x) & 0xff) << 24) +#define TX_MULT_G2(x) (((x) & 0x7) << 21) +#define TX_MULT_OG(x) (((x) & 0x3) << 22) +#define TX_MAX_PKT_OG(x) (((x) & 0x3f) << 16) #define TX_SLOT(x) (((x) & 0x3f) << 8) #define TX_TYPE(x) (((x) & 0x3) << 4) #define TX_SS_BURST(x) (((x) & 0xf) << 0) +#define TX_MULT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? TX_MULT_G2(x_) : TX_MULT_OG(x_); \ +}) +#define TX_MAX_PKT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? TX_MAX_PKT_G2(x_) : TX_MAX_PKT_OG(x_); \ +}) /* for TX_TYPE & RX_TYPE */ #define TYPE_BULK (0x0) @@ -160,11 +174,23 @@ #define RX_W1C_BITS (~(RX_SENTSTALL | RX_RXPKTRDY)) /* U3D_RX1CSR1 */ -#define RX_MULT(x) (((x) & 0x3) << 22) -#define RX_MAX_PKT(x) (((x) & 0x3f) << 16) +#define RX_MAX_PKT_G2(x) (((x) & 0xff) << 24) +#define RX_MULT_G2(x) (((x) & 0x7) << 21) +#define RX_MULT_OG(x) (((x) & 0x3) << 22) +#define RX_MAX_PKT_OG(x) (((x) & 0x3f) << 16) #define RX_SLOT(x) (((x) & 0x3f) << 8) #define RX_TYPE(x) (((x) & 0x3) << 4) #define RX_SS_BURST(x) (((x) & 0xf) << 0) +#define RX_MULT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? RX_MULT_G2(x_) : RX_MULT_OG(x_); \ +}) +#define RX_MAX_PKT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? RX_MAX_PKT_G2(x_) : RX_MAX_PKT_OG(x_); \ +}) /* U3D_RX1CSR2 */ #define RX_BINTERVAL(x) (((x) & 0xff) << 24) @@ -265,6 +291,7 @@ #define U3D_LTSSM_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0010) #define U3D_USB3_CONFIG (SSUSB_USB3_MAC_CSR_BASE + 0x001C) +#define U3D_LINK_STATE_MACHINE (SSUSB_USB3_MAC_CSR_BASE + 0x0134) #define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C) #define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140) @@ -282,6 +309,9 @@ /* U3D_USB3_CONFIG */ #define USB3_EN BIT(0) +/* U3D_LINK_STATE_MACHINE */ +#define LTSSM_STATE(x) ((x) & 0x1f) + /* U3D_LTSSM_INTR_ENABLE */ /* U3D_LTSSM_INTR */ #define U3_RESUME_INTR BIT(18) @@ -312,6 +342,8 @@ #define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C) #define U3D_LINK_POWER_CONTROL (SSUSB_USB3_SYS_CSR_BASE + 0x0210) #define U3D_LINK_ERR_COUNT (SSUSB_USB3_SYS_CSR_BASE + 0x0214) +#define U3D_DEV_NOTIF_0 (SSUSB_USB3_SYS_CSR_BASE + 0x0290) +#define U3D_DEV_NOTIF_1 (SSUSB_USB3_SYS_CSR_BASE + 0x0294) /*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/ @@ -336,6 +368,20 @@ #define CLR_LINK_ERR_CNT BIT(16) #define LINK_ERROR_COUNT GENMASK(15, 0) +/* U3D_DEV_NOTIF_0 */ +#define DEV_NOTIF_TYPE_SPECIFIC_LOW_MSK GENMASK(31, 8) +#define DEV_NOTIF_VAL_FW(x) (((x) & 0xff) << 8) +#define DEV_NOTIF_VAL_LTM(x) (((x) & 0xfff) << 8) +#define DEV_NOTIF_VAL_IAM(x) (((x) & 0xffff) << 8) +#define DEV_NOTIF_TYPE_MSK GENMASK(7, 4) +/* Notification Type */ +#define TYPE_FUNCTION_WAKE (0x1 << 4) +#define TYPE_LATENCY_TOLERANCE_MESSAGE (0x2 << 4) +#define TYPE_BUS_INTERVAL_ADJUST_MESSAGE (0x3 << 4) +#define TYPE_HOST_ROLE_REQUEST (0x4 << 4) +#define TYPE_SUBLINK_SPEED (0x5 << 4) +#define SEND_DEV_NOTIF BIT(0) + /*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/ #define U3D_POWER_MANAGEMENT (SSUSB_USB2_CSR_BASE + 0x0004) @@ -347,6 +393,7 @@ #define U3D_USB20_FRAME_NUM (SSUSB_USB2_CSR_BASE + 0x003C) #define U3D_USB20_LPM_PARAMETER (SSUSB_USB2_CSR_BASE + 0x0044) #define U3D_USB20_MISC_CONTROL (SSUSB_USB2_CSR_BASE + 0x004C) +#define U3D_USB20_OPSTATE (SSUSB_USB2_CSR_BASE + 0x0060) /*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/ @@ -419,6 +466,13 @@ #define U3D_SSUSB_DEV_RST_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x0098) #define U3D_SSUSB_HW_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A0) #define U3D_SSUSB_HW_SUB_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A4) +#define U3D_SSUSB_IP_TRUNK_VERS (U3D_SSUSB_HW_SUB_ID) +#define U3D_SSUSB_PRB_CTRL0 (SSUSB_SIFSLV_IPPC_BASE + 0x00B0) +#define U3D_SSUSB_PRB_CTRL1 (SSUSB_SIFSLV_IPPC_BASE + 0x00B4) +#define U3D_SSUSB_PRB_CTRL2 (SSUSB_SIFSLV_IPPC_BASE + 0x00B8) +#define U3D_SSUSB_PRB_CTRL3 (SSUSB_SIFSLV_IPPC_BASE + 0x00BC) +#define U3D_SSUSB_PRB_CTRL4 (SSUSB_SIFSLV_IPPC_BASE + 0x00C0) +#define U3D_SSUSB_PRB_CTRL5 (SSUSB_SIFSLV_IPPC_BASE + 0x00C4) #define U3D_SSUSB_IP_SPARE0 (SSUSB_SIFSLV_IPPC_BASE + 0x00C8) /*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/ @@ -483,4 +537,7 @@ /* U3D_SSUSB_DEV_RST_CTRL */ #define SSUSB_DEV_SW_RST BIT(0) +/* U3D_SSUSB_IP_TRUNK_VERS */ +#define IP_TRUNK_VERS(x) (((x) >> 16) & 0xffff) + #endif /* _SSUSB_HW_REGS_H_ */ diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index e086630e41a9..cc8a864dbd63 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -5,7 +5,6 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ -#include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/iopoll.h> #include <linux/kernel.h> @@ -13,9 +12,12 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/reset.h> #include "mtu3.h" #include "mtu3_dr.h" +#include "mtu3_debug.h" /* u2-port0 should be powered on and enabled; */ int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks) @@ -44,6 +46,32 @@ int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks) return 0; } +static int wait_for_ip_sleep(struct ssusb_mtk *ssusb) +{ + bool sleep_check = true; + u32 value; + int ret; + + if (!ssusb->is_host) + sleep_check = ssusb_gadget_ip_sleep_check(ssusb); + + if (!sleep_check) + return 0; + + /* wait for ip enter sleep mode */ + ret = readl_poll_timeout(ssusb->ippc_base + U3D_SSUSB_IP_PW_STS1, value, + (value & SSUSB_IP_SLEEP_STS), 100, 100000); + if (ret) { + dev_err(ssusb->dev, "ip sleep failed!!!\n"); + ret = -EBUSY; + } else { + /* workaround: avoid wrong wakeup signal latch for some soc */ + usleep_range(100, 200); + } + + return ret; +} + static int ssusb_phy_init(struct ssusb_mtk *ssusb) { int i; @@ -100,54 +128,6 @@ static void ssusb_phy_power_off(struct ssusb_mtk *ssusb) phy_power_off(ssusb->phys[i]); } -static int ssusb_clks_enable(struct ssusb_mtk *ssusb) -{ - int ret; - - ret = clk_prepare_enable(ssusb->sys_clk); - if (ret) { - dev_err(ssusb->dev, "failed to enable sys_clk\n"); - goto sys_clk_err; - } - - ret = clk_prepare_enable(ssusb->ref_clk); - if (ret) { - dev_err(ssusb->dev, "failed to enable ref_clk\n"); - goto ref_clk_err; - } - - ret = clk_prepare_enable(ssusb->mcu_clk); - if (ret) { - dev_err(ssusb->dev, "failed to enable mcu_clk\n"); - goto mcu_clk_err; - } - - ret = clk_prepare_enable(ssusb->dma_clk); - if (ret) { - dev_err(ssusb->dev, "failed to enable dma_clk\n"); - goto dma_clk_err; - } - - return 0; - -dma_clk_err: - clk_disable_unprepare(ssusb->mcu_clk); -mcu_clk_err: - clk_disable_unprepare(ssusb->ref_clk); -ref_clk_err: - clk_disable_unprepare(ssusb->sys_clk); -sys_clk_err: - return ret; -} - -static void ssusb_clks_disable(struct ssusb_mtk *ssusb) -{ - clk_disable_unprepare(ssusb->dma_clk); - clk_disable_unprepare(ssusb->mcu_clk); - clk_disable_unprepare(ssusb->ref_clk); - clk_disable_unprepare(ssusb->sys_clk); -} - static int ssusb_rscs_init(struct ssusb_mtk *ssusb) { int ret = 0; @@ -158,7 +138,7 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb) goto vusb33_err; } - ret = ssusb_clks_enable(ssusb); + ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks); if (ret) goto clks_err; @@ -179,7 +159,7 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb) phy_err: ssusb_phy_exit(ssusb); phy_init_err: - ssusb_clks_disable(ssusb); + clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); clks_err: regulator_disable(ssusb->vusb33); vusb33_err: @@ -188,7 +168,7 @@ vusb33_err: static void ssusb_rscs_exit(struct ssusb_mtk *ssusb) { - ssusb_clks_disable(ssusb); + clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); regulator_disable(ssusb->vusb33); ssusb_phy_power_off(ssusb); ssusb_phy_exit(ssusb); @@ -210,52 +190,55 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } -/* ignore the error if the clock does not exist */ -static struct clk *get_optional_clk(struct device *dev, const char *id) +static void ssusb_u3_drd_check(struct ssusb_mtk *ssusb) { - struct clk *opt_clk; + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + u32 dev_u3p_num; + u32 host_u3p_num; + u32 value; + + /* u3 port0 is disabled */ + if (ssusb->u3p_dis_msk & BIT(0)) { + otg_sx->is_u3_drd = false; + goto out; + } + + value = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_DEV_CAP); + dev_u3p_num = SSUSB_IP_DEV_U3_PORT_NUM(value); - opt_clk = devm_clk_get(dev, id); - /* ignore error number except EPROBE_DEFER */ - if (IS_ERR(opt_clk) && (PTR_ERR(opt_clk) != -EPROBE_DEFER)) - opt_clk = NULL; + value = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP); + host_u3p_num = SSUSB_IP_XHCI_U3_PORT_NUM(value); - return opt_clk; + otg_sx->is_u3_drd = !!(dev_u3p_num && host_u3p_num); + +out: + dev_info(ssusb->dev, "usb3-drd: %d\n", otg_sx->is_u3_drd); } static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) { struct device_node *node = pdev->dev.of_node; struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + struct clk_bulk_data *clks = ssusb->clks; struct device *dev = &pdev->dev; - struct regulator *vbus; - struct resource *res; int i; int ret; - ssusb->vusb33 = devm_regulator_get(&pdev->dev, "vusb33"); + ssusb->vusb33 = devm_regulator_get(dev, "vusb33"); if (IS_ERR(ssusb->vusb33)) { dev_err(dev, "failed to get vusb33\n"); return PTR_ERR(ssusb->vusb33); } - ssusb->sys_clk = devm_clk_get(dev, "sys_ck"); - if (IS_ERR(ssusb->sys_clk)) { - dev_err(dev, "failed to get sys clock\n"); - return PTR_ERR(ssusb->sys_clk); - } - - ssusb->ref_clk = get_optional_clk(dev, "ref_ck"); - if (IS_ERR(ssusb->ref_clk)) - return PTR_ERR(ssusb->ref_clk); - - ssusb->mcu_clk = get_optional_clk(dev, "mcu_ck"); - if (IS_ERR(ssusb->mcu_clk)) - return PTR_ERR(ssusb->mcu_clk); - - ssusb->dma_clk = get_optional_clk(dev, "dma_ck"); - if (IS_ERR(ssusb->dma_clk)) - return PTR_ERR(ssusb->dma_clk); + clks[0].id = "sys_ck"; + clks[1].id = "ref_ck"; + clks[2].id = "mcu_ck"; + clks[3].id = "dma_ck"; + clks[4].id = "xhci_ck"; + clks[5].id = "frmcnt_ck"; + ret = devm_clk_bulk_get_optional(dev, BULK_CLKS_CNT, clks); + if (ret) + return ret; ssusb->num_phys = of_count_phandle_with_args(node, "phys", "#phy-cells"); @@ -276,17 +259,22 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc"); - ssusb->ippc_base = devm_ioremap_resource(dev, res); + ssusb->ippc_base = devm_platform_ioremap_resource_byname(pdev, "ippc"); if (IS_ERR(ssusb->ippc_base)) return PTR_ERR(ssusb->ippc_base); + ssusb->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); + if (ssusb->wakeup_irq == -EPROBE_DEFER) + return ssusb->wakeup_irq; + ssusb->dr_mode = usb_get_dr_mode(dev); if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) ssusb->dr_mode = USB_DR_MODE_OTG; + of_property_read_u32(node, "mediatek,u3p-dis-msk", &ssusb->u3p_dis_msk); + if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL) - return 0; + goto out; /* if host role is supported */ ret = ssusb_wakeup_of_property_parse(ssusb, node); @@ -296,35 +284,42 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } /* optional property, ignore the error if it does not exist */ - of_property_read_u32(node, "mediatek,u3p-dis-msk", - &ssusb->u3p_dis_msk); + of_property_read_u32(node, "mediatek,u2p-dis-msk", + &ssusb->u2p_dis_msk); - vbus = devm_regulator_get(&pdev->dev, "vbus"); - if (IS_ERR(vbus)) { + otg_sx->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(otg_sx->vbus)) { dev_err(dev, "failed to get vbus\n"); - return PTR_ERR(vbus); + return PTR_ERR(otg_sx->vbus); } - otg_sx->vbus = vbus; if (ssusb->dr_mode == USB_DR_MODE_HOST) - return 0; + goto out; /* if dual-role mode is supported */ - 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"); + + /* can't disable port0 when use dual-role mode */ + ssusb->u2p_dis_msk &= ~0x1; - if (of_property_read_bool(node, "extcon")) { + if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled) + goto out; + + if (of_property_present(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"); - return PTR_ERR(otg_sx->edev); + return dev_err_probe(dev, PTR_ERR(otg_sx->edev), + "couldn't get extcon device\n"); } } - dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n", - ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk, - otg_sx->manual_drd_enabled ? "manual" : "auto"); +out: + dev_info(dev, "dr_mode: %d, drd: %s\n", ssusb->dr_mode, + otg_sx->manual_drd_enabled ? "manual" : "auto"); + dev_info(dev, "u2p_dis_msk: %x, u3p_dis_msk: %x\n", + ssusb->u2p_dis_msk, ssusb->u3p_dis_msk); return 0; } @@ -354,16 +349,38 @@ static int mtu3_probe(struct platform_device *pdev) if (ret) return ret; + ssusb_debugfs_create_root(ssusb); + /* enable power domain */ + pm_runtime_set_active(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 4000); pm_runtime_enable(dev); pm_runtime_get_sync(dev); - device_enable_async_suspend(dev); + + device_init_wakeup(dev, true); ret = ssusb_rscs_init(ssusb); if (ret) goto comm_init_err; + if (ssusb->wakeup_irq > 0) { + ret = dev_pm_set_dedicated_wake_irq_reverse(dev, ssusb->wakeup_irq); + if (ret) { + dev_err(dev, "failed to set wakeup irq %d\n", ssusb->wakeup_irq); + goto comm_exit; + } + dev_info(dev, "wakeup irq %d\n", ssusb->wakeup_irq); + } + + ret = device_reset_optional(dev); + if (ret) { + dev_err_probe(dev, ret, "failed to reset controller\n"); + goto comm_exit; + } + ssusb_ip_sw_reset(ssusb); + ssusb_u3_drd_check(ssusb); if (IS_ENABLED(CONFIG_USB_MTU3_HOST)) ssusb->dr_mode = USB_DR_MODE_HOST; @@ -401,7 +418,11 @@ static int mtu3_probe(struct platform_device *pdev) goto gadget_exit; } - ssusb_otg_switch_init(ssusb); + ret = ssusb_otg_switch_init(ssusb); + if (ret) { + dev_err(dev, "failed to initialize switch\n"); + goto host_exit; + } break; default: dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode); @@ -409,23 +430,32 @@ static int mtu3_probe(struct platform_device *pdev) goto comm_exit; } + device_enable_async_suspend(dev); + pm_runtime_put_autosuspend(dev); + pm_runtime_forbid(dev); + return 0; +host_exit: + ssusb_host_exit(ssusb); gadget_exit: ssusb_gadget_exit(ssusb); comm_exit: ssusb_rscs_exit(ssusb); comm_init_err: - pm_runtime_put_sync(dev); + pm_runtime_put_noidle(dev); pm_runtime_disable(dev); + ssusb_debugfs_remove_root(ssusb); return ret; } -static int mtu3_remove(struct platform_device *pdev) +static void mtu3_remove(struct platform_device *pdev) { struct ssusb_mtk *ssusb = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + switch (ssusb->dr_mode) { case USB_DR_MODE_PERIPHERAL: ssusb_gadget_exit(ssusb); @@ -438,51 +468,100 @@ static int mtu3_remove(struct platform_device *pdev) ssusb_gadget_exit(ssusb); ssusb_host_exit(ssusb); break; - default: - return -EINVAL; + case USB_DR_MODE_UNKNOWN: + /* + * This cannot happen because with dr_mode == + * USB_DR_MODE_UNKNOWN, .probe() doesn't succeed and so + * .remove() wouldn't be called at all. However (little + * surprising) the compiler isn't smart enough to see that, so + * we explicitly have this case item to not make the compiler + * wail about an unhandled enumeration value. + */ + break; } ssusb_rscs_exit(ssusb); - pm_runtime_put_sync(&pdev->dev); + ssusb_debugfs_remove_root(ssusb); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); +} + +static int resume_ip_and_ports(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + switch (ssusb->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + ssusb_gadget_resume(ssusb, msg); + break; + case USB_DR_MODE_HOST: + ssusb_host_resume(ssusb, false); + break; + case USB_DR_MODE_OTG: + ssusb_host_resume(ssusb, !ssusb->is_host); + if (!ssusb->is_host) + ssusb_gadget_resume(ssusb, msg); + + break; + default: + return -EINVAL; + } return 0; } -/* - * when support dual-role mode, we reject suspend when - * it works as device mode; - */ -static int __maybe_unused mtu3_suspend(struct device *dev) +static int mtu3_suspend_common(struct device *dev, pm_message_t msg) { struct ssusb_mtk *ssusb = dev_get_drvdata(dev); + int ret = 0; dev_dbg(dev, "%s\n", __func__); - /* REVISIT: disconnect it for only device mode? */ - if (!ssusb->is_host) - return 0; + switch (ssusb->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + ret = ssusb_gadget_suspend(ssusb, msg); + if (ret) + goto err; + + break; + case USB_DR_MODE_HOST: + ssusb_host_suspend(ssusb); + break; + case USB_DR_MODE_OTG: + if (!ssusb->is_host) { + ret = ssusb_gadget_suspend(ssusb, msg); + if (ret) + goto err; + } + ssusb_host_suspend(ssusb); + break; + default: + return -EINVAL; + } + + ret = wait_for_ip_sleep(ssusb); + if (ret) + goto sleep_err; - ssusb_host_disable(ssusb, true); ssusb_phy_power_off(ssusb); - ssusb_clks_disable(ssusb); + clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); ssusb_wakeup_set(ssusb, true); - return 0; + +sleep_err: + resume_ip_and_ports(ssusb, msg); +err: + return ret; } -static int __maybe_unused mtu3_resume(struct device *dev) +static int mtu3_resume_common(struct device *dev, pm_message_t msg) { struct ssusb_mtk *ssusb = dev_get_drvdata(dev); int ret; dev_dbg(dev, "%s\n", __func__); - if (!ssusb->is_host) - return 0; - ssusb_wakeup_set(ssusb, false); - ret = ssusb_clks_enable(ssusb); + ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks); if (ret) goto clks_err; @@ -490,41 +569,62 @@ static int __maybe_unused mtu3_resume(struct device *dev) if (ret) goto phy_err; - ssusb_host_enable(ssusb); - - return 0; + return resume_ip_and_ports(ssusb, msg); phy_err: - ssusb_clks_disable(ssusb); + clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); clks_err: return ret; } +static int __maybe_unused mtu3_suspend(struct device *dev) +{ + return mtu3_suspend_common(dev, PMSG_SUSPEND); +} + +static int __maybe_unused mtu3_resume(struct device *dev) +{ + return mtu3_resume_common(dev, PMSG_SUSPEND); +} + +static int __maybe_unused mtu3_runtime_suspend(struct device *dev) +{ + if (!device_may_wakeup(dev)) + return 0; + + return mtu3_suspend_common(dev, PMSG_AUTO_SUSPEND); +} + +static int __maybe_unused mtu3_runtime_resume(struct device *dev) +{ + if (!device_may_wakeup(dev)) + return 0; + + return mtu3_resume_common(dev, PMSG_AUTO_SUSPEND); +} + static const struct dev_pm_ops mtu3_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume) + SET_RUNTIME_PM_OPS(mtu3_runtime_suspend, + mtu3_runtime_resume, NULL) }; #define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL) -#ifdef CONFIG_OF - static const struct of_device_id mtu3_of_match[] = { {.compatible = "mediatek,mt8173-mtu3",}, {.compatible = "mediatek,mtu3",}, {}, }; - MODULE_DEVICE_TABLE(of, mtu3_of_match); -#endif - static struct platform_driver mtu3_driver = { .probe = mtu3_probe, .remove = mtu3_remove, .driver = { .name = MTU3_DRIVER_NAME, .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(mtu3_of_match), + .of_match_table = mtu3_of_match, }, }; module_platform_driver(mtu3_driver); diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 09f19f70fe8f..03f26589b056 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -22,17 +22,49 @@ #include <linux/iopoll.h> #include "mtu3.h" +#include "mtu3_trace.h" #define QMU_CHECKSUM_LEN 16 #define GPD_FLAGS_HWO BIT(0) #define GPD_FLAGS_BDP BIT(1) #define GPD_FLAGS_BPS BIT(2) +#define GPD_FLAGS_ZLP BIT(6) #define GPD_FLAGS_IOC BIT(7) - -#define GPD_EXT_FLAG_ZLP BIT(5) -#define GPD_EXT_NGP(x) (((x) & 0xf) << 4) -#define GPD_EXT_BUF(x) (((x) & 0xf) << 0) +#define GET_GPD_HWO(gpd) (le32_to_cpu((gpd)->dw0_info) & GPD_FLAGS_HWO) + +#define GPD_RX_BUF_LEN_OG(x) (((x) & 0xffff) << 16) +#define GPD_RX_BUF_LEN_EL(x) (((x) & 0xfffff) << 12) +#define GPD_RX_BUF_LEN(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_RX_BUF_LEN_EL(x_) : GPD_RX_BUF_LEN_OG(x_); \ +}) + +#define GPD_DATA_LEN_OG(x) ((x) & 0xffff) +#define GPD_DATA_LEN_EL(x) ((x) & 0xfffff) +#define GPD_DATA_LEN(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_DATA_LEN_EL(x_) : GPD_DATA_LEN_OG(x_); \ +}) + +#define GPD_EXT_FLAG_ZLP BIT(29) +#define GPD_EXT_NGP_OG(x) (((x) & 0xf) << 20) +#define GPD_EXT_BUF_OG(x) (((x) & 0xf) << 16) +#define GPD_EXT_NGP_EL(x) (((x) & 0xf) << 28) +#define GPD_EXT_BUF_EL(x) (((x) & 0xf) << 24) +#define GPD_EXT_NGP(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_EXT_NGP_EL(x_) : GPD_EXT_NGP_OG(x_); \ +}) + +#define GPD_EXT_BUF(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_EXT_BUF_EL(x_) : GPD_EXT_BUF_OG(x_); \ +}) #define HILO_GEN64(hi, lo) (((u64)(hi) << 32) + (lo)) #define HILO_DMA(hi, lo) \ @@ -125,7 +157,7 @@ static void reset_gpd_list(struct mtu3_ep *mep) struct qmu_gpd *gpd = ring->start; if (gpd) { - gpd->flag &= ~GPD_FLAGS_HWO; + gpd->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); gpd_ring_init(ring, gpd); } } @@ -178,6 +210,7 @@ static struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring) return ring->enqueue; } +/* @dequeue may be NULL if ring is unallocated or freed */ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring) { if (ring->dequeue < ring->end) @@ -188,8 +221,8 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring) return ring->dequeue; } -/* check if a ring is emtpy */ -static int gpd_ring_empty(struct mtu3_gpd_ring *ring) +/* check if a ring is empty */ +static bool gpd_ring_empty(struct mtu3_gpd_ring *ring) { struct qmu_gpd *enq = ring->enqueue; struct qmu_gpd *next; @@ -214,16 +247,14 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) struct mtu3_gpd_ring *ring = &mep->gpd_ring; struct qmu_gpd *gpd = ring->enqueue; struct usb_request *req = &mreq->request; + struct mtu3 *mtu = mep->mtu; dma_addr_t enq_dma; - u16 ext_addr; - - /* set all fields to zero as default value */ - memset(gpd, 0, sizeof(*gpd)); + u32 ext_addr; + gpd->dw0_info = 0; /* SW own it */ gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); - ext_addr = GPD_EXT_BUF(upper_32_bits(req->dma)); - gpd->buf_len = cpu_to_le16(req->length); - gpd->flag |= GPD_FLAGS_IOC; + ext_addr = GPD_EXT_BUF(mtu, upper_32_bits(req->dma)); + gpd->dw3_info = cpu_to_le32(GPD_DATA_LEN(mtu, req->length)); /* get the next GPD */ enq = advance_enq_gpd(ring); @@ -231,17 +262,24 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", mep->epnum, gpd, enq, &enq_dma); - enq->flag &= ~GPD_FLAGS_HWO; + enq->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); - ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); - gpd->tx_ext_addr = cpu_to_le16(ext_addr); - - if (req->zero) - gpd->ext_flag |= GPD_EXT_FLAG_ZLP; + ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma)); + gpd->dw0_info = cpu_to_le32(ext_addr); + + if (req->zero) { + if (mtu->gen2cp) + gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_ZLP); + else + gpd->dw3_info |= cpu_to_le32(GPD_EXT_FLAG_ZLP); + } - gpd->flag |= GPD_FLAGS_HWO; + /* prevent reorder, make sure GPD's HWO is set last */ + mb(); + gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO); mreq->gpd = gpd; + trace_mtu3_prepare_gpd(mep, gpd); return 0; } @@ -252,16 +290,14 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) struct mtu3_gpd_ring *ring = &mep->gpd_ring; struct qmu_gpd *gpd = ring->enqueue; struct usb_request *req = &mreq->request; + struct mtu3 *mtu = mep->mtu; dma_addr_t enq_dma; - u16 ext_addr; - - /* set all fields to zero as default value */ - memset(gpd, 0, sizeof(*gpd)); + u32 ext_addr; + gpd->dw0_info = 0; /* SW own it */ gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); - ext_addr = GPD_EXT_BUF(upper_32_bits(req->dma)); - gpd->data_buf_len = cpu_to_le16(req->length); - gpd->flag |= GPD_FLAGS_IOC; + ext_addr = GPD_EXT_BUF(mtu, upper_32_bits(req->dma)); + gpd->dw0_info = cpu_to_le32(GPD_RX_BUF_LEN(mtu, req->length)); /* get the next GPD */ enq = advance_enq_gpd(ring); @@ -269,13 +305,16 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", mep->epnum, gpd, enq, &enq_dma); - enq->flag &= ~GPD_FLAGS_HWO; + enq->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); - ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); - gpd->rx_ext_addr = cpu_to_le16(ext_addr); - gpd->flag |= GPD_FLAGS_HWO; + ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma)); + gpd->dw3_info = cpu_to_le32(ext_addr); + /* prevent reorder, make sure GPD's HWO is set last */ + mb(); + gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO); mreq->gpd = gpd; + trace_mtu3_prepare_gpd(mep, gpd); return 0; } @@ -350,6 +389,9 @@ void mtu3_qmu_stop(struct mtu3_ep *mep) } mtu3_writel(mbase, qcsr, QMU_Q_STOP); + if (mep->is_in) + mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_FLUSHFIFO); + ret = readl_poll_timeout_atomic(mbase + qcsr, value, !(value & QMU_Q_ACTIVE), 1, 1000); if (ret) { @@ -357,6 +399,10 @@ void mtu3_qmu_stop(struct mtu3_ep *mep) return; } + /* flush fifo again to make sure the fifo is empty */ + if (mep->is_in) + mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_FLUSHFIFO); + dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name); } @@ -382,27 +428,25 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) struct mtu3_gpd_ring *ring = &mep->gpd_ring; void __iomem *mbase = mtu->mac_base; struct qmu_gpd *gpd_current = NULL; - struct usb_request *req = NULL; struct mtu3_request *mreq; dma_addr_t cur_gpd_dma; u32 txcsr = 0; int ret; mreq = next_request(mep); - if (mreq && mreq->request.length == 0) - req = &mreq->request; - else + if (mreq && mreq->request.length != 0) return; cur_gpd_dma = read_txq_cur_addr(mbase, epnum); gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); - if (le16_to_cpu(gpd_current->buf_len) != 0) { + if (GPD_DATA_LEN(mtu, le32_to_cpu(gpd_current->dw3_info)) != 0) { dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum); return; } - dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, req); + dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq); + trace_mtu3_zlp_exp_gpd(mep, gpd_current); mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); @@ -413,10 +457,10 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) return; } mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY); - + /* prevent reorder, make sure GPD's HWO is set last */ + mb(); /* by pass the current GDP */ - gpd_current->flag |= GPD_FLAGS_BPS; - gpd_current->flag |= GPD_FLAGS_HWO; + gpd_current->dw0_info |= cpu_to_le32(GPD_FLAGS_BPS | GPD_FLAGS_HWO); /*enable DMAREQEN, switch back to QMU mode */ mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); @@ -424,6 +468,37 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) } /* + * when rx error happens (except zlperr), QMU will stop, and RQCPR saves + * the GPD encountered error, Done irq will arise after resuming QMU again. + */ +static void qmu_error_rx(struct mtu3 *mtu, u8 epnum) +{ + struct mtu3_ep *mep = mtu->out_eps + epnum; + struct mtu3_gpd_ring *ring = &mep->gpd_ring; + struct qmu_gpd *gpd_current = NULL; + struct mtu3_request *mreq; + dma_addr_t cur_gpd_dma; + + cur_gpd_dma = read_rxq_cur_addr(mtu->mac_base, epnum); + gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); + + mreq = next_request(mep); + if (!mreq || mreq->gpd != gpd_current) { + dev_err(mtu->dev, "no correct RX req is found\n"); + return; + } + + mreq->request.status = -EAGAIN; + + /* by pass the current GDP */ + gpd_current->dw0_info |= cpu_to_le32(GPD_FLAGS_BPS | GPD_FLAGS_HWO); + mtu3_qmu_resume(mep); + + dev_dbg(mtu->dev, "%s EP%d, current=%p, req=%p\n", + __func__, epnum, gpd_current, mreq); +} + +/* * NOTE: request list maybe is already empty as following case: * queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)--> * queue_tx --> process_tasklet(meanwhile, the second one is transferred, @@ -448,7 +523,7 @@ static void qmu_done_tx(struct mtu3 *mtu, u8 epnum) dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", __func__, epnum, gpd, gpd_current, ring->enqueue); - while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) { + while (gpd && gpd != gpd_current && !GET_GPD_HWO(gpd)) { mreq = next_request(mep); @@ -458,7 +533,8 @@ static void qmu_done_tx(struct mtu3 *mtu, u8 epnum) } request = &mreq->request; - request->actual = le16_to_cpu(gpd->buf_len); + request->actual = GPD_DATA_LEN(mtu, le32_to_cpu(gpd->dw3_info)); + trace_mtu3_complete_gpd(mep, gpd); mtu3_req_complete(mep, request, 0); gpd = advance_deq_gpd(ring); @@ -486,7 +562,7 @@ static void qmu_done_rx(struct mtu3 *mtu, u8 epnum) dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", __func__, epnum, gpd, gpd_current, ring->enqueue); - while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) { + while (gpd && gpd != gpd_current && !GET_GPD_HWO(gpd)) { mreq = next_request(mep); @@ -496,7 +572,8 @@ static void qmu_done_rx(struct mtu3 *mtu, u8 epnum) } req = &mreq->request; - req->actual = le16_to_cpu(gpd->buf_len); + req->actual = GPD_DATA_LEN(mtu, le32_to_cpu(gpd->dw3_info)); + trace_mtu3_complete_gpd(mep, gpd); mtu3_req_complete(mep, req, 0); gpd = advance_deq_gpd(ring); @@ -526,14 +603,18 @@ static void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status) if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) { errval = mtu3_readl(mbase, U3D_RQERRIR0); + mtu3_writel(mbase, U3D_RQERRIR0, errval); + for (i = 1; i < mtu->num_eps; i++) { if (errval & QMU_RX_CS_ERR(i)) dev_err(mtu->dev, "Rx %d CS error!\n", i); if (errval & QMU_RX_LEN_ERR(i)) dev_err(mtu->dev, "RX %d Length error\n", i); + + if (errval & (QMU_RX_CS_ERR(i) | QMU_RX_LEN_ERR(i))) + qmu_error_rx(mtu, i); } - mtu3_writel(mbase, U3D_RQERRIR0, errval); } if (qmu_status & RXQ_ZLPERR_INT) { @@ -574,6 +655,7 @@ irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu) dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n", (qmu_done_status & 0xFFFF), qmu_done_status >> 16, qmu_status); + trace_mtu3_qmu_isr(qmu_done_status, qmu_status); if (qmu_done_status) qmu_done_isr(mtu, qmu_done_status); diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h index 81f5151a55ed..66e1c0ab5a99 100644 --- a/drivers/usb/mtu3/mtu3_qmu.h +++ b/drivers/usb/mtu3/mtu3_qmu.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtu3_qmu.h - Queue Management Unit driver header * @@ -15,6 +15,7 @@ #define QMU_GPD_RING_SIZE (MAX_GPD_NUM * QMU_GPD_SIZE) #define GPD_BUF_SIZE 65532 +#define GPD_BUF_SIZE_EL 1048572 void mtu3_qmu_stop(struct mtu3_ep *mep); int mtu3_qmu_start(struct mtu3_ep *mep); diff --git a/drivers/usb/mtu3/mtu3_trace.c b/drivers/usb/mtu3/mtu3_trace.c new file mode 100644 index 000000000000..d17ddb87cdcf --- /dev/null +++ b/drivers/usb/mtu3/mtu3_trace.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtu3_trace.c - trace support + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#define CREATE_TRACE_POINTS +#include "mtu3_debug.h" +#include "mtu3_trace.h" + +void mtu3_dbg_trace(struct device *dev, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + trace_mtu3_log(dev, &vaf); + va_end(args); +} diff --git a/drivers/usb/mtu3/mtu3_trace.h b/drivers/usb/mtu3/mtu3_trace.h new file mode 100644 index 000000000000..89870175d635 --- /dev/null +++ b/drivers/usb/mtu3/mtu3_trace.h @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * mtu3_trace.h - trace support + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mtu3 + +#if !defined(__MTU3_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __MTU3_TRACE_H__ + +#include <linux/types.h> +#include <linux/tracepoint.h> + +#include "mtu3.h" + +TRACE_EVENT(mtu3_log, + TP_PROTO(struct device *dev, struct va_format *vaf), + TP_ARGS(dev, vaf), + TP_STRUCT__entry( + __string(name, dev_name(dev)) + __vstring(msg, vaf->fmt, vaf->va) + ), + TP_fast_assign( + __assign_str(name); + __assign_vstr(msg, vaf->fmt, vaf->va); + ), + TP_printk("%s: %s", __get_str(name), __get_str(msg)) +); + +TRACE_EVENT(mtu3_u3_ltssm_isr, + TP_PROTO(u32 intr), + TP_ARGS(intr), + TP_STRUCT__entry( + __field(u32, intr) + ), + TP_fast_assign( + __entry->intr = intr; + ), + TP_printk("(%08x) %s %s %s %s %s %s", __entry->intr, + __entry->intr & HOT_RST_INTR ? "HOT_RST" : "", + __entry->intr & WARM_RST_INTR ? "WARM_RST" : "", + __entry->intr & ENTER_U3_INTR ? "ENT_U3" : "", + __entry->intr & EXIT_U3_INTR ? "EXIT_U3" : "", + __entry->intr & VBUS_RISE_INTR ? "VBUS_RISE" : "", + __entry->intr & VBUS_FALL_INTR ? "VBUS_FALL" : "" + ) +); + +TRACE_EVENT(mtu3_u2_common_isr, + TP_PROTO(u32 intr), + TP_ARGS(intr), + TP_STRUCT__entry( + __field(u32, intr) + ), + TP_fast_assign( + __entry->intr = intr; + ), + TP_printk("(%08x) %s %s %s", __entry->intr, + __entry->intr & SUSPEND_INTR ? "SUSPEND" : "", + __entry->intr & RESUME_INTR ? "RESUME" : "", + __entry->intr & RESET_INTR ? "RESET" : "" + ) +); + +TRACE_EVENT(mtu3_qmu_isr, + TP_PROTO(u32 done_intr, u32 exp_intr), + TP_ARGS(done_intr, exp_intr), + TP_STRUCT__entry( + __field(u32, done_intr) + __field(u32, exp_intr) + ), + TP_fast_assign( + __entry->done_intr = done_intr; + __entry->exp_intr = exp_intr; + ), + TP_printk("done (tx %04x, rx %04x), exp (%08x)", + __entry->done_intr & 0xffff, + __entry->done_intr >> 16, + __entry->exp_intr + ) +); + +DECLARE_EVENT_CLASS(mtu3_log_setup, + TP_PROTO(struct usb_ctrlrequest *setup), + TP_ARGS(setup), + TP_STRUCT__entry( + __field(__u8, bRequestType) + __field(__u8, bRequest) + __field(__u16, wValue) + __field(__u16, wIndex) + __field(__u16, wLength) + ), + TP_fast_assign( + __entry->bRequestType = setup->bRequestType; + __entry->bRequest = setup->bRequest; + __entry->wValue = le16_to_cpu(setup->wValue); + __entry->wIndex = le16_to_cpu(setup->wIndex); + __entry->wLength = le16_to_cpu(setup->wLength); + ), + TP_printk("setup - %02x %02x %04x %04x %04x", + __entry->bRequestType, __entry->bRequest, + __entry->wValue, __entry->wIndex, __entry->wLength + ) +); + +DEFINE_EVENT(mtu3_log_setup, mtu3_handle_setup, + TP_PROTO(struct usb_ctrlrequest *setup), + TP_ARGS(setup) +); + +DECLARE_EVENT_CLASS(mtu3_log_request, + TP_PROTO(struct mtu3_request *mreq), + TP_ARGS(mreq), + TP_STRUCT__entry( + __string(name, mreq->mep->name) + __field(struct mtu3_request *, mreq) + __field(struct qmu_gpd *, gpd) + __field(unsigned int, actual) + __field(unsigned int, length) + __field(int, status) + __field(int, zero) + __field(int, no_interrupt) + ), + TP_fast_assign( + __assign_str(name); + __entry->mreq = mreq; + __entry->gpd = mreq->gpd; + __entry->actual = mreq->request.actual; + __entry->length = mreq->request.length; + __entry->status = mreq->request.status; + __entry->zero = mreq->request.zero; + __entry->no_interrupt = mreq->request.no_interrupt; + ), + TP_printk("%s: req %p gpd %p len %u/%u %s%s --> %d", + __get_str(name), __entry->mreq, __entry->gpd, + __entry->actual, __entry->length, + __entry->zero ? "Z" : "z", + __entry->no_interrupt ? "i" : "I", + __entry->status + ) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_alloc_request, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_free_request, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_gadget_queue, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_gadget_dequeue, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_req_complete, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DECLARE_EVENT_CLASS(mtu3_log_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd), + TP_STRUCT__entry( + __string(name, mep->name) + __field(struct qmu_gpd *, gpd) + __field(u32, dw0) + __field(u32, dw1) + __field(u32, dw2) + __field(u32, dw3) + ), + TP_fast_assign( + __assign_str(name); + __entry->gpd = gpd; + __entry->dw0 = le32_to_cpu(gpd->dw0_info); + __entry->dw1 = le32_to_cpu(gpd->next_gpd); + __entry->dw2 = le32_to_cpu(gpd->buffer); + __entry->dw3 = le32_to_cpu(gpd->dw3_info); + ), + TP_printk("%s: gpd %p - %08x %08x %08x %08x", + __get_str(name), __entry->gpd, + __entry->dw0, __entry->dw1, + __entry->dw2, __entry->dw3 + ) +); + +DEFINE_EVENT(mtu3_log_gpd, mtu3_prepare_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd) +); + +DEFINE_EVENT(mtu3_log_gpd, mtu3_complete_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd) +); + +DEFINE_EVENT(mtu3_log_gpd, mtu3_zlp_exp_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd) +); + +DECLARE_EVENT_CLASS(mtu3_log_ep, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep), + TP_STRUCT__entry( + __string(name, mep->name) + __field(unsigned int, type) + __field(unsigned int, slot) + __field(unsigned int, maxp) + __field(unsigned int, mult) + __field(unsigned int, maxburst) + __field(unsigned int, flags) + __field(unsigned int, direction) + __field(struct mtu3_gpd_ring *, gpd_ring) + ), + TP_fast_assign( + __assign_str(name); + __entry->type = mep->type; + __entry->slot = mep->slot; + __entry->maxp = mep->ep.maxpacket; + __entry->mult = mep->ep.mult; + __entry->maxburst = mep->ep.maxburst; + __entry->flags = mep->flags; + __entry->direction = mep->is_in; + __entry->gpd_ring = &mep->gpd_ring; + ), + TP_printk("%s: type %s maxp %d slot %d mult %d burst %d ring %p/%pad flags %c:%c%c%c:%c", + __get_str(name), usb_ep_type_string(__entry->type), + __entry->maxp, __entry->slot, + __entry->mult, __entry->maxburst, + __entry->gpd_ring, &__entry->gpd_ring->dma, + __entry->flags & MTU3_EP_ENABLED ? 'E' : 'e', + __entry->flags & MTU3_EP_STALL ? 'S' : 's', + __entry->flags & MTU3_EP_WEDGE ? 'W' : 'w', + __entry->flags & MTU3_EP_BUSY ? 'B' : 'b', + __entry->direction ? '<' : '>' + ) +); + +DEFINE_EVENT(mtu3_log_ep, mtu3_gadget_ep_enable, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep) +); + +DEFINE_EVENT(mtu3_log_ep, mtu3_gadget_ep_disable, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep) +); + +DEFINE_EVENT(mtu3_log_ep, mtu3_gadget_ep_set_halt, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep) +); + +#endif /* __MTU3_TRACE_H__ */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mtu3_trace + +#include <trace/define_trace.h> |
