All of lore.kernel.org
 help / color / mirror / Atom feed
From: chunfeng yun <chunfeng.yun@mediatek.com>
To: Oliver Neukum <oneukum@suse.com>
Cc: Matthias Brugger <matthias.bgg@gmail.com>,
	Mathias Nyman <mathias.nyman@intel.com>,
	Felipe Balbi <felipe.balbi@linux.intel.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Pawel Moll <pawel.moll@arm.com>, KumarGala <galak@codeaurora.org>,
	Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>,
	AlanCooper <alcooperx@gmail.com>,
	"Ian Campbell" <ijc+devicetree@hellion.org.uk>,
	Rob Herring <robh+dt@kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Alan Stern <stern@rowland.harvard.edu>,
	<devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<linux-usb@vger.kernel.org>
Subject: Re: [RESEND PATCH, v5 4/5] usb: Add MediaTek USB3 DRD Driver
Date: Fri, 26 Aug 2016 17:38:27 +0800	[thread overview]
Message-ID: <1472204307.27677.41.camel@mhfsdcap03> (raw)
In-Reply-To: <1472113973.2877.22.camel@suse.com>

Hi,

On Thu, 2016-08-25 at 10:32 +0200, Oliver Neukum wrote:
> On Thu, 2016-08-25 at 11:05 +0800, Chunfeng Yun wrote:
> > This patch adds support for the MediaTek USB3 controller
> > integrated into MT8173. It can be configured as Dual-Role
> > Device (DRD), Peripheral Only and Host Only (xHCI) modes.
> > 
> 
> > +/**
> > + * 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:
> > + *	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
> > + *	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
> > + * @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
> > + * @ext_flag:
> > + *	bit5 (TX ONLY): Zero Length Packet (ZLP),
> > + */
> > +struct qmu_gpd {
> > +	u8 flag;
> > +	u8 chksum;
> > +	u16 data_buf_len;
> > +	u32 next_gpd;
> > +	u32 buffer;
> > +	u16 buf_len;
> > +	u8 ext_len;
> > +	u8 ext_flag;
> > +} __packed;
> 
> It looks like this is shared with hardware in memory.
> But you leave the endianness of the bigger fields undeclared.
> 
The driver only supports Little-Endian SoCs currently, because I have no
Big-Endian platform to test it.
 
> > +/**
> > +* 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 {
> > +	dma_addr_t dma;
> > +	struct qmu_gpd *start;
> > +	struct qmu_gpd *end;
> > +	struct qmu_gpd *enqueue;
> > +	struct qmu_gpd *dequeue;
> > +};
> > +
> > +/**
> > +* @vbus: vbus 5V used by host mode
> > +* @edev: external connector used to detect vbus and iddig changes
> > +* @vbus_nb: notifier for vbus detection
> > +* @vbus_nb: notifier for iddig(idpin) detection
> > +* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
> > +*		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.
> 
> Please use a common interface for this. The Intel people are introducing
> one.
Can you give me the link address of the patch, I didn't find it.

> 
> 
> > +#endif
> > diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
> > new file mode 100644
> > index 0000000..fdc11b6
> > --- /dev/null
> > +++ b/drivers/usb/mtu3/mtu3_core.c
> > @@ -0,0 +1,874 @@
> > +/*
> > + * mtu3_core.c - hardware access layer and gadget init/exit of
> > + *                     MediaTek usb3 Dual-Role Controller Driver
> > + *
> > + * 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/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "mtu3.h"
> > +
> > +static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
> > +{
> > +	struct mtu3_fifo_info *fifo = mep->fifo;
> > +	u32 num_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
> > +	u32 start_bit;
> > +
> > +	/* ensure that @mep->fifo_seg_size is power of two */
> > +	num_bits = roundup_pow_of_two(num_bits);
> > +	if (num_bits > fifo->limit)
> > +		return -EINVAL;
> > +
> > +	mep->fifo_seg_size = num_bits * MTU3_EP_FIFO_UNIT;
> > +	num_bits = num_bits * (mep->slot + 1);
> > +	start_bit = bitmap_find_next_zero_area(fifo->bitmap,
> > +			fifo->limit, 0, num_bits, 0);
> > +	if (start_bit >= fifo->limit)
> > +		return -EOVERFLOW;
> > +
> > +	bitmap_set(fifo->bitmap, start_bit, num_bits);
> > +	mep->fifo_size = num_bits * MTU3_EP_FIFO_UNIT;
> > +	mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
> > +
> > +	dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
> > +		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
> 
> If you use the "f" option to dynamic debugging it will give you the
> function name anyway. So why include __func__ ?
> 
It's used when dynamic debugging is disable but defines DEBUG macro.

