lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <BYAPR07MB47095110AF28CC11B4C9E144DDA60@BYAPR07MB4709.namprd07.prod.outlook.com>
Date:   Tue, 11 Dec 2018 19:04:15 +0000
From:   Pawel Laszczak <pawell@...ence.com>
To:     Felipe Balbi <balbi@...nel.org>,
        "devicetree@...r.kernel.org" <devicetree@...r.kernel.org>
CC:     "gregkh@...uxfoundation.org" <gregkh@...uxfoundation.org>,
        "linux-usb@...r.kernel.org" <linux-usb@...r.kernel.org>,
        "rogerq@...com" <rogerq@...com>,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
        Alan Douglas <adouglas@...ence.com>,
        "jbergsagel@...com" <jbergsagel@...com>,
        "nsekhar@...com" <nsekhar@...com>, "nm@...com" <nm@...com>,
        Suresh Punnoose <sureshp@...ence.com>,
        "peter.chen@....com" <peter.chen@....com>,
        Pawel Jez <pjez@...ence.com>, Rahul Kumar <kurahul@...ence.com>
Subject: RE: [PATCH v1 2/2] usb:cdns3 Add Cadence USB3 DRD Driver

Hi, 
>
>Pawel Laszczak <pawell@...ence.com> writes:
>> +static int cdns3_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct resource	*res;
>> +	struct cdns3 *cdns;
>> +	void __iomem *regs;
>> +	int ret;
>> +
>> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
>> +	if (!cdns)
>> +		return -ENOMEM;
>> +
>> +	cdns->dev = dev;
>> +
>> +	platform_set_drvdata(pdev, cdns);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +	if (!res) {
>> +		dev_err(dev, "missing IRQ\n");
>> +		return -ENODEV;
>> +	}
>> +	cdns->irq = res->start;
>> +
>> +	cdns->xhci_res[0] = *res;
>> +
>> +	/*
>> +	 * Request memory region
>> +	 * region-0: xHCI
>> +	 * region-1: Peripheral
>> +	 * region-2: OTG registers
>> +	 */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	cdns->xhci_res[1] = *res;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +	regs = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +	cdns->dev_regs	= regs;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> +	regs = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +	cdns->otg_regs = regs;
>> +
>> +	mutex_init(&cdns->mutex);
>> +
>> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
>> +	if (IS_ERR(cdns->phy)) {
>> +		ret = PTR_ERR(cdns->phy);
>> +		if (ret == -ENOSYS || ret == -ENODEV) {
>
>Are you sure you can get ENOSYS here? Have you checked output of
>checkpatch --strict?
Yes this error code can be returned by related to phy function if CONFIG_GENERIC_PHY is disabled. 

I have seen this warning in output of checkpatch --strict .
>
>-:852: WARNING: ENOSYS means 'invalid syscall nr' and nothing else
>
>> +#ifdef CONFIG_PM_SLEEP
>> +static int cdns3_suspend(struct device *dev)
>> +{
>> +	/* TODO: Implements this function. */
>> +	return 0;
>> +}
>> +
>> +static int cdns3_resume(struct device *dev)
>> +{
>> +	/* TODO: Implements this function. */
>> +	return 0;
>> +}
>> +#endif /* CONFIG_PM_SLEEP */
>> +static int cdns3_runtime_suspend(struct device *dev)
>> +{	/* TODO: Implements this function. */
>> +	return 0;
>> +}
>> +
>> +static int cdns3_runtime_resume(struct device *dev)
>> +{
>> +	/* TODO: Implements this function. */
>> +	return 0;
>> +}
>> +#endif /* CONFIG_PM */
>
>please no TODO stubs. Just get rid of your dev_pm_ops if you don't
>implement them. Come up with a later patch adding a proper
>implementation for PM.

Ok. 

>
>> +static int __init cdns3_driver_platform_register(void)
>> +{
>> +	return platform_driver_register(&cdns3_driver);
>> +}
>> +module_init(cdns3_driver_platform_register);
>> +
>> +static void __exit cdns3_driver_platform_unregister(void)
>> +{
>> +	platform_driver_unregister(&cdns3_driver);
>> +}
>> +module_exit(cdns3_driver_platform_unregister);
>
>module_platform_driver()

Ok,

>
>> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
>> new file mode 100644
>> index 000000000000..afb81d224718
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debug.h
>> @@ -0,0 +1,346 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + * Debug header file.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@...ence.com>
>> + */
>> +#ifndef __LINUX_CDNS3_DEBUG
>> +#define __LINUX_CDNS3_DEBUG
>> +#include "gadget.h"
>> +
>> +static inline void cdns3_decode_get_status(u8 bRequestType, u16 wIndex,
>> +					   u16 wLength, char *str)
>> +{
>> +	switch (bRequestType & USB_RECIP_MASK) {
>> +	case USB_RECIP_INTERFACE:
>> +		sprintf(str, "Get Interface Status Intf = %d, L: = %d",
>> +			wIndex, wLength);
>> +		break;
>> +	case USB_RECIP_ENDPOINT:
>> +		sprintf(str, "Get Endpoint Status ep%d%s",
>> +			wIndex & ~USB_DIR_IN,
>> +			wIndex & USB_DIR_IN ? "in" : "out");
>> +		break;
>> +	}
>> +}
>> +
>> +static inline const char *cdns3_decode_device_feature(u16 wValue)
>> +{
>> +	switch (wValue) {
>> +	case USB_DEVICE_SELF_POWERED:
>> +		return "Self Powered";
>> +	case USB_DEVICE_REMOTE_WAKEUP:
>> +		return "Remote Wakeup";
>> +	case USB_DEVICE_TEST_MODE:
>> +		return "Test Mode";
>> +	case USB_DEVICE_U1_ENABLE:
>> +		return "U1 Enable";
>> +	case USB_DEVICE_U2_ENABLE:
>> +		return "U2 Enable";
>> +	case USB_DEVICE_LTM_ENABLE:
>> +		return "LTM Enable";
>> +	default:
>> +		return "UNKNOWN";
>> +	}
>> +}
>> +
>> +static inline const char *cdns3_decode_test_mode(u16 wIndex)
>> +{
>> +	switch (wIndex) {
>> +	case TEST_J:
>> +		return ": TEST_J";
>> +	case TEST_K:
>> +		return ": TEST_K";
>> +	case TEST_SE0_NAK:
>> +		return ": TEST_SE0_NAK";
>> +	case TEST_PACKET:
>> +		return ": TEST_PACKET";
>> +	case TEST_FORCE_EN:
>> +		return ": TEST_FORCE_EN";
>> +	default:
>> +		return ": UNKNOWN";
>> +	}
>> +}
>> +
>> +static inline void cdns3_decode_set_clear_feature(u8 bRequestType, u8 bRequest,
>> +						  u16 wValue, u16 wIndex,
>> +						  char *str)
>> +{
>> +	switch (bRequestType & USB_RECIP_MASK) {
>> +	case USB_RECIP_DEVICE:
>> +		sprintf(str, "%s Device Feature(%s%s)",
>> +			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
>> +			cdns3_decode_device_feature(wValue),
>> +			wValue == USB_DEVICE_TEST_MODE ?
>> +			cdns3_decode_test_mode(wIndex) : "");
>> +		break;
>> +	case USB_RECIP_INTERFACE:
>> +		sprintf(str, "%s Interface Feature(%s)",
>> +			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
>> +			wIndex == USB_INTRF_FUNC_SUSPEND ?
>> +			"Function Suspend" : "UNKNOWN");
>> +		break;
>> +	case USB_RECIP_ENDPOINT:
>> +		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
>> +			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
>> +			    wIndex == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
>> +			wIndex & ~USB_DIR_IN,
>> +			wIndex & USB_DIR_IN ? "in" : "out");
>> +		break;
>> +	}
>> +}
>> +
>> +static inline const char *cdns3_decode_descriptor(u16 wValue)
>> +{
>> +	switch (wValue >> 8) {
>> +	case USB_DT_DEVICE:
>> +		return "Device";
>> +	case USB_DT_CONFIG:
>> +		return "Configuration";
>> +	case USB_DT_STRING:
>> +		return "String";
>> +	case USB_DT_INTERFACE:
>> +		return "Interface";
>> +	case USB_DT_ENDPOINT:
>> +		return "Endpoint";
>> +	case USB_DT_DEVICE_QUALIFIER:
>> +		return "Device Qualifier";
>> +	case USB_DT_OTHER_SPEED_CONFIG:
>> +		return "Other Speed Config";
>> +	case USB_DT_INTERFACE_POWER:
>> +		return "Interface Power";
>> +	case USB_DT_OTG:
>> +		return "OTG";
>> +	case USB_DT_DEBUG:
>> +		return "Debug";
>> +	case USB_DT_INTERFACE_ASSOCIATION:
>> +		return "Interface Association";
>> +	case USB_DT_BOS:
>> +		return "BOS";
>> +	case USB_DT_DEVICE_CAPABILITY:
>> +		return "Device Capability";
>> +	case USB_DT_SS_ENDPOINT_COMP:
>> +		return "SS Endpoint Companion";
>> +	case USB_DT_SSP_ISOC_ENDPOINT_COMP:
>> +		return "SSP Isochronous Endpoint Companion";
>> +	default:
>> +		return "UNKNOWN";
>> +	}
>> +}
>> +
>> +/**
>> + * cdns3_decode_ctrl - returns a string represetion of ctrl request
>> + */
>> +static inline const char *cdns3_decode_ctrl(char *str, u8 bRequestType,
>> +					    u8 bRequest, u16 wValue,
>> +					    u16 wIndex, u16 wLength)
>> +{
>> +	switch (bRequest) {
>> +	case USB_REQ_GET_STATUS:
>> +		cdns3_decode_get_status(bRequestType, wIndex,
>> +					wLength, str);
>> +		break;
>> +	case USB_REQ_CLEAR_FEATURE:
>> +	case USB_REQ_SET_FEATURE:
>> +		cdns3_decode_set_clear_feature(bRequestType, bRequest,
>> +					       wValue, wIndex, str);
>> +		break;
>> +	case USB_REQ_SET_ADDRESS:
>> +		sprintf(str, "Set Address Addr: %02x", wValue);
>> +		break;
>> +	case USB_REQ_GET_DESCRIPTOR:
>> +		sprintf(str, "GET %s Descriptor I: %d, L: %d",
>> +			cdns3_decode_descriptor(wValue),
>> +			wValue & 0xff, wLength);
>> +		break;
>> +	case USB_REQ_SET_DESCRIPTOR:
>> +		sprintf(str, "SET %s Descriptor I: %d, L: %d",
>> +			cdns3_decode_descriptor(wValue),
>> +			wValue & 0xff, wLength);
>> +		break;
>> +	case USB_REQ_GET_CONFIGURATION:
>> +		sprintf(str, "Get Configuration L: %d", wLength);
>> +		break;
>> +	case USB_REQ_SET_CONFIGURATION:
>> +		sprintf(str, "Set Configuration Config: %d ", wValue);
>> +		break;
>> +	case USB_REQ_GET_INTERFACE:
>> +		sprintf(str, "Get Interface Intf: %d, L: %d", wIndex, wLength);
>> +		break;
>> +	case USB_REQ_SET_INTERFACE:
>> +		sprintf(str, "Set Interface Intf: %d, Alt: %d", wIndex, wValue);
>> +		break;
>> +	case USB_REQ_SYNCH_FRAME:
>> +		sprintf(str, "Synch Frame Ep: %d, L: %d", wIndex, wLength);
>> +		break;
>> +	case USB_REQ_SET_SEL:
>> +		sprintf(str, "Set SEL L: %d", wLength);
>> +		break;
>> +	case USB_REQ_SET_ISOCH_DELAY:
>> +		sprintf(str, "Set Isochronous Delay Delay: %d ns", wValue);
>> +		break;
>> +	default:
>> +		sprintf(str,
>> +			"SETUP BRT: %02x BR: %02x V: %04x I: %04x L: %04x\n",
>> +			bRequestType, bRequest,
>> +			wValue, wIndex, wLength);
>> +	}
>> +
>> +	return str;
>> +}
>
>All of these are a flat out copy of dwc3's implementation. It's much,
>much better to turn dwc3's implementation into a generic, reusable
>library function then spinning your own as a duplicated effort.
I agree, 
It would be nice to have a set of decoding function  in some generic library. It could be used 
also by other drivers.
Who should do this ?

>
>> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
>> new file mode 100644
>> index 000000000000..1ef0e9f73e3e
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/ep0.c
>> @@ -0,0 +1,864 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@...ence.com>,
>> + *          Pawel Laszczak <pawell@...ence.com>
>> + *	    Peter Chen <peter.chen@....com>
>> + */
>> +
>> +#include <linux/usb/composite.h>
>> +
>> +#include "gadget.h"
>> +#include "trace.h"
>> +
>> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
>> +	.bLength = USB_DT_ENDPOINT_SIZE,
>> +	.bDescriptorType = USB_DT_ENDPOINT,
>> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
>> +};
>> +
>> +/**
>> + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
>> + * @priv_dev: extended gadget object
>> + * @dma_addr: physical address where data is/will be stored
>> + * @length: data length
>> + * @erdy: set it to 1 when ERDY packet should be sent -
>> + *        exit from flow control state
>> + */
>> +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
>> +				   dma_addr_t dma_addr,
>> +				   unsigned int length, int erdy)
>> +{
>> +	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
>> +	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(priv_dev->gadget.ep0);
>> +
>> +	priv_dev->ep0_trb->buffer = TRB_BUFFER(dma_addr);
>> +	priv_dev->ep0_trb->length = TRB_LEN(length);
>> +	priv_dev->ep0_trb->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
>> +
>> +	trace_cdns3_prepare_trb(priv_ep, priv_dev->ep0_trb);
>> +
>> +	cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
>> +
>> +	writel(EP_STS_TRBERR, &regs->ep_sts);
>> +	writel(EP_TRADDR_TRADDR(priv_dev->ep0_trb_dma), &regs->ep_traddr);
>> +	trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out");
>> +
>> +	/* TRB should be prepared before starting transfer */
>> +	writel(EP_CMD_DRDY, &regs->ep_cmd);
>> +
>> +	if (erdy)
>> +		writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
>> +}
>> +
>> +/**
>> + * cdns3_ep0_delegate_req - Returns status of handling setup packet
>> + * Setup is handled by gadget driver
>> + * @priv_dev: extended gadget object
>> + * @ctrl_req: pointer to received setup packet
>> + *
>> + * Returns zero on success or negative value on failure
>> + */
>> +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
>> +				  struct usb_ctrlrequest *ctrl_req)
>> +{
>> +	int ret;
>> +
>> +	spin_unlock(&priv_dev->lock);
>> +	priv_dev->setup_pending = 1;
>> +	ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
>> +	priv_dev->setup_pending = 0;
>> +	spin_lock(&priv_dev->lock);
>> +	return ret;
>> +}
>> +
>> +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
>> +{
>> +	priv_dev->ep0_data_dir = 0;
>> +	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
>> +			       sizeof(struct usb_ctrlrequest), 0);
>> +}
>> +
>> +/**
>> + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
>> + * @priv_dev: extended gadget object
>> + * @ctrl_req: pointer to received setup packet
>> + *
>> + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
>> + * error code on error
>> + */
>> +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
>> +					   struct usb_ctrlrequest *ctrl_req)
>> +{
>> +	enum usb_device_state device_state = priv_dev->gadget.state;
>> +	struct cdns3_endpoint *priv_ep;
>> +	u32 config = le16_to_cpu(ctrl_req->wValue);
>> +	int result = 0;
>> +	int i;
>> +
>> +	switch (device_state) {
>> +	case USB_STATE_ADDRESS:
>> +		/* Configure non-control EPs */
>> +		for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
>> +			priv_ep = priv_dev->eps[i];
>> +			if (!priv_ep)
>> +				continue;
>> +
>> +			if (priv_ep->flags & EP_CLAIMED)
>> +				cdns3_ep_config(priv_ep);
>> +		}
>> +
>> +		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
>> +
>> +		if (result)
>> +			return result;
>> +
>> +		if (config) {
>> +			cdns3_set_hw_configuration(priv_dev);
>> +		} else {
>> +			cdns3_gadget_unconfig(priv_dev);
>> +			usb_gadget_set_state(&priv_dev->gadget,
>> +					     USB_STATE_ADDRESS);
>
>this is wrong. If address is zero, state should be default, not
>addressed. Addressed would be used on the other branch here, when you
>have a non-zero address

If address is zero then state should be USB_STATE_DEFAULT. Driver enters to this branch 
only if gadget.state  = USB_STATE_ADDRESS (address != 0). This state can be changed in composite.c file 
after delegating request to gadget driver. Because this state could be changed driver simple
restore USB_STATE_ADDRESS if config = 0. 

>
>> +		}
>> +		break;
>> +	case USB_STATE_CONFIGURED:
>
>where do you set this state?
It's set in set_config in composite.c file. 
>
>> +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
>> +					   struct usb_ctrlrequest *ctrl,
>> +					   int set)
>> +{
>> +	enum usb_device_state state;
>> +	enum usb_device_speed speed;
>> +	int ret = 0;
>> +	u32 wValue;
>> +	u32 wIndex;
>> +	u16 tmode;
>> +
>> +	wValue = le16_to_cpu(ctrl->wValue);
>> +	wIndex = le16_to_cpu(ctrl->wIndex);
>> +	state = priv_dev->gadget.state;
>> +	speed = priv_dev->gadget.speed;
>> +
>> +	switch (ctrl->wValue) {
>> +	case USB_DEVICE_REMOTE_WAKEUP:
>> +		priv_dev->wake_up_flag = !!set;
>> +		break;
>> +	case USB_DEVICE_U1_ENABLE:
>> +		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
>> +			return -EINVAL;
>> +
>> +		priv_dev->u1_allowed = !!set;
>> +		break;
>> +	case USB_DEVICE_U2_ENABLE:
>> +		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
>> +			return -EINVAL;
>> +
>> +		priv_dev->u2_allowed = !!set;
>> +		break;
>> +	case USB_DEVICE_LTM_ENABLE:
>> +		ret = -EINVAL;
>> +		break;
>> +	case USB_DEVICE_TEST_MODE:
>> +		if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
>> +			return -EINVAL;
>> +
>> +		tmode = le16_to_cpu(ctrl->wIndex);
>> +
>> +		if (!set || (tmode & 0xff) != 0)
>> +			return -EINVAL;
>> +
>> +		switch (tmode >> 8) {
>> +		case TEST_J:
>> +		case TEST_K:
>> +		case TEST_SE0_NAK:
>> +		case TEST_PACKET:
>> +			cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
>> +					       USB_CMD_STMODE |
>> +					       USB_STS_TMODE_SEL(tmode - 1));
>
>I'm 90% sure this won't work. There's a reason why we only enter the
>requested test mode from status stage. How have you tested this?

>From USB spec:
"The transition to test mode must be complete no later than 3 ms after the completion of the status stage of the
request."
But I don't remember any issues with this test on other ours drivers. Maybe status stage 
is armed in this case by controller. I have to confirm how it works with hardware team. 
Driver doesn't know when status stage was completed. We don't have 
any event on status stage completion.  I haven't checked it yet with tester on this driver.  

>
>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>> new file mode 100644
>> index 000000000000..a021eaf07aee
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/gadget.c
>
><snip>
>
>> +struct usb_request *cdns3_next_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>> +	return list_first_entry(list, struct usb_request, list);
>
>list_first_entry_or_null()

Ok.
>
>> +void cdns3_gadget_unconfig(struct cdns3_device *priv_dev)
>> +{
>> +	/* RESET CONFIGURATION */
>> +	writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
>> +
>> +	cdns3_allow_enable_l1(priv_dev, 0);
>> +	priv_dev->hw_configured_flag = 0;
>> +	priv_dev->onchip_mem_allocated_size = 0;
>
>clear all test modes? Reset ep0 max_packet_size to 512?
Test mode should be reset automatically on exit. 
W can't clear this mode in register. It's WAC (write only and auto clear)  bit.
This function only reset endpoint configuration in controller register. 
Ep0 max_packet_size should be unchanged here.  Ep0 max_packet it's updated on 
Connect/Disconnect/Reset events.  

Maybe this function should be called cdns3_hw_reset_eps_config. 
It doesn't unconfigure whole gadget but only reset endpoints configuration kept by 
controller.
I will change this function. 

>
>> +static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
>> +{
>> +	struct cdns3_device *priv_dev;
>> +	struct cdns3 *cdns = data;
>> +	irqreturn_t ret = IRQ_NONE;
>> +	unsigned long flags;
>> +	u32 reg;
>> +
>> +	priv_dev = cdns->gadget_dev;
>> +	spin_lock_irqsave(&priv_dev->lock, flags);
>
>you're already running in hardirq context. Why do you need this lock at
>all? I would be better to use the hardirq handler to mask your
>interrupts, so they don't fire again, then used the top-half (softirq)
>handler to actually handle the interrupts.

Yes, spin_lock_irqsave is not necessary here. 

Do you mean replacing devm_request_irq with a request_threaded_irq ?
I have single interrupt line shared between  Host, Driver, DRD/OTG. 
I'm not sure if it will work more efficiently. 

>
>> +	/* check USB device interrupt */
>> +	reg = readl(&priv_dev->regs->usb_ists);
>> +	writel(reg, &priv_dev->regs->usb_ists);
>> +
>> +	if (reg) {
>> +		dev_dbg(priv_dev->dev, "IRQ: usb_ists: %08X\n", reg);
>
>I strongly advise against using dev_dbg() for debugging. Even more so
>inside your IRQ handler.
Ok, It's not necessary in this place, especially, that there is invoked trace point 
inside cdns3_check_usb_interrupt_proceed which makes the same.

>
>> +		cdns3_check_usb_interrupt_proceed(priv_dev, reg);
>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	/* check endpoint interrupt */
>> +	reg = readl(&priv_dev->regs->ep_ists);
>> +
>> +	/* handle default endpoint OUT */
>> +	if (reg & EP_ISTS_EP_OUT0) {
>> +		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	/* handle default endpoint IN */
>> +	if (reg & EP_ISTS_EP_IN0) {
>> +		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	/* check if interrupt from non default endpoint, if no exit */
>> +	reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
>> +	if (!reg)
>> +		goto irqend;
>> +
>> +	do {
>> +		unsigned int bit_pos = ffs(reg);
>> +		u32 bit_mask = 1 << (bit_pos - 1);
>> +		int index;
>> +
>> +		index = cdns3_ep_reg_pos_to_index(bit_pos);
>> +		cdns3_check_ep_interrupt_proceed(priv_dev->eps[index]);
>> +		reg &= ~bit_mask;
>> +		ret = IRQ_HANDLED;
>> +	} while (reg);
>
>use for_each_set_bit() here.
Ok

>
>> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
>> +{
>> +	bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
>> +	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
>> +	u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
>> +	u32 interrupt_mask = EP_STS_EN_TRBERREN;
>> +	u32 max_packet_size = 0;
>> +	u32 ep_cfg = 0;
>> +	int ret;
>> +
>> +	if (!priv_ep->dir)
>> +		interrupt_mask |= EP_STS_EN_DESCMISEN;
>> +
>> +	if (priv_ep->type == USB_ENDPOINT_XFER_INT) {
>
>you can turn tis into a switch statement. It'll look nicer
Ok.
>
>> +		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
>> +	} else if (priv_ep->type == USB_ENDPOINT_XFER_BULK) {
>> +		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
>> +	} else {
>> +		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
>> +		interrupt_mask = 0xFFFFFFFF;
>> +	}
>> +
>> +	switch (priv_dev->gadget.speed) {
>> +	case USB_SPEED_FULL:
>> +		max_packet_size = is_iso_ep ? 1023 : 64;
>> +		break;
>> +	case USB_SPEED_HIGH:
>> +		max_packet_size = is_iso_ep ? 1024 : 512;
>> +		break;
>> +	case USB_SPEED_SUPER:
>> +		max_packet_size = 1024;
>> +		break;
>> +	default:
>> +		/* all other speed are not supported */
>> +		return;
>> +	}
>> +
>> +	ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
>> +	if (ret) {
>> +		dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
>> +		return;
>> +	}
>> +
>> +	ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
>> +		  EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
>> +		  EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
>> +
>> +	cdns3_select_ep(priv_dev, bEndpointAddress);
>> +
>> +	writel(ep_cfg, &priv_dev->regs->ep_cfg);
>> +	writel(interrupt_mask, &priv_dev->regs->ep_sts_en);
>> +
>> +	dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
>> +		priv_ep->name, ep_cfg);
>> +
>> +	/* enable interrupt for selected endpoint */
>> +	cdns3_set_register_bit(&priv_dev->regs->ep_ien,
>> +			       cdns3_ep_addr_to_bit_pos(bEndpointAddress));
>> +}
>
>--
Thanks 
Pawell

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