Merge branch 'usb/next' into next

* usb/next: (188 commits)
  LF-252 usb: cdns3: gadget: fix the issue for DMA scatter buffer list
  usb: dwc3: Add cache type configuration support
  usb: dwc3: Add chip-specific compatible string
  MLK-22675 usb: dwc3: host: disable park mode
  MLK-22878 usb: cdns3: gadget: add imx8qxp C0 support
  ...
This commit is contained in:
Dong Aisheng 2019-12-02 18:05:31 +08:00
commit 210428cc37
72 changed files with 5842 additions and 2961 deletions

View File

@ -0,0 +1,9 @@
What: /sys/bus/platform/devices/5b110000.usb3/role
Date: Jan 2019
Contact: Peter Chen <peter.chen@nxp.com>
Description:
It returns string "gadget", "host" and "none" when read it,
it indicates current controller role.
It will do role switch when write "gadget" or "host" to it.
Only controller at dual-role configuration supports writing.

View File

@ -0,0 +1,45 @@
What: Raise a uevent when a USB charger is inserted or removed
Date: 2019-11-11
KernelVersion: 5.5
Contact: linux-usb@vger.kernel.org
Description: There are two USB charger states:
USB_CHARGER_ABSENT
USB_CHARGER_PRESENT
There are five USB charger types:
USB_CHARGER_UNKNOWN_TYPE
USB_CHARGER_SDP_TYPE
USB_CHARGER_CDP_TYPE
USB_CHARGER_DCP_TYPE
USB_CHARGER_ACA_TYPE
Here are two examples taken using udevadm monitor -p when
USB charger is online:
UDEV [227.425096] change /devices/soc0/usbphynop1 (platform)
ACTION=change
DEVPATH=/devices/soc0/usbphynop1
DRIVER=usb_phy_generic
MODALIAS=of:Nusbphynop1T(null)Cusb-nop-xceiv
OF_COMPATIBLE_0=usb-nop-xceiv
OF_COMPATIBLE_N=1
OF_FULLNAME=/usbphynop1
OF_NAME=usbphynop1
SEQNUM=2493
SUBSYSTEM=platform
USB_CHARGER_STATE=USB_CHARGER_PRESENT
USB_CHARGER_TYPE=USB_CHARGER_SDP_TYPE
USEC_INITIALIZED=227422826
USB charger is offline:
KERNEL[229.533933] change /devices/soc0/usbphynop1 (platform)
ACTION=change
DEVPATH=/devices/soc0/usbphynop1
DRIVER=usb_phy_generic
MODALIAS=of:Nusbphynop1T(null)Cusb-nop-xceiv
OF_COMPATIBLE_0=usb-nop-xceiv
OF_COMPATIBLE_N=1
OF_FULLNAME=/usbphynop1
OF_NAME=usbphynop1
SEQNUM=2494
SUBSYSTEM=platform
USB_CHARGER_STATE=USB_CHARGER_ABSENT
USB_CHARGER_TYPE=USB_CHARGER_UNKNOWN_TYPE

View File

@ -1,45 +1,39 @@
Binding for the Cadence USBSS-DRD controller
* Cadence USB3 Controller
Required properties:
- reg: Physical base address and size of the controller's register areas.
Controller has 3 different regions:
- HOST registers area
- DEVICE registers area
- OTG/DRD registers area
- reg-names - register memory area names:
"xhci" - for HOST registers space
"dev" - for DEVICE registers space
"otg" - for OTG/DRD registers space
- compatible: Should contain: "cdns,usb3"
- interrupts: Interrupts used by cdns3 controller:
"host" - interrupt used by XHCI driver.
"peripheral" - interrupt used by device driver
"otg" - interrupt used by DRD/OTG part of driver
- compatible: "Cadence,usb3";
- reg: base address and length of the registers
- interrupts: interrupt for the USB controller
- interrupt-parent: the interrupt parent for this module
- clocks: reference to the USB clock
- clock-names: the name of clocks
- cdns3,usbphy: reference to the USB PHY
Optional properties:
- maximum-speed : valid arguments are "super-speed", "high-speed" and
"full-speed"; refer to usb/generic.txt
- dr_mode: Should be one of "host", "peripheral" or "otg".
- phys: reference to the USB PHY
- phy-names: from the *Generic PHY* bindings;
Supported names are:
- cdns3,usb2-phy
- cdns3,usb3-phy
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
- extcon: extcon phandler for cdns3 device
- power-domains: the power domain for cdns3 controller and phy
- cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints
buffers expressed in KB
Examples:
Example:
usb@f3000000 {
compatible = "cdns,usb3";
interrupts = <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
<GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
<GIC_USB_IRQ 8 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "host", "peripheral", "otg";
reg = <0xf3000000 0x10000>, /* memory area for HOST registers */
<0xf3010000 0x10000>, /* memory area for DEVICE registers */
<0xf3020000 0x10000>; /* memory area for OTG/DRD registers */
reg-names = "xhci", "dev", "otg";
phys = <&usb2_phy>, <&usb3_phy>;
phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy";
};
usbotg3: cdns3@5b110000 {
compatible = "Cadence,usb3";
reg = <0x0 0x5B110000 0x0 0x10000>,
<0x0 0x5B130000 0x0 0x10000>,
<0x0 0x5B140000 0x0 0x10000>,
<0x0 0x5B160000 0x0 0x40000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8QM_USB3_LPM_CLK>,
<&clk IMX8QM_USB3_BUS_CLK>,
<&clk IMX8QM_USB3_ACLK>,
<&clk IMX8QM_USB3_IPG_CLK>,
<&clk IMX8QM_USB3_CORE_PCLK>;
clock-names = "usb3_lpm_clk", "usb3_bus_clk", "usb3_aclk",
"usb3_ipg_clk", "usb3_core_pclk";
power-domains = <&pd_conn_usb2>;
cdns3,usbphy = <&usbphynop1>;
dr_mode = "otg";
extcon = <&typec_ptn5150>;
status = "disabled";
};

View File

@ -86,6 +86,7 @@ Optional properties:
case, the "idle" state needs to pull down the data and strobe pin
and the "active" state needs to pull up the strobe pin.
- pinctrl-n: alternate pin modes
- ci-disable-lpm: Some chipidea hardware need to disable low power mode
i.mx specific properties
- fsl,usbmisc: phandler of non-core register device, with one

View File

@ -4,7 +4,15 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
as described in 'usb/generic.txt'
Required properties:
- compatible: must be "snps,dwc3"
- compatible: must be "snps,dwc3" and (if applicable) may contain a
chip-specific compatible string in front of it to allow dwc3 driver be
able to update cache type configuration accordingly, otherwise
Layerscape SoC will encounter USB init failure when adding property
dma-coherent on device tree.
Example:
* "fsl,layerscape-dwc3", "snps,dwc3"
* "snps,dwc3"
- reg : Address and length of the register set for the device
- interrupts: Interrupts used by the dwc3 controller.
- clock-names: should contain "ref", "bus_early", "suspend"
@ -109,6 +117,10 @@ Optional properties:
more than one value, which means undefined length INCR burst type
enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256.
- snps,host-vbus-glitches: Power off all Root Hub ports immediately after
setting host mode to avoid vbus (negative) glitch happen in later
xhci reset. And the vbus will back to 5V automatically when reset done.
- in addition all properties from usb-xhci.txt from the current directory are
supported as well

View File

@ -0,0 +1,30 @@
Typec orientation switch via a GPIO
-----------------------------------
Required properties:
- compatible: should be set one of following:
- "nxp,ptn36043" for NXP Type-C SuperSpeed active switch.
- gpios: the GPIO used to switch the super speed active channel,
GPIO_ACTIVE_HIGH: GPIO state high for cc1;
GPIO_ACTIVE_LOW: GPIO state low for cc1.
- orientation-switch: must be present.
Required sub-node:
- port: specify the remote endpoint of typec switch consumer.
Example:
ptn36043 {
compatible = "nxp,ptn36043";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ss_sel>;
gpios = <&gpio3 15 GPIO_ACTIVE_HIGH>;
orientation-switch;
port {
usb3_data_ss: endpoint {
remote-endpoint = <&typec_con_ss>;
};
};
};

View File

@ -42,6 +42,8 @@ Optional properties:
- quirk-broken-port-ped: set if the controller has broken port disable mechanism
- imod-interval-ns: default interrupt moderation interval is 5000ns
- phys : see usb-hcd.yaml in the current directory
- usb3-resume-missing-cas: set if the CAS(Cold Attach Status) may lose in case
device plugged in while system sleep.
additionally the properties from usb-hcd.yaml (in the current directory) are
supported.

View File

@ -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

View File

@ -12,8 +12,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_CDNS3) += cdns3/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/

View File

@ -1,46 +1,26 @@
config USB_CDNS3
tristate "Cadence USB3 Dual-Role Controller"
depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
select USB_XHCI_PLATFORM if USB_XHCI_HCD
select USB_ROLE_SWITCH
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.
Say Y here if your system has a cadence USB3 dual-role controller.
It supports: dual-role switch Host-only, and Peripheral-only.
If you choose to build this driver is a dynamically linked
as module, the module will be called cdns3.ko.
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=y || USB_GADGET=USB_CDNS3
depends on USB_GADGET
help
Say Y here to enable device controller functionality of the
Cadence USBSS-DEV driver.
This controller supports FF, HS and SS mode. It doesn't support
LS and SSP mode.
cadence usb3 driver.
config USB_CDNS3_HOST
bool "Cadence USB3 host controller"
depends on USB=y || USB=USB_CDNS3
depends on USB_XHCI_HCD
help
Say Y here to enable host controller functionality of the
Cadence driver.
Host controller is compliant with XHCI so it will use
standard XHCI driver.
config USB_CDNS3_PCI_WRAP
tristate "Cadence USB3 support on PCIe-based platforms"
depends on USB_PCI && ACPI
default USB_CDNS3
help
If you're using the USBSS Core IP with a PCIe, please say
'Y' or 'M' here.
If you choose to build this driver as module it will
be dynamically linked and module will be called cdns3-pci.ko
cadence usb3 driver.
endif

View File

@ -1,16 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_trace.o := -I$(src)
CFLAGS_trace.o := -I$(src)
cdns3-y := core.o drd.o
obj-$(CONFIG_USB_CDNS3) += cdns3.o
obj-$(CONFIG_USB_CDNS3) += cdns3.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
cdns3-$(CONFIG_TRACING) += trace.o
endif
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
cdns3-y := core.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o trace.o
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o

View File

@ -0,0 +1,174 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
#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 SW_RESET_MASK (0x3f << 26)
#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 MDCTRL_CLK_SEL (1 << 7)
#define MODE_STRAP_MASK (0x7)
#define DEV_MODE (1 << 2)
#define HOST_MODE (1 << 1)
#define OTG_MODE (1 << 0)
/* USB3_INT_REG */
#define CLK_125_REQ (1 << 29)
#define LPM_CLK_REQ (1 << 28)
#define DEVU3_WAEKUP_EN (1 << 14)
#define OTG_WAKEUP_EN (1 << 12)
#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
/* USB3_CORE_STATUS */
#define MDCTRL_CLK_STATUS (1 << 15)
#define DEV_POWER_ON_READY (1 << 13)
#define HOST_POWER_ON_READY (1 << 12)
/* USB3_SSPHY_STATUS */
#define PHY_REFCLK_REQ (1 << 0)
/* 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)
#define TB_ADDR_TX_RCVDETSC_CTRL (0x4124 * 4)
/* Register bits definition */
/* TB_ADDR_TX_RCVDETSC_CTRL */
#define RXDET_IN_P3_32KHZ (1 << 0)
/* OTG registers definition */
#define OTGSTS 0x4
#define OTGREFCLK 0xc
/* Register bits definition */
/* OTGSTS */
#define OTG_NRDY (1 << 11)
/* OTGREFCLK */
#define OTG_STB_CLK_SWITCH_EN (1 << 31)
/* xHCI registers definition */
#define XECP_PORT_CAP_REG 0x8000
#define XECP_PM_PMCSR 0x8018
#define XECP_AUX_CTRL_REG1 0x8120
/* Register bits definition */
/* XECP_PORT_CAP_REG */
#define LPM_2_STB_SWITCH_EN (1 << 25)
/* XECP_AUX_CTRL_REG1 */
#define CFG_RXDET_P3_EN (1 << 15)
/* XECP_PM_PMCSR */
#define PS_MASK (3 << 0)
#define PS_D0 0
#define PS_D1 (1 << 0)
#endif /* __DRIVERS_USB_CDNS3_NXP_H */

View File

@ -1,204 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS PCI Glue driver
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
struct cdns3_wrap {
struct platform_device *plat_dev;
struct resource dev_res[6];
int devfn;
};
#define RES_IRQ_HOST_ID 0
#define RES_IRQ_PERIPHERAL_ID 1
#define RES_IRQ_OTG_ID 2
#define RES_HOST_ID 3
#define RES_DEV_ID 4
#define RES_DRD_ID 5
#define PCI_BAR_HOST 0
#define PCI_BAR_DEV 2
#define PCI_BAR_OTG 0
#define PCI_DEV_FN_HOST_DEVICE 0
#define PCI_DEV_FN_OTG 1
#define PCI_DRIVER_NAME "cdns3-pci-usbss"
#define PLAT_DRIVER_NAME "cdns-usb3"
#define CDNS_VENDOR_ID 0x17cd
#define CDNS_DEVICE_ID 0x0100
static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
{
struct pci_dev *func;
/*
* Gets the second function.
* It's little tricky, but this platform has two function.
* The fist keeps resources for Host/Device while the second
* keeps resources for DRD/OTG.
*/
func = pci_get_device(pdev->vendor, pdev->device, NULL);
if (unlikely(!func))
return NULL;
if (func->devfn == pdev->devfn) {
func = pci_get_device(pdev->vendor, pdev->device, func);
if (unlikely(!func))
return NULL;
}
return func;
}
static int cdns3_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct platform_device_info plat_info;
struct cdns3_wrap *wrap;
struct resource *res;
struct pci_dev *func;
int err;
/*
* for GADGET/HOST PCI (devfn) function number is 0,
* for OTG PCI (devfn) function number is 1
*/
if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
pdev->devfn != PCI_DEV_FN_OTG))
return -EINVAL;
func = cdns3_get_second_fun(pdev);
if (unlikely(!func))
return -EINVAL;
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
return err;
}
pci_set_master(pdev);
if (pci_is_enabled(func)) {
wrap = pci_get_drvdata(func);
} else {
wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
if (!wrap) {
pci_disable_device(pdev);
return -ENOMEM;
}
}
res = wrap->dev_res;
if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
/* function 0: host(BAR_0) + device(BAR_1).*/
dev_dbg(&pdev->dev, "Initialize Device resources\n");
res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV);
res[RES_DEV_ID].name = "dev";
res[RES_DEV_ID].flags = IORESOURCE_MEM;
dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
&res[RES_DEV_ID].start);
res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
res[RES_HOST_ID].name = "xhci";
res[RES_HOST_ID].flags = IORESOURCE_MEM;
dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
&res[RES_HOST_ID].start);
/* Interrupt for XHCI */
wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
/* Interrupt device. It's the same as for HOST. */
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
} else {
res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
res[RES_DRD_ID].name = "otg";
res[RES_DRD_ID].flags = IORESOURCE_MEM;
dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
&res[RES_DRD_ID].start);
/* Interrupt for OTG/DRD. */
wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
}
if (pci_is_enabled(func)) {
/* set up platform device info */
memset(&plat_info, 0, sizeof(plat_info));
plat_info.parent = &pdev->dev;
plat_info.fwnode = pdev->dev.fwnode;
plat_info.name = PLAT_DRIVER_NAME;
plat_info.id = pdev->devfn;
wrap->devfn = pdev->devfn;
plat_info.res = wrap->dev_res;
plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
plat_info.dma_mask = pdev->dma_mask;
/* register platform device */
wrap->plat_dev = platform_device_register_full(&plat_info);
if (IS_ERR(wrap->plat_dev)) {
pci_disable_device(pdev);
err = PTR_ERR(wrap->plat_dev);
kfree(wrap);
return err;
}
}
pci_set_drvdata(pdev, wrap);
return err;
}
static void cdns3_pci_remove(struct pci_dev *pdev)
{
struct cdns3_wrap *wrap;
struct pci_dev *func;
func = cdns3_get_second_fun(pdev);
wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
if (wrap->devfn == pdev->devfn)
platform_device_unregister(wrap->plat_dev);
if (!pci_is_enabled(func))
kfree(wrap);
}
static const struct pci_device_id cdns3_pci_ids[] = {
{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
{ 0, }
};
static struct pci_driver cdns3_pci_driver = {
.name = PCI_DRIVER_NAME,
.id_table = cdns3_pci_ids,
.probe = cdns3_pci_probe,
.remove = cdns3_pci_remove,
};
module_pci_driver(cdns3_pci_driver);
MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Header File.
/**
* core.h - Cadence USB3 DRD Controller Core header file
*
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2018-2019 Cadence.
* Copyright 2017-2019 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#ifndef __LINUX_CDNS3_CORE_H
#define __LINUX_CDNS3_CORE_H
#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
@ -23,76 +35,100 @@ struct cdns3;
* @suspend: suspend callback for this role
* @resume: resume callback for this role
* @irq: irq handler for this role
* @thread_irq: thread irq handler for this role
* @name: role name string (host/gadget)
* @state: current state
*/
struct cdns3_role_driver {
int (*start)(struct cdns3 *cdns);
void (*stop)(struct cdns3 *cdns);
int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
int (*resume)(struct cdns3 *cdns, bool hibernated);
int (*start)(struct cdns3 *);
void (*stop)(struct cdns3 *);
int (*suspend)(struct cdns3 *, bool do_wakeup);
int (*resume)(struct cdns3 *, bool hibernated);
irqreturn_t (*irq)(struct cdns3 *);
irqreturn_t (*thread_irq)(struct cdns3 *);
const char *name;
#define CDNS3_ROLE_STATE_INACTIVE 0
#define CDNS3_ROLE_STATE_ACTIVE 1
int state;
};
#define CDNS3_XHCI_RESOURCES_NUM 2
#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
* @otg_res: the resource for otg
* @otg_v0_regs: pointer to base of v0 otg registers
* @otg_v1_regs: pointer to base of v1 otg registers
* @none_core_regs: pointer to base of nxp wrapper registers
* @phy_regs: pointer to base of phy registers
* @otg_regs: pointer to base of otg registers
* @otg_irq: irq number for otg controller
* @dev_irq: irq number for device controller
* @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
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @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
* @in_lpm: the controller in low power mode
* @wakeup_int: the wakeup interrupt
* @mutex: the mutex for concurrent code at driver
* @dr_mode: supported mode of operation it can be only Host, only Device
* or OTG mode that allow to switch between Device and Host mode.
* This field based on firmware setting, kernel configuration
* and hardware configuration.
* @role_sw: pointer to role switch object.
* @role_override: set 1 if role rely on SW.
*/
struct cdns3 {
struct device *dev;
void __iomem *xhci_regs;
struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
struct cdns3_usb_regs __iomem *dev_regs;
struct resource otg_res;
struct cdns3_otg_legacy_regs *otg_v0_regs;
struct cdns3_otg_regs *otg_v1_regs;
struct cdns3_otg_common_regs *otg_regs;
#define CDNS3_CONTROLLER_V0 0
#define CDNS3_CONTROLLER_V1 1
u32 version;
int otg_irq;
int dev_irq;
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
enum usb_role role;
struct platform_device *host_dev;
struct cdns3_device *gadget_dev;
struct phy *usb2_phy;
struct phy *usb3_phy;
/* mutext used in workqueue*/
struct mutex mutex;
enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw;
int role_override;
struct device *dev;
void __iomem *xhci_regs;
struct resource *xhci_res;
struct cdns3_usb_regs __iomem *dev_regs;
void __iomem *none_core_regs;
void __iomem *phy_regs;
void __iomem *otg_regs;
int irq;
struct cdns3_role_driver *roles[CDNS3_ROLE_END];
enum cdns3_roles role;
struct device *host_dev;
struct cdns3_device *gadget_dev;
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;
bool in_lpm;
bool wakeup_int;
struct mutex mutex;
};
int cdns3_hw_role_switch(struct cdns3 *cdns);
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];
}
#endif /* __LINUX_CDNS3_CORE_H */
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;
mutex_lock(&cdns->mutex);
cdns->role = role;
ret = cdns->roles[role]->start(cdns);
mutex_unlock(&cdns->mutex);
return ret;
}
static inline void cdns3_role_stop(struct cdns3 *cdns)
{
enum cdns3_roles role = cdns->role;
if (role == CDNS3_ROLE_END)
return;
mutex_lock(&cdns->mutex);
cdns->roles[role]->stop(cdns);
cdns->role = CDNS3_ROLE_END;
mutex_unlock(&cdns->mutex);
}
int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
#endif /* __DRIVERS_USB_CDNS3_CORE_H */

View File

