diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 72eb3e41e3b6..5e810e2ae7df 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -125,6 +125,8 @@ source "drivers/usb/chipidea/Kconfig" source "drivers/usb/isp1760/Kconfig" +source "drivers/usb/cdns3/Kconfig" + comment "USB port drivers" if USB diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 060643a1b5c8..4a96f18a616f 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_SUPPORT) += phy/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_ISP1760) += isp1760/ +obj-$(CONFIG_USB_CDNS3) += cdns3/ obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_USB_MTU3) += mtu3/ diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig new file mode 100644 index 000000000000..165afdb99d15 --- /dev/null +++ b/drivers/usb/cdns3/Kconfig @@ -0,0 +1,26 @@ +config USB_CDNS3 + tristate "Cadence USB3 Dual-Role Controller" + depends on ((USB_XHCI_HCD && USB_GADGET) || (USB_XHCI_HCD && !USB_GADGET) || (!USB_XHCI_HCD && USB_GADGET)) && HAS_DMA + select EXTCON + help + Say Y here if your system has a cadence USB3 dual-role controller. + It supports: dual-role switch Host-only, and Peripheral-only. + + When compiled dynamically, the module will be called cdns3.ko. + +if USB_CDNS3 + +config USB_CDNS3_GADGET + bool "Cadence USB3 device controller" + depends on USB_GADGET + help + Say Y here to enable device controller functionality of the + cadence usb3 driver. + +config USB_CDNS3_HOST + bool "Cadence USB3 host controller" + depends on USB_XHCI_HCD + help + Say Y here to enable host controller functionality of the + cadence usb3 driver. +endif diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile new file mode 100644 index 000000000000..7328cb9fcc89 --- /dev/null +++ b/drivers/usb/cdns3/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_USB_CDNS3) += cdns3.o + +cdns3-y := core.o +cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o +cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o diff --git a/drivers/usb/cdns3/cdns3-nxp-reg-def.h b/drivers/usb/cdns3/cdns3-nxp-reg-def.h new file mode 100644 index 000000000000..2a07e8f2784c --- /dev/null +++ b/drivers/usb/cdns3/cdns3-nxp-reg-def.h @@ -0,0 +1,132 @@ +/** + * cdns3-nxp-reg-def.h - nxp wrap layer register definition + * + * Copyright 2017 NXP + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DRIVERS_USB_CDNS3_NXP_H +#define __DRIVERS_USB_CDNS3_NXP_H + +#define USB3_CORE_CTRL1 0x00 +#define USB3_CORE_CTRL2 0x04 +#define USB3_INT_REG 0x08 +#define USB3_CORE_STATUS 0x0c +#define XHCI_DEBUG_LINK_ST 0x10 +#define XHCI_DEBUG_BUS 0x14 +#define USB3_SSPHY_CTRL1 0x40 +#define USB3_SSPHY_CTRL2 0x44 +#define USB3_SSPHY_STATUS 0x4c +#define USB2_PHY_CTRL1 0x50 +#define USB2_PHY_CTRL2 0x54 +#define USB2_PHY_STATUS 0x5c + +/* Register bits definition */ + +/* USB3_CORE_CTRL1 */ +#define PWR_SW_RESET (1 << 31) +#define APB_SW_RESET (1 << 30) +#define AXI_SW_RESET (1 << 29) +#define RW_SW_RESET (1 << 28) +#define PHY_SW_RESET (1 << 27) +#define PHYAHB_SW_RESET (1 << 26) +#define ALL_SW_RESET (PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \ + RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET) +#define OC_DISABLE (1 << 9) +#define MODE_STRAP_MASK (0x7) +#define DEV_MODE (1 << 2) +#define HOST_MODE (1 << 1) +#define OTG_MODE (1 << 0) + +/* USB3_INT_REG */ +#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ +#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ + +/* USB3_CORE_STATUS */ +#define DEV_POWER_ON_READY (1 << 13) +#define HOST_POWER_ON_READY (1 << 12) + + +/* PHY register definition */ +#define PHY_PMA_CMN_CTRL1 (0xC800 * 4) +#define TB_ADDR_CMN_DIAG_HSCLK_SEL (0x01e0 * 4) +#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR (0x0084 * 4) +#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR (0x0085 * 4) +#define TB_ADDR_CMN_PLL0_INTDIV (0x0094 * 4) +#define TB_ADDR_CMN_PLL0_FRACDIV (0x0095 * 4) +#define TB_ADDR_CMN_PLL0_HIGH_THR (0x0096 * 4) +#define TB_ADDR_CMN_PLL0_SS_CTRL1 (0x0098 * 4) +#define TB_ADDR_CMN_PLL0_SS_CTRL2 (0x0099 * 4) +#define TB_ADDR_CMN_PLL0_DSM_DIAG (0x0097 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_OVRD (0x01c2 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD (0x01c0 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD (0x01c1 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE (0x01C5 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE (0x01C6 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG (0x01C7 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE (0x01c4 * 4) +#define TB_ADDR_CMN_PSM_CLK_CTRL (0x0061 * 4) +#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR (0x40ea * 4) +#define TB_ADDR_XCVR_PSM_RCTRL (0x4001 * 4) +#define TB_ADDR_TX_PSC_A0 (0x4100 * 4) +#define TB_ADDR_TX_PSC_A1 (0x4101 * 4) +#define TB_ADDR_TX_PSC_A2 (0x4102 * 4) +#define TB_ADDR_TX_PSC_A3 (0x4103 * 4) +#define TB_ADDR_TX_DIAG_ECTRL_OVRD (0x41f5 * 4) +#define TB_ADDR_TX_PSC_CAL (0x4106 * 4) +#define TB_ADDR_TX_PSC_RDY (0x4107 * 4) +#define TB_ADDR_RX_PSC_A0 (0x8000 * 4) +#define TB_ADDR_RX_PSC_A1 (0x8001 * 4) +#define TB_ADDR_RX_PSC_A2 (0x8002 * 4) +#define TB_ADDR_RX_PSC_A3 (0x8003 * 4) +#define TB_ADDR_RX_PSC_CAL (0x8006 * 4) +#define TB_ADDR_RX_PSC_RDY (0x8007 * 4) +#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 (0x4058 * 4) +#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 * 4) +#define TB_ADDR_RX_SLC_CU_ITER_TMR (0x80e3 * 4) +#define TB_ADDR_RX_SIGDET_HL_FILT_TMR (0x8090 * 4) +#define TB_ADDR_RX_SAMP_DAC_CTRL (0x8058 * 4) +#define TB_ADDR_RX_DIAG_SIGDET_TUNE (0x81dc * 4) +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 (0x81df * 4) +#define TB_ADDR_RX_DIAG_BS_TM (0x81f5 * 4) +#define TB_ADDR_RX_DIAG_DFE_CTRL1 (0x81d3 * 4) +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 (0x81c7 * 4) +#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 (0x81c2 * 4) +#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 (0x81c1 * 4) +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 (0x81c9 * 4) +#define TB_ADDR_RX_DIAG_RXFE_TM3 (0x81f8 * 4) +#define TB_ADDR_RX_DIAG_RXFE_TM4 (0x81f9 * 4) +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE (0x81dd * 4) +#define TB_ADDR_RX_DIAG_DFE_CTRL3 (0x81d5 * 4) +#define TB_ADDR_RX_DIAG_SC2C_DELAY (0x81e1 * 4) +#define TB_ADDR_RX_REE_VGA_GAIN_NODFE (0x81bf * 4) +#define TB_ADDR_XCVR_PSM_CAL_TMR (0x4002 * 4) +#define TB_ADDR_XCVR_PSM_A0BYP_TMR (0x4004 * 4) +#define TB_ADDR_XCVR_PSM_A0IN_TMR (0x4003 * 4) +#define TB_ADDR_XCVR_PSM_A1IN_TMR (0x4005 * 4) +#define TB_ADDR_XCVR_PSM_A2IN_TMR (0x4006 * 4) +#define TB_ADDR_XCVR_PSM_A3IN_TMR (0x4007 * 4) +#define TB_ADDR_XCVR_PSM_A4IN_TMR (0x4008 * 4) +#define TB_ADDR_XCVR_PSM_A5IN_TMR (0x4009 * 4) +#define TB_ADDR_XCVR_PSM_A0OUT_TMR (0x400a * 4) +#define TB_ADDR_XCVR_PSM_A1OUT_TMR (0x400b * 4) +#define TB_ADDR_XCVR_PSM_A2OUT_TMR (0x400c * 4) +#define TB_ADDR_XCVR_PSM_A3OUT_TMR (0x400d * 4) +#define TB_ADDR_XCVR_PSM_A4OUT_TMR (0x400e * 4) +#define TB_ADDR_XCVR_PSM_A5OUT_TMR (0x400f * 4) +#define TB_ADDR_TX_RCVDET_EN_TMR (0x4122 * 4) +#define TB_ADDR_TX_RCVDET_ST_TMR (0x4123 * 4) +#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR (0x40f2 * 4) + +#endif /* __DRIVERS_USB_CDNS3_NXP_H */ diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c new file mode 100644 index 000000000000..675cb3aefaf5 --- /dev/null +++ b/drivers/usb/cdns3/core.c @@ -0,0 +1,620 @@ +/** + * core.c - Cadence USB3 DRD Controller Core file + * + * Copyright 2017 NXP + * + * Authors: Peter Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdns3-nxp-reg-def.h" +#include "core.h" +#include "host-export.h" +#include "gadget-export.h" + +static void cdns3_usb_phy_init(void __iomem *regs) +{ + pr_debug("begin of %s\n", __func__); + + writel(0x0830, regs + PHY_PMA_CMN_CTRL1); + writel(0x10, regs + TB_ADDR_CMN_DIAG_HSCLK_SEL); + writel(0x00F0, regs + TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR); + writel(0x0018, regs + TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR); + writel(0x00D0, regs + TB_ADDR_CMN_PLL0_INTDIV); + writel(0x4aaa, regs + TB_ADDR_CMN_PLL0_FRACDIV); + writel(0x0034, regs + TB_ADDR_CMN_PLL0_HIGH_THR); + writel(0x1ee, regs + TB_ADDR_CMN_PLL0_SS_CTRL1); + writel(0x7F03, regs + TB_ADDR_CMN_PLL0_SS_CTRL2); + writel(0x0020, regs + TB_ADDR_CMN_PLL0_DSM_DIAG); + writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_OVRD); + writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD); + writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD); + writel(0x0007, regs + TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE); + writel(0x0027, regs + TB_ADDR_CMN_DIAG_PLL0_CP_TUNE); + writel(0x0008, regs + TB_ADDR_CMN_DIAG_PLL0_LF_PROG); + writel(0x0022, regs + TB_ADDR_CMN_DIAG_PLL0_TEST_MODE); + writel(0x000a, regs + TB_ADDR_CMN_PSM_CLK_CTRL); + writel(0x139, regs + TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR); + writel(0xbefc, regs + TB_ADDR_XCVR_PSM_RCTRL); + + writel(0x7799, regs + TB_ADDR_TX_PSC_A0); + writel(0x7798, regs + TB_ADDR_TX_PSC_A1); + writel(0x509b, regs + TB_ADDR_TX_PSC_A2); + writel(0x3, regs + TB_ADDR_TX_DIAG_ECTRL_OVRD); + writel(0x5098, regs + TB_ADDR_TX_PSC_A3); + writel(0x2090, regs + TB_ADDR_TX_PSC_CAL); + writel(0x2090, regs + TB_ADDR_TX_PSC_RDY); + + writel(0xA6FD, regs + TB_ADDR_RX_PSC_A0); + writel(0xA6FD, regs + TB_ADDR_RX_PSC_A1); + writel(0xA410, regs + TB_ADDR_RX_PSC_A2); + writel(0x2410, regs + TB_ADDR_RX_PSC_A3); + + writel(0x23FF, regs + TB_ADDR_RX_PSC_CAL); + writel(0x2010, regs + TB_ADDR_RX_PSC_RDY); + + writel(0x0020, regs + TB_ADDR_TX_TXCC_MGNLS_MULT_000); + writel(0x00ff, regs + TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY); + writel(0x0002, regs + TB_ADDR_RX_SLC_CU_ITER_TMR); + writel(0x0013, regs + TB_ADDR_RX_SIGDET_HL_FILT_TMR); + writel(0x0000, regs + TB_ADDR_RX_SAMP_DAC_CTRL); + writel(0x1004, regs + TB_ADDR_RX_DIAG_SIGDET_TUNE); + writel(0x4041, regs + TB_ADDR_RX_DIAG_LFPSDET_TUNE2); + writel(0x0480, regs + TB_ADDR_RX_DIAG_BS_TM); + writel(0x8006, regs + TB_ADDR_RX_DIAG_DFE_CTRL1); + writel(0x003f, regs + TB_ADDR_RX_DIAG_ILL_IQE_TRIM4); + writel(0x543f, regs + TB_ADDR_RX_DIAG_ILL_E_TRIM0); + writel(0x543f, regs + TB_ADDR_RX_DIAG_ILL_IQ_TRIM0); + writel(0x0000, regs + TB_ADDR_RX_DIAG_ILL_IQE_TRIM6); + writel(0x8000, regs + TB_ADDR_RX_DIAG_RXFE_TM3); + writel(0x0003, regs + TB_ADDR_RX_DIAG_RXFE_TM4); + writel(0x2408, regs + TB_ADDR_RX_DIAG_LFPSDET_TUNE); + writel(0x05ca, regs + TB_ADDR_RX_DIAG_DFE_CTRL3); + writel(0x0258, regs + TB_ADDR_RX_DIAG_SC2C_DELAY); + writel(0x1fff, regs + TB_ADDR_RX_REE_VGA_GAIN_NODFE); + + writel(0x02c6, regs + TB_ADDR_XCVR_PSM_CAL_TMR); + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A0BYP_TMR); + writel(0x02c6, regs + TB_ADDR_XCVR_PSM_A0IN_TMR); + writel(0x0010, regs + TB_ADDR_XCVR_PSM_A1IN_TMR); + writel(0x0010, regs + TB_ADDR_XCVR_PSM_A2IN_TMR); + writel(0x0010, regs + TB_ADDR_XCVR_PSM_A3IN_TMR); + writel(0x0010, regs + TB_ADDR_XCVR_PSM_A4IN_TMR); + writel(0x0010, regs + TB_ADDR_XCVR_PSM_A5IN_TMR); + + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A0OUT_TMR); + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A1OUT_TMR); + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A2OUT_TMR); + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A3OUT_TMR); + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A4OUT_TMR); + writel(0x0002, regs + TB_ADDR_XCVR_PSM_A5OUT_TMR); + + /* Change rx detect parameter */ + writel(0x960, regs + TB_ADDR_TX_RCVDET_EN_TMR); + writel(0x01e0, regs + TB_ADDR_TX_RCVDET_ST_TMR); + writel(0x0090, regs + TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR); + + /* Force B Session Valid as 1 */ + writel(0x0060, regs + 0x380a4); + + udelay(10); + + pr_debug("end of %s\n", __func__); +} + +static void cdns_set_role(struct cdns3 *cdns, enum cdns3_roles role) +{ + u32 value; + int timeout_us = 100000; + + if (role == CDNS3_ROLE_END) + return; + + /* Wait clk value */ + value = readl(cdns->none_core_regs + USB3_SSPHY_STATUS); + writel(value, cdns->none_core_regs + USB3_SSPHY_STATUS); + udelay(1); + value = readl(cdns->none_core_regs + USB3_SSPHY_STATUS); + while ((value & 0xf0000000) != 0xf0000000 && timeout_us-- > 0) { + value = readl(cdns->none_core_regs + USB3_SSPHY_STATUS); + dev_dbg(cdns->dev, "clkvld:0x%x\n", value); + udelay(1); + } + + if (timeout_us <= 0) + dev_err(cdns->dev, "wait clkvld timeout\n"); + + /* Set all Reset bits */ + value = readl(cdns->none_core_regs + USB3_CORE_CTRL1); + value |= ALL_SW_RESET; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + udelay(1); + + if (role == CDNS3_ROLE_HOST) { + value = readl(cdns->none_core_regs + USB3_CORE_CTRL1); + value = (value & ~MODE_STRAP_MASK) | HOST_MODE | OC_DISABLE; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + value &= ~PHYAHB_SW_RESET; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + mdelay(1); + cdns3_usb_phy_init(cdns->phy_regs); + mdelay(1); + + value = readl(cdns->none_core_regs + USB3_INT_REG); + value |= HOST_INT1_EN; + writel(value, cdns->none_core_regs + USB3_INT_REG); + + value = readl(cdns->none_core_regs + USB3_CORE_CTRL1); + value &= ~ALL_SW_RESET; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + + dev_dbg(cdns->dev, "wait xhci_power_on_ready\n"); + + value = readl(cdns->none_core_regs + USB3_CORE_STATUS); + timeout_us = 100000; + while (!(value & HOST_POWER_ON_READY) && timeout_us-- > 0) { + value = readl(cdns->none_core_regs + USB3_CORE_STATUS); + udelay(1); + } + + if (timeout_us <= 0) + dev_err(cdns->dev, "wait xhci_power_on_ready timeout\n"); + + mdelay(1); + + dev_dbg(cdns->dev, "switch to host role successfully\n"); + } else { /* gadget mode */ + value = readl(cdns->none_core_regs + USB3_CORE_CTRL1); + value = (value & ~MODE_STRAP_MASK) | DEV_MODE; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + value &= ~PHYAHB_SW_RESET; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + + cdns3_usb_phy_init(cdns->phy_regs); + value = readl(cdns->none_core_regs + USB3_INT_REG); + value |= DEV_INT_EN; + writel(value, cdns->none_core_regs + USB3_INT_REG); + + value = readl(cdns->none_core_regs + USB3_CORE_CTRL1); + value &= ~ALL_SW_RESET; + writel(value, cdns->none_core_regs + USB3_CORE_CTRL1); + + dev_dbg(cdns->dev, "wait gadget_power_on_ready\n"); + + value = readl(cdns->none_core_regs + USB3_CORE_STATUS); + timeout_us = 100000; + while (!(value & DEV_POWER_ON_READY) && timeout_us-- > 0) { + value = readl(cdns->none_core_regs + USB3_CORE_STATUS); + udelay(1); + } + + if (timeout_us <= 0) + dev_err(cdns->dev, + "wait gadget_power_on_ready timeout\n"); + + mdelay(1); + + dev_dbg(cdns->dev, "switch to gadget role successfully\n"); + } +} + +static enum cdns3_roles cdns3_get_role(struct cdns3 *cdns) +{ + if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) { + if (extcon_get_state(cdns->extcon, EXTCON_USB_HOST)) + return CDNS3_ROLE_HOST; + else if (extcon_get_state(cdns->extcon, EXTCON_USB)) + return CDNS3_ROLE_GADGET; + else + return CDNS3_ROLE_END; + } else { + return cdns->roles[CDNS3_ROLE_HOST] + ? CDNS3_ROLE_HOST + : CDNS3_ROLE_GADGET; + } +} + +/** + * cdns3_core_init_role - initialize role of operation + * @cdns: Pointer to cdns3 structure + * + * Returns 0 on success otherwise negative errno + */ +static int cdns3_core_init_role(struct cdns3 *cdns) +{ + struct device *dev = cdns->dev; + enum usb_dr_mode dr_mode = usb_get_dr_mode(dev); + + cdns->role = CDNS3_ROLE_END; + if (dr_mode == USB_DR_MODE_UNKNOWN) + dr_mode = USB_DR_MODE_OTG; + + if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { + if (cdns3_host_init(cdns)) + dev_info(dev, "doesn't support host\n"); + } + + if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { + if (cdns3_gadget_init(cdns)) + dev_info(dev, "doesn't support gadget\n"); + } + + if (!cdns->roles[CDNS3_ROLE_HOST] && !cdns->roles[CDNS3_ROLE_GADGET]) { + dev_err(dev, "no supported roles\n"); + return -ENODEV; + } + + return 0; +} + +/** + * cdns3_irq - interrupt handler for cdns3 core device + * + * @irq: irq number for cdns3 core device + * @data: structure of cdns3 + * + * Returns IRQ_HANDLED or IRQ_NONE + */ +static irqreturn_t cdns3_irq(int irq, void *data) +{ + struct cdns3 *cdns = data; + irqreturn_t ret = IRQ_NONE; + + /* Handle device/host interrupt */ + if (cdns->role != CDNS3_ROLE_END) + ret = cdns3_role(cdns)->irq(cdns); + + return ret; +} + +static int cdns3_get_clks(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + int ret = 0; + + cdns->cdns3_clks[0] = devm_clk_get(dev, "usb3_lpm_clk"); + if (IS_ERR(cdns->cdns3_clks[0])) { + ret = PTR_ERR(cdns->cdns3_clks[0]); + dev_err(dev, "Failed to get usb3_lpm_clk, err=%d\n", ret); + return ret; + } + + cdns->cdns3_clks[1] = devm_clk_get(dev, "usb3_bus_clk"); + if (IS_ERR(cdns->cdns3_clks[1])) { + ret = PTR_ERR(cdns->cdns3_clks[1]); + dev_err(dev, "Failed to get usb3_bus_clk, err=%d\n", ret); + return ret; + } + + cdns->cdns3_clks[2] = devm_clk_get(dev, "usb3_aclk"); + if (IS_ERR(cdns->cdns3_clks[2])) { + ret = PTR_ERR(cdns->cdns3_clks[2]); + dev_err(dev, "Failed to get usb3_aclk, err=%d\n", ret); + return ret; + } + + cdns->cdns3_clks[3] = devm_clk_get(dev, "usb3_ipg_clk"); + if (IS_ERR(cdns->cdns3_clks[3])) { + ret = PTR_ERR(cdns->cdns3_clks[3]); + dev_err(dev, "Failed to get usb3_ipg_clk, err=%d\n", ret); + return ret; + } + + cdns->cdns3_clks[4] = devm_clk_get(dev, "usb3_core_pclk"); + if (IS_ERR(cdns->cdns3_clks[4])) { + ret = PTR_ERR(cdns->cdns3_clks[4]); + dev_err(dev, "Failed to get usb3_core_pclk, err=%d\n", ret); + return ret; + } + + return 0; +} + +static int cdns3_prepare_enable_clks(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + int i, j, ret = 0; + + for (i = 0; i < CDNS3_NUM_OF_CLKS; i++) { + ret = clk_prepare_enable(cdns->cdns3_clks[i]); + if (ret) { + dev_err(dev, + "Failed to prepare/enable cdns3 clk, err=%d\n", + ret); + goto err; + } + } + + return ret; +err: + for (j = i; j > 0; j--) + clk_disable_unprepare(cdns->cdns3_clks[j - 1]); + + return ret; +} + +static void cdns3_disable_unprepare_clks(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + int i; + + for (i = CDNS3_NUM_OF_CLKS - 1; i >= 0; i--) + clk_disable_unprepare(cdns->cdns3_clks[i]); +} + +static void cdns3_remove_roles(struct cdns3 *cdns) +{ + cdns3_gadget_remove(cdns); + cdns3_host_remove(cdns); +} + +static int cdsn3_do_role_switch(struct cdns3 *cdns, enum cdns3_roles role) +{ + dev_dbg(cdns->dev, "current role is %d, switch to %d\n", + cdns->role, role); + + if (cdns->role == role) + return 0; + + cdns3_role_stop(cdns); + if (role == CDNS3_ROLE_END) + return 0; + + cdns_set_role(cdns, role); + return cdns3_role_start(cdns, role); +} + +/** + * cdns3_role_switch - work queue handler for role switch + * + * @work: work queue item structure + * + */ +static void cdns3_role_switch(struct work_struct *work) +{ + struct cdns3 *cdns = container_of(work, struct cdns3, + role_switch_wq); + bool is_device, is_host; + + is_device = extcon_get_state(cdns->extcon, EXTCON_USB_HOST); + is_host = extcon_get_state(cdns->extcon, EXTCON_USB); + + disable_irq(cdns->irq); + if (is_device) + cdsn3_do_role_switch(cdns, CDNS3_ROLE_HOST); + else if (is_host) + cdsn3_do_role_switch(cdns, CDNS3_ROLE_GADGET); + else + cdsn3_do_role_switch(cdns, CDNS3_ROLE_END); + enable_irq(cdns->irq); +} + +static int cdns3_extcon_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct cdns3 *cdns = container_of(nb, struct cdns3, extcon_nb); + + queue_work(system_power_efficient_wq, &cdns->role_switch_wq); + + return NOTIFY_DONE; +} + +static int cdns3_register_extcon(struct cdns3 *cdns) +{ + struct extcon_dev *extcon; + struct device *dev = cdns->dev; + int ret; + + if (of_property_read_bool(dev->of_node, "extcon")) { + extcon = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(extcon)) + return PTR_ERR(extcon); + + ret = devm_extcon_register_notifier(dev, extcon, + EXTCON_USB_HOST, &cdns->extcon_nb); + if (ret < 0) { + dev_err(dev, "register Host Connector failed\n"); + return ret; + } + + ret = devm_extcon_register_notifier(dev, extcon, + EXTCON_USB, &cdns->extcon_nb); + if (ret < 0) { + dev_err(dev, "register Device Connector failed\n"); + return ret; + } + + cdns->extcon = extcon; + cdns->extcon_nb.notifier_call = cdns3_extcon_notifier; + } + + return 0; +} + +/** + * cdns3_probe - probe for cdns3 core device + * @pdev: Pointer to cdns3 core platform device + * + * Returns 0 on success otherwise negative errno + */ +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; + + /* + * Request memory region + * region-0: nxp wrap registers + * region-1: xHCI + * region-2: Peripheral + * region-3: PHY registers + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + cdns->none_core_regs = regs; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + cdns->xhci_regs = regs; + cdns->xhci_res = res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + 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, 3); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + cdns->phy_regs = regs; + + ret = cdns3_get_clks(dev); + if (ret) + return ret; + + ret = cdns3_prepare_enable_clks(dev); + if (ret) + return ret; + + cdns->usbphy = devm_usb_get_phy_by_phandle(dev, "cdns3,usbphy", 0); + if (IS_ERR(cdns->usbphy)) { + ret = PTR_ERR(cdns->usbphy); + if (ret == -ENODEV) + ret = -EINVAL; + goto err1; + } + + ret = usb_phy_init(cdns->usbphy); + if (ret) + goto err1; + + ret = cdns3_register_extcon(cdns); + if (ret) + goto err2; + + ret = cdns3_core_init_role(cdns); + if (ret) + goto err2; + + cdns->role = cdns3_get_role(cdns); + dev_dbg(dev, "the init role is %d\n", cdns->role); + cdns_set_role(cdns, cdns->role); + ret = cdns3_role_start(cdns, cdns->role); + if (ret) { + dev_err(dev, "can't start %s role\n", + cdns3_role(cdns)->name); + goto err3; + } + + ret = devm_request_irq(dev, cdns->irq, cdns3_irq, IRQF_SHARED, + dev_name(dev), cdns); + if (ret) + goto err4; + + INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch); + dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); + + return 0; + +err4: + cdns3_role_stop(cdns); +err3: + cdns3_remove_roles(cdns); +err2: + usb_phy_shutdown(cdns->usbphy); +err1: + cdns3_disable_unprepare_clks(dev); + return ret; +} + +/** + * cdns3_remove - unbind our drd driver and clean up + * @pdev: Pointer to Linux platform device + * + * Returns 0 on success otherwise negative errno + */ +static int cdns3_remove(struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id of_cdns3_match[] = { + { .compatible = "Cadence,usb3" }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_cdns3_match); +#endif + +static struct platform_driver cdns3_driver = { + .probe = cdns3_probe, + .remove = cdns3_remove, + .driver = { + .name = "cdns-usb3", + .of_match_table = of_match_ptr(of_cdns3_match), + }, +}; + +static int __init cdns3_driver_platform_register(void) +{ + cdns3_host_driver_init(); + 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_ALIAS("platform:cdns3"); +MODULE_AUTHOR("Peter Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver"); diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h new file mode 100644 index 000000000000..793d5ccc6b4c --- /dev/null +++ b/drivers/usb/cdns3/core.h @@ -0,0 +1,119 @@ +/** + * core.h - Cadence USB3 DRD Controller Core header file + * + * Copyright 2017 NXP + * + * Authors: Peter Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DRIVERS_USB_CDNS3_CORE_H +#define __DRIVERS_USB_CDNS3_CORE_H + +struct cdns3; +enum cdns3_roles { + CDNS3_ROLE_HOST = 0, + CDNS3_ROLE_GADGET, + CDNS3_ROLE_END, +}; + +/** + * struct cdns3_role_driver - host/gadget role driver + * @start: start this role + * @stop: stop this role + * @irq: irq handler for this role + * @name: role name string (host/gadget) + */ +struct cdns3_role_driver { + int (*start)(struct cdns3 *); + void (*stop)(struct cdns3 *); + irqreturn_t (*irq)(struct cdns3 *); + const char *name; +}; + +#define CDNS3_NUM_OF_CLKS 5 +/** + * struct cdns3 - Representation of Cadence USB3 DRD controller. + * @dev: pointer to Cadence device struct + * @xhci_regs: pointer to base of xhci registers + * @xhci_res: the resource for xhci + * @dev_regs: pointer to base of dev registers + * @none_core_regs: pointer to base of nxp wrapper registers + * @phy_regs: pointer to base of phy registers + * @irq: irq number for controller + * @roles: array of supported roles for this controller + * @role: current role + * @host_dev: the child host device pointer for cdns3 core + * @gadget_dev: the child gadget device pointer for cdns3 core + * @usbphy: usbphy for this controller + * @cdns3_clks: Clock pointer array for cdns3 core + * @extcon: Type-C extern connector + * @extcon_nb: notifier block for Type-C extern connector + * @role_switch_wq: work queue item for role switch + */ +struct cdns3 { + struct device *dev; + void __iomem *xhci_regs; + struct resource *xhci_res; + struct usbss_dev_register_block_type __iomem *dev_regs; + void __iomem *none_core_regs; + int irq; + struct cdns3_role_driver *roles[CDNS3_ROLE_END]; + enum cdns3_roles role; + struct device *host_dev; + struct device *gadget_dev; + void __iomem *phy_regs; + struct usb_phy *usbphy; + struct clk *cdns3_clks[CDNS3_NUM_OF_CLKS]; + struct extcon_dev *extcon; + struct notifier_block extcon_nb; + struct work_struct role_switch_wq; +}; + +static inline struct cdns3_role_driver *cdns3_role(struct cdns3 *cdns) +{ + WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]); + return cdns->roles[cdns->role]; +} + +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role) +{ + int ret; + + if (role >= CDNS3_ROLE_END) + return 0; + + if (!cdns->roles[role]) + return -ENXIO; + + ret = cdns->roles[role]->start(cdns); + if (!ret) + cdns->role = role; + + return ret; +} + +static inline void cdns3_role_stop(struct cdns3 *cdns) +{ + enum cdns3_roles role = cdns->role; + + if (role == CDNS3_ROLE_END) + return; + + cdns->role = CDNS3_ROLE_END; + + cdns->roles[role]->stop(cdns); +} + +#endif /* __DRIVERS_USB_CDNS3_CORE_H */ diff --git a/drivers/usb/cdns3/dev-regs-macro.h b/drivers/usb/cdns3/dev-regs-macro.h new file mode 100644 index 000000000000..e87e05352965 --- /dev/null +++ b/drivers/usb/cdns3/dev-regs-macro.h @@ -0,0 +1,891 @@ +/** + * dev-regs-macro.h - Cadence USB3 Device register definition + * + * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com + * Copyright 2017 NXP + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __REG_USBSS_DEV_ADDR_MAP_MACRO_H__ +#define __REG_USBSS_DEV_ADDR_MAP_MACRO_H__ + + +/* macros for BlueprintGlobalNameSpace::USB_CONF */ +#ifndef __USB_CONF_MACRO__ +#define __USB_CONF_MACRO__ + +/* macros for field CFGRST */ +#define USB_CONF__CFGRST__MASK 0x00000001U +#define USB_CONF__CFGSET__MASK 0x00000002U +#define USB_CONF__USB3DIS__MASK 0x00000008U +#define USB_CONF__DEVEN__MASK 0x00004000U +#define USB_CONF__L1EN__MASK 0x00010000U +#define USB_CONF__L1DS__MASK 0x00020000U +#define USB_CONF__CLK2OFFDS__MASK 0x00080000U +#define USB_CONF__U1EN__MASK 0x01000000U +#define USB_CONF__U1DS__MASK 0x02000000U +#define USB_CONF__U2EN__MASK 0x04000000U +#define USB_CONF__U2DS__MASK 0x08000000U +#endif /* __USB_CONF_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_conf */ +#ifndef __USB_STS_MACRO__ +#define __USB_STS_MACRO__ + +/* macros for field CFGSTS */ +#define USB_STS__CFGSTS__MASK 0x00000001U +#define USB_STS__USBSPEED__READ(src) (((uint32_t)(src) & 0x00000070U) >> 4) + +/* macros for field ENDIAN_MIRROR */ +#define USB_STS__LPMST__READ(src) (((uint32_t)(src) & 0x000c0000U) >> 18) + +/* macros for field USB2CONS */ +#define USB_STS__U1ENS__MASK 0x01000000U +#define USB_STS__U2ENS__MASK 0x02000000U +#define USB_STS__LST__READ(src) (((uint32_t)(src) & 0x3c000000U) >> 26) + +/* macros for field DMAOFF */ +#endif /* __USB_STS_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_sts */ +#ifndef __USB_CMD_MACRO__ +#define __USB_CMD_MACRO__ + +/* macros for field SET_ADDR */ +#define USB_CMD__SET_ADDR__MASK 0x00000001U +#define USB_CMD__FADDR__WRITE(src) (((uint32_t)(src) << 1) & 0x000000feU) +#endif /* __USB_CMD_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cmd */ +#ifndef __USB_ITPN_MACRO__ +#define __USB_ITPN_MACRO__ + +/* macros for field ITPN */ +#endif /* __USB_ITPN_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_iptn */ +#ifndef __USB_LPM_MACRO__ +#define __USB_LPM_MACRO__ + +/* macros for field HIRD */ +#endif /* __USB_LPM_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_lpm */ +#ifndef __USB_IEN_MACRO__ +#define __USB_IEN_MACRO__ + +/* macros for field CONIEN */ +#define USB_IEN__CONIEN__MASK 0x00000001U +#define USB_IEN__DISIEN__MASK 0x00000002U +#define USB_IEN__UWRESIEN__MASK 0x00000004U +#define USB_IEN__UHRESIEN__MASK 0x00000008U +#define USB_IEN__U3EXTIEN__MASK 0x00000020U +#define USB_IEN__CON2IEN__MASK 0x00010000U +#define USB_IEN__U2RESIEN__MASK 0x00040000U +#define USB_IEN__L2ENTIEN__MASK 0x00100000U +#define USB_IEN__L2EXTIEN__MASK 0x00200000U +#endif /* __USB_IEN_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_ien */ +#ifndef __USB_ISTS_MACRO__ +#define __USB_ISTS_MACRO__ + +/* macros for field CONI */ +#define USB_ISTS__CONI__SHIFT 0 +#define USB_ISTS__DISI__SHIFT 1 +#define USB_ISTS__UWRESI__SHIFT 2 +#define USB_ISTS__UHRESI__SHIFT 3 +#define USB_ISTS__U3EXTI__SHIFT 5 +#define USB_ISTS__CON2I__SHIFT 16 +#define USB_ISTS__DIS2I__SHIFT 17 +#define USB_ISTS__DIS2I__MASK 0x00020000U +#define USB_ISTS__U2RESI__SHIFT 18 +#define USB_ISTS__L2ENTI__SHIFT 20 +#define USB_ISTS__L2EXTI__SHIFT 21 +#endif /* __USB_ISTS_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_ists */ +#ifndef __EP_SEL_MACRO__ +#define __EP_SEL_MACRO__ + +/* macros for field EPNO */ +#endif /* __EP_SEL_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_sel */ +#ifndef __EP_TRADDR_MACRO__ +#define __EP_TRADDR_MACRO__ + +/* macros for field TRADDR */ +#define EP_TRADDR__TRADDR__WRITE(src) ((uint32_t)(src) & 0xffffffffU) +#endif /* __EP_TRADDR_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_traddr */ +#ifndef __EP_CFG_MACRO__ +#define __EP_CFG_MACRO__ + +/* macros for field ENABLE */ +#define EP_CFG__ENABLE__MASK 0x00000001U +#define EP_CFG__EPTYPE__WRITE(src) (((uint32_t)(src) << 1) & 0x00000006U) +#define EP_CFG__MAXBURST__WRITE(src) (((uint32_t)(src) << 8) & 0x00000f00U) +#define EP_CFG__MAXPKTSIZE__WRITE(src) (((uint32_t)(src) << 16) & 0x07ff0000U) +#define EP_CFG__BUFFERING__WRITE(src) (((uint32_t)(src) << 27) & 0xf8000000U) +#endif /* __EP_CFG_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_cfg */ +#ifndef __EP_CMD_MACRO__ +#define __EP_CMD_MACRO__ + +/* macros for field EPRST */ +#define EP_CMD__EPRST__MASK 0x00000001U +#define EP_CMD__SSTALL__MASK 0x00000002U +#define EP_CMD__CSTALL__MASK 0x00000004U +#define EP_CMD__ERDY__MASK 0x00000008U +#define EP_CMD__REQ_CMPL__MASK 0x00000020U +#define EP_CMD__DRDY__MASK 0x00000040U +#define EP_CMD__DFLUSH__MASK 0x00000080U +#endif /* __EP_CMD_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_cmd */ +#ifndef __EP_STS_MACRO__ +#define __EP_STS_MACRO__ + +/* macros for field SETUP */ +#define EP_STS__SETUP__MASK 0x00000001U +#define EP_STS__STALL__MASK 0x00000002U +#define EP_STS__IOC__MASK 0x00000004U +#define EP_STS__ISP__MASK 0x00000008U +#define EP_STS__DESCMIS__MASK 0x00000010U +#define EP_STS__TRBERR__MASK 0x00000080U +#define EP_STS__NRDY__MASK 0x00000100U +#define EP_STS__DBUSY__MASK 0x00000200U +#define EP_STS__OUTSMM__MASK 0x00004000U +#define EP_STS__ISOERR__MASK 0x00008000U +#endif /* __EP_STS_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_sts */ +#ifndef __EP_STS_SID_MACRO__ +#define __EP_STS_SID_MACRO__ + +/* macros for field SID */ +#endif /* __EP_STS_SID_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_sts_sid */ +#ifndef __EP_STS_EN_MACRO__ +#define __EP_STS_EN_MACRO__ + +/* macros for field SETUPEN */ +#define EP_STS_EN__SETUPEN__MASK 0x00000001U +#define EP_STS_EN__DESCMISEN__MASK 0x00000010U +#define EP_STS_EN__TRBERREN__MASK 0x00000080U +#endif /* __EP_STS_EN_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_sts_en */ +#ifndef __DRBL_MACRO__ +#define __DRBL_MACRO__ + +/* macros for field DRBL0O */ +#endif /* __DRBL_MACRO__ */ + + +/* macros for usbss_dev_register_block.drbl */ +#ifndef __EP_IEN_MACRO__ +#define __EP_IEN_MACRO__ + +/* macros for field EOUTEN0 */ +#define EP_IEN__EOUTEN0__MASK 0x00000001U +#define EP_IEN__EINEN0__MASK 0x00010000U +#endif /* __EP_IEN_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_ien */ +#ifndef __EP_ISTS_MACRO__ +#define __EP_ISTS_MACRO__ + +/* macros for field EOUT0 */ +#define EP_ISTS__EOUT0__MASK 0x00000001U +#define EP_ISTS__EIN0__MASK 0x00010000U +#endif /* __EP_ISTS_MACRO__ */ + + +/* macros for usbss_dev_register_block.ep_ists */ +#ifndef __USB_PWR_MACRO__ +#define __USB_PWR_MACRO__ + +/* macros for field PSO_EN */ +#endif /* __USB_PWR_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_pwr */ +#ifndef __USB_CONF2_MACRO__ +#define __USB_CONF2_MACRO__ + +/* macros for field AHB_RETRY_EN */ +#endif /* __USB_CONF2_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_conf2 */ +#ifndef __USB_CAP1_MACRO__ +#define __USB_CAP1_MACRO__ + +/* macros for field SFR_TYPE */ +#endif /* __USB_CAP1_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cap1 */ +#ifndef __USB_CAP2_MACRO__ +#define __USB_CAP2_MACRO__ + +/* macros for field ACTUAL_MEM_SIZE */ +#endif /* __USB_CAP2_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cap2 */ +#ifndef __USB_CAP3_MACRO__ +#define __USB_CAP3_MACRO__ + +/* macros for field EPOUT_N */ +#endif /* __USB_CAP3_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cap3 */ +#ifndef __USB_CAP4_MACRO__ +#define __USB_CAP4_MACRO__ + +/* macros for field EPOUTI_N */ +#endif /* __USB_CAP4_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cap4 */ +#ifndef __USB_CAP5_MACRO__ +#define __USB_CAP5_MACRO__ + +/* macros for field EPOUTI_N */ +#endif /* __USB_CAP5_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cap5 */ +#ifndef __USB_CAP6_MACRO__ +#define __USB_CAP6_MACRO__ + +/* macros for field VERSION */ +#endif /* __USB_CAP6_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cap6 */ +#ifndef __USB_CPKT1_MACRO__ +#define __USB_CPKT1_MACRO__ + +/* macros for field CPKT1 */ +#endif /* __USB_CPKT1_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cpkt1 */ +#ifndef __USB_CPKT2_MACRO__ +#define __USB_CPKT2_MACRO__ + +/* macros for field CPKT2 */ +#endif /* __USB_CPKT2_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cpkt2 */ +#ifndef __USB_CPKT3_MACRO__ +#define __USB_CPKT3_MACRO__ + +/* macros for field CPKT3 */ +#endif /* __USB_CPKT3_MACRO__ */ + + +/* macros for usbss_dev_register_block.usb_cpkt3 */ +#ifndef __CFG_REG1_MACRO__ +#define __CFG_REG1_MACRO__ + +/* macros for field DEBOUNCER_CNT */ +#endif /* __CFG_REG1_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg1 */ +#ifndef __DBG_LINK1_MACRO__ +#define __DBG_LINK1_MACRO__ + +/* macros for field LFPS_MIN_DET_U1_EXIT */ +#define DBG_LINK1__LFPS_MIN_GEN_U1_EXIT__WRITE(src) \ + (((uint32_t)(src)\ + << 8) & 0x0000ff00U) +#define DBG_LINK1__LFPS_MIN_GEN_U1_EXIT_SET__MASK 0x02000000U +#endif /* __DBG_LINK1_MACRO__ */ + + +/* macros for usbss_dev_register_block.dbg_link1 */ +#ifndef __DBG_LINK2_MACRO__ +#define __DBG_LINK2_MACRO__ + +/* macros for field RXEQTR_AVAL */ +#endif /* __DBG_LINK2_MACRO__ */ + + +/* macros for usbss_dev_register_block.dbg_link2 */ +#ifndef __CFG_REG4_MACRO__ +#define __CFG_REG4_MACRO__ + +/* macros for field RXDETECT_QUIET_TIMEOUT */ +#endif /* __CFG_REG4_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg4 */ +#ifndef __CFG_REG5_MACRO__ +#define __CFG_REG5_MACRO__ + +/* macros for field U3_HDSK_FAIL_TIMEOUT */ +#endif /* __CFG_REG5_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg5 */ +#ifndef __CFG_REG6_MACRO__ +#define __CFG_REG6_MACRO__ + +/* macros for field SSINACTIVE_QUIET_TIMEOUT */ +#endif /* __CFG_REG6_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg6 */ +#ifndef __CFG_REG7_MACRO__ +#define __CFG_REG7_MACRO__ + +/* macros for field POLLING_LFPS_TIMEOUT */ +#endif /* __CFG_REG7_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg7 */ +#ifndef __CFG_REG8_MACRO__ +#define __CFG_REG8_MACRO__ + +/* macros for field POLLING_ACTIVE_TIMEOUT */ +#endif /* __CFG_REG8_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg8 */ +#ifndef __CFG_REG9_MACRO__ +#define __CFG_REG9_MACRO__ + +/* macros for field POLLING_IDLE_TIMEOUT */ +#endif /* __CFG_REG9_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg9 */ +#ifndef __CFG_REG10_MACRO__ +#define __CFG_REG10_MACRO__ + +/* macros for field POLLING_CONF_TIMEOUT */ +#endif /* __CFG_REG10_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg10 */ +#ifndef __CFG_REG11_MACRO__ +#define __CFG_REG11_MACRO__ + +/* macros for field RECOVERY_ACTIVE_TIMEOUT */ +#endif /* __CFG_REG11_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg11 */ +#ifndef __CFG_REG12_MACRO__ +#define __CFG_REG12_MACRO__ + +/* macros for field RECOVERY_CONF_TIMEOUT */ +#endif /* __CFG_REG12_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg12 */ +#ifndef __CFG_REG13_MACRO__ +#define __CFG_REG13_MACRO__ + +/* macros for field RECOVERY_IDLE_TIMEOUT */ +#endif /* __CFG_REG13_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg13 */ +#ifndef __CFG_REG14_MACRO__ +#define __CFG_REG14_MACRO__ + +/* macros for field HOTRESET_ACTIVE_TIMEOUT */ +#endif /* __CFG_REG14_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg14 */ +#ifndef __CFG_REG15_MACRO__ +#define __CFG_REG15_MACRO__ + +/* macros for field HOTRESET_EXIT_TIMEOUT */ +#endif /* __CFG_REG15_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg15 */ +#ifndef __CFG_REG16_MACRO__ +#define __CFG_REG16_MACRO__ + +/* macros for field LFPS_PING_REPEAT */ +#endif /* __CFG_REG16_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg16 */ +#ifndef __CFG_REG17_MACRO__ +#define __CFG_REG17_MACRO__ + +/* macros for field PENDING_HP_TIMEOUT */ +#endif /* __CFG_REG17_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg17 */ +#ifndef __CFG_REG18_MACRO__ +#define __CFG_REG18_MACRO__ + +/* macros for field CREDIT_HP_TIMEOUT */ +#endif /* __CFG_REG18_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg18 */ +#ifndef __CFG_REG19_MACRO__ +#define __CFG_REG19_MACRO__ + +/* macros for field LUP_TIMEOUT */ +#endif /* __CFG_REG19_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg19 */ +#ifndef __CFG_REG20_MACRO__ +#define __CFG_REG20_MACRO__ + +/* macros for field LDN_TIMEOUT */ +#endif /* __CFG_REG20_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg20 */ +#ifndef __CFG_REG21_MACRO__ +#define __CFG_REG21_MACRO__ + +/* macros for field PM_LC_TIMEOUT */ +#endif /* __CFG_REG21_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg21 */ +#ifndef __CFG_REG22_MACRO__ +#define __CFG_REG22_MACRO__ + +/* macros for field PM_ENTRY_TIMEOUT */ +#endif /* __CFG_REG22_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg22 */ +#ifndef __CFG_REG23_MACRO__ +#define __CFG_REG23_MACRO__ + +/* macros for field UX_EXIT_TIMEOUT */ +#endif /* __CFG_REG23_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg23 */ +#ifndef __CFG_REG24_MACRO__ +#define __CFG_REG24_MACRO__ + +/* macros for field LFPS_DET_RESET_MIN */ +#endif /* __CFG_REG24_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg24 */ +#ifndef __CFG_REG25_MACRO__ +#define __CFG_REG25_MACRO__ + +/* macros for field LFPS_DET_RESET_MAX */ +#endif /* __CFG_REG25_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg25 */ +#ifndef __CFG_REG26_MACRO__ +#define __CFG_REG26_MACRO__ + +/* macros for field LFPS_DET_POLLING_MIN */ +#endif /* __CFG_REG26_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg26 */ +#ifndef __CFG_REG27_MACRO__ +#define __CFG_REG27_MACRO__ + +/* macros for field LFPS_DET_POLLING_MAX */ +#endif /* __CFG_REG27_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg27 */ +#ifndef __CFG_REG28_MACRO__ +#define __CFG_REG28_MACRO__ + +/* macros for field LFPS_DET_PING_MIN */ +#endif /* __CFG_REG28_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg28 */ +#ifndef __CFG_REG29_MACRO__ +#define __CFG_REG29_MACRO__ + +/* macros for field LFPS_DET_PING_MAX */ +#endif /* __CFG_REG29_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg29 */ +#ifndef __CFG_REG30_MACRO__ +#define __CFG_REG30_MACRO__ + +/* macros for field LFPS_DET_U1EXIT_MIN */ +#endif /* __CFG_REG30_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg30 */ +#ifndef __CFG_REG31_MACRO__ +#define __CFG_REG31_MACRO__ + +/* macros for field LFPS_DET_U1EXIT_MAX */ +#endif /* __CFG_REG31_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg31 */ +#ifndef __CFG_REG32_MACRO__ +#define __CFG_REG32_MACRO__ + +/* macros for field LFPS_DET_U2EXIT_MIN */ +#endif /* __CFG_REG32_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg32 */ +#ifndef __CFG_REG33_MACRO__ +#define __CFG_REG33_MACRO__ + +/* macros for field LFPS_DET_U2EXIT_MAX */ +#endif /* __CFG_REG33_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg33 */ +#ifndef __CFG_REG34_MACRO__ +#define __CFG_REG34_MACRO__ + +/* macros for field LFPS_DET_U3EXIT_MIN */ +#endif /* __CFG_REG34_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg34 */ +#ifndef __CFG_REG35_MACRO__ +#define __CFG_REG35_MACRO__ + +/* macros for field LFPS_DET_U3EXIT_MAX */ +#endif /* __CFG_REG35_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg35 */ +#ifndef __CFG_REG36_MACRO__ +#define __CFG_REG36_MACRO__ + +/* macros for field LFPS_GEN_PING */ +#endif /* __CFG_REG36_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg36 */ +#ifndef __CFG_REG37_MACRO__ +#define __CFG_REG37_MACRO__ + +/* macros for field LFPS_GEN_POLLING */ +#endif /* __CFG_REG37_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg37 */ +#ifndef __CFG_REG38_MACRO__ +#define __CFG_REG38_MACRO__ + +/* macros for field LFPS_GEN_U1EXIT */ +#endif /* __CFG_REG38_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg38 */ +#ifndef __CFG_REG39_MACRO__ +#define __CFG_REG39_MACRO__ + +/* macros for field LFPS_GEN_U3EXIT */ +#endif /* __CFG_REG39_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg39 */ +#ifndef __CFG_REG40_MACRO__ +#define __CFG_REG40_MACRO__ + +/* macros for field LFPS_MIN_GEN_U1EXIT */ +#endif /* __CFG_REG40_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg40 */ +#ifndef __CFG_REG41_MACRO__ +#define __CFG_REG41_MACRO__ + +/* macros for field LFPS_MIN_GEN_U2EXIT */ +#endif /* __CFG_REG41_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg41 */ +#ifndef __CFG_REG42_MACRO__ +#define __CFG_REG42_MACRO__ + +/* macros for field LFPS_POLLING_REPEAT */ +#endif /* __CFG_REG42_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg42 */ +#ifndef __CFG_REG43_MACRO__ +#define __CFG_REG43_MACRO__ + +/* macros for field LFPS_POLLING_MAX_TREPEAT */ +#endif /* __CFG_REG43_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg43 */ +#ifndef __CFG_REG44_MACRO__ +#define __CFG_REG44_MACRO__ + +/* macros for field LFPS_POLLING_MIN_TREPEAT */ +#endif /* __CFG_REG44_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg44 */ +#ifndef __CFG_REG45_MACRO__ +#define __CFG_REG45_MACRO__ + +/* macros for field ITP_WAKEUP_TIMEOUT */ +#endif /* __CFG_REG45_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg45 */ +#ifndef __CFG_REG46_MACRO__ +#define __CFG_REG46_MACRO__ + +/* macros for field TSEQ_QUANTITY */ +#endif /* __CFG_REG46_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg46 */ +#ifndef __CFG_REG47_MACRO__ +#define __CFG_REG47_MACRO__ + +/* macros for field ERDY_TIMEOUT_CNT */ +#endif /* __CFG_REG47_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg47 */ +#ifndef __CFG_REG48_MACRO__ +#define __CFG_REG48_MACRO__ + +/* macros for field TWTRSTFS_J_CNT */ +#endif /* __CFG_REG48_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg48 */ +#ifndef __CFG_REG49_MACRO__ +#define __CFG_REG49_MACRO__ + +/* macros for field TUCH_CNT */ +#endif /* __CFG_REG49_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg49 */ +#ifndef __CFG_REG50_MACRO__ +#define __CFG_REG50_MACRO__ + +/* macros for field TWAITCHK_CNT */ +#endif /* __CFG_REG50_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg50 */ +#ifndef __CFG_REG51_MACRO__ +#define __CFG_REG51_MACRO__ + +/* macros for field TWTFS_CNT */ +#endif /* __CFG_REG51_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg51 */ +#ifndef __CFG_REG52_MACRO__ +#define __CFG_REG52_MACRO__ + +/* macros for field TWTREV_CNT */ +#endif /* __CFG_REG52_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg52 */ +#ifndef __CFG_REG53_MACRO__ +#define __CFG_REG53_MACRO__ + +/* macros for field TWTRSTHS_CNT */ +#endif /* __CFG_REG53_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg53 */ +#ifndef __CFG_REG54_MACRO__ +#define __CFG_REG54_MACRO__ + +/* macros for field TWTRSM_CNT */ +#endif /* __CFG_REG54_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg54 */ +#ifndef __CFG_REG55_MACRO__ +#define __CFG_REG55_MACRO__ + +/* macros for field TDRSMUP_CNT */ +#endif /* __CFG_REG55_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg55 */ +#ifndef __CFG_REG56_MACRO__ +#define __CFG_REG56_MACRO__ + +/* macros for field TOUTHS_CNT */ +#endif /* __CFG_REG56_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg56 */ +#ifndef __CFG_REG57_MACRO__ +#define __CFG_REG57_MACRO__ + +/* macros for field LFPS_DEB_WIDTH */ +#endif /* __CFG_REG57_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg57 */ +#ifndef __CFG_REG58_MACRO__ +#define __CFG_REG58_MACRO__ + +/* macros for field LFPS_GEN_U2EXIT */ +#endif /* __CFG_REG58_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg58 */ +#ifndef __CFG_REG59_MACRO__ +#define __CFG_REG59_MACRO__ + +/* macros for field LFPS_MIN_GEN_U3EXIT */ +#endif /* __CFG_REG59_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg59 */ +#ifndef __CFG_REG60_MACRO__ +#define __CFG_REG60_MACRO__ + +/* macros for field PORT_CONFIG_TIMEOUT */ +#endif /* __CFG_REG60_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg60 */ +#ifndef __CFG_REG61_MACRO__ +#define __CFG_REG61_MACRO__ + +/* macros for field LFPS_POL_LFPS_TO_RXEQ */ +#endif /* __CFG_REG61_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg61 */ +#ifndef __CFG_REG62_MACRO__ +#define __CFG_REG62_MACRO__ + +/* macros for field PHY_TX_LATENCY */ +#endif /* __CFG_REG62_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg62 */ +#ifndef __CFG_REG63_MACRO__ +#define __CFG_REG63_MACRO__ + +/* macros for field U2_INACTIVITY_TMOUT */ +#endif /* __CFG_REG63_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg63 */ +#ifndef __CFG_REG64_MACRO__ +#define __CFG_REG64_MACRO__ + +/* macros for field TFILTSE0 */ +#endif /* __CFG_REG64_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg64 */ +#ifndef __CFG_REG65_MACRO__ +#define __CFG_REG65_MACRO__ + +/* macros for field TFILT */ +#endif /* __CFG_REG65_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg65 */ +#ifndef __CFG_REG66_MACRO__ +#define __CFG_REG66_MACRO__ + +/* macros for field TWTRSTFS_SE0 */ +#endif /* __CFG_REG66_MACRO__ */ + + +/* macros for usbss_dev_register_block.cfg_reg66 */ +#ifndef __DMA_AXI_CTRL_MACRO__ +#define __DMA_AXI_CTRL_MACRO__ + +/* macros for field MAWPROT */ +#endif /* __DMA_AXI_CTRL_MACRO__ */ + + +/* macros for usbss_dev_register_block.dma_axi_ctrl */ +#ifndef __DMA_AXI_ID_MACRO__ +#define __DMA_AXI_ID_MACRO__ + +/* macros for field MAW_ID */ +#endif /* __DMA_AXI_ID_MACRO__ */ + + +/* macros for usbss_dev_register_block.dma_axi_id */ +#ifndef __DMA_AXI_CAP_MACRO__ +#define __DMA_AXI_CAP_MACRO__ + +/* macros for field RESERVED0 */ +#endif /* __DMA_AXI_CAP_MACRO__ */ + + +/* macros for usbss_dev_register_block.dma_axi_cap */ +#ifndef __DMA_AXI_CTRL0_MACRO__ +#define __DMA_AXI_CTRL0_MACRO__ + +/* macros for field B_MAX */ +#endif /* __DMA_AXI_CTRL0_MACRO__ */ + + +/* macros for usbss_dev_register_block.dma_axi_ctrl0 */ +#ifndef __DMA_AXI_CTRL1_MACRO__ +#define __DMA_AXI_CTRL1_MACRO__ + +/* macros for field ROT */ +#endif /* __DMA_AXI_CTRL1_MACRO__ */ + + +/* macros for usbss_dev_register_block.dma_axi_ctrl1 */ +#endif /* __REG_USBSS_DEV_ADDR_MAP_MACRO_H__ */ diff --git a/drivers/usb/cdns3/dev-regs-map.h b/drivers/usb/cdns3/dev-regs-map.h new file mode 100644 index 000000000000..ef9cfe2ff342 --- /dev/null +++ b/drivers/usb/cdns3/dev-regs-map.h @@ -0,0 +1,126 @@ +/** + * dev-regs-map.h - Cadence USB3 Device register map definition + * + * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com + * Copyright 2017 NXP + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + + +#ifndef __REG_USBSS_DEV_ADDR_MAP_H__ +#define __REG_USBSS_DEV_ADDR_MAP_H__ + +#include "dev-regs-macro.h" + +struct usbss_dev_register_block_type { + uint32_t usb_conf; /* 0x0 - 0x4 */ + uint32_t usb_sts; /* 0x4 - 0x8 */ + uint32_t usb_cmd; /* 0x8 - 0xc */ + uint32_t usb_iptn; /* 0xc - 0x10 */ + uint32_t usb_lpm; /* 0x10 - 0x14 */ + uint32_t usb_ien; /* 0x14 - 0x18 */ + uint32_t usb_ists; /* 0x18 - 0x1c */ + uint32_t ep_sel; /* 0x1c - 0x20 */ + uint32_t ep_traddr; /* 0x20 - 0x24 */ + uint32_t ep_cfg; /* 0x24 - 0x28 */ + uint32_t ep_cmd; /* 0x28 - 0x2c */ + uint32_t ep_sts; /* 0x2c - 0x30 */ + uint32_t ep_sts_sid; /* 0x30 - 0x34 */ + uint32_t ep_sts_en; /* 0x34 - 0x38 */ + uint32_t drbl; /* 0x38 - 0x3c */ + uint32_t ep_ien; /* 0x3c - 0x40 */ + uint32_t ep_ists; /* 0x40 - 0x44 */ + uint32_t usb_pwr; /* 0x44 - 0x48 */ + uint32_t usb_conf2; /* 0x48 - 0x4c */ + uint32_t usb_cap1; /* 0x4c - 0x50 */ + uint32_t usb_cap2; /* 0x50 - 0x54 */ + uint32_t usb_cap3; /* 0x54 - 0x58 */ + uint32_t usb_cap4; /* 0x58 - 0x5c */ + uint32_t usb_cap5; /* 0x5c - 0x60 */ + uint32_t PAD2_73; /* 0x60 - 0x64 */ + uint32_t usb_cpkt1; /* 0x64 - 0x68 */ + uint32_t usb_cpkt2; /* 0x68 - 0x6c */ + uint32_t usb_cpkt3; /* 0x6c - 0x70 */ + char pad__0[0x90]; /* 0x70 - 0x100 */ + uint32_t PAD2_78; /* 0x100 - 0x104 */ + uint32_t dbg_link1; /* 0x104 - 0x108 */ + uint32_t PAD2_80; /* 0x108 - 0x10c */ + uint32_t PAD2_81; /* 0x10c - 0x110 */ + uint32_t PAD2_82; /* 0x110 - 0x114 */ + uint32_t PAD2_83; /* 0x114 - 0x118 */ + uint32_t PAD2_84; /* 0x118 - 0x11c */ + uint32_t PAD2_85; /* 0x11c - 0x120 */ + uint32_t PAD2_86; /* 0x120 - 0x124 */ + uint32_t PAD2_87; /* 0x124 - 0x128 */ + uint32_t PAD2_88; /* 0x128 - 0x12c */ + uint32_t PAD2_89; /* 0x12c - 0x130 */ + uint32_t PAD2_90; /* 0x130 - 0x134 */ + uint32_t PAD2_91; /* 0x134 - 0x138 */ + uint32_t PAD2_92; /* 0x138 - 0x13c */ + uint32_t PAD2_93; /* 0x13c - 0x140 */ + uint32_t PAD2_94; /* 0x140 - 0x144 */ + uint32_t PAD2_95; /* 0x144 - 0x148 */ + uint32_t PAD2_96; /* 0x148 - 0x14c */ + uint32_t PAD2_97; /* 0x14c - 0x150 */ + uint32_t PAD2_98; /* 0x150 - 0x154 */ + uint32_t PAD2_99; /* 0x154 - 0x158 */ + uint32_t PAD2_100; /* 0x158 - 0x15c */ + uint32_t PAD2_101; /* 0x15c - 0x160 */ + uint32_t PAD2_102; /* 0x160 - 0x164 */ + uint32_t PAD2_103; /* 0x164 - 0x168 */ + uint32_t PAD2_104; /* 0x168 - 0x16c */ + uint32_t PAD2_105; /* 0x16c - 0x170 */ + uint32_t PAD2_106; /* 0x170 - 0x174 */ + uint32_t PAD2_107; /* 0x174 - 0x178 */ + uint32_t PAD2_108; /* 0x178 - 0x17c */ + uint32_t PAD2_109; /* 0x17c - 0x180 */ + uint32_t PAD2_110; /* 0x180 - 0x184 */ + uint32_t PAD2_111; /* 0x184 - 0x188 */ + uint32_t PAD2_112; /* 0x188 - 0x18c */ + char pad__1[0x20]; /* 0x18c - 0x1ac */ + uint32_t PAD2_114; /* 0x1ac - 0x1b0 */ + uint32_t PAD2_115; /* 0x1b0 - 0x1b4 */ + uint32_t PAD2_116; /* 0x1b4 - 0x1b8 */ + uint32_t PAD2_117; /* 0x1b8 - 0x1bc */ + uint32_t PAD2_118; /* 0x1bc - 0x1c0 */ + uint32_t PAD2_119; /* 0x1c0 - 0x1c4 */ + uint32_t PAD2_120; /* 0x1c4 - 0x1c8 */ + uint32_t PAD2_121; /* 0x1c8 - 0x1cc */ + uint32_t PAD2_122; /* 0x1cc - 0x1d0 */ + uint32_t PAD2_123; /* 0x1d0 - 0x1d4 */ + uint32_t PAD2_124; /* 0x1d4 - 0x1d8 */ + uint32_t PAD2_125; /* 0x1d8 - 0x1dc */ + uint32_t PAD2_126; /* 0x1dc - 0x1e0 */ + uint32_t PAD2_127; /* 0x1e0 - 0x1e4 */ + uint32_t PAD2_128; /* 0x1e4 - 0x1e8 */ + uint32_t PAD2_129; /* 0x1e8 - 0x1ec */ + uint32_t PAD2_130; /* 0x1ec - 0x1f0 */ + uint32_t PAD2_131; /* 0x1f0 - 0x1f4 */ + uint32_t PAD2_132; /* 0x1f4 - 0x1f8 */ + uint32_t PAD2_133; /* 0x1f8 - 0x1fc */ + uint32_t PAD2_134; /* 0x1fc - 0x200 */ + uint32_t PAD2_135; /* 0x200 - 0x204 */ + uint32_t PAD2_136; /* 0x204 - 0x208 */ + uint32_t PAD2_137; /* 0x208 - 0x20c */ + uint32_t PAD2_138; /* 0x20c - 0x210 */ + uint32_t PAD2_139; /* 0x210 - 0x214 */ + uint32_t PAD2_140; /* 0x214 - 0x218 */ + uint32_t PAD2_141; /* 0x218 - 0x21c */ + uint32_t PAD2_142; /* 0x21c - 0x220 */ + uint32_t PAD2_143; /* 0x220 - 0x224 */ + uint32_t PAD2_144; /* 0x224 - 0x228 */ + char pad__2[0xd8]; /* 0x228 - 0x300 */ + uint32_t dma_axi_ctrl; /* 0x300 - 0x304 */ + uint32_t PAD2_147; /* 0x304 - 0x308 */ + uint32_t PAD2_148; /* 0x308 - 0x30c */ + uint32_t PAD2_149; /* 0x30c - 0x310 */ + uint32_t PAD2_150; /* 0x310 - 0x314 */ +}; + +#endif /* __REG_USBSS_DEV_ADDR_MAP_H__ */ diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h new file mode 100644 index 000000000000..e085ed38ea05 --- /dev/null +++ b/drivers/usb/cdns3/gadget-export.h @@ -0,0 +1,36 @@ +/* + * gadget-export.h - Gadget Export APIs + * + * Copyright 2017 NXP + * Authors: Peter Chen + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __CDNS3_GADGET_EXPORT_H +#define __CDNS3_GADGET_EXPORT_H + +#ifdef CONFIG_USB_CDNS3_GADGET + +int cdns3_gadget_init(struct cdns3 *cdns); +void cdns3_gadget_remove(struct cdns3 *cdns); +#else + +static inline int cdns3_gadget_init(struct cdns3 *cdns) +{ + return -ENXIO; +} + +static inline void cdns3_gadget_remove(struct cdns3 *cdns) +{ + +} + +#endif + +#endif /* __CDNS3_GADGET_EXPORT_H */ diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c new file mode 100644 index 000000000000..0936336504da --- /dev/null +++ b/drivers/usb/cdns3/gadget.c @@ -0,0 +1,2184 @@ +/** + * gadget.c - Cadence USB3 Device Core file + * + * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com + * Copyright 2017 NXP + * + * Authors: Pawel Jez , + * Konrad Kociolek + * Peter Chen + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "gadget-export.h" +#include "gadget.h" +#include "io.h" + +/*-------------------------------------------------------------------------*/ +/* Function declarations */ + +static void select_ep(struct usb_ss_dev *usb_ss, u32 ep); +static int usb_ss_allocate_trb_pool(struct usb_ss_endpoint *usb_ss_ep); +static void cdns_ep_stall_flush(struct usb_ss_endpoint *usb_ss_ep); +static void cdns_ep0_config(struct usb_ss_dev *usb_ss); +static void cdns_gadget_unconfig(struct usb_ss_dev *usb_ss); +static void cdns_ep0_run_transfer(struct usb_ss_dev *usb_ss, + dma_addr_t dma_addr, unsigned int length, int erdy); +static int cdns_ep_run_transfer(struct usb_ss_endpoint *usb_ss_ep); +static int cdns_get_setup_ret(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static int cdns_req_ep0_set_address(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static int cdns_req_ep0_get_status(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static int cdns_req_ep0_handle_feature(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req, int set); +static int cdns_req_ep0_set_sel(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static int cdns_req_ep0_set_isoch_delay(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static int cdns_req_ep0_set_configuration(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static int cdns_ep0_standard_request(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req); +static void cdns_ep0_setup_phase(struct usb_ss_dev *usb_ss); +static int cdns_check_ep_interrupt_proceed(struct usb_ss_endpoint *usb_ss_ep); +static void cdns_check_ep0_interrupt_proceed(struct usb_ss_dev *usb_ss, + int dir); +static void cdns_check_usb_interrupt_proceed(struct usb_ss_dev *usb_ss, + u32 usb_ists); +#ifdef CDNS_THREADED_IRQ_HANDLING +static irqreturn_t cdns_irq_handler(int irq, void *_usb_ss); +#endif +static int usb_ss_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc); +static int usb_ss_gadget_ep0_disable(struct usb_ep *ep); +static int usb_ss_gadget_ep0_set_halt(struct usb_ep *ep, int value); +static int usb_ss_gadget_ep0_queue(struct usb_ep *ep, + struct usb_request *request, gfp_t gfp_flags); +static void cdns_ep_config(struct usb_ss_endpoint *usb_ss_ep, int index); +static int usb_ss_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc); +static int usb_ss_gadget_ep_disable(struct usb_ep *ep); +static struct usb_request *usb_ss_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags); +static void usb_ss_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request); +static int usb_ss_gadget_ep_queue(struct usb_ep *ep, + struct usb_request *request, gfp_t gfp_flags); +static int usb_ss_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request); +static int usb_ss_gadget_ep_set_halt(struct usb_ep *ep, int value); +static int usb_ss_gadget_ep_set_wedge(struct usb_ep *ep); +static int usb_ss_gadget_get_frame(struct usb_gadget *gadget); +static int usb_ss_gadget_wakeup(struct usb_gadget *gadget); +static int usb_ss_gadget_set_selfpowered(struct usb_gadget *gadget, + int is_selfpowered); +static int usb_ss_gadget_pullup(struct usb_gadget *gadget, int is_on); +static int usb_ss_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +static int usb_ss_gadget_udc_stop(struct usb_gadget *gadget); +static int usb_ss_init_ep(struct usb_ss_dev *usb_ss); +static int usb_ss_init_ep0(struct usb_ss_dev *usb_ss); + +static u32 gadget_readl(struct usb_ss_dev *usb_ss, uint32_t __iomem *reg) +{ + return cdns_readl(reg); +} + +static void gadget_writel(struct usb_ss_dev *usb_ss, + uint32_t __iomem *reg, u32 value) +{ + cdns_writel(reg, value); +} + +/** + * next_request - returns next request from list + * @list: list containing requests + * + * Retuns request or NULL if no requests in list + */ +static struct usb_request *next_request(struct list_head *list) +{ + if (list_empty(list)) + return NULL; + return list_first_entry(list, struct usb_request, list); +} + +/** + * select_ep - selects endpoint + * @usb_ss: extended gadget object + * @ep: endpoint address + */ +static void select_ep(struct usb_ss_dev *usb_ss, u32 ep) +{ + if (!usb_ss || !usb_ss->regs) { + dev_err(&usb_ss->dev, "Failed to select endpoint!\n"); + return; + } + + gadget_writel(usb_ss, &usb_ss->regs->ep_sel, ep); +} + +/** + *usb_ss_allocate_trb_pool - Allocates TRB's pool for selected endpoint + * @usb_ss_ep: extended endpoint object + * + * Function will return 0 on success or -ENOMEM on allocation error + */ +static int usb_ss_allocate_trb_pool(struct usb_ss_endpoint *usb_ss_ep) +{ + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + + if (usb_ss_ep->trb_pool) + return 0; + + usb_ss_ep->trb_pool = dma_zalloc_coherent(usb_ss->sysdev, + sizeof(struct usb_ss_trb) * USB_SS_TRBS_NUM, + &usb_ss_ep->trb_pool_dma, GFP_DMA); + + if (!usb_ss_ep->trb_pool) { + dev_err(&usb_ss->dev, + "Failed to allocate TRB pool for endpoint %s\n", + usb_ss_ep->name); + return -ENOMEM; + } + + return 0; +} + +/** + * cdns_ep_stall_flush - Stalls and flushes selected endpoint + * @usb_ss_ep: extended endpoint object + * + * Endpoint must be selected before call to this function + */ +static void cdns_ep_stall_flush(struct usb_ss_endpoint *usb_ss_ep) +{ + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__DFLUSH__MASK | EP_CMD__ERDY__MASK | + EP_CMD__SSTALL__MASK); + + /* wait for DFLUSH cleared */ + while (gadget_readl(usb_ss, + &usb_ss->regs->ep_cmd) & EP_CMD__DFLUSH__MASK) + ; + + usb_ss_ep->stalled_flag = 1; +} + +/** + * cdns_ep0_config - Configures default endpoint + * @usb_ss: extended gadget object + * + * Functions sets parameters: maximal packet size and enables interrupts + */ +static void cdns_ep0_config(struct usb_ss_dev *usb_ss) +{ + u32 max_packet_size = 0; + + switch (usb_ss->gadget.speed) { + case USB_SPEED_UNKNOWN: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_0; + break; + + case USB_SPEED_LOW: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_8; + break; + + case USB_SPEED_FULL: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_64; + break; + + case USB_SPEED_HIGH: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_64; + break; + + case USB_SPEED_WIRELESS: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_64; + break; + + case USB_SPEED_SUPER: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_512; + break; + + case USB_SPEED_SUPER_PLUS: + dev_warn(&usb_ss->dev, "USB 3.1 is not supported\n"); + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_512; + break; + } + + /* init ep out */ + select_ep(usb_ss, USB_DIR_OUT); + + gadget_writel(usb_ss, &usb_ss->regs->ep_cfg, + EP_CFG__ENABLE__MASK | + EP_CFG__MAXPKTSIZE__WRITE(max_packet_size)); + gadget_writel(usb_ss, &usb_ss->regs->ep_sts_en, + EP_STS_EN__SETUPEN__MASK | + EP_STS_EN__DESCMISEN__MASK | + EP_STS_EN__TRBERREN__MASK); + + /* init ep in */ + select_ep(usb_ss, USB_DIR_IN); + + gadget_writel(usb_ss, &usb_ss->regs->ep_cfg, + EP_CFG__ENABLE__MASK | + EP_CFG__MAXPKTSIZE__WRITE(max_packet_size)); + gadget_writel(usb_ss, &usb_ss->regs->ep_sts_en, + EP_STS_EN__SETUPEN__MASK | + EP_STS_EN__TRBERREN__MASK); +} + +/** + * cdns_gadget_unconfig - Unconfigures device controller + * @usb_ss: extended gadget object + */ +static void cdns_gadget_unconfig(struct usb_ss_dev *usb_ss) +{ + /* RESET CONFIGURATION */ + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__CFGRST__MASK); + + usb_ss->hw_configured_flag = 0; +} + +/** + * cdns_ep0_run_transfer - Do transfer on default endpoint hardware + * @usb_ss: 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 cdns_ep0_run_transfer(struct usb_ss_dev *usb_ss, + dma_addr_t dma_addr, unsigned int length, int erdy) +{ + usb_ss->trb_ep0[0] = TRB_SET_DATA_BUFFER_POINTER(dma_addr); + usb_ss->trb_ep0[1] = TRB_SET_TRANSFER_LENGTH((u32)length); + usb_ss->trb_ep0[2] = TRB_SET_CYCLE_BIT | + TRB_SET_INT_ON_COMPLETION | TRB_TYPE_NORMAL; + + dev_dbg(&usb_ss->dev, "DRBL(%02X)\n", + usb_ss->ep0_data_dir ? USB_DIR_IN : USB_DIR_OUT); + + select_ep(usb_ss, usb_ss->ep0_data_dir + ? USB_DIR_IN : USB_DIR_OUT); + + gadget_writel(usb_ss, &usb_ss->regs->ep_traddr, + EP_TRADDR__TRADDR__WRITE(usb_ss->trb_ep0_dma)); + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__DRDY__MASK); /* drbl */ + + if (erdy) + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK); +} + +/** + * cdns_ep_run_transfer - Do transfer on no-default endpoint hardware + * @usb_ss: extended gadget object + */ +static int cdns_ep_run_transfer(struct usb_ss_endpoint *usb_ss_ep) +{ + dma_addr_t trb_dma; + struct usb_request *request = next_request(&usb_ss_ep->request_list); + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + int sg_iter = 0; + struct usb_ss_trb *trb; + + if (request == NULL) + return -EINVAL; + + if (request->num_sgs > USB_SS_TRBS_NUM) + return -EINVAL; + + dev_dbg(&usb_ss->dev, "DRBL(%02X)\n", + usb_ss_ep->endpoint.desc->bEndpointAddress); + + usb_ss_ep->hw_pending_flag = 1; + trb_dma = request->dma; + + /* must allocate buffer aligned to 8 */ + if (request->dma % ADDR_MODULO_8) { + memcpy(usb_ss_ep->cpu_addr, request->buf, request->length); + trb_dma = usb_ss_ep->dma_addr; + } + + trb = usb_ss_ep->trb_pool; + + do { + /* fill TRB */ + trb->offset0 = TRB_SET_DATA_BUFFER_POINTER(request->num_sgs == 0 + ? trb_dma : request->sg[sg_iter].dma_address); + + trb->offset4 = TRB_SET_BURST_LENGTH(16) | + TRB_SET_TRANSFER_LENGTH(request->num_sgs == 0 ? + request->length : request->sg[sg_iter].length); + + trb->offset8 = TRB_SET_CYCLE_BIT + | TRB_SET_INT_ON_COMPLETION + | TRB_SET_INT_ON_SHORT_PACKET + | TRB_TYPE_NORMAL; + + ++sg_iter; + ++trb; + + } while (sg_iter < request->num_sgs); + + /* arm transfer on selected endpoint */ + select_ep(usb_ss_ep->usb_ss, + usb_ss_ep->endpoint.desc->bEndpointAddress); + + gadget_writel(usb_ss, &usb_ss->regs->ep_traddr, + EP_TRADDR__TRADDR__WRITE(usb_ss_ep->trb_pool_dma)); + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__DRDY__MASK); /* DRDY */ + return 0; +} + +/** + * cdns_get_setup_ret - Returns status of handling setup packet + * Setup is handled by gadget driver + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + */ +static int cdns_get_setup_ret(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + int ret; + + spin_unlock(&usb_ss->lock); + usb_ss->setup_pending = 1; + ret = usb_ss->gadget_driver->setup(&usb_ss->gadget, ctrl_req); + usb_ss->setup_pending = 0; + spin_lock(&usb_ss->lock); + return ret; +} + +/** + * cdns_req_ep0_set_address - Handling of SET_ADDRESS standard USB request + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns_req_ep0_set_address(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state device_state = usb_ss->gadget.state; + u32 reg; + u32 addr; + + addr = le16_to_cpu(ctrl_req->wValue); + + if (addr > DEVICE_ADDRESS_MAX) { + dev_err(&usb_ss->dev, + "Device address (%d) cannot be greater than %d\n", + addr, DEVICE_ADDRESS_MAX); + return -EINVAL; + } + + if (device_state == USB_STATE_CONFIGURED) { + dev_err(&usb_ss->dev, "USB device already configured\n"); + return -EINVAL; + } + + reg = gadget_readl(usb_ss, &usb_ss->regs->usb_cmd); + + gadget_writel(usb_ss, &usb_ss->regs->usb_cmd, reg + | USB_CMD__FADDR__WRITE(addr) + | USB_CMD__SET_ADDR__MASK); + + usb_gadget_set_state(&usb_ss->gadget, + (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); + + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | EP_CMD__REQ_CMPL__MASK); + return 0; +} + +/** + * cdns_req_ep0_set_address - Handling of GET_STATUS standard USB request + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns_req_ep0_get_status(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + u16 usb_status = 0; + unsigned int length = 2; + u32 recip = ctrl_req->bRequestType & USB_RECIP_MASK; + u32 reg; + + switch (recip) { + + case USB_RECIP_DEVICE: + /* handling otg features */ + if (ctrl_req->wIndex == OTG_STS_SELECTOR) { + length = 1; + usb_status = usb_ss->gadget.host_request_flag; + } else { + + reg = gadget_readl(usb_ss, &usb_ss->regs->usb_sts); + + if (reg & USB_STS__U1ENS__MASK) + usb_status |= 1uL << USB_DEV_STAT_U1_ENABLED; + + if (reg & USB_STS__U2ENS__MASK) + usb_status |= 1uL << USB_DEV_STAT_U2_ENABLED; + + if (usb_ss->wake_up_flag) + usb_status |= 1uL << USB_DEVICE_REMOTE_WAKEUP; + + /* self powered */ + usb_status |= 1uL << USB_DEVICE_SELF_POWERED; + } + break; + + case USB_RECIP_INTERFACE: + return cdns_get_setup_ret(usb_ss, ctrl_req); + + case USB_RECIP_ENDPOINT: + /* check if endpoint is stalled */ + select_ep(usb_ss, ctrl_req->wIndex); + if (gadget_readl(usb_ss, &usb_ss->regs->ep_sts) + & EP_STS__STALL__MASK) + usb_status = 1; + break; + + default: + return -EINVAL; + } + + *(u16 *)usb_ss->setup = cpu_to_le16(usb_status); + + usb_ss->actual_ep0_request = NULL; + cdns_ep0_run_transfer(usb_ss, usb_ss->setup_dma, length, 1); + return 0; +} + +/** + * cdns_req_ep0_set_address - Handling of GET/SET_FEATURE standard USB request + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + * @set: must be set to 1 for SET_FEATURE request + * + * Returns 0 if success, error code on error + */ +static int cdns_req_ep0_handle_feature(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req, int set) +{ + u32 recip = ctrl_req->bRequestType & USB_RECIP_MASK; + struct usb_ss_endpoint *usb_ss_ep; + u32 reg; + + switch (recip) { + + case USB_RECIP_DEVICE: + + switch (ctrl_req->wValue) { + + case USB_DEVICE_U1_ENABLE: + if (usb_ss->gadget.state != USB_STATE_CONFIGURED) + return -EINVAL; + if (usb_ss->gadget.speed != USB_SPEED_SUPER) + return -EINVAL; + + reg = gadget_readl(usb_ss, &usb_ss->regs->usb_conf); + if (set) + /* set U1EN */ + reg |= USB_CONF__U1EN__MASK; + else + /* set U1 disable */ + reg |= USB_CONF__U1DS__MASK; + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, reg); + break; + + case USB_DEVICE_U2_ENABLE: + if (usb_ss->gadget.state != USB_STATE_CONFIGURED) + return -EINVAL; + if (usb_ss->gadget.speed != USB_SPEED_SUPER) + return -EINVAL; + + reg = gadget_readl(usb_ss, &usb_ss->regs->usb_conf); + if (set) + /* set U2EN */ + reg |= USB_CONF__U2EN__MASK; + else + /* set U2 disable */ + reg |= USB_CONF__U2DS__MASK; + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, reg); + break; + + case USB_DEVICE_A_ALT_HNP_SUPPORT: + break; + + case USB_DEVICE_A_HNP_SUPPORT: + break; + + case USB_DEVICE_B_HNP_ENABLE: + if (!usb_ss->gadget.b_hnp_enable && set) + usb_ss->gadget.b_hnp_enable = 1; + break; + + case USB_DEVICE_REMOTE_WAKEUP: + usb_ss->wake_up_flag = !!set; + break; + + default: + return -EINVAL; + + } + break; + + case USB_RECIP_INTERFACE: + return cdns_get_setup_ret(usb_ss, ctrl_req); + + case USB_RECIP_ENDPOINT: + select_ep(usb_ss, ctrl_req->wIndex); + + if (set) { + /* set stall */ + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__SSTALL__MASK); + + /* handle non zero endpoint software endpoint */ + if (ctrl_req->wIndex & 0x7F) { + usb_ss_ep = usb_ss->eps[CAST_EP_ADDR_TO_INDEX( + ctrl_req->wIndex)]; + usb_ss_ep->stalled_flag = 1; + } + } else { + struct usb_request *request; + + if (ctrl_req->wIndex & 0x7F) { + if (usb_ss->eps[CAST_EP_ADDR_TO_INDEX( + ctrl_req->wIndex)]->wedge_flag) + goto jmp_wedge; + } + + /* clear stall */ + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__CSTALL__MASK | EP_CMD__EPRST__MASK); + /* wait for EPRST cleared */ + while (gadget_readl(usb_ss, &usb_ss->regs->ep_cmd) + & EP_CMD__EPRST__MASK) + ; + + /* handle non zero endpoint software endpoint */ + if (ctrl_req->wIndex & 0x7F) { + usb_ss_ep = usb_ss->eps[CAST_EP_ADDR_TO_INDEX( + ctrl_req->wIndex)]; + usb_ss_ep->stalled_flag = 0; + + request = next_request( + &usb_ss_ep->request_list); + if (request) + cdns_ep_run_transfer(usb_ss_ep); + } + } +jmp_wedge: + select_ep(usb_ss, 0x00); + break; + + default: + return -EINVAL; + } + + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | EP_CMD__REQ_CMPL__MASK); + + return 0; +} + +/** + * cdns_req_ep0_set_sel - Handling of SET_SEL standard USB request + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns_req_ep0_set_sel(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + if (usb_ss->gadget.state < USB_STATE_ADDRESS) + return -EINVAL; + + if (ctrl_req->wLength != 6) { + dev_err(&usb_ss->dev, "Set SEL should be 6 bytes, got %d\n", + ctrl_req->wLength); + return -EINVAL; + } + + usb_ss->ep0_data_dir = 0; + usb_ss->actual_ep0_request = NULL; + cdns_ep0_run_transfer(usb_ss, usb_ss->setup_dma, 6, 1); + + return 0; +} + +/** + * cdns_req_ep0_set_isoch_delay - + * Handling of GET_ISOCH_DELAY standard USB request + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns_req_ep0_set_isoch_delay(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + if (ctrl_req->wIndex || ctrl_req->wLength) + return -EINVAL; + + usb_ss->isoch_delay = ctrl_req->wValue; + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | EP_CMD__REQ_CMPL__MASK); + return 0; +} + +/** + * cdns_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, 0x7FFF on deferred status stage, error code on error + */ +static int cdns_req_ep0_set_configuration(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state device_state = usb_ss->gadget.state; + u32 config = le16_to_cpu(ctrl_req->wValue); + struct usb_ep *ep; + int i, result = 0; + + switch (device_state) { + + case USB_STATE_ADDRESS: + + if (config) { + for (i = 0; i < usb_ss->ep_nums; i++) + cdns_ep_config(usb_ss->eps[i], i); + } +#ifdef CDNS_THREADED_IRQ_HANDLING + usb_ss->ep_ien = gadget_readl(usb_ss, &usb_ss->regs->ep_ien) + | EP_IEN__EOUTEN0__MASK | EP_IEN__EINEN0__MASK; +#endif + result = cdns_get_setup_ret(usb_ss, ctrl_req); + + if (result != 0) + return result; + + if (config) { + + if (!usb_ss->hw_configured_flag) { + /* SET CONFIGURATION */ + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__CFGSET__MASK); + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | + EP_CMD__REQ_CMPL__MASK); + /* wait until configuration set */ + while (!(gadget_readl(usb_ss, + &usb_ss->regs->usb_sts) + & USB_STS__CFGSTS__MASK)) + ; + usb_ss->hw_configured_flag = 1; + + list_for_each_entry(ep, + &usb_ss->gadget.ep_list, + ep_list) { + if (ep->enabled) + cdns_ep_run_transfer( + to_usb_ss_ep(ep)); + } + } + } else { + cdns_gadget_unconfig(usb_ss); + for (i = 0; i < usb_ss->ep_nums; i++) + usb_ss->eps[i]->endpoint.enabled = 0; + usb_gadget_set_state(&usb_ss->gadget, + USB_STATE_ADDRESS); + } + break; + + case USB_STATE_CONFIGURED: + result = cdns_get_setup_ret(usb_ss, ctrl_req); + if (!config && !result) { + cdns_gadget_unconfig(usb_ss); + for (i = 0; i < usb_ss->ep_nums; i++) + usb_ss->eps[i]->endpoint.enabled = 0; + usb_gadget_set_state(&usb_ss->gadget, + USB_STATE_ADDRESS); + } + break; + + default: + result = -EINVAL; + } + + return result; +} + +/** + * cdns_ep0_standard_request - Handling standard USB requests + * @usb_ss: extended gadget object + * @ctrl_req: pointer to received setup packet + */ +static int cdns_ep0_standard_request(struct usb_ss_dev *usb_ss, + struct usb_ctrlrequest *ctrl_req) +{ + switch (ctrl_req->bRequest) { + case USB_REQ_SET_ADDRESS: + return cdns_req_ep0_set_address(usb_ss, ctrl_req); + case USB_REQ_SET_CONFIGURATION: + return cdns_req_ep0_set_configuration(usb_ss, ctrl_req); + case USB_REQ_GET_STATUS: + return cdns_req_ep0_get_status(usb_ss, ctrl_req); + case USB_REQ_CLEAR_FEATURE: + return cdns_req_ep0_handle_feature(usb_ss, ctrl_req, 0); + case USB_REQ_SET_FEATURE: + return cdns_req_ep0_handle_feature(usb_ss, ctrl_req, 1); + case USB_REQ_SET_SEL: + return cdns_req_ep0_set_sel(usb_ss, ctrl_req); + case USB_REQ_SET_ISOCH_DELAY: + return cdns_req_ep0_set_isoch_delay(usb_ss, ctrl_req); + default: + return cdns_get_setup_ret(usb_ss, ctrl_req); + } +} + +/** + * cdns_ep0_setup_phase - Handling setup USB requests + * @usb_ss: extended gadget object + */ +static void cdns_ep0_setup_phase(struct usb_ss_dev *usb_ss) +{ + int result; + struct usb_ctrlrequest *ctrl_req = + (struct usb_ctrlrequest *)usb_ss->setup; + + if ((ctrl_req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + result = cdns_ep0_standard_request(usb_ss, ctrl_req); + else + result = cdns_get_setup_ret(usb_ss, ctrl_req); + + if (result != 0 && result != USB_GADGET_DELAYED_STATUS) { + dev_dbg(&usb_ss->dev, "STALL(00) %d\n", result); + + /* set_stall on ep0 */ + select_ep(usb_ss, 0x00); + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__SSTALL__MASK); + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | EP_CMD__REQ_CMPL__MASK); + return; + } +} + +/** + * cdns_check_ep_interrupt_proceed - Processes interrupt related to endpoint + * @usb_ss_ep: extended endpoint object + */ +static int cdns_check_ep_interrupt_proceed(struct usb_ss_endpoint *usb_ss_ep) +{ + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + struct usb_request *request; + u32 ep_sts_reg; + + select_ep(usb_ss, usb_ss_ep->endpoint.address); + ep_sts_reg = gadget_readl(usb_ss, &usb_ss->regs->ep_sts); + + dev_dbg(&usb_ss->dev, "EP_STS: %08X\n", ep_sts_reg); + + if (ep_sts_reg & EP_STS__TRBERR__MASK) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__TRBERR__MASK); + + dev_dbg(&usb_ss->dev, "TRBERR(%02X)\n", + usb_ss_ep->endpoint.desc->bEndpointAddress); + } + + if (ep_sts_reg & EP_STS__ISOERR__MASK) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__ISOERR__MASK); + dev_dbg(&usb_ss->dev, "ISOERR(%02X)\n", + usb_ss_ep->endpoint.desc->bEndpointAddress); + } + + if (ep_sts_reg & EP_STS__OUTSMM__MASK) { + gadget_writel(usb_ss, &usb_ss->regs->ep_sts, + EP_STS__OUTSMM__MASK); + dev_dbg(&usb_ss->dev, "OUTSMM(%02X)\n", + usb_ss_ep->endpoint.desc->bEndpointAddress); + } + + if (ep_sts_reg & EP_STS__NRDY__MASK) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__NRDY__MASK); + dev_dbg(&usb_ss->dev, "NRDY(%02X)\n", + usb_ss_ep->endpoint.desc->bEndpointAddress); + } + + if ((ep_sts_reg & EP_STS__IOC__MASK) + || (ep_sts_reg & EP_STS__ISP__MASK)) { + gadget_writel(usb_ss, &usb_ss->regs->ep_sts, + EP_STS__IOC__MASK | EP_STS__ISP__MASK); + + /* get just completed request */ + request = next_request(&usb_ss_ep->request_list); + usb_gadget_unmap_request_by_dev(usb_ss->sysdev, request, + usb_ss_ep->endpoint.desc->bEndpointAddress + & ENDPOINT_DIR_MASK); + + request->status = 0; + request->actual = + le32_to_cpu(((u32 *) usb_ss_ep->trb_pool)[1]) + & ACTUAL_TRANSFERRED_BYTES_MASK; + + dev_dbg(&usb_ss->dev, "IOC(%02X) %d\n", + usb_ss_ep->endpoint.desc->bEndpointAddress, + request->actual); + + list_del(&request->list); + + usb_ss_ep->hw_pending_flag = 0; + if (request->complete) { + spin_unlock(&usb_ss->lock); + usb_gadget_giveback_request(&usb_ss_ep->endpoint, + request); + spin_lock(&usb_ss->lock); + } + + /* handle deferred STALL */ + if (usb_ss_ep->stalled_flag) { + cdns_ep_stall_flush(usb_ss_ep); + return 0; + } + + /* exit if hardware transfer already started */ + if (usb_ss_ep->hw_pending_flag) + return 0; + + /* if any request queued run it! */ + if (!list_empty(&usb_ss_ep->request_list)) + cdns_ep_run_transfer(usb_ss_ep); + } + + if (ep_sts_reg & EP_STS__DESCMIS__MASK) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__DESCMIS__MASK); + dev_dbg(&usb_ss->dev, "DESCMIS(%02X)\n", + usb_ss_ep->endpoint.desc->bEndpointAddress); + } + + return 0; +} + +/** + * cdns_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0 + * @usb_ss: extended gadget object + * @dir: 1 for IN direction, 0 for OUT direction + */ +static void cdns_check_ep0_interrupt_proceed(struct usb_ss_dev *usb_ss, int dir) +{ + u32 ep_sts_reg; + int i; + + select_ep(usb_ss, 0 | (dir ? USB_DIR_IN : USB_DIR_OUT)); + ep_sts_reg = gadget_readl(usb_ss, &usb_ss->regs->ep_sts); + + dev_dbg(&usb_ss->dev, "EP_STS: %08X\n", ep_sts_reg); + + if ((ep_sts_reg & EP_STS__SETUP__MASK) && (dir == 0)) { + dev_dbg(&usb_ss->dev, "SETUP(%02X)\n", 0x00); + + gadget_writel(usb_ss, &usb_ss->regs->ep_sts, + EP_STS__SETUP__MASK | + EP_STS__IOC__MASK | EP_STS__ISP__MASK); + + dev_dbg(&usb_ss->dev, "SETUP: "); + for (i = 0; i < 8; i++) + dev_dbg(&usb_ss->dev, "%02X ", usb_ss->setup[i]); + dev_dbg(&usb_ss->dev, "\nSTATE: %d\n", usb_ss->gadget.state); + usb_ss->ep0_data_dir = usb_ss->setup[0] & USB_DIR_IN; + cdns_ep0_setup_phase(usb_ss); + ep_sts_reg &= ~(EP_STS__SETUP__MASK | + EP_STS__IOC__MASK | + EP_STS__ISP__MASK); + } + + if (ep_sts_reg & EP_STS__TRBERR__MASK) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__TRBERR__MASK); + dev_dbg(&usb_ss->dev, "TRBERR(%02X)\n", + dir ? USB_DIR_IN : USB_DIR_OUT); + } + + if (ep_sts_reg & EP_STS__DESCMIS__MASK) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__DESCMIS__MASK); + + dev_dbg(&usb_ss->dev, "DESCMIS(%02X)\n", + dir ? USB_DIR_IN : USB_DIR_OUT); + + if (dir == 0 && !usb_ss->setup_pending) { + usb_ss->ep0_data_dir = 0; + cdns_ep0_run_transfer(usb_ss, + usb_ss->setup_dma, 8, 0); + } + } + + if ((ep_sts_reg & EP_STS__IOC__MASK) + || (ep_sts_reg & EP_STS__ISP__MASK)) { + gadget_writel(usb_ss, + &usb_ss->regs->ep_sts, EP_STS__IOC__MASK); + gadget_writel(usb_ss, + &usb_ss->regs->ep_cmd, EP_CMD__REQ_CMPL__MASK); + + if (usb_ss->actual_ep0_request) { + usb_gadget_unmap_request_by_dev(usb_ss->sysdev, + usb_ss->actual_ep0_request, + usb_ss->ep0_data_dir); + + usb_ss->actual_ep0_request->actual = + le32_to_cpu((usb_ss->trb_ep0)[1]) + & ACTUAL_TRANSFERRED_BYTES_MASK; + + dev_dbg(&usb_ss->dev, "IOC(%02X) %d\n", + dir ? USB_DIR_IN : USB_DIR_OUT, + usb_ss->actual_ep0_request->actual); + } + + if (usb_ss->actual_ep0_request + && usb_ss->actual_ep0_request->complete) { + spin_unlock(&usb_ss->lock); + usb_ss->actual_ep0_request->complete(usb_ss->gadget.ep0, + usb_ss->actual_ep0_request); + spin_lock(&usb_ss->lock); + } + } +} + +/** + * cdns_check_usb_interrupt_proceed - Processes interrupt related to device + * @usb_ss: extended gadget object + * @usb_ists: bitmap representation of device's reported interrupts + * (usb_ists register value) + */ +static void cdns_check_usb_interrupt_proceed(struct usb_ss_dev *usb_ss, + u32 usb_ists) +{ + int interrupt_bit = ffs(usb_ists) - 1; + int speed; + u32 val; + + dev_dbg(&usb_ss->dev, "USB interrupt detected\n"); + + switch (interrupt_bit) { + case USB_ISTS__CON2I__SHIFT: + /* FS/HS Connection detected */ + dev_dbg(&usb_ss->dev, + "[Interrupt] FS/HS Connection detected\n"); + val = gadget_readl(usb_ss, &usb_ss->regs->usb_sts); + speed = USB_STS__USBSPEED__READ(val); + if (speed == USB_SPEED_WIRELESS) + speed = USB_SPEED_SUPER; + dev_dbg(&usb_ss->dev, "Speed value: %s (%d), usbsts:0x%x\n", + usb_speed_string(speed), speed, val); + usb_ss->gadget.speed = speed; + usb_ss->is_connected = 1; + usb_gadget_set_state(&usb_ss->gadget, USB_STATE_POWERED); + cdns_ep0_config(usb_ss); + break; + + case USB_ISTS__CONI__SHIFT: + /* SS Connection detected */ + dev_dbg(&usb_ss->dev, "[Interrupt] SS Connection detected\n"); + val = gadget_readl(usb_ss, &usb_ss->regs->usb_sts); + speed = USB_STS__USBSPEED__READ(val); + if (speed == USB_SPEED_WIRELESS) + speed = USB_SPEED_SUPER; + dev_dbg(&usb_ss->dev, "Speed value: %s (%d), usbsts:0x%x\n", + usb_speed_string(speed), speed, val); + usb_ss->gadget.speed = speed; + usb_ss->is_connected = 1; + usb_gadget_set_state(&usb_ss->gadget, USB_STATE_POWERED); + cdns_ep0_config(usb_ss); + break; + + case USB_ISTS__DIS2I__SHIFT: + case USB_ISTS__DISI__SHIFT: + /* SS Disconnection detected */ + val = gadget_readl(usb_ss, &usb_ss->regs->usb_sts); + dev_dbg(&usb_ss->dev, + "[Interrupt] Disconnection detected: usbsts:0x%x\n", + val); + if (usb_ss->gadget_driver + && usb_ss->gadget_driver->disconnect) { + + spin_unlock(&usb_ss->lock); + usb_ss->gadget_driver->disconnect(&usb_ss->gadget); + spin_lock(&usb_ss->lock); + } + usb_ss->gadget.speed = USB_SPEED_UNKNOWN; + usb_gadget_set_state(&usb_ss->gadget, USB_STATE_NOTATTACHED); + usb_ss->is_connected = 0; + cdns_gadget_unconfig(usb_ss); + break; + + case USB_ISTS__L2ENTI__SHIFT: + dev_dbg(&usb_ss->dev, + "[Interrupt] Device suspended\n"); + break; + + case USB_ISTS__L2EXTI__SHIFT: + dev_dbg(&usb_ss->dev, "[Interrupt] L2 exit detected\n"); + /* + * Exit from standby mode + * on L2 exit (Suspend in HS/FS or SS) + */ + break; + case USB_ISTS__U3EXTI__SHIFT: + /* + * Exit from standby mode + * on U3 exit (Suspend in HS/FS or SS) + */ + dev_dbg(&usb_ss->dev, "[Interrupt] U3 exit detected\n"); + break; + + /* resets cases */ + case USB_ISTS__UWRESI__SHIFT: + case USB_ISTS__UHRESI__SHIFT: + case USB_ISTS__U2RESI__SHIFT: + dev_dbg(&usb_ss->dev, "[Interrupt] Reset detected\n"); + speed = USB_STS__USBSPEED__READ( + gadget_readl(usb_ss, &usb_ss->regs->usb_sts)); + if (speed == USB_SPEED_WIRELESS) + speed = USB_SPEED_SUPER; + usb_gadget_set_state(&usb_ss->gadget, USB_STATE_DEFAULT); + usb_ss->gadget.speed = speed; + cdns_gadget_unconfig(usb_ss); + cdns_ep0_config(usb_ss); + break; + default: + break; + } + + /* Clear interrupt bit */ + gadget_writel(usb_ss, &usb_ss->regs->usb_ists, (1uL << interrupt_bit)); +} + +#ifdef CDNS_THREADED_IRQ_HANDLING +static irqreturn_t cdns_irq_handler(int irq, void *_usb_ss) +{ + struct usb_ss_dev *usb_ss = _usb_ss; + + usb_ss->usb_ien = gadget_readl(usb_ss, &usb_ss->regs->usb_ien); + usb_ss->ep_ien = gadget_readl(usb_ss, &usb_ss->regs->ep_ien); + + if (!gadget_readl(usb_ss, &usb_ss->regs->usb_ists) + && !gadget_readl(usb_ss, &usb_ss->regs->ep_ists)) { + dev_dbg(&usb_ss->dev, "--BUBBLE INTERRUPT 0 !!!\n"); + if (gadget_readl(usb_ss, &usb_ss->regs->usb_sts) & + USB_STS__CFGSTS__MASK) + return IRQ_HANDLED; + return IRQ_NONE; + } + + gadget_writel(usb_ss, &usb_ss->regs->usb_ien, 0); + gadget_writel(usb_ss, &usb_ss->regs->ep_ien, 0); + + gadget_readl(usb_ss, &usb_ss->regs->dma_axi_ctrl); + return IRQ_WAKE_THREAD; +} +#endif + +/** + * cdns_irq_handler - irq line interrupt handler + * @irq: interrupt line number + * @_usb_ss: pointer to extended gadget object + * + * Returns IRQ_HANDLED when interrupt raised by USBSS_DEV, + * IRQ_NONE when interrupt raised by other device connected + * to the irq line + */ +static irqreturn_t cdns_irq_handler_thread(struct cdns3 *cdns) +{ + struct usb_ss_dev *usb_ss = + container_of(cdns->gadget_dev, struct usb_ss_dev, dev); + u32 reg; + enum irqreturn ret = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&usb_ss->lock, flags); + + /* check USB device interrupt */ + reg = gadget_readl(usb_ss, &usb_ss->regs->usb_ists); + if (reg) { + dev_dbg(&usb_ss->dev, "usb_ists: %08X\n", reg); + cdns_check_usb_interrupt_proceed(usb_ss, reg); + ret = IRQ_HANDLED; + } + + /* check endpoint interrupt */ + reg = gadget_readl(usb_ss, &usb_ss->regs->ep_ists); + if (reg != 0) { + dev_dbg(&usb_ss->dev, "ep_ists: %08X\n", reg); + } else { + if (gadget_readl(usb_ss, &usb_ss->regs->usb_sts) & + USB_STS__CFGSTS__MASK) + ret = IRQ_HANDLED; + goto irqend; + } + + /* handle default endpoint OUT */ + if (reg & EP_ISTS__EOUT0__MASK) { + cdns_check_ep0_interrupt_proceed(usb_ss, 0); + ret = IRQ_HANDLED; + } + + /* handle default endpoint IN */ + if (reg & EP_ISTS__EIN0__MASK) { + cdns_check_ep0_interrupt_proceed(usb_ss, 1); + ret = IRQ_HANDLED; + } + + /* check if interrupt from non default endpoint, if no exit */ + reg &= ~(EP_ISTS__EOUT0__MASK | EP_ISTS__EIN0__MASK); + if (!reg) + goto irqend; + + do { + unsigned int bit_pos = ffs(reg); + u32 bit_mask = 1 << (bit_pos - 1); + + dev_dbg(&usb_ss->dev, "Interrupt on index: %d bitmask %08X\n", + CAST_EP_REG_POS_TO_INDEX(bit_pos), bit_mask); + cdns_check_ep_interrupt_proceed( + usb_ss->eps[CAST_EP_REG_POS_TO_INDEX(bit_pos)]); + reg &= ~bit_mask; + } while (reg); + +irqend: + + spin_unlock_irqrestore(&usb_ss->lock, flags); +#ifdef CDNS_THREADED_IRQ_HANDLING + local_irq_save(flags); + gadget_writel(usb_ss, &usb_ss->regs->usb_ien, usb_ss->usb_ien); + gadget_writel(usb_ss, &usb_ss->regs->ep_ien, usb_ss->ep_ien); + local_irq_restore(flags); +#endif + return ret; +} + +/** + * usb_ss_gadget_ep0_enable + * Function shouldn't be called by gadget driver, + * endpoint 0 is allways active + */ +static int usb_ss_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +/** + * usb_ss_gadget_ep0_disable + * Function shouldn't be called by gadget driver, + * endpoint 0 is allways active + */ +static int usb_ss_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/** + * usb_ss_gadget_ep0_set_halt + * @ep: pointer to endpoint zero object + * @value: 1 for set stall, 0 for clear stall + * + * Returns 0 + */ +static int usb_ss_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + /* TODO */ + return 0; +} + +/** + * usb_ss_gadget_ep0_queue Transfer data on endpoint zero + * @ep: pointer to endpoint zero object + * @request: pointer to request object + * @gfp_flags: gfp flags + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep0_queue(struct usb_ep *ep, + struct usb_request *request, gfp_t gfp_flags) +{ + int ret; + unsigned long flags; + int erdy_sent = 0; + /* get extended endpoint */ + struct usb_ss_endpoint *usb_ss_ep = + to_usb_ss_ep(ep); + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + + dev_dbg(&usb_ss->dev, "QUEUE(%02X) %d\n", + usb_ss->ep0_data_dir ? USB_DIR_IN : USB_DIR_OUT, + request->length); + + /* send STATUS stage */ + if (request->length == 0 && request->zero == 0) { + spin_lock_irqsave(&usb_ss->lock, flags); + select_ep(usb_ss, 0x00); + if (!usb_ss->hw_configured_flag) { + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__CFGSET__MASK); /* SET CONFIGURATION */ + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | EP_CMD__REQ_CMPL__MASK); + /* wait until configuration set */ + while (!(gadget_readl(usb_ss, &usb_ss->regs->usb_sts) + & USB_STS__CFGSTS__MASK)) + ; + erdy_sent = 1; + usb_ss->hw_configured_flag = 1; + + list_for_each_entry(ep, + &usb_ss->gadget.ep_list, + ep_list) { + + if (ep->enabled) + cdns_ep_run_transfer( + to_usb_ss_ep(ep)); + } + } + if (!erdy_sent) + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__ERDY__MASK | EP_CMD__REQ_CMPL__MASK); + if (request->complete) + request->complete(usb_ss->gadget.ep0, request); + spin_unlock_irqrestore(&usb_ss->lock, flags); + return 0; + } + + spin_lock_irqsave(&usb_ss->lock, flags); + ret = usb_gadget_map_request_by_dev(usb_ss->sysdev, request, + usb_ss->ep0_data_dir); + if (ret) { + dev_err(&usb_ss->dev, "failed to map request\n"); + return -EINVAL; + } + + usb_ss->actual_ep0_request = request; + cdns_ep0_run_transfer(usb_ss, request->dma, request->length, 1); + + spin_unlock_irqrestore(&usb_ss->lock, flags); + + return 0; +} + +/** + * cdns_ep_config Configure hardware endpoint + * @usb_ss_ep: extended endpoint object + * @index: index for endpoints + */ +static void cdns_ep_config(struct usb_ss_endpoint *usb_ss_ep, int index) +{ + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + u32 ep_cfg = 0; + u32 max_packet_size = 0; + u32 bEndpointAddress = (u32)CAST_INDEX_TO_EP_ADDR(index); + u32 interrupt_mask = 0; + + usb_ss_ep->endpoint.address = bEndpointAddress; + if (usb_ss_ep->is_iso_flag) { + ep_cfg = EP_CFG__EPTYPE__WRITE(USB_ENDPOINT_XFER_ISOC); + interrupt_mask = INTERRUPT_MASK; + } else { + ep_cfg = EP_CFG__EPTYPE__WRITE(USB_ENDPOINT_XFER_BULK); + } + + switch (usb_ss->gadget.speed) { + case USB_SPEED_UNKNOWN: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_0; + break; + + case USB_SPEED_LOW: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_8; + break; + + case USB_SPEED_FULL: + max_packet_size = (usb_ss_ep->is_iso_flag ? + ENDPOINT_MAX_PACKET_SIZE_1023 : + ENDPOINT_MAX_PACKET_SIZE_64); + break; + + case USB_SPEED_HIGH: + max_packet_size = (usb_ss_ep->is_iso_flag ? + ENDPOINT_MAX_PACKET_SIZE_1024 : + ENDPOINT_MAX_PACKET_SIZE_512); + break; + + case USB_SPEED_WIRELESS: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_512; + break; + + case USB_SPEED_SUPER: + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_1024; + break; + + case USB_SPEED_SUPER_PLUS: + dev_warn(&usb_ss->dev, "USB 3.1 is not supported\n"); + max_packet_size = ENDPOINT_MAX_PACKET_SIZE_1024; + break; + } + + ep_cfg |= EP_CFG__MAXPKTSIZE__WRITE(max_packet_size); + + if (usb_ss_ep->is_iso_flag) { + ep_cfg |= EP_CFG__BUFFERING__WRITE(1); + ep_cfg |= EP_CFG__MAXBURST__WRITE(0); + } else { + ep_cfg |= EP_CFG__BUFFERING__WRITE(3); + ep_cfg |= EP_CFG__MAXBURST__WRITE(15); + } + + ep_cfg |= EP_CFG__ENABLE__MASK; + + select_ep(usb_ss, bEndpointAddress); + gadget_writel(usb_ss, &usb_ss->regs->ep_cfg, ep_cfg); + gadget_writel(usb_ss, &usb_ss->regs->ep_sts_en, + EP_STS_EN__TRBERREN__MASK | interrupt_mask); + + /* enable interrupt for selected endpoint */ + ep_cfg = gadget_readl(usb_ss, &usb_ss->regs->ep_ien); + ep_cfg |= CAST_EP_ADDR_TO_BIT_POS(bEndpointAddress); + gadget_writel(usb_ss, &usb_ss->regs->ep_ien, ep_cfg); +} + +/** + * usb_ss_gadget_ep_enable Enable endpoint + * @ep: endpoint object + * @desc: endpoint descriptor + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct usb_ss_endpoint *usb_ss_ep; + struct usb_ss_dev *usb_ss; + unsigned long flags; + int ret; + + usb_ss_ep = to_usb_ss_ep(ep); + usb_ss = usb_ss_ep->usb_ss; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + dev_err(&usb_ss->dev, "usb-ss: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + dev_err(&usb_ss->dev, "usb-ss: missing wMaxPacketSize\n"); + return -EINVAL; + } + + ret = usb_ss_allocate_trb_pool(usb_ss_ep); + if (ret) + return ret; + + if (!usb_ss_ep->cpu_addr) { + usb_ss_ep->cpu_addr = dma_alloc_coherent(usb_ss->sysdev, 4096, + &usb_ss_ep->dma_addr, GFP_DMA); + + if (!usb_ss_ep->cpu_addr) + return -ENOMEM; + } + + spin_lock_irqsave(&usb_ss->lock, flags); + dev_dbg(&usb_ss->dev, "Enabling endpoint: %s\n", ep->name); + ep->enabled = 1; + usb_ss_ep->hw_pending_flag = 0; + usb_ss_ep->endpoint.desc = desc; + spin_unlock_irqrestore(&usb_ss->lock, flags); + + return 0; +} + +/** + * usb_ss_gadget_ep_disable Disable endpoint + * @ep: endpoint object + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep_disable(struct usb_ep *ep) +{ + struct usb_ss_endpoint *usb_ss_ep; + struct usb_ss_dev *usb_ss; + unsigned long flags; + int ret = 0; + struct usb_request *request; + + if (!ep) { + pr_debug("usb-ss: invalid parameters\n"); + return -EINVAL; + } + + usb_ss_ep = to_usb_ss_ep(ep); + usb_ss = usb_ss_ep->usb_ss; + + spin_lock_irqsave(&usb_ss->lock, flags); + dev_dbg(&usb_ss->dev, + "Disabling endpoint: %s\n", ep->name); + select_ep(usb_ss, ep->desc->bEndpointAddress); + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__EPRST__MASK); + while (gadget_readl(usb_ss, + &usb_ss->regs->ep_cmd) & EP_CMD__EPRST__MASK) + ; + + while (!list_empty(&usb_ss_ep->request_list)) { + + request = next_request(&usb_ss_ep->request_list); + usb_gadget_unmap_request_by_dev(usb_ss->sysdev, request, + ep->desc->bEndpointAddress & USB_DIR_IN); + request->status = -ESHUTDOWN; + list_del(&request->list); + spin_unlock(&usb_ss->lock); + usb_gadget_giveback_request(ep, request); + spin_lock(&usb_ss->lock); + } + + ep->desc = NULL; + ep->enabled = 0; + + spin_unlock_irqrestore(&usb_ss->lock, flags); + + return ret; +} + +/** + * usb_ss_gadget_ep_alloc_request Allocates request + * @ep: endpoint object associated with request + * @gfp_flags: gfp flags + * + * Returns allocated request address, NULL on allocation error + */ +static struct usb_request *usb_ss_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct usb_request *request; + + request = kzalloc(sizeof(struct usb_request), gfp_flags); + if (!request) + return NULL; + + return request; +} + +/** + * usb_ss_gadget_ep_free_request Free memory occupied by request + * @ep: endpoint object associated with request + * @request: request to free memory + */ +static void usb_ss_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + kfree(request); +} + +/** + * usb_ss_gadget_ep_queue Transfer data on endpoint + * @ep: endpoint object + * @request: request object + * @gfp_flags: gfp flags + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep_queue(struct usb_ep *ep, + struct usb_request *request, gfp_t gfp_flags) +{ + struct usb_ss_endpoint *usb_ss_ep = + to_usb_ss_ep(ep); + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + unsigned long flags; + int ret = 0; + int empty_list = 0; + + spin_lock_irqsave(&usb_ss->lock, flags); + + request->actual = 0; + request->status = -EINPROGRESS; + + dev_dbg(&usb_ss->dev, + "Queuing endpoint: %s\n", usb_ss_ep->name); + + dev_dbg(&usb_ss->dev, "QUEUE(%02X) %d\n", + ep->desc->bEndpointAddress, request->length); + + ret = usb_gadget_map_request_by_dev(usb_ss->sysdev, request, + ep->desc->bEndpointAddress & USB_DIR_IN); + + if (ret) { + spin_unlock_irqrestore(&usb_ss->lock, flags); + return ret; + } + + empty_list = list_empty(&usb_ss_ep->request_list); + list_add_tail(&request->list, &usb_ss_ep->request_list); + + if (!usb_ss->hw_configured_flag) { + spin_unlock_irqrestore(&usb_ss->lock, flags); + return 0; + } + + if (empty_list) { + if (!usb_ss_ep->stalled_flag) + cdns_ep_run_transfer(usb_ss_ep); + } + spin_unlock_irqrestore(&usb_ss->lock, flags); + + return ret; +} + +/** + * usb_ss_gadget_ep_dequeue Remove request from transfer queue + * @ep: endpoint object associated with request + * @request: request object + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct usb_ss_endpoint *usb_ss_ep = + to_usb_ss_ep(ep); + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + unsigned long flags; + + spin_lock_irqsave(&usb_ss->lock, flags); + dev_dbg(&usb_ss->dev, "DEQUEUE(%02X) %d\n", + ep->address, request->length); + usb_gadget_unmap_request_by_dev(usb_ss->sysdev, request, + ep->address & USB_DIR_IN); + request->status = -ECONNRESET; + + if (ep->address) + list_del(&request->list); + + if (request->complete) { + spin_unlock(&usb_ss->lock); + request->complete(ep, request); + spin_lock(&usb_ss->lock); + } + + spin_unlock_irqrestore(&usb_ss->lock, flags); + return 0; +} + +/** + * usb_ss_gadget_ep_set_halt Sets/clears stall on selected endpoint + * @ep: endpoint object to set/clear stall on + * @value: 1 for set stall, 0 for clear stall + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct usb_ss_endpoint *usb_ss_ep = + to_usb_ss_ep(ep); + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + unsigned long flags; + + /* return error when endpoint disabled */ + if (!ep->enabled) + return -EPERM; + + /* if actual transfer is pending defer setting stall on this endpoint */ + if (usb_ss_ep->hw_pending_flag && value) { + usb_ss_ep->stalled_flag = 1; + return 0; + } + + dev_dbg(&usb_ss->dev, "HALT(%02X) %d\n", ep->address, value); + + spin_lock_irqsave(&usb_ss->lock, flags); + + select_ep(usb_ss, ep->desc->bEndpointAddress); + if (value) { + cdns_ep_stall_flush(usb_ss_ep); + } else { + /* + * TODO: + * epp->wedgeFlag = 0; + */ + usb_ss_ep->wedge_flag = 0; + gadget_writel(usb_ss, &usb_ss->regs->ep_cmd, + EP_CMD__CSTALL__MASK | EP_CMD__EPRST__MASK); + /* wait for EPRST cleared */ + while (gadget_readl(usb_ss, + &usb_ss->regs->ep_cmd) & EP_CMD__EPRST__MASK) + ; + usb_ss_ep->stalled_flag = 0; + } + usb_ss_ep->hw_pending_flag = 0; + + spin_unlock_irqrestore(&usb_ss->lock, flags); + + return 0; +} + +/** + * usb_ss_gadget_ep_set_wedge Set wedge on selected endpoint + * @ep: endpoint object + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct usb_ss_endpoint *usb_ss_ep = to_usb_ss_ep(ep); + struct usb_ss_dev *usb_ss = usb_ss_ep->usb_ss; + + dev_dbg(&usb_ss->dev, "WEDGE(%02X)\n", ep->address); + usb_ss_gadget_ep_set_halt(ep, 1); + usb_ss_ep->wedge_flag = 1; + return 0; +} + +static const struct usb_ep_ops usb_ss_gadget_ep0_ops = { + .enable = usb_ss_gadget_ep0_enable, + .disable = usb_ss_gadget_ep0_disable, + .alloc_request = usb_ss_gadget_ep_alloc_request, + .free_request = usb_ss_gadget_ep_free_request, + .queue = usb_ss_gadget_ep0_queue, + .dequeue = usb_ss_gadget_ep_dequeue, + .set_halt = usb_ss_gadget_ep0_set_halt, + .set_wedge = usb_ss_gadget_ep_set_wedge, +}; + +static const struct usb_ep_ops usb_ss_gadget_ep_ops = { + .enable = usb_ss_gadget_ep_enable, + .disable = usb_ss_gadget_ep_disable, + .alloc_request = usb_ss_gadget_ep_alloc_request, + .free_request = usb_ss_gadget_ep_free_request, + .queue = usb_ss_gadget_ep_queue, + .dequeue = usb_ss_gadget_ep_dequeue, + .set_halt = usb_ss_gadget_ep_set_halt, + .set_wedge = usb_ss_gadget_ep_set_wedge, +}; + +/** + * usb_ss_gadget_get_frame Returns number of actual ITP frame + * @gadget: gadget object + * + * Returns number of actual ITP frame + */ +static int usb_ss_gadget_get_frame(struct usb_gadget *gadget) +{ + struct usb_ss_dev *usb_ss = gadget_to_usb_ss(gadget); + + dev_dbg(&usb_ss->dev, "usb_ss_gadget_get_frame\n"); + return gadget_readl(usb_ss, &usb_ss->regs->usb_iptn); +} + +static int usb_ss_gadget_wakeup(struct usb_gadget *gadget) +{ + struct usb_ss_dev *usb_ss = gadget_to_usb_ss(gadget); + + dev_dbg(&usb_ss->dev, "usb_ss_gadget_wakeup\n"); + return 0; +} + +static int usb_ss_gadget_set_selfpowered(struct usb_gadget *gadget, + int is_selfpowered) +{ + struct usb_ss_dev *usb_ss = gadget_to_usb_ss(gadget); + + dev_dbg(&usb_ss->dev, "usb_ss_gadget_set_selfpowered: %d\n", + is_selfpowered); + return 0; +} + +static int usb_ss_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct usb_ss_dev *usb_ss = gadget_to_usb_ss(gadget); + + dev_dbg(&usb_ss->dev, "usb_ss_gadget_pullup: %d\n", is_on); + return 0; +} + +/** + * usb_ss_gadget_udc_start Gadget start + * @gadget: gadget object + * @driver: driver which operates on this gadget + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct usb_ss_dev *usb_ss = gadget_to_usb_ss(gadget); + unsigned long flags; + + if (usb_ss->gadget_driver) { + dev_err(&usb_ss->dev, "%s is already bound to %s\n", + usb_ss->gadget.name, + usb_ss->gadget_driver->driver.name); + return -EBUSY; + } + + dev_dbg(&usb_ss->dev, "%s begins\n", __func__); + + spin_lock_irqsave(&usb_ss->lock, flags); + usb_ss->gadget_driver = driver; + if (!usb_ss->start_gadget) { + spin_unlock_irqrestore(&usb_ss->lock, flags); + return 0; + } + + /* configure endpoint 0 hardware */ + cdns_ep0_config(usb_ss); + + /* enable interrupts for endpoint 0 (in and out) */ + gadget_writel(usb_ss, &usb_ss->regs->ep_ien, + EP_IEN__EOUTEN0__MASK | EP_IEN__EINEN0__MASK); + + /* enable interrupt for device */ + gadget_writel(usb_ss, &usb_ss->regs->usb_ien, + USB_IEN__U2RESIEN__MASK + | USB_ISTS__DIS2I__MASK + | USB_IEN__CON2IEN__MASK + | USB_IEN__UHRESIEN__MASK + | USB_IEN__UWRESIEN__MASK + | USB_IEN__DISIEN__MASK + | USB_IEN__CONIEN__MASK + | USB_IEN__U3EXTIEN__MASK + | USB_IEN__L2ENTIEN__MASK + | USB_IEN__L2EXTIEN__MASK); + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__CLK2OFFDS__MASK + /* | USB_CONF__USB3DIS__MASK */ + | USB_CONF__L1DS__MASK); + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__DEVEN__MASK + | USB_CONF__U1DS__MASK + | USB_CONF__U2DS__MASK + /* + * TODO: + * | USB_CONF__L1EN__MASK + */ + ); + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__DEVEN__MASK + | USB_CONF__U1DS__MASK + | USB_CONF__U2DS__MASK + /* + * TODO: + * | USB_CONF__L1EN__MASK + */ + ); + + gadget_writel(usb_ss, &usb_ss->regs->dbg_link1, + DBG_LINK1__LFPS_MIN_GEN_U1_EXIT_SET__MASK | + DBG_LINK1__LFPS_MIN_GEN_U1_EXIT__WRITE(0x3C)); + spin_unlock_irqrestore(&usb_ss->lock, flags); + dev_dbg(&usb_ss->dev, "%s ends\n", __func__); + + return 0; +} + +/** + * usb_ss_gadget_udc_stop Stops gadget + * @gadget: gadget object + * + * Returns 0 + */ +static int usb_ss_gadget_udc_stop(struct usb_gadget *gadget) +{ + struct usb_ss_dev *usb_ss = gadget_to_usb_ss(gadget); + unsigned long flags; + + spin_lock_irqsave(&usb_ss->lock, flags); + usb_ss->gadget_driver = NULL; + /* disable interrupt for device */ + gadget_writel(usb_ss, &usb_ss->regs->usb_ien, 0); + spin_unlock_irqrestore(&usb_ss->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops usb_ss_gadget_ops = { + .get_frame = usb_ss_gadget_get_frame, + .wakeup = usb_ss_gadget_wakeup, + .set_selfpowered = usb_ss_gadget_set_selfpowered, + .pullup = usb_ss_gadget_pullup, + .udc_start = usb_ss_gadget_udc_start, + .udc_stop = usb_ss_gadget_udc_stop, +}; + +/** + * usb_ss_init_ep Initializes software endpoints of gadget + * @gadget: gadget object + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_init_ep(struct usb_ss_dev *usb_ss) +{ + struct usb_ss_endpoint *usb_ss_ep; + u32 ep_enabled_reg, iso_ep_reg, bulk_ep_reg; + int i; + int ep_reg_pos, ep_dir, ep_number; + int found_endpoints = 0; + + /* Read it from USB_CAP3 to USB_CAP5 */ + ep_enabled_reg = 0x00ff00ff; + iso_ep_reg = 0x00fe00fe; + bulk_ep_reg = 0x00fe00fe; + + dev_dbg(&usb_ss->dev, "Initializing non-zero endpoints\n"); + dev_dbg(&usb_ss->dev, + "ep_enabled_reg: 0x%x, iso_ep_reg: 0x%x, bulk_ep_reg:0x%x\n", + ep_enabled_reg, iso_ep_reg, bulk_ep_reg); + + for (i = 0; i < USB_SS_ENDPOINTS_MAX_COUNT; i++) { + ep_number = (i / 2) + 1; + ep_dir = i % 2; + ep_reg_pos = (16 * ep_dir) + ep_number; + + if (!(ep_enabled_reg & (1uL << ep_reg_pos))) + continue; + + /* create empty endpoint object */ + usb_ss_ep = devm_kzalloc(&usb_ss->dev, sizeof(*usb_ss_ep), + GFP_KERNEL); + if (!usb_ss_ep) + return -ENOMEM; + + /* set parent of endpoint object */ + usb_ss_ep->usb_ss = usb_ss; + + /* set index of endpoint in endpoints container */ + usb_ss->eps[found_endpoints++] = usb_ss_ep; + + /* set name of endpoint */ + snprintf(usb_ss_ep->name, sizeof(usb_ss_ep->name), "ep%d%s", + ep_number, !!ep_dir ? "in" : "out"); + usb_ss_ep->endpoint.name = usb_ss_ep->name; + dev_dbg(&usb_ss->dev, "Initializing endpoint: %s\n", + usb_ss_ep->name); + + usb_ep_set_maxpacket_limit(&usb_ss_ep->endpoint, + ENDPOINT_MAX_PACKET_LIMIT); + usb_ss_ep->endpoint.max_streams = ENDPOINT_MAX_STREAMS; + usb_ss_ep->endpoint.ops = &usb_ss_gadget_ep_ops; + if (ep_dir) + usb_ss_ep->endpoint.caps.dir_in = 1; + else + usb_ss_ep->endpoint.caps.dir_out = 1; + + /* check endpoint type */ + if (iso_ep_reg & (1uL << ep_reg_pos)) { + usb_ss_ep->endpoint.caps.type_iso = 1; + usb_ss_ep->is_iso_flag = 1; + } + + if (bulk_ep_reg & (1uL << ep_reg_pos)) { + usb_ss_ep->endpoint.caps.type_bulk = 1; + usb_ss_ep->endpoint.caps.type_int = 1; + usb_ss_ep->endpoint.maxburst = 15; + } + + list_add_tail(&usb_ss_ep->endpoint.ep_list, + &usb_ss->gadget.ep_list); + INIT_LIST_HEAD(&usb_ss_ep->request_list); + } + usb_ss->ep_nums = found_endpoints; + return 0; +} + +/** + * usb_ss_init_ep0 Initializes software endpoint 0 of gadget + * @gadget: gadget object + * + * Returns 0 on success, error code elsewhere + */ +static int usb_ss_init_ep0(struct usb_ss_dev *usb_ss) +{ + struct usb_ss_endpoint *ep0; + + dev_dbg(&usb_ss->dev, "Initializing EP0\n"); + ep0 = devm_kzalloc(&usb_ss->dev, sizeof(struct usb_ss_endpoint), + GFP_KERNEL); + + if (!ep0) + return -ENOMEM; + + /* fill CDNS fields */ + ep0->usb_ss = usb_ss; + sprintf(ep0->name, "ep0"); + + /* fill linux fields */ + ep0->endpoint.ops = &usb_ss_gadget_ep0_ops; + ep0->endpoint.maxburst = 1; + usb_ep_set_maxpacket_limit(&ep0->endpoint, ENDPOINT0_MAX_PACKET_LIMIT); + ep0->endpoint.address = 0; + ep0->endpoint.enabled = 1; + ep0->endpoint.caps.type_control = 1; + ep0->endpoint.caps.dir_in = 1; + ep0->endpoint.caps.dir_out = 1; + ep0->endpoint.name = ep0->name; + + usb_ss->gadget.ep0 = &ep0->endpoint; + + return 0; +} + +static void cdns3_gadget_release(struct device *dev) +{ + struct usb_ss_dev *usb_ss = container_of(dev, struct usb_ss_dev, dev); + + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); + kfree(usb_ss); +} + +static int __cdns3_gadget_init(struct cdns3 *cdns) +{ + struct usb_ss_dev *usb_ss; + int ret; + struct device *dev; + + usb_ss = kzalloc(sizeof(*usb_ss), GFP_KERNEL); + if (!usb_ss) + return -ENOMEM; + + dev = &usb_ss->dev; + dev->release = cdns3_gadget_release; + dev->parent = cdns->dev; + dev_set_name(dev, "gadget-cdns3"); + cdns->gadget_dev = dev; + usb_ss->sysdev = cdns->dev; + ret = device_register(dev); + if (ret) + goto err1; + + usb_ss->regs = cdns->dev_regs; + + /* fill gadget fields */ + usb_ss->gadget.ops = &usb_ss_gadget_ops; + usb_ss->gadget.max_speed = USB_SPEED_SUPER; + usb_ss->gadget.speed = USB_SPEED_UNKNOWN; + usb_ss->gadget.name = "usb-ss-gadget"; + usb_ss->gadget.sg_supported = 1; + usb_ss->is_connected = 0; + + usb_ss->in_standby_mode = 1; + + /* initialize endpoint container */ + INIT_LIST_HEAD(&usb_ss->gadget.ep_list); + ret = usb_ss_init_ep0(usb_ss); + if (ret) { + dev_err(dev, "Failed to create endpoint 0\n"); + ret = -ENOMEM; + goto err2; + } + + ret = usb_ss_init_ep(usb_ss); + if (ret) { + dev_err(dev, "Failed to create non zero endpoints\n"); + ret = -ENOMEM; + goto err2; + } + + /* allocate memory for default endpoint TRB */ + usb_ss->trb_ep0 = (u32 *)dma_alloc_coherent(usb_ss->sysdev, 20, + &usb_ss->trb_ep0_dma, GFP_DMA); + if (!usb_ss->trb_ep0) { + dev_err(dev, "Failed to allocate memory for ep0 TRB\n"); + ret = -ENOMEM; + goto err2; + } + + /* allocate memory for setup packet buffer */ + usb_ss->setup = (u8 *)dma_alloc_coherent(usb_ss->sysdev, 8, + &usb_ss->setup_dma, + GFP_DMA); + if (!usb_ss->setup) { + dev_err(dev, "Failed to allocate memory for SETUP buffer\n"); + ret = -ENOMEM; + goto err3; + } + + /* add USB gadget device */ + ret = usb_add_gadget_udc(&usb_ss->dev, &usb_ss->gadget); + if (ret < 0) { + dev_err(dev, "Failed to register USB device controller\n"); + goto err4; + } + + return 0; + +err4: + dma_free_coherent(usb_ss->sysdev, 8, usb_ss->setup, + usb_ss->setup_dma); +err3: + dma_free_coherent(usb_ss->sysdev, 20, usb_ss->trb_ep0, + usb_ss->trb_ep0_dma); +err2: + device_del(dev); +err1: + put_device(dev); + cdns->gadget_dev = NULL; + + return ret; +} + +/** + * cdns3_gadget_remove: parent remove must call this to remove UDC + * + * cdns: cdns3 instance + * + */ +void cdns3_gadget_remove(struct cdns3 *cdns) +{ + struct usb_ss_dev *usb_ss; + + if (!cdns->roles[CDNS3_ROLE_GADGET]) + return; + + usb_ss = container_of(cdns->gadget_dev, struct usb_ss_dev, dev); + usb_del_gadget_udc(&usb_ss->gadget); + dma_free_coherent(usb_ss->sysdev, 8, usb_ss->setup, usb_ss->setup_dma); + dma_free_coherent(usb_ss->sysdev, 20, usb_ss->trb_ep0, + usb_ss->trb_ep0_dma); + device_unregister(cdns->gadget_dev); + cdns->gadget_dev = NULL; +} + +static int cdns3_gadget_start(struct cdns3 *cdns) +{ + struct usb_ss_dev *usb_ss = container_of(cdns->gadget_dev, + struct usb_ss_dev, dev); + unsigned long flags; + + dev_dbg(&usb_ss->dev, "%s begins\n", __func__); + + spin_lock_irqsave(&usb_ss->lock, flags); + usb_ss->start_gadget = 1; + if (!usb_ss->gadget_driver) { + spin_unlock_irqrestore(&usb_ss->lock, flags); + return 0; + } + + /* configure endpoint 0 hardware */ + cdns_ep0_config(usb_ss); + + /* enable interrupts for endpoint 0 (in and out) */ + gadget_writel(usb_ss, &usb_ss->regs->ep_ien, + EP_IEN__EOUTEN0__MASK | EP_IEN__EINEN0__MASK); + + /* enable interrupt for device */ + gadget_writel(usb_ss, &usb_ss->regs->usb_ien, + USB_IEN__U2RESIEN__MASK + | USB_ISTS__DIS2I__MASK + | USB_IEN__CON2IEN__MASK + | USB_IEN__UHRESIEN__MASK + | USB_IEN__UWRESIEN__MASK + | USB_IEN__DISIEN__MASK + | USB_IEN__CONIEN__MASK + | USB_IEN__U3EXTIEN__MASK + | USB_IEN__L2ENTIEN__MASK + | USB_IEN__L2EXTIEN__MASK); + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__CLK2OFFDS__MASK + /* | USB_CONF__USB3DIS__MASK */ + | USB_CONF__L1DS__MASK); + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, + USB_CONF__U1DS__MASK + | USB_CONF__U2DS__MASK + /* + * TODO: + * | USB_CONF__L1EN__MASK + */ + ); + + gadget_writel(usb_ss, &usb_ss->regs->usb_conf, USB_CONF__DEVEN__MASK); + + gadget_writel(usb_ss, &usb_ss->regs->dbg_link1, + DBG_LINK1__LFPS_MIN_GEN_U1_EXIT_SET__MASK | + DBG_LINK1__LFPS_MIN_GEN_U1_EXIT__WRITE(0x3C)); + usb_ss->in_standby_mode = 0; + spin_unlock_irqrestore(&usb_ss->lock, flags); + dev_dbg(&usb_ss->dev, "%s ends\n", __func__); + + return 0; +} + +static void cdns3_gadget_stop(struct cdns3 *cdns) +{ + struct usb_ss_dev *usb_ss; + unsigned long flags; + + usb_ss = container_of(cdns->gadget_dev, struct usb_ss_dev, dev); + spin_lock_irqsave(&usb_ss->lock, flags); + + /* disable interrupt for device */ + gadget_writel(usb_ss, &usb_ss->regs->usb_ien, 0); + usb_ss->start_gadget = 0; + spin_unlock_irqrestore(&usb_ss->lock, flags); +} + +/** + * cdns3_gadget_init - initialize device structure + * + * cdns: cdns3 instance + * + * This function initializes the gadget. + */ +int cdns3_gadget_init(struct cdns3 *cdns) +{ + struct cdns3_role_driver *rdrv; + + rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); + if (!rdrv) + return -ENOMEM; + + rdrv->start = cdns3_gadget_start; + rdrv->stop = cdns3_gadget_stop; + rdrv->irq = cdns_irq_handler_thread; + rdrv->name = "gadget"; + cdns->roles[CDNS3_ROLE_GADGET] = rdrv; + return __cdns3_gadget_init(cdns); +} diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h new file mode 100644 index 000000000000..6ecf380672e9 --- /dev/null +++ b/drivers/usb/cdns3/gadget.h @@ -0,0 +1,208 @@ +/** + * gadget.h - Cadence USB3 device Controller Core file + * + * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com + * Copyright 2017 NXP + * + * Authors: Pawel Jez , + * Konrad Kociolek , + * Peter Chen + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __DRIVERS_CDNS3_GADGET +#define __DRIVERS_CDNS3_GADGET + +#include "dev-regs-map.h" + +#if IS_ENABLED(CONFIG_USB_CDNS_MISC) +#include "cdns_misc.h" +#endif + +#define gadget_to_usb_ss(g) \ + (container_of(g, struct usb_ss_dev, gadget)) + +#define to_usb_ss_ep(ep) \ + (container_of(ep, struct usb_ss_endpoint, endpoint)) + +#define ep_to_usb_ss_ep(ep) \ + (container_of(ep, struct usb_ss_endpoint, endpoint)) + +/*-------------------------------------------------------------------------*/ +/* TRB macros */ + +/* Common TRB fields */ +#define TRB_SET_CYCLE_BIT 1uL +#define TRB_SET_CHAIN_BIT 0x10 + +/* offset 0 */ +#define TRB_DATA_BUFFER_POINTER_MASK 0xFFFFFFFF +#define TRB_SET_DATA_BUFFER_POINTER(p) (p & TRB_DATA_BUFFER_POINTER_MASK) + +/* offset 4 */ +#define TRB_TRANSFER_LENGTH_MASK 0x1FFFF +#define TRB_SET_TRANSFER_LENGTH(l) (l & TRB_TRANSFER_LENGTH_MASK) + +#define TRB_BURST_LENGTH_MASK 0xFF +#define TRB_SET_BURST_LENGTH(l) ((l & TRB_BURST_LENGTH_MASK) << 24) + +/* offset 8 */ +#define TRB_SET_INT_ON_SHORT_PACKET 0x04 +#define TRB_SET_FIFO_MODE 0x08 +#define TRB_SET_INT_ON_COMPLETION 0x20 + +#define TRB_TYPE_NORMAL 0x400 + +#define TRB_STREAM_ID_MASK 0xFFFF +#define TRB_SET_STREAM_ID(sid) ((sid & TRB_STREAM_ID_MASK) << 16) + +/*-------------------------------------------------------------------------*/ +/* Driver numeric constants */ + + +#define DEVICE_ADDRESS_MAX 127 + +/* Endpoint init values */ +#define ENDPOINT_MAX_PACKET_LIMIT 1024 +#define ENDPOINT_MAX_STREAMS 15 + +#define ENDPOINT0_MAX_PACKET_LIMIT 512 + +/* All endpoints except EP0 */ +#define USB_SS_ENDPOINTS_MAX_COUNT 30 + +#define USB_SS_TRBS_NUM 32 + +/* Standby mode */ +#define STB_CLK_SWITCH_DONE_MASK 0x200 +#define STB_CLK_SWITCH_EN_MASK 0x100 +#define STB_CLK_SWITCH_EN_SHIFT 8 + +#define ENDPOINT_MAX_PACKET_SIZE_0 0 +#define ENDPOINT_MAX_PACKET_SIZE_8 8 +#define ENDPOINT_MAX_PACKET_SIZE_64 64 +#define ENDPOINT_MAX_PACKET_SIZE_512 512 +#define ENDPOINT_MAX_PACKET_SIZE_1023 1023 +#define ENDPOINT_MAX_PACKET_SIZE_1024 1024 + +#define SS_LINK_STATE_U3 3 +#define FSHS_LPM_STATE_L2 2 + +#define ADDR_MODULO_8 8 + +#define INTERRUPT_MASK 0xFFFFFFFF + +#define ACTUAL_TRANSFERRED_BYTES_MASK 0x1FFFF + +#define ENDPOINT_DIR_MASK 0x80 + +/*-------------------------------------------------------------------------*/ + +/** + * IS_REG_REQUIRING_ACTIVE_REF_CLOCK - Macro checks if desired + * register requires active clock, it involves such registers as: + * EP_CFG, EP_TR_ADDR, EP_CMD, EP_SEL, USB_CONF + * @usb_ss: extended gadget object + * @reg: register address + */ +#define IS_REG_REQUIRING_ACTIVE_REF_CLOCK(usb_ss, reg) (!reg || \ + (reg >= &usb_ss->regs->ep_sel && reg <= &usb_ss->regs->ep_cmd)) + +/** + * CAST_EP_REG_POS_TO_INDEX - Macro converts bit position of ep_ists register to + * index of endpoint object in usb_ss_dev.eps[] container + * @i: bit position of endpoint for which endpoint object is required + * + * Remember that endpoint container doesn't contain default endpoint + */ +#define CAST_EP_REG_POS_TO_INDEX(i) (((i) / 16) + ((((i) % 16) - 2) * 2)) + +/** + * CAST_EP_ADDR_TO_INDEX - Macro converts endpoint address to + * index of endpoint object in usb_ss_dev.eps[] container + * @ep_addr: endpoint address for which endpoint object is required + * + * Remember that endpoint container doesn't contain default endpoint + */ +#define CAST_EP_ADDR_TO_INDEX(ep_addr) \ + (((ep_addr & 0x7F) - 1) + ((ep_addr & 0x80) ? 1 : 0)) + +/** + * CAST_EP_ADDR_TO_BIT_POS - Macro converts endpoint address to + * bit position in ep_ists register + * @ep_addr: endpoint address for which bit position is required + * + * Remember that endpoint container doesn't contain default endpoint + */ +#define CAST_EP_ADDR_TO_BIT_POS(ep_addr) \ + (((uint32_t)1 << (ep_addr & 0x7F)) << ((ep_addr & 0x80) ? 16 : 0)) + + +#define CAST_INDEX_TO_EP_ADDR(index) \ + ((index / 2 + 1) | ((index % 2) ? 0x80 : 0x00)) + +/*-------------------------------------------------------------------------*/ +/* Used structs */ + +struct usb_ss_trb { + u32 offset0; + u32 offset4; + u32 offset8; +}; + +struct usb_ss_dev; + +struct usb_ss_endpoint { + struct usb_ep endpoint; + struct list_head request_list; + + struct usb_ss_trb *trb_pool; + dma_addr_t trb_pool_dma; + + struct usb_ss_dev *usb_ss; + char name[20]; + int hw_pending_flag; + int stalled_flag; + int is_iso_flag; + int wedge_flag; + void *cpu_addr; + dma_addr_t dma_addr; +}; + +struct usb_ss_dev { + struct device dev; + struct usbss_dev_register_block_type __iomem *regs; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + + dma_addr_t setup_dma; + dma_addr_t trb_ep0_dma; + u32 *trb_ep0; + u8 *setup; + + struct usb_ss_endpoint *eps[USB_SS_ENDPOINTS_MAX_COUNT]; + int ep_nums; + struct usb_request *actual_ep0_request; + int ep0_data_dir; + int hw_configured_flag; + int wake_up_flag; + u16 isoch_delay; + spinlock_t lock; + + unsigned is_connected:1; + unsigned in_standby_mode:1; + + u32 usb_ien; + u32 ep_ien; + int setup_pending; + struct device *sysdev; + bool start_gadget; /* The device mode is enabled */ +}; + +#endif /* __DRIVERS_CDNS3_GADGET */ diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h new file mode 100644 index 000000000000..a981d5cf3658 --- /dev/null +++ b/drivers/usb/cdns3/host-export.h @@ -0,0 +1,43 @@ +/* + * host-export.h - Host Export APIs + * + * Copyright 2017 NXP + * Authors: Peter Chen + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __DRIVERS_USB_CDNS3_HOST_H +#define __DRIVERS_USB_CDNS3_HOST_H + +#ifdef CONFIG_USB_CDNS3_HOST + +int cdns3_host_init(struct cdns3 *cdns); +void cdns3_host_remove(struct cdns3 *cdns); +void cdns3_host_driver_init(void); + +#else + +static inline int cdns3_host_init(struct cdns3 *cdns) +{ + return -ENXIO; +} + +static inline void cdns3_host_remove(struct cdns3 *cdns) +{ + +} + +static inline void cdns3_host_driver_init(void) +{ + +} + +#endif /* CONFIG_USB_CDNS3_HOST */ + +#endif /* __DRIVERS_USB_CDNS3_HOST_H */ diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c new file mode 100644 index 000000000000..26cd76f088d0 --- /dev/null +++ b/drivers/usb/cdns3/host.c @@ -0,0 +1,211 @@ +/* + * host.c - Cadence USB3 host controller driver + * + * Copyright 2017 NXP + * Authors: Peter Chen + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../host/xhci.h" + +#include "core.h" +#include "host-export.h" + +static struct hc_driver __read_mostly xhci_cdns3_hc_driver; + +static void xhci_cdns3_quirks(struct device *dev, struct xhci_hcd *xhci) +{ + /* + * As of now platform drivers don't provide MSI support so we ensure + * here that the generic code does not try to make a pci_dev from our + * dev struct in order to setup MSI + */ + xhci->quirks |= XHCI_PLAT; +} + +static int xhci_cdns3_setup(struct usb_hcd *hcd) +{ + return xhci_gen_setup(hcd, xhci_cdns3_quirks); +} + +static const struct xhci_driver_overrides xhci_cdns3_overrides __initconst = { + .extra_priv_size = sizeof(struct xhci_hcd), + .reset = xhci_cdns3_setup, +}; + +struct cdns3_host { + struct device dev; + struct usb_hcd *hcd; +}; + +static irqreturn_t cdns3_host_irq(struct cdns3 *cdns) +{ + struct device *dev = cdns->host_dev; + struct usb_hcd *hcd; + + if (dev) + hcd = dev_get_drvdata(dev); + else + return IRQ_NONE; + + if (hcd) + return usb_hcd_irq(cdns->irq, hcd); + else + return IRQ_NONE; +} + +static void cdns3_host_release(struct device *dev) +{ + struct cdns3_host *host = container_of(dev, struct cdns3_host, dev); + + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); + kfree(host); +} + +static int cdns3_host_start(struct cdns3 *cdns) +{ + struct cdns3_host *host; + struct device *dev; + struct device *sysdev; + struct xhci_hcd *xhci; + int ret; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + dev = &host->dev; + dev->release = cdns3_host_release; + dev->parent = cdns->dev; + dev_set_name(dev, "xhci-cdns3"); + cdns->host_dev = dev; + ret = device_register(dev); + if (ret) + goto err1; + + sysdev = cdns->dev; + /* Try to set 64-bit DMA first */ + if (WARN_ON(!sysdev->dma_mask)) + /* Platform did not initialize dma_mask */ + ret = dma_coerce_mask_and_coherent(sysdev, + DMA_BIT_MASK(64)); + else + ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); + + /* If setting 64-bit DMA mask fails, fall back to 32-bit DMA mask */ + if (ret) { + ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + } + dev_info(dev, "%s begins create hcd\n", __func__); + + host->hcd = __usb_create_hcd(&xhci_cdns3_hc_driver, sysdev, dev, + dev_name(dev), NULL); + if (!host->hcd) { + ret = -ENOMEM; + goto err2; + } + + host->hcd->regs = cdns->xhci_regs; + host->hcd->rsrc_start = cdns->xhci_res->start; + host->hcd->rsrc_len = resource_size(cdns->xhci_res); + + device_wakeup_enable(host->hcd->self.controller); + + xhci = hcd_to_xhci(host->hcd); + + xhci->main_hcd = host->hcd; + xhci->shared_hcd = __usb_create_hcd(&xhci_cdns3_hc_driver, sysdev, dev, + dev_name(dev), host->hcd); + if (!xhci->shared_hcd) { + ret = -ENOMEM; + goto err3; + } + + ret = usb_add_hcd(host->hcd, 0, IRQF_SHARED); + if (ret) + goto err4; + + ret = usb_add_hcd(xhci->shared_hcd, 0, IRQF_SHARED); + if (ret) + goto err5; + + dev_dbg(dev, "%s ends\n", __func__); + return 0; + +err5: + usb_remove_hcd(host->hcd); +err4: + usb_put_hcd(xhci->shared_hcd); +err3: + usb_put_hcd(host->hcd); +err2: + device_del(dev); +err1: + put_device(dev); + cdns->host_dev = NULL; + return ret; +} + +static void cdns3_host_stop(struct cdns3 *cdns) +{ + struct device *dev = cdns->host_dev; + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + if (dev) { + hcd = dev_get_drvdata(dev); + xhci = hcd_to_xhci(hcd); + usb_remove_hcd(hcd); + usb_remove_hcd(xhci->shared_hcd); + cdns->role = CDNS3_ROLE_END; + synchronize_irq(cdns->irq); + usb_put_hcd(hcd); + usb_put_hcd(xhci->shared_hcd); + cdns->host_dev = NULL; + device_del(dev); + put_device(dev); + } +} + +int cdns3_host_init(struct cdns3 *cdns) +{ + struct cdns3_role_driver *rdrv; + + rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); + if (!rdrv) + return -ENOMEM; + + rdrv->start = cdns3_host_start; + rdrv->stop = cdns3_host_stop; + rdrv->irq = cdns3_host_irq; + rdrv->name = "host"; + cdns->roles[CDNS3_ROLE_HOST] = rdrv; + + return 0; +} + +void cdns3_host_remove(struct cdns3 *cdns) +{ + cdns3_host_stop(cdns); +} + +void __init cdns3_host_driver_init(void) +{ + xhci_init_driver(&xhci_cdns3_hc_driver, &xhci_cdns3_overrides); +} diff --git a/drivers/usb/cdns3/io.h b/drivers/usb/cdns3/io.h new file mode 100644 index 000000000000..c16edbf54b8b --- /dev/null +++ b/drivers/usb/cdns3/io.h @@ -0,0 +1,35 @@ +/** + * io.h - Cadence USB3 IO Header + * + * Copyright (C) 2016 Cadence Design Systems - https://www.cadence.com/ + * + * Authors: Rafal Ozieblo , + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __DRIVERS_USB_CDNS_IO_H +#define __DRIVERS_USB_CDNS_IO_H + +#include + +static inline u32 cdns_readl(uint32_t __iomem *reg) +{ + u32 value = 0; + + value = readl(reg); + return value; +} + +static inline void cdns_writel(uint32_t __iomem *reg, u32 value) +{ + writel(value, reg); +} + + +#endif /* __DRIVERS_USB_CDNS_IO_H */