summaryrefslogtreecommitdiff
path: root/drivers/usb/mtu3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/mtu3')
-rw-r--r--drivers/usb/mtu3/Kconfig6
-rw-r--r--drivers/usb/mtu3/Makefile11
-rw-r--r--drivers/usb/mtu3/mtu3.h150
-rw-r--r--drivers/usb/mtu3/mtu3_core.c372
-rw-r--r--drivers/usb/mtu3/mtu3_debug.h51
-rw-r--r--drivers/usb/mtu3/mtu3_debugfs.c515
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c332
-rw-r--r--drivers/usb/mtu3/mtu3_dr.h36
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c167
-rw-r--r--drivers/usb/mtu3/mtu3_gadget_ep0.c67
-rw-r--r--drivers/usb/mtu3/mtu3_host.c175
-rw-r--r--drivers/usb/mtu3/mtu3_hw_regs.h67
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c360
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.c174
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.h3
-rw-r--r--drivers/usb/mtu3/mtu3_trace.c24
-rw-r--r--drivers/usb/mtu3/mtu3_trace.h277
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>