@ -10,8 +10,6 @@
#ifndef __LINUX_CDNS3_DEBUG
#define __LINUX_CDNS3_DEBUG
#include "core.h"
static inline char *cdns3_decode_usb_irq(char *str,
enum usb_device_speed speed,
u32 usb_ists)
@ -28,12 +26,7 @@ static inline char *cdns3_decode_usb_irq(char *str,
ret += sprintf(str + ret, "Disconnection ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "suspended ");
if (usb_ists & USB_ISTS_L1ENTI)
ret += sprintf(str + ret, "L1 enter ");
if (usb_ists & USB_ISTS_L1EXTI)
ret += sprintf(str + ret, "L1 exit ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "L2 enter ");
if (usb_ists & USB_ISTS_L2EXTI)
ret += sprintf(str + ret, "L2 exit ");
if (usb_ists & USB_ISTS_U3EXTI)
@ -158,4 +151,5 @@ static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
return str;
}
void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...);
#endif /*__LINUX_CDNS3_DEBUG*/

View File

@ -0,0 +1,894 @@
/**
* 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__DEVDS__MASK 0x00008000U
#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__STMODE 0x00000200U
#define USB_CMD__TMODE_SEL(x) (x << 10)
#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__ */

View File

@ -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__ */

View File

@ -1,381 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2019 Texas Instruments
*
* Author: Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*
*
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/usb/otg.h>
#include "gadget.h"
#include "drd.h"
#include "core.h"
/**
* cdns3_set_mode - change mode of OTG Core
* @cdns: pointer to context structure
* @mode: selected mode from cdns_role
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
{
int ret = 0;
u32 reg;
switch (mode) {
case USB_DR_MODE_PERIPHERAL:
break;
case USB_DR_MODE_HOST:
break;
case USB_DR_MODE_OTG:
dev_dbg(cdns->dev, "Set controller to OTG mode\n");
if (cdns->version == CDNS3_CONTROLLER_V1) {
reg = readl(&cdns->otg_v1_regs->override);
reg |= OVERRIDE_IDPULLUP;
writel(reg, &cdns->otg_v1_regs->override);
} else {
reg = readl(&cdns->otg_v0_regs->ctrl1);
reg |= OVERRIDE_IDPULLUP_V0;
writel(reg, &cdns->otg_v0_regs->ctrl1);
}
/*
* Hardware specification says: "ID_VALUE must be valid within
* 50ms after idpullup is set to '1" so driver must wait
* 50ms before reading this pin.
*/
usleep_range(50000, 60000);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
return -EINVAL;
}
return ret;
}
int cdns3_get_id(struct cdns3 *cdns)
{
int id;
id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
dev_dbg(cdns->dev, "OTG ID: %d", id);
return id;
}
int cdns3_get_vbus(struct cdns3 *cdns)
{
int vbus;
vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
return vbus;
}
int cdns3_is_host(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_HOST)
return 1;
else if (!cdns3_get_id(cdns))
return 1;
return 0;
}
int cdns3_is_device(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
return 1;
else if (cdns->dr_mode == USB_DR_MODE_OTG)
if (cdns3_get_id(cdns))
return 1;
return 0;
}
/**
* cdns3_otg_disable_irq - Disable all OTG interrupts
* @cdns: Pointer to controller context structure
*/
static void cdns3_otg_disable_irq(struct cdns3 *cdns)
{
writel(0, &cdns->otg_regs->ien);
}
/**
* cdns3_otg_enable_irq - enable id and sess_valid interrupts
* @cdns: Pointer to controller context structure
*/
static void cdns3_otg_enable_irq(struct cdns3 *cdns)
{
writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
}
/**
* cdns3_drd_switch_host - start/stop host
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_XHCI_READY,
1, 100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
return ret;
}
} else {
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till H_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK),
1, 2000000);
}
return 0;
}
/**
* cdns3_drd_switch_gadget - start/stop gadget
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_DEV_READY,
1, 100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
return ret;
}
} else {
/*
* driver should wait at least 10us after disabling Device
* before turning-off Device (DEV_BUS_DROP)
*/
usleep_range(20, 30);
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till DEV_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK),
1, 2000000);
}
return 0;
}
/**
* cdns3_init_otg_mode - initialize drd controller
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_init_otg_mode(struct cdns3 *cdns)
{
int ret = 0;
cdns3_otg_disable_irq(cdns);
/* clear all interrupts */
writel(~0, &cdns->otg_regs->ivect);
ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
if (ret)
return ret;
cdns3_otg_enable_irq(cdns);
return ret;
}
/**
* cdns3_drd_update_mode - initialize mode of operation
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_update_mode(struct cdns3 *cdns)
{
int ret = 0;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
break;
case USB_DR_MODE_HOST:
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
break;
case USB_DR_MODE_OTG:
ret = cdns3_init_otg_mode(cdns);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n",
cdns->dr_mode);
return -EINVAL;
}
return ret;
}
static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
{
struct cdns3 *cdns = data;
cdns3_hw_role_switch(cdns);
return IRQ_HANDLED;
}
/**
* cdns3_drd_irq - interrupt handler for OTG events
*
* @irq: irq number for cdns3 core device
* @data: structure of cdns3
*
* Returns IRQ_HANDLED or IRQ_NONE
*/
static irqreturn_t cdns3_drd_irq(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
struct cdns3 *cdns = data;
u32 reg;
if (cdns->dr_mode != USB_DR_MODE_OTG)
return ret;
reg = readl(&cdns->otg_regs->ivect);
if (!reg)
return ret;
if (reg & OTGIEN_ID_CHANGE_INT) {
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
cdns3_get_id(cdns));
ret = IRQ_WAKE_THREAD;
}
if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
cdns3_get_vbus(cdns));
ret = IRQ_WAKE_THREAD;
}
writel(~0, &cdns->otg_regs->ivect);
return ret;
}
int cdns3_drd_init(struct cdns3 *cdns)
{
void __iomem *regs;
int ret = 0;
u32 state;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
if (IS_ERR(regs))
return PTR_ERR(regs);
/* Detection of DRD version. Controller has been released
* in two versions. Both are similar, but they have same changes
* in register maps.
* The first register in old version is command register and it's read
* only, so driver should read 0 from it. On the other hand, in v1
* the first register contains device ID number which is not set to 0.
* Driver uses this fact to detect the proper version of
* controller.
*/
cdns->otg_v0_regs = regs;
if (!readl(&cdns->otg_v0_regs->cmd)) {
cdns->version = CDNS3_CONTROLLER_V0;
cdns->otg_v1_regs = NULL;
cdns->otg_regs = regs;
writel(1, &cdns->otg_v0_regs->simulate);
dev_info(cdns->dev, "DRD version v0 (%08x)\n",
readl(&cdns->otg_v0_regs->version));
} else {
cdns->otg_v0_regs = NULL;
cdns->otg_v1_regs = regs;
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
cdns->version = CDNS3_CONTROLLER_V1;
writel(1, &cdns->otg_v1_regs->simulate);
dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
readl(&cdns->otg_v1_regs->did),
readl(&cdns->otg_v1_regs->rid));
}
state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
/* Update dr_mode according to STRAP configuration. */
cdns->dr_mode = USB_DR_MODE_OTG;
if (state == OTGSTS_STRAP_HOST) {
dev_dbg(cdns->dev, "Controller strapped to HOST\n");
cdns->dr_mode = USB_DR_MODE_HOST;
} else if (state == OTGSTS_STRAP_GADGET) {
dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
}
ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
cdns3_drd_irq,
cdns3_drd_thread_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
dev_err(cdns->dev, "couldn't get otg_irq\n");
return ret;
}
state = readl(&cdns->otg_regs->sts);
if (OTGSTS_OTG_NRDY(state) != 0) {
dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
return -ENODEV;
}
return ret;
}
int cdns3_drd_exit(struct cdns3 *cdns)
{
cdns3_otg_disable_irq(cdns);
return 0;
}

View File

@ -1,167 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USB3 DRD header file.
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS3_DRD
#define __LINUX_CDNS3_DRD
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include "core.h"
/* DRD register interface for version v1. */
struct cdns3_otg_regs {
__le32 did;
__le32 rid;
__le32 capabilities;
__le32 reserved1;
__le32 cmd;
__le32 sts;
__le32 state;
__le32 reserved2;
__le32 ien;
__le32 ivect;
__le32 refclk;
__le32 tmr;
__le32 reserved3[4];
__le32 simulate;
__le32 override;
__le32 susp_ctrl;
__le32 reserved4;
__le32 anasts;
__le32 adp_ramp_time;
__le32 ctrl1;
__le32 ctrl2;
};
/* DRD register interface for version v0. */
struct cdns3_otg_legacy_regs {
__le32 cmd;
__le32 sts;
__le32 state;
__le32 refclk;
__le32 ien;
__le32 ivect;
__le32 reserved1[3];
__le32 tmr;
__le32 reserved2[2];
__le32 version;
__le32 capabilities;
__le32 reserved3[2];
__le32 simulate;
__le32 reserved4[5];
__le32 ctrl1;
};
/*
* Common registers interface for both version of DRD.
*/
struct cdns3_otg_common_regs {
__le32 cmd;
__le32 sts;
__le32 state;
__le32 different1;
__le32 ien;
__le32 ivect;
};
/* CDNS_RID - bitmasks */
#define CDNS_RID(p) ((p) & GENMASK(15, 0))
/* CDNS_VID - bitmasks */
#define CDNS_DID(p) ((p) & GENMASK(31, 0))
/* OTGCMD - bitmasks */
/* "Request the bus for Device mode. */
#define OTGCMD_DEV_BUS_REQ BIT(0)
/* Request the bus for Host mode */
#define OTGCMD_HOST_BUS_REQ BIT(1)
/* Enable OTG mode. */
#define OTGCMD_OTG_EN BIT(2)
/* Disable OTG mode */
#define OTGCMD_OTG_DIS BIT(3)
/*"Configure OTG as A-Device. */
#define OTGCMD_A_DEV_EN BIT(4)
/*"Configure OTG as A-Device. */
#define OTGCMD_A_DEV_DIS BIT(5)
/* Drop the bus for Device mod e. */
#define OTGCMD_DEV_BUS_DROP BIT(8)
/* Drop the bus for Host mode*/
#define OTGCMD_HOST_BUS_DROP BIT(9)
/* Power Down USBSS-DEV. */
#define OTGCMD_DEV_POWER_OFF BIT(11)
/* Power Down CDNSXHCI. */
#define OTGCMD_HOST_POWER_OFF BIT(12)
/* OTGIEN - bitmasks */
/* ID change interrupt enable */
#define OTGIEN_ID_CHANGE_INT BIT(0)
/* Vbusvalid fall detected interrupt enable.*/
#define OTGIEN_VBUSVALID_RISE_INT BIT(4)
/* Vbusvalid fall detected interrupt enable */
#define OTGIEN_VBUSVALID_FALL_INT BIT(5)
/* OTGSTS - bitmasks */
/*
* Current value of the ID pin. It is only valid when idpullup in
* OTGCTRL1_TYPE register is set to '1'.
*/
#define OTGSTS_ID_VALUE BIT(0)
/* Current value of the vbus_valid */
#define OTGSTS_VBUS_VALID BIT(1)
/* Current value of the b_sess_vld */
#define OTGSTS_SESSION_VALID BIT(2)
/*Device mode is active*/
#define OTGSTS_DEV_ACTIVE BIT(3)
/* Host mode is active. */
#define OTGSTS_HOST_ACTIVE BIT(4)
/* OTG Controller not ready. */
#define OTGSTS_OTG_NRDY_MASK BIT(11)
#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
/*
* Value of the strap pins.
* 000 - no default configuration
* 010 - Controller initiall configured as Host
* 100 - Controller initially configured as Device
*/
#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
#define OTGSTS_STRAP_HOST_OTG 0x01
#define OTGSTS_STRAP_HOST 0x02
#define OTGSTS_STRAP_GADGET 0x04
/* Host mode is turned on. */
#define OTGSTS_XHCI_READY BIT(26)
/* "Device mode is turned on .*/
#define OTGSTS_DEV_READY BIT(27)
/* OTGSTATE- bitmasks */
#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3)
#define OTGSTATE_HOST_STATE_IDLE 0x0
#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7
#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
/* OTGREFCLK - bitmasks */
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
/* OVERRIDE - bitmasks */
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24)
int cdns3_is_host(struct cdns3 *cdns);
int cdns3_is_device(struct cdns3 *cdns);
int cdns3_get_id(struct cdns3 *cdns);
int cdns3_get_vbus(struct cdns3 *cdns);
int cdns3_drd_init(struct cdns3 *cdns);
int cdns3_drd_exit(struct cdns3 *cdns);
int cdns3_drd_update_mode(struct cdns3 *cdns);
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
#endif /* __LINUX_CDNS3_DRD */

View File

@ -7,14 +7,16 @@
*
* Authors: Pawel Jez <pjez@cadence.com>,
* Pawel Laszczak <pawell@cadence.com>
* Peter Chen <peter.chen@nxp.com>
* Peter Chen <peter.chen@nxp.com>
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/usb/composite.h>
#include <linux/iopoll.h>
#include "gadget.h"
#include "trace.h"
#include "core.h"
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@ -32,25 +34,14 @@ static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
unsigned int length, int erdy, int zlp)
unsigned int length, int erdy)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool[0].length = TRB_LEN(length);
if (zlp) {
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool[1].length = TRB_LEN(0);
priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL);
} else {
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL);
priv_ep->trb_pool[1].control = 0;
}
priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool->length = TRB_LEN(length);
priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
@ -61,12 +52,9 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
readl(&regs->ep_traddr));
/* TRB should be prepared before starting transfer. */
/* TRB should be prepared before starting transfer */
writel(EP_CMD_DRDY, &regs->ep_cmd);
/* Resume controller before arming transfer. */
__cdns3_gadget_wakeup(priv_dev);
if (erdy)
writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
}
@ -82,11 +70,14 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
int ret;
int ret = 0;
spin_unlock(&priv_dev->lock);
priv_dev->setup_pending = 1;
ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
if (priv_dev->gadget_driver && priv_dev->gadget_driver->setup
&& get_gadget_data(&priv_dev->gadget))
ret = priv_dev->gadget_driver->setup(&priv_dev->gadget,
ctrl_req);
priv_dev->setup_pending = 0;
spin_lock(&priv_dev->lock);
return ret;
@ -97,7 +88,7 @@ static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
priv_dev->ep0_data_dir = 0;
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(struct usb_ctrlrequest), 0, 0);
sizeof(struct usb_ctrlrequest), 0);
}
static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
@ -111,7 +102,7 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
list_del_init(&request->list);
if (send_stall) {
trace_cdns3_halt(priv_ep, send_stall, 0);
cdns3_dbg(priv_ep->cdns3_dev, "STALL for ep0\n");
/* set_stall on ep0 */
cdns3_select_ep(priv_dev, 0x00);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
@ -122,8 +113,6 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
cdns3_allow_enable_l1(priv_dev, 1);
}
/**
@ -234,11 +223,10 @@ static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl)
{
struct cdns3_endpoint *priv_ep;
__le16 *response_pkt;
u16 usb_status = 0;
u32 recip;
u8 index;
u32 reg;
recip = ctrl->bRequestType & USB_RECIP_MASK;
@ -254,6 +242,8 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
if (priv_dev->gadget.speed != USB_SPEED_SUPER)
break;
reg = readl(&priv_dev->regs->usb_sts);
if (priv_dev->u1_allowed)
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
@ -264,13 +254,9 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
case USB_RECIP_INTERFACE:
return cdns3_ep0_delegate_req(priv_dev, ctrl);
case USB_RECIP_ENDPOINT:
index = cdns3_ep_addr_to_index(ctrl->wIndex);
priv_ep = priv_dev->eps[index];
/* check if endpoint is stalled or stall is pending */
/* check if endpoint is stalled */
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)) ||
(priv_ep->flags & EP_STALL_PENDING))
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
usb_status = BIT(USB_ENDPOINT_HALT);
break;
default:
@ -281,7 +267,7 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
*response_pkt = cpu_to_le16(usb_status);
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(*response_pkt), 1, 0);
sizeof(*response_pkt), 1);
return 0;
}
@ -293,13 +279,15 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
u32 wIndex;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
state = priv_dev->gadget.state;
speed = priv_dev->gadget.speed;
switch (wValue) {
switch (ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
priv_dev->wake_up_flag = !!set;
break;
@ -338,7 +326,7 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
* for sending status stage.
* This time should be less then 3ms.
*/
mdelay(1);
usleep_range(1000, 2000);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
@ -392,12 +380,40 @@ static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (set)
__cdns3_gadget_ep_set_halt(priv_ep);
else if (!(priv_ep->flags & EP_WEDGE))
ret = __cdns3_gadget_ep_clear_halt(priv_ep);
if (set) {
cdns3_dbg(priv_ep->cdns3_dev, "Stall endpoint %s\n",
priv_ep->name);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
priv_ep->flags |= EP_STALL;
} else {
struct usb_request *request;
cdns3_select_ep(priv_dev, 0x00);
if (priv_dev->eps[index]->flags & EP_WEDGE) {
cdns3_select_ep(priv_dev, 0x00);
return 0;
}
cdns3_dbg(priv_ep->cdns3_dev, "Clear Stalled endpoint %s\n",
priv_ep->name);
writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
/* wait for EPRST cleared */
ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
EP_CMD_EPRST, 0, 100);
if (ret)
return -EINVAL;
priv_ep->flags &= ~EP_STALL;
request = cdns3_next_request(&priv_ep->pending_req_list);
if (request) {
cdns3_dbg(priv_ep->cdns3_dev, "Resume transfer for %s\n",
priv_ep->name);
cdns3_rearm_transfer(priv_ep, 1);
}
}
return ret;
}
@ -457,7 +473,7 @@ static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
return -EINVAL;
}
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
return 0;
}
@ -544,6 +560,30 @@ void cdns3_pending_setup_status_handler(struct work_struct *work)
spin_unlock_irqrestore(&priv_dev->lock, flags);
}
/**
* cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
* @priv_ep: The endpoint to whom the request belongs to
* @priv_req: The request we're giving back
* @status: completion code for the request
*
* Must be called with controller's lock held and interrupts disabled. This
* function will unmap @req and call its ->complete() callback to notify upper
* layers that it has completed.
*/
void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
int status)
{
struct cdns3_endpoint *priv_ep;
struct usb_request *request;
priv_ep = priv_dev->eps[0];
request = cdns3_next_request(&priv_ep->pending_req_list);
priv_ep->dir = priv_dev->ep0_data_dir;
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
}
/**
* cdns3_ep0_setup_phase - Handling setup USB requests
* @priv_dev: extended gadget object
@ -600,6 +640,10 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
priv_ep->dir = priv_dev->ep0_data_dir;
if (request->zero && request->length && priv_ep->dir
&& (request->length % priv_dev->gadget.ep0->maxpacket == 0))
cdns3_ep0_run_transfer(priv_dev, request->dma, 0, 1);
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
}
@ -640,12 +684,7 @@ void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
__pending_setup_status_handler(priv_dev);
if (ep_sts_reg & EP_STS_SETUP)
priv_dev->wait_for_setup = 1;
if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
priv_dev->wait_for_setup = 0;
cdns3_allow_enable_l1(priv_dev, 0);
if ((ep_sts_reg & EP_STS_SETUP)) {
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
@ -709,9 +748,10 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
unsigned long flags;
int erdy_sent = 0;
int ret = 0;
u8 zlp = 0;
trace_cdns3_ep0_queue(priv_dev, request);
cdns3_dbg(priv_ep->cdns3_dev, "Queue to Ep0%s L: %d\n",
priv_dev->ep0_data_dir ? "IN" : "OUT",
request->length);
/* cancel the request if controller receive new SETUP packet. */
if (cdns3_check_new_setup(priv_dev))
@ -728,8 +768,6 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
if (!erdy_sent)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
cdns3_allow_enable_l1(priv_dev, 1);
request->actual = 0;
priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request;
@ -762,13 +800,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
request->status = -EINPROGRESS;
list_add_tail(&request->list, &priv_ep->pending_req_list);
if (request->zero && request->length &&
(request->length % ep->maxpacket == 0))
zlp = 1;
cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
@ -838,13 +870,6 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
/* init ep out */
cdns3_select_ep(priv_dev, USB_DIR_OUT);
if (priv_dev->dev_ver >= DEV_VER_V3) {
cdns3_set_register_bit(&priv_dev->regs->dtrans,
BIT(0) | BIT(16));
cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
BIT(0) | BIT(16));
}
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);

View File

