RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver

From: Pawel Laszczak
Date: Thu Dec 27 2018 - 08:12:21 EST


HI,

>>
>> The host side of USBSS-DRD controller is compliance
>> with XHCI specification, so it works with
>> standard XHCI linux driver.
>>
>
>After adding my glue layer change (with my phy part) and make one
>change for this code,
>the xHCI can work at my platform. I list the comments from today's
>review and debug.
>The comments are at Makefile and drd.c.
>
>> + If you choose to build this driver as module it will
>> + be dynamically linked and module will be called cdns3-pci.ko
>> +
>> +endif
>> diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
>> new file mode 100644
>> index 000000000000..3f63baa24294
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/Makefile
>> @@ -0,0 +1,16 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +# define_trace.h needs to know how to find our header
>> +CFLAGS_trace.o := -I$(src)
>> +
>> +obj-$(CONFIG_USB_CDNS3) += cdns3.o
>> +obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci.o
>> +
>> +cdns3-y := core.o drd.o trace.o
>> +
>> +ifneq ($(CONFIG_DEBUG_FS),)
>> + cdns3-y += debugfs.o
>> +endif
>> +
>> +cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
>> +cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
>> +cdns3-pci-y := cdns3-pci-wrap.o
>
>Why do you want add two lines for pci_wrap? I change this Makefile like below:

I don't need these two lines. I will change it as you suggested.