> 
> > +/* enable/disable U3D SS function */
> > +static void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
> 
> Inline maybe ?
> 
> > +{
> > +	/* If usb3_en==0, LTSSM will go to SS.Disable state */
> > +	if (enable)
> > +		mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> > +	else
> > +		mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> > +
> > +	dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
> > +}
> > +
> 
> [..]
> 
> 
> > +int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
> > +			int interval, int burst, int mult)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	int epnum = mep->epnum;
> > +	u32 csr0, csr1, csr2;
> > +	int fifo_sgsz, fifo_addr;
> > +	int num_pkts;
> > +
> > +	fifo_addr = ep_fifo_alloc(mep, mep->maxp);
> > +	if (fifo_addr < 0) {
> > +		dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", mep->maxp);
> > +		return -ENOMEM;
> > +	}
> > +	fifo_sgsz = ilog2(mep->fifo_seg_size);
> > +	dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifo_sgsz,
> > +		mep->fifo_seg_size, mep->fifo_size);
> > +
> > +	if (mep->is_in) {
> > +		csr0 = TX_TXMAXPKTSZ(mep->maxp);
> > +		csr0 |= TX_DMAREQEN;
> > +
> > +		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);
> > +
> > +		csr2 = TX_FIFOADDR(fifo_addr >> 4);
> > +		csr2 |= TX_FIFOSEGSIZE(fifo_sgsz);
> > +
> > +		switch (mep->type) {
> > +		case USB_ENDPOINT_XFER_BULK:
> > +			csr1 |= TX_TYPE(TYPE_BULK);
> > +			break;
> > +		case USB_ENDPOINT_XFER_ISOC:
> > +			csr1 |= TX_TYPE(TYPE_ISO);
> > +			csr2 |= TX_BINTERVAL(interval);
> > +			break;
> > +		case USB_ENDPOINT_XFER_INT:
> > +			csr1 |= TX_TYPE(TYPE_INT);
> > +			csr2 |= TX_BINTERVAL(interval);
> > +			break;
> 
> And if it is control?
The function is only called for non control EP.
I will add a comment for it.
> 
> > +		}
> > +
> > +		/* Enable QMU Done interrupt */
> > +		mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
> > +
> > +		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
> > +		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
> > +		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
> > +
> > +		dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> > +			epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
> > +	} else {
> > +		csr0 = RX_RXMAXPKTSZ(mep->maxp);
> > +		csr0 |= RX_DMAREQEN;
> > +
> > +		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);
> > +
> > +		csr2 = RX_FIFOADDR(fifo_addr >> 4);
> > +		csr2 |= RX_FIFOSEGSIZE(fifo_sgsz);
> > +
> > +		switch (mep->type) {
> > +		case USB_ENDPOINT_XFER_BULK:
> > +			csr1 |= RX_TYPE(TYPE_BULK);
> > +			break;
> > +		case USB_ENDPOINT_XFER_ISOC:
> > +			csr1 |= RX_TYPE(TYPE_ISO);
> > +			csr2 |= RX_BINTERVAL(interval);
> > +			break;
> > +		case USB_ENDPOINT_XFER_INT:
> > +			csr1 |= RX_TYPE(TYPE_INT);
> > +			csr2 |= RX_BINTERVAL(interval);
> > +			break;
> 
> Again: control endpoints?
Only for non-EP0.

> 
> > +		}
> > +
> > +		/*Enable QMU Done interrupt */
> > +		mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
> > +
> > +		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
> > +		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
> > +		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
> > +
> > +		dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> > +			epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
> > +	}
> > +
> > +	dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
> > +	dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
> > +		__func__, mep->name, mep->fifo_addr, mep->fifo_size,
> > +		fifo_sgsz, mep->fifo_seg_size);
> > +
> > +	return 0;
> > +}
> > +
> 
> [..]
> > +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;
> 
> You are missing the checks for USB_SPEED_WIRELESS in general.
> Can you just ignore it?
Doesn't support USB_SPEED_WIRELESS, so ignore it.
> 
> > +
> > +	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);
> > +	}
> > +	dev_info(mtu->dev, "max_speed: %s\n",
> > +		usb_speed_string(mtu->max_speed));
> > +}
> 
> [..]
> > +static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	enum usb_device_speed udev_speed;
> > +	u32 maxpkt = 64;
> > +	u32 link;
> > +	u32 speed;
> > +
> > +	link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
> > +	link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
> > +	mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
> > +	dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
> > +
> > +	if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
> > +		return IRQ_NONE;
> 
> Shouldn't you do this check before you clear interrupts?
In fact, U3D_DEV_LINK_INTR has only one interrupt,
SSUSB_DEV_SPEED_CHG_INTR, others can be treated as spurious ones.

> 
> > +	speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
> 
> Do you really want to read this out of the hardware on every interrupt?
Yes, the interrupt is triggered only when speed is changed,
and get the new speed here.

> 
> > +
> > +	switch (speed) {
> > +	case MTU3_SPEED_FULL:
> > +		udev_speed = USB_SPEED_FULL;
> > +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> > +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> > +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> > +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> > +				LPM_BESL_STALL | LPM_BESLD_STALL);
> > +		break;
> > +	case MTU3_SPEED_HIGH:
> > +		udev_speed = USB_SPEED_HIGH;
> > +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> > +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> > +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> > +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> > +				LPM_BESL_STALL | LPM_BESLD_STALL);
> > +		break;
> > +	case MTU3_SPEED_SUPER:
> > +		udev_speed = USB_SPEED_SUPER;
> > +		maxpkt = 512;
> > +		break;
> > +	default:
> > +		udev_speed = USB_SPEED_UNKNOWN;
> > +		break;
> > +	}
> > +	dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
> > +
> > +	mtu->g.speed = udev_speed;
> > +	mtu->g.ep0->maxpacket = maxpkt;
> > +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> > +
> > +	if (udev_speed == USB_SPEED_UNKNOWN)
> > +		mtu3_gadget_disconnect(mtu);
> > +	else
> > +		mtu3_ep0_setup(mtu);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	u32 ltssm;
> > +
> > +	ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
> > +	ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
> > +	mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
> > +	dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
> > +
> > +	if (ltssm & HOT_RST_INTR)
> > +		mtu3_gadget_reset(mtu);
> > +
> > +	if (ltssm & WARM_RST_INTR)
> > +		mtu3_gadget_reset(mtu);
> 
> You really would reset the gadget twice if that happens?
> And do the rest of the tests make sense in that case?
I will merge them into one as following:

   if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
        ...