@ -2,8 +2,7 @@
/*
* Cadence USBSS DRD Driver - Gadget Export APIs.
*
* Copyright (C) 2017 NXP
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2017-2019 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*/

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
* USBSS device controller driver header file
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2017-2019 NXP
*
* Author: Pawel Laszczak <pawell@cadence.com>
* Pawel Jez <pjez@cadence.com>
@ -20,52 +20,42 @@
/**
* struct cdns3_usb_regs - device controller registers.
* @usb_conf: Global Configuration.
* @usb_sts: Global Status.
* @usb_cmd: Global Command.
* @usb_itpn: ITP/SOF number.
* @usb_lpm: Global Command.
* @usb_ien: USB Interrupt Enable.
* @usb_ists: USB Interrupt Status.
* @ep_sel: Endpoint Select.
* @ep_traddr: Endpoint Transfer Ring Address.
* @ep_cfg: Endpoint Configuration.
* @ep_cmd: Endpoint Command.
* @ep_sts: Endpoint Status.
* @ep_sts_sid: Endpoint Status.
* @ep_sts_en: Endpoint Status Enable.
* @drbl: Doorbell.
* @ep_ien: EP Interrupt Enable.
* @ep_ists: EP Interrupt Status.
* @usb_pwr: Global Power Configuration.
* @usb_conf2: Global Configuration 2.
* @usb_cap1: Capability 1.
* @usb_cap2: Capability 2.
* @usb_cap3: Capability 3.
* @usb_cap4: Capability 4.
* @usb_cap5: Capability 5.
* @usb_cap6: Capability 6.
* @usb_cpkt1: Custom Packet 1.
* @usb_cpkt2: Custom Packet 2.
* @usb_cpkt3: Custom Packet 3.
* @ep_dma_ext_addr: Upper address for DMA operations.
* @buf_addr: Address for On-chip Buffer operations.
* @buf_data: Data for On-chip Buffer operations.
* @buf_ctrl: On-chip Buffer Access Control.
* @dtrans: DMA Transfer Mode.
* @tdl_from_trb: Source of TD Configuration.
* @tdl_beh: TDL Behavior Configuration.
* @ep_tdl: Endpoint TDL.
* @tdl_beh2: TDL Behavior 2 Configuration.
* @dma_adv_td: DMA Advance TD Configuration.
* @usb_conf: Global Configuration Register.
* @usb_sts: Global Status Register.
* @usb_cmd: Global Command Register.
* @usb_itpn: ITP/SOF number Register.
* @usb_lpm: Global Command Register.
* @usb_ien: USB Interrupt Enable Register.
* @usb_ists: USB Interrupt Status Register.
* @ep_sel: Endpoint Select Register.
* @ep_traddr: Endpoint Transfer Ring Address Register.
* @ep_cfg: Endpoint Configuration Register.
* @ep_cmd: Endpoint Command Register.
* @ep_sts: Endpoint Status Register.
* @ep_sts_sid: Endpoint Status Register.
* @ep_sts_en: Endpoint Status Register Enable.
* @drbl: Doorbell Register.
* @ep_ien: EP Interrupt Enable Register.
* @ep_ists: EP Interrupt Status Register.
* @usb_pwr: Global Power Configuration Register.
* @usb_conf2: Global Configuration Register 2.
* @usb_cap1: Capability Register 1.
* @usb_cap2: Capability Register 2.
* @usb_cap3: Capability Register 3.
* @usb_cap4: Capability Register 4.
* @usb_cap5: Capability Register 5.
* @usb_cap6: Capability Register 6.
* @usb_cpkt1: Custom Packet Register 1.
* @usb_cpkt2: Custom Packet Register 2.
* @usb_cpkt3: Custom Packet Register 3.
* @reserved1: Reserved.
* @cfg_regs: Configuration.
* @cfg_regs: Configuration registers.
* @reserved2: Reserved.
* @dma_axi_ctrl: AXI Control.
* @dma_axi_ctrl: AXI Control register.
* @dma_axi_id: AXI ID register.
* @dma_axi_cap: AXI Capability.
* @dma_axi_ctrl0: AXI Control 0.
* @dma_axi_ctrl1: AXI Control 1.
* @dma_axi_cap: AXI Capability register.
* @dma_axi_ctrl0: AXI Control 0 register.
* @dma_axi_ctrl1: AXI Control 1 register.
*/
struct cdns3_usb_regs {
__le32 usb_conf;
@ -96,22 +86,12 @@ struct cdns3_usb_regs {
__le32 usb_cpkt1;
__le32 usb_cpkt2;
__le32 usb_cpkt3;
__le32 ep_dma_ext_addr;
__le32 buf_addr;
__le32 buf_data;
__le32 buf_ctrl;
__le32 dtrans;
__le32 tdl_from_trb;
__le32 tdl_beh;
__le32 ep_tdl;
__le32 tdl_beh2;
__le32 dma_adv_td;
__le32 reserved1[26];
__le32 reserved1[36];
__le32 cfg_reg1;
__le32 dbg_link1;
__le32 dbg_link2;
__le32 cfg_regs[74];
__le32 reserved2[51];
__le32 reserved2[34];
__le32 dma_axi_ctrl;
__le32 dma_axi_id;
__le32 dma_axi_cap;
@ -138,9 +118,9 @@ struct cdns3_usb_regs {
#define USB_CONF_BENDIAN BIT(6)
/* Device software reset. */
#define USB_CONF_SWRST BIT(7)
/* Singular DMA transfer mode. Only for VER < DEV_VER_V3*/
/* Singular DMA transfer mode. */
#define USB_CONF_DSING BIT(8)
/* Multiple DMA transfers mode. Only for VER < DEV_VER_V3 */
/* Multiple DMA transfers mode. */
#define USB_CONF_DMULT BIT(9)
/* DMA clock turn-off enable. */
#define USB_CONF_DMAOFFEN BIT(10)
@ -185,6 +165,19 @@ struct cdns3_usb_regs {
#define USB_CONF_LGO_U2 BIT(30)
/* SS.Inactive state entry request (used in SS mode) */
#define USB_CONF_LGO_SSINACT BIT(31)
/* USB_CONF2- bitmasks */
/*
* Writing 1 disables TDL calculation basing on TRB feature in controller
* for DMULT mode.
* Bit supported only for DEV_VER_V2 version.
*/
#define USB_CONF2_DIS_TDL_TRB BIT(1)
/*
* Writing 1 enables TDL calculation basing on TRB feature in controller
* for DMULT mode.
* Bit supported only for DEV_VER_V2 version.
*/
#define USB_CONF2_EN_TDL_TRB BIT(2)
/* USB_STS - bitmasks */
/*
@ -212,7 +205,6 @@ struct cdns3_usb_regs {
* DMA transfer configuration status.
* 0 - single request.
* 1 - multiple TRB chain
* Supported only for controller version < DEV_VER_V3
*/
#define USB_STS_DTRANS_MASK BIT(3)
#define USB_STS_DTRANS(p) ((p) & USB_STS_DTRANS_MASK)
@ -265,13 +257,6 @@ struct cdns3_usb_regs {
*/
#define USB_STS_IN_RST_MASK BIT(10)
#define USB_STS_IN_RST(p) ((p) & USB_STS_IN_RST_MASK)
/*
* Status of the "TDL calculation basing on TRB" feature.
* 0 - disabled
* 1 - enabled
* Supported only for DEV_VER_V2 controller version.
*/
#define USB_STS_TDL_TRB_ENABLED BIT(11)
/*
* Device enable Status.
* 0 - USB device is disabled (VBUS input is disconnected from internal logic).
@ -280,7 +265,7 @@ struct cdns3_usb_regs {
#define USB_STS_DEVS_MASK BIT(14)
#define USB_STS_DEVS(p) ((p) & USB_STS_DEVS_MASK)
/*
* Address status.
* DAddress statuss.
* 0 - USB device is default state.
* 1 - USB device is at least in address state.
*/
@ -365,7 +350,7 @@ struct cdns3_usb_regs {
#define USB_STS_DMAOFF_MASK BIT(30)
#define USB_STS_DMAOFF(p) ((p) & USB_STS_DMAOFF_MASK)
/*
* SFR Endian status.
* SFR Endian statuss.
* 0 - Little Endian order (default after hardware reset).
* 1 - Big Endian order.
*/
@ -475,7 +460,7 @@ struct cdns3_usb_regs {
#define USB_IEN_INIT (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
| USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
| USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
| USB_IEN_L2EXTIEN | USB_IEN_L1ENTIEN | USB_IEN_U3ENTIEN)
| USB_IEN_L2EXTIEN)
/* USB_ISTS - bitmasks */
/* SS Connection detected. */
@ -589,20 +574,14 @@ struct cdns3_usb_regs {
/*
* Transfer Descriptor Length write (used only for Bulk Stream capable
* endpoints in SS mode).
* Bit Removed from DEV_VER_V3 controller version.
*/
#define EP_CMD_STDL BIT(8)
/*
* Transfer Descriptor Length (used only in SS mode for bulk endpoints).
* Bits Removed from DEV_VER_V3 controller version.
*/
/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
#define EP_CMD_TDL_MASK GENMASK(15, 9)
#define EP_CMD_TDL_SET(p) (((p) << 9) & EP_CMD_TDL_MASK)
#define EP_CMD_TDL_GET(p) (((p) & EP_CMD_TDL_MASK) >> 9)
#define EP_CMD_TDL(p) (((p) << 9) & EP_CMD_TDL_MASK)
/* ERDY Stream ID value (used in SS mode). */
#define EP_CMD_ERDY_SID_MASK GENMASK(31, 16)
#define EP_CMD_ERDY_SID(p) (((p) << 16) & EP_CMD_ERDY_SID_MASK)
#define EP_CMD_ERDY_SID(p) (((p) << 16) & EP_CMD_SID_MASK)
/* EP_STS - bitmasks */
/* Setup transfer complete. */
@ -623,8 +602,8 @@ struct cdns3_usb_regs {
#define EP_STS_TRBERR BIT(7)
/* Not ready (used only in SS mode). */
#define EP_STS_NRDY BIT(8)
/* DMA busy bit. */
#define EP_STS_DBUSY BIT(9)
/* DMA busy. */
#define EP_STS_DBUSY(p) ((p) & BIT(9))
/* Endpoint Buffer Empty */
#define EP_STS_BUFFEMPTY(p) ((p) & BIT(10))
/* Current Cycle Status */
@ -702,7 +681,7 @@ struct cdns3_usb_regs {
#define EP_ISTS_EP_OUT0 BIT(0)
#define EP_ISTS_EP_IN0 BIT(16)
/* USB_PWR- bitmasks */
/* EP_PWR- bitmasks */
/*Power Shut Off capability enable*/
#define PUSB_PWR_PSO_EN BIT(0)
/*Power Shut Off capability disable*/
@ -721,21 +700,7 @@ struct cdns3_usb_regs {
/* This bit informs if Fast Registers Access is enabled. */
#define PUSB_PWR_FST_REG_ACCESS_STAT BIT(30)
/* Fast Registers Access Enable. */
#define PUSB_PWR_FST_REG_ACCESS BIT(31)
/* USB_CONF2- bitmasks */
/*
* Writing 1 disables TDL calculation basing on TRB feature in controller
* for DMULT mode.
* Bit supported only for DEV_VER_V2 version.
*/
#define USB_CONF2_DIS_TDL_TRB BIT(1)
/*
* Writing 1 enables TDL calculation basing on TRB feature in controller
* for DMULT mode.
* Bit supported only for DEV_VER_V2 version.
*/
#define USB_CONF2_EN_TDL_TRB BIT(2)
#define PUSB_PWR_FST_REG_ACCESS BIT(31)
/* USB_CAP1- bitmasks */
/*
@ -852,13 +817,6 @@ struct cdns3_usb_regs {
*/
#define USB_CAP1_OTG_READY(p) ((p) & BIT(27))
/*
* When set, indicates that controller supports automatic internal TDL
* calculation basing on the size provided in TRB (TRB[22:17]) for DMULT mode
* Supported only for DEV_VER_V2 controller version.
*/
#define USB_CAP1_TDL_FROM_TRB(p) ((p) & BIT(28))
/* USB_CAP2- bitmasks */
/*
* The actual size of the connected On-chip RAM memory in kB:
@ -950,13 +908,6 @@ struct cdns3_usb_regs {
*/
#define DBG_LINK1_LFPS_GEN_PING_SET BIT(27)
/* DMA_AXI_CTRL- bitmasks */
/* The mawprot pin configuration. */
#define DMA_AXI_CTRL_MARPROT(p) ((p) & GENMASK(2, 0))
/* The marprot pin configuration. */
#define DMA_AXI_CTRL_MAWPROT(p) (((p) & GENMASK(2, 0)) << 16)
#define DMA_AXI_CTRL_NON_SECURE 0x02
#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
@ -965,14 +916,10 @@ struct cdns3_usb_regs {
/*
* USBSS-DEV DMA interface.
*/
#define TRBS_PER_SEGMENT 40
#define TRBS_PER_SEGMENT 150
#define ISO_MAX_INTERVAL 10
#if TRBS_PER_SEGMENT < 2
#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
#endif
/*
*Only for ISOC endpoints - maximum number of TRBs is calculated as
* pow(2, bInterval-1) * number of usb requests. It is limitation made by
@ -1021,16 +968,6 @@ struct cdns3_trb {
*/
#define TRB_TOGGLE BIT(1)
/*
* Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
* processed while USB short packet was received. No more buffers defined by
* the TD will be used. DMA will automatically advance to next TD.
* - Shall be set to 0 by Software when putting TRB on the Transfer Ring
* - Shall be set to 1 by Controller when Short Packet condition for this TRB
* is detected independent if ISP is set or not.
*/
#define TRB_SP BIT(1)
/* Interrupt on short packet*/
#define TRB_ISP BIT(2)
/*Setting this bit enables FIFO DMA operation mode*/
@ -1041,9 +978,7 @@ struct cdns3_trb {
#define TRB_IOC BIT(5)
/* stream ID bitmasks. */
#define TRB_STREAM_ID_BITMASK GENMASK(31, 16)
#define TRB_STREAM_ID(p) ((p) << 16)
#define TRB_FIELD_TO_STREAMID(p) (((p) & TRB_STREAM_ID_BITMASK) >> 16)
#define TRB_STREAM_ID(p) ((p) & GENMASK(31, 16))
/* Size of TD expressed in USB packets for HS/FS mode. */
#define TRB_TDL_HS_SIZE(p) (((p) << 16) & GENMASK(31, 16))
@ -1083,7 +1018,6 @@ struct cdns3_trb {
#define CDNS3_EP_ISO_SS_BURST 3
#define CDNS3_MAX_NUM_DESCMISS_BUF 32
#define CDNS3_DESCMIS_BUF_SIZE 2048 /* Bytes */
#define CDNS3_WA2_NUM_BUFFERS 128
/*-------------------------------------------------------------------------*/
/* Used structs */
@ -1094,7 +1028,7 @@ struct cdns3_device;
* @endpoint: usb endpoint
* @pending_req_list: list of requests queuing on transfer ring.
* @deferred_req_list: list of requests waiting for queuing on transfer ring.
* @wa2_descmiss_req_list: list of requests internally allocated by driver.
* @descmiss_req_list: list of requests internally allocated by driver (WA2).
* @trb_pool: transfer ring - array of transaction buffers
* @trb_pool_dma: dma address of transfer ring
* @cdns3_dev: device associated with this endpoint
@ -1119,8 +1053,7 @@ struct cdns3_endpoint {
struct usb_ep endpoint;
struct list_head pending_req_list;
struct list_head deferred_req_list;
struct list_head wa2_descmiss_req_list;
int wa2_counter;
struct list_head descmiss_req_list;
struct cdns3_trb *trb_pool;
dma_addr_t trb_pool_dma;
@ -1129,19 +1062,17 @@ struct cdns3_endpoint {
char name[20];
#define EP_ENABLED BIT(0)
#define EP_STALLED BIT(1)
#define EP_STALL_PENDING BIT(2)
#define EP_WEDGE BIT(3)
#define EP_TRANSFER_STARTED BIT(4)
#define EP_UPDATE_EP_TRBADDR BIT(5)
#define EP_PENDING_REQUEST BIT(6)
#define EP_RING_FULL BIT(7)
#define EP_CLAIMED BIT(8)
#define EP_DEFERRED_DRDY BIT(9)
#define EP_QUIRK_ISO_OUT_EN BIT(10)
#define EP_QUIRK_END_TRANSFER BIT(11)
#define EP_QUIRK_EXTRA_BUF_DET BIT(12)
#define EP_QUIRK_EXTRA_BUF_EN BIT(13)
#define EP_STALL BIT(1)
#define EP_WEDGE BIT(2)
#define EP_TRANSFER_STARTED BIT(3)
#define EP_UPDATE_EP_TRBADDR BIT(4)
#define EP_PENDING_REQUEST BIT(5)
#define EP_RING_FULL BIT(6)
#define EP_CLAIMED BIT(7)
#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
#define EP_QUIRK_EXTRA_BUF_EN BIT(9)
#define EP_QUIRK_END_TRANSFER BIT(10)
u32 flags;
struct cdns3_request *descmis_req;
@ -1193,7 +1124,7 @@ struct cdns3_aligned_buf {
* @aligned_buf: object holds information about aligned buffer associated whit
* this endpoint
* @flags: flag specifying special usage of request
* @list: used by internally allocated request to add to wa2_descmiss_req_list.
* @list: used by internally allocated request to add to descmiss_req_list.
*/
struct cdns3_request {
struct usb_request request;
@ -1234,7 +1165,8 @@ struct cdns3_request {
* @ep0_data_dir: direction for control transfer
* @eps: array of pointers to all endpoints with exclusion ep0
* @aligned_buf_list: list of aligned buffers internally allocated by driver
* @aligned_buf_wq: workqueue freeing no longer used aligned buf.
* @run_garbage_colector: infroms that at least one element of aligned_buf_list
* can be freed
* @selected_ep: actually selected endpoint. It's used only to improve
* performance.
* @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
@ -1246,10 +1178,13 @@ struct cdns3_request {
* @wake_up_flag: allow device to remote up the host
* @status_completion_no_call: indicate that driver is waiting for status s
* stage completion. It's used in deferred SET_CONFIGURATION request.
* @onchip_buffers: number of available on-chip buffers.
* @onchip_used_size: actual size of on-chip memory assigned to endpoints.
* @onchip_mem_allocated_size: actual size of on-chip memory assigned
* to endpoints
* @pending_status_wq: workqueue handling status stage for deferred requests.
* @shadow_ep_en: hold information about endpoints that will be enabled
* in soft irq.
* @pending_status_request: request for which status stage was deferred
* @start_gadget: the current role is at CDNS3_ROLE_GADGET
*/
struct cdns3_device {
struct device *dev;
@ -1277,12 +1212,11 @@ struct cdns3_device {
struct cdns3_endpoint *eps[CDNS3_ENDPOINTS_MAX_COUNT];
struct list_head aligned_buf_list;
struct work_struct aligned_buf_wq;
unsigned run_garbage_colector:1;
u32 selected_ep;
u16 isoch_delay;
unsigned wait_for_setup:1;
unsigned u1_allowed:1;
unsigned u2_allowed:1;
unsigned is_selfpowered:1;
@ -1290,14 +1224,14 @@ struct cdns3_device {
int hw_configured_flag:1;
int wake_up_flag:1;
unsigned status_completion_no_call:1;
int out_mem_is_allocated;
int out_mem_is_allocated:1;
struct work_struct pending_status_wq;
struct usb_request *pending_status_request;
u32 shadow_ep_en;
/*in KB */
u16 onchip_buffers;
u16 onchip_used_size;
int onchip_mem_allocated_size;
unsigned start_gadget:1;
};
void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
@ -1317,8 +1251,6 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
u8 cdns3_ep_addr_to_index(u8 ep_addr);
int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep);
int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep);
struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags);
void cdns3_gadget_ep_free_request(struct usb_ep *ep,
@ -1333,6 +1265,5 @@ int cdns3_init_ep0(struct cdns3_device *priv_dev,
void cdns3_ep0_config(struct cdns3_device *priv_dev);
void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev);
#endif /* __LINUX_CDNS3_GADGET */

View File

@ -1,17 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Host Export APIs
*
* Copyright (C) 2017-2018 NXP
* host-export.h - Host Export APIs
*
* Copyright 2017 NXP
* Authors: Peter Chen <peter.chen@nxp.com>
*
* 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 __LINUX_CDNS3_HOST_EXPORT
#define __LINUX_CDNS3_HOST_EXPORT
#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
@ -20,8 +28,16 @@ static inline int cdns3_host_init(struct cdns3 *cdns)
return -ENXIO;
}
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
static inline void cdns3_host_remove(struct cdns3 *cdns)
{
}
static inline void cdns3_host_driver_init(void)
{
}
#endif /* CONFIG_USB_CDNS3_HOST */
#endif /* __LINUX_CDNS3_HOST_EXPORT */
#endif /* __DRIVERS_USB_CDNS3_HOST_H */

View File

@ -1,59 +1,276 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver - host side
*
* Copyright (C) 2018-2019 Cadence Design Systems.
* Copyright (C) 2017-2018 NXP
* host.c - Cadence USB3 host controller driver
*
* Copyright 2017 NXP
* Authors: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
*
* 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 <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/pm_runtime.h>
#include <linux/usb/of.h>
#include "../host/xhci.h"
#include "core.h"
#include "drd.h"
#include "host-export.h"
#include "cdns3-nxp-reg-def.h"
static int __cdns3_host_init(struct cdns3 *cdns)
#define XHCI_WAKEUP_STATUS (PORT_RC | PORT_PLC)
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 | XHCI_AVOID_BEI | XHCI_CDNS_HOST);
}
static int xhci_cdns3_setup(struct usb_hcd *hcd)
{
struct platform_device *xhci;
int ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
u32 command;
cdns3_drd_switch_host(cdns, 1);
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
dev_err(cdns->dev, "couldn't allocate xHCI device\n");
return -ENOMEM;
}
xhci->dev.parent = cdns->dev;
cdns->host_dev = xhci;
ret = platform_device_add_resources(xhci, cdns->xhci_res,
CDNS3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
goto err1;
}
ret = platform_device_add(xhci);
if (ret) {
dev_err(cdns->dev, "failed to register xHCI device\n");
goto err1;
}
ret = xhci_gen_setup(hcd, xhci_cdns3_quirks);
if (ret)
return ret;
/* set usbcmd.EU3S */
command = readl(&xhci->op_regs->command);
command |= CMD_PM_INDEX;
writel(command, &xhci->op_regs->command);
return 0;
}
struct cdns3_host {
struct device dev;
struct usb_hcd *hcd;
struct cdns3 *cdns;
};
static int xhci_cdns3_bus_suspend(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
struct cdns3_host *host = container_of(dev, struct cdns3_host, dev);
struct cdns3 *cdns = host->cdns;
void __iomem *xhci_regs = cdns->xhci_regs;
u32 value;
int ret;
ret = xhci_bus_suspend(hcd);
if (ret)
return ret;
value = readl(xhci_regs + XECP_AUX_CTRL_REG1);
value |= CFG_RXDET_P3_EN;
writel(value, xhci_regs + XECP_AUX_CTRL_REG1);
return 0;
}
static const struct xhci_driver_overrides xhci_cdns3_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_hcd),
.reset = xhci_cdns3_setup,
.bus_suspend = xhci_cdns3_bus_suspend,
};
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;
host->cdns = cdns;
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;
}
pm_runtime_set_active(dev);
pm_runtime_no_callbacks(dev);
pm_runtime_enable(dev);
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;
}
host->hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
xhci->shared_hcd->tpl_support = host->hcd->tpl_support;
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;
device_set_wakeup_capable(dev, true);
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:
platform_device_put(xhci);
put_device(dev);
cdns->host_dev = NULL;
return ret;
}
static void cdns3_host_exit(struct cdns3 *cdns)
static void cdns3_host_stop(struct cdns3 *cdns)
{
platform_device_unregister(cdns->host_dev);
cdns->host_dev = NULL;
cdns3_drd_switch_host(cdns, 0);
struct device *dev = cdns->host_dev;
struct usb_hcd *hcd, *shared_hcd;
struct xhci_hcd *xhci;
if (dev) {
hcd = dev_get_drvdata(dev);
xhci = hcd_to_xhci(hcd);
shared_hcd = xhci->shared_hcd;
xhci->xhc_state |= XHCI_STATE_REMOVING;
usb_remove_hcd(shared_hcd);
xhci->shared_hcd = NULL;
usb_remove_hcd(hcd);
synchronize_irq(cdns->irq);
usb_put_hcd(shared_hcd);
usb_put_hcd(hcd);
cdns->host_dev = NULL;
pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
device_del(dev);
put_device(dev);
}
}
static int cdns3_host_suspend(struct cdns3 *cdns, bool do_wakeup)
{
struct device *dev = cdns->host_dev;
struct xhci_hcd *xhci;
void __iomem *xhci_regs = cdns->xhci_regs;
u32 portsc_usb2, portsc_usb3;
int ret;
if (!dev)
return 0;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
ret = xhci_suspend(xhci, do_wakeup);
if (ret)
return ret;
portsc_usb2 = readl(xhci_regs + 0x480);
portsc_usb3 = readl(xhci_regs + 0x490);
if ((portsc_usb2 & XHCI_WAKEUP_STATUS) ||
(portsc_usb3 & XHCI_WAKEUP_STATUS)) {
dev_dbg(cdns->dev, "wakeup occurs\n");
cdns3_role(cdns)->resume(cdns, false);
return -EBUSY;
}
return ret;
}
static int cdns3_host_resume(struct cdns3 *cdns, bool hibernated)
{
struct device *dev = cdns->host_dev;
struct xhci_hcd *xhci;
if (!dev)
return 0;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
return xhci_resume(xhci, hibernated);
}
int cdns3_host_init(struct cdns3 *cdns)
@ -64,12 +281,23 @@ int cdns3_host_init(struct cdns3 *cdns)
if (!rdrv)
return -ENOMEM;
rdrv->start = __cdns3_host_init;
rdrv->stop = cdns3_host_exit;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->start = cdns3_host_start;
rdrv->stop = cdns3_host_stop;
rdrv->irq = cdns3_host_irq;
rdrv->suspend = cdns3_host_suspend;
rdrv->resume = cdns3_host_resume;
rdrv->name = "host";
cdns->roles[USB_ROLE_HOST] = rdrv;
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);
}

35
drivers/usb/cdns3/io.h Normal file
View File

@ -0,0 +1,35 @@
/**
* io.h - Cadence USB3 IO Header
*
* Copyright (C) 2016 Cadence Design Systems - https://www.cadence.com/
*
* Authors: Rafal Ozieblo <rafalo@cadence.com>,
*
* 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 <linux/io.h>
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 */

View File

@ -2,10 +2,22 @@
/*
* USBSS device controller driver Trace Support
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2018 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
trace_cdns3_log(priv_dev, &vaf);
va_end(args);
}

View File

@ -3,7 +3,7 @@
* USBSS device controller driver.
* Trace support header file.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2018 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
@ -18,55 +18,23 @@
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
#include "core.h"
#include "gadget.h"
#include "debug.h"
#define CDNS3_MSG_MAX 500
TRACE_EVENT(cdns3_halt,
TP_PROTO(struct cdns3_endpoint *ep_priv, u8 halt, u8 flush),
TP_ARGS(ep_priv, halt, flush),
TRACE_EVENT(cdns3_log,
TP_PROTO(struct cdns3_device *priv_dev, struct va_format *vaf),
TP_ARGS(priv_dev, vaf),
TP_STRUCT__entry(
__string(name, ep_priv->name)
__field(u8, halt)
__field(u8, flush)
__string(name, dev_name(priv_dev->dev))
__dynamic_array(char, msg, CDNS3_MSG_MAX)
),
TP_fast_assign(
__assign_str(name, ep_priv->name);
__entry->halt = halt;
__entry->flush = flush;
__assign_str(name, dev_name(priv_dev->dev));
vsnprintf(__get_str(msg), CDNS3_MSG_MAX, vaf->fmt, *vaf->va);
),
TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
__get_str(name), __entry->halt ? "set" : "cleared")
);
TRACE_EVENT(cdns3_wa1,
TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
TP_ARGS(ep_priv, msg),
TP_STRUCT__entry(
__string(ep_name, ep_priv->name)
__string(msg, msg)
),
TP_fast_assign(
__assign_str(ep_name, ep_priv->name);
__assign_str(msg, msg);
),
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
);
TRACE_EVENT(cdns3_wa2,
TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
TP_ARGS(ep_priv, msg),
TP_STRUCT__entry(
__string(ep_name, ep_priv->name)
__string(msg, msg)
),
TP_fast_assign(
__assign_str(ep_name, ep_priv->name);
__assign_str(msg, msg);
),
TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg))
TP_printk("%s: %s", __get_str(name), __get_str(msg))
);
DECLARE_EVENT_CLASS(cdns3_log_doorbell,
@ -80,7 +48,7 @@ DECLARE_EVENT_CLASS(cdns3_log_doorbell,
__assign_str(name, ep_name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
__entry->ep_trbaddr)
);
@ -230,9 +198,9 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
" trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
__get_str(name), __entry->req, __entry->buf, __entry->actual,
__entry->length,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "I" : "i",
__entry->zero ? "zero | " : "",
__entry->short_not_ok ? "short | " : "",
__entry->no_interrupt ? "no int" : "",
__entry->status,
__entry->start_trb,
__entry->end_trb,
@ -266,21 +234,6 @@ DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
TP_ARGS(req)
);
TRACE_EVENT(cdns3_ep0_queue,
TP_PROTO(struct cdns3_device *dev_priv, struct usb_request *request),
TP_ARGS(dev_priv, request),
TP_STRUCT__entry(
__field(int, dir)
__field(int, length)
),
TP_fast_assign(
__entry->dir = dev_priv->ep0_data_dir;
__entry->length = request->length;
),
TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
__entry->length)
);
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
TP_PROTO(struct cdns3_request *priv_req),
TP_ARGS(priv_req),
@ -302,9 +255,9 @@ DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
__entry->aligned_dma = priv_req->aligned_buf->dma;
__entry->aligned_buf_size = priv_req->aligned_buf->size;
),
TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d",
__get_str(name), __entry->req, __entry->buf, &__entry->dma,
__entry->aligned_buf, &__entry->aligned_dma,
TP_printk("%s: req: %p, req buf %p, dma %08llx a_buf %p a_dma %08llx, size %d",
__get_str(name), __entry->req, __entry->buf, __entry->dma,
__entry->aligned_buf, __entry->aligned_dma,
__entry->aligned_buf_size
)
);
@ -422,7 +375,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ep,
__entry->maxburst, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
__entry->flags & EP_STALLED ? "STALLED | " : "",
__entry->flags & EP_STALL ? "STALL | " : "",
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",

View File

@ -70,6 +70,9 @@
#define PORTSC_FPR BIT(6)
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
#define PORTSC_LS (BIT(11) | BIT(10))
#define PORTSC_LS_J BIT(11)
#define PORTSC_LS_K BIT(10)
#define PORTSC_PP BIT(12)
#define PORTSC_PTC (0x0FUL << 16)
#define PORTSC_WKCN BIT(20)
@ -78,6 +81,7 @@
#define PORTSC_PFSC BIT(24)
#define PORTSC_PTS(d) \
(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
#define PORT_SPEED_LOW(d) ((((d) >> 26) & 0x3) == 1)
#define PORTSC_PTW BIT(28)
#define PORTSC_STS BIT(29)

View File

@ -126,12 +126,16 @@ enum ci_revision {
* @start: start this role
* @stop: stop this role
* @irq: irq handler for this role
* @suspend: system suspend handler for this role
* @resume: system resume handler for this role
* @name: role name string (host/gadget)
*/
struct ci_role_driver {
int (*start)(struct ci_hdrc *);
void (*stop)(struct ci_hdrc *);
irqreturn_t (*irq)(struct ci_hdrc *);
void (*suspend)(struct ci_hdrc *);
void (*resume)(struct ci_hdrc *, bool power_lost);
const char *name;
};
@ -203,6 +207,9 @@ struct hw_bank {
* @in_lpm: if the core in low power mode
* @wakeup_int: if wakeup interrupt occur
* @rev: The revision number for controller
* @mutex: protect code from concorrent running
* @power_lost_work: work item when controller power is lost
* @power_lost_wq: work queue for controller power is lost
*/
struct ci_hdrc {
struct device *dev;
@ -256,6 +263,20 @@ struct ci_hdrc {
bool in_lpm;
bool wakeup_int;
enum ci_revision rev;
/* register save area for suspend&resume */
u32 pm_command;
u32 pm_status;
u32 pm_intr_enable;
u32 pm_frame_index;
u32 pm_segment;
u32 pm_frame_list;
u32 pm_async_next;
u32 pm_configured_flag;
u32 pm_portsc;
u32 pm_usbmode;
struct work_struct power_lost_work;
struct workqueue_struct *power_lost_wq;
struct mutex mutex;
};
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
@ -275,9 +296,21 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
return -ENXIO;
ret = ci->roles[role]->start(ci);
if (!ret)
ci->role = role;
return ret;
if (ret)
return ret;
ci->role = role;
if (ci->usb_phy) {
if (role == CI_ROLE_HOST)
usb_phy_set_mode(ci->usb_phy,
CUR_USB_MODE_HOST);
else
usb_phy_set_mode(ci->usb_phy,
CUR_USB_MODE_DEVICE);
}
return 0;
}
static inline void ci_role_stop(struct ci_hdrc *ci)
@ -290,6 +323,9 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->role = CI_ROLE_END;
ci->roles[role]->stop(ci);
if (ci->usb_phy)
usb_phy_set_mode(ci->usb_phy, CUR_USB_MODE_NONE);
}
static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
@ -453,8 +489,10 @@ u8 hw_port_test_get(struct ci_hdrc *ci);
void hw_phymode_configure(struct ci_hdrc *ci);
void ci_platform_configure(struct ci_hdrc *ci);
int hw_controller_reset(struct ci_hdrc *ci);
void dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable);
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */

View File

@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_qos.h>
#include <linux/busfreq-imx.h>
#include "ci.h"
#include "ci_hdrc_imx.h"
@ -165,6 +166,11 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
data->ulpi = 1;
of_property_read_u32(np, "picophy,pre-emp-curr-control",
&data->emp_curr_control);
of_property_read_u32(np, "picophy,dc-vol-level-adjust",
&data->dc_vol_level_adjust);
return data;
}
@ -271,14 +277,18 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
struct device *dev = ci->dev->parent;
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
struct imx_usbmisc_data *mdata = data->usbmisc_data;
switch (event) {
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
ret = pinctrl_select_state(data->pinctrl,
data->pinctrl_hsic_active);
if (ret)
dev_err(dev, "hsic_active select failed, err=%d\n",
ret);
if (data->pinctrl) {
ret = pinctrl_select_state(data->pinctrl,
data->pinctrl_hsic_active);
if (ret)
dev_err(dev,
"hsic_active select failed, err=%d\n",
ret);
}
break;
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
@ -286,6 +296,12 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
dev_err(dev,
"hsic_set_connect failed, err=%d\n", ret);
break;
case CI_HDRC_CONTROLLER_VBUS_EVENT:
if (ci->vbus_active)
ret = imx_usbmisc_charger_detection(mdata, true);
else
ret = imx_usbmisc_charger_detection(mdata, false);
break;
default:
break;
}
@ -306,7 +322,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct pinctrl_state *pinctrl_hsic_idle;
of_id = of_match_device(ci_hdrc_imx_dt_ids, dev);
if (!of_id)
@ -330,12 +345,42 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
pdata.flags |= CI_HDRC_IMX_IS_HSIC;
data->usbmisc_data->hsic = 1;
data->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(data->pinctrl)) {
dev_err(dev, "pinctrl get failed, err=%ld\n",
if (PTR_ERR(data->pinctrl) == -ENODEV)
data->pinctrl = NULL;
else if (IS_ERR(data->pinctrl)) {
if (PTR_ERR(data->pinctrl) != -EPROBE_DEFER)
dev_err(dev, "pinctrl get failed, err=%ld\n",
PTR_ERR(data->pinctrl));
return PTR_ERR(data->pinctrl);
}
data->hsic_pad_regulator =
devm_regulator_get_optional(dev, "hsic");
if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
/* no pad regualator is needed */
data->hsic_pad_regulator = NULL;
} else if (IS_ERR(data->hsic_pad_regulator)) {
if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER)
dev_err(dev,
"Get HSIC pad regulator error: %ld\n",
PTR_ERR(data->hsic_pad_regulator));
return PTR_ERR(data->hsic_pad_regulator);
}
if (data->hsic_pad_regulator) {
ret = regulator_enable(data->hsic_pad_regulator);
if (ret) {
dev_err(dev,
"Failed to enable HSIC pad regulator\n");
return ret;
}
}
}
/* HSIC pinctrl handling */
if (data->pinctrl) {
struct pinctrl_state *pinctrl_hsic_idle;
pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
if (IS_ERR(pinctrl_hsic_idle)) {
dev_err(dev,
@ -358,33 +403,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
PTR_ERR(data->pinctrl_hsic_active));
return PTR_ERR(data->pinctrl_hsic_active);
}
data->hsic_pad_regulator = devm_regulator_get(dev, "hsic");
if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
/* no pad regualator is needed */
data->hsic_pad_regulator = NULL;
} else if (IS_ERR(data->hsic_pad_regulator)) {
dev_err(dev, "Get HSIC pad regulator error: %ld\n",
PTR_ERR(data->hsic_pad_regulator));
return PTR_ERR(data->hsic_pad_regulator);
}
if (data->hsic_pad_regulator) {
ret = regulator_enable(data->hsic_pad_regulator);
if (ret) {
dev_err(dev,
"Failed to enable HSIC pad regulator\n");
return ret;
}
}
}
if (pdata.flags & CI_HDRC_PMQOS)
pm_qos_add_request(&data->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
request_bus_freq(BUS_FREQ_HIGH);
ret = imx_get_clks(dev);
if (ret)
goto disable_hsic_regulator;
@ -404,6 +429,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
pdata.usb_phy = data->phy;
if (data->usbmisc_data)
data->usbmisc_data->usb_phy = data->phy;
if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
@ -416,6 +443,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
data->supports_runtime_pm = true;
if (of_find_property(np, "ci-disable-lpm", NULL)) {
data->supports_runtime_pm = false;
pdata.flags &= ~CI_HDRC_SUPPORTS_RUNTIME_PM;
}
ret = imx_usbmisc_init(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
@ -433,12 +465,24 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto err_clk;
}
if (!IS_ERR(pdata.id_extcon.edev) ||
of_property_read_bool(np, "usb-role-switch"))
data->usbmisc_data->ext_id = 1;
if (!IS_ERR(pdata.vbus_extcon.edev) ||
of_property_read_bool(np, "usb-role-switch"))
data->usbmisc_data->ext_vbus = 1;
ret = imx_usbmisc_init_post(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc post failed, ret=%d\n", ret);
goto disable_device;
}
/* usbmisc needs to know dr mode to choose wakeup setting */
data->usbmisc_data->available_role =
ci_hdrc_query_available_role(data->ci_pdev);
if (data->supports_runtime_pm) {
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@ -453,6 +497,7 @@ disable_device:
err_clk:
imx_disable_unprepare_clks(dev);
disable_hsic_regulator:
release_bus_freq(BUS_FREQ_HIGH);
if (data->hsic_pad_regulator)
/* don't overwrite original ret (cf. EPROBE_DEFER) */
regulator_disable(data->hsic_pad_regulator);
@ -466,6 +511,10 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
/* usbmisc needs to know dr mode to choose wakeup setting */
data->usbmisc_data->available_role =
ci_hdrc_query_available_role(data->ci_pdev);
if (data->supports_runtime_pm) {
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@ -477,6 +526,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
usb_phy_shutdown(data->phy);
if (data->ci_pdev) {
imx_disable_unprepare_clks(&pdev->dev);
release_bus_freq(BUS_FREQ_HIGH);
if (data->plat_data->flags & CI_HDRC_PMQOS)
pm_qos_remove_request(&data->pm_qos_req);
if (data->hsic_pad_regulator)
@ -491,20 +541,24 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
static int __maybe_unused imx_controller_suspend(struct device *dev)
static int __maybe_unused imx_controller_suspend(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
dev_dbg(dev, "at %s\n", __func__);
ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false);
ret = imx_usbmisc_suspend(data->usbmisc_data,
PMSG_IS_AUTO(msg) || device_may_wakeup(dev));
if (ret) {
dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
dev_err(dev,
"usbmisc suspend failed, ret=%d\n", ret);
return ret;
}
imx_disable_unprepare_clks(dev);
release_bus_freq(BUS_FREQ_HIGH);
if (data->plat_data->flags & CI_HDRC_PMQOS)
pm_qos_remove_request(&data->pm_qos_req);
@ -513,44 +567,37 @@ static int __maybe_unused imx_controller_suspend(struct device *dev)
return 0;
}
static int __maybe_unused imx_controller_resume(struct device *dev)
static int __maybe_unused imx_controller_resume(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
dev_dbg(dev, "at %s\n", __func__);
if (!data->in_lpm) {
WARN_ON(1);
if (!data->in_lpm)
return 0;
}
if (data->plat_data->flags & CI_HDRC_PMQOS)
pm_qos_add_request(&data->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
request_bus_freq(BUS_FREQ_HIGH);
ret = imx_prepare_enable_clks(dev);
if (ret)
return ret;
data->in_lpm = false;
ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
ret = imx_usbmisc_resume(data->usbmisc_data,
PMSG_IS_AUTO(msg) || device_may_wakeup(dev));
if (ret) {
dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
dev_err(dev, "usbmisc resume failed, ret=%d\n", ret);
goto clk_disable;
}
ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true);
if (ret) {
dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
goto hsic_set_clk_fail;
}
return 0;
hsic_set_clk_fail:
imx_usbmisc_set_wakeup(data->usbmisc_data, true);
clk_disable:
imx_disable_unprepare_clks(dev);
return ret;
@ -566,16 +613,12 @@ static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
/* The core's suspend doesn't run */
return 0;
if (device_may_wakeup(dev)) {
ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
if (ret) {
dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
ret);
return ret;
}
}
ret = imx_controller_suspend(dev, PMSG_SUSPEND);
if (ret)
return ret;
return imx_controller_suspend(dev);
pinctrl_pm_select_sleep_state(dev);
return ret;
}
static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
@ -583,7 +626,8 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
ret = imx_controller_resume(dev);
pinctrl_pm_select_default_state(dev);
ret = imx_controller_resume(dev, PMSG_RESUME);
if (!ret && data->supports_runtime_pm) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@ -596,25 +640,16 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
if (data->in_lpm) {
WARN_ON(1);
if (data->in_lpm)
return 0;
}
ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
if (ret) {
dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
return ret;
}
return imx_controller_suspend(dev);
return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev);
return imx_controller_resume(dev, PMSG_AUTO_RESUME);
}
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {

View File

@ -22,12 +22,19 @@ struct imx_usbmisc_data {
unsigned int evdo:1; /* set external vbus divider option */
unsigned int ulpi:1; /* connected to an ULPI phy */
unsigned int hsic:1; /* HSIC controlller */
enum usb_dr_mode available_role;
unsigned int ext_id:1; /* ID from exteranl event */
unsigned int ext_vbus:1; /* Vbus from exteranl event */
int emp_curr_control;
int dc_vol_level_adjust;
struct usb_phy *usb_phy;
};
int imx_usbmisc_init(struct imx_usbmisc_data *data);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data);
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup);
int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup);
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */

View File

@ -220,7 +220,7 @@ static void hw_wait_phy_stable(void)
}
/* The PHY enters/leaves low power mode */
static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
{
enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
@ -475,7 +475,7 @@ void ci_platform_configure(struct ci_hdrc *ci)
*
* This function returns an error code
*/
static int hw_controller_reset(struct ci_hdrc *ci)
int hw_controller_reset(struct ci_hdrc *ci)
{
int count = 0;
@ -899,6 +899,33 @@ void ci_hdrc_remove_device(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(ci_hdrc_remove_device);
/**
* ci_hdrc_query_available_role: get runtime available operation mode
*
* The glue layer can get current operation mode (host/peripheral/otg)
* This function should be called after ci core device has created.
*
* @pdev: the platform device of ci core.
*
* Return USB_DR_MODE_XXX.
*/
enum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev)
{
struct ci_hdrc *ci = platform_get_drvdata(pdev);
if (!ci)
return USB_DR_MODE_UNKNOWN;
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])
return USB_DR_MODE_OTG;
else if (ci->roles[CI_ROLE_HOST])
return USB_DR_MODE_HOST;
else if (ci->roles[CI_ROLE_GADGET])
return USB_DR_MODE_PERIPHERAL;
else
return USB_DR_MODE_UNKNOWN;
}
EXPORT_SYMBOL_GPL(ci_hdrc_query_available_role);
static inline void ci_role_destroy(struct ci_hdrc *ci)
{
ci_hdrc_gadget_destroy(ci);
@ -973,6 +1000,53 @@ static struct attribute *ci_attrs[] = {
};
ATTRIBUTE_GROUPS(ci);
static enum ci_role ci_get_role(struct ci_hdrc *ci)
{
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
if (ci->is_otg) {
hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
return ci_otg_role(ci);
} else {
/*
* If the controller is not OTG capable, but support
* role switch, the defalt role is gadget, and the
* user can switch it through debugfs.
*/
return CI_ROLE_GADGET;
}
} else {
return ci->roles[CI_ROLE_HOST]
? CI_ROLE_HOST
: CI_ROLE_GADGET;
}
}
static void ci_start_new_role(struct ci_hdrc *ci)
{
enum ci_role role = ci_get_role(ci);
if (ci->role != role) {
ci_handle_id_switch(ci);
} else if (role == CI_ROLE_GADGET) {
if (ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget);
ci_handle_vbus_connected(ci);
}
}
static void ci_power_lost_work(struct work_struct *work)
{
struct ci_hdrc *ci = container_of(work, struct ci_hdrc,
power_lost_work);
disable_irq_nosync(ci->irq);
pm_runtime_get_sync(ci->dev);
if (!ci_otg_is_fsm_mode(ci))
ci_start_new_role(ci);
pm_runtime_put_sync(ci->dev);
enable_irq(ci->irq);
}
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1143,11 +1217,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
: CI_ROLE_GADGET;
}
if (!ci_otg_is_fsm_mode(ci)) {
/* only update vbus status for peripheral */
if (ci->role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
ci->role = ci_get_role(ci);
/* only update vbus status for peripheral */
if (ci->role == CI_ROLE_GADGET) {
/* Let DP pull down if it isn't currently */
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
ci_handle_vbus_connected(ci);
}
if (!ci_otg_is_fsm_mode(ci)) {
ret = ci_role_start(ci, ci->role);
if (ret) {
dev_err(dev, "can't start %s role\n",
@ -1178,9 +1256,20 @@ static int ci_hdrc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
dbg_create_files(ci);
/* Init workqueue for controller power lost handling */
ci->power_lost_wq = create_freezable_workqueue("ci_power_lost");
if (!ci->power_lost_wq) {
dev_err(ci->dev, "can't create power_lost workqueue\n");
goto remove_debug;
}
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
mutex_init(&ci->mutex);
return 0;
remove_debug:
dbg_remove_files(ci);
stop:
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
@ -1212,6 +1301,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
}
flush_workqueue(ci->power_lost_wq);
destroy_workqueue(ci->power_lost_wq);
dbg_remove_files(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
@ -1239,13 +1330,10 @@ static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
{
if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
(ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
if (!hw_read_otgsc(ci, OTGSC_ID)) {
ci->fsm.a_srp_det = 1;
ci->fsm.a_bus_drop = 0;
} else {
if (!hw_read_otgsc(ci, OTGSC_ID))
otg_add_timer(&ci->fsm, A_DP_END);
else
ci->fsm.id = 1;
}
ci_otg_queue_work(ci);
}
}
@ -1268,10 +1356,8 @@ static int ci_controller_resume(struct device *dev)
dev_dbg(dev, "at %s\n", __func__);
if (!ci->in_lpm) {
WARN_ON(1);
if (!ci->in_lpm)
return 0;
}
ci_hdrc_enter_lpm(ci, false);
@ -1303,6 +1389,7 @@ static int ci_suspend(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
flush_workqueue(ci->power_lost_wq);
if (ci->wq)
flush_workqueue(ci->wq);
/*
@ -1319,6 +1406,10 @@ static int ci_suspend(struct device *dev)
return 0;
}
/* Extra routine per role before system suspend */
if (ci->role != CI_ROLE_END && ci_role(ci)->suspend)
ci_role(ci)->suspend(ci);
if (device_may_wakeup(dev)) {
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_suspend_for_srp(ci);
@ -1335,8 +1426,18 @@ static int ci_suspend(struct device *dev)
static int ci_resume(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
bool power_lost = false;
u32 sample_reg_val;
int ret;
/* Check if controller resume from power lost */
sample_reg_val = hw_read(ci, OP_ENDPTLISTADDR, ~0);
if (sample_reg_val == 0)
power_lost = true;
else if (sample_reg_val == 0xFFFFFFFF)
/* Restore value 0 if it was set for power lost check */
hw_write(ci, OP_ENDPTLISTADDR, ~0, 0);
if (device_may_wakeup(dev))
disable_irq_wake(ci->irq);
@ -1344,6 +1445,19 @@ static int ci_resume(struct device *dev)
if (ret)
return ret;
if (power_lost) {
/* shutdown and re-init for phy */
ci_usb_phy_exit(ci);
ci_usb_phy_init(ci);
}
/* Extra routine per role after system resume */
if (ci->role != CI_ROLE_END && ci_role(ci)->resume)
ci_role(ci)->resume(ci, power_lost);
if (power_lost)
queue_work(ci->power_lost_wq, &ci->power_lost_work);
if (ci->supports_runtime_pm) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@ -1360,10 +1474,8 @@ static int ci_runtime_suspend(struct device *dev)
dev_dbg(dev, "at %s\n", __func__);
if (ci->in_lpm) {
WARN_ON(1);
if (ci->in_lpm)
return 0;
}
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_suspend_for_srp(ci);

View File

@ -23,6 +23,7 @@
static struct hc_driver __read_mostly ci_ehci_hc_driver;
static int (*orig_bus_suspend)(struct usb_hcd *hcd);
static int (*orig_bus_resume)(struct usb_hcd *hcd);
struct ehci_ci_priv {
struct regulator *reg_vbus;
@ -99,7 +100,10 @@ static const struct ehci_driver_overrides ehci_ci_overrides = {
static irqreturn_t host_irq(struct ci_hdrc *ci)
{
return usb_hcd_irq(ci->irq, ci->hcd);
if (ci->hcd)
return usb_hcd_irq(ci->irq, ci->hcd);
else
return IRQ_NONE;
}
static int host_start(struct ci_hdrc *ci)
@ -213,11 +217,111 @@ static void host_stop(struct ci_hdrc *ci)
ci->platdata->pins_default);
}
bool ci_hdrc_host_has_device(struct ci_hdrc *ci)
{
struct usb_device *roothub;
int i;
if ((ci->role == CI_ROLE_HOST) && ci->hcd) {
roothub = ci->hcd->self.root_hub;
for (i = 0; i < roothub->maxchild; ++i) {
if (usb_hub_find_child(roothub, (i + 1)))
return true;
}
}
return false;
}
static void ci_hdrc_host_save_for_power_lost(struct ci_hdrc *ci)
{
struct ehci_hcd *ehci;
if (!ci->hcd)
return;
ehci = hcd_to_ehci(ci->hcd);
/* save EHCI registers */
ci->pm_usbmode = ehci_readl(ehci, &ehci->regs->usbmode);
ci->pm_command = ehci_readl(ehci, &ehci->regs->command);
ci->pm_command &= ~CMD_RUN;
ci->pm_status = ehci_readl(ehci, &ehci->regs->status);
ci->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable);
ci->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index);
ci->pm_segment = ehci_readl(ehci, &ehci->regs->segment);
ci->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list);
ci->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next);
ci->pm_configured_flag =
ehci_readl(ehci, &ehci->regs->configured_flag);
ci->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]);
}
static void ci_hdrc_host_restore_from_power_lost(struct ci_hdrc *ci)
{
struct ehci_hcd *ehci;
unsigned long flags;
u32 tmp;
int step_ms;
/*
* If the vbus is off during system suspend, most of devices will pull
* DP up within 200ms when they see vbus, set 1000ms for safety.
*/
int timeout_ms = 1000;
if (!ci->hcd)
return;
hw_controller_reset(ci);
ehci = hcd_to_ehci(ci->hcd);
spin_lock_irqsave(&ehci->lock, flags);
/* Restore EHCI registers */
ehci_writel(ehci, ci->pm_usbmode, &ehci->regs->usbmode);
ehci_writel(ehci, ci->pm_portsc, &ehci->regs->port_status[0]);
ehci_writel(ehci, ci->pm_command, &ehci->regs->command);
ehci_writel(ehci, ci->pm_intr_enable, &ehci->regs->intr_enable);
ehci_writel(ehci, ci->pm_frame_index, &ehci->regs->frame_index);
ehci_writel(ehci, ci->pm_segment, &ehci->regs->segment);
ehci_writel(ehci, ci->pm_frame_list, &ehci->regs->frame_list);
ehci_writel(ehci, ci->pm_async_next, &ehci->regs->async_next);
ehci_writel(ehci, ci->pm_configured_flag,
&ehci->regs->configured_flag);
/* Restore the PHY's connect notifier setting */
if (ci->pm_portsc & PORTSC_HSP)
usb_phy_notify_connect(ci->usb_phy, USB_SPEED_HIGH);
tmp = ehci_readl(ehci, &ehci->regs->command);
tmp |= CMD_RUN;
ehci_writel(ehci, tmp, &ehci->regs->command);
spin_unlock_irqrestore(&ehci->lock, flags);
if (!(ci->pm_portsc & PORTSC_CCS))
return;
for (step_ms = 0; step_ms < timeout_ms; step_ms += 25) {
if (ehci_readl(ehci, &ehci->regs->port_status[0]) & PORTSC_CCS)
break;
msleep(25);
}
}
static void ci_hdrc_host_suspend(struct ci_hdrc *ci)
{
ci_hdrc_host_save_for_power_lost(ci);
}
static void ci_hdrc_host_resume(struct ci_hdrc *ci, bool power_lost)
{
if (power_lost)
ci_hdrc_host_restore_from_power_lost(ci);
}
void ci_hdrc_host_destroy(struct ci_hdrc *ci)
{
if (ci->role == CI_ROLE_HOST && ci->hcd)
if (ci->role == CI_ROLE_HOST && ci->hcd) {
disable_irq_nosync(ci->irq);
host_stop(ci);
enable_irq(ci->irq);
}
}
/* The below code is based on tegra ehci driver */
@ -232,7 +336,7 @@ static int ci_ehci_hub_control(
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
u32 __iomem *status_reg;
u32 temp;
u32 temp, suspend_line_state;
unsigned long flags;
int retval = 0;
struct device *dev = hcd->self.controller;
@ -261,6 +365,17 @@ static int ci_ehci_hub_control(
PORT_SUSPEND, 5000))
ehci_err(ehci, "timeout waiting for SUSPEND\n");
if (ci->platdata->flags & CI_HDRC_HOST_SUSP_PHY_LPM) {
if (PORT_SPEED_LOW(temp))
suspend_line_state = PORTSC_LS_K;
else
suspend_line_state = PORTSC_LS_J;
if (!ehci_handshake(ehci, status_reg, PORTSC_LS,
suspend_line_state, 5000))
ci_hdrc_enter_lpm(ci, true);
}
if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
@ -271,6 +386,14 @@ static int ci_ehci_hub_control(
ehci_writel(ehci, temp, status_reg);
}
spin_unlock_irqrestore(&ehci->lock, flags);
if (ehci_port_speed(ehci, temp) ==
USB_PORT_STAT_HIGH_SPEED && hcd->usb_phy) {
/* notify the USB PHY */
usb_phy_notify_suspend(hcd->usb_phy, USB_SPEED_HIGH);
}
spin_lock_irqsave(&ehci->lock, flags);
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
goto done;
}
@ -284,6 +407,14 @@ static int ci_ehci_hub_control(
/* Make sure the resume has finished, it should be finished */
if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000))
ehci_err(ehci, "timeout waiting for resume\n");
temp = ehci_readl(ehci, status_reg);
if (ehci_port_speed(ehci, temp) ==
USB_PORT_STAT_HIGH_SPEED && hcd->usb_phy) {
/* notify the USB PHY */
usb_phy_notify_resume(hcd->usb_phy, USB_SPEED_HIGH);
}
}
spin_unlock_irqrestore(&ehci->lock, flags);
@ -330,6 +461,15 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
* It needs a short delay between set RS bit and PHCD.
*/
usleep_range(150, 200);
/*
* If a transaction is in progress, there may be
* a delay in suspending the port. Poll until the
* port is suspended.
*/
if (test_bit(port, &ehci->bus_suspended) &&
ehci_handshake(ehci, reg, PORT_SUSPEND,
PORT_SUSPEND, 5000))
ehci_err(ehci, "timeout waiting for SUSPEND\n");
/*
* Need to clear WKCN and WKOC for imx HSIC,
* otherwise, there will be wakeup event.
@ -340,6 +480,15 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
ehci_writel(ehci, tmp, reg);
}
if (hcd->usb_phy && test_bit(port, &ehci->bus_suspended)
&& (ehci_port_speed(ehci, portsc) ==
USB_PORT_STAT_HIGH_SPEED))
/*
* notify the USB PHY, it is for global
* suspend case.
*/
usb_phy_notify_suspend(hcd->usb_phy,
USB_SPEED_HIGH);
break;
}
}
@ -347,6 +496,36 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
return 0;
}
static int ci_ehci_bus_resume(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int port;
int ret = orig_bus_resume(hcd);
if (ret)
return ret;
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status[port];
u32 portsc = ehci_readl(ehci, reg);
/*
* Notify PHY after resume signal has finished, it is
* for global suspend case.
*/
if (hcd->usb_phy
&& test_bit(port, &ehci->bus_suspended)
&& (portsc & PORT_CONNECT)
&& (ehci_port_speed(ehci, portsc) ==
USB_PORT_STAT_HIGH_SPEED))
/* notify the USB PHY */
usb_phy_notify_resume(hcd->usb_phy, USB_SPEED_HIGH);
}
return 0;
}
int ci_hdrc_host_init(struct ci_hdrc *ci)
{
struct ci_role_driver *rdrv;
@ -361,6 +540,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
rdrv->start = host_start;
rdrv->stop = host_stop;
rdrv->irq = host_irq;
rdrv->suspend = ci_hdrc_host_suspend;
rdrv->resume = ci_hdrc_host_resume;
rdrv->name = "host";
ci->roles[CI_ROLE_HOST] = rdrv;
@ -371,6 +552,8 @@ void ci_hdrc_host_driver_init(void)
{
ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
orig_bus_resume = ci_ehci_hc_driver.bus_resume;
ci_ehci_hc_driver.bus_resume = ci_ehci_bus_resume;
ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
ci_ehci_hc_driver.hub_control = ci_ehci_hub_control;
}

View File

@ -7,6 +7,7 @@
int ci_hdrc_host_init(struct ci_hdrc *ci);
void ci_hdrc_host_destroy(struct ci_hdrc *ci);
void ci_hdrc_host_driver_init(void);
bool ci_hdrc_host_has_device(struct ci_hdrc *ci);
#else
@ -20,11 +21,16 @@ static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci)
}
static void ci_hdrc_host_driver_init(void)
static inline void ci_hdrc_host_driver_init(void)
{
}
static inline bool ci_hdrc_host_has_device(struct ci_hdrc *ci)
{
return false;
}
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */

View File

@ -2,7 +2,8 @@
/*
* otg.c - ChipIdea USB IP core OTG driver
*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
* Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
*
* Author: Peter Chen
*/
@ -20,6 +21,7 @@
#include "bits.h"
#include "otg.h"
#include "otg_fsm.h"
#include "host.h"
/**
* hw_read_otgsc returns otgsc register bits value.
@ -126,6 +128,20 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
return role;
}
void ci_handle_vbus_connected(struct ci_hdrc *ci)
{
/*
* TODO: if the platform does not supply 5v to udc, or use other way
* to supply 5v, it needs to use other conditions to call
* usb_gadget_vbus_connect.
*/
if (!ci->is_otg)
return;
if (hw_read_otgsc(ci, OTGSC_BSV))
usb_gadget_vbus_connect(&ci->gadget);
}
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
if (!ci->is_otg)
@ -162,10 +178,13 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
return 0;
}
static void ci_handle_id_switch(struct ci_hdrc *ci)
void ci_handle_id_switch(struct ci_hdrc *ci)
{
enum ci_role role = ci_otg_role(ci);
enum ci_role role;
int ret = 0;
mutex_lock(&ci->mutex);
role = ci_otg_role(ci);
if (role != ci->role) {
dev_dbg(ci->dev, "switching from %s to %s\n",
ci_role(ci)->name, ci->roles[role]->name);
@ -181,13 +200,30 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
* care vbus on the board, since it will not affect
* external connector status.
*/
hw_wait_vbus_lower_bsv(ci);
ret = hw_wait_vbus_lower_bsv(ci);
else if (ci->vbus_active)
/*
* If the role switch happens(e.g. during
* system sleep), and we lose vbus drop
* event, disconnect gadget for it before
* start host.
*/
usb_gadget_vbus_disconnect(&ci->gadget);
ci_role_start(ci, role);
/* vbus change may have already occurred */
if (role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
/*
* If the role switch happens(e.g. during system
* sleep) and vbus keeps on afterwards, we connect
* gadget as vbus connect event lost.
*/
if (ret == -ETIMEDOUT)
usb_gadget_vbus_connect(&ci->gadget);
}
mutex_unlock(&ci->mutex);
}
/**
* ci_otg_work - perform otg (vbus/id) event handle

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
*
* Author: Peter Chen
*/
@ -14,6 +14,8 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci);
void ci_hdrc_otg_destroy(struct ci_hdrc *ci);
enum ci_role ci_otg_role(struct ci_hdrc *ci);
void ci_handle_vbus_change(struct ci_hdrc *ci);
void ci_handle_id_switch(struct ci_hdrc *ci);
void ci_handle_vbus_connected(struct ci_hdrc *ci);
static inline void ci_otg_queue_work(struct ci_hdrc *ci)
{
disable_irq_nosync(ci->irq);

View File

@ -25,6 +25,7 @@
#include "ci.h"
#include "bits.h"
#include "otg.h"
#include "udc.h"
#include "otg_fsm.h"
/* Add for otg: interact with user space app */
@ -211,6 +212,7 @@ static unsigned otg_timer_ms[] = {
0,
TB_DATA_PLS,
TB_SSEND_SRP,
TA_DP_END,
};
/*
@ -356,6 +358,13 @@ static int b_ssend_srp_tmout(struct ci_hdrc *ci)
return 1;
}
static int a_dp_end_tmout(struct ci_hdrc *ci)
{
ci->fsm.a_bus_drop = 0;
ci->fsm.a_srp_det = 1;
return 0;
}
/*
* Keep this list in the same order as timers indexed
* by enum otg_fsm_timer in include/linux/usb/otg-fsm.h
@ -373,6 +382,7 @@ static int (*otg_timer_handlers[])(struct ci_hdrc *) = {
NULL, /* A_WAIT_ENUM */
b_data_pls_tmout, /* B_DATA_PLS */
b_ssend_srp_tmout, /* B_SSEND_SRP */
a_dp_end_tmout, /* A_DP_END */
};
/*
@ -559,10 +569,7 @@ static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
{
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
if (on)
usb_gadget_vbus_connect(&ci->gadget);
else
usb_gadget_vbus_disconnect(&ci->gadget);
ci_hdrc_gadget_connect(&ci->gadget, on);
return 0;
}
@ -742,8 +749,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
if (otg_int_src) {
if (otg_int_src & OTGSC_DPIS) {
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
fsm->a_srp_det = 1;
fsm->a_bus_drop = 0;
ci_otg_add_timer(ci, A_DP_END);
} else if (otg_int_src & OTGSC_IDIS) {
hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
if (fsm->id == 0) {

View File

@ -40,6 +40,8 @@
* for safe
*/
#define TA_DP_END (200)
/*
* B-device timing constants
*/

View File

@ -1540,27 +1540,19 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
usb_phy_set_charger_state(ci->usb_phy, is_active ?
USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
if (gadget_ready) {
if (is_active) {
pm_runtime_get_sync(&_gadget->dev);
hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma);
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
usb_udc_vbus_handler(_gadget, true);
} else {
usb_udc_vbus_handler(_gadget, false);
if (ci->driver)
ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(&ci->gadget);
pm_runtime_put_sync(&_gadget->dev);
usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
}
/* Charger Detection */
ci_usb_charger_connect(ci, is_active);
if (ci->usb_phy) {
if (is_active)
usb_phy_set_event(ci->usb_phy, USB_EVENT_VBUS);
else
usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
}
if (gadget_ready)
ci_hdrc_gadget_connect(_gadget, is_active);
return 0;
}
@ -1625,12 +1617,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
if (ci_otg_is_fsm_mode(ci) || ci->role == CI_ROLE_HOST)
return 0;
pm_runtime_get_sync(&ci->gadget.dev);
pm_runtime_get_sync(ci->dev);
if (is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
pm_runtime_put_sync(&ci->gadget.dev);
pm_runtime_put_sync(ci->dev);
return 0;
}
@ -1780,23 +1772,14 @@ static int ci_udc_start(struct usb_gadget *gadget,
ci->driver = driver;
/* Start otg fsm for B-device */
if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
ci_hdrc_otg_fsm_start(ci);
if (ci_otg_is_fsm_mode(ci)) {
if (ci->fsm.id)
ci_hdrc_otg_fsm_start(ci);
return retval;
}
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) {
hw_device_reset(ci);
} else {
usb_udc_vbus_handler(&ci->gadget, false);
pm_runtime_put_sync(&ci->gadget.dev);
return retval;
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
if (retval)
pm_runtime_put_sync(&ci->gadget.dev);
if (ci->vbus_active)
ci_hdrc_gadget_connect(&ci->gadget, 1);
return retval;
}
@ -1835,7 +1818,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
CI_HDRC_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
pm_runtime_put(&ci->gadget.dev);
pm_runtime_put(ci->dev);
}
ci->driver = NULL;
@ -1881,6 +1864,9 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
if (USBi_PCI & intr) {
ci->gadget.speed = hw_port_is_high_speed(ci) ?
USB_SPEED_HIGH : USB_SPEED_FULL;
if (ci->usb_phy)
usb_phy_set_event(ci->usb_phy,
USB_EVENT_ENUMERATED);
if (ci->suspended) {
if (ci->driver->resume) {
spin_unlock(&ci->lock);
@ -1967,9 +1953,6 @@ static int udc_start(struct ci_hdrc *ci)
if (retval)
goto destroy_eps;
pm_runtime_no_callbacks(&ci->gadget.dev);
pm_runtime_enable(&ci->gadget.dev);
return retval;
destroy_eps:
@ -1999,6 +1982,52 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
dma_pool_destroy(ci->qh_pool);
}
int ci_usb_charger_connect(struct ci_hdrc *ci, int is_active)
{
int ret = 0;
pm_runtime_get_sync(ci->dev);
if (ci->usb_phy->charger_detect) {
usb_phy_set_charger_state(ci->usb_phy, is_active ?
USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
} else if (ci->platdata->notify_event) {
ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_VBUS_EVENT);
schedule_work(&ci->usb_phy->chg_work);
}
pm_runtime_put_sync(ci->dev);
return ret;
}
/**
* ci_hdrc_gadget_connect: caller make sure gadget driver is binded
*/
void ci_hdrc_gadget_connect(struct usb_gadget *gadget, int is_active)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
if (is_active) {
pm_runtime_get_sync(ci->dev);
hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma);
usb_gadget_set_state(gadget, USB_STATE_POWERED);
usb_udc_vbus_handler(gadget, true);
} else {
usb_udc_vbus_handler(gadget, false);
if (ci->driver)
ci->driver->disconnect(gadget);
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(gadget);
pm_runtime_put_sync(ci->dev);
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
}
}
static int udc_id_switch_for_device(struct ci_hdrc *ci)
{
if (ci->platdata->pins_device)
@ -2029,6 +2058,44 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
ci->platdata->pins_default);
}
static void udc_suspend_for_power_lost(struct ci_hdrc *ci)
{
/*
* Set OP_ENDPTLISTADDR to be non-zero for
* checking if controller resume from power lost
* in non-host mode.
*/
if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0)
hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0);
}
/* Power lost with device mode */
static void udc_resume_from_power_lost(struct ci_hdrc *ci)
{
if (ci->is_otg)
hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
OTGSC_BSVIS | OTGSC_BSVIE);
}
static void udc_suspend(struct ci_hdrc *ci)
{
udc_suspend_for_power_lost(ci);
if (ci->driver && ci->vbus_active &&
(ci->gadget.state != USB_STATE_SUSPENDED))
usb_gadget_disconnect(&ci->gadget);
}
static void udc_resume(struct ci_hdrc *ci, bool power_lost)
{
if (power_lost) {
udc_resume_from_power_lost(ci);
} else {
if (ci->driver && ci->vbus_active)
usb_gadget_connect(&ci->gadget);
}
}
/**
* ci_hdrc_gadget_init - initialize device related bits
* ci: the controller
@ -2050,6 +2117,8 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
rdrv->start = udc_id_switch_for_device;
rdrv->stop = udc_id_switch_for_host;
rdrv->irq = udc_irq;
rdrv->suspend = udc_suspend;
rdrv->resume = udc_resume;
rdrv->name = "gadget";
ret = udc_start(ci);

View File

@ -82,6 +82,8 @@ struct ci_hw_req {
int ci_hdrc_gadget_init(struct ci_hdrc *ci);
void ci_hdrc_gadget_destroy(struct ci_hdrc *ci);
int ci_usb_charger_connect(struct ci_hdrc *ci, int is_active);
void ci_hdrc_gadget_connect(struct usb_gadget *gadget, int is_active);
#else
@ -95,6 +97,17 @@ static inline void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
}
static inline int ci_usb_charger_connect(struct ci_hdrc *ci, int is_active)
{
return 0;
}
static inline void ci_hdrc_gadget_connect(struct usb_gadget *gadget,
int is_active)
{
}
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */

View File

@ -8,6 +8,8 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
#include <linux/regulator/consumer.h>
#include "ci_hdrc_imx.h"
@ -93,6 +95,18 @@
#define VF610_OVER_CUR_DIS BIT(7)
#define MX7D_USBNC_USB_CTRL2 0x4
/* The default DM/DP value is pull-down */
#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
BIT(14) | BIT(15))
#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
#define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6)
#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1)
#define MX7D_USBNC_AUTO_RESUME BIT(2)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK 0x3
#define MX7D_USB_VBUS_WAKEUP_SOURCE(v) (v << 0)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS MX7D_USB_VBUS_WAKEUP_SOURCE(0)
@ -100,6 +114,27 @@
#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
#define MX7D_USB_OTG_PHY_CFG1 0x30
#define TXPREEMPAMPTUNE0_BIT 28
#define TXPREEMPAMPTUNE0_MASK (3 << 28)
#define TXVREFTUNE0_BIT 20
#define TXVREFTUNE0_MASK (0xf << 20)
#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
#define MX7D_USB_OTG_PHY_CFG2 0x34
#define MX7D_USB_OTG_PHY_STATUS 0x3c
#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
MX6_BM_ID_WAKEUP)
struct usbmisc_ops {
/* It's called once when probe a usb device */
int (*init)(struct imx_usbmisc_data *data);
@ -111,6 +146,11 @@ struct usbmisc_ops {
int (*hsic_set_connect)(struct imx_usbmisc_data *data);
/* It's called during suspend/resume */
int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
/* It's called when system resume from usb power lost */
int (*power_lost_check)(struct imx_usbmisc_data *data);
/* usb charger detection */
int (*charger_detection)(struct imx_usbmisc_data *data);
void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on);
};
struct imx_usbmisc {
@ -119,6 +159,8 @@ struct imx_usbmisc {
const struct usbmisc_ops *ops;
};
static struct regulator *vbus_wakeup_reg;
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data);
static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
@ -330,15 +372,26 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
return 0;
}
static u32 usbmisc_wakeup_setting(struct imx_usbmisc_data *data)
{
u32 wakeup_setting = MX6_USB_OTG_WAKEUP_BITS;
if (data->ext_id || data->available_role != USB_DR_MODE_OTG)
wakeup_setting &= ~MX6_BM_ID_WAKEUP;
if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST)
wakeup_setting &= ~MX6_BM_VBUS_WAKEUP;
return wakeup_setting;
}
static int usbmisc_imx6q_set_wakeup
(struct imx_usbmisc_data *data, bool enabled)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
int ret = 0;
u32 val;
if (data->index > 3)
return -EINVAL;
@ -346,14 +399,22 @@ static int usbmisc_imx6q_set_wakeup
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + data->index * 4);
if (enabled) {
val |= wakeup_setting;
val &= ~MX6_USB_OTG_WAKEUP_BITS;
val |= usbmisc_wakeup_setting(data);
if (vbus_wakeup_reg) {
spin_unlock_irqrestore(&usbmisc->lock, flags);
ret = regulator_enable(vbus_wakeup_reg);
spin_lock_irqsave(&usbmisc->lock, flags);
}
} else {
if (val & MX6_BM_WAKEUP_INTR)
pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
val &= ~wakeup_setting;
val &= ~MX6_USB_OTG_WAKEUP_BITS;
}
writel(val, usbmisc->base + data->index * 4);
spin_unlock_irqrestore(&usbmisc->lock, flags);
if (vbus_wakeup_reg && regulator_is_enabled(vbus_wakeup_reg))
regulator_disable(vbus_wakeup_reg);
return ret;
}
@ -547,17 +608,17 @@ static int usbmisc_imx7d_set_wakeup
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base);
if (enabled) {
writel(val | wakeup_setting, usbmisc->base);
val &= ~MX6_USB_OTG_WAKEUP_BITS;
val |= usbmisc_wakeup_setting(data);
writel(val, usbmisc->base);
} else {
if (val & MX6_BM_WAKEUP_INTR)
dev_dbg(data->dev, "wakeup int\n");
writel(val & ~wakeup_setting, usbmisc->base);
writel(val & ~MX6_USB_OTG_WAKEUP_BITS, usbmisc->base);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
@ -594,10 +655,32 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
/* SoC non-burst setting */
reg = readl(usbmisc->base);
writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
if (!data->hsic) {
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID
| MX7D_USBNC_AUTO_RESUME,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
/* PHY tuning for signal quality */
reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
if (data->emp_curr_control && data->emp_curr_control <=
(TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) {
reg &= ~TXPREEMPAMPTUNE0_MASK;
reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT);
}
if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <=
(TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) {
reg &= ~TXVREFTUNE0_MASK;
reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT);
}
writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
@ -606,6 +689,315 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
return 0;
}
static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base);
spin_unlock_irqrestore(&usbmisc->lock, flags);
/*
* Here use a power on reset value to judge
* if the controller experienced a power lost
*/
if (val == 0x30001000)
return 1;
else
return 0;
}
static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
struct usb_phy *usb_phy = data->usb_phy;
int val;
unsigned long flags;
/* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
usleep_range(1000, 2000);
/*
* Per BC 1.2, check voltage of D+:
* DCP: if greater than VDAT_REF;
* CDP: if less than VDAT_REF.
*/
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) {
dev_dbg(data->dev, "It is a dedicate charging port\n");
usb_phy->chg_type = DCP_TYPE;
} else {
dev_dbg(data->dev, "It is a charging downstream port\n");
usb_phy->chg_type = CDP_TYPE;
}
return 0;
}
static void imx7_disable_charger_detector(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL);
writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
/* Set OPMODE to be 2'b00 and disable its override */
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
}
static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
int i, data_pin_contact_count = 0;
/* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
for (i = 0; i < 100; i = i + 1) {
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) {
if (data_pin_contact_count++ > 5)
/* Data pin makes contact */
break;
usleep_range(5000, 10000);
} else {
data_pin_contact_count = 0;
usleep_range(5000, 6000);
}
}
/* Disable DCD after finished data contact check */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
if (i == 100) {
dev_err(data->dev,
"VBUS is coming from a dedicated power supply.\n");
return -ENXIO;
}
return 0;
}
static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
struct usb_phy *usb_phy = data->usb_phy;
unsigned long flags;
u32 val;
/* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL;
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
usleep_range(1000, 2000);
/* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) {
dev_dbg(data->dev, "It is a standard downstream port\n");
usb_phy->chg_type = SDP_TYPE;
}
return 0;
}
/**
* Whole charger detection process:
* 1. OPMODE override to be non-driving
* 2. Data contact check
* 3. Primary detection
* 4. Secondary detection
* 5. Disable charger detection
*/
static int imx7d_charger_detection(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
struct usb_phy *usb_phy = data->usb_phy;
unsigned long flags;
u32 val;
int ret;
/* Check if vbus is valid */
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) {
dev_err(data->dev, "vbus is error\n");
return -EINVAL;
}
/*
* Keep OPMODE to be non-driving mode during the whole
* charger detection process.
*/
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING;
writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
ret = imx7d_charger_data_contact_detect(data);
if (ret)
return ret;
ret = imx7d_charger_primary_detection(data);
if (!ret && usb_phy->chg_type != SDP_TYPE)
ret = imx7d_charger_secondary_detection(data);
imx7_disable_charger_detector(data);
return ret;
}
static void usbmisc_imx7d_vbus_comparator_on(struct imx_usbmisc_data *data,
bool on)
{
unsigned long flags;
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
u32 val;
if (data->hsic)
return;
spin_lock_irqsave(&usbmisc->lock, flags);
/*
* Disable VBUS valid comparator when in suspend mode,
* when OTG is disabled and DRVVBUS0 is asserted case
* the Bandgap circuitry and VBUS Valid comparator are
* still powered, even in Suspend or Sleep mode.
*/
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
if (on)
val |= MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
else
val &= ~MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
}
static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + data->index * 4);
spin_unlock_irqrestore(&usbmisc->lock, flags);
/*
* Here use a power on reset value to judge
* if the controller experienced a power lost
*/
if (val == 0x30001000)
return 1;
else
return 0;
}
static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 reg;
if (data->index >= 1)
return -EINVAL;
spin_lock_irqsave(&usbmisc->lock, flags);
reg = readl(usbmisc->base);
if (data->disable_oc) {
reg |= MX6_BM_OVER_CUR_DIS;
} else {
reg &= ~MX6_BM_OVER_CUR_DIS;
/*
* If the polarity is not configured keep it as setup by the
* bootloader.
*/
if (data->oc_pol_configured && data->oc_pol_active_low)
reg |= MX6_BM_OVER_CUR_POLARITY;
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
/* If the polarity is not set keep it as setup by the bootlader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
/* SoC non-burst setting */
reg = readl(usbmisc->base);
writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
if (data->hsic) {
reg = readl(usbmisc->base);
writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base);
reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
/*
* For non-HSIC controller, the autoresume is enabled
* at MXS PHY driver (usbphy_ctrl bit18).
*/
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
writel(reg | MX7D_USBNC_AUTO_RESUME,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
} else {
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
usbmisc_imx7d_set_wakeup(data, false);
return 0;
}
static const struct usbmisc_ops imx25_usbmisc_ops = {
.init = usbmisc_imx25_init,
.post = usbmisc_imx25_post,
@ -639,11 +1031,23 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = {
.init = usbmisc_imx6sx_init,
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
.power_lost_check = usbmisc_imx6sx_power_lost_check,
};
static const struct usbmisc_ops imx7d_usbmisc_ops = {
.init = usbmisc_imx7ulp_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
.power_lost_check = usbmisc_imx7d_power_lost_check,
.charger_detection = imx7d_charger_detection,
.vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
};
static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
.init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
.power_lost_check = usbmisc_imx7d_power_lost_check,
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
};
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
@ -670,31 +1074,30 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc;
int ret = 0;
if (!data)
return 0;
usbmisc = dev_get_drvdata(data->dev);
if (!usbmisc->ops->post)
return 0;
return usbmisc->ops->post(data);
if (usbmisc->ops->post)
ret = usbmisc->ops->post(data);
if (ret) {
dev_err(data->dev, "post init failed, ret=%d\n", ret);
return ret;
}
if (usbmisc->ops->set_wakeup)
ret = usbmisc->ops->set_wakeup(data, false);
if (ret) {
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
{
struct imx_usbmisc *usbmisc;
if (!data)
return 0;
usbmisc = dev_get_drvdata(data->dev);
if (!usbmisc->ops->set_wakeup)
return 0;
return usbmisc->ops->set_wakeup(data, enabled);
}
EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc;
@ -709,19 +1112,116 @@ int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data)
}
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect);
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect)
{
struct imx_usbmisc *usbmisc;
struct usb_phy *usb_phy;
int ret = 0;
if (!data)
return -EINVAL;
usbmisc = dev_get_drvdata(data->dev);
usb_phy = data->usb_phy;
if (!usbmisc->ops->charger_detection)
return -ENOTSUPP;
if (connect) {
ret = usbmisc->ops->charger_detection(data);
if (ret) {
dev_err(data->dev,
"Error occurs during detection: %d\n",
ret);
usb_phy->chg_state = USB_CHARGER_ABSENT;
} else {
usb_phy->chg_state = USB_CHARGER_PRESENT;
}
} else {
usb_phy->chg_state = USB_CHARGER_ABSENT;
usb_phy->chg_type = UNKNOWN_TYPE;
}
return ret;
}
EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection);
int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
{
struct imx_usbmisc *usbmisc;
int ret = 0;
if (!data)
return 0;
usbmisc = dev_get_drvdata(data->dev);
if (!usbmisc->ops->hsic_set_clk || !data->hsic)
return 0;
return usbmisc->ops->hsic_set_clk(data, on);
if (usbmisc->ops->vbus_comparator_on)
usbmisc->ops->vbus_comparator_on(data, false);
if (wakeup && usbmisc->ops->set_wakeup)
ret = usbmisc->ops->set_wakeup(data, true);
if (ret) {
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
return ret;
}
if (usbmisc->ops->hsic_set_clk && data->hsic)
ret = usbmisc->ops->hsic_set_clk(data, false);
if (ret) {
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
return ret;
}
return ret;
}
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
EXPORT_SYMBOL_GPL(imx_usbmisc_suspend);
int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup)
{
struct imx_usbmisc *usbmisc;
int ret = 0;
if (!data)
return 0;
usbmisc = dev_get_drvdata(data->dev);
if (usbmisc->ops->power_lost_check)
ret = usbmisc->ops->power_lost_check(data);
if (ret > 0) {
/* re-init if resume from power lost */
ret = imx_usbmisc_init(data);
if (ret) {
dev_err(data->dev, "re-init failed, ret=%d\n", ret);
return ret;
}
}
if (wakeup && usbmisc->ops->set_wakeup)
ret = usbmisc->ops->set_wakeup(data, false);
if (ret) {
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
return ret;
}
if (usbmisc->ops->hsic_set_clk && data->hsic)
ret = usbmisc->ops->hsic_set_clk(data, true);
if (ret) {
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
goto hsic_set_clk_fail;
}
if (usbmisc->ops->vbus_comparator_on)
usbmisc->ops->vbus_comparator_on(data, true);
return 0;
hsic_set_clk_fail:
if (wakeup && usbmisc->ops->set_wakeup)
usbmisc->ops->set_wakeup(data, true);
return ret;
}
EXPORT_SYMBOL_GPL(imx_usbmisc_resume);
static const struct of_device_id usbmisc_imx_dt_ids[] = {
{
.compatible = "fsl,imx25-usbmisc",
@ -765,7 +1265,7 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
},
{
.compatible = "fsl,imx7ulp-usbmisc",
.data = &imx7d_usbmisc_ops,
.data = &imx7ulp_usbmisc_ops,
},
{ /* sentinel */ }
};
@ -793,6 +1293,18 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
data->ops = (const struct usbmisc_ops *)of_id->data;
platform_set_drvdata(pdev, data);
vbus_wakeup_reg = devm_regulator_get_optional(&pdev->dev, "vbus-wakeup");
if (PTR_ERR(vbus_wakeup_reg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
else if (PTR_ERR(vbus_wakeup_reg) == -ENODEV)
/* no vbus regualator is needed */
vbus_wakeup_reg = NULL;
else if (IS_ERR(vbus_wakeup_reg)) {
dev_err(&pdev->dev, "Getting regulator error: %ld\n",
PTR_ERR(vbus_wakeup_reg));
return PTR_ERR(vbus_wakeup_reg);
}
return 0;
}

View File

@ -2103,6 +2103,140 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
return hcd->driver->get_frame_number (hcd);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_HCD_TEST_MODE
static void usb_ehset_completion(struct urb *urb)
{
struct completion *done = urb->context;
complete(done);
}
/*
* Allocate and initialize a control URB. This request will be used by the
* EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
* of the GetDescriptor request are sent 15 seconds after the SETUP stage.
* Return NULL if failed.
*/
static struct urb *request_single_step_set_feature_urb(
struct usb_device *udev,
void *dr,
void *buf,
struct completion *done
) {
struct urb *urb;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_host_endpoint *ep;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return NULL;
urb->pipe = usb_rcvctrlpipe(udev, 0);
ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
[usb_pipeendpoint(urb->pipe)];
if (!ep) {
usb_free_urb(urb);
return NULL;
}
urb->ep = ep;
urb->dev = udev;
urb->setup_packet = (void *)dr;
urb->transfer_buffer = buf;
urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
urb->complete = usb_ehset_completion;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->transfer_flags = URB_DIR_IN;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
urb->setup_dma = dma_map_single(
hcd->self.sysdev,
urb->setup_packet,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
urb->transfer_dma = dma_map_single(
hcd->self.sysdev,
urb->transfer_buffer,
urb->transfer_buffer_length,
DMA_FROM_DEVICE);
urb->context = done;
return urb;
}
int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
int retval = -ENOMEM;
struct usb_ctrlrequest *dr;
struct urb *urb;
struct usb_device *udev;
struct usb_device_descriptor *buf;
DECLARE_COMPLETION_ONSTACK(done);
/* Obtain udev of the rhub's child port */
udev = usb_hub_find_child(hcd->self.root_hub, port);
if (!udev) {
dev_err(hcd->self.controller, "No device attached to the RootHub\n");
return -ENODEV;
}
buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
if (!dr) {
kfree(buf);
return -ENOMEM;
}
/* Fill Setup packet for GetDescriptor */
dr->bRequestType = USB_DIR_IN;
dr->bRequest = USB_REQ_GET_DESCRIPTOR;
dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
dr->wIndex = 0;
dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
if (!urb)
goto cleanup;
/* Submit just the SETUP stage */
retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 1);
if (retval)
goto out1;
if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
usb_kill_urb(urb);
retval = -ETIMEDOUT;
dev_err(hcd->self.controller,
"%s SETUP stage timed out on ep0\n", __func__);
goto out1;
}
msleep(15 * 1000);
/* Complete remaining DATA and STATUS stages using the same URB */
urb->status = -EINPROGRESS;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0);
if (!retval && !wait_for_completion_timeout(&done,
msecs_to_jiffies(2000))) {
usb_kill_urb(urb);
retval = -ETIMEDOUT;
dev_err(hcd->self.controller,
"%s IN stage timed out on ep0\n", __func__);
}
out1:
usb_free_urb(urb);
cleanup:
kfree(dr);
kfree(buf);
return retval;
}
EXPORT_SYMBOL_GPL(ehset_single_step_set_feature);
#endif /* CONFIG_USB_HCD_TEST_MODE */
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM

View File

@ -4705,7 +4705,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
}
if (r) {
if (r != -ENODEV)
dev_err(&udev->dev, "device descriptor read/64, error %d\n",
dev_err(&udev->dev,
"device no response, device descriptor read/64, error %d\n",
r);
retval = -EMSGSIZE;
continue;

View File

@ -13,35 +13,62 @@
*/
static struct usb_device_id whitelist_table[] = {
/* hubs are optional in OTG, but very handy ... */
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */
/* FIXME actually, printers are NOT supposed to use device classes;
* they're supposed to use interface classes...
*/
{ USB_DEVICE_INFO(7, 1, 1) },
{ USB_DEVICE_INFO(7, 1, 2) },
{ USB_DEVICE_INFO(7, 1, 3) },
/* Add FSL i.mx whitelist, the default list is for USB Compliance Test */
#if defined(CONFIG_USB_EHSET_TEST_FIXTURE) \
|| defined(CONFIG_USB_EHSET_TEST_FIXTURE_MODULE)
#define TEST_SE0_NAK_PID 0x0101
#define TEST_J_PID 0x0102
#define TEST_K_PID 0x0103
#define TEST_PACKET_PID 0x0104
#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106
#define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107
#define TEST_SINGLE_STEP_SET_FEATURE 0x0108
{ USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) },
{ USB_DEVICE(0x1a0a, TEST_J_PID) },
{ USB_DEVICE(0x1a0a, TEST_K_PID) },
{ USB_DEVICE(0x1a0a, TEST_PACKET_PID) },
{ USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) },
{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) },
{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) },
#endif
#ifdef CONFIG_USB_NET_CDCETHER
/* Linux-USB CDC Ethernet gadget */
{ USB_DEVICE(0x0525, 0xa4a1), },
/* Linux-USB CDC Ethernet + RNDIS gadget */
{ USB_DEVICE(0x0525, 0xa4a2), },
#endif
#define USB_INTERFACE_CLASS_INFO(cl) \
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, \
.bInterfaceClass = (cl)
#if IS_ENABLED(CONFIG_USB_TEST)
/* gadget zero, for testing */
{ USB_DEVICE(0x0525, 0xa4a0), },
{USB_INTERFACE_CLASS_INFO(USB_CLASS_HUB) },
#if defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE)
{USB_INTERFACE_CLASS_INFO(USB_CLASS_MASS_STORAGE) },
#endif
#if defined(CONFIG_USB_HID) || defined(CONFIG_USB_HID_MODULE)
{USB_INTERFACE_CLASS_INFO(USB_CLASS_HID) },
#endif
{ } /* Terminating entry */
};
static bool match_int_class(struct usb_device_id *id, struct usb_device *udev)
{
struct usb_host_config *c;
int num_configs, i;
/* Copy the code from generic.c */
c = udev->config;
num_configs = udev->descriptor.bNumConfigurations;
for (i = 0; i < num_configs; (i++, c++)) {
struct usb_interface_descriptor *desc = NULL;
/* It's possible that a config has no interfaces! */
if (c->desc.bNumInterfaces > 0)
desc = &c->intf_cache[0]->altsetting->desc;
if (desc && (desc->bInterfaceClass == id->bInterfaceClass))
return true;
}
return false;
}
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
@ -90,6 +117,10 @@ static int is_targeted(struct usb_device *dev)
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
(!match_int_class(id, dev)))
continue;
return 1;
}

View File

@ -894,6 +894,73 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
}
static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
{
u32 reg, scale;
if (dwc->num_clks == 0)
return;
/*
* The power down scale field specifies how many suspend_clk
* periods fit into a 16KHz clock period. When performing
* the division, round up the remainder.
*/
scale = DIV_ROUND_UP(clk_get_rate(dwc->clks[2].clk), 16384);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
reg |= DWC3_GCTL_PWRDNSCALE(scale);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
#ifdef CONFIG_OF
struct dwc3_cache_type {
u8 transfer_type_datard;
u8 transfer_type_descrd;
u8 transfer_type_datawr;
u8 transfer_type_descwr;
};
static const struct dwc3_cache_type layerscape_dwc3_cache_type = {
.transfer_type_datard = 2,
.transfer_type_descrd = 2,
.transfer_type_datawr = 2,
.transfer_type_descwr = 2,
};
/**
* dwc3_set_cache_type - Configure cache type registers
* @dwc: Pointer to our controller context structure
*/
static void dwc3_set_cache_type(struct dwc3 *dwc)
{
u32 tmp, reg;
const struct dwc3_cache_type *cache_type =
device_get_match_data(dwc->dev);
if (cache_type) {
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
tmp = reg;
reg &= ~DWC3_GSBUSCFG0_DATARD(~0);
reg |= DWC3_GSBUSCFG0_DATARD(cache_type->transfer_type_datard);
reg &= ~DWC3_GSBUSCFG0_DESCRD(~0);
reg |= DWC3_GSBUSCFG0_DESCRD(cache_type->transfer_type_descrd);
reg &= ~DWC3_GSBUSCFG0_DATAWR(~0);
reg |= DWC3_GSBUSCFG0_DATAWR(cache_type->transfer_type_datawr);
reg &= ~DWC3_GSBUSCFG0_DESCWR(~0);
reg |= DWC3_GSBUSCFG0_DESCWR(cache_type->transfer_type_descwr);
if (tmp != reg)
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
}
}
#endif
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@ -918,6 +985,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->maximum_speed = USB_SPEED_HIGH;
}
dwc3_set_power_down_clk_scale(dwc);
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
@ -952,6 +1021,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
#ifdef CONFIG_OF
dwc3_set_cache_type(dwc);
#endif
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
@ -1009,6 +1082,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GUCTL_HSTINAUTORETRY;
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
/*
* Disable Park Mode for super speed:
* Park mode is used in host mode when only a single async
* endpoint is active, but which has a known issue cause
* USB3.0 HC may die when read and write at the same time,
* considering the advantages of this mode are minimal,
* this issue only impacts super speed and exist on all IP
* versions, disable it for SS, Synopsys will release a formal
* STAR 9001415732, and disable it by default in next IP
* release.
*/
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
/*
@ -1236,6 +1324,17 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
if (dwc->dr_mode == USB_DR_MODE_OTG) {
dwc->otg_caps.otg_rev = 0x0300;
dwc->otg_caps.hnp_support = true;
dwc->otg_caps.srp_support = true;
dwc->otg_caps.adp_support = true;
/* Update otg capabilities by DT properties */
of_usb_update_otg_caps(dev->of_node,
&dwc->otg_caps);
}
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->sysdev_is_parent = device_property_read_bool(dev,
@ -1315,6 +1414,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");
dwc->host_vbus_glitches = device_property_read_bool(dev,
"snps,host-vbus-glitches");
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@ -1837,12 +1939,9 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
.compatible = "snps,dwc3"
},
{
.compatible = "synopsys,dwc3"
},
{ .compatible = "fsl,layerscape-dwc3", .data = &layerscape_dwc3_cache_type, },
{ .compatible = "snps,dwc3" },
{ .compatible = "synopsys,dwc3" },
{ },
};
MODULE_DEVICE_TABLE(of, of_dwc3_match);

View File

