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:
commit
210428cc37
|
@ -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.
|
|
@ -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
|
|
@ -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";
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
@ -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 */
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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(®s->ep_traddr));
|
||||
|
||||
/* TRB should be prepared before starting transfer. */
|
||||
/* TRB should be prepared before starting transfer */
|
||||
writel(EP_CMD_DRDY, ®s->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),
|
||||
®s->ep_cfg);
|
||||
|
||||
|
|
|
@ -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
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 | " : "",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
* for safe
|
||||
*/
|
||||
|
||||
#define TA_DP_END (200)
|
||||
|
||||
/*
|
||||
* B-device timing constants
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
* VCO’s 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>");
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -54,6 +54,7 @@ enum otg_fsm_timer {
|
|||
A_WAIT_ENUM,
|
||||
B_DATA_PLS,
|
||||
B_SSEND_SRP,
|
||||
A_DP_END,
|
||||
|
||||
NUM_OTG_FSM_TIMERS,
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue