diff options
Diffstat (limited to 'drivers/usb/mtu3')
| -rw-r--r-- | drivers/usb/mtu3/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3.h | 59 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_core.c | 72 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_dr.c | 72 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_dr.h | 17 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_gadget.c | 14 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_gadget_ep0.c | 27 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_host.c | 89 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_hw_regs.h | 24 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_plat.c | 176 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_qmu.c | 113 | ||||
| -rw-r--r-- | drivers/usb/mtu3/mtu3_qmu.h | 11 | 
12 files changed, 370 insertions, 305 deletions
diff --git a/drivers/usb/mtu3/Makefile b/drivers/usb/mtu3/Makefile index 60e0fff7a847..4a9715812bf9 100644 --- a/drivers/usb/mtu3/Makefile +++ b/drivers/usb/mtu3/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0  ccflags-$(CONFIG_USB_MTU3_DEBUG)	+= -DDEBUG diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index b26fffc58446..3c888d942a9f 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3.h - MediaTek USB3 DRD header   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #ifndef __MTU3_H__ @@ -46,6 +37,9 @@ struct mtu3_request;  #define	MU3D_EP_RXCR1(epnum)	(U3D_RX1CSR1 + (((epnum) - 1) * 0x10))  #define	MU3D_EP_RXCR2(epnum)	(U3D_RX1CSR2 + (((epnum) - 1) * 0x10)) +#define USB_QMU_TQHIAR(epnum)	(U3D_TXQHIAR1 + (((epnum) - 1) * 0x4)) +#define USB_QMU_RQHIAR(epnum)	(U3D_RXQHIAR1 + (((epnum) - 1) * 0x4)) +  #define USB_QMU_RQCSR(epnum)	(U3D_RXQCSR1 + (((epnum) - 1) * 0x10))  #define USB_QMU_RQSAR(epnum)	(U3D_RXQSAR1 + (((epnum) - 1) * 0x10))  #define USB_QMU_RQCPR(epnum)	(U3D_RXQCPR1 + (((epnum) - 1) * 0x10)) @@ -91,6 +85,7 @@ enum mtu3_speed {  	MTU3_SPEED_FULL = 1,  	MTU3_SPEED_HIGH = 3,  	MTU3_SPEED_SUPER = 4, +	MTU3_SPEED_SUPER_PLUS = 5,  };  /** @@ -112,6 +107,19 @@ enum mtu3_g_ep0_state {  };  /** + * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode + *		by IDPIN signal. + * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG + *		IDPIN signal. + * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. + */ +enum mtu3_dr_force_mode { +	MTU3_DR_FORCE_NONE = 0, +	MTU3_DR_FORCE_HOST, +	MTU3_DR_FORCE_DEVICE, +}; + +/**   * @base: the base address of fifo   * @limit: the bitmap size in bits   * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -138,23 +146,33 @@ struct mtu3_fifo_info {   *	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   * @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),   */  struct qmu_gpd {  	__u8 flag;  	__u8 chksum; -	__le16 data_buf_len; +	union { +		__le16 data_buf_len; +		__le16 tx_ext_addr; +	};  	__le32 next_gpd;  	__le32 buffer;  	__le16 buf_len; -	__u8 ext_len; +	union { +		__u8 ext_len; +		__u8 rx_ext_addr; +	};  	__u8 ext_flag;  } __packed; @@ -183,7 +201,6 @@ struct mtu3_gpd_ring {  *		xHCI driver initialization, it's necessary for system bootup  *		as device.  * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not -* @id_*: used to maually switch between host and device modes by idpin  * @manual_drd_enabled: it's true when supports dual-role device by debugfs  *		to switch host/device modes depending on user input.  */ @@ -194,10 +211,6 @@ struct otg_switch_mtk {  	struct notifier_block id_nb;  	struct delayed_work extcon_reg_dwork;  	bool is_u3_drd; -	/* dual-role switch by debugfs */ -	struct pinctrl *id_pinctrl; -	struct pinctrl_state *id_float; -	struct pinctrl_state *id_ground;  	bool manual_drd_enabled;  }; @@ -206,14 +219,17 @@ struct otg_switch_mtk {   * @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 + * @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   * @wakeup_en: it's true when supports remote wakeup in host mode - * @wk_deb_p0: port0's wakeup debounce clock - * @wk_deb_p1: it's optional, and depends on port1 is supported or not   */  struct ssusb_mtk {  	struct device *dev; @@ -226,17 +242,18 @@ struct ssusb_mtk {  	struct regulator *vusb33;  	struct clk *sys_clk;  	struct clk *ref_clk; +	struct clk *mcu_clk; +	struct clk *dma_clk;  	/* otg */  	struct otg_switch_mtk otg_switch;  	enum usb_dr_mode dr_mode;  	bool is_host;  	int u2_ports;  	int u3_ports; +	int u3p_dis_msk;  	struct dentry *dbgfs_root;  	/* usb wakeup for host mode */  	bool wakeup_en; -	struct clk *wk_deb_p0; -	struct clk *wk_deb_p1;  	struct regmap *pericfg;  }; diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 99c65b0788ff..b1b99a8f6a7a 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_core.c - hardware access layer and gadget init/exit of   *                     MediaTek usb3 Dual-Role Controller Driver @@ -5,18 +6,9 @@   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */ +#include <linux/dma-mapping.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/of_address.h> @@ -114,7 +106,9 @@ static int mtu3_device_enable(struct mtu3 *mtu)  	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0),  		(SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |  		SSUSB_U2_PORT_HOST_SEL)); -	mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); + +	if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) +		mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);  	return ssusb_check_clocks(mtu->ssusb, check_clk);  } @@ -129,7 +123,10 @@ static void mtu3_device_disable(struct mtu3 *mtu)  	mtu3_setbits(ibase, SSUSB_U2_CTRL(0),  		SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN); -	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); + +	if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) +		mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); +  	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);  } @@ -236,7 +233,7 @@ 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->is_u3_ip && mtu->max_speed >= USB_SPEED_SUPER)  		mtu3_ss_func_set(mtu, is_on);  	else  		mtu3_hs_softconn_set(mtu, is_on); @@ -546,6 +543,9 @@ static void mtu3_set_speed(struct mtu3 *mtu)  		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", @@ -623,6 +623,10 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)  		udev_speed = USB_SPEED_SUPER;  		maxpkt = 512;  		break; +	case MTU3_SPEED_SUPER_PLUS: +		udev_speed = USB_SPEED_SUPER_PLUS; +		maxpkt = 512; +		break;  	default:  		udev_speed = USB_SPEED_UNKNOWN;  		break; @@ -759,7 +763,31 @@ 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. + */ +static int mtu3_set_dma_mask(struct mtu3 *mtu) +{ +	struct device *dev = mtu->dev; +	bool is_36bit = false; +	int ret = 0; +	u32 value; + +	value = mtu3_readl(mtu->mac_base, U3D_MISC_CTRL); +	if (value & DMA_ADDR_36BIT) { +		is_36bit = true; +		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); +		/* If set 36-bit DMA mask fails, fall back to 32-bit DMA mask */ +		if (ret) { +			is_36bit = false; +			ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); +		} +	} +	dev_info(dev, "dma mask: %s bits\n", is_36bit ? "36" : "32"); + +	return ret; +}  int ssusb_gadget_init(struct ssusb_mtk *ssusb)  { @@ -774,9 +802,9 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)  		return -ENOMEM;  	mtu->irq = platform_get_irq(pdev, 0); -	if (mtu->irq <= 0) { +	if (mtu->irq < 0) {  		dev_err(dev, "fail to get irq number\n"); -		return -ENODEV; +		return mtu->irq;  	}  	dev_info(dev, "irq %d\n", mtu->irq); @@ -800,14 +828,15 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)  	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 SS */ -		mtu->max_speed = USB_SPEED_SUPER; +		/* default as SSP */ +		mtu->max_speed = USB_SPEED_SUPER_PLUS;  		break;  	} @@ -820,6 +849,12 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)  		return ret;  	} +	ret = mtu3_set_dma_mask(mtu); +	if (ret) { +		dev_err(dev, "mtu3 set dma_mask failed:%d\n", ret); +		goto dma_mask_err; +	} +  	ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu);  	if (ret) {  		dev_err(dev, "request irq %d failed!\n", mtu->irq); @@ -845,6 +880,7 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)  gadget_err:  	device_init_wakeup(dev, false); +dma_mask_err:  irq_err:  	mtu3_hw_exit(mtu);  	ssusb->u3d = NULL; diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 560256115b23..db7562d99b95 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_dr.c - dual role switch and host glue layer   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #include <linux/debugfs.h> @@ -261,21 +252,22 @@ static void extcon_register_dwork(struct work_struct *work)   * depending on user input.   * This is useful in special cases, such as uses TYPE-A receptacle but also   * wants to support dual-role mode. - * It generates cable state changes by pulling up/down IDPIN and - * notifies driver to switch mode by "extcon-usb-gpio". - * NOTE: when use MICRO receptacle, should not enable this interface.   */  static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)  {  	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; -	if (to_host) -		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground); -	else -		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float); +	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; @@ -388,17 +380,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)  	debugfs_remove_recursive(ssusb->dbgfs_root);  } +void ssusb_set_force_mode(struct ssusb_mtk *ssusb, +			  enum mtu3_dr_force_mode mode) +{ +	u32 value; + +	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); +} +  int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)  {  	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; -	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork); - -	if (otg_sx->manual_drd_enabled) +	if (otg_sx->manual_drd_enabled) {  		ssusb_debugfs_init(ssusb); - -	/* It is enough to delay 1s for waiting for host initialization */ -	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ); +	} else { +		INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, +				  extcon_register_dwork); + +		/* +		 * It is enough to delay 1s for waiting for +		 * host initialization +		 */ +		schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ); +	}  	return 0;  } @@ -407,8 +427,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)  {  	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; -	cancel_delayed_work(&otg_sx->extcon_reg_dwork); -  	if (otg_sx->manual_drd_enabled)  		ssusb_debugfs_exit(ssusb); +	else +		cancel_delayed_work(&otg_sx->extcon_reg_dwork);  } diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index 9b228b5811b0..c179192408ba 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_dr.h - dual role switch and host glue layer header   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #ifndef _MTU3_DR_H_ @@ -87,6 +78,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)  int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);  void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);  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);  #else @@ -103,6 +96,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)  	return 0;  } +static inline void +ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode) +{} +  #endif  #endif		/* _MTU3_DR_H_ */ diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index 434fca58143c..f05f10f5c171 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_gadget.c - MediaTek usb3 DRD peripheral support   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #include "mtu3.h" @@ -89,6 +80,7 @@ static int mtu3_ep_enable(struct mtu3_ep *mep)  	switch (mtu->g.speed) {  	case USB_SPEED_SUPER: +	case USB_SPEED_SUPER_PLUS:  		if (usb_endpoint_xfer_int(desc) ||  				usb_endpoint_xfer_isoc(desc)) {  			interval = desc->bInterval; @@ -456,7 +448,7 @@ static int mtu3_gadget_wakeup(struct usb_gadget *gadget)  		return  -EOPNOTSUPP;  	spin_lock_irqsave(&mtu->lock, flags); -	if (mtu->g.speed == USB_SPEED_SUPER) { +	if (mtu->g.speed >= USB_SPEED_SUPER) {  		mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);  	} else {  		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME); diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 958d74dd2b78..ebdcf7a38c29 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling   *   * Copyright (c) 2016 MediaTek Inc.   *   * Author:  Chunfeng.Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #include <linux/usb/composite.h> @@ -212,8 +203,8 @@ ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)  	case USB_RECIP_DEVICE:  		result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED;  		result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; -		/* superspeed only */ -		if (mtu->g.speed == USB_SPEED_SUPER) { + +		if (mtu->g.speed >= USB_SPEED_SUPER) {  			result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED;  			result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED;  		} @@ -329,8 +320,8 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu,  		handled = handle_test_mode(mtu, setup);  		break;  	case USB_DEVICE_U1_ENABLE: -		if (mtu->g.speed != USB_SPEED_SUPER || -			mtu->g.state != USB_STATE_CONFIGURED) +		if (mtu->g.speed < USB_SPEED_SUPER || +		    mtu->g.state != USB_STATE_CONFIGURED)  			break;  		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); @@ -344,8 +335,8 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu,  		handled = 1;  		break;  	case USB_DEVICE_U2_ENABLE: -		if (mtu->g.speed != USB_SPEED_SUPER || -			mtu->g.state != USB_STATE_CONFIGURED) +		if (mtu->g.speed < USB_SPEED_SUPER || +		    mtu->g.state != USB_STATE_CONFIGURED)  			break;  		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); @@ -384,8 +375,8 @@ static int ep0_handle_feature(struct mtu3 *mtu,  		break;  	case USB_RECIP_INTERFACE:  		/* superspeed only */ -		if ((value == USB_INTRF_FUNC_SUSPEND) -			&& (mtu->g.speed == USB_SPEED_SUPER)) { +		if (value == USB_INTRF_FUNC_SUSPEND && +		    mtu->g.speed >= USB_SPEED_SUPER) {  			/*  			 * forward the request because function drivers  			 * should handle it diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index e42d308b8dc2..d237d7e65c44 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_dr.c - dual role switch and host glue layer   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #include <linux/clk.h> @@ -79,20 +70,6 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,  	if (!ssusb->wakeup_en)  		return 0; -	ssusb->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0"); -	if (IS_ERR(ssusb->wk_deb_p0)) { -		dev_err(dev, "fail to get wakeup_deb_p0\n"); -		return PTR_ERR(ssusb->wk_deb_p0); -	} - -	if (of_property_read_bool(dn, "wakeup_deb_p1")) { -		ssusb->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1"); -		if (IS_ERR(ssusb->wk_deb_p1)) { -			dev_err(dev, "fail to get wakeup_deb_p1\n"); -			return PTR_ERR(ssusb->wk_deb_p1); -		} -	} -  	ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,  						"mediatek,syscon-wakeup");  	if (IS_ERR(ssusb->pericfg)) { @@ -103,36 +80,6 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,  	return 0;  } -static int ssusb_wakeup_clks_enable(struct ssusb_mtk *ssusb) -{ -	int ret; - -	ret = clk_prepare_enable(ssusb->wk_deb_p0); -	if (ret) { -		dev_err(ssusb->dev, "failed to enable wk_deb_p0\n"); -		goto usb_p0_err; -	} - -	ret = clk_prepare_enable(ssusb->wk_deb_p1); -	if (ret) { -		dev_err(ssusb->dev, "failed to enable wk_deb_p1\n"); -		goto usb_p1_err; -	} - -	return 0; - -usb_p1_err: -	clk_disable_unprepare(ssusb->wk_deb_p0); -usb_p0_err: -	return -EINVAL; -} - -static void ssusb_wakeup_clks_disable(struct ssusb_mtk *ssusb) -{ -	clk_disable_unprepare(ssusb->wk_deb_p1); -	clk_disable_unprepare(ssusb->wk_deb_p0); -} -  static void host_ports_num_get(struct ssusb_mtk *ssusb)  {  	u32 xhci_cap; @@ -151,6 +98,7 @@ 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;  	u32 check_clk;  	u32 value;  	int i; @@ -158,8 +106,14 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb)  	/* power on host ip */  	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); -	/* power on and enable all u3 ports */ +	/* power on and enable u3 ports except skipped ones */ +	u3_ports_disabed = 0;  	for (i = 0; i < num_u3p; i++) { +		if ((0x1 << i) & ssusb->u3p_dis_msk) { +			u3_ports_disabed++; +			continue; +		} +  		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));  		value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);  		value |= SSUSB_U3_PORT_HOST_SEL; @@ -175,7 +129,7 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb)  	}  	check_clk = SSUSB_XHCI_RST_B_STS; -	if (num_u3p) +	if (num_u3p > u3_ports_disabed)  		check_clk = SSUSB_U3_MAC_RST_B_STS;  	return ssusb_check_clocks(ssusb, check_clk); @@ -190,8 +144,11 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)  	int ret;  	int i; -	/* power down and disable all u3 ports */ +	/* power down and disable 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;  		value |= suspend ? 0 : SSUSB_U3_PORT_DIS; @@ -223,6 +180,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)  static void ssusb_host_setup(struct ssusb_mtk *ssusb)  { +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; +  	host_ports_num_get(ssusb);  	/* @@ -231,6 +190,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)  	 */  	ssusb_host_enable(ssusb); +	if (otg_sx->manual_drd_enabled) +		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);  } @@ -276,19 +238,14 @@ void ssusb_host_exit(struct ssusb_mtk *ssusb)  int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)  { -	int ret = 0; - -	if (ssusb->wakeup_en) { -		ret = ssusb_wakeup_clks_enable(ssusb); +	if (ssusb->wakeup_en)  		ssusb_wakeup_ip_sleep_en(ssusb); -	} -	return ret; + +	return 0;  }  void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)  { -	if (ssusb->wakeup_en) { +	if (ssusb->wakeup_en)  		ssusb_wakeup_ip_sleep_dis(ssusb); -		ssusb_wakeup_clks_disable(ssusb); -	}  } diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 06b29664470f..6ee371478d89 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #ifndef _SSUSB_HW_REGS_H_ @@ -58,6 +49,8 @@  #define U3D_QCR1		(SSUSB_DEV_BASE + 0x0404)  #define U3D_QCR2		(SSUSB_DEV_BASE + 0x0408)  #define U3D_QCR3		(SSUSB_DEV_BASE + 0x040C) +#define U3D_TXQHIAR1		(SSUSB_DEV_BASE + 0x0484) +#define U3D_RXQHIAR1		(SSUSB_DEV_BASE + 0x04C4)  #define U3D_TXQCSR1		(SSUSB_DEV_BASE + 0x0510)  #define U3D_TXQSAR1		(SSUSB_DEV_BASE + 0x0514) @@ -189,6 +182,13 @@  #define QMU_RX_COZ(x)		(BIT(16) << (x))  #define QMU_RX_ZLP(x)		(BIT(0) << (x)) +/* U3D_TXQHIAR1 */ +/* U3D_RXQHIAR1 */ +#define QMU_LAST_DONE_PTR_HI(x)	(((x) >> 16) & 0xf) +#define QMU_CUR_GPD_ADDR_HI(x)	(((x) >> 8) & 0xf) +#define QMU_START_ADDR_HI_MSK	GENMASK(3, 0) +#define QMU_START_ADDR_HI(x)	(((x) & 0xf) << 0) +  /* U3D_TXQCSR1 */  /* U3D_RXQCSR1 */  #define QMU_Q_ACTIVE		BIT(15) @@ -225,6 +225,7 @@  #define CAP_TX_EP_NUM(x)	((x) & 0x1f)  /* U3D_MISC_CTRL */ +#define DMA_ADDR_36BIT		BIT(31)  #define VBUS_ON			BIT(1)  #define VBUS_FRC_EN		BIT(0) @@ -457,11 +458,14 @@  #define SSUSB_VBUS_CHG_INT_B_EN		BIT(6)  /* U3D_SSUSB_U3_CTRL_0P */ +#define SSUSB_U3_PORT_SSP_SPEED	BIT(9)  #define SSUSB_U3_PORT_HOST_SEL		BIT(2)  #define SSUSB_U3_PORT_PDN		BIT(1)  #define SSUSB_U3_PORT_DIS		BIT(0)  /* U3D_SSUSB_U2_CTRL_0P */ +#define SSUSB_U2_PORT_RG_IDDIG		BIT(12) +#define SSUSB_U2_PORT_FORCE_IDDIG	BIT(11)  #define SSUSB_U2_PORT_VBUSVALID	BIT(9)  #define SSUSB_U2_PORT_OTG_SEL		BIT(7)  #define SSUSB_U2_PORT_HOST		BIT(2) diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 088e3e685c4f..3650fd11fc49 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #include <linux/clk.h> @@ -21,7 +12,6 @@  #include <linux/module.h>  #include <linux/of_address.h>  #include <linux/of_irq.h> -#include <linux/pinctrl/consumer.h>  #include <linux/platform_device.h>  #include "mtu3.h" @@ -110,15 +100,9 @@ static void ssusb_phy_power_off(struct ssusb_mtk *ssusb)  		phy_power_off(ssusb->phys[i]);  } -static int ssusb_rscs_init(struct ssusb_mtk *ssusb) +static int ssusb_clks_enable(struct ssusb_mtk *ssusb)  { -	int ret = 0; - -	ret = regulator_enable(ssusb->vusb33); -	if (ret) { -		dev_err(ssusb->dev, "failed to enable vusb33\n"); -		goto vusb33_err; -	} +	int ret;  	ret = clk_prepare_enable(ssusb->sys_clk);  	if (ret) { @@ -132,6 +116,52 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb)  		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; + +	ret = regulator_enable(ssusb->vusb33); +	if (ret) { +		dev_err(ssusb->dev, "failed to enable vusb33\n"); +		goto vusb33_err; +	} + +	ret = ssusb_clks_enable(ssusb); +	if (ret) +		goto clks_err; +  	ret = ssusb_phy_init(ssusb);  	if (ret) {  		dev_err(ssusb->dev, "failed to init phy\n"); @@ -149,20 +179,16 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb)  phy_err:  	ssusb_phy_exit(ssusb);  phy_init_err: -	clk_disable_unprepare(ssusb->ref_clk); -ref_clk_err: -	clk_disable_unprepare(ssusb->sys_clk); -sys_clk_err: +	ssusb_clks_disable(ssusb); +clks_err:  	regulator_disable(ssusb->vusb33);  vusb33_err: -  	return ret;  }  static void ssusb_rscs_exit(struct ssusb_mtk *ssusb)  { -	clk_disable_unprepare(ssusb->sys_clk); -	clk_disable_unprepare(ssusb->ref_clk); +	ssusb_clks_disable(ssusb);  	regulator_disable(ssusb->vusb33);  	ssusb_phy_power_off(ssusb);  	ssusb_phy_exit(ssusb); @@ -176,31 +202,17 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)  	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);  } -static int get_iddig_pinctrl(struct ssusb_mtk *ssusb) +/* ignore the error if the clock does not exist */ +static struct clk *get_optional_clk(struct device *dev, const char *id)  { -	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - -	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev); -	if (IS_ERR(otg_sx->id_pinctrl)) { -		dev_err(ssusb->dev, "Cannot find id pinctrl!\n"); -		return PTR_ERR(otg_sx->id_pinctrl); -	} - -	otg_sx->id_float = -		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float"); -	if (IS_ERR(otg_sx->id_float)) { -		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n"); -		return PTR_ERR(otg_sx->id_float); -	} +	struct clk *opt_clk; -	otg_sx->id_ground = -		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground"); -	if (IS_ERR(otg_sx->id_ground)) { -		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n"); -		return PTR_ERR(otg_sx->id_ground); -	} +	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; -	return 0; +	return opt_clk;  }  static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) @@ -225,18 +237,17 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)  		return PTR_ERR(ssusb->sys_clk);  	} -	/* -	 * reference clock is usually a "fixed-clock", make it optional -	 * for backward compatibility and ignore the error if it does -	 * not exist. -	 */ -	ssusb->ref_clk = devm_clk_get(dev, "ref_ck"); -	if (IS_ERR(ssusb->ref_clk)) { -		if (PTR_ERR(ssusb->ref_clk) == -EPROBE_DEFER) -			return -EPROBE_DEFER; +	ssusb->ref_clk = get_optional_clk(dev, "ref_ck"); +	if (IS_ERR(ssusb->ref_clk)) +		return PTR_ERR(ssusb->ref_clk); -		ssusb->ref_clk = NULL; -	} +	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);  	ssusb->num_phys = of_count_phandle_with_args(node,  			"phys", "#phy-cells"); @@ -263,10 +274,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)  		return PTR_ERR(ssusb->ippc_base);  	ssusb->dr_mode = usb_get_dr_mode(dev); -	if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) { -		dev_err(dev, "dr_mode is error\n"); -		return -EINVAL; -	} +	if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) +		ssusb->dr_mode = USB_DR_MODE_OTG;  	if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)  		return 0; @@ -276,10 +285,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)  	if (ret)  		return ret; -	if (ssusb->dr_mode != USB_DR_MODE_OTG) -		return 0; +	/* optional property, ignore the error if it does not exist */ +	of_property_read_u32(node, "mediatek,u3p-dis-msk", +			     &ssusb->u3p_dis_msk); -	/* if dual-role mode is supported */  	vbus = devm_regulator_get(&pdev->dev, "vbus");  	if (IS_ERR(vbus)) {  		dev_err(dev, "failed to get vbus\n"); @@ -287,6 +296,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)  	}  	otg_sx->vbus = vbus; +	if (ssusb->dr_mode == USB_DR_MODE_HOST) +		return 0; + +	/* 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"); @@ -297,15 +310,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)  			dev_err(ssusb->dev, "couldn't get extcon device\n");  			return -EPROBE_DEFER;  		} -		if (otg_sx->manual_drd_enabled) { -			ret = get_iddig_pinctrl(ssusb); -			if (ret) -				return ret; -		}  	} -	dev_info(dev, "dr_mode: %d, is_u3_dr: %d\n", -		ssusb->dr_mode, otg_sx->is_u3_drd); +	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");  	return 0;  } @@ -447,8 +456,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)  	ssusb_host_disable(ssusb, true);  	ssusb_phy_power_off(ssusb); -	clk_disable_unprepare(ssusb->sys_clk); -	clk_disable_unprepare(ssusb->ref_clk); +	ssusb_clks_disable(ssusb);  	ssusb_wakeup_enable(ssusb);  	return 0; @@ -466,27 +474,21 @@ static int __maybe_unused mtu3_resume(struct device *dev)  		return 0;  	ssusb_wakeup_disable(ssusb); -	ret = clk_prepare_enable(ssusb->sys_clk); -	if (ret) -		goto err_sys_clk; - -	ret = clk_prepare_enable(ssusb->ref_clk); +	ret = ssusb_clks_enable(ssusb);  	if (ret) -		goto err_ref_clk; +		goto clks_err;  	ret = ssusb_phy_power_on(ssusb);  	if (ret) -		goto err_power_on; +		goto phy_err;  	ssusb_host_enable(ssusb);  	return 0; -err_power_on: -	clk_disable_unprepare(ssusb->ref_clk); -err_ref_clk: -	clk_disable_unprepare(ssusb->sys_clk); -err_sys_clk: +phy_err: +	ssusb_clks_disable(ssusb); +clks_err:  	return ret;  } diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 7d9ba8a52368..ff62ba232177 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_qmu.c - Queue Management Unit driver for device controller   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  /* @@ -40,7 +31,58 @@  #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 HILO_GEN64(hi, lo) (((u64)(hi) << 32) + (lo)) +#define HILO_DMA(hi, lo)	\ +	((dma_addr_t)HILO_GEN64((le32_to_cpu(hi)), (le32_to_cpu(lo)))) + +static dma_addr_t read_txq_cur_addr(void __iomem *mbase, u8 epnum) +{ +	u32 txcpr; +	u32 txhiar; + +	txcpr = mtu3_readl(mbase, USB_QMU_TQCPR(epnum)); +	txhiar = mtu3_readl(mbase, USB_QMU_TQHIAR(epnum)); +	return HILO_DMA(QMU_CUR_GPD_ADDR_HI(txhiar), txcpr); +} + +static dma_addr_t read_rxq_cur_addr(void __iomem *mbase, u8 epnum) +{ +	u32 rxcpr; +	u32 rxhiar; + +	rxcpr = mtu3_readl(mbase, USB_QMU_RQCPR(epnum)); +	rxhiar = mtu3_readl(mbase, USB_QMU_RQHIAR(epnum)); + +	return HILO_DMA(QMU_CUR_GPD_ADDR_HI(rxhiar), rxcpr); +} + +static void write_txq_start_addr(void __iomem *mbase, u8 epnum, dma_addr_t dma) +{ +	u32 tqhiar; + +	mtu3_writel(mbase, USB_QMU_TQSAR(epnum), +		    cpu_to_le32(lower_32_bits(dma))); +	tqhiar = mtu3_readl(mbase, USB_QMU_TQHIAR(epnum)); +	tqhiar &= ~QMU_START_ADDR_HI_MSK; +	tqhiar |= QMU_START_ADDR_HI(upper_32_bits(dma)); +	mtu3_writel(mbase, USB_QMU_TQHIAR(epnum), tqhiar); +} + +static void write_rxq_start_addr(void __iomem *mbase, u8 epnum, dma_addr_t dma) +{ +	u32 rqhiar; + +	mtu3_writel(mbase, USB_QMU_RQSAR(epnum), +		    cpu_to_le32(lower_32_bits(dma))); +	rqhiar = mtu3_readl(mbase, USB_QMU_RQHIAR(epnum)); +	rqhiar &= ~QMU_START_ADDR_HI_MSK; +	rqhiar |= QMU_START_ADDR_HI(upper_32_bits(dma)); +	mtu3_writel(mbase, USB_QMU_RQHIAR(epnum), rqhiar); +}  static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring,  		dma_addr_t dma_addr) @@ -193,21 +235,27 @@ 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; +	dma_addr_t enq_dma; +	u16 ext_addr;  	/* set all fields to zero as default value */  	memset(gpd, 0, sizeof(*gpd)); -	gpd->buffer = cpu_to_le32((u32)req->dma); +	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;  	/* get the next GPD */  	enq = advance_enq_gpd(ring); -	dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n", -		mep->epnum, gpd, enq); +	enq_dma = gpd_virt_to_dma(ring, enq); +	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; -	gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq)); +	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; @@ -226,21 +274,27 @@ 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; +	dma_addr_t enq_dma; +	u16 ext_addr;  	/* set all fields to zero as default value */  	memset(gpd, 0, sizeof(*gpd)); -	gpd->buffer = cpu_to_le32((u32)req->dma); +	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;  	/* get the next GPD */  	enq = advance_enq_gpd(ring); -	dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n", -		mep->epnum, gpd, enq); +	enq_dma = gpd_virt_to_dma(ring, enq); +	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; -	gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq)); +	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->chksum = qmu_calc_checksum((u8 *)gpd);  	gpd->flag |= GPD_FLAGS_HWO; @@ -267,8 +321,8 @@ int mtu3_qmu_start(struct mtu3_ep *mep)  	if (mep->is_in) {  		/* set QMU start address */ -		mtu3_writel(mbase, USB_QMU_TQSAR(mep->epnum), ring->dma); -		mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); +		write_txq_start_addr(mbase, epnum, ring->dma); +		mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN);  		mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum));  		/* send zero length packet according to ZLP flag in GPD */  		mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum)); @@ -282,8 +336,8 @@ int mtu3_qmu_start(struct mtu3_ep *mep)  		mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);  	} else { -		mtu3_writel(mbase, USB_QMU_RQSAR(mep->epnum), ring->dma); -		mtu3_setbits(mbase, MU3D_EP_RXCR0(mep->epnum), RX_DMAREQEN); +		write_rxq_start_addr(mbase, epnum, ring->dma); +		mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN);  		mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum));  		/* don't expect ZLP */  		mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum)); @@ -353,9 +407,9 @@ 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; -	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));  	struct usb_request *req = NULL;  	struct mtu3_request *mreq; +	dma_addr_t cur_gpd_dma;  	u32 txcsr = 0;  	int ret; @@ -365,7 +419,8 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)  	else  		return; -	gpd_current = gpd_dma_to_virt(ring, gpd_dma); +	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) {  		dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum); @@ -408,12 +463,13 @@ static void qmu_done_tx(struct mtu3 *mtu, u8 epnum)  	void __iomem *mbase = mtu->mac_base;  	struct qmu_gpd *gpd = ring->dequeue;  	struct qmu_gpd *gpd_current = NULL; -	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));  	struct usb_request *request = NULL;  	struct mtu3_request *mreq; +	dma_addr_t cur_gpd_dma;  	/*transfer phy address got from QMU register to virtual address */ -	gpd_current = gpd_dma_to_virt(ring, gpd_dma); +	cur_gpd_dma = read_txq_cur_addr(mbase, epnum); +	gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma);  	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",  		__func__, epnum, gpd, gpd_current, ring->enqueue); @@ -446,11 +502,12 @@ static void qmu_done_rx(struct mtu3 *mtu, u8 epnum)  	void __iomem *mbase = mtu->mac_base;  	struct qmu_gpd *gpd = ring->dequeue;  	struct qmu_gpd *gpd_current = NULL; -	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum));  	struct usb_request *req = NULL;  	struct mtu3_request *mreq; +	dma_addr_t cur_gpd_dma; -	gpd_current = gpd_dma_to_virt(ring, gpd_dma); +	cur_gpd_dma = read_rxq_cur_addr(mbase, epnum); +	gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma);  	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",  		__func__, epnum, gpd, gpd_current, ring->enqueue); diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h index 4dafa16bf120..81f5151a55ed 100644 --- a/drivers/usb/mtu3/mtu3_qmu.h +++ b/drivers/usb/mtu3/mtu3_qmu.h @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * mtu3_qmu.h - Queue Management Unit driver header   *   * Copyright (C) 2016 MediaTek Inc.   *   * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - *   */  #ifndef __MTK_QMU_H__  | 