@ -25,6 +25,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@ -165,6 +166,21 @@
/* Bit fields */
/* Global SoC Bus Configuration INCRx Register 0 */
#ifdef CONFIG_OF
#define DWC3_GSBUSCFG0_DATARD_SHIFT 28
#define DWC3_GSBUSCFG0_DATARD(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DATARD_SHIFT)
#define DWC3_GSBUSCFG0_DESCRD_SHIFT 24
#define DWC3_GSBUSCFG0_DESCRD(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DESCRD_SHIFT)
#define DWC3_GSBUSCFG0_DATAWR_SHIFT 20
#define DWC3_GSBUSCFG0_DATAWR(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DATAWR_SHIFT)
#define DWC3_GSBUSCFG0_DESCWR_SHIFT 16
#define DWC3_GSBUSCFG0_DESCWR(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DESCWR_SHIFT)
#endif
#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
@ -223,6 +239,7 @@
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_PWRDNSCALE_MASK DWC3_GCTL_PWRDNSCALE(0x1fff)
#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
@ -251,6 +268,7 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
/* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10)
@ -941,6 +959,7 @@ struct dwc3_scratchpad_array {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @otg_caps: the OTG capabilities from hardware point
* @revision: revision register contents
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
@ -1029,6 +1048,8 @@ struct dwc3_scratchpad_array {
* 2 - No de-emphasis
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @host_vbus_glitches: set to avoid vbus glitch during
* xhci reset.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
@ -1078,6 +1099,7 @@ struct dwc3 {
void __iomem *regs;
size_t regs_size;
struct usb_role_switch *role_switch;
enum usb_dr_mode dr_mode;
u32 current_dr_role;
u32 desired_dr_role;
@ -1094,6 +1116,7 @@ struct dwc3 {
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
struct usb_otg_caps otg_caps;
/*
* All 3.1 IP version constants are greater than the 3.0 IP
@ -1218,6 +1241,7 @@ struct dwc3 {
unsigned tx_de_emphasis:2;
unsigned dis_metastability_quirk:1;
unsigned host_vbus_glitches:1;
u16 imod_interval;
};

View File

@ -476,6 +476,43 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
return edev;
}
static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role)
{
u32 mode;
switch (role) {
case USB_ROLE_HOST:
mode = DWC3_GCTL_PRTCAP_HOST;
break;
case USB_ROLE_DEVICE:
mode = DWC3_GCTL_PRTCAP_DEVICE;
break;
default:
return 0;
};
dwc3_set_mode(dev_get_drvdata(dev), mode);
return 0;
}
static enum usb_role dwc3_usb_role_switch_get(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
enum usb_role role;
spin_lock_irqsave(&dwc->lock, flags);
role = dwc->current_dr_role;
spin_unlock_irqrestore(&dwc->lock, flags);
return role;
}
static struct usb_role_switch_desc dwc3_role_switch = {
.set = dwc3_usb_role_switch_set,
.get = dwc3_usb_role_switch_get,
};
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret, irq;
@ -484,7 +521,13 @@ int dwc3_drd_init(struct dwc3 *dwc)
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
if (dwc->edev) {
if (device_property_read_bool(dwc->dev, "usb-role-switch")) {
dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
dwc->role_switch = usb_role_switch_register(dwc->dev,
&dwc3_role_switch);
if (IS_ERR(dwc->role_switch))
return PTR_ERR(dwc->role_switch);
} else if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@ -531,6 +574,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
{
unsigned long flags;
if (dwc->role_switch)
usb_role_switch_unregister(dwc->role_switch);
if (dwc->edev)
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);

View File

@ -270,7 +270,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 1000;
u32 timeout = 2000;
u32 saved_config = 0;
u32 reg;
@ -3343,6 +3343,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
dwc->gadget.lpm_capable = true;
dwc->gadget.is_otg = (dwc->dr_mode == USB_DR_MODE_OTG) &&
(dwc->otg_caps.hnp_support ||
dwc->otg_caps.srp_support ||
dwc->otg_caps.adp_support);
/*
* FIXME We might be setting max_speed to <SUPER, however versions

View File

@ -9,8 +9,49 @@
#include <linux/platform_device.h>
#include "../host/xhci.h"
#include "core.h"
#define XHCI_HCSPARAMS1 0x4
#define XHCI_PORTSC_BASE 0x400
/*
* dwc3_power_off_all_roothub_ports - Power off all Root hub ports
* @dwc3: Pointer to our controller context structure
*/
static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
{
int i, port_num;
u32 reg, op_regs_base, offset;
void __iomem *xhci_regs;
/* xhci regs is not mapped yet, do it temperary here */
if (dwc->xhci_resources[0].start) {
xhci_regs = ioremap(dwc->xhci_resources[0].start,
DWC3_XHCI_REGS_END);
if (IS_ERR(xhci_regs)) {
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
return;
}
op_regs_base = HC_LENGTH(readl(xhci_regs));
reg = readl(xhci_regs + XHCI_HCSPARAMS1);
port_num = HCS_MAX_PORTS(reg);
for (i = 1; i <= port_num; i++) {
offset = op_regs_base + XHCI_PORTSC_BASE + 0x10*(i-1);
reg = readl(xhci_regs + offset);
reg &= ~PORT_POWER;
writel(reg, xhci_regs + offset);
}
iounmap(xhci_regs);
} else
dev_err(dwc->dev, "xhci base reg invalid\n");
}
static int dwc3_host_get_irq(struct dwc3 *dwc)
{
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
@ -50,6 +91,13 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
int prop_idx = 0;
/*
* We have to power off all Root hub ports immediately after DWC3 set
* to host mode to avoid VBUS glitch happen when xhci get reset later.
*/
if (dwc->host_vbus_glitches)
dwc3_power_off_all_roothub_ports(dwc);
irq = dwc3_host_get_irq(dwc);
if (irq < 0)
return irq;

View File

@ -1052,10 +1052,11 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep)
u32 bitmask;
struct ep_queue_head *qh;
ep = container_of(_ep, struct fsl_ep, ep);
if (!_ep || (!ep->ep.desc && ep_index(ep) != 0))
if (!_ep || _ep->desc || !(_ep->desc->bEndpointAddress&0xF))
return -ENODEV;
ep = container_of(_ep, struct fsl_ep, ep);
udc = (struct fsl_udc *)ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
@ -1595,14 +1596,13 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
struct fsl_req *curr_req)
{
struct ep_td_struct *curr_td;
int td_complete, actual, remaining_length, j, tmp;
int actual, remaining_length, j, tmp;
int status = 0;
int errors = 0;
struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
int direction = pipe % 2;
curr_td = curr_req->head;
td_complete = 0;
actual = curr_req->req.length;
for (j = 0; j < curr_req->dtd_count; j++) {
@ -1647,11 +1647,9 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
status = -EPROTO;
break;
} else {
td_complete++;
break;
}
} else {
td_complete++;
VDBG("dTD transmitted successful");
}

View File

@ -80,7 +80,7 @@ module_param (log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
/* initial park setting: slower than hw default */
static unsigned park = 0;
static unsigned park = 3;
module_param (park, uint, S_IRUGO);
MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
@ -1232,6 +1232,10 @@ static const struct hc_driver ehci_hc_driver = {
* device support
*/
.free_dev = ehci_remove_device,
#ifdef CONFIG_USB_HCD_TEST_MODE
/* EH SINGLE_STEP_SET_FEATURE test support */
.submit_single_step_set_feature = ehci_submit_single_step_set_feature,
#endif
};
void ehci_init_driver(struct hc_driver *drv,

View File

@ -724,145 +724,6 @@ ehci_hub_descriptor (
desc->wHubCharacteristics = cpu_to_le16(temp);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_HCD_TEST_MODE
#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
static void usb_ehset_completion(struct urb *urb)
{
struct completion *done = urb->context;
complete(done);
}
static int submit_single_step_set_feature(
struct usb_hcd *hcd,
struct urb *urb,
int is_setup
);
/*
* Allocate and initialize a control URB. This request will be used by the
* EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
* of the GetDescriptor request are sent 15 seconds after the SETUP stage.
* Return NULL if failed.
*/
static struct urb *request_single_step_set_feature_urb(
struct usb_device *udev,
void *dr,
void *buf,
struct completion *done
) {
struct urb *urb;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_host_endpoint *ep;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return NULL;
urb->pipe = usb_rcvctrlpipe(udev, 0);
ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
[usb_pipeendpoint(urb->pipe)];
if (!ep) {
usb_free_urb(urb);
return NULL;
}
urb->ep = ep;
urb->dev = udev;
urb->setup_packet = (void *)dr;
urb->transfer_buffer = buf;
urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
urb->complete = usb_ehset_completion;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->transfer_flags = URB_DIR_IN;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
urb->setup_dma = dma_map_single(
hcd->self.sysdev,
urb->setup_packet,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
urb->transfer_dma = dma_map_single(
hcd->self.sysdev,
urb->transfer_buffer,
urb->transfer_buffer_length,
DMA_FROM_DEVICE);
urb->context = done;
return urb;
}
static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
int retval = -ENOMEM;
struct usb_ctrlrequest *dr;
struct urb *urb;
struct usb_device *udev;
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct usb_device_descriptor *buf;
DECLARE_COMPLETION_ONSTACK(done);
/* Obtain udev of the rhub's child port */
udev = usb_hub_find_child(hcd->self.root_hub, port);
if (!udev) {
ehci_err(ehci, "No device attached to the RootHub\n");
return -ENODEV;
}
buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
if (!dr) {
kfree(buf);
return -ENOMEM;
}
/* Fill Setup packet for GetDescriptor */
dr->bRequestType = USB_DIR_IN;
dr->bRequest = USB_REQ_GET_DESCRIPTOR;
dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
dr->wIndex = 0;
dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
if (!urb)
goto cleanup;
/* Submit just the SETUP stage */
retval = submit_single_step_set_feature(hcd, urb, 1);
if (retval)
goto out1;
if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
usb_kill_urb(urb);
retval = -ETIMEDOUT;
ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
goto out1;
}
msleep(15 * 1000);
/* Complete remaining DATA and STATUS stages using the same URB */
urb->status = -EINPROGRESS;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
retval = submit_single_step_set_feature(hcd, urb, 0);
if (!retval && !wait_for_completion_timeout(&done,
msecs_to_jiffies(2000))) {
usb_kill_urb(urb);
retval = -ETIMEDOUT;
ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
}
out1:
usb_free_urb(urb);
cleanup:
kfree(dr);
kfree(buf);
return retval;
}
#endif /* CONFIG_USB_HCD_TEST_MODE */
/*-------------------------------------------------------------------------*/
int ehci_hub_control(

View File

@ -1154,7 +1154,7 @@ submit_async (
* performed; TRUE - SETUP and FALSE - IN+STATUS
* Returns 0 if success
*/
static int submit_single_step_set_feature(
static int ehci_submit_single_step_set_feature(
struct usb_hcd *hcd,
struct urb *urb,
int is_setup

View File

@ -1359,6 +1359,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
if (hcd->speed != HCD_USB2)
goto error;
#ifdef CONFIG_USB_HCD_TEST_MODE
if (test_mode == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
spin_unlock_irqrestore(&xhci->lock, flags);
retval = ehset_single_step_set_feature(hcd,
wIndex + 1);
spin_lock_irqsave(&xhci->lock, flags);
break;
}
#endif
if (test_mode > TEST_FORCE_EN || test_mode < TEST_J)
goto error;
retval = xhci_enter_test_mode(xhci, test_mode, wIndex,
@ -1637,6 +1646,7 @@ retry:
spin_unlock_irqrestore(&xhci->lock, flags);
return 0;
}
EXPORT_SYMBOL(xhci_bus_suspend);
/*
* Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3.
@ -1654,7 +1664,8 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
return false;
if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) &&
((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE))
((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE) &&
((portsc & PORT_PLS_MASK) != XDEV_RXDETECT))
return false;
/* clear wakeup/change bits, and do a warm port reset */

View File

@ -291,6 +291,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
if (device_property_read_bool(tmpdev,
"usb3-resume-missing-cas"))
xhci->quirks |= XHCI_MISSING_CAS;
}
hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);

View File

@ -2028,12 +2028,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
switch (trb_comp_code) {
case COMP_SUCCESS:
if (trb_type != TRB_STATUS) {
xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
if (trb_type != TRB_STATUS)
xhci_dbg(xhci, "Success on ctrl %s TRB without IOC set?\n",
(trb_type == TRB_DATA) ? "data" : "setup");
*status = -ESHUTDOWN;
break;
}
*status = 0;
break;
case COMP_SHORT_PACKET:
@ -3519,6 +3516,129 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return 0;
}
#ifdef CONFIG_USB_HCD_TEST_MODE
/*
* This function prepare TRBs and submits them for the
* SINGLE_STEP_SET_FEATURE Test.
* This is done in two parts: first SETUP req for GetDesc is sent then
* 15 seconds later, the IN stage for GetDesc starts to req data from dev
*
* is_setup : argument decides which of the two stage needs to be
* performed; TRUE - SETUP and FALSE - IN+STATUS
* Returns 0 if success
*/
int xhci_submit_single_step_set_feature(struct usb_hcd *hcd,
struct urb *urb, int is_setup)
{
int slot_id;
unsigned int ep_index;
struct xhci_ring *ep_ring;
int ret;
struct usb_ctrlrequest *setup;
struct xhci_generic_trb *start_trb;
int start_cycle;
u32 field, length_field, remainder;
struct urb_priv *urb_priv;
struct xhci_td *td;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
/* urb_priv will be free after transcation has completed */
urb_priv = kzalloc(sizeof(struct urb_priv) +
sizeof(struct xhci_td), GFP_KERNEL);
if (!urb_priv)
return -ENOMEM;
td = &urb_priv->td[0];
urb_priv->num_tds = 1;
urb_priv->num_tds_done = 0;
urb->hcpriv = urb_priv;
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
if (!ep_ring) {
ret = -EINVAL;
goto free_priv;
}
slot_id = urb->dev->slot_id;
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
setup = (struct usb_ctrlrequest *) urb->setup_packet;
if (is_setup) {
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
1, urb, 0, GFP_KERNEL);
if (ret < 0)
goto free_priv;
start_trb = &ep_ring->enqueue->generic;
start_cycle = ep_ring->cycle_state;
/* Save the DMA address of the last TRB in the TD */
td->last_trb = ep_ring->enqueue;
field = TRB_IOC | TRB_IDT | TRB_TYPE(TRB_SETUP) | start_cycle;
/* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
if ((xhci->hci_version >= 0x100) ||
(xhci->quirks & XHCI_MTK_HOST))
field |= TRB_TX_TYPE(TRB_DATA_IN);
queue_trb(xhci, ep_ring, false,
setup->bRequestType | setup->bRequest << 8 |
le16_to_cpu(setup->wValue) << 16,
le16_to_cpu(setup->wIndex) |
le16_to_cpu(setup->wLength) << 16,
TRB_LEN(8) | TRB_INTR_TARGET(0),
/* Immediate data in pointer */
field);
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
start_cycle, start_trb);
return 0;
}
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
2, urb, 0, GFP_KERNEL);
if (ret < 0)
goto free_priv;
start_trb = &ep_ring->enqueue->generic;
start_cycle = ep_ring->cycle_state;
field = TRB_ISP | TRB_TYPE(TRB_DATA);
remainder = xhci_td_remainder(xhci, 0,
urb->transfer_buffer_length,
urb->transfer_buffer_length,
urb, 1);
length_field = TRB_LEN(urb->transfer_buffer_length) |
TRB_TD_SIZE(remainder) |
TRB_INTR_TARGET(0);
if (urb->transfer_buffer_length > 0) {
field |= TRB_DIR_IN;
queue_trb(xhci, ep_ring, true,
lower_32_bits(urb->transfer_dma),
upper_32_bits(urb->transfer_dma),
length_field,
field | ep_ring->cycle_state);
}
td->last_trb = ep_ring->enqueue;
field = TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state;
queue_trb(xhci, ep_ring, false,
0,
0,
TRB_INTR_TARGET(0),
field);
giveback_first_trb(xhci, slot_id, ep_index, 0,
start_cycle, start_trb);
return 0;
free_priv:
xhci_urb_free_priv(urb_priv);
return ret;
}
#endif /* CONFIG_USB_HCD_TEST_MODE */
/*
* The transfer burst count field of the isochronous TRB defines the number of
* bursts that are required to move all packets in this TD. Only SuperSpeed

View File

@ -193,7 +193,7 @@ int xhci_reset(struct xhci_hcd *xhci)
* Without this delay, the subsequent HC register access,
* may result in a system hang very rarely.
*/
if (xhci->quirks & XHCI_INTEL_HOST)
if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_CDNS_HOST))
udelay(1000);
ret = xhci_handshake(&xhci->op_regs->command,
@ -5355,6 +5355,7 @@ static const struct hc_driver xhci_hc_driver = {
.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
.find_raw_port_number = xhci_find_raw_port_number,
.clear_tt_buffer_complete = xhci_clear_tt_buffer_complete,
.submit_single_step_set_feature = xhci_submit_single_step_set_feature,
};
void xhci_init_driver(struct hc_driver *drv,
@ -5371,6 +5372,8 @@ void xhci_init_driver(struct hc_driver *drv,
drv->reset = over->reset;
if (over->start)
drv->start = over->start;
if (over->bus_suspend)
drv->bus_suspend = over->bus_suspend;
}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);

View File

@ -1867,6 +1867,7 @@ struct xhci_hcd {
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
#define XHCI_CDNS_HOST BIT_ULL(36)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@ -1901,6 +1902,7 @@ struct xhci_driver_overrides {
size_t extra_priv_size;
int (*reset)(struct usb_hcd *hcd);
int (*start)(struct usb_hcd *hcd);
int (*bus_suspend)(struct usb_hcd *hcd);
};
#define XHCI_CFC_DELAY 10
@ -2132,6 +2134,16 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
void xhci_hc_died(struct xhci_hcd *xhci);
#ifdef CONFIG_USB_HCD_TEST_MODE
int xhci_submit_single_step_set_feature(struct usb_hcd *hcd,
struct urb *urb, int is_setup);
#else
static inline int xhci_submit_single_step_set_feature(struct usb_hcd *hcd,
struct urb *urb, int is_setup)
{
return 0;
}
#endif
#ifdef CONFIG_PM
int xhci_bus_suspend(struct usb_hcd *hcd);

View File

@ -152,7 +152,7 @@ config USB_MV_OTG
config USB_MXS_PHY
tristate "Freescale MXS USB PHY support"
depends on ARCH_MXC || ARCH_MXS
depends on ARCH_MXC || ARCH_MXS || ARCH_MXC_ARM64
select STMP_DEVICE
select USB_PHY
help

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2012-2014 Freescale Semiconductor, Inc.
* Copyright 2012-2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
* Copyright (C) 2012 Marek Vasut <marex@denx.de>
* on behalf of DENX Software Engineering GmbH
*/
@ -18,6 +19,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
#include <linux/regulator/consumer.h>
#define DRIVER_NAME "mxs_phy"
@ -70,6 +72,12 @@
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
/* Anatop Registers */
#define ANADIG_PLL_USB2 0x20
#define ANADIG_PLL_USB2_SET 0x24
#define ANADIG_PLL_USB2_CLR 0x28
#define ANADIG_REG_1P1_SET 0x114
#define ANADIG_REG_1P1_CLR 0x118
#define ANADIG_ANA_MISC0 0x150
#define ANADIG_ANA_MISC0_SET 0x154
#define ANADIG_ANA_MISC0_CLR 0x158
@ -117,6 +125,42 @@
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
/* System Integration Module (SIM) Registers */
#define SIM_GPR1 0x30
#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
#define BM_ANADIG_PLL_USB2_HOLD_RING_OFF BIT(11)
/* DCD module, the offset is 0x800 */
#define DCD_CONTROL 0x800
#define DCD_CLOCK (DCD_CONTROL + 0x4)
#define DCD_STATUS (DCD_CONTROL + 0x8)
#define DCD_CONTROL_SR BIT(25)
#define DCD_CONTROL_START BIT(24)
#define DCD_CONTROL_BC12 BIT(17)
#define DCD_CONTROL_IE BIT(16)
#define DCD_CONTROL_IF BIT(8)
#define DCD_CONTROL_IACK BIT(0)
#define DCD_CLOCK_MHZ BIT(0)
#define DCD_STATUS_ACTIVE BIT(22)
#define DCD_STATUS_TO BIT(21)
#define DCD_STATUS_ERR BIT(20)
#define DCD_STATUS_SEQ_STAT (BIT(18) | BIT(19))
#define DCD_CHG_PORT BIT(19)
#define DCD_CHG_DET (BIT(18) | BIT(19))
#define DCD_CHG_DPIN BIT(18)
#define DCD_STATUS_SEQ_RES (BIT(16) | BIT(17))
#define DCD_SDP_PORT BIT(16)
#define DCD_CDP_PORT BIT(17)
#define DCD_DCP_PORT (BIT(16) | BIT(17))
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@ -149,6 +193,19 @@
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
/*
* At some versions, the PHY2's clock is controlled by hardware directly,
* eg, according to PHY's suspend status. In these PHYs, we only need to
* open the clock at the initialization and close it at its shutdown routine.
* It will be benefit for remote wakeup case which needs to send resume
* signal as soon as possible, and in this case, the resume signal can be sent
* out without software interfere.
*/
#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
/* The MXS PHYs which have DCD module for charger detection */
#define MXS_PHY_HAS_DCD BIT(5)
struct mxs_phy_data {
unsigned int flags;
};
@ -160,12 +217,14 @@ static const struct mxs_phy_data imx23_phy_data = {
static const struct mxs_phy_data imx6q_phy_data = {
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
MXS_PHY_NEED_IP_FIX |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6sl_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
MXS_PHY_NEED_IP_FIX |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data vf610_phy_data = {
@ -174,14 +233,17 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6ul_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx7ulp_phy_data = {
.flags = MXS_PHY_HAS_DCD,
};
static const struct of_device_id mxs_phy_dt_ids[] = {
@ -201,9 +263,14 @@ struct mxs_phy {
struct clk *clk;
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
struct regmap *regmap_sim;
int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
struct regulator *phy_3p0;
bool hardware_control_phy2_clk;
enum usb_current_mode mode;
unsigned long clk_rate;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@ -221,6 +288,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
return mxs_phy->data == &imx7ulp_phy_data;
}
static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
{
return mxs_phy->data == &imx6ul_phy_data;
}
/*
* PHY needs some 32K cycles to switch from 32K clock to
* bus (such as AHB/AXI, etc) clock.
@ -288,6 +360,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (ret)
goto disable_pll;
if (mxs_phy->phy_3p0) {
ret = regulator_enable(mxs_phy->phy_3p0);
if (ret) {
dev_err(mxs_phy->phy.dev,
"Failed to enable 3p0 regulator, ret=%d\n",
ret);
return ret;
}
}
/* Power up the PHY */
writel(0, base + HW_USBPHY_PWD);
@ -386,21 +468,10 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
usleep_range(500, 1000);
}
static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
{
void __iomem *base = mxs_phy->phy.io_priv;
u32 phyctrl = readl(base + HW_USBPHY_CTRL);
if (IS_ENABLED(CONFIG_USB_OTG) &&
!(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
return true;
return false;
}
static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
{
bool vbus_is_on = false;
enum usb_phy_events last_event = mxs_phy->phy.last_event;
/* If the SoCs don't need to disconnect line without vbus, quit */
if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
@ -412,7 +483,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
if (on && ((!vbus_is_on && mxs_phy->mode != CUR_USB_MODE_HOST) ||
(last_event == USB_EVENT_VBUS)))
__mxs_phy_disconnect_line(mxs_phy, true);
else
__mxs_phy_disconnect_line(mxs_phy, false);
@ -453,6 +525,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
if (is_imx7ulp_phy(mxs_phy))
mxs_phy_pll_enable(phy->io_priv, false);
if (mxs_phy->phy_3p0)
regulator_disable(mxs_phy->phy_3p0);
clk_disable_unprepare(mxs_phy->clk);
}
@ -506,14 +581,49 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
} else {
writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
}
/*
* USB2 PLL use ring VCO, when the PLL power up, the ring
* VCOs supply also ramp up. There is a possibility that
* the ring VCO start oscillation at multi nodes in this
* phase, especially for VCO which has many stages, then
* the multiwave will be kept until PLL power down. the bit
* hold_ring_off can force the VCO in one determined state
* to avoid the multiwave issue when VCO supply start ramp
* up.
*/
if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop)
regmap_write(mxs_phy->regmap_anatop,
ANADIG_PLL_USB2_SET,
BM_ANADIG_PLL_USB2_HOLD_RING_OFF);
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
clk_disable_unprepare(mxs_phy->clk);
if (!(mxs_phy->port_id == 1 &&
mxs_phy->hardware_control_phy2_clk))
clk_disable_unprepare(mxs_phy->clk);
} else {
mxs_phy_clock_switch_delay();
ret = clk_prepare_enable(mxs_phy->clk);
if (ret)
return ret;
if (!(mxs_phy->port_id == 1 &&
mxs_phy->hardware_control_phy2_clk)) {
ret = clk_prepare_enable(mxs_phy->clk);
if (ret)
return ret;
}
/*
* Per IC design's requirement, hold_ring_off bit can be
* cleared 25us after PLL power up and 25us before any USB
* TX/RX.
*/
if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop) {
udelay(25);
regmap_write(mxs_phy->regmap_anatop,
ANADIG_PLL_USB2_CLR,
BM_ANADIG_PLL_USB2_HOLD_RING_OFF);
udelay(25);
}
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@ -708,6 +818,169 @@ static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy)
return chgr_type;
}
static int mxs_phy_on_suspend(struct usb_phy *phy,
enum usb_device_speed speed)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
dev_dbg(phy->dev, "%s device has suspended\n",
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
/* delay 4ms to wait bus entering idle */
usleep_range(4000, 5000);
if (mxs_phy->data->flags & MXS_PHY_ABNORMAL_IN_SUSPEND) {
writel_relaxed(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
writel_relaxed(0, phy->io_priv + HW_USBPHY_PWD);
}
if (speed == USB_SPEED_HIGH)
writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_CLR);
return 0;
}
/*
* The resume signal must be finished here.
*/
static int mxs_phy_on_resume(struct usb_phy *phy,
enum usb_device_speed speed)
{
dev_dbg(phy->dev, "%s device has resumed\n",
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
if (speed == USB_SPEED_HIGH) {
/* Make sure the device has switched to High-Speed mode */
udelay(500);
writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_SET);
}
return 0;
}
/*
* Set the usb current role for phy.
*/
static int mxs_phy_set_mode(struct usb_phy *phy,
enum usb_current_mode mode)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
mxs_phy->mode = mode;
return 0;
}
static int mxs_phy_dcd_start(struct mxs_phy *mxs_phy)
{
void __iomem *base = mxs_phy->phy.io_priv;
u32 value;
value = readl(base + DCD_CONTROL);
writel(value | DCD_CONTROL_SR, base + DCD_CONTROL);
if (!mxs_phy->clk_rate)
return -EINVAL;
value = readl(base + DCD_CONTROL);
writel(((mxs_phy->clk_rate / 1000000) << 2) | DCD_CLOCK_MHZ,
base + DCD_CLOCK);
value = readl(base + DCD_CONTROL);
value &= ~DCD_CONTROL_IE;
writel(value | DCD_CONTROL_BC12, base + DCD_CONTROL);
value = readl(base + DCD_CONTROL);
writel(value | DCD_CONTROL_START, base + DCD_CONTROL);
return 0;
}
#define DCD_CHARGING_DURTION 1000 /* One second according to BC 1.2 */
static enum usb_charger_type mxs_phy_dcd_flow(struct usb_phy *phy)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
void __iomem *base = mxs_phy->phy.io_priv;
u32 value;
int i = 0;
enum usb_charger_type chgr_type;
if (mxs_phy_dcd_start(mxs_phy))
return UNKNOWN_TYPE;
while (i++ <= (DCD_CHARGING_DURTION / 50)) {
value = readl(base + DCD_CONTROL);
if (value & DCD_CONTROL_IF) {
value = readl(base + DCD_STATUS);
if (value & DCD_STATUS_ACTIVE) {
dev_err(phy->dev, "still detecting\n");
chgr_type = UNKNOWN_TYPE;
break;
}
if (value & DCD_STATUS_TO) {
dev_err(phy->dev, "detect timeout\n");
chgr_type = UNKNOWN_TYPE;
break;
}
if (value & DCD_STATUS_ERR) {
dev_err(phy->dev, "detect error\n");
chgr_type = UNKNOWN_TYPE;
break;
}
if ((value & DCD_STATUS_SEQ_STAT) <= DCD_CHG_DPIN) {
dev_err(phy->dev, "error occurs\n");
chgr_type = UNKNOWN_TYPE;
break;
}
/* SDP */
if (((value & DCD_STATUS_SEQ_STAT) == DCD_CHG_PORT) &&
((value & DCD_STATUS_SEQ_RES)
== DCD_SDP_PORT)) {
dev_dbg(phy->dev, "SDP\n");
chgr_type = SDP_TYPE;
break;
}
if ((value & DCD_STATUS_SEQ_STAT) == DCD_CHG_DET) {
if ((value & DCD_STATUS_SEQ_RES) ==
DCD_CDP_PORT) {
dev_dbg(phy->dev, "CDP\n");
chgr_type = CDP_TYPE;
break;
}
if ((value & DCD_STATUS_SEQ_RES) ==
DCD_DCP_PORT) {
dev_dbg(phy->dev, "DCP\n");
chgr_type = DCP_TYPE;
break;
}
}
dev_err(phy->dev, "unknown error occurs\n");
chgr_type = UNKNOWN_TYPE;
break;
}
msleep(50);
}
if (i > 20) {
dev_err(phy->dev, "charger detecting timeout\n");
chgr_type = UNKNOWN_TYPE;
}
/* disable dcd module */
readl(base + DCD_STATUS);
writel(DCD_CONTROL_IACK, base + DCD_CONTROL);
writel(DCD_CONTROL_SR, base + DCD_CONTROL);
return chgr_type;
}
static int mxs_phy_probe(struct platform_device *pdev)
{
struct resource *res;
@ -739,6 +1012,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
if (!mxs_phy)
return -ENOMEM;
mxs_phy->clk_rate = clk_get_rate(clk);
/* Some SoCs don't have anatop registers */
if (of_get_property(np, "fsl,anatop", NULL)) {
mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
@ -750,6 +1024,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
/* Currently, only imx7ulp has SIM module */
if (of_get_property(np, "nxp,sim", NULL)) {
mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
(np, "nxp,sim");
if (IS_ERR(mxs_phy->regmap_sim)) {
dev_dbg(&pdev->dev,
"failed to find regmap for sim\n");
return PTR_ERR(mxs_phy->regmap_sim);
}
}
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
@ -784,6 +1069,8 @@ static int mxs_phy_probe(struct platform_device *pdev)
ret = of_alias_get_id(np, "usbphy");
if (ret < 0)
dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
mxs_phy->clk = clk;
mxs_phy->data = of_id->data;
mxs_phy->port_id = ret;
mxs_phy->phy.io_priv = base;
@ -796,10 +1083,33 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
mxs_phy->phy.type = USB_PHY_TYPE_USB2;
mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup;
mxs_phy->phy.charger_detect = mxs_phy_charger_detect;
if (mxs_phy->data->flags & MXS_PHY_HAS_DCD)
mxs_phy->phy.charger_detect = mxs_phy_dcd_flow;
else
mxs_phy->phy.charger_detect = mxs_phy_charger_detect;
mxs_phy->clk = clk;
mxs_phy->data = of_id->data;
mxs_phy->phy.set_mode = mxs_phy_set_mode;
if (mxs_phy->data->flags & MXS_PHY_SENDING_SOF_TOO_FAST) {
mxs_phy->phy.notify_suspend = mxs_phy_on_suspend;
mxs_phy->phy.notify_resume = mxs_phy_on_resume;
}
mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
if (PTR_ERR(mxs_phy->phy_3p0) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV) {
/* not exist */
mxs_phy->phy_3p0 = NULL;
} else if (IS_ERR(mxs_phy->phy_3p0)) {
dev_err(&pdev->dev, "Getting regulator error: %ld\n",
PTR_ERR(mxs_phy->phy_3p0));
return PTR_ERR(mxs_phy->phy_3p0);
}
if (mxs_phy->phy_3p0)
regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
if (mxs_phy->data->flags & MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)
mxs_phy->hardware_control_phy2_clk = true;
platform_set_drvdata(pdev, mxs_phy);
@ -818,28 +1128,58 @@ static int mxs_phy_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
{
u32 mask = USB_PHY_VLLS_WAKEUP_EN;
/* If the SoCs don't have SIM, quit */
if (!mxs_phy->regmap_sim)
return;
if (on) {
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
udelay(500);
} else {
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
}
}
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{
unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
unsigned int reg;
u32 value;
/* If the SoCs don't have anatop, quit */
if (!mxs_phy->regmap_anatop)
return;
if (is_imx6q_phy(mxs_phy))
if (is_imx6q_phy(mxs_phy)) {
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop, reg,
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
else if (is_imx6sl_phy(mxs_phy))
} else if (is_imx6sl_phy(mxs_phy)) {
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop,
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
} else if (is_imx6ul_phy(mxs_phy)) {
reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
if (mxs_phy_get_vbus_status(mxs_phy) && on)
regmap_write(mxs_phy->regmap_anatop, reg, value);
else if (!on)
regmap_write(mxs_phy->regmap_anatop, reg, value);
}
}
static int mxs_phy_system_suspend(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
mxs_phy_wakeup_enable(mxs_phy, true);
}
return 0;
}
@ -848,8 +1188,10 @@ static int mxs_phy_system_resume(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
mxs_phy_wakeup_enable(mxs_phy, false);
}
return 0;
}