> 
> > +
> > +	if (ltssm & VBUS_FALL_INTR)
> > +		mtu3_ss_func_set(mtu, false);
> > +
> > +	if (ltssm & VBUS_RISE_INTR)
> > +		mtu3_ss_func_set(mtu, true);
> > +
> > +	if (ltssm & EXIT_U3_INTR)
> > +		mtu3_gadget_resume(mtu);
> > +
> > +	if (ltssm & ENTER_U3_INTR)
> > +		mtu3_gadget_suspend(mtu);
> > +
> > +	return IRQ_HANDLED;
> > +}
> 
> [..]
> 
> > +static int mtu3_hw_init(struct mtu3 *mtu)
> > +{
> > +	int ret;
> > +
> > +	mtu3_device_reset(mtu);
> > +
> > +	ret = mtu3_device_enable(mtu);
> > +	if (ret) {
> > +		dev_err(mtu->dev, "device enable failed %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = mtu3_mem_alloc(mtu);
> > +	if (ret) {
> > +		dev_err(mtu->dev, "mem alloc failed, aborting\n");
> 
> 1. You can't leave the device enabled in that case.
It enable device mac and port, otherwise I can't initialize other
register.
and the function is not enable until udc_start() is called.

> 2. No need for a message. The kernel will already do that if kmalloc()
> fails under such circumstances.
remove it latter.
> 
> > +		return -ENOMEM;
> > +	}
> > +
> > +	mtu3_regs_init(mtu);
> > +
> > +	return 0;
> > +}
> 
> > diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
> > new file mode 100644
> > index 0000000..f560f93
> > --- /dev/null
> > +++ b/drivers/usb/mtu3/mtu3_dr.c
> > @@ -0,0 +1,375 @@
> > +/*
> > + * 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>
> > +#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 "mtu3.h"
> > +#include "mtu3_dr.h"
> > +
> > +#define USB2_PORT 2
> > +#define USB3_PORT 3
> > +
[...]
> > +
> > +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_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");
> 
> No proper error returns

add it later.

> > +
> > +	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 void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
> > +{
> > +	debugfs_remove_recursive(ssusb->dbgfs_root);
> > +}
> > +
> > +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)
> > +		ssusb_debugfs_init(ssusb);
> > +
> > +	/* It is enough to delay 1s for waiting for host initialization */
> > +	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
> 
> You need to handle this still pending when you are deregistered.
OK.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
> > +{
> > +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> > +
> > +	if (otg_sx->edev) {
> > +		extcon_unregister_notifier(otg_sx->edev,
> > +			EXTCON_USB, &otg_sx->vbus_nb);
> > +		extcon_unregister_notifier(otg_sx->edev,
> > +			EXTCON_USB_HOST, &otg_sx->id_nb);
> > +	}
> > +
> > +	if (otg_sx->manual_drd_enabled)
> > +		ssusb_debugfs_exit(ssusb);
> > +}
> 
> Could you break this up a bit? It is extensively long a patch?
I afraid I can't break the patch into small ones but also provide
complete functionality at the same time.
Sorry for inconvenience.
> 
Thank you very much :-)

> 	Regards
> 		Oliver
> 

WARNING: multiple messages have this Message-ID (diff)
From: chunfeng yun <chunfeng.yun-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
To: Oliver Neukum <oneukum-IBi9RG/b67k@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Felipe Balbi
	<felipe.balbi-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>,
	Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org>,
	Ian Campbell
	<ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org>,
	Greg Kroah-Hartman
	<gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>,
	Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>,
	AlanCooper <alcooperx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Mathias Nyman
	<mathias.nyman-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>,
	Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	KumarGala <galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>,
	Matthias Brugger
	<matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
	Alan Stern
	<stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz@public.gmane.org>,
	Sergei Shtylyov
	<sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Subject: Re: [RESEND PATCH, v5 4/5] usb: Add MediaTek USB3 DRD Driver
Date: Fri, 26 Aug 2016 17:38:27 +0800	[thread overview]
Message-ID: <1472204307.27677.41.camel@mhfsdcap03> (raw)
In-Reply-To: <1472113973.2877.22.camel-IBi9RG/b67k@public.gmane.org>

Hi,

On Thu, 2016-08-25 at 10:32 +0200, Oliver Neukum wrote:
> On Thu, 2016-08-25 at 11:05 +0800, Chunfeng Yun wrote:
> > This patch adds support for the MediaTek USB3 controller
> > integrated into MT8173. It can be configured as Dual-Role
> > Device (DRD), Peripheral Only and Host Only (xHCI) modes.
> > 
> 
> > +/**
> > + * 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:
> > + *	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
> > + *	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
> > + * @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
> > + * @ext_flag:
> > + *	bit5 (TX ONLY): Zero Length Packet (ZLP),
> > + */
> > +struct qmu_gpd {
> > +	u8 flag;
> > +	u8 chksum;
> > +	u16 data_buf_len;
> > +	u32 next_gpd;
> > +	u32 buffer;
> > +	u16 buf_len;
> > +	u8 ext_len;
> > +	u8 ext_flag;
> > +} __packed;
> 
> It looks like this is shared with hardware in memory.
> But you leave the endianness of the bigger fields undeclared.
> 
The driver only supports Little-Endian SoCs currently, because I have no
Big-Endian platform to test it.
 
> > +/**
> > +* 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 {
> > +	dma_addr_t dma;
> > +	struct qmu_gpd *start;
> > +	struct qmu_gpd *end;
> > +	struct qmu_gpd *enqueue;
> > +	struct qmu_gpd *dequeue;
> > +};
> > +
> > +/**
> > +* @vbus: vbus 5V used by host mode
> > +* @edev: external connector used to detect vbus and iddig changes
> > +* @vbus_nb: notifier for vbus detection
> > +* @vbus_nb: notifier for iddig(idpin) detection
> > +* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
> > +*		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.
> 
> Please use a common interface for this. The Intel people are introducing
> one.
Can you give me the link address of the patch, I didn't find it.

> 
> 
> > +#endif
> > diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
> > new file mode 100644
> > index 0000000..fdc11b6
> > --- /dev/null
> > +++ b/drivers/usb/mtu3/mtu3_core.c
> > @@ -0,0 +1,874 @@
> > +/*
> > + * mtu3_core.c - hardware access layer and gadget init/exit of
> > + *                     MediaTek usb3 Dual-Role Controller Driver
> > + *
> > + * Copyright (C) 2016 MediaTek Inc.
> > + *
> > + * Author: Chunfeng Yun <chunfeng.yun-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * 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/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "mtu3.h"
> > +
> > +static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
> > +{
> > +	struct mtu3_fifo_info *fifo = mep->fifo;
> > +	u32 num_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
> > +	u32 start_bit;
> > +
> > +	/* ensure that @mep->fifo_seg_size is power of two */
> > +	num_bits = roundup_pow_of_two(num_bits);
> > +	if (num_bits > fifo->limit)
> > +		return -EINVAL;
> > +
> > +	mep->fifo_seg_size = num_bits * MTU3_EP_FIFO_UNIT;
> > +	num_bits = num_bits * (mep->slot + 1);
> > +	start_bit = bitmap_find_next_zero_area(fifo->bitmap,
> > +			fifo->limit, 0, num_bits, 0);
> > +	if (start_bit >= fifo->limit)
> > +		return -EOVERFLOW;
> > +
> > +	bitmap_set(fifo->bitmap, start_bit, num_bits);
> > +	mep->fifo_size = num_bits * MTU3_EP_FIFO_UNIT;
> > +	mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
> > +
> > +	dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
> > +		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
> 
> If you use the "f" option to dynamic debugging it will give you the
> function name anyway. So why include __func__ ?
> 
It's used when dynamic debugging is disable but defines DEBUG macro.

> 
> > +/* enable/disable U3D SS function */
> > +static void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
> 
> Inline maybe ?
> 
> > +{
> > +	/* If usb3_en==0, LTSSM will go to SS.Disable state */
> > +	if (enable)
> > +		mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> > +	else
> > +		mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> > +
> > +	dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
> > +}
> > +
> 
> [..]
> 
> 
> > +int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
> > +			int interval, int burst, int mult)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	int epnum = mep->epnum;
> > +	u32 csr0, csr1, csr2;
> > +	int fifo_sgsz, fifo_addr;
> > +	int num_pkts;
> > +
> > +	fifo_addr = ep_fifo_alloc(mep, mep->maxp);
> > +	if (fifo_addr < 0) {
> > +		dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", mep->maxp);
> > +		return -ENOMEM;
> > +	}
> > +	fifo_sgsz = ilog2(mep->fifo_seg_size);
> > +	dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifo_sgsz,
> > +		mep->fifo_seg_size, mep->fifo_size);
> > +
> > +	if (mep->is_in) {
> > +		csr0 = TX_TXMAXPKTSZ(mep->maxp);
> > +		csr0 |= TX_DMAREQEN;
> > +
> > +		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);
> > +
> > +		csr2 = TX_FIFOADDR(fifo_addr >> 4);
> > +		csr2 |= TX_FIFOSEGSIZE(fifo_sgsz);
> > +
> > +		switch (mep->type) {
> > +		case USB_ENDPOINT_XFER_BULK:
> > +			csr1 |= TX_TYPE(TYPE_BULK);
> > +			break;
> > +		case USB_ENDPOINT_XFER_ISOC:
> > +			csr1 |= TX_TYPE(TYPE_ISO);
> > +			csr2 |= TX_BINTERVAL(interval);
> > +			break;
> > +		case USB_ENDPOINT_XFER_INT:
> > +			csr1 |= TX_TYPE(TYPE_INT);
> > +			csr2 |= TX_BINTERVAL(interval);
> > +			break;
> 
> And if it is control?
The function is only called for non control EP.
I will add a comment for it.
> 
> > +		}
> > +
> > +		/* Enable QMU Done interrupt */
> > +		mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
> > +
> > +		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
> > +		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
> > +		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
> > +
> > +		dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> > +			epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
> > +	} else {
> > +		csr0 = RX_RXMAXPKTSZ(mep->maxp);
> > +		csr0 |= RX_DMAREQEN;
> > +
> > +		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);
> > +
> > +		csr2 = RX_FIFOADDR(fifo_addr >> 4);
> > +		csr2 |= RX_FIFOSEGSIZE(fifo_sgsz);
> > +
> > +		switch (mep->type) {
> > +		case USB_ENDPOINT_XFER_BULK:
> > +			csr1 |= RX_TYPE(TYPE_BULK);
> > +			break;
> > +		case USB_ENDPOINT_XFER_ISOC:
> > +			csr1 |= RX_TYPE(TYPE_ISO);
> > +			csr2 |= RX_BINTERVAL(interval);
> > +			break;
> > +		case USB_ENDPOINT_XFER_INT:
> > +			csr1 |= RX_TYPE(TYPE_INT);
> > +			csr2 |= RX_BINTERVAL(interval);
> > +			break;
> 
> Again: control endpoints?
Only for non-EP0.

> 
> > +		}
> > +
> > +		/*Enable QMU Done interrupt */
> > +		mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
> > +
> > +		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
> > +		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
> > +		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
> > +
> > +		dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> > +			epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
> > +	}
> > +
> > +	dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
> > +	dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
> > +		__func__, mep->name, mep->fifo_addr, mep->fifo_size,
> > +		fifo_sgsz, mep->fifo_seg_size);
> > +
> > +	return 0;
> > +}
> > +
> 
> [..]
> > +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;
> 
> You are missing the checks for USB_SPEED_WIRELESS in general.
> Can you just ignore it?
Doesn't support USB_SPEED_WIRELESS, so ignore it.
> 
> > +
> > +	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);
> > +	}
> > +	dev_info(mtu->dev, "max_speed: %s\n",
> > +		usb_speed_string(mtu->max_speed));
> > +}
> 
> [..]
> > +static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	enum usb_device_speed udev_speed;
> > +	u32 maxpkt = 64;
> > +	u32 link;
> > +	u32 speed;
> > +
> > +	link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
> > +	link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
> > +	mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
> > +	dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
> > +
> > +	if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
> > +		return IRQ_NONE;
> 
> Shouldn't you do this check before you clear interrupts?
In fact, U3D_DEV_LINK_INTR has only one interrupt,
SSUSB_DEV_SPEED_CHG_INTR, others can be treated as spurious ones.

> 
> > +	speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
> 
> Do you really want to read this out of the hardware on every interrupt?
Yes, the interrupt is triggered only when speed is changed,
and get the new speed here.

> 
> > +
> > +	switch (speed) {
> > +	case MTU3_SPEED_FULL:
> > +		udev_speed = USB_SPEED_FULL;
> > +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> > +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> > +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> > +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> > +				LPM_BESL_STALL | LPM_BESLD_STALL);
> > +		break;
> > +	case MTU3_SPEED_HIGH:
> > +		udev_speed = USB_SPEED_HIGH;
> > +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> > +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> > +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> > +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> > +				LPM_BESL_STALL | LPM_BESLD_STALL);
> > +		break;
> > +	case MTU3_SPEED_SUPER:
> > +		udev_speed = USB_SPEED_SUPER;
> > +		maxpkt = 512;
> > +		break;
> > +	default:
> > +		udev_speed = USB_SPEED_UNKNOWN;
> > +		break;
> > +	}
> > +	dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
> > +
> > +	mtu->g.speed = udev_speed;
> > +	mtu->g.ep0->maxpacket = maxpkt;
> > +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> > +
> > +	if (udev_speed == USB_SPEED_UNKNOWN)
> > +		mtu3_gadget_disconnect(mtu);
> > +	else
> > +		mtu3_ep0_setup(mtu);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	u32 ltssm;
> > +
> > +	ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
> > +	ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
> > +	mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
> > +	dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
> > +
> > +	if (ltssm & HOT_RST_INTR)
> > +		mtu3_gadget_reset(mtu);
> > +
> > +	if (ltssm & WARM_RST_INTR)
> > +		mtu3_gadget_reset(mtu);
> 
> You really would reset the gadget twice if that happens?
> And do the rest of the tests make sense in that case?
I will merge them into one as following:

   if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
        ...

> 
> > +
> > +	if (ltssm & VBUS_FALL_INTR)
> > +		mtu3_ss_func_set(mtu, false);
> > +
> > +	if (ltssm & VBUS_RISE_INTR)
> > +		mtu3_ss_func_set(mtu, true);
> > +
> > +	if (ltssm & EXIT_U3_INTR)
> > +		mtu3_gadget_resume(mtu);
> > +
> > +	if (ltssm & ENTER_U3_INTR)
> > +		mtu3_gadget_suspend(mtu);
> > +
> > +	return IRQ_HANDLED;
> > +}
> 
> [..]
> 
> > +static int mtu3_hw_init(struct mtu3 *mtu)
> > +{
> > +	int ret;
> > +
> > +	mtu3_device_reset(mtu);
> > +
> > +	ret = mtu3_device_enable(mtu);
> > +	if (ret) {
> > +		dev_err(mtu->dev, "device enable failed %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = mtu3_mem_alloc(mtu);
> > +	if (ret) {
> > +		dev_err(mtu->dev, "mem alloc failed, aborting\n");
> 
> 1. You can't leave the device enabled in that case.
It enable device mac and port, otherwise I can't initialize other
register.
and the function is not enable until udc_start() is called.

> 2. No need for a message. The kernel will already do that if kmalloc()
> fails under such circumstances.
remove it latter.
> 
> > +		return -ENOMEM;
> > +	}
> > +
> > +	mtu3_regs_init(mtu);
> > +
> > +	return 0;
> > +}
> 
> > diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
> > new file mode 100644
> > index 0000000..f560f93
> > --- /dev/null
> > +++ b/drivers/usb/mtu3/mtu3_dr.c
> > @@ -0,0 +1,375 @@
> > +/*
> > + * mtu3_dr.c - dual role switch and host glue layer
> > + *
> > + * Copyright (C) 2016 MediaTek Inc.
> > + *
> > + * Author: Chunfeng Yun <chunfeng.yun-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * 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>
> > +#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 "mtu3.h"
> > +#include "mtu3_dr.h"
> > +
> > +#define USB2_PORT 2
> > +#define USB3_PORT 3
> > +
[...]
> > +
> > +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_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");
> 
> No proper error returns

add it later.

> > +
> > +	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 void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
> > +{
> > +	debugfs_remove_recursive(ssusb->dbgfs_root);
> > +}
> > +
> > +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)
> > +		ssusb_debugfs_init(ssusb);
> > +
> > +	/* It is enough to delay 1s for waiting for host initialization */
> > +	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
> 
> You need to handle this still pending when you are deregistered.
OK.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
> > +{
> > +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> > +
> > +	if (otg_sx->edev) {
> > +		extcon_unregister_notifier(otg_sx->edev,
> > +			EXTCON_USB, &otg_sx->vbus_nb);
> > +		extcon_unregister_notifier(otg_sx->edev,
> > +			EXTCON_USB_HOST, &otg_sx->id_nb);
> > +	}
> > +
> > +	if (otg_sx->manual_drd_enabled)
> > +		ssusb_debugfs_exit(ssusb);
> > +}
> 
> Could you break this up a bit? It is extensively long a patch?
I afraid I can't break the patch into small ones but also provide
complete functionality at the same time.
Sorry for inconvenience.
> 
Thank you very much :-)

> 	Regards
> 		Oliver
> 

WARNING: multiple messages have this Message-ID (diff)
From: chunfeng.yun@mediatek.com (chunfeng yun)
To: linux-arm-kernel@lists.infradead.org
Subject: [RESEND PATCH, v5 4/5] usb: Add MediaTek USB3 DRD Driver
Date: Fri, 26 Aug 2016 17:38:27 +0800	[thread overview]
Message-ID: <1472204307.27677.41.camel@mhfsdcap03> (raw)
In-Reply-To: <1472113973.2877.22.camel@suse.com>

Hi,

On Thu, 2016-08-25 at 10:32 +0200, Oliver Neukum wrote:
> On Thu, 2016-08-25 at 11:05 +0800, Chunfeng Yun wrote:
> > This patch adds support for the MediaTek USB3 controller
> > integrated into MT8173. It can be configured as Dual-Role
> > Device (DRD), Peripheral Only and Host Only (xHCI) modes.
> > 
> 
> > +/**
> > + * 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:
> > + *	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
> > + *	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
> > + * @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
> > + * @ext_flag:
> > + *	bit5 (TX ONLY): Zero Length Packet (ZLP),
> > + */
> > +struct qmu_gpd {
> > +	u8 flag;
> > +	u8 chksum;
> > +	u16 data_buf_len;
> > +	u32 next_gpd;
> > +	u32 buffer;
> > +	u16 buf_len;
> > +	u8 ext_len;
> > +	u8 ext_flag;
> > +} __packed;
> 
> It looks like this is shared with hardware in memory.
> But you leave the endianness of the bigger fields undeclared.
> 
The driver only supports Little-Endian SoCs currently, because I have no
Big-Endian platform to test it.
 
> > +/**
> > +* 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 {
> > +	dma_addr_t dma;
> > +	struct qmu_gpd *start;
> > +	struct qmu_gpd *end;
> > +	struct qmu_gpd *enqueue;
> > +	struct qmu_gpd *dequeue;
> > +};
> > +
> > +/**
> > +* @vbus: vbus 5V used by host mode
> > +* @edev: external connector used to detect vbus and iddig changes
> > +* @vbus_nb: notifier for vbus detection
> > +* @vbus_nb: notifier for iddig(idpin) detection
> > +* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
> > +*		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.
> 
> Please use a common interface for this. The Intel people are introducing
> one.
Can you give me the link address of the patch, I didn't find it.

> 
> 
> > +#endif
> > diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
> > new file mode 100644
> > index 0000000..fdc11b6
> > --- /dev/null
> > +++ b/drivers/usb/mtu3/mtu3_core.c
> > @@ -0,0 +1,874 @@
> > +/*
> > + * mtu3_core.c - hardware access layer and gadget init/exit of
> > + *                     MediaTek usb3 Dual-Role Controller Driver
> > + *
> > + * 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/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "mtu3.h"
> > +
> > +static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
> > +{
> > +	struct mtu3_fifo_info *fifo = mep->fifo;
> > +	u32 num_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
> > +	u32 start_bit;
> > +
> > +	/* ensure that @mep->fifo_seg_size is power of two */
> > +	num_bits = roundup_pow_of_two(num_bits);
> > +	if (num_bits > fifo->limit)
> > +		return -EINVAL;
> > +
> > +	mep->fifo_seg_size = num_bits * MTU3_EP_FIFO_UNIT;
> > +	num_bits = num_bits * (mep->slot + 1);
> > +	start_bit = bitmap_find_next_zero_area(fifo->bitmap,
> > +			fifo->limit, 0, num_bits, 0);
> > +	if (start_bit >= fifo->limit)
> > +		return -EOVERFLOW;
> > +
> > +	bitmap_set(fifo->bitmap, start_bit, num_bits);
> > +	mep->fifo_size = num_bits * MTU3_EP_FIFO_UNIT;
> > +	mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
> > +
> > +	dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
> > +		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
> 
> If you use the "f" option to dynamic debugging it will give you the
> function name anyway. So why include __func__ ?
> 
It's used when dynamic debugging is disable but defines DEBUG macro.

> 
> > +/* enable/disable U3D SS function */
> > +static void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
> 
> Inline maybe ?
> 
> > +{
> > +	/* If usb3_en==0, LTSSM will go to SS.Disable state */
> > +	if (enable)
> > +		mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> > +	else
> > +		mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> > +
> > +	dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
> > +}
> > +
> 
> [..]
> 
> 
> > +int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
> > +			int interval, int burst, int mult)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	int epnum = mep->epnum;
> > +	u32 csr0, csr1, csr2;
> > +	int fifo_sgsz, fifo_addr;
> > +	int num_pkts;
> > +
> > +	fifo_addr = ep_fifo_alloc(mep, mep->maxp);
> > +	if (fifo_addr < 0) {
> > +		dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", mep->maxp);
> > +		return -ENOMEM;
> > +	}
> > +	fifo_sgsz = ilog2(mep->fifo_seg_size);
> > +	dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifo_sgsz,
> > +		mep->fifo_seg_size, mep->fifo_size);
> > +
> > +	if (mep->is_in) {
> > +		csr0 = TX_TXMAXPKTSZ(mep->maxp);
> > +		csr0 |= TX_DMAREQEN;
> > +
> > +		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);
> > +
> > +		csr2 = TX_FIFOADDR(fifo_addr >> 4);
> > +		csr2 |= TX_FIFOSEGSIZE(fifo_sgsz);
> > +
> > +		switch (mep->type) {
> > +		case USB_ENDPOINT_XFER_BULK:
> > +			csr1 |= TX_TYPE(TYPE_BULK);
> > +			break;
> > +		case USB_ENDPOINT_XFER_ISOC:
> > +			csr1 |= TX_TYPE(TYPE_ISO);
> > +			csr2 |= TX_BINTERVAL(interval);
> > +			break;
> > +		case USB_ENDPOINT_XFER_INT:
> > +			csr1 |= TX_TYPE(TYPE_INT);
> > +			csr2 |= TX_BINTERVAL(interval);
> > +			break;
> 
> And if it is control?
The function is only called for non control EP.
I will add a comment for it.
> 
> > +		}
> > +
> > +		/* Enable QMU Done interrupt */
> > +		mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
> > +
> > +		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
> > +		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
> > +		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
> > +
> > +		dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> > +			epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
> > +	} else {
> > +		csr0 = RX_RXMAXPKTSZ(mep->maxp);
> > +		csr0 |= RX_DMAREQEN;
> > +
> > +		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);
> > +
> > +		csr2 = RX_FIFOADDR(fifo_addr >> 4);
> > +		csr2 |= RX_FIFOSEGSIZE(fifo_sgsz);
> > +
> > +		switch (mep->type) {
> > +		case USB_ENDPOINT_XFER_BULK:
> > +			csr1 |= RX_TYPE(TYPE_BULK);
> > +			break;
> > +		case USB_ENDPOINT_XFER_ISOC:
> > +			csr1 |= RX_TYPE(TYPE_ISO);
> > +			csr2 |= RX_BINTERVAL(interval);
> > +			break;
> > +		case USB_ENDPOINT_XFER_INT:
> > +			csr1 |= RX_TYPE(TYPE_INT);
> > +			csr2 |= RX_BINTERVAL(interval);
> > +			break;
> 
> Again: control endpoints?
Only for non-EP0.

> 
> > +		}
> > +
> > +		/*Enable QMU Done interrupt */
> > +		mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
> > +
> > +		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
> > +		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
> > +		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
> > +
> > +		dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> > +			epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
> > +			mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
> > +	}
> > +
> > +	dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
> > +	dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
> > +		__func__, mep->name, mep->fifo_addr, mep->fifo_size,
> > +		fifo_sgsz, mep->fifo_seg_size);
> > +
> > +	return 0;
> > +}
> > +
> 
> [..]
> > +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;
> 
> You are missing the checks for USB_SPEED_WIRELESS in general.
> Can you just ignore it?
Doesn't support USB_SPEED_WIRELESS, so ignore it.
> 
> > +
> > +	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);
> > +	}
> > +	dev_info(mtu->dev, "max_speed: %s\n",
> > +		usb_speed_string(mtu->max_speed));
> > +}
> 
> [..]
> > +static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	enum usb_device_speed udev_speed;
> > +	u32 maxpkt = 64;
> > +	u32 link;
> > +	u32 speed;
> > +
> > +	link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
> > +	link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
> > +	mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
> > +	dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
> > +
> > +	if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
> > +		return IRQ_NONE;
> 
> Shouldn't you do this check before you clear interrupts?
In fact, U3D_DEV_LINK_INTR has only one interrupt,
SSUSB_DEV_SPEED_CHG_INTR, others can be treated as spurious ones.