>
># SPDX-License-Identifier: GPL-2.0
># define_trace.h needs to know how to find our header
>CFLAGS_trace.o := -I$(src)
>
>cdns3-y := core.o drd.o trace.o
>obj-$(CONFIG_USB_CDNS3) += cdns3.o
>ifneq ($(CONFIG_DEBUG_FS),)
> cdns3-y += debugfs.o
>endif
>
>cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
>cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
>obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
>obj-$(CONFIG_USB_CDNS3_IMX_WRAP) += cdns3-imx.o
>
>and below is the diff:
>
>diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
>index 3f63baa24294..d1bca2829f57 100644
>--- a/drivers/usb/cdns3/Makefile
>+++ b/drivers/usb/cdns3/Makefile
>@@ -2,15 +2,13 @@
> # define_trace.h needs to know how to find our header
> CFLAGS_trace.o := -I$(src)
>
>-obj-$(CONFIG_USB_CDNS3) += cdns3.o
>-obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci.o
>-
> cdns3-y := core.o drd.o trace.o
>-
>+obj-$(CONFIG_USB_CDNS3) += cdns3.o
> ifneq ($(CONFIG_DEBUG_FS),)
> cdns3-y += debugfs.o
> endif
>
> cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
> cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
>-cdns3-pci-y := cdns3-pci-wrap.o
>+obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
>+obj-$(CONFIG_USB_CDNS3_IMX_WRAP) += cdns3-imx.o
>
>
>> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
>> new file mode 100644
>> index 000000000000..b0c32302eb0b
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/drd.c
>> @@ -0,0 +1,350 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@xxxxxxxxxxx
>> + *
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include "gadget.h"
>> +#include "drd.h"
>> +#include "core.h"
>> +
>> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
>> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
>> +
>> +/**
>> + * cdns3_set_mode - change mode of OTG Core
>> + * @cdns: pointer to context structure
>> + * @mode: selected mode from cdns_role
>> + */
>> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
>> +{
>> + u32 reg;
>> +
>> + cdns->current_dr_mode = mode;
>> +
>> + switch (mode) {
>> + case USB_DR_MODE_PERIPHERAL:
>> + dev_info(cdns->dev, "Set controller to Gadget mode\n");
>> + cdns3_drd_switch_gadget(cdns, 1);
>> + break;
>> + case USB_DR_MODE_HOST:
>> + dev_info(cdns->dev, "Set controller to Host mode\n");
>> + cdns3_drd_switch_host(cdns, 1);
>> + break;
>> + case USB_DR_MODE_OTG:
>> + dev_info(cdns->dev, "Set controller to OTG mode\n");
>> + if (cdns->version == CDNS3_CONTROLLER_V1) {
>> + reg = readl(&cdns->otg_v1_regs->override);
>> + reg |= OVERRIDE_IDPULLUP;
>> + writel(reg, &cdns->otg_v1_regs->override);
>> + } else {
>> + reg = readl(&cdns->otg_v0_regs->ctrl1);
>> + reg |= OVERRIDE_IDPULLUP_V0;
>> + writel(reg, &cdns->otg_v0_regs->ctrl1);
>> + }
>> +
>> + /*
>> + * Hardware specification says: "ID_VALUE must be valid within
>> + * 50ms after idpullup is set to '1" so driver must wait
>> + * 50ms before reading this pin.
>> + */
>> + usleep_range(50000, 60000);
>> + break;
>> + default:
>> + cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> + dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
>> + return;
>> + }
>> +}
>
>I suggest adding return value since switch may fail.
>
>
>> +/**
>> + * cdns3_init_otg_mode - initialize drd controller
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
>
>You may add return value for this function too like your comment.
Ok, I will do it.
>
>> +{
>> + cdns3_otg_disable_irq(cdns);
>> + /* clear all interrupts */
>> + writel(~0, &cdns->otg_regs->ivect);
>> +
>> + cdns3_set_mode(cdns, USB_DR_MODE_OTG);
>
>return value
>> +
>> + if (cdns3_is_host(cdns))
>> + cdns3_drd_switch_host(cdns, 1);
>
>ditto
>
>> + else
>> + cdns3_drd_switch_gadget(cdns, 1);
>
>ditto
>> +
>> + cdns3_otg_enable_irq(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_drd_update_mode - initialize mode of operation
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +int cdns3_drd_update_mode(struct cdns3 *cdns)
>> +{
>> + int ret = 0;
>> +
>> + if (cdns->desired_dr_mode == cdns->current_dr_mode)
>> + return ret;
>> +
>> + cdns3_drd_switch_gadget(cdns, 0);
>
>return value
>
>> + cdns3_drd_switch_host(cdns, 0);
In these two line we don't need check return value.
For disabling mode these functions always return 0.

>ditto
>> +
>> + switch (cdns->desired_dr_mode) {
>> + case USB_DR_MODE_PERIPHERAL:
>> + cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
>
>ditto
>
>> + break;
>> + case USB_DR_MODE_HOST:
>> + cdns3_set_mode(cdns, USB_DR_MODE_HOST);
>
>ditto
>> + break;
>> + case USB_DR_MODE_OTG:
>> + cdns3_init_otg_mode(cdns);
>
>ditto
>> + break;
>> + default:
>> + dev_err(cdns->dev, "Unsupported mode of operation %d\n",
>> + cdns->dr_mode);
>> + return -EINVAL;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +/**
>> + * cdns3_drd_irq - interrupt handler for OTG events
>> + *
>> + * @irq: irq number for cdns3 core device
>> + * @data: structure of cdns3
>> + *
>> + * Returns IRQ_HANDLED or IRQ_NONE
>> + */
>> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
>> +{
>> + irqreturn_t ret = IRQ_NONE;
>> + struct cdns3 *cdns = data;
>> + u32 reg;
>> +
>> + if (cdns->dr_mode != USB_DR_MODE_OTG)
>> + return ret;
>> +
>> + reg = readl(&cdns->otg_regs->ivect);
>> +
>> + if (!reg)
>> + return ret;
>> +
>> + if (reg & OTGIEN_ID_CHANGE_INT) {
>> + dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
>> + cdns3_get_id(cdns));
>> +
>> + queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> + writel(~0, &cdns->otg_regs->ivect);
>> + return ret;
>> +}
>> +
>> +int cdns3_drd_init(struct cdns3 *cdns)
>> +{
>> + void __iomem *regs;
>> + int ret = 0;
>> + u32 state;
>> +
>> + regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
>> + if (IS_ERR(regs))
>> + return PTR_ERR(regs);
>> +
>> + /* Detection of DRD version. Controller has been released
>> + * in two versions. Both are similar, but they have same changes
>> + * in register maps.
>> + * The first register in old version is command register and it's read
>> + * only, so driver should read 0 from it. On the other hand, in v1
>> + * the first register contains device ID number which is not set to 0.
>> + * Driver uses this fact to detect the proper version of
>> + * controller.
>> + */
>> + cdns->otg_v0_regs = regs;
>> + if (!readl(&cdns->otg_v0_regs->cmd)) {
>> + cdns->version = CDNS3_CONTROLLER_V0;
>> + cdns->otg_v1_regs = NULL;
>> + cdns->otg_regs = regs;
>> + dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> + readl(&cdns->otg_v0_regs->version));
>> + } else {
>> + cdns->otg_v0_regs = NULL;
>> + cdns->otg_v1_regs = regs;
>> + cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
>> + cdns->version = CDNS3_CONTROLLER_V1;
>> + dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
>> + readl(&cdns->otg_v1_regs->did),
>> + readl(&cdns->otg_v1_regs->rid));
>> + }
>> +
>> + state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
>> +
>> + /* Update dr_mode according to STRAP configuration. */
>> + cdns->dr_mode = USB_DR_MODE_OTG;
>> + if (state == OTGSTS_STRAP_HOST) {
>> + dev_info(cdns->dev, "Controller strapped to HOST\n");
>> + cdns->dr_mode = USB_DR_MODE_HOST;
>> + } else if (state == OTGSTS_STRAP_GADGET) {
>> + dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
>> + cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
>> + }
>> +
>> + cdns->desired_dr_mode = cdns->dr_mode;
>> + cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +
>> + ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
>> + NULL, IRQF_SHARED,
>> + dev_name(cdns->dev), cdns);
>> +
>> + if (ret)
>> + return ret;
>> +
>> + state = readl(&cdns->otg_regs->sts);
>> + if (OTGSTS_OTG_NRDY(state) != 0) {
>> + dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> + return -ENODEV;
>> + }
>> +
>> + ret = cdns3_drd_update_mode(cdns);
>> +
>
>Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
>do you know possible reasons? After commenting out this function, my
>xHCI function
>works.
>
The prorblem is in:
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);

After disabling current mode driver must wait 1s before turning on new mode.

For simulation this time can be shortened to 2-3 ms by mans of OTGSIMULATE register.

Cheers,
Pawel