View File

@ -34,6 +34,14 @@ struct phy_devm {
struct notifier_block *nb;
};
static const char *const usb_chger_type[] = {
[UNKNOWN_TYPE] = "USB_CHARGER_UNKNOWN_TYPE",
[SDP_TYPE] = "USB_CHARGER_SDP_TYPE",
[CDP_TYPE] = "USB_CHARGER_CDP_TYPE",
[DCP_TYPE] = "USB_CHARGER_DCP_TYPE",
[ACA_TYPE] = "USB_CHARGER_ACA_TYPE",
};
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@ -98,7 +106,8 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
{
struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
char uchger_state[50] = { 0 };
char *envp[] = { uchger_state, NULL };
char uchger_type[50] = { 0 };
char *envp[] = { uchger_state, uchger_type, NULL };
unsigned int min, max;
switch (usb_phy->chg_state) {
@ -122,6 +131,8 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
return;
}
snprintf(uchger_type, ARRAY_SIZE(uchger_type),
"USB_CHARGER_TYPE=%s", usb_chger_type[usb_phy->chg_type]);
kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
}

View File

@ -9,4 +9,10 @@ config TYPEC_MUX_PI3USB30532
Say Y or M if your system has a Pericom PI3USB30532 Type-C cross
switch / mux chip found on some devices with a Type-C port.
config TYPEC_SWITCH_GPIO
tristate "Simple Super Speed Active Switch via GPIO"
help
Say Y or M if your system has a typec super speed channel
switch via a simple GPIO control.
endmenu

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
obj-$(CONFIG_TYPEC_SWITCH_GPIO) += gpio-switch.o

View File

@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0
/**
* gpio-switch.c - typec switch via a simple GPIO control.
*
* Copyright 2019 NXP
* Author: Jun Li <jun.li@nxp.com>
*
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/usb/typec_mux.h>
struct gpio_typec_switch {
struct typec_switch *sw;
struct mutex lock;
struct gpio_desc *ss_sel;
struct gpio_desc *ss_reset;
};
static int switch_gpio_set(struct typec_switch *sw,
enum typec_orientation orientation)
{
struct gpio_typec_switch *gpio_sw = typec_switch_get_drvdata(sw);
mutex_lock(&gpio_sw->lock);
switch (orientation) {
case TYPEC_ORIENTATION_NORMAL:
gpiod_set_value_cansleep(gpio_sw->ss_sel, 1);
break;
case TYPEC_ORIENTATION_REVERSE:
gpiod_set_value_cansleep(gpio_sw->ss_sel, 0);
break;
case TYPEC_ORIENTATION_NONE:
break;
}
mutex_unlock(&gpio_sw->lock);
return 0;
}
static int typec_switch_gpio_probe(struct platform_device *pdev)
{
struct gpio_typec_switch *gpio_sw;
struct device *dev = &pdev->dev;
struct typec_switch_desc sw_desc;
gpio_sw = devm_kzalloc(dev, sizeof(*gpio_sw), GFP_KERNEL);
if (!gpio_sw)
return -ENOMEM;
platform_set_drvdata(pdev, gpio_sw);
sw_desc.drvdata = gpio_sw;
sw_desc.fwnode = dev->fwnode;
sw_desc.set = switch_gpio_set;
mutex_init(&gpio_sw->lock);
/* Get the super speed mux reset GPIO, it's optional */
gpio_sw->ss_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(gpio_sw->ss_reset))
return PTR_ERR(gpio_sw->ss_reset);
if (gpio_sw->ss_reset)
usleep_range(700, 1000);
/* Get the super speed active channel selection GPIO */
gpio_sw->ss_sel = devm_gpiod_get(dev, "switch", GPIOD_OUT_LOW);
if (IS_ERR(gpio_sw->ss_sel))
return PTR_ERR(gpio_sw->ss_sel);
gpio_sw->sw = typec_switch_register(dev, &sw_desc);
if (IS_ERR(gpio_sw->sw)) {
dev_err(dev, "Error registering typec switch: %ld\n", PTR_ERR(gpio_sw->sw));
return PTR_ERR(gpio_sw->sw);
}
return 0;
}
static int typec_switch_gpio_remove(struct platform_device *pdev)
{
struct gpio_typec_switch *gpio_sw = platform_get_drvdata(pdev);
typec_switch_unregister(gpio_sw->sw);
return 0;
}
static const struct of_device_id of_typec_switch_gpio_match[] = {
{ .compatible = "nxp,ptn36043" },
{ .compatible = "nxp,cbtl04gp" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_typec_switch_gpio_match);
static struct platform_driver typec_switch_gpio_driver = {
.probe = typec_switch_gpio_probe,
.remove = typec_switch_gpio_remove,
.driver = {
.name = "typec-switch-gpio",
.of_match_table = of_typec_switch_gpio_match,
},
};
module_platform_driver(typec_switch_gpio_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("TypeC Super Speed Switch GPIO driver");
MODULE_AUTHOR("Jun Li <jun.li@nxp.com>");

View File

@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/usb/pd.h>
@ -108,9 +109,6 @@ static int tcpci_start_toggling(struct tcpc_dev *tcpc,
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = TCPC_ROLE_CTRL_DRP;
if (port_type != TYPEC_PORT_DRP)
return -EOPNOTSUPP;
/* Handle vendor drp toggling */
if (tcpci->data->start_drp_toggling) {
ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
@ -566,6 +564,7 @@ static int tcpci_probe(struct i2c_client *client,
if (IS_ERR(chip->tcpci))
return PTR_ERR(chip->tcpci);
irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
_tcpci_irq,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
@ -575,6 +574,8 @@ static int tcpci_probe(struct i2c_client *client,
return err;
}
device_set_wakeup_capable(chip->tcpci->dev, true);
return 0;
}
@ -583,10 +584,40 @@ static int tcpci_remove(struct i2c_client *client)
struct tcpci_chip *chip = i2c_get_clientdata(client);
tcpci_unregister_port(chip->tcpci);
irq_clear_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
return 0;
}
static int __maybe_unused tcpci_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(dev))
enable_irq_wake(i2c->irq);
else
disable_irq(i2c->irq);
return 0;
}
static int __maybe_unused tcpci_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(dev))
disable_irq_wake(i2c->irq);
else
enable_irq(i2c->irq);
return 0;
}
static const struct dev_pm_ops tcpci_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tcpci_suspend, tcpci_resume)
};
static const struct i2c_device_id tcpci_id[] = {
{ "tcpci", 0 },
{ }
@ -604,6 +635,7 @@ MODULE_DEVICE_TABLE(of, tcpci_of_match);
static struct i2c_driver tcpci_i2c_driver = {
.driver = {
.name = "tcpci",
.pm = &tcpci_pm_ops,
.of_match_table = of_match_ptr(tcpci_of_match),
},
.probe = tcpci_probe,

View File

@ -29,6 +29,8 @@
#include <linux/usb/tcpm.h>
#include <linux/usb/typec_altmode.h>
#include <linux/workqueue.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#define FOREACH_STATE(S) \
S(INVALID_STATE), \
@ -139,6 +141,12 @@ static const char * const tcpm_states[] = {
FOREACH_STATE(GENERATE_STRING)
};
static const unsigned int tcpm_extcon_cable[] = {
EXTCON_USB_HOST,
EXTCON_USB,
EXTCON_NONE,
};
enum vdm_states {
VDM_STATE_ERR_BUSY = -3,
VDM_STATE_ERR_SEND = -2,
@ -193,6 +201,7 @@ struct pd_pps_data {
struct tcpm_port {
struct device *dev;
struct extcon_dev *edev;
struct mutex lock; /* tcpm state machine lock */
struct workqueue_struct *wq;
@ -670,6 +679,20 @@ static int tcpm_mux_set(struct tcpm_port *port, int state,
ret = usb_role_switch_set_role(port->role_sw, usb_role);
if (ret)
return ret;
} else if (port->edev) {
if (usb_role == USB_ROLE_NONE) {
extcon_set_state_sync(port->edev, EXTCON_USB_HOST,
false);
extcon_set_state_sync(port->edev, EXTCON_USB, false);
} else if (usb_role == USB_ROLE_DEVICE) {
extcon_set_state_sync(port->edev, EXTCON_USB_HOST,
false);
extcon_set_state_sync(port->edev, EXTCON_USB, true);
} else {
extcon_set_state_sync(port->edev, EXTCON_USB, false);
extcon_set_state_sync(port->edev, EXTCON_USB_HOST,
true);
}
}
return typec_set_mode(port->typec_port, state);
@ -2623,6 +2646,8 @@ static int tcpm_src_attach(struct tcpm_port *port)
if (port->attached)
return 0;
tcpm_set_cc(port, tcpm_rp_cc(port));
ret = tcpm_set_polarity(port, polarity);
if (ret < 0)
return ret;
@ -2744,6 +2769,8 @@ static int tcpm_snk_attach(struct tcpm_port *port)
if (port->attached)
return 0;
tcpm_set_cc(port, TYPEC_CC_RD);
ret = tcpm_set_polarity(port, port->cc2 != TYPEC_CC_OPEN ?
TYPEC_POLARITY_CC2 : TYPEC_POLARITY_CC1);
if (ret < 0)
@ -3146,7 +3173,11 @@ static void run_state_machine(struct tcpm_port *port)
ret = tcpm_snk_attach(port);
if (ret < 0)
tcpm_set_state(port, SNK_UNATTACHED, 0);
else
else if (port->port_type == TYPEC_PORT_SRC &&
port->typec_caps.data == TYPEC_PORT_DRD) {
tcpm_typec_connect(port);
tcpm_log(port, "Keep at SNK_ATTACHED for USB data.");
} else
tcpm_set_state(port, SNK_STARTUP, 0);
break;
case SNK_STARTUP:
@ -4744,7 +4775,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
mutex_init(&port->lock);
mutex_init(&port->swap_lock);
port->wq = create_singlethread_workqueue(dev_name(dev));
port->wq = create_freezable_workqueue(dev_name(dev));
if (!port->wq)
return ERR_PTR(-ENOMEM);
INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work);
@ -4820,6 +4851,19 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
}
}
port->edev = devm_extcon_dev_allocate(port->dev, tcpm_extcon_cable);
if (IS_ERR(port->edev)) {
dev_err(port->dev, "failed to allocate extcon dev.\n");
err = -ENOMEM;
goto out_role_sw_put;
}
err = devm_extcon_dev_register(port->dev, port->edev);
if (err) {
dev_err(port->dev, "failed to register extcon dev.\n");
goto out_role_sw_put;
}
mutex_lock(&port->lock);
tcpm_init(port);
mutex_unlock(&port->lock);
@ -4840,6 +4884,9 @@ void tcpm_unregister_port(struct tcpm_port *port)
{
int i;
cancel_delayed_work_sync(&port->state_machine);
cancel_delayed_work_sync(&port->vdm_state_machine);
tcpm_reset_port(port);
for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
typec_unregister_altmode(port->port_altmode[i]);

View File

@ -62,11 +62,15 @@ struct ci_hdrc_platform_data {
#define CI_HDRC_REQUIRES_ALIGNED_DMA BIT(13)
#define CI_HDRC_IMX_IS_HSIC BIT(14)
#define CI_HDRC_PMQOS BIT(15)
/* PHY enter low power mode when bus suspend */
#define CI_HDRC_HOST_SUSP_PHY_LPM BIT(16)
enum usb_dr_mode dr_mode;
#define CI_HDRC_CONTROLLER_RESET_EVENT 0
#define CI_HDRC_CONTROLLER_STOPPED_EVENT 1
#define CI_HDRC_IMX_HSIC_ACTIVE_EVENT 2
#define CI_HDRC_IMX_HSIC_SUSPEND_EVENT 3
#define CI_HDRC_CONTROLLER_VBUS_EVENT 4
#define CI_HDRC_NOTIFY_RET_DEFER_EVENT 5
int (*notify_event) (struct ci_hdrc *ci, unsigned event);
struct regulator *reg_vbus;
struct usb_otg_caps ci_otg_caps;
@ -99,4 +103,6 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
/* Remove ci hdrc device */
void ci_hdrc_remove_device(struct platform_device *pdev);
/* Get current available role */
enum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev);
#endif

View File

@ -409,7 +409,10 @@ struct hc_driver {
int (*find_raw_port_number)(struct usb_hcd *, int);
/* Call for power on/off the port if necessary */
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
/* Call for SINGLE_STEP_SET_FEATURE Test for USB2 EH certification */
#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
int (*submit_single_step_set_feature)(struct usb_hcd *,
struct urb *, int);
};
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@ -474,6 +477,14 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr,
struct platform_device;
extern void usb_hcd_platform_shutdown(struct platform_device *dev);
#ifdef CONFIG_USB_HCD_TEST_MODE
extern int ehset_single_step_set_feature(struct usb_hcd *hcd, int port);
#else
static inline int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
return 0;
}
#endif /* CONFIG_USB_HCD_TEST_MODE */
#ifdef CONFIG_USB_PCI
struct pci_dev;

View File

@ -54,6 +54,7 @@ enum otg_fsm_timer {
A_WAIT_ENUM,
B_DATA_PLS,
B_SSEND_SRP,
A_DP_END,
NUM_OTG_FSM_TIMERS,
};

View File

@ -63,6 +63,13 @@ enum usb_otg_state {
OTG_STATE_A_VBUS_ERR,
};
/* The usb role of phy to be working with */
enum usb_current_mode {
CUR_USB_MODE_NONE,
CUR_USB_MODE_HOST,
CUR_USB_MODE_DEVICE,
};
struct usb_phy;
struct usb_otg;
@ -155,6 +162,15 @@ struct usb_phy {
* manually detect the charger type.
*/
enum usb_charger_type (*charger_detect)(struct usb_phy *x);
int (*notify_suspend)(struct usb_phy *x,
enum usb_device_speed speed);
int (*notify_resume)(struct usb_phy *x,
enum usb_device_speed speed);
int (*set_mode)(struct usb_phy *x,
enum usb_current_mode mode);
};
/* for board-specific init logic */
@ -213,6 +229,15 @@ usb_phy_vbus_off(struct usb_phy *x)
return x->set_vbus(x, false);
}
static inline int
usb_phy_set_mode(struct usb_phy *x, enum usb_current_mode mode)
{
if (!x || !x->set_mode)
return 0;
return x->set_mode(x, mode);
}
/* for usb host and peripheral controller drivers */
#if IS_ENABLED(CONFIG_USB_PHY)
extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
@ -334,6 +359,24 @@ usb_phy_notify_disconnect(struct usb_phy *x, enum usb_device_speed speed)
return 0;
}
static inline int usb_phy_notify_suspend
(struct usb_phy *x, enum usb_device_speed speed)
{
if (x && x->notify_suspend)
return x->notify_suspend(x, speed);
else
return 0;
}
static inline int usb_phy_notify_resume
(struct usb_phy *x, enum usb_device_speed speed)
{
if (x && x->notify_resume)
return x->notify_resume(x, speed);
else
return 0;
}
/* notifiers */
static inline int
usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)