> 
> > +	speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
> 
> Do you really want to read this out of the hardware on every interrupt?
Yes, the interrupt is triggered only when speed is changed,
and get the new speed here.

> 
> > +
> > +	switch (speed) {
> > +	case MTU3_SPEED_FULL:
> > +		udev_speed = USB_SPEED_FULL;
> > +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> > +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> > +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> > +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> > +				LPM_BESL_STALL | LPM_BESLD_STALL);
> > +		break;
> > +	case MTU3_SPEED_HIGH:
> > +		udev_speed = USB_SPEED_HIGH;
> > +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> > +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> > +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> > +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> > +				LPM_BESL_STALL | LPM_BESLD_STALL);
> > +		break;
> > +	case MTU3_SPEED_SUPER:
> > +		udev_speed = USB_SPEED_SUPER;
> > +		maxpkt = 512;
> > +		break;
> > +	default:
> > +		udev_speed = USB_SPEED_UNKNOWN;
> > +		break;
> > +	}
> > +	dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
> > +
> > +	mtu->g.speed = udev_speed;
> > +	mtu->g.ep0->maxpacket = maxpkt;
> > +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> > +
> > +	if (udev_speed == USB_SPEED_UNKNOWN)
> > +		mtu3_gadget_disconnect(mtu);
> > +	else
> > +		mtu3_ep0_setup(mtu);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
> > +{
> > +	void __iomem *mbase = mtu->mac_base;
> > +	u32 ltssm;
> > +
> > +	ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
> > +	ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
> > +	mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
> > +	dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
> > +
> > +	if (ltssm & HOT_RST_INTR)
> > +		mtu3_gadget_reset(mtu);
> > +
> > +	if (ltssm & WARM_RST_INTR)
> > +		mtu3_gadget_reset(mtu);
> 
> You really would reset the gadget twice if that happens?
> And do the rest of the tests make sense in that case?
I will merge them into one as following:

   if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
        ...

> 
> > +
> > +	if (ltssm & VBUS_FALL_INTR)
> > +		mtu3_ss_func_set(mtu, false);
> > +
> > +	if (ltssm & VBUS_RISE_INTR)
> > +		mtu3_ss_func_set(mtu, true);
> > +
> > +	if (ltssm & EXIT_U3_INTR)
> > +		mtu3_gadget_resume(mtu);
> > +
> > +	if (ltssm & ENTER_U3_INTR)
> > +		mtu3_gadget_suspend(mtu);
> > +
> > +	return IRQ_HANDLED;
> > +}
> 
> [..]
> 
> > +static int mtu3_hw_init(struct mtu3 *mtu)
> > +{
> > +	int ret;
> > +
> > +	mtu3_device_reset(mtu);
> > +
> > +	ret = mtu3_device_enable(mtu);
> > +	if (ret) {
> > +		dev_err(mtu->dev, "device enable failed %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = mtu3_mem_alloc(mtu);
> > +	if (ret) {
> > +		dev_err(mtu->dev, "mem alloc failed, aborting\n");
> 
> 1. You can't leave the device enabled in that case.
It enable device mac and port, otherwise I can't initialize other
register.
and the function is not enable until udc_start() is called.

> 2. No need for a message. The kernel will already do that if kmalloc()
> fails under such circumstances.
remove it latter.
> 
> > +		return -ENOMEM;
> > +	}
> > +
> > +	mtu3_regs_init(mtu);
> > +
> > +	return 0;
> > +}
> 
> > diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
> > new file mode 100644
> > index 0000000..f560f93
> > --- /dev/null
> > +++ b/drivers/usb/mtu3/mtu3_dr.c
> > @@ -0,0 +1,375 @@
> > +/*
> > + * 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>
> > +#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 "mtu3.h"
> > +#include "mtu3_dr.h"
> > +
> > +#define USB2_PORT 2
> > +#define USB3_PORT 3
> > +
[...]
> > +
> > +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_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");
> 
> No proper error returns

add it later.

> > +
> > +	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 void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
> > +{
> > +	debugfs_remove_recursive(ssusb->dbgfs_root);
> > +}
> > +
> > +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)
> > +		ssusb_debugfs_init(ssusb);
> > +
> > +	/* It is enough to delay 1s for waiting for host initialization */
> > +	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
> 
> You need to handle this still pending when you are deregistered.
OK.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
> > +{
> > +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> > +
> > +	if (otg_sx->edev) {
> > +		extcon_unregister_notifier(otg_sx->edev,
> > +			EXTCON_USB, &otg_sx->vbus_nb);
> > +		extcon_unregister_notifier(otg_sx->edev,
> > +			EXTCON_USB_HOST, &otg_sx->id_nb);
> > +	}
> > +
> > +	if (otg_sx->manual_drd_enabled)
> > +		ssusb_debugfs_exit(ssusb);
> > +}
> 
> Could you break this up a bit? It is extensively long a patch?
I afraid I can't break the patch into small ones but also provide
complete functionality at the same time.
Sorry for inconvenience.
> 
Thank you very much :-)

> 	Regards
> 		Oliver
> 

  reply	other threads:[~2016-08-26  9:40 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-25  3:05 [RESEND PATCH V5, 0/5] Add MediaTek USB3 DRD Driver Chunfeng Yun
2016-08-25  3:05 ` Chunfeng Yun
2016-08-25  3:05 ` Chunfeng Yun
2016-08-25  3:05 ` [RESEND PATCH, v5 1/5] dt-bindings: mt8173-xhci: support host side of dual-role mode Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  3:05 ` [RESEND PATCH, v5 2/5] dt-bindings: mt8173-mtu3: add devicetree bindings Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  3:05 ` [RESEND PATCH, v5 3/5] usb: xhci-mtk: make IPPC register optional Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-29  8:01   ` Felipe Balbi
2016-08-29  8:01     ` Felipe Balbi
2016-08-29  8:01     ` Felipe Balbi
2016-08-25  3:05 ` [RESEND PATCH, v5 4/5] usb: Add MediaTek USB3 DRD Driver Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  8:32   ` Oliver Neukum
2016-08-25  8:32     ` Oliver Neukum
2016-08-25  8:32     ` Oliver Neukum
2016-08-26  9:38     ` chunfeng yun [this message]
2016-08-26  9:38       ` chunfeng yun
2016-08-26  9:38       ` chunfeng yun
2016-08-30 17:20       ` Greg Kroah-Hartman
2016-08-30 17:20         ` Greg Kroah-Hartman
2016-08-30 17:20         ` Greg Kroah-Hartman
2016-08-31 12:11         ` Chunfeng Yun
2016-08-31 12:11           ` Chunfeng Yun
2016-08-31 12:11           ` Chunfeng Yun
2016-08-25  3:05 ` [RESEND PATCH, v5 5/5] arm64: dts: mediatek: add USB3 DRD driver Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun
2016-08-25  3:05   ` Chunfeng Yun

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1472204307.27677.41.camel@mhfsdcap03 \
    --to=chunfeng.yun@mediatek.com \
    --cc=alcooperx@gmail.com \
    --cc=devicetree@vger.kernel.org \
    --cc=felipe.balbi@linux.intel.com \
    --cc=galak@codeaurora.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=ijc+devicetree@hellion.org.uk \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mathias.nyman@intel.com \
    --cc=matthias.bgg@gmail.com \
    --cc=oneukum@suse.com \
    --cc=pawel.moll@arm.com \
    --cc=robh+dt@kernel.org \
    --cc=s.hauer@pengutronix.de \
    --cc=sergei.shtylyov@cogentembedded.com \
    --cc=stern@rowland.harvard.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.