USB patches for 4.7-rc1
Here's the big pull request for USB and PHY drivers for 4.7-rc1 Full details in the shortlog, but it's the normal major gadget driver updates, phy updates, new usbip code, as well as a bit of lots of other stuff. All have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlc/0P8ACgkQMUfUDdst+ykkFQCg0kJlxIiCU1FYBZYriqo4vX3F 9N8AoM/8nO8Y6vMpF2LWnamafYgqscTE =ZuCh -----END PGP SIGNATURE----- Merge tag 'usb-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here's the big pull request for USB and PHY drivers for 4.7-rc1 Full details in the shortlog, but it's the normal major gadget driver updates, phy updates, new usbip code, as well as a bit of lots of other stuff. All have been in linux-next with no reported issues" * tag 'usb-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (164 commits) USB: serial: ti_usb_3410_5052: add MOXA UPORT 11x0 support USB: serial: fix minor-number allocation USB: serial: quatech2: fix use-after-free in probe error path USB: serial: mxuport: fix use-after-free in probe error path USB: serial: keyspan: fix debug and error messages USB: serial: keyspan: fix URB unlink USB: serial: keyspan: fix use-after-free in probe error path USB: serial: io_edgeport: fix memory leaks in probe error path USB: serial: io_edgeport: fix memory leaks in attach error path usb: Remove unnecessary space before operator ','. usb: Remove unnecessary space before open square bracket. USB: FHCI: avoid redundant condition usb: host: xhci-rcar: Avoid long wait in xhci_reset() usb/host/fotg210: remove dead code in create_sysfs_files usb: wusbcore: Do not initialise statics to 0. usb: wusbcore: Remove space before ',' and '(' . USB: serial: cp210x: clean up CRTSCTS flag code USB: serial: cp210x: get rid of magic numbers in CRTSCTS flag code USB: serial: cp210x: fix hardware flow-control disable USB: serial: option: add even more ZTE device ids ...
This commit is contained in:
commit
19e36ad292
|
@ -0,0 +1,35 @@
|
|||
What: /sys/devices/platform/usbip-vudc.%d/dev_desc
|
||||
Date: April 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Krzysztof Opasiak <k.opasiak@samsung.com>
|
||||
Description:
|
||||
This file allows to read device descriptor of
|
||||
gadget driver which is currently bound to this
|
||||
controller. It is possible to read this file
|
||||
only if gadget driver is bound, otherwise error
|
||||
is returned.
|
||||
|
||||
What: /sys/devices/platform/usbip-vudc.%d/usbip_status
|
||||
Date: April 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Krzysztof Opasiak <k.opasiak@samsung.com>
|
||||
Description:
|
||||
Current status of the device.
|
||||
Allowed values:
|
||||
1 - Device is available and can be exported
|
||||
2 - Device is currently exported
|
||||
3 - Fatal error occurred during communication
|
||||
with peer
|
||||
|
||||
What: /sys/devices/platform/usbip-vudc.%d/usbip_sockfd
|
||||
Date: April 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Krzysztof Opasiak <k.opasiak@samsung.com>
|
||||
Description:
|
||||
This file allows to export usb device to
|
||||
connection peer. It is done by writing to this
|
||||
file socket fd (as a string for example "8")
|
||||
associated with a connection to remote peer who
|
||||
would like to use this device. It is possible to
|
||||
close the connection by writing -1 instead of
|
||||
socked fd.
|
|
@ -0,0 +1,21 @@
|
|||
Driver for Broadcom Northstar USB 2.0 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: brcm,ns-usb2-phy
|
||||
- reg: iomem address range of DMU (Device Management Unit)
|
||||
- reg-names: "dmu", the only needed & supported reg right now
|
||||
- clocks: USB PHY reference clock
|
||||
- clock-names: "phy-ref-clk", the only needed & supported clock right now
|
||||
|
||||
To initialize USB 2.0 PHY driver needs to setup PLL correctly. To do this it
|
||||
requires passing phandle to the USB PHY reference clock.
|
||||
|
||||
Example:
|
||||
usb2-phy {
|
||||
compatible = "brcm,ns-usb2-phy";
|
||||
reg = <0x1800c000 0x1000>;
|
||||
reg-names = "dmu";
|
||||
#phy-cells = <0>;
|
||||
clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
|
||||
clock-names = "phy-ref-clk";
|
||||
};
|
|
@ -1,14 +1,17 @@
|
|||
* Broadcom SATA3 PHY for STB
|
||||
* Broadcom SATA3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one or more of
|
||||
"brcm,bcm7425-sata-phy"
|
||||
"brcm,bcm7445-sata-phy"
|
||||
"brcm,iproc-ns2-sata-phy"
|
||||
"brcm,phy-sata3"
|
||||
- address-cells: should be 1
|
||||
- size-cells: should be 0
|
||||
- reg: register range for the PHY PCB interface
|
||||
- reg-names: should be "phy"
|
||||
- reg: register ranges for the PHY PCB interface
|
||||
- reg-names: should be "phy" and "phy-ctrl"
|
||||
The "phy-ctrl" registers are only required for
|
||||
"brcm,iproc-ns2-sata-phy".
|
||||
|
||||
Sub-nodes:
|
||||
Each port's PHY should be represented as a sub-node.
|
||||
|
@ -16,12 +19,12 @@ Sub-nodes:
|
|||
Sub-nodes required properties:
|
||||
- reg: the PHY number
|
||||
- phy-cells: generic PHY binding; must be 0
|
||||
Optional:
|
||||
- brcm,enable-ssc: use spread spectrum clocking (SSC) on this port
|
||||
|
||||
Sub-nodes optional properties:
|
||||
- brcm,enable-ssc: use spread spectrum clocking (SSC) on this port
|
||||
This property is not applicable for "brcm,iproc-ns2-sata-phy".
|
||||
|
||||
Example:
|
||||
|
||||
sata-phy@f0458100 {
|
||||
compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3";
|
||||
reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>;
|
|
@ -4,7 +4,9 @@ mt65xx USB3.0 PHY binding
|
|||
This binding describes a usb3.0 phy for mt65xx platforms of Medaitek SoC.
|
||||
|
||||
Required properties (controller (parent) node):
|
||||
- compatible : should be "mediatek,mt8173-u3phy"
|
||||
- compatible : should be one of
|
||||
"mediatek,mt2701-u3phy"
|
||||
"mediatek,mt8173-u3phy"
|
||||
- reg : offset and length of register for phy, exclude port's
|
||||
register.
|
||||
- clocks : a list of phandle + clock-specifier pairs, one for each
|
||||
|
|
|
@ -7,6 +7,12 @@ Required properties:
|
|||
- compatible: "renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC.
|
||||
"renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC.
|
||||
"renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC.
|
||||
"renesas,rcar-gen2-usb-phy" for a generic R-Car Gen2 compatible device.
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first
|
||||
followed by the generic version.
|
||||
|
||||
- reg: offset and length of the register block.
|
||||
- #address-cells: number of address cells for the USB channel subnodes, must
|
||||
be <1>.
|
||||
|
@ -34,7 +40,7 @@ the USB channel; see the selector meanings below:
|
|||
Example (Lager board):
|
||||
|
||||
usb-phy@e6590100 {
|
||||
compatible = "renesas,usb-phy-r8a7790";
|
||||
compatible = "renesas,usb-phy-r8a7790", "renesas,rcar-gen2-usb-phy";
|
||||
reg = <0 0xe6590100 0 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
@ -6,6 +6,12 @@ This file provides information on what the device node for the R-Car generation
|
|||
Required properties:
|
||||
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
|
||||
SoC.
|
||||
"renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first
|
||||
followed by the generic version.
|
||||
|
||||
- reg: offset and length of the partial USB 2.0 Host register block.
|
||||
- clocks: clock phandle and specifier pair(s).
|
||||
- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
|
||||
|
@ -15,18 +21,20 @@ To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
|
|||
combined, the device tree node should set interrupt properties to use the
|
||||
channel as USB OTG:
|
||||
- interrupts: interrupt specifier for the PHY.
|
||||
- vbus-supply: Phandle to a regulator that provides power to the VBUS. This
|
||||
regulator will be managed during the PHY power on/off sequence.
|
||||
|
||||
Example (R-Car H3):
|
||||
|
||||
usb-phy@ee080200 {
|
||||
compatible = "renesas,usb2-phy-r8a7795";
|
||||
compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
|
||||
reg = <0 0xee080200 0 0x700>;
|
||||
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
|
||||
};
|
||||
|
||||
usb-phy@ee0a0200 {
|
||||
compatible = "renesas,usb2-phy-r8a7795";
|
||||
compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
|
||||
reg = <0 0xee0a0200 0 0x700>;
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
|
||||
};
|
||||
|
|
|
@ -2,9 +2,20 @@ Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY
|
|||
-------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "samsung,s5pv210-mipi-video-phy";
|
||||
- compatible : should be one of the listed compatibles:
|
||||
- "samsung,s5pv210-mipi-video-phy"
|
||||
- "samsung,exynos5420-mipi-video-phy"
|
||||
- "samsung,exynos5433-mipi-video-phy"
|
||||
- #phy-cells : from the generic phy bindings, must be 1;
|
||||
- syscon - phandle to the PMU system controller;
|
||||
|
||||
In case of s5pv210 and exynos5420 compatible PHYs:
|
||||
- syscon - phandle to the PMU system controller
|
||||
|
||||
In case of exynos5433 compatible PHY:
|
||||
- samsung,pmu-syscon - phandle to the PMU system controller
|
||||
- samsung,disp-sysreg - phandle to the DISP system registers controller
|
||||
- samsung,cam0-sysreg - phandle to the CAM0 system registers controller
|
||||
- samsung,cam1-sysreg - phandle to the CAM1 system registers controller
|
||||
|
||||
For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
|
||||
the PHY specifier identifies the PHY and its meaning is as follows:
|
||||
|
@ -12,6 +23,9 @@ the PHY specifier identifies the PHY and its meaning is as follows:
|
|||
1 - MIPI DSIM 0,
|
||||
2 - MIPI CSIS 1,
|
||||
3 - MIPI DSIM 1.
|
||||
"samsung,exynos5420-mipi-video-phy" and "samsung,exynos5433-mipi-video-phy"
|
||||
supports additional fifth PHY:
|
||||
4 - MIPI CSIS 2.
|
||||
|
||||
Samsung EXYNOS SoC series Display Port PHY
|
||||
-------------------------------------------------
|
||||
|
|
|
@ -14,7 +14,6 @@ Optional properties:
|
|||
the second element is expected to be a handle to the USB3/SS PHY
|
||||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings
|
||||
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
|
||||
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
|
||||
Only really useful for FPGA builds.
|
||||
|
@ -38,6 +37,8 @@ Optional properties:
|
|||
- snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy.
|
||||
- snps,dis_enblslpm_quirk: when set clears the enblslpm in GUSB2PHYCFG,
|
||||
disabling the suspend signal to the PHY.
|
||||
- snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection
|
||||
in PHY P3 power state.
|
||||
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
|
||||
utmi_l1_suspend_n, false when asserts utmi_sleep_n
|
||||
- snps,hird-threshold: HIRD threshold
|
||||
|
@ -47,6 +48,8 @@ Optional properties:
|
|||
register for post-silicon frame length adjustment when the
|
||||
fladj_30mhz_sdbnd signal is invalid or incorrect.
|
||||
|
||||
- <DEPRECATED> tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
|
||||
This is usually a subnode to DWC3 glue to which it is connected.
|
||||
|
||||
dwc3@4a030000 {
|
||||
|
@ -54,5 +57,4 @@ dwc3@4a030000 {
|
|||
reg = <0x4a030000 0xcfff>;
|
||||
interrupts = <0 92 4>
|
||||
usb-phy = <&usb2_phy>, <&usb3,phy>;
|
||||
tx-fifo-resize;
|
||||
};
|
||||
|
|
|
@ -59,7 +59,6 @@ Example device nodes:
|
|||
interrupts = <0 205 0x4>;
|
||||
phys = <&hs_phy>, <&ss_phy>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
tx-fifo-resize;
|
||||
dr_mode = "host";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,14 +3,17 @@
|
|||
To show how to demo OTG HNP and SRP functions via sys input files
|
||||
with 2 Freescale i.MX6Q sabre SD boards.
|
||||
|
||||
1.1 How to enable OTG FSM in menuconfig
|
||||
1.1 How to enable OTG FSM
|
||||
---------------------------------------
|
||||
Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
|
||||
If you want to check some internal variables for otg fsm,
|
||||
mount debugfs, there are 2 files which can show otg fsm
|
||||
variables and some controller registers value:
|
||||
1.1.1 Select CONFIG_USB_OTG_FSM in menuconfig, rebuild kernel
|
||||
Image and modules. If you want to check some internal
|
||||
variables for otg fsm, mount debugfs, there are 2 files
|
||||
which can show otg fsm variables and some controller registers value:
|
||||
cat /sys/kernel/debug/ci_hdrc.0/otg
|
||||
cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
1.1.2 Add below entries in your dts file for your controller node
|
||||
otg-rev = <0x0200>;
|
||||
adp-disable;
|
||||
|
||||
1.2 Test operations
|
||||
-------------------
|
||||
|
|
|
@ -15,6 +15,15 @@ config GENERIC_PHY
|
|||
phy users can obtain reference to the PHY. All the users of this
|
||||
framework should select this config.
|
||||
|
||||
config PHY_BCM_NS_USB2
|
||||
tristate "Broadcom Northstar USB 2.0 PHY Driver"
|
||||
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||
depends on HAS_IOMEM && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support Broadcom USB 2.0 PHY connected to the USB
|
||||
controller on Northstar family.
|
||||
|
||||
config PHY_BERLIN_USB
|
||||
tristate "Marvell Berlin USB PHY Driver"
|
||||
depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
|
||||
|
@ -113,14 +122,15 @@ config PHY_MIPHY365X
|
|||
|
||||
config PHY_RCAR_GEN2
|
||||
tristate "Renesas R-Car generation 2 USB PHY driver"
|
||||
depends on ARCH_SHMOBILE
|
||||
depends on ARCH_RENESAS
|
||||
depends on GENERIC_PHY
|
||||
help
|
||||
Support for USB PHY found on Renesas R-Car generation 2 SoCs.
|
||||
|
||||
config PHY_RCAR_GEN3_USB2
|
||||
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
|
||||
depends on OF && ARCH_SHMOBILE
|
||||
depends on ARCH_RENESAS
|
||||
depends on EXTCON
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
|
||||
|
@ -218,9 +228,8 @@ config PHY_MT65XX_USB3
|
|||
depends on ARCH_MEDIATEK && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Say 'Y' here to add support for Mediatek USB3.0 PHY driver
|
||||
for mt65xx SoCs. it supports two usb2.0 ports and
|
||||
one usb3.0 port.
|
||||
Say 'Y' here to add support for Mediatek USB3.0 PHY driver,
|
||||
it supports multiple usb2.0 and usb3.0 ports.
|
||||
|
||||
config PHY_HI6220_USB
|
||||
tristate "hi6220 USB PHY support"
|
||||
|
@ -250,7 +259,8 @@ config PHY_SUN9I_USB
|
|||
tristate "Allwinner sun9i SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
depends on RESET_CONTROLLER
|
||||
depends on USB_COMMON
|
||||
depends on USB_SUPPORT
|
||||
select USB_COMMON
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the transceiver that is part of Allwinner
|
||||
|
@ -403,14 +413,15 @@ config PHY_TUSB1210
|
|||
help
|
||||
Support for TI TUSB1210 USB ULPI PHY.
|
||||
|
||||
config PHY_BRCMSTB_SATA
|
||||
tristate "Broadcom STB SATA PHY driver"
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
config PHY_BRCM_SATA
|
||||
tristate "Broadcom SATA PHY driver"
|
||||
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
default ARCH_BCM_IPROC
|
||||
help
|
||||
Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs.
|
||||
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
|
||||
Enable this to support the Broadcom SATA PHY.
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_CYGNUS_PCIE
|
||||
tristate "Broadcom Cygnus PCIe PHY driver"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
|
||||
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
|
||||
|
@ -49,7 +50,7 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
|
|||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
|
||||
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||
obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
|
||||
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
|
||||
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
|
||||
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
|
||||
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Broadcom Northstar USB 2.0 PHY Driver
|
||||
*
|
||||
* Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.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 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct bcm_ns_usb2 {
|
||||
struct device *dev;
|
||||
struct clk *ref_clk;
|
||||
struct phy *phy;
|
||||
void __iomem *dmu;
|
||||
};
|
||||
|
||||
static int bcm_ns_usb2_phy_init(struct phy *phy)
|
||||
{
|
||||
struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy);
|
||||
struct device *dev = usb2->dev;
|
||||
void __iomem *dmu = usb2->dmu;
|
||||
u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv;
|
||||
int err = 0;
|
||||
|
||||
err = clk_prepare_enable(usb2->ref_clk);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Failed to prepare ref clock: %d\n", err);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ref_clk_rate = clk_get_rate(usb2->ref_clk);
|
||||
if (!ref_clk_rate) {
|
||||
dev_err(dev, "Failed to get ref clock rate\n");
|
||||
err = -EINVAL;
|
||||
goto err_clk_off;
|
||||
}
|
||||
|
||||
usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL);
|
||||
|
||||
if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) {
|
||||
usb_pll_pdiv = usb2ctl;
|
||||
usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK;
|
||||
usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT;
|
||||
} else {
|
||||
usb_pll_pdiv = 1 << 3;
|
||||
}
|
||||
|
||||
/* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */
|
||||
usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate;
|
||||
|
||||
/* Unlock DMU PLL settings with some magic value */
|
||||
writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY);
|
||||
|
||||
/* Write USB 2.0 PLL control setting */
|
||||
usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK;
|
||||
usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT;
|
||||
writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL);
|
||||
|
||||
/* Lock DMU PLL settings */
|
||||
writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY);
|
||||
|
||||
err_clk_off:
|
||||
clk_disable_unprepare(usb2->ref_clk);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.init = bcm_ns_usb2_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int bcm_ns_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcm_ns_usb2 *usb2;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL);
|
||||
if (!usb2)
|
||||
return -ENOMEM;
|
||||
usb2->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu");
|
||||
usb2->dmu = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(usb2->dmu)) {
|
||||
dev_err(dev, "Failed to map DMU regs\n");
|
||||
return PTR_ERR(usb2->dmu);
|
||||
}
|
||||
|
||||
usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk");
|
||||
if (IS_ERR(usb2->ref_clk)) {
|
||||
dev_err(dev, "Clock not defined\n");
|
||||
return PTR_ERR(usb2->ref_clk);
|
||||
}
|
||||
|
||||
usb2->phy = devm_phy_create(dev, NULL, &ops);
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
phy_set_drvdata(usb2->phy, usb2);
|
||||
platform_set_drvdata(pdev, usb2);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm_ns_usb2_id_table[] = {
|
||||
{ .compatible = "brcm,ns-usb2-phy", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table);
|
||||
|
||||
static struct platform_driver bcm_ns_usb2_driver = {
|
||||
.probe = bcm_ns_usb2_probe,
|
||||
.driver = {
|
||||
.name = "bcm_ns_usb2",
|
||||
.of_match_table = bcm_ns_usb2_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(bcm_ns_usb2_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* Broadcom SATA3 AHCI Controller PHY Driver
|
||||
*
|
||||
* Copyright (C) 2016 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define SATA_PCB_BANK_OFFSET 0x23c
|
||||
#define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4)
|
||||
|
||||
#define MAX_PORTS 2
|
||||
|
||||
/* Register offset between PHYs in PCB space */
|
||||
#define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000
|
||||
|
||||
/* The older SATA PHY registers duplicated per port registers within the map,
|
||||
* rather than having a separate map per port.
|
||||
*/
|
||||
#define SATA_PCB_REG_40NM_SPACE_SIZE 0x10
|
||||
|
||||
/* Register offset between PHYs in PHY control space */
|
||||
#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8
|
||||
|
||||
enum brcm_sata_phy_version {
|
||||
BRCM_SATA_PHY_STB_28NM,
|
||||
BRCM_SATA_PHY_STB_40NM,
|
||||
BRCM_SATA_PHY_IPROC_NS2,
|
||||
};
|
||||
|
||||
struct brcm_sata_port {
|
||||
int portnum;
|
||||
struct phy *phy;
|
||||
struct brcm_sata_phy *phy_priv;
|
||||
bool ssc_en;
|
||||
};
|
||||
|
||||
struct brcm_sata_phy {
|
||||
struct device *dev;
|
||||
void __iomem *phy_base;
|
||||
void __iomem *ctrl_base;
|
||||
enum brcm_sata_phy_version version;
|
||||
|
||||
struct brcm_sata_port phys[MAX_PORTS];
|
||||
};
|
||||
|
||||
enum sata_phy_regs {
|
||||
BLOCK0_REG_BANK = 0x000,
|
||||
BLOCK0_XGXSSTATUS = 0x81,
|
||||
BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12),
|
||||
BLOCK0_SPARE = 0x8d,
|
||||
BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3,
|
||||
BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1,
|
||||
|
||||
PLL_REG_BANK_0 = 0x050,
|
||||
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
|
||||
|
||||
PLL1_REG_BANK = 0x060,
|
||||
PLL1_ACTRL2 = 0x82,
|
||||
PLL1_ACTRL3 = 0x83,
|
||||
PLL1_ACTRL4 = 0x84,
|
||||
|
||||
OOB_REG_BANK = 0x150,
|
||||
OOB_CTRL1 = 0x80,
|
||||
OOB_CTRL1_BURST_MAX_MASK = 0xf,
|
||||
OOB_CTRL1_BURST_MAX_SHIFT = 12,
|
||||
OOB_CTRL1_BURST_MIN_MASK = 0xf,
|
||||
OOB_CTRL1_BURST_MIN_SHIFT = 8,
|
||||
OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf,
|
||||
OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4,
|
||||
OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf,
|
||||
OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0,
|
||||
OOB_CTRL2 = 0x81,
|
||||
OOB_CTRL2_SEL_ENA_SHIFT = 15,
|
||||
OOB_CTRL2_SEL_ENA_RC_SHIFT = 14,
|
||||
OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f,
|
||||
OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8,
|
||||
OOB_CTRL2_BURST_CNT_MASK = 0x3,
|
||||
OOB_CTRL2_BURST_CNT_SHIFT = 6,
|
||||
OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f,
|
||||
OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0,
|
||||
|
||||
TXPMD_REG_BANK = 0x1a0,
|
||||
TXPMD_CONTROL1 = 0x81,
|
||||
TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0),
|
||||
TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1),
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff,
|
||||
};
|
||||
|
||||
enum sata_phy_ctrl_regs {
|
||||
PHY_CTRL_1 = 0x0,
|
||||
PHY_CTRL_1_RESET = BIT(0),
|
||||
};
|
||||
|
||||
static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port)
|
||||
{
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 size = 0;
|
||||
|
||||
switch (priv->version) {
|
||||
case BRCM_SATA_PHY_STB_28NM:
|
||||
case BRCM_SATA_PHY_IPROC_NS2:
|
||||
size = SATA_PCB_REG_28NM_SPACE_SIZE;
|
||||
break;
|
||||
case BRCM_SATA_PHY_STB_40NM:
|
||||
size = SATA_PCB_REG_40NM_SPACE_SIZE;
|
||||
break;
|
||||
default:
|
||||
dev_err(priv->dev, "invalid phy version\n");
|
||||
break;
|
||||
};
|
||||
|
||||
return priv->phy_base + (port->portnum * size);
|
||||
}
|
||||
|
||||
static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port)
|
||||
{
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 size = 0;
|
||||
|
||||
switch (priv->version) {
|
||||
case BRCM_SATA_PHY_IPROC_NS2:
|
||||
size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE;
|
||||
break;
|
||||
default:
|
||||
dev_err(priv->dev, "invalid phy version\n");
|
||||
break;
|
||||
};
|
||||
|
||||
return priv->ctrl_base + (port->portnum * size);
|
||||
}
|
||||
|
||||
static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank,
|
||||
u32 ofs, u32 msk, u32 value)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
writel(bank, pcb_base + SATA_PCB_BANK_OFFSET);
|
||||
tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs));
|
||||
tmp = (tmp & msk) | value;
|
||||
writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs));
|
||||
}
|
||||
|
||||
static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs)
|
||||
{
|
||||
writel(bank, pcb_base + SATA_PCB_BANK_OFFSET);
|
||||
return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs));
|
||||
}
|
||||
|
||||
/* These defaults were characterized by H/W group */
|
||||
#define STB_FMIN_VAL_DEFAULT 0x3df
|
||||
#define STB_FMAX_VAL_DEFAULT 0x3df
|
||||
#define STB_FMAX_VAL_SSC 0x83
|
||||
|
||||
static int brcm_stb_sata_init(struct brcm_sata_port *port)
|
||||
{
|
||||
void __iomem *base = brcm_sata_pcb_base(port);
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 tmp;
|
||||
|
||||
/* override the TX spread spectrum setting */
|
||||
tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC;
|
||||
brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp);
|
||||
|
||||
/* set fixed min freq */
|
||||
brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2,
|
||||
~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK,
|
||||
STB_FMIN_VAL_DEFAULT);
|
||||
|
||||
/* set fixed max freq depending on SSC config */
|
||||
if (port->ssc_en) {
|
||||
dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum);
|
||||
tmp = STB_FMAX_VAL_SSC;
|
||||
} else {
|
||||
tmp = STB_FMAX_VAL_DEFAULT;
|
||||
}
|
||||
|
||||
brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
|
||||
~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NS2 SATA PLL1 defaults were characterized by H/W group */
|
||||
#define NS2_PLL1_ACTRL2_MAGIC 0x1df8
|
||||
#define NS2_PLL1_ACTRL3_MAGIC 0x2b00
|
||||
#define NS2_PLL1_ACTRL4_MAGIC 0x8824
|
||||
|
||||
static int brcm_ns2_sata_init(struct brcm_sata_port *port)
|
||||
{
|
||||
int try;
|
||||
unsigned int val;
|
||||
void __iomem *base = brcm_sata_pcb_base(port);
|
||||
void __iomem *ctrl_base = brcm_sata_ctrl_base(port);
|
||||
struct device *dev = port->phy_priv->dev;
|
||||
|
||||
/* Configure OOB control */
|
||||
val = 0x0;
|
||||
val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT);
|
||||
val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT);
|
||||
val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT);
|
||||
val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT);
|
||||
brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val);
|
||||
val = 0x0;
|
||||
val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT);
|
||||
val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT);
|
||||
val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT);
|
||||
brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val);
|
||||
|
||||
/* Configure PHY PLL register bank 1 */
|
||||
val = NS2_PLL1_ACTRL2_MAGIC;
|
||||
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val);
|
||||
val = NS2_PLL1_ACTRL3_MAGIC;
|
||||
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val);
|
||||
val = NS2_PLL1_ACTRL4_MAGIC;
|
||||
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val);
|
||||
|
||||
/* Configure PHY BLOCK0 register bank */
|
||||
/* Set oob_clk_sel to refclk/2 */
|
||||
brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE,
|
||||
~BLOCK0_SPARE_OOB_CLK_SEL_MASK,
|
||||
BLOCK0_SPARE_OOB_CLK_SEL_REFBY2);
|
||||
|
||||
/* Strobe PHY reset using PHY control register */
|
||||
writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1);
|
||||
mdelay(1);
|
||||
writel(0x0, ctrl_base + PHY_CTRL_1);
|
||||
mdelay(1);
|
||||
|
||||
/* Wait for PHY PLL lock by polling pll_lock bit */
|
||||
try = 50;
|
||||
while (try) {
|
||||
val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK,
|
||||
BLOCK0_XGXSSTATUS);
|
||||
if (val & BLOCK0_XGXSSTATUS_PLL_LOCK)
|
||||
break;
|
||||
msleep(20);
|
||||
try--;
|
||||
}
|
||||
if (!try) {
|
||||
/* PLL did not lock; give up */
|
||||
dev_err(dev, "port%d PLL did not lock\n", port->portnum);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "port%d initialized\n", port->portnum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcm_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
int rc;
|
||||
struct brcm_sata_port *port = phy_get_drvdata(phy);
|
||||
|
||||
switch (port->phy_priv->version) {
|
||||
case BRCM_SATA_PHY_STB_28NM:
|
||||
case BRCM_SATA_PHY_STB_40NM:
|
||||
rc = brcm_stb_sata_init(port);
|
||||
break;
|
||||
case BRCM_SATA_PHY_IPROC_NS2:
|
||||
rc = brcm_ns2_sata_init(port);
|
||||
break;
|
||||
default:
|
||||
rc = -ENODEV;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops phy_ops = {
|
||||
.init = brcm_sata_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_sata_phy_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7445-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_STB_28NM },
|
||||
{ .compatible = "brcm,bcm7425-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_STB_40NM },
|
||||
{ .compatible = "brcm,iproc-ns2-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_IPROC_NS2 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
|
||||
|
||||
static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
const struct of_device_id *of_id;
|
||||
struct brcm_sata_phy *priv;
|
||||
struct resource *res;
|
||||
struct phy_provider *provider;
|
||||
int ret, count = 0;
|
||||
|
||||
if (of_get_child_count(dn) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
|
||||
priv->phy_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->phy_base))
|
||||
return PTR_ERR(priv->phy_base);
|
||||
|
||||
of_id = of_match_node(brcm_sata_phy_of_match, dn);
|
||||
if (of_id)
|
||||
priv->version = (enum brcm_sata_phy_version)of_id->data;
|
||||
else
|
||||
priv->version = BRCM_SATA_PHY_STB_28NM;
|
||||
|
||||
if (priv->version == BRCM_SATA_PHY_IPROC_NS2) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"phy-ctrl");
|
||||
priv->ctrl_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->ctrl_base))
|
||||
return PTR_ERR(priv->ctrl_base);
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
unsigned int id;
|
||||
struct brcm_sata_port *port;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &id)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
if (id >= MAX_PORTS) {
|
||||
dev_err(dev, "invalid reg: %u\n", id);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
if (priv->phys[id].phy) {
|
||||
dev_err(dev, "already registered port %u\n", id);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
port = &priv->phys[id];
|
||||
port->portnum = id;
|
||||
port->phy_priv = priv;
|
||||
port->phy = devm_phy_create(dev, child, &phy_ops);
|
||||
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
|
||||
if (IS_ERR(port->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
ret = PTR_ERR(port->phy);
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
phy_set_drvdata(port->phy, port);
|
||||
count++;
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(provider)) {
|
||||
dev_err(dev, "could not register PHY provider\n");
|
||||
return PTR_ERR(provider);
|
||||
}
|
||||
|
||||
dev_info(dev, "registered %d port(s)\n", count);
|
||||
|
||||
return 0;
|
||||
put_child:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver brcm_sata_phy_driver = {
|
||||
.probe = brcm_sata_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = brcm_sata_phy_of_match,
|
||||
.name = "brcm-sata-phy",
|
||||
}
|
||||
};
|
||||
module_platform_driver(brcm_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom SATA PHY driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Marc Carino");
|
||||
MODULE_AUTHOR("Brian Norris");
|
||||
MODULE_ALIAS("platform:phy-brcm-sata");
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* Broadcom SATA3 AHCI Controller PHY Driver
|
||||
*
|
||||
* Copyright © 2009-2015 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define SATA_MDIO_BANK_OFFSET 0x23c
|
||||
#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4)
|
||||
|
||||
#define MAX_PORTS 2
|
||||
|
||||
/* Register offset between PHYs in PCB space */
|
||||
#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000
|
||||
|
||||
/* The older SATA PHY registers duplicated per port registers within the map,
|
||||
* rather than having a separate map per port.
|
||||
*/
|
||||
#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10
|
||||
|
||||
enum brcm_sata_phy_version {
|
||||
BRCM_SATA_PHY_28NM,
|
||||
BRCM_SATA_PHY_40NM,
|
||||
};
|
||||
|
||||
struct brcm_sata_port {
|
||||
int portnum;
|
||||
struct phy *phy;
|
||||
struct brcm_sata_phy *phy_priv;
|
||||
bool ssc_en;
|
||||
};
|
||||
|
||||
struct brcm_sata_phy {
|
||||
struct device *dev;
|
||||
void __iomem *phy_base;
|
||||
enum brcm_sata_phy_version version;
|
||||
|
||||
struct brcm_sata_port phys[MAX_PORTS];
|
||||
};
|
||||
|
||||
enum sata_mdio_phy_regs {
|
||||
PLL_REG_BANK_0 = 0x50,
|
||||
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
|
||||
|
||||
TXPMD_REG_BANK = 0x1a0,
|
||||
TXPMD_CONTROL1 = 0x81,
|
||||
TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0),
|
||||
TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1),
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff,
|
||||
};
|
||||
|
||||
static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
|
||||
{
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 offset = 0;
|
||||
|
||||
if (priv->version == BRCM_SATA_PHY_28NM)
|
||||
offset = SATA_MDIO_REG_28NM_SPACE_SIZE;
|
||||
else if (priv->version == BRCM_SATA_PHY_40NM)
|
||||
offset = SATA_MDIO_REG_40NM_SPACE_SIZE;
|
||||
else
|
||||
dev_err(priv->dev, "invalid phy version\n");
|
||||
|
||||
return priv->phy_base + (port->portnum * offset);
|
||||
}
|
||||
|
||||
static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
|
||||
u32 msk, u32 value)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
writel(bank, addr + SATA_MDIO_BANK_OFFSET);
|
||||
tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs));
|
||||
tmp = (tmp & msk) | value;
|
||||
writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs));
|
||||
}
|
||||
|
||||
/* These defaults were characterized by H/W group */
|
||||
#define FMIN_VAL_DEFAULT 0x3df
|
||||
#define FMAX_VAL_DEFAULT 0x3df
|
||||
#define FMAX_VAL_SSC 0x83
|
||||
|
||||
static void brcm_sata_cfg_ssc(struct brcm_sata_port *port)
|
||||
{
|
||||
void __iomem *base = brcm_sata_phy_base(port);
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 tmp;
|
||||
|
||||
/* override the TX spread spectrum setting */
|
||||
tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC;
|
||||
brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp);
|
||||
|
||||
/* set fixed min freq */
|
||||
brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2,
|
||||
~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK,
|
||||
FMIN_VAL_DEFAULT);
|
||||
|
||||
/* set fixed max freq depending on SSC config */
|
||||
if (port->ssc_en) {
|
||||
dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum);
|
||||
tmp = FMAX_VAL_SSC;
|
||||
} else {
|
||||
tmp = FMAX_VAL_DEFAULT;
|
||||
}
|
||||
|
||||
brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
|
||||
~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp);
|
||||
}
|
||||
|
||||
static int brcm_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
struct brcm_sata_port *port = phy_get_drvdata(phy);
|
||||
|
||||
brcm_sata_cfg_ssc(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops phy_ops = {
|
||||
.init = brcm_sata_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_sata_phy_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7445-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_28NM },
|
||||
{ .compatible = "brcm,bcm7425-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_40NM },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
|
||||
|
||||
static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
const struct of_device_id *of_id;
|
||||
struct brcm_sata_phy *priv;
|
||||
struct resource *res;
|
||||
struct phy_provider *provider;
|
||||
int ret, count = 0;
|
||||
|
||||
if (of_get_child_count(dn) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
|
||||
priv->phy_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->phy_base))
|
||||
return PTR_ERR(priv->phy_base);
|
||||
|
||||
of_id = of_match_node(brcm_sata_phy_of_match, dn);
|
||||
if (of_id)
|
||||
priv->version = (enum brcm_sata_phy_version)of_id->data;
|
||||
else
|
||||
priv->version = BRCM_SATA_PHY_28NM;
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
unsigned int id;
|
||||
struct brcm_sata_port *port;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &id)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
if (id >= MAX_PORTS) {
|
||||
dev_err(dev, "invalid reg: %u\n", id);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
if (priv->phys[id].phy) {
|
||||
dev_err(dev, "already registered port %u\n", id);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
port = &priv->phys[id];
|
||||
port->portnum = id;
|
||||
port->phy_priv = priv;
|
||||
port->phy = devm_phy_create(dev, child, &phy_ops);
|
||||
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
|
||||
if (IS_ERR(port->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
ret = PTR_ERR(port->phy);
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
phy_set_drvdata(port->phy, port);
|
||||
count++;
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(provider)) {
|
||||
dev_err(dev, "could not register PHY provider\n");
|
||||
return PTR_ERR(provider);
|
||||
}
|
||||
|
||||
dev_info(dev, "registered %d port(s)\n", count);
|
||||
|
||||
return 0;
|
||||
put_child:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver brcm_sata_phy_driver = {
|
||||
.probe = brcm_sata_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = brcm_sata_phy_of_match,
|
||||
.name = "brcmstb-sata-phy",
|
||||
}
|
||||
};
|
||||
module_platform_driver(brcm_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom STB SATA PHY driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Marc Carino");
|
||||
MODULE_AUTHOR("Brian Norris");
|
||||
MODULE_ALIAS("platform:phy-brcmstb-sata");
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Copyright (C) 2013,2016 Samsung Electronics Co., Ltd.
|
||||
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -13,96 +13,276 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon/exynos4-pmu.h>
|
||||
#include <linux/mfd/syscon/exynos5-pmu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */
|
||||
#define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4)
|
||||
|
||||
enum exynos_mipi_phy_id {
|
||||
EXYNOS_MIPI_PHY_ID_NONE = -1,
|
||||
EXYNOS_MIPI_PHY_ID_CSIS0,
|
||||
EXYNOS_MIPI_PHY_ID_DSIM0,
|
||||
EXYNOS_MIPI_PHY_ID_CSIS1,
|
||||
EXYNOS_MIPI_PHY_ID_DSIM1,
|
||||
EXYNOS_MIPI_PHY_ID_CSIS2,
|
||||
EXYNOS_MIPI_PHYS_NUM
|
||||
};
|
||||
|
||||
#define is_mipi_dsim_phy_id(id) \
|
||||
((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
|
||||
enum exynos_mipi_phy_regmap_id {
|
||||
EXYNOS_MIPI_REGMAP_PMU,
|
||||
EXYNOS_MIPI_REGMAP_DISP,
|
||||
EXYNOS_MIPI_REGMAP_CAM0,
|
||||
EXYNOS_MIPI_REGMAP_CAM1,
|
||||
EXYNOS_MIPI_REGMAPS_NUM
|
||||
};
|
||||
|
||||
struct mipi_phy_device_desc {
|
||||
int num_phys;
|
||||
int num_regmaps;
|
||||
const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM];
|
||||
struct exynos_mipi_phy_desc {
|
||||
enum exynos_mipi_phy_id coupled_phy_id;
|
||||
u32 enable_val;
|
||||
unsigned int enable_reg;
|
||||
enum exynos_mipi_phy_regmap_id enable_map;
|
||||
u32 resetn_val;
|
||||
unsigned int resetn_reg;
|
||||
enum exynos_mipi_phy_regmap_id resetn_map;
|
||||
} phys[EXYNOS_MIPI_PHYS_NUM];
|
||||
};
|
||||
|
||||
static const struct mipi_phy_device_desc s5pv210_mipi_phy = {
|
||||
.num_regmaps = 1,
|
||||
.regmap_names = {"syscon"},
|
||||
.num_phys = 4,
|
||||
.phys = {
|
||||
{
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS0 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
|
||||
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
|
||||
.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_DSIM0 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
|
||||
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
|
||||
.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS1 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
|
||||
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
|
||||
.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_DSIM1 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
|
||||
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
|
||||
.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mipi_phy_device_desc exynos5420_mipi_phy = {
|
||||
.num_regmaps = 1,
|
||||
.regmap_names = {"syscon"},
|
||||
.num_phys = 5,
|
||||
.phys = {
|
||||
{
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS0 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
|
||||
.resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_DSIM0 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS5_MIPI_PHY_M_RESETN,
|
||||
.resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS1 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
|
||||
.resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_DSIM1 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS5_MIPI_PHY_M_RESETN,
|
||||
.resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS2 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5420_MIPI_PHY2_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
|
||||
.resetn_reg = EXYNOS5420_MIPI_PHY2_CONTROL,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C
|
||||
#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014
|
||||
#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020
|
||||
|
||||
static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
|
||||
.num_regmaps = 4,
|
||||
.regmap_names = {
|
||||
"samsung,pmu-syscon",
|
||||
"samsung,disp-sysreg",
|
||||
"samsung,cam0-sysreg",
|
||||
"samsung,cam1-sysreg"
|
||||
},
|
||||
.num_phys = 5,
|
||||
.phys = {
|
||||
{
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS0 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = BIT(0),
|
||||
.resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_CAM0,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_DSIM0 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = BIT(0),
|
||||
.resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_DISP,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS1 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = BIT(1),
|
||||
.resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_CAM0,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_DSIM1 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = BIT(1),
|
||||
.resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_DISP,
|
||||
}, {
|
||||
/* EXYNOS_MIPI_PHY_ID_CSIS2 */
|
||||
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
|
||||
.enable_val = EXYNOS5_PHY_ENABLE,
|
||||
.enable_reg = EXYNOS5433_MIPI_PHY2_CONTROL,
|
||||
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
|
||||
.resetn_val = BIT(0),
|
||||
.resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON,
|
||||
.resetn_map = EXYNOS_MIPI_REGMAP_CAM1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
struct exynos_mipi_video_phy {
|
||||
struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM];
|
||||
int num_phys;
|
||||
struct video_phy_desc {
|
||||
struct phy *phy;
|
||||
unsigned int index;
|
||||
const struct exynos_mipi_phy_desc *data;
|
||||
} phys[EXYNOS_MIPI_PHYS_NUM];
|
||||
spinlock_t slock;
|
||||
void __iomem *regs;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int __set_phy_state(struct exynos_mipi_video_phy *state,
|
||||
enum exynos_mipi_phy_id id, unsigned int on)
|
||||
static inline int __is_running(const struct exynos_mipi_phy_desc *data,
|
||||
struct exynos_mipi_video_phy *state)
|
||||
{
|
||||
const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2);
|
||||
void __iomem *addr;
|
||||
u32 val, reset;
|
||||
u32 val;
|
||||
|
||||
if (is_mipi_dsim_phy_id(id))
|
||||
reset = EXYNOS4_MIPI_PHY_MRESETN;
|
||||
else
|
||||
reset = EXYNOS4_MIPI_PHY_SRESETN;
|
||||
regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val);
|
||||
return val & data->resetn_val;
|
||||
}
|
||||
|
||||
static int __set_phy_state(const struct exynos_mipi_phy_desc *data,
|
||||
struct exynos_mipi_video_phy *state, unsigned int on)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
spin_lock(&state->slock);
|
||||
|
||||
if (!IS_ERR(state->regmap)) {
|
||||
regmap_read(state->regmap, offset, &val);
|
||||
if (on)
|
||||
val |= reset;
|
||||
else
|
||||
val &= ~reset;
|
||||
regmap_write(state->regmap, offset, val);
|
||||
if (on)
|
||||
val |= EXYNOS4_MIPI_PHY_ENABLE;
|
||||
else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
|
||||
val &= ~EXYNOS4_MIPI_PHY_ENABLE;
|
||||
regmap_write(state->regmap, offset, val);
|
||||
} else {
|
||||
addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
|
||||
/* disable in PMU sysreg */
|
||||
if (!on && data->coupled_phy_id >= 0 &&
|
||||
!__is_running(state->phys[data->coupled_phy_id].data, state)) {
|
||||
regmap_read(state->regmaps[data->enable_map], data->enable_reg,
|
||||
&val);
|
||||
val &= ~data->enable_val;
|
||||
regmap_write(state->regmaps[data->enable_map], data->enable_reg,
|
||||
val);
|
||||
}
|
||||
|
||||
val = readl(addr);
|
||||
if (on)
|
||||
val |= reset;
|
||||
else
|
||||
val &= ~reset;
|
||||
writel(val, addr);
|
||||
/* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */
|
||||
if (on)
|
||||
val |= EXYNOS4_MIPI_PHY_ENABLE;
|
||||
else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
|
||||
val &= ~EXYNOS4_MIPI_PHY_ENABLE;
|
||||
/* PHY reset */
|
||||
regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val);
|
||||
val = on ? (val | data->resetn_val) : (val & ~data->resetn_val);
|
||||
regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val);
|
||||
|
||||
writel(val, addr);
|
||||
/* enable in PMU sysreg */
|
||||
if (on) {
|
||||
regmap_read(state->regmaps[data->enable_map], data->enable_reg,
|
||||
&val);
|
||||
val |= data->enable_val;
|
||||
regmap_write(state->regmaps[data->enable_map], data->enable_reg,
|
||||
val);
|
||||
}
|
||||
|
||||
spin_unlock(&state->slock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define to_mipi_video_phy(desc) \
|
||||
container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
|
||||
container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index])
|
||||
|
||||
static int exynos_mipi_video_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
|
||||
struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
|
||||
|
||||
return __set_phy_state(state, phy_desc->index, 1);
|
||||
return __set_phy_state(phy_desc->data, state, 1);
|
||||
}
|
||||
|
||||
static int exynos_mipi_video_phy_power_off(struct phy *phy)
|
||||
|
@ -110,7 +290,7 @@ static int exynos_mipi_video_phy_power_off(struct phy *phy)
|
|||
struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
|
||||
struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
|
||||
|
||||
return __set_phy_state(state, phy_desc->index, 0);
|
||||
return __set_phy_state(phy_desc->data, state, 0);
|
||||
}
|
||||
|
||||
static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
|
||||
|
@ -118,7 +298,7 @@ static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
|
|||
{
|
||||
struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
|
||||
|
||||
if (WARN_ON(args->args[0] >= EXYNOS_MIPI_PHYS_NUM))
|
||||
if (WARN_ON(args->args[0] >= state->num_phys))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return state->phys[args->args[0]].phy;
|
||||
|
@ -132,32 +312,33 @@ static const struct phy_ops exynos_mipi_video_phy_ops = {
|
|||
|
||||
static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct mipi_phy_device_desc *phy_dev;
|
||||
struct exynos_mipi_video_phy *state;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct phy_provider *phy_provider;
|
||||
unsigned int i;
|
||||
|
||||
phy_dev = of_device_get_match_data(dev);
|
||||
if (!phy_dev)
|
||||
return -ENODEV;
|
||||
|
||||
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
|
||||
if (IS_ERR(state->regmap)) {
|
||||
struct resource *res;
|
||||
|
||||
dev_info(dev, "regmap lookup failed: %ld\n",
|
||||
PTR_ERR(state->regmap));
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
state->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(state->regs))
|
||||
return PTR_ERR(state->regs);
|
||||
for (i = 0; i < phy_dev->num_regmaps; i++) {
|
||||
state->regmaps[i] = syscon_regmap_lookup_by_phandle(np,
|
||||
phy_dev->regmap_names[i]);
|
||||
if (IS_ERR(state->regmaps[i]))
|
||||
return PTR_ERR(state->regmaps[i]);
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, state);
|
||||
state->num_phys = phy_dev->num_phys;
|
||||
spin_lock_init(&state->slock);
|
||||
|
||||
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
|
||||
dev_set_drvdata(dev, state);
|
||||
|
||||
for (i = 0; i < state->num_phys; i++) {
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos_mipi_video_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
|
@ -167,6 +348,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
|||
|
||||
state->phys[i].phy = phy;
|
||||
state->phys[i].index = i;
|
||||
state->phys[i].data = &phy_dev->phys[i];
|
||||
phy_set_drvdata(phy, &state->phys[i]);
|
||||
}
|
||||
|
||||
|
@ -177,8 +359,17 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
|
||||
{ .compatible = "samsung,s5pv210-mipi-video-phy" },
|
||||
{ },
|
||||
{
|
||||
.compatible = "samsung,s5pv210-mipi-video-phy",
|
||||
.data = &s5pv210_mipi_phy,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5420-mipi-video-phy",
|
||||
.data = &exynos5420_mipi_phy,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-mipi-video-phy",
|
||||
.data = &exynos5433_mipi_phy,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
|
||||
|
||||
|
|
|
@ -134,6 +134,11 @@
|
|||
#define U3P_SR_COEF_DIVISOR 1000
|
||||
#define U3P_FM_DET_CYCLE_CNT 1024
|
||||
|
||||
struct mt65xx_phy_pdata {
|
||||
/* avoid RX sensitivity level degradation only for mt8173 */
|
||||
bool avoid_rx_sen_degradation;
|
||||
};
|
||||
|
||||
struct mt65xx_phy_instance {
|
||||
struct phy *phy;
|
||||
void __iomem *port_base;
|
||||
|
@ -145,6 +150,7 @@ struct mt65xx_u3phy {
|
|||
struct device *dev;
|
||||
void __iomem *sif_base; /* include sif2, but exclude port's */
|
||||
struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
|
||||
const struct mt65xx_phy_pdata *pdata;
|
||||
struct mt65xx_phy_instance **phys;
|
||||
int nphys;
|
||||
};
|
||||
|
@ -241,7 +247,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
|
|||
tmp = readl(port_base + U3P_U2PHYACR4);
|
||||
tmp &= ~P2C_U2_GPIO_CTR_MSK;
|
||||
writel(tmp, port_base + U3P_U2PHYACR4);
|
||||
}
|
||||
|
||||
if (u3phy->pdata->avoid_rx_sen_degradation) {
|
||||
if (!index) {
|
||||
tmp = readl(port_base + U3P_USBPHYACR2);
|
||||
tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR2);
|
||||
|
@ -258,6 +267,7 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
|
|||
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = readl(port_base + U3P_USBPHYACR6);
|
||||
tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
|
||||
|
@ -318,7 +328,7 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
|
|||
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
|
||||
writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
|
||||
|
||||
/* [mt8173]switch 100uA current to SSUSB */
|
||||
/* switch 100uA current to SSUSB */
|
||||
tmp = readl(port_base + U3P_USBPHYACR5);
|
||||
tmp |= PA5_RG_U2_HS_100U_U3_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
|
@ -335,7 +345,7 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
|
|||
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4);
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
|
||||
if (index) {
|
||||
if (u3phy->pdata->avoid_rx_sen_degradation && index) {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
|
@ -386,7 +396,9 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
|
|||
tmp = readl(port_base + U3P_U3_PHYA_REG0);
|
||||
tmp &= ~P3A_RG_U3_VUSB10_ON;
|
||||
writel(tmp, port_base + U3P_U3_PHYA_REG0);
|
||||
} else {
|
||||
}
|
||||
|
||||
if (u3phy->pdata->avoid_rx_sen_degradation && index) {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
|
@ -402,7 +414,7 @@ static void phy_instance_exit(struct mt65xx_u3phy *u3phy,
|
|||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
if (index) {
|
||||
if (u3phy->pdata->avoid_rx_sen_degradation && index) {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
|
@ -502,8 +514,24 @@ static struct phy_ops mt65xx_u3phy_ops = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct mt65xx_phy_pdata mt2701_pdata = {
|
||||
.avoid_rx_sen_degradation = false,
|
||||
};
|
||||
|
||||
static const struct mt65xx_phy_pdata mt8173_pdata = {
|
||||
.avoid_rx_sen_degradation = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id mt65xx_u3phy_id_table[] = {
|
||||
{ .compatible = "mediatek,mt2701-u3phy", .data = &mt2701_pdata },
|
||||
{ .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table);
|
||||
|
||||
static int mt65xx_u3phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child_np;
|
||||
|
@ -513,10 +541,15 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
|
|||
struct resource res;
|
||||
int port, retval;
|
||||
|
||||
match = of_match_node(mt65xx_u3phy_id_table, pdev->dev.of_node);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
|
||||
if (!u3phy)
|
||||
return -ENOMEM;
|
||||
|
||||
u3phy->pdata = match->data;
|
||||
u3phy->nphys = of_get_child_count(np);
|
||||
u3phy->phys = devm_kcalloc(dev, u3phy->nphys,
|
||||
sizeof(*u3phy->phys), GFP_KERNEL);
|
||||
|
@ -587,12 +620,6 @@ put_child:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static const struct of_device_id mt65xx_u3phy_id_table[] = {
|
||||
{ .compatible = "mediatek,mt8173-u3phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table);
|
||||
|
||||
static struct platform_driver mt65xx_u3phy_driver = {
|
||||
.probe = mt65xx_u3phy_probe,
|
||||
.driver = {
|
||||
|
|
|
@ -195,6 +195,7 @@ static const struct of_device_id rcar_gen2_phy_match_table[] = {
|
|||
{ .compatible = "renesas,usb-phy-r8a7790" },
|
||||
{ .compatible = "renesas,usb-phy-r8a7791" },
|
||||
{ .compatible = "renesas,usb-phy-r8a7794" },
|
||||
{ .compatible = "renesas,rcar-gen2-usb-phy" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -19,6 +20,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/******* USB2.0 Host registers (original offset is +0x200) *******/
|
||||
#define USB2_INT_ENABLE 0x000
|
||||
|
@ -74,20 +76,17 @@
|
|||
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
||||
#define USB2_ADPCTRL_DRVVBUS BIT(4)
|
||||
|
||||
struct rcar_gen3_data {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct rcar_gen3_chan {
|
||||
struct rcar_gen3_data usb2;
|
||||
void __iomem *base;
|
||||
struct extcon_dev *extcon;
|
||||
struct phy *phy;
|
||||
struct regulator *vbus;
|
||||
bool has_otg;
|
||||
};
|
||||
|
||||
static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val = readl(usb2_base + USB2_COMMCTRL);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host);
|
||||
|
@ -100,7 +99,7 @@ static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
|
|||
|
||||
static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val = readl(usb2_base + USB2_LINECTRL1);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm);
|
||||
|
@ -114,7 +113,7 @@ static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
|
|||
|
||||
static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val = readl(usb2_base + USB2_ADPCTRL);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus);
|
||||
|
@ -130,6 +129,9 @@ static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
|
|||
rcar_gen3_set_linectrl(ch, 1, 1);
|
||||
rcar_gen3_set_host_mode(ch, 1);
|
||||
rcar_gen3_enable_vbus_ctrl(ch, 1);
|
||||
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true);
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB, false);
|
||||
}
|
||||
|
||||
static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
|
||||
|
@ -137,17 +139,20 @@ static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
|
|||
rcar_gen3_set_linectrl(ch, 0, 1);
|
||||
rcar_gen3_set_host_mode(ch, 0);
|
||||
rcar_gen3_enable_vbus_ctrl(ch, 0);
|
||||
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false);
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB, true);
|
||||
}
|
||||
|
||||
static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
return !!(readl(ch->usb2.base + USB2_ADPCTRL) &
|
||||
return !!(readl(ch->base + USB2_ADPCTRL) &
|
||||
USB2_ADPCTRL_OTGSESSVLD);
|
||||
}
|
||||
|
||||
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
return !!(readl(ch->usb2.base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
|
||||
return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
|
||||
}
|
||||
|
||||
static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
|
||||
|
@ -166,7 +171,7 @@ static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
|
|||
|
||||
static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val;
|
||||
|
||||
val = readl(usb2_base + USB2_VBCTRL);
|
||||
|
@ -187,7 +192,7 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
|
|||
static int rcar_gen3_phy_usb2_init(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *usb2_base = channel->usb2.base;
|
||||
void __iomem *usb2_base = channel->base;
|
||||
|
||||
/* Initialize USB2 part */
|
||||
writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
|
||||
|
@ -205,7 +210,7 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
|
|||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
|
||||
writel(0, channel->usb2.base + USB2_INT_ENABLE);
|
||||
writel(0, channel->base + USB2_INT_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -213,8 +218,15 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
|
|||
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *usb2_base = channel->usb2.base;
|
||||
void __iomem *usb2_base = channel->base;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (channel->vbus) {
|
||||
ret = regulator_enable(channel->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = readl(usb2_base + USB2_USBCTR);
|
||||
val |= USB2_USBCTR_PLL_RST;
|
||||
|
@ -225,17 +237,29 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
int ret = 0;
|
||||
|
||||
if (channel->vbus)
|
||||
ret = regulator_disable(channel->vbus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops rcar_gen3_phy_usb2_ops = {
|
||||
.init = rcar_gen3_phy_usb2_init,
|
||||
.exit = rcar_gen3_phy_usb2_exit,
|
||||
.power_on = rcar_gen3_phy_usb2_power_on,
|
||||
.power_off = rcar_gen3_phy_usb2_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
||||
{
|
||||
struct rcar_gen3_chan *ch = _ch;
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 status = readl(usb2_base + USB2_OBINTSTA);
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
|
@ -251,10 +275,17 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
|||
|
||||
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
|
||||
{ .compatible = "renesas,usb2-phy-r8a7795" },
|
||||
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
|
||||
|
||||
static const unsigned int rcar_gen3_phy_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -273,18 +304,30 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
channel->usb2.base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(channel->usb2.base))
|
||||
return PTR_ERR(channel->usb2.base);
|
||||
channel->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(channel->base))
|
||||
return PTR_ERR(channel->base);
|
||||
|
||||
/* call request_irq for OTG */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0) {
|
||||
int ret;
|
||||
|
||||
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
|
||||
IRQF_SHARED, dev_name(dev), channel);
|
||||
if (irq < 0)
|
||||
dev_err(dev, "No irq handler (%d)\n", irq);
|
||||
channel->has_otg = true;
|
||||
channel->extcon = devm_extcon_dev_allocate(dev,
|
||||
rcar_gen3_phy_cable);
|
||||
if (IS_ERR(channel->extcon))
|
||||
return PTR_ERR(channel->extcon);
|
||||
|
||||
ret = devm_extcon_dev_register(dev, channel->extcon);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to register extcon\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* devm_phy_create() will call pm_runtime_enable(dev); */
|
||||
|
@ -294,6 +337,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(channel->phy);
|
||||
}
|
||||
|
||||
channel->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (IS_ERR(channel->vbus)) {
|
||||
if (PTR_ERR(channel->vbus) == -EPROBE_DEFER)
|
||||
return PTR_ERR(channel->vbus);
|
||||
channel->vbus = NULL;
|
||||
}
|
||||
|
||||
phy_set_drvdata(channel->phy, channel);
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
|
|
@ -216,7 +216,7 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
|
|||
init.parent_names = &clk_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.flags = CLK_IS_ROOT;
|
||||
init.flags = 0;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,6 @@ if USB_SUPPORT
|
|||
|
||||
config USB_COMMON
|
||||
tristate
|
||||
default y
|
||||
depends on USB || USB_GADGET
|
||||
|
||||
config USB_ARCH_HAS_HCD
|
||||
def_bool y
|
||||
|
@ -41,6 +39,7 @@ config USB_ARCH_HAS_HCD
|
|||
config USB
|
||||
tristate "Support for Host-side USB"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select USB_COMMON
|
||||
select NLS # for UTF-8 strings
|
||||
---help---
|
||||
Universal Serial Bus (USB) is a specification for a serial bus
|
||||
|
|
|
@ -292,10 +292,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
|
||||
data->supports_runtime_pm = true;
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = imx_usbmisc_init(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
|
||||
|
|
|
@ -61,8 +61,6 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int state_changed;
|
||||
|
||||
/* Called when leaving a state. Do state clean up jobs here */
|
||||
static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
||||
{
|
||||
|
@ -208,7 +206,6 @@ static void otg_start_hnp_polling(struct otg_fsm *fsm)
|
|||
/* Called when entering a state */
|
||||
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
||||
{
|
||||
state_changed = 1;
|
||||
if (fsm->otg->state == new_state)
|
||||
return 0;
|
||||
VDBG("Set state: %s\n", usb_otg_state_string(new_state));
|
||||
|
@ -324,6 +321,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
|||
}
|
||||
|
||||
fsm->otg->state = new_state;
|
||||
fsm->state_changed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -335,7 +333,7 @@ int otg_statemachine(struct otg_fsm *fsm)
|
|||
mutex_lock(&fsm->lock);
|
||||
|
||||
state = fsm->otg->state;
|
||||
state_changed = 0;
|
||||
fsm->state_changed = 0;
|
||||
/* State machine state change judgement */
|
||||
|
||||
switch (state) {
|
||||
|
@ -448,7 +446,7 @@ int otg_statemachine(struct otg_fsm *fsm)
|
|||
}
|
||||
mutex_unlock(&fsm->lock);
|
||||
|
||||
VDBG("quit statemachine, changed = %d\n", state_changed);
|
||||
return state_changed;
|
||||
VDBG("quit statemachine, changed = %d\n", fsm->state_changed);
|
||||
return fsm->state_changed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(otg_statemachine);
|
||||
|
|
|
@ -122,6 +122,9 @@ void *hcd_buffer_alloc(
|
|||
struct usb_hcd *hcd = bus_to_hcd(bus);
|
||||
int i;
|
||||
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
|
||||
/* some USB hosts just use PIO */
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!bus->controller->dma_mask &&
|
||||
|
|
|
@ -216,7 +216,7 @@ static void usbdev_vm_close(struct vm_area_struct *vma)
|
|||
dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
|
||||
}
|
||||
|
||||
struct vm_operations_struct usbdev_vm_ops = {
|
||||
static struct vm_operations_struct usbdev_vm_ops = {
|
||||
.open = usbdev_vm_open,
|
||||
.close = usbdev_vm_close
|
||||
};
|
||||
|
@ -1316,10 +1316,11 @@ static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
|
|||
|
||||
static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
|
||||
{
|
||||
struct usbdevfs_connectinfo ci = {
|
||||
.devnum = ps->dev->devnum,
|
||||
.slow = ps->dev->speed == USB_SPEED_LOW
|
||||
};
|
||||
struct usbdevfs_connectinfo ci;
|
||||
|
||||
memset(&ci, 0, sizeof(ci));
|
||||
ci.devnum = ps->dev->devnum;
|
||||
ci.slow = ps->dev->speed == USB_SPEED_LOW;
|
||||
|
||||
if (copy_to_user(arg, &ci, sizeof(ci)))
|
||||
return -EFAULT;
|
||||
|
|
|
@ -284,7 +284,7 @@ static int usb_probe_interface(struct device *dev)
|
|||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
const struct usb_device_id *id;
|
||||
int error = -ENODEV;
|
||||
int lpm_disable_error;
|
||||
int lpm_disable_error = -ENODEV;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
|
@ -336,13 +336,15 @@ static int usb_probe_interface(struct device *dev)
|
|||
* setting during probe, that should also be fine. usb_set_interface()
|
||||
* will attempt to disable LPM, and fail if it can't disable it.
|
||||
*/
|
||||
if (driver->disable_hub_initiated_lpm) {
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
||||
if (lpm_disable_error) {
|
||||
dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
error = lpm_disable_error;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Carry out a deferred switch to altsetting 0 */
|
||||
if (intf->needs_altsetting0) {
|
||||
|
@ -391,7 +393,8 @@ static int usb_unbind_interface(struct device *dev)
|
|||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_host_endpoint *ep, **eps = NULL;
|
||||
struct usb_device *udev;
|
||||
int i, j, error, r, lpm_disable_error;
|
||||
int i, j, error, r;
|
||||
int lpm_disable_error = -ENODEV;
|
||||
|
||||
intf->condition = USB_INTERFACE_UNBINDING;
|
||||
|
||||
|
@ -399,11 +402,12 @@ static int usb_unbind_interface(struct device *dev)
|
|||
udev = interface_to_usbdev(intf);
|
||||
error = usb_autoresume_device(udev);
|
||||
|
||||
/* Hub-initiated LPM policy may change, so attempt to disable LPM until
|
||||
/* If hub-initiated LPM policy may change, attempt to disable LPM until
|
||||
* the driver is unbound. If LPM isn't disabled, that's fine because it
|
||||
* wouldn't be enabled unless all the bound interfaces supported
|
||||
* hub-initiated LPM.
|
||||
*/
|
||||
if (driver->disable_hub_initiated_lpm)
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
|
||||
/*
|
||||
|
@ -505,7 +509,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
struct device *dev;
|
||||
struct usb_device *udev;
|
||||
int retval = 0;
|
||||
int lpm_disable_error;
|
||||
int lpm_disable_error = -ENODEV;
|
||||
|
||||
if (!iface)
|
||||
return -ENODEV;
|
||||
|
@ -526,13 +530,15 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
|
||||
iface->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
/* Disable LPM until this driver is bound. */
|
||||
/* See the comment about disabling LPM in usb_probe_interface(). */
|
||||
if (driver->disable_hub_initiated_lpm) {
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
||||
if (lpm_disable_error) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Claimed interfaces are initially inactive (suspended) and
|
||||
* runtime-PM-enabled, but only if the driver has autosuspend
|
||||
|
|
|
@ -994,7 +994,7 @@ static void usb_bus_init (struct usb_bus *bus)
|
|||
bus->bandwidth_allocated = 0;
|
||||
bus->bandwidth_int_reqs = 0;
|
||||
bus->bandwidth_isoc_reqs = 0;
|
||||
mutex_init(&bus->usb_address0_mutex);
|
||||
mutex_init(&bus->devnum_next_mutex);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -1118,6 +1118,7 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
/* Did the HC die before the root hub was registered? */
|
||||
if (HCD_DEAD(hcd))
|
||||
usb_hc_died (hcd); /* This time clean up */
|
||||
usb_dev->dev.of_node = parent_dev->of_node;
|
||||
}
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
|
@ -2521,6 +2522,14 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
|
|||
return NULL;
|
||||
}
|
||||
if (primary_hcd == NULL) {
|
||||
hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
|
||||
GFP_KERNEL);
|
||||
if (!hcd->address0_mutex) {
|
||||
kfree(hcd);
|
||||
dev_dbg(dev, "hcd address0 mutex alloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
mutex_init(hcd->address0_mutex);
|
||||
hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
|
||||
GFP_KERNEL);
|
||||
if (!hcd->bandwidth_mutex) {
|
||||
|
@ -2532,6 +2541,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
|
|||
dev_set_drvdata(dev, hcd);
|
||||
} else {
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
hcd->address0_mutex = primary_hcd->address0_mutex;
|
||||
hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
|
||||
hcd->primary_hcd = primary_hcd;
|
||||
primary_hcd->primary_hcd = primary_hcd;
|
||||
|
@ -2598,8 +2608,10 @@ static void hcd_release(struct kref *kref)
|
|||
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
|
||||
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
if (usb_hcd_is_primary_hcd(hcd))
|
||||
if (usb_hcd_is_primary_hcd(hcd)) {
|
||||
kfree(hcd->address0_mutex);
|
||||
kfree(hcd->bandwidth_mutex);
|
||||
}
|
||||
if (hcd->shared_hcd) {
|
||||
struct usb_hcd *peer = hcd->shared_hcd;
|
||||
|
||||
|
|
|
@ -104,6 +104,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev);
|
|||
|
||||
static inline char *portspeed(struct usb_hub *hub, int portstatus)
|
||||
{
|
||||
if (hub_is_superspeedplus(hub->hdev))
|
||||
return "10.0 Gb/s";
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
return "5.0 Gb/s";
|
||||
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
|
||||
|
@ -2080,7 +2082,7 @@ static void choose_devnum(struct usb_device *udev)
|
|||
struct usb_bus *bus = udev->bus;
|
||||
|
||||
/* be safe when more hub events are proceed in parallel */
|
||||
mutex_lock(&bus->usb_address0_mutex);
|
||||
mutex_lock(&bus->devnum_next_mutex);
|
||||
if (udev->wusb) {
|
||||
devnum = udev->portnum + 1;
|
||||
BUG_ON(test_bit(devnum, bus->devmap.devicemap));
|
||||
|
@ -2098,7 +2100,7 @@ static void choose_devnum(struct usb_device *udev)
|
|||
set_bit(devnum, bus->devmap.devicemap);
|
||||
udev->devnum = devnum;
|
||||
}
|
||||
mutex_unlock(&bus->usb_address0_mutex);
|
||||
mutex_unlock(&bus->devnum_next_mutex);
|
||||
}
|
||||
|
||||
static void release_devnum(struct usb_device *udev)
|
||||
|
@ -4364,7 +4366,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
if (oldspeed == USB_SPEED_LOW)
|
||||
delay = HUB_LONG_RESET_TIME;
|
||||
|
||||
mutex_lock(&hdev->bus->usb_address0_mutex);
|
||||
mutex_lock(hcd->address0_mutex);
|
||||
|
||||
/* Reset the device; full speed may morph to high speed */
|
||||
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
|
||||
|
@ -4650,7 +4652,7 @@ fail:
|
|||
hub_port_disable(hub, port1, 0);
|
||||
update_devnum(udev, devnum); /* for disconnect processing */
|
||||
}
|
||||
mutex_unlock(&hdev->bus->usb_address0_mutex);
|
||||
mutex_unlock(hcd->address0_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -302,9 +302,10 @@ static void sg_complete(struct urb *urb)
|
|||
*/
|
||||
spin_unlock(&io->lock);
|
||||
for (i = 0, found = 0; i < io->entries; i++) {
|
||||
if (!io->urbs[i] || !io->urbs[i]->dev)
|
||||
if (!io->urbs[i])
|
||||
continue;
|
||||
if (found) {
|
||||
usb_block_urb(io->urbs[i]);
|
||||
retval = usb_unlink_urb(io->urbs[i]);
|
||||
if (retval != -EINPROGRESS &&
|
||||
retval != -ENODEV &&
|
||||
|
@ -515,12 +516,10 @@ void usb_sg_wait(struct usb_sg_request *io)
|
|||
int retval;
|
||||
|
||||
io->urbs[i]->dev = io->dev;
|
||||
retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC);
|
||||
|
||||
/* after we submit, let completions or cancellations fire;
|
||||
* we handshake using io->status.
|
||||
*/
|
||||
spin_unlock_irq(&io->lock);
|
||||
|
||||
retval = usb_submit_urb(io->urbs[i], GFP_NOIO);
|
||||
|
||||
switch (retval) {
|
||||
/* maybe we retrying will recover */
|
||||
case -ENXIO: /* hc didn't queue this one */
|
||||
|
@ -578,20 +577,20 @@ EXPORT_SYMBOL_GPL(usb_sg_wait);
|
|||
void usb_sg_cancel(struct usb_sg_request *io)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i, retval;
|
||||
|
||||
spin_lock_irqsave(&io->lock, flags);
|
||||
|
||||
/* shut everything down, if it didn't already */
|
||||
if (!io->status) {
|
||||
int i;
|
||||
|
||||
if (io->status) {
|
||||
spin_unlock_irqrestore(&io->lock, flags);
|
||||
return;
|
||||
}
|
||||
/* shut everything down */
|
||||
io->status = -ECONNRESET;
|
||||
spin_unlock(&io->lock);
|
||||
for (i = 0; i < io->entries; i++) {
|
||||
int retval;
|
||||
spin_unlock_irqrestore(&io->lock, flags);
|
||||
|
||||
for (i = io->entries - 1; i >= 0; --i) {
|
||||
usb_block_urb(io->urbs[i]);
|
||||
|
||||
if (!io->urbs[i]->dev)
|
||||
continue;
|
||||
retval = usb_unlink_urb(io->urbs[i]);
|
||||
if (retval != -EINPROGRESS
|
||||
&& retval != -ENODEV
|
||||
|
@ -600,9 +599,6 @@ void usb_sg_cancel(struct usb_sg_request *io)
|
|||
dev_warn(&io->dev->dev, "%s, unlink --> %d\n",
|
||||
__func__, retval);
|
||||
}
|
||||
spin_lock(&io->lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&io->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_sg_cancel);
|
||||
|
||||
|
|
|
@ -466,7 +466,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->route = 0;
|
||||
|
||||
dev->dev.parent = bus->controller;
|
||||
dev->dev.of_node = bus->controller->of_node;
|
||||
dev_set_name(&dev->dev, "usb%d", bus->busnum);
|
||||
root_hub = 1;
|
||||
} else {
|
||||
|
|
|
@ -2425,6 +2425,9 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
|
|||
u32 gintsts;
|
||||
u32 gintmsk;
|
||||
|
||||
if (!dwc2_is_device_mode(hsotg))
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock(&hsotg->lock);
|
||||
irq_retry:
|
||||
gintsts = dwc2_readl(hsotg->regs + GINTSTS);
|
||||
|
@ -2631,7 +2634,10 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
|||
desc->wMaxPacketSize, desc->bInterval);
|
||||
|
||||
/* not to be called for EP0 */
|
||||
WARN_ON(index == 0);
|
||||
if (index == 0) {
|
||||
dev_err(hsotg->dev, "%s: called for EP 0\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
|
||||
if (dir_in != hs_ep->dir_in) {
|
||||
|
|
|
@ -4703,6 +4703,7 @@ fail2:
|
|||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
urb->hcpriv = NULL;
|
||||
kfree(qtd);
|
||||
qtd = NULL;
|
||||
fail1:
|
||||
if (qh_allocated) {
|
||||
struct dwc2_qtd *qtd2, *qtd2_tmp;
|
||||
|
|
|
@ -552,6 +552,7 @@ static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
|
|||
{
|
||||
list_del(&qtd->qtd_list_entry);
|
||||
kfree(qtd);
|
||||
qtd = NULL;
|
||||
}
|
||||
|
||||
/* Descriptor DMA support functions */
|
||||
|
|
|
@ -1709,7 +1709,8 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
|
||||
dwc2_deschedule_periodic(hsotg, qh);
|
||||
hsotg->periodic_qh_count--;
|
||||
if (!hsotg->periodic_qh_count) {
|
||||
if (!hsotg->periodic_qh_count &&
|
||||
hsotg->core_params->dma_desc_enable <= 0) {
|
||||
intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
intr_mask &= ~GINTSTS_SOF;
|
||||
dwc2_writel(intr_mask, hsotg->regs + GINTMSK);
|
||||
|
|
|
@ -562,7 +562,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
|
||||
retval = dwc2_get_dr_mode(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Reset before dwc2_get_hwparams() then it could get power-on real
|
||||
|
|
|
@ -60,6 +60,20 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
|||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
}
|
||||
|
||||
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
|
||||
DWC3_GDBGFIFOSPACE_NUM(dep->number) |
|
||||
DWC3_GDBGFIFOSPACE_TYPE(type));
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
|
||||
|
||||
return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
|
||||
* @dwc: pointer to our context structure
|
||||
|
@ -203,14 +217,11 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
|
|||
static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_event_buffers; i++) {
|
||||
evt = dwc->ev_buffs[i];
|
||||
evt = dwc->ev_buf;
|
||||
if (evt)
|
||||
dwc3_free_one_event_buffer(dwc, evt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
|
||||
|
@ -222,18 +233,6 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
|||
*/
|
||||
static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
||||
{
|
||||
int num;
|
||||
int i;
|
||||
|
||||
num = DWC3_NUM_INT(dwc->hwparams.hwparams1);
|
||||
dwc->num_event_buffers = num;
|
||||
|
||||
dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
|
||||
GFP_KERNEL);
|
||||
if (!dwc->ev_buffs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct dwc3_event_buffer *evt;
|
||||
|
||||
evt = dwc3_alloc_one_event_buffer(dwc, length);
|
||||
|
@ -241,8 +240,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
|||
dev_err(dwc->dev, "can't allocate event buffer\n");
|
||||
return PTR_ERR(evt);
|
||||
}
|
||||
dwc->ev_buffs[i] = evt;
|
||||
}
|
||||
dwc->ev_buf = evt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -256,10 +254,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
|||
static int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < dwc->num_event_buffers; n++) {
|
||||
evt = dwc->ev_buffs[n];
|
||||
evt = dwc->ev_buf;
|
||||
dwc3_trace(trace_dwc3_core,
|
||||
"Event buf %p dma %08llx length %d\n",
|
||||
evt->buf, (unsigned long long) evt->dma,
|
||||
|
@ -267,14 +263,13 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
|||
|
||||
evt->lpos = 0;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
|
||||
lower_32_bits(evt->dma));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
|
||||
upper_32_bits(evt->dma));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
|
||||
DWC3_GEVNTSIZ_SIZE(evt->length));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -282,19 +277,16 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
|||
static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < dwc->num_event_buffers; n++) {
|
||||
evt = dwc->ev_buffs[n];
|
||||
evt = dwc->ev_buf;
|
||||
|
||||
evt->lpos = 0;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
|
||||
| DWC3_GEVNTSIZ_SIZE(0));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
|
||||
}
|
||||
|
||||
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
|
||||
|
@ -434,6 +426,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
|||
if (dwc->u2ss_inp3_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
|
||||
|
||||
if (dwc->dis_rxdet_inp3_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
|
||||
|
||||
if (dwc->req_p1p2p3_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
|
||||
|
||||
|
@ -882,9 +877,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dwc->usb3_lpm_capable = device_property_read_bool(dev,
|
||||
"snps,usb3_lpm_capable");
|
||||
|
||||
dwc->needs_fifo_resize = device_property_read_bool(dev,
|
||||
"tx-fifo-resize");
|
||||
|
||||
dwc->disable_scramble_quirk = device_property_read_bool(dev,
|
||||
"snps,disable_scramble_quirk");
|
||||
dwc->u2exit_lfps_quirk = device_property_read_bool(dev,
|
||||
|
@ -907,6 +899,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
"snps,dis_u2_susphy_quirk");
|
||||
dwc->dis_enblslpm_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_enblslpm_quirk");
|
||||
dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_rxdet_inp3_quirk");
|
||||
|
||||
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
|
||||
"snps,tx_de_emphasis_quirk");
|
||||
|
@ -926,7 +920,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
if (pdata->hird_threshold)
|
||||
hird_threshold = pdata->hird_threshold;
|
||||
|
||||
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
|
||||
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
|
||||
|
@ -941,6 +934,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
|
||||
dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
|
||||
dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk;
|
||||
dwc->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_quirk;
|
||||
|
||||
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
|
||||
if (pdata->tx_de_emphasis)
|
||||
|
@ -1050,19 +1044,11 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err5;
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize debugfs\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
dwc3_debugfs_init(dwc);
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
||||
err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
|
|
|
@ -152,6 +152,24 @@
|
|||
|
||||
/* Bit fields */
|
||||
|
||||
/* Global Debug Queue/FIFO Space Available Register */
|
||||
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
|
||||
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
|
||||
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
|
||||
|
||||
#define DWC3_TXFIFOQ 1
|
||||
#define DWC3_RXFIFOQ 3
|
||||
#define DWC3_TXREQQ 5
|
||||
#define DWC3_RXREQQ 7
|
||||
#define DWC3_RXINFOQ 9
|
||||
#define DWC3_DESCFETCHQ 13
|
||||
#define DWC3_EVENTQ 15
|
||||
|
||||
/* Global RX Threshold Configuration Register */
|
||||
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
|
||||
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
|
||||
#define DWC3_GRXTHRCFG_PKTCNTSEL (1 << 29)
|
||||
|
||||
/* Global Configuration Register */
|
||||
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
|
||||
#define DWC3_GCTL_U2RSTECN (1 << 16)
|
||||
|
@ -193,6 +211,7 @@
|
|||
/* Global USB3 PIPE Control Register */
|
||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
||||
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
|
||||
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
|
||||
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
|
||||
|
@ -257,6 +276,9 @@
|
|||
#define DWC3_DCFG_LOWSPEED (2 << 0)
|
||||
#define DWC3_DCFG_FULLSPEED1 (3 << 0)
|
||||
|
||||
#define DWC3_DCFG_NUMP_SHIFT 17
|
||||
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
|
||||
#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
|
||||
#define DWC3_DCFG_LPM_CAP (1 << 22)
|
||||
|
||||
/* Device Control Register */
|
||||
|
@ -438,18 +460,17 @@ struct dwc3_event_buffer {
|
|||
#define DWC3_EP_DIRECTION_TX true
|
||||
#define DWC3_EP_DIRECTION_RX false
|
||||
|
||||
#define DWC3_TRB_NUM 32
|
||||
#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1)
|
||||
#define DWC3_TRB_NUM 256
|
||||
|
||||
/**
|
||||
* struct dwc3_ep - device side endpoint representation
|
||||
* @endpoint: usb endpoint
|
||||
* @request_list: list of requests for this endpoint
|
||||
* @req_queued: list of requests on this ep which have TRBs setup
|
||||
* @pending_list: list of pending requests for this endpoint
|
||||
* @started_list: list of started requests on this endpoint
|
||||
* @trb_pool: array of transaction buffers
|
||||
* @trb_pool_dma: dma address of @trb_pool
|
||||
* @free_slot: next slot which is going to be used
|
||||
* @busy_slot: first slot which is owned by HW
|
||||
* @trb_enqueue: enqueue 'pointer' into TRB array
|
||||
* @trb_dequeue: dequeue 'pointer' into TRB array
|
||||
* @desc: usb_endpoint_descriptor pointer
|
||||
* @dwc: pointer to DWC controller
|
||||
* @saved_state: ep state saved during hibernation
|
||||
|
@ -464,13 +485,11 @@ struct dwc3_event_buffer {
|
|||
*/
|
||||
struct dwc3_ep {
|
||||
struct usb_ep endpoint;
|
||||
struct list_head request_list;
|
||||
struct list_head req_queued;
|
||||
struct list_head pending_list;
|
||||
struct list_head started_list;
|
||||
|
||||
struct dwc3_trb *trb_pool;
|
||||
dma_addr_t trb_pool_dma;
|
||||
u32 free_slot;
|
||||
u32 busy_slot;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
|
@ -486,6 +505,18 @@ struct dwc3_ep {
|
|||
/* This last one is specific to EP0 */
|
||||
#define DWC3_EP0_DIR_IN (1 << 31)
|
||||
|
||||
/*
|
||||
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
|
||||
* use a u8 type here. If anybody decides to increase number of TRBs to
|
||||
* anything larger than 256 - I can't see why people would want to do
|
||||
* this though - then this type needs to be changed.
|
||||
*
|
||||
* By using u8 types we ensure that our % operator when incrementing
|
||||
* enqueue and dequeue get optimized away by the compiler.
|
||||
*/
|
||||
u8 trb_enqueue;
|
||||
u8 trb_dequeue;
|
||||
|
||||
u8 number;
|
||||
u8 type;
|
||||
u8 resource_index;
|
||||
|
@ -557,6 +588,7 @@ enum dwc3_link_state {
|
|||
#define DWC3_TRB_CTRL_IOC (1 << 11)
|
||||
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
|
||||
|
||||
#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
|
||||
#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
|
||||
#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2)
|
||||
#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3)
|
||||
|
@ -623,19 +655,32 @@ struct dwc3_hwparams {
|
|||
/* HWPARAMS7 */
|
||||
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
|
||||
/**
|
||||
* struct dwc3_request - representation of a transfer request
|
||||
* @request: struct usb_request to be transferred
|
||||
* @list: a list_head used for request queueing
|
||||
* @dep: struct dwc3_ep owning this request
|
||||
* @first_trb_index: index to first trb used by this request
|
||||
* @epnum: endpoint number to which this request refers
|
||||
* @trb: pointer to struct dwc3_trb
|
||||
* @trb_dma: DMA address of @trb
|
||||
* @direction: IN or OUT direction flag
|
||||
* @mapped: true when request has been dma-mapped
|
||||
* @queued: true when request has been queued to HW
|
||||
*/
|
||||
struct dwc3_request {
|
||||
struct usb_request request;
|
||||
struct list_head list;
|
||||
struct dwc3_ep *dep;
|
||||
u32 start_slot;
|
||||
|
||||
u8 first_trb_index;
|
||||
u8 epnum;
|
||||
struct dwc3_trb *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned queued:1;
|
||||
unsigned started:1;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -667,7 +712,6 @@ struct dwc3_scratchpad_array {
|
|||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @nr_scratch: number of scratch buffers
|
||||
* @num_event_buffers: calculated number of event buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
|
@ -709,9 +753,7 @@ struct dwc3_scratchpad_array {
|
|||
* 0 - utmi_sleep_n
|
||||
* 1 - utmi_l1_suspend_n
|
||||
* @is_fpga: true when we are using the FPGA board
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
|
@ -756,7 +798,7 @@ struct dwc3 {
|
|||
struct platform_device *xhci;
|
||||
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
|
||||
|
||||
struct dwc3_event_buffer **ev_buffs;
|
||||
struct dwc3_event_buffer *ev_buf;
|
||||
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
|
||||
|
||||
struct usb_gadget gadget;
|
||||
|
@ -780,7 +822,6 @@ struct dwc3 {
|
|||
u32 gctl;
|
||||
|
||||
u32 nr_scratch;
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
|
||||
|
@ -855,9 +896,7 @@ struct dwc3 {
|
|||
unsigned has_lpm_erratum:1;
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
unsigned is_fpga:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned usb3_lpm_capable:1;
|
||||
|
@ -873,6 +912,7 @@ struct dwc3 {
|
|||
unsigned dis_u3_susphy_quirk:1;
|
||||
unsigned dis_u2_susphy_quirk:1;
|
||||
unsigned dis_enblslpm_quirk:1;
|
||||
unsigned dis_rxdet_inp3_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
@ -938,6 +978,10 @@ struct dwc3_event_depevt {
|
|||
#define DEPEVT_STATUS_CONTROL_DATA 1
|
||||
#define DEPEVT_STATUS_CONTROL_STATUS 2
|
||||
|
||||
/* In response to Start Transfer */
|
||||
#define DEPEVT_TRANSFER_NO_RESOURCE 1
|
||||
#define DEPEVT_TRANSFER_BUS_EXPIRY 2
|
||||
|
||||
u32 parameters:16;
|
||||
} __packed;
|
||||
|
||||
|
@ -1025,7 +1069,7 @@ struct dwc3_gadget_ep_cmd_params {
|
|||
|
||||
/* prototypes */
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
|
||||
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
|
||||
|
||||
/* check whether we are on the DWC_usb31 core */
|
||||
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
|
||||
|
|
|
@ -217,11 +217,11 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
|
|||
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern int dwc3_debugfs_init(struct dwc3 *);
|
||||
extern void dwc3_debugfs_init(struct dwc3 *);
|
||||
extern void dwc3_debugfs_exit(struct dwc3 *);
|
||||
#else
|
||||
static inline int dwc3_debugfs_init(struct dwc3 *d)
|
||||
{ return 0; }
|
||||
static inline void dwc3_debugfs_init(struct dwc3 *d)
|
||||
{ }
|
||||
static inline void dwc3_debugfs_exit(struct dwc3 *d)
|
||||
{ }
|
||||
#endif
|
||||
|
|
|
@ -618,24 +618,323 @@ static const struct file_operations dwc3_link_state_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
int dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
struct dwc3_ep_file_map {
|
||||
char name[25];
|
||||
int (*show)(struct seq_file *s, void *unused);
|
||||
};
|
||||
|
||||
static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_tx_request_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXREQQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_event_queue_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!(dep->flags & DWC3_EP_ENABLED) ||
|
||||
!dep->endpoint.desc) {
|
||||
seq_printf(s, "--\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (usb_endpoint_type(dep->endpoint.desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
seq_printf(s, "control\n");
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
seq_printf(s, "isochronous\n");
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
seq_printf(s, "bulk\n");
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
seq_printf(s, "interrupt\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "--\n");
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline const char *dwc3_trb_type_string(struct dwc3_trb *trb)
|
||||
{
|
||||
switch (DWC3_TRBCTL_TYPE(trb->ctrl)) {
|
||||
case DWC3_TRBCTL_NORMAL:
|
||||
return "normal";
|
||||
case DWC3_TRBCTL_CONTROL_SETUP:
|
||||
return "control-setup";
|
||||
case DWC3_TRBCTL_CONTROL_STATUS2:
|
||||
return "control-status2";
|
||||
case DWC3_TRBCTL_CONTROL_STATUS3:
|
||||
return "control-status3";
|
||||
case DWC3_TRBCTL_CONTROL_DATA:
|
||||
return "control-data";
|
||||
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
|
||||
return "isoc-first";
|
||||
case DWC3_TRBCTL_ISOCHRONOUS:
|
||||
return "isoc";
|
||||
case DWC3_TRBCTL_LINK_TRB:
|
||||
return "link";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dep->number <= 1) {
|
||||
seq_printf(s, "--\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
seq_printf(s, "enqueue pointer %d\n", dep->trb_enqueue);
|
||||
seq_printf(s, "dequeue pointer %d\n", dep->trb_dequeue);
|
||||
seq_printf(s, "\n--------------------------------------------------\n\n");
|
||||
seq_printf(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n");
|
||||
|
||||
for (i = 0; i < DWC3_TRB_NUM; i++) {
|
||||
struct dwc3_trb *trb = &dep->trb_pool[i];
|
||||
|
||||
seq_printf(s, "%08x%08x,%d,%s,%d,%d,%d,%d,%d,%d\n",
|
||||
trb->bph, trb->bpl, trb->size,
|
||||
dwc3_trb_type_string(trb),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_IOC),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_ISP_IMI),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_CSP),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_CHN),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_LST),
|
||||
!!(trb->ctrl & DWC3_TRB_CTRL_HWO));
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dwc3_ep_file_map map[] = {
|
||||
{ "tx_fifo_queue", dwc3_tx_fifo_queue_show, },
|
||||
{ "rx_fifo_queue", dwc3_rx_fifo_queue_show, },
|
||||
{ "tx_request_queue", dwc3_tx_request_queue_show, },
|
||||
{ "rx_request_queue", dwc3_rx_request_queue_show, },
|
||||
{ "rx_info_queue", dwc3_rx_info_queue_show, },
|
||||
{ "descriptor_fetch_queue", dwc3_descriptor_fetch_queue_show, },
|
||||
{ "event_queue", dwc3_event_queue_show, },
|
||||
{ "transfer_type", dwc3_ep_transfer_type_show, },
|
||||
{ "trb_ring", dwc3_ep_trb_ring_show, },
|
||||
};
|
||||
|
||||
static int dwc3_endpoint_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const char *file_name = file_dentry(file)->d_iname;
|
||||
struct dwc3_ep_file_map *f_map;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(map); i++) {
|
||||
f_map = &map[i];
|
||||
|
||||
if (strcmp(f_map->name, file_name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return single_open(file, f_map->show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_endpoint_fops = {
|
||||
.open = dwc3_endpoint_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_file(struct dwc3_ep *dep,
|
||||
struct dentry *parent, int type)
|
||||
{
|
||||
struct dentry *file;
|
||||
struct dwc3_ep_file_map *ep_file = &map[type];
|
||||
|
||||
file = debugfs_create_file(ep_file->name, S_IRUGO, parent, dep,
|
||||
&dwc3_endpoint_fops);
|
||||
}
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
|
||||
struct dentry *parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(map); i++)
|
||||
dwc3_debugfs_create_endpoint_file(dep, parent, i);
|
||||
}
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep,
|
||||
struct dentry *parent)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
dir = debugfs_create_dir(dep->name, parent);
|
||||
if (IS_ERR_OR_NULL(dir))
|
||||
return;
|
||||
|
||||
dwc3_debugfs_create_endpoint_files(dep, dir);
|
||||
}
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_dirs(struct dwc3 *dwc,
|
||||
struct dentry *parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_in_eps; i++) {
|
||||
u8 epnum = (i << 1) | 1;
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
dwc3_debugfs_create_endpoint_dir(dep, parent);
|
||||
}
|
||||
|
||||
for (i = 0; i < dwc->num_out_eps; i++) {
|
||||
u8 epnum = (i << 1);
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
dwc3_debugfs_create_endpoint_dir(dep, parent);
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct dentry *file;
|
||||
int ret;
|
||||
|
||||
root = debugfs_create_dir(dev_name(dwc->dev), NULL);
|
||||
if (!root) {
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
if (IS_ERR_OR_NULL(root)) {
|
||||
if (!root)
|
||||
dev_err(dwc->dev, "Can't create debugfs root\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dwc->root = root;
|
||||
|
||||
dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
|
||||
if (!dwc->regset) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
debugfs_remove_recursive(root);
|
||||
return;
|
||||
}
|
||||
|
||||
dwc->regset->regs = dwc3_regs;
|
||||
|
@ -643,47 +942,30 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
dwc->regset->base = dwc->regs;
|
||||
|
||||
file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
if (!file)
|
||||
dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
|
||||
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_mode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
if (!file)
|
||||
dev_dbg(dwc->dev, "Can't create debugfs mode\n");
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
|
||||
IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
|
||||
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_testmode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
if (!file)
|
||||
dev_dbg(dwc->dev, "Can't create debugfs testmode\n");
|
||||
|
||||
file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR,
|
||||
root, dwc, &dwc3_link_state_fops);
|
||||
if (!file)
|
||||
dev_dbg(dwc->dev, "Can't create debugfs link_state\n");
|
||||
|
||||
dwc3_debugfs_create_endpoint_dirs(dwc, root);
|
||||
}
|
||||
|
||||
file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_link_state_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
kfree(dwc->regset);
|
||||
|
||||
err1:
|
||||
debugfs_remove_recursive(root);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dwc3_debugfs_exit(struct dwc3 *dwc)
|
||||
|
|
|
@ -126,8 +126,6 @@ struct dwc3_omap {
|
|||
u32 debug_offset;
|
||||
u32 irq0_offset;
|
||||
|
||||
u32 dma_status:1;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct notifier_block vbus_nb;
|
||||
struct notifier_block id_nb;
|
||||
|
@ -277,9 +275,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
|||
|
||||
reg = dwc3_omap_read_irqmisc_status(omap);
|
||||
|
||||
if (reg & USBOTGSS_IRQMISC_DMADISABLECLR)
|
||||
omap->dma_status = false;
|
||||
|
||||
dwc3_omap_write_irqmisc_status(omap, reg);
|
||||
|
||||
reg = dwc3_omap_read_irq0_status(omap);
|
||||
|
@ -331,8 +326,6 @@ static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
|
|||
dwc3_omap_write_irqmisc_clr(omap, reg);
|
||||
}
|
||||
|
||||
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
static int dwc3_omap_id_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
|
@ -490,7 +483,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
omap->irq = irq;
|
||||
omap->base = base;
|
||||
omap->vbus_reg = vbus_reg;
|
||||
dev->dma_mask = &dwc3_omap_dma_mask;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
|
@ -504,7 +496,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
|
|
|
@ -47,7 +47,7 @@ static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
|
|||
{ },
|
||||
};
|
||||
|
||||
static int dwc3_pci_quirks(struct pci_dev *pdev)
|
||||
static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
|
||||
{
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
|
||||
|
@ -77,8 +77,7 @@ static int dwc3_pci_quirks(struct pci_dev *pdev)
|
|||
pdata.dis_u3_susphy_quirk = true;
|
||||
pdata.dis_u2_susphy_quirk = true;
|
||||
|
||||
return platform_device_add_data(pci_get_drvdata(pdev), &pdata,
|
||||
sizeof(pdata));
|
||||
return platform_device_add_data(dwc3, &pdata, sizeof(pdata));
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
|
@ -123,8 +122,7 @@ static int dwc3_pci_quirks(struct pci_dev *pdev)
|
|||
pdata.has_lpm_erratum = true;
|
||||
pdata.dis_enblslpm_quirk = true;
|
||||
|
||||
return platform_device_add_data(pci_get_drvdata(pdev), &pdata,
|
||||
sizeof(pdata));
|
||||
return platform_device_add_data(dwc3, &pdata, sizeof(pdata));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -169,20 +167,20 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
return ret;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, dwc3);
|
||||
ret = dwc3_pci_quirks(pci);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dwc3->dev.parent = dev;
|
||||
ACPI_COMPANION_SET(&dwc3->dev, ACPI_COMPANION(dev));
|
||||
|
||||
ret = dwc3_pci_quirks(pci, dwc3);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, dwc3);
|
||||
return 0;
|
||||
err:
|
||||
platform_device_put(dwc3);
|
||||
|
|
|
@ -70,10 +70,10 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
|||
return 0;
|
||||
}
|
||||
|
||||
trb = &dwc->ep0_trb[dep->free_slot];
|
||||
trb = &dwc->ep0_trb[dep->trb_enqueue];
|
||||
|
||||
if (chain)
|
||||
dep->free_slot++;
|
||||
dep->trb_enqueue++;
|
||||
|
||||
trb->bpl = lower_32_bits(buf_dma);
|
||||
trb->bph = upper_32_bits(buf_dma);
|
||||
|
@ -124,7 +124,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
|||
req->request.status = -EINPROGRESS;
|
||||
req->epnum = dep->number;
|
||||
|
||||
list_add_tail(&req->list, &dep->request_list);
|
||||
list_add_tail(&req->list, &dep->pending_list);
|
||||
|
||||
/*
|
||||
* Gadget driver might not be quick enough to queue a request
|
||||
|
@ -240,7 +240,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
}
|
||||
|
||||
/* we share one TRB for ep0/1 */
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
if (!list_empty(&dep->pending_list)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
@ -272,10 +272,10 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
|
|||
dep->flags = DWC3_EP_ENABLED;
|
||||
dwc->delayed_status = false;
|
||||
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
if (!list_empty(&dep->pending_list)) {
|
||||
struct dwc3_request *req;
|
||||
|
||||
req = next_request(&dep->request_list);
|
||||
req = next_request(&dep->pending_list);
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
}
|
||||
|
||||
|
@ -463,6 +463,12 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
if (!set)
|
||||
return -EINVAL;
|
||||
|
||||
switch (wIndex >> 8) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
dwc->test_mode_nr = wIndex >> 8;
|
||||
dwc->test_mode = true;
|
||||
break;
|
||||
|
@ -470,6 +476,10 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RECIP_INTERFACE:
|
||||
switch (wValue) {
|
||||
|
@ -586,9 +596,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
dwc->resize_fifos = true;
|
||||
dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -809,7 +816,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
|
||||
trace_dwc3_complete_trb(ep0, trb);
|
||||
|
||||
r = next_request(&ep0->request_list);
|
||||
r = next_request(&ep0->pending_list);
|
||||
if (!r)
|
||||
return;
|
||||
|
||||
|
@ -848,7 +855,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
trb++;
|
||||
length = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
ep0->free_slot = 0;
|
||||
ep0->trb_enqueue = 0;
|
||||
}
|
||||
|
||||
transfer_size = roundup((ur->length - transfer_size),
|
||||
|
@ -897,8 +904,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
|||
|
||||
trace_dwc3_complete_trb(dep, trb);
|
||||
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
r = next_request(&dep->request_list);
|
||||
if (!list_empty(&dep->pending_list)) {
|
||||
r = next_request(&dep->pending_list);
|
||||
|
||||
dwc3_gadget_giveback(dep, r, 0);
|
||||
}
|
||||
|
@ -1027,12 +1034,6 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
|||
|
||||
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
if (dwc->resize_fifos) {
|
||||
dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
|
||||
dwc3_gadget_resize_tx_fifos(dwc);
|
||||
dwc->resize_fifos = 0;
|
||||
}
|
||||
|
||||
WARN_ON(dwc3_ep0_start_control_status(dep));
|
||||
}
|
||||
|
||||
|
|
|
@ -145,90 +145,21 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
|
||||
* @dwc: pointer to our context structure
|
||||
*
|
||||
* This function will a best effort FIFO allocation in order
|
||||
* to improve FIFO usage and throughput, while still allowing
|
||||
* us to enable as many endpoints as possible.
|
||||
*
|
||||
* Keep in mind that this operation will be highly dependent
|
||||
* on the configured size for RAM1 - which contains TxFifo -,
|
||||
* the amount of endpoints enabled on coreConsultant tool, and
|
||||
* the width of the Master Bus.
|
||||
*
|
||||
* In the ideal world, we would always be able to satisfy the
|
||||
* following equation:
|
||||
*
|
||||
* ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \
|
||||
* (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes
|
||||
*
|
||||
* Unfortunately, due to many variables that's not always the case.
|
||||
*/
|
||||
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
|
||||
static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
|
||||
{
|
||||
int last_fifo_depth = 0;
|
||||
int ram1_depth;
|
||||
int fifo_size;
|
||||
int mdwidth;
|
||||
int num;
|
||||
|
||||
if (!dwc->needs_fifo_resize)
|
||||
return 0;
|
||||
|
||||
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
|
||||
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||
mdwidth >>= 3;
|
||||
|
||||
/*
|
||||
* FIXME For now we will only allocate 1 wMaxPacketSize space
|
||||
* for each enabled endpoint, later patches will come to
|
||||
* improve this algorithm so that we better use the internal
|
||||
* FIFO space
|
||||
*/
|
||||
for (num = 0; num < dwc->num_in_eps; num++) {
|
||||
/* bit0 indicates direction; 1 means IN ep */
|
||||
struct dwc3_ep *dep = dwc->eps[(num << 1) | 1];
|
||||
int mult = 1;
|
||||
int tmp;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
|
||||
|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
mult = 3;
|
||||
|
||||
/*
|
||||
* REVISIT: the following assumes we will always have enough
|
||||
* space available on the FIFO RAM for all possible use cases.
|
||||
* Make sure that's true somehow and change FIFO allocation
|
||||
* accordingly.
|
||||
*
|
||||
* If we have Bulk or Isochronous endpoints, we want
|
||||
* them to be able to be very, very fast. So we're giving
|
||||
* those endpoints a fifo_size which is enough for 3 full
|
||||
* packets
|
||||
*/
|
||||
tmp = mult * (dep->endpoint.maxpacket + mdwidth);
|
||||
tmp += mdwidth;
|
||||
|
||||
fifo_size = DIV_ROUND_UP(tmp, mdwidth);
|
||||
|
||||
fifo_size |= (last_fifo_depth << 16);
|
||||
|
||||
dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d",
|
||||
dep->name, last_fifo_depth, fifo_size & 0xffff);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
|
||||
|
||||
last_fifo_depth += (fifo_size & 0xffff);
|
||||
dep->trb_enqueue++;
|
||||
dep->trb_enqueue %= DWC3_TRB_NUM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
|
||||
{
|
||||
dep->trb_dequeue++;
|
||||
dep->trb_dequeue %= DWC3_TRB_NUM;
|
||||
}
|
||||
|
||||
static int dwc3_ep_is_last_trb(unsigned int index)
|
||||
{
|
||||
return index == DWC3_TRB_NUM - 1;
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
|
@ -237,21 +168,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
int i;
|
||||
|
||||
if (req->queued) {
|
||||
if (req->started) {
|
||||
i = 0;
|
||||
do {
|
||||
dep->busy_slot++;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
/*
|
||||
* Skip LINK TRB. We can't use req->trb and check for
|
||||
* DWC3_TRBCTL_LINK_TRB because it points the TRB we
|
||||
* just completed (not the LINK TRB).
|
||||
*/
|
||||
if (((dep->busy_slot & DWC3_TRB_MASK) ==
|
||||
DWC3_TRB_NUM- 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
dep->busy_slot++;
|
||||
if (dwc3_ep_is_last_trb(dep->trb_dequeue))
|
||||
dwc3_ep_inc_deq(dep);
|
||||
} while(++i < req->request.num_mapped_sgs);
|
||||
req->queued = false;
|
||||
req->started = false;
|
||||
}
|
||||
list_del(&req->list);
|
||||
req->trb = NULL;
|
||||
|
@ -307,6 +236,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
|
|||
} while (1);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
|
||||
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
{
|
||||
|
@ -314,8 +245,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
int susphy = false;
|
||||
int ret = -EINVAL;
|
||||
|
||||
trace_dwc3_gadget_ep_cmd(dep, cmd, params);
|
||||
|
||||
/*
|
||||
* Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
|
||||
* we're issuing an endpoint command, we must check if
|
||||
* GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
|
||||
*
|
||||
* We will also set SUSPHY bit to what it was before returning as stated
|
||||
* by the same section on Synopsys databook.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
|
||||
susphy = true;
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
|
||||
int needs_wakeup;
|
||||
|
||||
needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
|
||||
dwc->link_state == DWC3_LINK_STATE_U2 ||
|
||||
dwc->link_state == DWC3_LINK_STATE_U3);
|
||||
|
||||
if (unlikely(needs_wakeup)) {
|
||||
ret = __dwc3_gadget_wakeup(dwc);
|
||||
dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
|
||||
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
|
||||
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2);
|
||||
|
@ -324,12 +287,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
|
||||
if (!(reg & DWC3_DEPCMD_CMDACT)) {
|
||||
int cmd_status = DWC3_DEPCMD_STATUS(reg);
|
||||
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"Command Complete --> %d",
|
||||
DWC3_DEPCMD_STATUS(reg));
|
||||
if (DWC3_DEPCMD_STATUS(reg))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
cmd_status);
|
||||
|
||||
switch (cmd_status) {
|
||||
case 0:
|
||||
ret = 0;
|
||||
break;
|
||||
case DEPEVT_TRANSFER_NO_RESOURCE:
|
||||
dwc3_trace(trace_dwc3_gadget, "%s: no resource available");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case DEPEVT_TRANSFER_BUS_EXPIRY:
|
||||
/*
|
||||
* SW issues START TRANSFER command to
|
||||
* isochronous ep with future frame interval. If
|
||||
* future interval time has already passed when
|
||||
* core receives the command, it will respond
|
||||
* with an error status of 'Bus Expiry'.
|
||||
*
|
||||
* Instead of always returning -EINVAL, let's
|
||||
* give a hint to the gadget driver that this is
|
||||
* the case by returning -EAGAIN.
|
||||
*/
|
||||
dwc3_trace(trace_dwc3_gadget, "%s: bus expiry");
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
default:
|
||||
dev_WARN(dwc->dev, "UNKNOWN cmd status\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -340,11 +331,20 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|||
if (!timeout) {
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"Command Timed Out");
|
||||
return -ETIMEDOUT;
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
} while (1);
|
||||
|
||||
if (unlikely(susphy)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
|
||||
|
@ -464,9 +464,19 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
|
||||
/* Burst size is only needed in SuperSpeed mode */
|
||||
if (dwc->gadget.speed >= USB_SPEED_SUPER) {
|
||||
u32 burst = dep->endpoint.maxburst - 1;
|
||||
u32 burst = dep->endpoint.maxburst;
|
||||
u32 nump;
|
||||
u32 reg;
|
||||
|
||||
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
|
||||
/* update NumP */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
nump = DWC3_DCFG_NUMP(reg);
|
||||
nump = max(nump, burst);
|
||||
reg &= ~DWC3_DCFG_NUMP_MASK;
|
||||
reg |= nump << DWC3_DCFG_NUMP_SHIFT;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
|
||||
}
|
||||
|
||||
if (ignore)
|
||||
|
@ -567,10 +577,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
reg |= DWC3_DALEPENA_EP(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
|
||||
if (!usb_endpoint_xfer_isoc(desc))
|
||||
if (usb_endpoint_xfer_control(desc))
|
||||
goto out;
|
||||
|
||||
/* Link TRB for ISOC. The HWO bit is never reset */
|
||||
/* Link TRB. The HWO bit is never reset */
|
||||
trb_st_hw = &dep->trb_pool[0];
|
||||
|
||||
trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
|
||||
|
@ -608,19 +618,19 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
{
|
||||
struct dwc3_request *req;
|
||||
|
||||
if (!list_empty(&dep->req_queued)) {
|
||||
if (!list_empty(&dep->started_list)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
|
||||
/* - giveback all requests to gadget driver */
|
||||
while (!list_empty(&dep->req_queued)) {
|
||||
req = next_request(&dep->req_queued);
|
||||
while (!list_empty(&dep->started_list)) {
|
||||
req = next_request(&dep->started_list);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
while (!list_empty(&dep->request_list)) {
|
||||
req = next_request(&dep->request_list);
|
||||
while (!list_empty(&dep->pending_list)) {
|
||||
req = next_request(&dep->pending_list);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
||||
}
|
||||
|
@ -783,20 +793,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
chain ? " chain" : "");
|
||||
|
||||
|
||||
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
|
||||
if (!req->trb) {
|
||||
dwc3_gadget_move_request_queued(req);
|
||||
dwc3_gadget_move_started_request(req);
|
||||
req->trb = trb;
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
||||
req->start_slot = dep->free_slot & DWC3_TRB_MASK;
|
||||
req->first_trb_index = dep->trb_enqueue;
|
||||
}
|
||||
|
||||
dep->free_slot++;
|
||||
/* Skip the LINK-TRB on ISOC */
|
||||
if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
dep->free_slot++;
|
||||
dwc3_ep_inc_enq(dep);
|
||||
/* Skip the LINK-TRB */
|
||||
if (dwc3_ep_is_last_trb(dep->trb_enqueue))
|
||||
dwc3_ep_inc_enq(dep);
|
||||
|
||||
trb->size = DWC3_TRB_SIZE_LENGTH(length);
|
||||
trb->bpl = lower_32_bits(dma);
|
||||
|
@ -812,6 +821,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
else
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
|
||||
|
||||
/* always enable Interrupt on Missed ISOC */
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
|
@ -826,15 +838,14 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
BUG();
|
||||
}
|
||||
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
/* always enable Continue on Short Packet */
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
} else if (last) {
|
||||
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
|
||||
|
||||
if (last)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_LST;
|
||||
}
|
||||
|
||||
if (chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CHN;
|
||||
|
@ -860,55 +871,29 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|||
{
|
||||
struct dwc3_request *req, *n;
|
||||
u32 trbs_left;
|
||||
u32 max;
|
||||
unsigned int last_one = 0;
|
||||
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
|
||||
|
||||
/* the first request must not be queued */
|
||||
trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
|
||||
|
||||
/* Can't wrap around on a non-isoc EP since there's no link TRB */
|
||||
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
|
||||
if (trbs_left > max)
|
||||
trbs_left = max;
|
||||
}
|
||||
trbs_left = dep->trb_dequeue - dep->trb_enqueue;
|
||||
|
||||
/*
|
||||
* If busy & slot are equal than it is either full or empty. If we are
|
||||
* starting to process requests then we are empty. Otherwise we are
|
||||
* If enqueue & dequeue are equal than it is either full or empty. If we
|
||||
* are starting to process requests then we are empty. Otherwise we are
|
||||
* full and don't do anything
|
||||
*/
|
||||
if (!trbs_left) {
|
||||
if (!starting)
|
||||
return;
|
||||
|
||||
trbs_left = DWC3_TRB_NUM;
|
||||
/*
|
||||
* In case we start from scratch, we queue the ISOC requests
|
||||
* starting from slot 1. This is done because we use ring
|
||||
* buffer and have no LST bit to stop us. Instead, we place
|
||||
* IOC bit every TRB_NUM/4. We try to avoid having an interrupt
|
||||
* after the first request so we start at slot 1 and have
|
||||
* 7 requests proceed before we hit the first IOC.
|
||||
* Other transfer types don't use the ring buffer and are
|
||||
* processed from the first TRB until the last one. Since we
|
||||
* don't wrap around we have to start at the beginning.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dep->busy_slot = 1;
|
||||
dep->free_slot = 1;
|
||||
} else {
|
||||
dep->busy_slot = 0;
|
||||
dep->free_slot = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The last TRB is a link TRB, not used for xfer */
|
||||
if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
if (trbs_left <= 1)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(req, n, &dep->request_list, list) {
|
||||
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
|
||||
unsigned length;
|
||||
dma_addr_t dma;
|
||||
last_one = false;
|
||||
|
@ -927,7 +912,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|||
|
||||
if (i == (request->num_mapped_sgs - 1) ||
|
||||
sg_is_last(s)) {
|
||||
if (list_empty(&dep->request_list))
|
||||
if (list_empty(&dep->pending_list))
|
||||
last_one = true;
|
||||
chain = false;
|
||||
}
|
||||
|
@ -957,7 +942,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|||
last_one = 1;
|
||||
|
||||
/* Is this the last request? */
|
||||
if (list_is_last(&req->list, &dep->request_list))
|
||||
if (list_is_last(&req->list, &dep->pending_list))
|
||||
last_one = 1;
|
||||
|
||||
dwc3_prepare_one_trb(dep, req, dma, length,
|
||||
|
@ -988,18 +973,18 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|||
* new requests as we try to set the IOC bit only on the last request.
|
||||
*/
|
||||
if (start_new) {
|
||||
if (list_empty(&dep->req_queued))
|
||||
if (list_empty(&dep->started_list))
|
||||
dwc3_prepare_trbs(dep, start_new);
|
||||
|
||||
/* req points to the first request which will be sent */
|
||||
req = next_request(&dep->req_queued);
|
||||
req = next_request(&dep->started_list);
|
||||
} else {
|
||||
dwc3_prepare_trbs(dep, start_new);
|
||||
|
||||
/*
|
||||
* req points to the first request where HWO changed from 0 to 1
|
||||
*/
|
||||
req = next_request(&dep->req_queued);
|
||||
req = next_request(&dep->started_list);
|
||||
}
|
||||
if (!req) {
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
|
@ -1046,7 +1031,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|||
{
|
||||
u32 uf;
|
||||
|
||||
if (list_empty(&dep->request_list)) {
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"ISOC ep %s run out for requests",
|
||||
dep->name);
|
||||
|
@ -1114,7 +1099,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_add_tail(&req->list, &dep->request_list);
|
||||
list_add_tail(&req->list, &dep->pending_list);
|
||||
|
||||
/*
|
||||
* If there are no pending requests and the endpoint isn't already
|
||||
|
@ -1149,7 +1134,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
* notion of current microframe.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
if (list_empty(&dep->req_queued)) {
|
||||
if (list_empty(&dep->started_list)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
|
@ -1267,13 +1252,13 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &dep->request_list, list) {
|
||||
list_for_each_entry(r, &dep->pending_list, list) {
|
||||
if (r == req)
|
||||
break;
|
||||
}
|
||||
|
||||
if (r != req) {
|
||||
list_for_each_entry(r, &dep->req_queued, list) {
|
||||
list_for_each_entry(r, &dep->started_list, list) {
|
||||
if (r == req)
|
||||
break;
|
||||
}
|
||||
|
@ -1313,10 +1298,10 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|||
|
||||
if (value) {
|
||||
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
|
||||
(!list_empty(&dep->req_queued) ||
|
||||
!list_empty(&dep->request_list)))) {
|
||||
(!list_empty(&dep->started_list) ||
|
||||
!list_empty(&dep->pending_list)))) {
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: pending request, cannot halt\n",
|
||||
"%s: pending request, cannot halt",
|
||||
dep->name);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
@ -1417,22 +1402,16 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
|
|||
return DWC3_DSTS_SOFFN(reg);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
|
||||
unsigned long timeout;
|
||||
unsigned long flags;
|
||||
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
u8 link_state;
|
||||
u8 speed;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
/*
|
||||
* According to the Databook Remote wakeup request should
|
||||
* be issued only when the device is in early suspend state.
|
||||
|
@ -1445,8 +1424,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
if ((speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
link_state = DWC3_DSTS_USBLNKST(reg);
|
||||
|
@ -1459,14 +1437,13 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
dwc3_trace(trace_dwc3_gadget,
|
||||
"can't wakeup from '%s'\n",
|
||||
dwc3_gadget_link_string(link_state));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
|
||||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "failed to put link in Recovery\n");
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Recent versions do this automatically */
|
||||
|
@ -1490,10 +1467,20 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
|
||||
if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
|
||||
dev_err(dwc->dev, "failed to send remote wakeup\n");
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_wakeup(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -1620,7 +1607,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
|
||||
IRQF_SHARED, "dwc3", dwc);
|
||||
IRQF_SHARED, "dwc3", dwc->ev_buf);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
|
@ -1682,6 +1669,17 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
}
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
/*
|
||||
* We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP
|
||||
* field instead of letting dwc3 itself calculate that automatically.
|
||||
*
|
||||
* This way, we maximize the chances that we'll be able to get several
|
||||
* bursts of data without going through any sort of endpoint throttling.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
|
||||
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
|
||||
/* Start with SuperSpeed Default */
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
|
@ -1720,7 +1718,7 @@ err2:
|
|||
err1:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
free_irq(irq, dwc);
|
||||
free_irq(irq, dwc->ev_buf);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
|
@ -1743,7 +1741,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
|||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
free_irq(irq, dwc);
|
||||
free_irq(irq, dwc->ev_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1815,8 +1813,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|||
dep->endpoint.caps.dir_in = !!direction;
|
||||
dep->endpoint.caps.dir_out = !direction;
|
||||
|
||||
INIT_LIST_HEAD(&dep->request_list);
|
||||
INIT_LIST_HEAD(&dep->req_queued);
|
||||
INIT_LIST_HEAD(&dep->pending_list);
|
||||
INIT_LIST_HEAD(&dep->started_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1913,11 +1911,11 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
* If there are still queued request
|
||||
* then wait, do not issue either END
|
||||
* or UPDATE TRANSFER, just attach next
|
||||
* request in request_list during
|
||||
* request in pending_list during
|
||||
* giveback.If any future queued request
|
||||
* is successfully transferred then we
|
||||
* will issue UPDATE TRANSFER for all
|
||||
* request in the request_list.
|
||||
* request in the pending_list.
|
||||
*/
|
||||
dep->flags |= DWC3_EP_MISSED_ISOC;
|
||||
} else {
|
||||
|
@ -1963,15 +1961,14 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
int ret;
|
||||
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
req = next_request(&dep->started_list);
|
||||
if (WARN_ON_ONCE(!req))
|
||||
return 1;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
slot = req->start_slot + i;
|
||||
if ((slot == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
slot = req->first_trb_index + i;
|
||||
if (slot == DWC3_TRB_NUM - 1)
|
||||
slot++;
|
||||
slot %= DWC3_TRB_NUM;
|
||||
trb = &dep->trb_pool[slot];
|
||||
|
@ -1989,8 +1986,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
} while (1);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
list_empty(&dep->req_queued)) {
|
||||
if (list_empty(&dep->request_list)) {
|
||||
list_empty(&dep->started_list)) {
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
/*
|
||||
* If there is no entry in request list then do
|
||||
* not issue END TRANSFER now. Just set PENDING
|
||||
|
@ -2039,7 +2036,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
|||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
if (!list_empty(&dep->req_queued))
|
||||
if (!list_empty(&dep->started_list))
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2686,14 +2683,13 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
|
|||
}
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
|
||||
static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
struct dwc3 *dwc = evt->dwc;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int left;
|
||||
u32 reg;
|
||||
|
||||
evt = dwc->ev_buffs[buf];
|
||||
left = evt->count;
|
||||
|
||||
if (!(evt->flags & DWC3_EVENT_PENDING))
|
||||
|
@ -2718,7 +2714,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
|
|||
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
|
||||
left -= 4;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4);
|
||||
}
|
||||
|
||||
evt->count = 0;
|
||||
|
@ -2726,39 +2722,34 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
|
|||
ret = IRQ_HANDLED;
|
||||
|
||||
/* Unmask interrupt */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
|
||||
reg &= ~DWC3_GEVNTSIZ_INTMASK;
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
|
||||
{
|
||||
struct dwc3 *dwc = _dwc;
|
||||
struct dwc3_event_buffer *evt = _evt;
|
||||
struct dwc3 *dwc = evt->dwc;
|
||||
unsigned long flags;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
for (i = 0; i < dwc->num_event_buffers; i++)
|
||||
ret |= dwc3_process_event_buf(dwc, i);
|
||||
|
||||
ret = dwc3_process_event_buf(evt);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
|
||||
static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
struct dwc3 *dwc = evt->dwc;
|
||||
u32 count;
|
||||
u32 reg;
|
||||
|
||||
evt = dwc->ev_buffs[buf];
|
||||
|
||||
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
|
||||
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
|
||||
count &= DWC3_GEVNTCOUNT_MASK;
|
||||
if (!count)
|
||||
return IRQ_NONE;
|
||||
|
@ -2767,28 +2758,18 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
|
|||
evt->flags |= DWC3_EVENT_PENDING;
|
||||
|
||||
/* Mask interrupt */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
|
||||
reg |= DWC3_GEVNTSIZ_INTMASK;
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_evt)
|
||||
{
|
||||
struct dwc3 *dwc = _dwc;
|
||||
int i;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct dwc3_event_buffer *evt = _evt;
|
||||
|
||||
for (i = 0; i < dwc->num_event_buffers; i++) {
|
||||
irqreturn_t status;
|
||||
|
||||
status = dwc3_check_event_buf(dwc, i);
|
||||
if (status == IRQ_WAKE_THREAD)
|
||||
ret = status;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return dwc3_check_event_buf(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,12 +68,12 @@ static inline struct dwc3_request *next_request(struct list_head *list)
|
|||
return list_first_entry(list, struct dwc3_request, list);
|
||||
}
|
||||
|
||||
static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
|
||||
static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3_ep *dep = req->dep;
|
||||
|
||||
req->queued = true;
|
||||
list_move_tail(&req->list, &dep->req_queued);
|
||||
req->started = true;
|
||||
list_move_tail(&req->list, &dep->started_list);
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
struct dwc3_platform_data {
|
||||
enum usb_device_speed maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
bool tx_fifo_resize;
|
||||
bool usb3_lpm_capable;
|
||||
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
|
@ -43,6 +42,7 @@ struct dwc3_platform_data {
|
|||
unsigned dis_u3_susphy_quirk:1;
|
||||
unsigned dis_u2_susphy_quirk:1;
|
||||
unsigned dis_enblslpm_quirk:1;
|
||||
unsigned dis_rxdet_inp3_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
menuconfig USB_GADGET
|
||||
tristate "USB Gadget Support"
|
||||
select USB_COMMON
|
||||
select NLS
|
||||
help
|
||||
USB is a master/slave protocol, organized with one master
|
||||
|
|
|
@ -66,20 +66,36 @@ function_descriptors(struct usb_function *f,
|
|||
{
|
||||
struct usb_descriptor_header **descriptors;
|
||||
|
||||
/*
|
||||
* NOTE: we try to help gadget drivers which might not be setting
|
||||
* max_speed appropriately.
|
||||
*/
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
descriptors = f->ssp_descriptors;
|
||||
if (descriptors)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
if (descriptors)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
if (descriptors)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we can't find any descriptors at all, then this gadget deserves to
|
||||
* Oops with a NULL pointer dereference
|
||||
*/
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
|
|
|
@ -651,7 +651,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
|
|||
if (io_data->read && ret > 0) {
|
||||
use_mm(io_data->mm);
|
||||
ret = copy_to_iter(io_data->buf, ret, &io_data->data);
|
||||
if (iov_iter_count(&io_data->data))
|
||||
if (ret != io_data->req->actual && iov_iter_count(&io_data->data))
|
||||
ret = -EFAULT;
|
||||
unuse_mm(io_data->mm);
|
||||
}
|
||||
|
|
|
@ -2977,25 +2977,6 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
|
||||
|
||||
int fsg_common_run_thread(struct fsg_common *common)
|
||||
{
|
||||
common->state = FSG_STATE_IDLE;
|
||||
/* Tell the thread to start working */
|
||||
common->thread_task =
|
||||
kthread_create(fsg_main_thread, common, "file-storage");
|
||||
if (IS_ERR(common->thread_task)) {
|
||||
common->state = FSG_STATE_TERMINATED;
|
||||
return PTR_ERR(common->thread_task);
|
||||
}
|
||||
|
||||
DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
|
||||
|
||||
wake_up_process(common->thread_task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsg_common_run_thread);
|
||||
|
||||
static void fsg_common_release(struct kref *ref)
|
||||
{
|
||||
struct fsg_common *common = container_of(ref, struct fsg_common, ref);
|
||||
|
@ -3005,6 +2986,7 @@ static void fsg_common_release(struct kref *ref)
|
|||
if (common->state != FSG_STATE_TERMINATED) {
|
||||
raise_exception(common, FSG_STATE_EXIT);
|
||||
wait_for_completion(&common->thread_notifier);
|
||||
common->thread_task = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
|
||||
|
@ -3050,10 +3032,22 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (ret)
|
||||
return ret;
|
||||
fsg_common_set_inquiry_string(fsg->common, NULL, NULL);
|
||||
ret = fsg_common_run_thread(fsg->common);
|
||||
if (ret)
|
||||
}
|
||||
|
||||
if (!common->thread_task) {
|
||||
common->state = FSG_STATE_IDLE;
|
||||
common->thread_task =
|
||||
kthread_create(fsg_main_thread, common, "file-storage");
|
||||
if (IS_ERR(common->thread_task)) {
|
||||
int ret = PTR_ERR(common->thread_task);
|
||||
common->thread_task = NULL;
|
||||
common->state = FSG_STATE_TERMINATED;
|
||||
return ret;
|
||||
}
|
||||
DBG(common, "I/O thread pid: %d\n",
|
||||
task_pid_nr(common->thread_task));
|
||||
wake_up_process(common->thread_task);
|
||||
}
|
||||
|
||||
fsg->gadget = gadget;
|
||||
|
||||
|
|
|
@ -153,8 +153,6 @@ int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
|
|||
void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
|
||||
const char *pn);
|
||||
|
||||
int fsg_common_run_thread(struct fsg_common *common);
|
||||
|
||||
void fsg_config_from_params(struct fsg_config *cfg,
|
||||
const struct fsg_module_parameters *params,
|
||||
unsigned int fsg_num_buffers);
|
||||
|
|
|
@ -133,10 +133,6 @@ static int acm_ms_do_config(struct usb_configuration *c)
|
|||
if (status < 0)
|
||||
goto put_msg;
|
||||
|
||||
status = fsg_common_run_thread(opts->common);
|
||||
if (status)
|
||||
goto remove_acm;
|
||||
|
||||
status = usb_add_function(c, f_msg);
|
||||
if (status)
|
||||
goto remove_acm;
|
||||
|
|
|
@ -132,10 +132,6 @@ static int msg_do_config(struct usb_configuration *c)
|
|||
if (IS_ERR(f_msg))
|
||||
return PTR_ERR(f_msg);
|
||||
|
||||
ret = fsg_common_run_thread(opts->common);
|
||||
if (ret)
|
||||
goto put_func;
|
||||
|
||||
ret = usb_add_function(c, f_msg);
|
||||
if (ret)
|
||||
goto put_func;
|
||||
|
|
|
@ -137,7 +137,6 @@ static struct usb_function *f_msg_rndis;
|
|||
|
||||
static int rndis_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct fsg_opts *fsg_opts;
|
||||
int ret;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
|
@ -169,11 +168,6 @@ static int rndis_do_config(struct usb_configuration *c)
|
|||
goto err_fsg;
|
||||
}
|
||||
|
||||
fsg_opts = fsg_opts_from_func_inst(fi_msg);
|
||||
ret = fsg_common_run_thread(fsg_opts->common);
|
||||
if (ret)
|
||||
goto err_run;
|
||||
|
||||
ret = usb_add_function(c, f_msg_rndis);
|
||||
if (ret)
|
||||
goto err_run;
|
||||
|
@ -225,7 +219,6 @@ static struct usb_function *f_msg_multi;
|
|||
|
||||
static int cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct fsg_opts *fsg_opts;
|
||||
int ret;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
|
@ -258,11 +251,6 @@ static int cdc_do_config(struct usb_configuration *c)
|
|||
goto err_fsg;
|
||||
}
|
||||
|
||||
fsg_opts = fsg_opts_from_func_inst(fi_msg);
|
||||
ret = fsg_common_run_thread(fsg_opts->common);
|
||||
if (ret)
|
||||
goto err_run;
|
||||
|
||||
ret = usb_add_function(c, f_msg_multi);
|
||||
if (ret)
|
||||
goto err_run;
|
||||
|
|
|
@ -152,7 +152,6 @@ static int nokia_bind_config(struct usb_configuration *c)
|
|||
struct usb_function *f_ecm;
|
||||
struct usb_function *f_obex2 = NULL;
|
||||
struct usb_function *f_msg;
|
||||
struct fsg_opts *fsg_opts;
|
||||
int status = 0;
|
||||
int obex1_stat = -1;
|
||||
int obex2_stat = -1;
|
||||
|
@ -222,12 +221,6 @@ static int nokia_bind_config(struct usb_configuration *c)
|
|||
goto err_ecm;
|
||||
}
|
||||
|
||||
fsg_opts = fsg_opts_from_func_inst(fi_msg);
|
||||
|
||||
status = fsg_common_run_thread(fsg_opts->common);
|
||||
if (status)
|
||||
goto err_msg;
|
||||
|
||||
status = usb_add_function(c, f_msg);
|
||||
if (status)
|
||||
goto err_msg;
|
||||
|
|
|
@ -1726,10 +1726,7 @@ static int at91sam9261_udc_init(struct at91_udc *udc)
|
|||
|
||||
udc->matrix = syscon_regmap_lookup_by_phandle(udc->pdev->dev.of_node,
|
||||
"atmel,matrix");
|
||||
if (IS_ERR(udc->matrix))
|
||||
return PTR_ERR(udc->matrix);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(udc->matrix);
|
||||
}
|
||||
|
||||
static void at91sam9261_udc_pullup(struct at91_udc *udc, int is_on)
|
||||
|
|
|
@ -325,11 +325,8 @@ struct pch_vbus_gpio_data {
|
|||
* @pdev: reference to the PCI device
|
||||
* @ep: array of endpoints
|
||||
* @lock: protects all state
|
||||
* @active: enabled the PCI device
|
||||
* @stall: stall requested
|
||||
* @prot_stall: protcol stall requested
|
||||
* @irq_registered: irq registered with system
|
||||
* @mem_region: device memory mapped
|
||||
* @registered: driver registered with system
|
||||
* @suspended: driver in suspended state
|
||||
* @connected: gadget driver associated
|
||||
|
@ -339,12 +336,8 @@ struct pch_vbus_gpio_data {
|
|||
* @data_requests: DMA pool for data requests
|
||||
* @stp_requests: DMA pool for setup requests
|
||||
* @dma_addr: DMA pool for received
|
||||
* @ep0out_buf: Buffer for DMA
|
||||
* @setup_data: Received setup data
|
||||
* @phys_addr: of device memory
|
||||
* @base_addr: for mapped device memory
|
||||
* @bar: Indicates which PCI BAR for USB regs
|
||||
* @irq: IRQ line for the device
|
||||
* @cfg_data: current cfg, intf, and alt in use
|
||||
* @vbus_gpio: GPIO informaton for detecting VBUS
|
||||
*/
|
||||
|
@ -354,11 +347,9 @@ struct pch_udc_dev {
|
|||
struct pci_dev *pdev;
|
||||
struct pch_udc_ep ep[PCH_UDC_EP_NUM];
|
||||
spinlock_t lock; /* protects all state */
|
||||
unsigned active:1,
|
||||
unsigned
|
||||
stall:1,
|
||||
prot_stall:1,
|
||||
irq_registered:1,
|
||||
mem_region:1,
|
||||
suspended:1,
|
||||
connected:1,
|
||||
vbus_session:1,
|
||||
|
@ -367,12 +358,8 @@ struct pch_udc_dev {
|
|||
struct pci_pool *data_requests;
|
||||
struct pci_pool *stp_requests;
|
||||
dma_addr_t dma_addr;
|
||||
void *ep0out_buf;
|
||||
struct usb_ctrlrequest setup_data;
|
||||
unsigned long phys_addr;
|
||||
void __iomem *base_addr;
|
||||
unsigned bar;
|
||||
unsigned irq;
|
||||
struct pch_udc_cfg_data cfg_data;
|
||||
struct pch_vbus_gpio_data vbus_gpio;
|
||||
};
|
||||
|
@ -380,8 +367,10 @@ struct pch_udc_dev {
|
|||
|
||||
#define PCH_UDC_PCI_BAR_QUARK_X1000 0
|
||||
#define PCH_UDC_PCI_BAR 1
|
||||
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939
|
||||
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
#define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D
|
||||
#define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808
|
||||
|
@ -1732,14 +1721,12 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
|
|||
static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
|
||||
{
|
||||
struct pch_udc_ep *ep;
|
||||
struct pch_udc_dev *dev;
|
||||
unsigned long iflags;
|
||||
|
||||
if (!usbep)
|
||||
return -EINVAL;
|
||||
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if ((usbep->name == ep0_string) || !ep->ep.desc)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1770,12 +1757,10 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
|
|||
struct pch_udc_request *req;
|
||||
struct pch_udc_ep *ep;
|
||||
struct pch_udc_data_dma_desc *dma_desc;
|
||||
struct pch_udc_dev *dev;
|
||||
|
||||
if (!usbep)
|
||||
return NULL;
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
req = kzalloc(sizeof *req, gfp);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
@ -1948,12 +1933,10 @@ static int pch_udc_pcd_dequeue(struct usb_ep *usbep,
|
|||
{
|
||||
struct pch_udc_ep *ep;
|
||||
struct pch_udc_request *req;
|
||||
struct pch_udc_dev *dev;
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!usbep || !usbreq || (!ep->ep.desc && ep->num))
|
||||
return ret;
|
||||
req = container_of(usbreq, struct pch_udc_request, req);
|
||||
|
@ -1985,14 +1968,12 @@ static int pch_udc_pcd_dequeue(struct usb_ep *usbep,
|
|||
static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
|
||||
{
|
||||
struct pch_udc_ep *ep;
|
||||
struct pch_udc_dev *dev;
|
||||
unsigned long iflags;
|
||||
int ret;
|
||||
|
||||
if (!usbep)
|
||||
return -EINVAL;
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!ep->ep.desc && !ep->num)
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
|
@ -2030,14 +2011,12 @@ static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
|
|||
static int pch_udc_pcd_set_wedge(struct usb_ep *usbep)
|
||||
{
|
||||
struct pch_udc_ep *ep;
|
||||
struct pch_udc_dev *dev;
|
||||
unsigned long iflags;
|
||||
int ret;
|
||||
|
||||
if (!usbep)
|
||||
return -EINVAL;
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!ep->ep.desc && !ep->num)
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
|
@ -2647,7 +2626,7 @@ static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev)
|
|||
static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
|
||||
{
|
||||
u32 reg, dev_stat = 0;
|
||||
int i, ret;
|
||||
int i;
|
||||
|
||||
dev_stat = pch_udc_read_device_status(dev);
|
||||
dev->cfg_data.cur_intf = (dev_stat & UDC_DEVSTS_INTF_MASK) >>
|
||||
|
@ -2676,7 +2655,7 @@ static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
|
|||
}
|
||||
dev->stall = 0;
|
||||
spin_lock(&dev->lock);
|
||||
ret = dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
|
@ -2687,7 +2666,7 @@ static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
|
|||
*/
|
||||
static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
|
||||
{
|
||||
int i, ret;
|
||||
int i;
|
||||
u32 reg, dev_stat = 0;
|
||||
|
||||
dev_stat = pch_udc_read_device_status(dev);
|
||||
|
@ -2713,7 +2692,7 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
|
|||
|
||||
/* call gadget zero with setup data received */
|
||||
spin_lock(&dev->lock);
|
||||
ret = dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
|
@ -2855,17 +2834,6 @@ static void pch_udc_setup_ep0(struct pch_udc_dev *dev)
|
|||
UDC_DEVINT_SI | UDC_DEVINT_SC);
|
||||
}
|
||||
|
||||
/**
|
||||
* gadget_release() - Free the gadget driver private data
|
||||
* @pdev reference to struct pci_dev
|
||||
*/
|
||||
static void gadget_release(struct device *pdev)
|
||||
{
|
||||
struct pch_udc_dev *dev = dev_get_drvdata(pdev);
|
||||
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_udc_pcd_reinit() - This API initializes the endpoint structures
|
||||
* @dev: Reference to the driver structure
|
||||
|
@ -2949,6 +2917,7 @@ static int init_dma_pools(struct pch_udc_dev *dev)
|
|||
{
|
||||
struct pch_udc_stp_dma_desc *td_stp;
|
||||
struct pch_udc_data_dma_desc *td_data;
|
||||
void *ep0out_buf;
|
||||
|
||||
/* DMA setup */
|
||||
dev->data_requests = pci_pool_create("data_requests", dev->pdev,
|
||||
|
@ -2991,10 +2960,11 @@ static int init_dma_pools(struct pch_udc_dev *dev)
|
|||
dev->ep[UDC_EP0IN_IDX].td_data = NULL;
|
||||
dev->ep[UDC_EP0IN_IDX].td_data_phys = 0;
|
||||
|
||||
dev->ep0out_buf = kzalloc(UDC_EP0OUT_BUFF_SIZE * 4, GFP_KERNEL);
|
||||
if (!dev->ep0out_buf)
|
||||
ep0out_buf = devm_kzalloc(&dev->pdev->dev, UDC_EP0OUT_BUFF_SIZE * 4,
|
||||
GFP_KERNEL);
|
||||
if (!ep0out_buf)
|
||||
return -ENOMEM;
|
||||
dev->dma_addr = dma_map_single(&dev->pdev->dev, dev->ep0out_buf,
|
||||
dev->dma_addr = dma_map_single(&dev->pdev->dev, ep0out_buf,
|
||||
UDC_EP0OUT_BUFF_SIZE * 4,
|
||||
DMA_FROM_DEVICE);
|
||||
return 0;
|
||||
|
@ -3078,129 +3048,80 @@ static void pch_udc_remove(struct pci_dev *pdev)
|
|||
if (dev->dma_addr)
|
||||
dma_unmap_single(&dev->pdev->dev, dev->dma_addr,
|
||||
UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE);
|
||||
kfree(dev->ep0out_buf);
|
||||
|
||||
pch_vbus_gpio_free(dev);
|
||||
|
||||
pch_udc_exit(dev);
|
||||
|
||||
if (dev->irq_registered)
|
||||
free_irq(pdev->irq, dev);
|
||||
if (dev->base_addr)
|
||||
iounmap(dev->base_addr);
|
||||
if (dev->mem_region)
|
||||
release_mem_region(dev->phys_addr,
|
||||
pci_resource_len(pdev, dev->bar));
|
||||
if (dev->active)
|
||||
pci_disable_device(pdev);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pch_udc_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pch_udc_suspend(struct device *d)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(d);
|
||||
struct pch_udc_dev *dev = pci_get_drvdata(pdev);
|
||||
|
||||
pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
|
||||
pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||
|
||||
if (pci_save_state(pdev)) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: could not save PCI config state\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_udc_resume(struct pci_dev *pdev)
|
||||
static int pch_udc_resume(struct device *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: pci_enable_device failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pch_udc_pm, pch_udc_suspend, pch_udc_resume);
|
||||
#define PCH_UDC_PM_OPS (&pch_udc_pm)
|
||||
#else
|
||||
#define pch_udc_suspend NULL
|
||||
#define pch_udc_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
#define PCH_UDC_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int pch_udc_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
unsigned long resource;
|
||||
unsigned long len;
|
||||
int bar;
|
||||
int retval;
|
||||
struct pch_udc_dev *dev;
|
||||
|
||||
/* init */
|
||||
dev = kzalloc(sizeof *dev, GFP_KERNEL);
|
||||
if (!dev) {
|
||||
pr_err("%s: no memory for device structure\n", __func__);
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* pci setup */
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
kfree(dev);
|
||||
pr_err("%s: pci_enable_device failed\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
dev->active = 1;
|
||||
retval = pcim_enable_device(pdev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
/* Determine BAR based on PCI ID */
|
||||
if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC)
|
||||
dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000;
|
||||
bar = PCH_UDC_PCI_BAR_QUARK_X1000;
|
||||
else
|
||||
dev->bar = PCH_UDC_PCI_BAR;
|
||||
bar = PCH_UDC_PCI_BAR;
|
||||
|
||||
/* PCI resource allocation */
|
||||
resource = pci_resource_start(pdev, dev->bar);
|
||||
len = pci_resource_len(pdev, dev->bar);
|
||||
retval = pcim_iomap_regions(pdev, 1 << bar, pci_name(pdev));
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (!request_mem_region(resource, len, KBUILD_MODNAME)) {
|
||||
dev_err(&pdev->dev, "%s: pci device used already\n", __func__);
|
||||
retval = -EBUSY;
|
||||
goto finished;
|
||||
}
|
||||
dev->phys_addr = resource;
|
||||
dev->mem_region = 1;
|
||||
dev->base_addr = pcim_iomap_table(pdev)[bar];
|
||||
|
||||
dev->base_addr = ioremap_nocache(resource, len);
|
||||
if (!dev->base_addr) {
|
||||
pr_err("%s: device memory cannot be mapped\n", __func__);
|
||||
retval = -ENOMEM;
|
||||
goto finished;
|
||||
}
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "%s: irq not set\n", __func__);
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
}
|
||||
/* initialize the hardware */
|
||||
if (pch_udc_pcd_init(dev)) {
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
}
|
||||
if (request_irq(pdev->irq, pch_udc_isr, IRQF_SHARED, KBUILD_MODNAME,
|
||||
dev)) {
|
||||
if (pch_udc_pcd_init(dev))
|
||||
return -ENODEV;
|
||||
|
||||
pci_enable_msi(pdev);
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, pdev->irq, pch_udc_isr,
|
||||
IRQF_SHARED, KBUILD_MODNAME, dev);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "%s: request_irq(%d) fail\n", __func__,
|
||||
pdev->irq);
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
}
|
||||
dev->irq = pdev->irq;
|
||||
dev->irq_registered = 1;
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
@ -3219,8 +3140,7 @@ static int pch_udc_probe(struct pci_dev *pdev,
|
|||
|
||||
/* Put the device in disconnected state till a driver is bound */
|
||||
pch_udc_set_disconnect(dev);
|
||||
retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
|
||||
gadget_release);
|
||||
retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
|
||||
if (retval)
|
||||
goto finished;
|
||||
return 0;
|
||||
|
@ -3262,9 +3182,10 @@ static struct pci_driver pch_udc_driver = {
|
|||
.id_table = pch_udc_pcidev_id,
|
||||
.probe = pch_udc_probe,
|
||||
.remove = pch_udc_remove,
|
||||
.suspend = pch_udc_suspend,
|
||||
.resume = pch_udc_resume,
|
||||
.shutdown = pch_udc_shutdown,
|
||||
.driver = {
|
||||
.pm = PCH_UDC_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
module_pci_driver(pch_udc_driver);
|
||||
|
|
|
@ -296,7 +296,7 @@ static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum,
|
|||
} while ((tmp & mask) != loop);
|
||||
}
|
||||
|
||||
static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
static void pipe_change(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
{
|
||||
struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];
|
||||
|
||||
|
|
|
@ -61,11 +61,9 @@ static int udc_bind_to_driver(struct usb_udc *udc,
|
|||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
|
||||
int usb_gadget_map_request(struct usb_gadget *gadget,
|
||||
int usb_gadget_map_request_by_dev(struct device *dev,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
struct device *dev = gadget->dev.parent;
|
||||
|
||||
if (req->length == 0)
|
||||
return 0;
|
||||
|
||||
|
@ -75,7 +73,7 @@ int usb_gadget_map_request(struct usb_gadget *gadget,
|
|||
mapped = dma_map_sg(dev, req->sg, req->num_sgs,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
if (mapped == 0) {
|
||||
dev_err(&gadget->dev, "failed to map SGs\n");
|
||||
dev_err(dev, "failed to map SGs\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
@ -92,24 +90,38 @@ int usb_gadget_map_request(struct usb_gadget *gadget,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev);
|
||||
|
||||
int usb_gadget_map_request(struct usb_gadget *gadget,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
|
||||
|
||||
void usb_gadget_unmap_request(struct usb_gadget *gadget,
|
||||
void usb_gadget_unmap_request_by_dev(struct device *dev,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
if (req->length == 0)
|
||||
return;
|
||||
|
||||
if (req->num_mapped_sgs) {
|
||||
dma_unmap_sg(gadget->dev.parent, req->sg, req->num_mapped_sgs,
|
||||
dma_unmap_sg(dev, req->sg, req->num_mapped_sgs,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
req->num_mapped_sgs = 0;
|
||||
} else {
|
||||
dma_unmap_single(gadget->dev.parent, req->dma, req->length,
|
||||
dma_unmap_single(dev, req->dma, req->length,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
|
||||
|
||||
void usb_gadget_unmap_request(struct usb_gadget *gadget,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
|
||||
|
||||
#endif /* CONFIG_HAS_DMA */
|
||||
|
|
|
@ -35,6 +35,7 @@ config USB_XHCI_PCI
|
|||
|
||||
config USB_XHCI_PLATFORM
|
||||
tristate "Generic xHCI driver for a platform device"
|
||||
select USB_XHCI_RCAR if ARCH_RENESAS
|
||||
---help---
|
||||
Adds an xHCI host driver for a generic platform device, which
|
||||
provides a memory space and an irq.
|
||||
|
@ -63,7 +64,7 @@ config USB_XHCI_MVEBU
|
|||
|
||||
config USB_XHCI_RCAR
|
||||
tristate "xHCI support for Renesas R-Car SoCs"
|
||||
select USB_XHCI_PLATFORM
|
||||
depends on USB_XHCI_PLATFORM
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
|
|
|
@ -352,10 +352,8 @@ static int bcma_hcd_probe(struct bcma_device *core)
|
|||
usb_dev->core = core;
|
||||
|
||||
if (core->dev.of_node)
|
||||
usb_dev->gpio_desc = devm_get_gpiod_from_child(&core->dev, "vcc",
|
||||
&core->dev.of_node->fwnode);
|
||||
if (!IS_ERR_OR_NULL(usb_dev->gpio_desc))
|
||||
gpiod_direction_output(usb_dev->gpio_desc, 1);
|
||||
usb_dev->gpio_desc = devm_gpiod_get(&core->dev, "vcc",
|
||||
GPIOD_OUT_HIGH);
|
||||
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_USB20_HOST:
|
||||
|
|
|
@ -52,13 +52,6 @@ static void dbg_hcs_params(struct ehci_hcd *ehci, char *label)
|
|||
ehci_dbg(ehci, "%s portroute %s\n", label, buf);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
static inline void dbg_hcs_params(struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
/*
|
||||
* check the values in the HCCPARAMS register
|
||||
|
@ -92,13 +85,6 @@ static void dbg_hcc_params(struct ehci_hcd *ehci, char *label)
|
|||
" 32 periodic list" : "");
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
static inline void dbg_hcc_params(struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_qtd(const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
|
@ -281,37 +267,6 @@ dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
|
|||
(status & PORT_CONNECT) ? " CONNECT" : "");
|
||||
}
|
||||
|
||||
#else
|
||||
static inline void __maybe_unused
|
||||
dbg_qh(char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
||||
static inline void
|
||||
dbg_status(struct ehci_hcd *ehci, const char *label, u32 status)
|
||||
{
|
||||
|
@ -341,13 +296,6 @@ dbg_port(struct ehci_hcd *ehci, const char *label, int port, u32 status)
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
static inline void create_debug_files(struct ehci_hcd *bus) { }
|
||||
static inline void remove_debug_files(struct ehci_hcd *bus) { }
|
||||
|
||||
#else
|
||||
|
||||
/* troubleshooting help: expose state in debugfs */
|
||||
|
||||
static int debug_async_open(struct inode *, struct file *);
|
||||
|
@ -1120,4 +1068,38 @@ static inline void remove_debug_files(struct ehci_hcd *ehci)
|
|||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
#else /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
||||
static inline void dbg_hcs_params(struct ehci_hcd *ehci, char *label) { }
|
||||
static inline void dbg_hcc_params(struct ehci_hcd *ehci, char *label) { }
|
||||
|
||||
static inline void __maybe_unused dbg_qh(const char *label,
|
||||
struct ehci_hcd *ehci, struct ehci_qh *qh) { }
|
||||
|
||||
static inline int __maybe_unused dbg_status_buf(const char *buf,
|
||||
unsigned int len, const char *label, u32 status)
|
||||
{ return 0; }
|
||||
|
||||
static inline int __maybe_unused dbg_command_buf(const char *buf,
|
||||
unsigned int len, const char *label, u32 command)
|
||||
{ return 0; }
|
||||
|
||||
static inline int __maybe_unused dbg_intr_buf(const char *buf,
|
||||
unsigned int len, const char *label, u32 enable)
|
||||
{ return 0; }
|
||||
|
||||
static inline int __maybe_unused dbg_port_buf(char *buf,
|
||||
unsigned int len, const char *label, int port, u32 status)
|
||||
{ return 0; }
|
||||
|
||||
static inline void dbg_status(struct ehci_hcd *ehci, const char *label,
|
||||
u32 status) { }
|
||||
static inline void dbg_cmd(struct ehci_hcd *ehci, const char *label,
|
||||
u32 command) { }
|
||||
static inline void dbg_port(struct ehci_hcd *ehci, const char *label,
|
||||
int port, u32 status) { }
|
||||
|
||||
static inline void create_debug_files(struct ehci_hcd *bus) { }
|
||||
static inline void remove_debug_files(struct ehci_hcd *bus) { }
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
|
|
@ -321,7 +321,7 @@ static struct platform_driver exynos_ehci_driver = {
|
|||
.of_match_table = of_match_ptr(exynos_ehci_match),
|
||||
}
|
||||
};
|
||||
static const struct ehci_driver_overrides exynos_overrides __initdata = {
|
||||
static const struct ehci_driver_overrides exynos_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct exynos_ehci_hcd),
|
||||
};
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ static struct platform_driver ehci_msm_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct ehci_driver_overrides msm_overrides __initdata = {
|
||||
static const struct ehci_driver_overrides msm_overrides __initconst = {
|
||||
.reset = ehci_msm_reset,
|
||||
};
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ static inline u32 ehci_read(void __iomem *base, u32 reg)
|
|||
|
||||
static struct hc_driver __read_mostly ehci_omap_hc_driver;
|
||||
|
||||
static const struct ehci_driver_overrides ehci_omap_overrides __initdata = {
|
||||
static const struct ehci_driver_overrides ehci_omap_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct omap_hcd),
|
||||
};
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ static struct platform_driver spear_ehci_hcd_driver = {
|
|||
}
|
||||
};
|
||||
|
||||
static const struct ehci_driver_overrides spear_overrides __initdata = {
|
||||
static const struct ehci_driver_overrides spear_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct spear_ehci),
|
||||
};
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ static int scan_ed_list(struct fhci_usb *usb,
|
|||
list_for_each_entry(ed, list, node) {
|
||||
td = ed->td_head;
|
||||
|
||||
if (!td || (td && td->status == USB_TD_INPROGRESS))
|
||||
if (!td || td->status == USB_TD_INPROGRESS)
|
||||
continue;
|
||||
|
||||
if (ed->state != FHCI_ED_OPER) {
|
||||
|
|
|
@ -4795,14 +4795,8 @@ static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max,
|
|||
static inline int create_sysfs_files(struct fotg210_hcd *fotg210)
|
||||
{
|
||||
struct device *controller = fotg210_to_hcd(fotg210)->self.controller;
|
||||
int i = 0;
|
||||
|
||||
if (i)
|
||||
goto out;
|
||||
|
||||
i = device_create_file(controller, &dev_attr_uframe_periodic_max);
|
||||
out:
|
||||
return i;
|
||||
return device_create_file(controller, &dev_attr_uframe_periodic_max);
|
||||
}
|
||||
|
||||
static inline void remove_sysfs_files(struct fotg210_hcd *fotg210)
|
||||
|
|
|
@ -257,14 +257,14 @@ static int whc_probe(struct umc_dev *umc)
|
|||
|
||||
ret = whc_init(whc);
|
||||
if (ret)
|
||||
goto error;
|
||||
goto error_whc_init;
|
||||
|
||||
wusbhc->dev = dev;
|
||||
wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent);
|
||||
if (!wusbhc->uwb_rc) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "cannot get radio controller\n");
|
||||
goto error;
|
||||
goto error_uwb_rc;
|
||||
}
|
||||
|
||||
if (whc->n_devices > USB_MAXCHILDREN) {
|
||||
|
@ -311,8 +311,9 @@ error_usb_add_hcd:
|
|||
wusbhc_destroy(wusbhc);
|
||||
error_wusbhc_create:
|
||||
uwb_rc_put(wusbhc->uwb_rc);
|
||||
error:
|
||||
error_uwb_rc:
|
||||
whc_clean_up(whc);
|
||||
error_whc_init:
|
||||
usb_put_hcd(usb_hcd);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -314,7 +314,7 @@ void qset_free_std(struct whc *whc, struct whc_std *std)
|
|||
kfree(std->bounce_buf);
|
||||
}
|
||||
if (std->pl_virt) {
|
||||
if (std->dma_addr)
|
||||
if (!dma_mapping_error(whc->wusbhc.dev, std->dma_addr))
|
||||
dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
|
||||
std->num_pointers * sizeof(struct whc_page_list_entry),
|
||||
DMA_TO_DEVICE);
|
||||
|
@ -535,9 +535,11 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
|
|||
list_for_each_entry(std, &qset->stds, list_node) {
|
||||
if (std->ntds_remaining == -1) {
|
||||
pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
|
||||
std->ntds_remaining = ntds--;
|
||||
std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt,
|
||||
pl_len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(whc->wusbhc.dev, std->dma_addr))
|
||||
return -EFAULT;
|
||||
std->ntds_remaining = ntds--;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -618,6 +620,8 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset,
|
|||
|
||||
std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len,
|
||||
is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(&whc->umc->dev, std->dma_addr))
|
||||
return -EFAULT;
|
||||
|
||||
if (qset_fill_page_list(whc, std, mem_flags) < 0)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "xhci-mvebu.h"
|
||||
|
||||
#define USB3_MAX_WINDOWS 4
|
||||
|
@ -41,8 +44,10 @@ static void xhci_mvebu_mbus_config(void __iomem *base,
|
|||
}
|
||||
}
|
||||
|
||||
int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
|
||||
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct mbus_dram_target_info *dram;
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
|
||||
#ifndef __LINUX_XHCI_MVEBU_H
|
||||
#define __LINUX_XHCI_MVEBU_H
|
||||
|
||||
struct usb_hcd;
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
|
||||
int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev);
|
||||
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
|
||||
#else
|
||||
static inline int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
|
||||
static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -37,27 +37,32 @@ static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
|
|||
.start = xhci_plat_start,
|
||||
};
|
||||
|
||||
static void xhci_priv_plat_start(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
||||
|
||||
if (priv->plat_start)
|
||||
priv->plat_start(hcd);
|
||||
}
|
||||
|
||||
static int xhci_priv_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
||||
|
||||
if (!priv->init_quirk)
|
||||
return 0;
|
||||
|
||||
return priv->init_quirk(hcd);
|
||||
}
|
||||
|
||||
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
{
|
||||
struct usb_hcd *hcd = xhci_to_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;
|
||||
|
||||
/*
|
||||
* On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set
|
||||
* to 1. However, these SoCs don't support 64-bit address memory
|
||||
* pointers. So, this driver clears the AC64 bit of xhci->hcc_params
|
||||
* to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in
|
||||
* xhci_gen_setup().
|
||||
*/
|
||||
if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2) ||
|
||||
xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3))
|
||||
xhci->quirks |= XHCI_NO_64BIT_SUPPORT;
|
||||
}
|
||||
|
||||
/* called during probe() after chip reset completes */
|
||||
|
@ -65,38 +70,35 @@ static int xhci_plat_setup(struct usb_hcd *hcd)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2) ||
|
||||
xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3)) {
|
||||
ret = xhci_rcar_init_quirk(hcd);
|
||||
|
||||
ret = xhci_priv_init_quirk(hcd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return xhci_gen_setup(hcd, xhci_plat_quirks);
|
||||
}
|
||||
|
||||
static int xhci_plat_start(struct usb_hcd *hcd)
|
||||
{
|
||||
if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2) ||
|
||||
xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3))
|
||||
xhci_rcar_start(hcd);
|
||||
|
||||
xhci_priv_plat_start(hcd);
|
||||
return xhci_run(hcd);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct xhci_plat_priv xhci_plat_marvell_armada = {
|
||||
.type = XHCI_PLAT_TYPE_MARVELL_ARMADA,
|
||||
.init_quirk = xhci_mvebu_mbus_init_quirk,
|
||||
};
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
|
||||
.type = XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2,
|
||||
.firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1,
|
||||
.init_quirk = xhci_rcar_init_quirk,
|
||||
.plat_start = xhci_rcar_start,
|
||||
};
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
|
||||
.type = XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3,
|
||||
.firmware_name = XHCI_RCAR_FIRMWARE_NAME_V2,
|
||||
.init_quirk = xhci_rcar_init_quirk,
|
||||
.plat_start = xhci_rcar_start,
|
||||
};
|
||||
|
||||
static const struct of_device_id usb_xhci_of_match[] = {
|
||||
|
@ -207,12 +209,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
*priv = *priv_match;
|
||||
}
|
||||
|
||||
if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_MARVELL_ARMADA)) {
|
||||
ret = xhci_mvebu_mbus_init_quirk(pdev);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
|
||||
xhci->clk = clk;
|
||||
|
|
|
@ -13,27 +13,11 @@
|
|||
|
||||
#include "xhci.h" /* for hcd_to_xhci() */
|
||||
|
||||
enum xhci_plat_type {
|
||||
XHCI_PLAT_TYPE_MARVELL_ARMADA = 1,
|
||||
XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2,
|
||||
XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3,
|
||||
};
|
||||
|
||||
struct xhci_plat_priv {
|
||||
enum xhci_plat_type type;
|
||||
const char *firmware_name;
|
||||
void (*plat_start)(struct usb_hcd *);
|
||||
int (*init_quirk)(struct usb_hcd *);
|
||||
};
|
||||
|
||||
#define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
|
||||
|
||||
static inline bool xhci_plat_type_is(struct usb_hcd *hcd,
|
||||
enum xhci_plat_type type)
|
||||
{
|
||||
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
||||
|
||||
if (priv && priv->type == type)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
#endif /* _XHCI_PLAT_H */
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/firmware.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
@ -76,6 +77,24 @@ static void xhci_rcar_start_gen2(struct usb_hcd *hcd)
|
|||
writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
|
||||
}
|
||||
|
||||
static int xhci_rcar_is_gen2(struct device *dev)
|
||||
{
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
return of_device_is_compatible(node, "renesas,xhci-r8a7790") ||
|
||||
of_device_is_compatible(node, "renesas,xhci-r8a7791") ||
|
||||
of_device_is_compatible(node, "renesas,xhci-r8a7793") ||
|
||||
of_device_is_compatible(node, "renensas,rcar-gen2-xhci");
|
||||
}
|
||||
|
||||
static int xhci_rcar_is_gen3(struct device *dev)
|
||||
{
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
return of_device_is_compatible(node, "renesas,xhci-r8a7795") ||
|
||||
of_device_is_compatible(node, "renesas,rcar-gen3-xhci");
|
||||
}
|
||||
|
||||
void xhci_rcar_start(struct usb_hcd *hcd)
|
||||
{
|
||||
u32 temp;
|
||||
|
@ -85,7 +104,7 @@ void xhci_rcar_start(struct usb_hcd *hcd)
|
|||
temp = readl(hcd->regs + RCAR_USB3_INT_ENA);
|
||||
temp |= RCAR_USB3_INT_ENA_VAL;
|
||||
writel(temp, hcd->regs + RCAR_USB3_INT_ENA);
|
||||
if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2))
|
||||
if (xhci_rcar_is_gen2(hcd->self.controller))
|
||||
xhci_rcar_start_gen2(hcd);
|
||||
}
|
||||
}
|
||||
|
@ -156,9 +175,22 @@ static int xhci_rcar_download_firmware(struct usb_hcd *hcd)
|
|||
/* This function needs to initialize a "phy" of usb before */
|
||||
int xhci_rcar_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
/* If hcd->regs is NULL, we don't just call the following function */
|
||||
if (!hcd->regs)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set
|
||||
* to 1. However, these SoCs don't support 64-bit address memory
|
||||
* pointers. So, this driver clears the AC64 bit of xhci->hcc_params
|
||||
* to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in
|
||||
* xhci_gen_setup().
|
||||
*/
|
||||
if (xhci_rcar_is_gen2(hcd->self.controller) ||
|
||||
xhci_rcar_is_gen3(hcd->self.controller))
|
||||
xhci->quirks |= XHCI_NO_64BIT_SUPPORT;
|
||||
|
||||
return xhci_rcar_download_firmware(hcd);
|
||||
}
|
||||
|
|
|
@ -373,7 +373,11 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
|
|||
}
|
||||
}
|
||||
|
||||
static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
/* Get the right ring for the given slot_id, ep_index and stream_id.
|
||||
* If the endpoint supports streams, boundary check the URB's stream ID.
|
||||
* If the endpoint doesn't support streams, return the singular endpoint ring.
|
||||
*/
|
||||
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id)
|
||||
{
|
||||
|
@ -405,17 +409,6 @@ static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the right ring for the given URB.
|
||||
* If the endpoint supports streams, boundary check the URB's stream ID.
|
||||
* If the endpoint doesn't support streams, return the singular endpoint ring.
|
||||
*/
|
||||
static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb)
|
||||
{
|
||||
return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
|
||||
xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the xHC's endpoint ring dequeue pointer past cur_td.
|
||||
* Record the new state of the xHC's endpoint ring dequeue segment,
|
||||
|
@ -1768,7 +1761,7 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
|
|||
if (trb_comp_code == COMP_TX_ERR ||
|
||||
trb_comp_code == COMP_BABBLE ||
|
||||
trb_comp_code == COMP_SPLIT_ERR)
|
||||
/* The 0.96 spec says a babbling control endpoint
|
||||
/* The 0.95 spec says a babbling control endpoint
|
||||
* is not halted. The 0.96 spec says it is. Some HW
|
||||
* claims to be 0.95 compliant, but it halts the control
|
||||
* endpoint anyway. Check if a babble halted the
|
||||
|
@ -2938,46 +2931,55 @@ static int prepare_transfer(struct xhci_hcd *xhci,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
|
||||
static unsigned int count_trbs(u64 addr, u64 len)
|
||||
{
|
||||
int num_sgs, num_trbs, running_total, temp, i;
|
||||
struct scatterlist *sg;
|
||||
unsigned int num_trbs;
|
||||
|
||||
sg = NULL;
|
||||
num_sgs = urb->num_mapped_sgs;
|
||||
temp = urb->transfer_buffer_length;
|
||||
|
||||
num_trbs = 0;
|
||||
for_each_sg(urb->sg, sg, num_sgs, i) {
|
||||
unsigned int len = sg_dma_len(sg);
|
||||
|
||||
/* Scatter gather list entries may cross 64KB boundaries */
|
||||
running_total = TRB_MAX_BUFF_SIZE -
|
||||
(sg_dma_address(sg) & (TRB_MAX_BUFF_SIZE - 1));
|
||||
running_total &= TRB_MAX_BUFF_SIZE - 1;
|
||||
if (running_total != 0)
|
||||
num_trbs = DIV_ROUND_UP(len + (addr & (TRB_MAX_BUFF_SIZE - 1)),
|
||||
TRB_MAX_BUFF_SIZE);
|
||||
if (num_trbs == 0)
|
||||
num_trbs++;
|
||||
|
||||
/* How many more 64KB chunks to transfer, how many more TRBs? */
|
||||
while (running_total < sg_dma_len(sg) && running_total < temp) {
|
||||
num_trbs++;
|
||||
running_total += TRB_MAX_BUFF_SIZE;
|
||||
}
|
||||
len = min_t(int, len, temp);
|
||||
temp -= len;
|
||||
if (temp == 0)
|
||||
break;
|
||||
}
|
||||
return num_trbs;
|
||||
}
|
||||
|
||||
static void check_trb_math(struct urb *urb, int num_trbs, int running_total)
|
||||
static inline unsigned int count_trbs_needed(struct urb *urb)
|
||||
{
|
||||
if (num_trbs != 0)
|
||||
dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated number of "
|
||||
"TRBs, %d left\n", __func__,
|
||||
urb->ep->desc.bEndpointAddress, num_trbs);
|
||||
if (running_total != urb->transfer_buffer_length)
|
||||
return count_trbs(urb->transfer_dma, urb->transfer_buffer_length);
|
||||
}
|
||||
|
||||
static unsigned int count_sg_trbs_needed(struct urb *urb)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
unsigned int i, len, full_len, num_trbs = 0;
|
||||
|
||||
full_len = urb->transfer_buffer_length;
|
||||
|
||||
for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) {
|
||||
len = sg_dma_len(sg);
|
||||
num_trbs += count_trbs(sg_dma_address(sg), len);
|
||||
len = min_t(unsigned int, len, full_len);
|
||||
full_len -= len;
|
||||
if (full_len == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return num_trbs;
|
||||
}
|
||||
|
||||
static unsigned int count_isoc_trbs_needed(struct urb *urb, int i)
|
||||
{
|
||||
u64 addr, len;
|
||||
|
||||
addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
|
||||
len = urb->iso_frame_desc[i].length;
|
||||
|
||||
return count_trbs(addr, len);
|
||||
}
|
||||
|
||||
static void check_trb_math(struct urb *urb, int running_total)
|
||||
{
|
||||
if (unlikely(running_total != urb->transfer_buffer_length))
|
||||
dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, "
|
||||
"queued %#x (%d), asked for %#x (%d)\n",
|
||||
__func__,
|
||||
|
@ -3003,26 +3005,20 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
|
|||
xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* xHCI uses normal TRBs for both bulk and interrupt. When the interrupt
|
||||
* endpoint is to be serviced, the xHC will consume (at most) one TD. A TD
|
||||
* (comprised of sg list entries) can take several service intervals to
|
||||
* transmit.
|
||||
*/
|
||||
int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||
static void check_interval(struct xhci_hcd *xhci, struct urb *urb,
|
||||
struct xhci_ep_ctx *ep_ctx)
|
||||
{
|
||||
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci,
|
||||
xhci->devs[slot_id]->out_ctx, ep_index);
|
||||
int xhci_interval;
|
||||
int ep_interval;
|
||||
|
||||
xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info));
|
||||
ep_interval = urb->interval;
|
||||
|
||||
/* Convert to microframes */
|
||||
if (urb->dev->speed == USB_SPEED_LOW ||
|
||||
urb->dev->speed == USB_SPEED_FULL)
|
||||
ep_interval *= 8;
|
||||
|
||||
/* FIXME change this to a warning and a suggestion to use the new API
|
||||
* to set the polling interval (once the API is added).
|
||||
*/
|
||||
|
@ -3037,6 +3033,22 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
urb->dev->speed == USB_SPEED_FULL)
|
||||
urb->interval /= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xHCI uses normal TRBs for both bulk and interrupt. When the interrupt
|
||||
* endpoint is to be serviced, the xHC will consume (at most) one TD. A TD
|
||||
* (comprised of sg list entries) can take several service intervals to
|
||||
* transmit.
|
||||
*/
|
||||
int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||
{
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, xhci->devs[slot_id]->out_ctx, ep_index);
|
||||
check_interval(xhci, urb, ep_ctx);
|
||||
|
||||
return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||||
}
|
||||
|
||||
|
@ -3086,177 +3098,6 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
|
|||
return (total_packet_count - ((transferred + trb_buff_len) / maxp));
|
||||
}
|
||||
|
||||
|
||||
static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||
{
|
||||
struct xhci_ring *ep_ring;
|
||||
unsigned int num_trbs;
|
||||
struct urb_priv *urb_priv;
|
||||
struct xhci_td *td;
|
||||
struct scatterlist *sg;
|
||||
int num_sgs;
|
||||
int trb_buff_len, this_sg_len, running_total, ret;
|
||||
unsigned int total_packet_count;
|
||||
bool zero_length_needed;
|
||||
bool first_trb;
|
||||
int last_trb_num;
|
||||
u64 addr;
|
||||
bool more_trbs_coming;
|
||||
|
||||
struct xhci_generic_trb *start_trb;
|
||||
int start_cycle;
|
||||
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring)
|
||||
return -EINVAL;
|
||||
|
||||
num_trbs = count_sg_trbs_needed(xhci, urb);
|
||||
num_sgs = urb->num_mapped_sgs;
|
||||
total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
|
||||
usb_endpoint_maxp(&urb->ep->desc));
|
||||
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, 0, mem_flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
/* Deal with URB_ZERO_PACKET - need one more td/trb */
|
||||
zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET &&
|
||||
urb_priv->length == 2;
|
||||
if (zero_length_needed) {
|
||||
num_trbs++;
|
||||
xhci_dbg(xhci, "Creating zero length td.\n");
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
1, urb, 1, mem_flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
td = urb_priv->td[0];
|
||||
|
||||
/*
|
||||
* Don't give the first TRB to the hardware (by toggling the cycle bit)
|
||||
* until we've finished creating all the other TRBs. The ring's cycle
|
||||
* state may change as we enqueue the other TRBs, so save it too.
|
||||
*/
|
||||
start_trb = &ep_ring->enqueue->generic;
|
||||
start_cycle = ep_ring->cycle_state;
|
||||
|
||||
running_total = 0;
|
||||
/*
|
||||
* How much data is in the first TRB?
|
||||
*
|
||||
* There are three forces at work for TRB buffer pointers and lengths:
|
||||
* 1. We don't want to walk off the end of this sg-list entry buffer.
|
||||
* 2. The transfer length that the driver requested may be smaller than
|
||||
* the amount of memory allocated for this scatter-gather list.
|
||||
* 3. TRBs buffers can't cross 64KB boundaries.
|
||||
*/
|
||||
sg = urb->sg;
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
this_sg_len = sg_dma_len(sg);
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1));
|
||||
trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
|
||||
if (trb_buff_len > urb->transfer_buffer_length)
|
||||
trb_buff_len = urb->transfer_buffer_length;
|
||||
|
||||
first_trb = true;
|
||||
last_trb_num = zero_length_needed ? 2 : 1;
|
||||
/* Queue the first TRB, even if it's zero-length */
|
||||
do {
|
||||
u32 field = 0;
|
||||
u32 length_field = 0;
|
||||
u32 remainder = 0;
|
||||
|
||||
/* Don't change the cycle bit of the first TRB until later */
|
||||
if (first_trb) {
|
||||
first_trb = false;
|
||||
if (start_cycle == 0)
|
||||
field |= 0x1;
|
||||
} else
|
||||
field |= ep_ring->cycle_state;
|
||||
|
||||
/* Chain all the TRBs together; clear the chain bit in the last
|
||||
* TRB to indicate it's the last TRB in the chain.
|
||||
*/
|
||||
if (num_trbs > last_trb_num) {
|
||||
field |= TRB_CHAIN;
|
||||
} else if (num_trbs == last_trb_num) {
|
||||
td->last_trb = ep_ring->enqueue;
|
||||
field |= TRB_IOC;
|
||||
} else if (zero_length_needed && num_trbs == 1) {
|
||||
trb_buff_len = 0;
|
||||
urb_priv->td[1]->last_trb = ep_ring->enqueue;
|
||||
field |= TRB_IOC;
|
||||
}
|
||||
|
||||
/* Only set interrupt on short packet for IN endpoints */
|
||||
if (usb_urb_dir_in(urb))
|
||||
field |= TRB_ISP;
|
||||
|
||||
if (TRB_MAX_BUFF_SIZE -
|
||||
(addr & (TRB_MAX_BUFF_SIZE - 1)) < trb_buff_len) {
|
||||
xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n");
|
||||
xhci_dbg(xhci, "Next boundary at %#x, end dma = %#x\n",
|
||||
(unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
|
||||
(unsigned int) addr + trb_buff_len);
|
||||
}
|
||||
|
||||
/* Set the TRB length, TD size, and interrupter fields. */
|
||||
remainder = xhci_td_remainder(xhci, running_total, trb_buff_len,
|
||||
urb->transfer_buffer_length,
|
||||
urb, num_trbs - 1);
|
||||
|
||||
length_field = TRB_LEN(trb_buff_len) |
|
||||
TRB_TD_SIZE(remainder) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
||||
if (num_trbs > 1)
|
||||
more_trbs_coming = true;
|
||||
else
|
||||
more_trbs_coming = false;
|
||||
queue_trb(xhci, ep_ring, more_trbs_coming,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
length_field,
|
||||
field | TRB_TYPE(TRB_NORMAL));
|
||||
--num_trbs;
|
||||
running_total += trb_buff_len;
|
||||
|
||||
/* Calculate length for next transfer --
|
||||
* Are we done queueing all the TRBs for this sg entry?
|
||||
*/
|
||||
this_sg_len -= trb_buff_len;
|
||||
if (this_sg_len == 0) {
|
||||
--num_sgs;
|
||||
if (num_sgs == 0)
|
||||
break;
|
||||
sg = sg_next(sg);
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
this_sg_len = sg_dma_len(sg);
|
||||
} else {
|
||||
addr += trb_buff_len;
|
||||
}
|
||||
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE -
|
||||
(addr & (TRB_MAX_BUFF_SIZE - 1));
|
||||
trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
|
||||
if (running_total + trb_buff_len > urb->transfer_buffer_length)
|
||||
trb_buff_len =
|
||||
urb->transfer_buffer_length - running_total;
|
||||
} while (num_trbs > 0);
|
||||
|
||||
check_trb_math(urb, num_trbs, running_total);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||
start_cycle, start_trb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is very similar to what ehci-q.c qtd_fill() does */
|
||||
int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||
|
@ -3264,51 +3105,40 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
struct xhci_ring *ep_ring;
|
||||
struct urb_priv *urb_priv;
|
||||
struct xhci_td *td;
|
||||
int num_trbs;
|
||||
struct xhci_generic_trb *start_trb;
|
||||
bool first_trb;
|
||||
int last_trb_num;
|
||||
struct scatterlist *sg = NULL;
|
||||
bool more_trbs_coming;
|
||||
bool zero_length_needed;
|
||||
int start_cycle;
|
||||
u32 field, length_field;
|
||||
|
||||
int running_total, trb_buff_len, ret;
|
||||
unsigned int total_packet_count;
|
||||
unsigned int num_trbs, last_trb_num, i;
|
||||
unsigned int start_cycle, num_sgs = 0;
|
||||
unsigned int running_total, block_len, trb_buff_len;
|
||||
unsigned int full_len;
|
||||
int ret;
|
||||
u32 field, length_field, remainder;
|
||||
u64 addr;
|
||||
|
||||
if (urb->num_sgs)
|
||||
return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||||
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring)
|
||||
return -EINVAL;
|
||||
|
||||
num_trbs = 0;
|
||||
/* How much data is (potentially) left before the 64KB boundary? */
|
||||
running_total = TRB_MAX_BUFF_SIZE -
|
||||
(urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
|
||||
running_total &= TRB_MAX_BUFF_SIZE - 1;
|
||||
|
||||
/* If there's some data on this 64KB chunk, or we have to send a
|
||||
* zero-length transfer, we need at least one TRB
|
||||
*/
|
||||
if (running_total != 0 || urb->transfer_buffer_length == 0)
|
||||
num_trbs++;
|
||||
/* How many more 64KB chunks to transfer, how many more TRBs? */
|
||||
while (running_total < urb->transfer_buffer_length) {
|
||||
num_trbs++;
|
||||
running_total += TRB_MAX_BUFF_SIZE;
|
||||
}
|
||||
/* If we have scatter/gather list, we use it. */
|
||||
if (urb->num_sgs) {
|
||||
num_sgs = urb->num_mapped_sgs;
|
||||
sg = urb->sg;
|
||||
num_trbs = count_sg_trbs_needed(urb);
|
||||
} else
|
||||
num_trbs = count_trbs_needed(urb);
|
||||
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, 0, mem_flags);
|
||||
if (ret < 0)
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
last_trb_num = num_trbs - 1;
|
||||
|
||||
/* Deal with URB_ZERO_PACKET - need one more td/trb */
|
||||
zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET &&
|
||||
urb_priv->length == 2;
|
||||
|
@ -3318,7 +3148,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
1, urb, 1, mem_flags);
|
||||
if (ret < 0)
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -3332,43 +3162,58 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
start_trb = &ep_ring->enqueue->generic;
|
||||
start_cycle = ep_ring->cycle_state;
|
||||
|
||||
full_len = urb->transfer_buffer_length;
|
||||
running_total = 0;
|
||||
total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
|
||||
usb_endpoint_maxp(&urb->ep->desc));
|
||||
/* How much data is in the first TRB? */
|
||||
addr = (u64) urb->transfer_dma;
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE -
|
||||
(urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
|
||||
if (trb_buff_len > urb->transfer_buffer_length)
|
||||
trb_buff_len = urb->transfer_buffer_length;
|
||||
block_len = 0;
|
||||
|
||||
first_trb = true;
|
||||
last_trb_num = zero_length_needed ? 2 : 1;
|
||||
/* Queue the first TRB, even if it's zero-length */
|
||||
do {
|
||||
u32 remainder = 0;
|
||||
field = 0;
|
||||
/* Queue the TRBs, even if they are zero-length */
|
||||
for (i = 0; i < num_trbs; i++) {
|
||||
field = TRB_TYPE(TRB_NORMAL);
|
||||
|
||||
if (block_len == 0) {
|
||||
/* A new contiguous block. */
|
||||
if (sg) {
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
block_len = sg_dma_len(sg);
|
||||
} else {
|
||||
addr = (u64) urb->transfer_dma;
|
||||
block_len = full_len;
|
||||
}
|
||||
/* TRB buffer should not cross 64KB boundaries */
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
trb_buff_len = min_t(unsigned int,
|
||||
trb_buff_len,
|
||||
block_len);
|
||||
} else {
|
||||
/* Further through the contiguous block. */
|
||||
trb_buff_len = block_len;
|
||||
if (trb_buff_len > TRB_MAX_BUFF_SIZE)
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE;
|
||||
}
|
||||
|
||||
if (running_total + trb_buff_len > full_len)
|
||||
trb_buff_len = full_len - running_total;
|
||||
|
||||
/* Don't change the cycle bit of the first TRB until later */
|
||||
if (first_trb) {
|
||||
first_trb = false;
|
||||
if (i == 0) {
|
||||
if (start_cycle == 0)
|
||||
field |= 0x1;
|
||||
field |= TRB_CYCLE;
|
||||
} else
|
||||
field |= ep_ring->cycle_state;
|
||||
|
||||
/* Chain all the TRBs together; clear the chain bit in the last
|
||||
* TRB to indicate it's the last TRB in the chain.
|
||||
*/
|
||||
if (num_trbs > last_trb_num) {
|
||||
if (i < last_trb_num) {
|
||||
field |= TRB_CHAIN;
|
||||
} else if (num_trbs == last_trb_num) {
|
||||
td->last_trb = ep_ring->enqueue;
|
||||
} else {
|
||||
field |= TRB_IOC;
|
||||
} else if (zero_length_needed && num_trbs == 1) {
|
||||
if (i == last_trb_num)
|
||||
td->last_trb = ep_ring->enqueue;
|
||||
else if (zero_length_needed) {
|
||||
trb_buff_len = 0;
|
||||
urb_priv->td[1]->last_trb = ep_ring->enqueue;
|
||||
field |= TRB_IOC;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only set interrupt on short packet for IN endpoints */
|
||||
|
@ -3376,15 +3221,15 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
field |= TRB_ISP;
|
||||
|
||||
/* Set the TRB length, TD size, and interrupter fields. */
|
||||
remainder = xhci_td_remainder(xhci, running_total, trb_buff_len,
|
||||
urb->transfer_buffer_length,
|
||||
urb, num_trbs - 1);
|
||||
remainder = xhci_td_remainder(xhci, running_total,
|
||||
trb_buff_len, full_len,
|
||||
urb, num_trbs - i - 1);
|
||||
|
||||
length_field = TRB_LEN(trb_buff_len) |
|
||||
TRB_TD_SIZE(remainder) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
||||
if (num_trbs > 1)
|
||||
if (i < num_trbs - 1)
|
||||
more_trbs_coming = true;
|
||||
else
|
||||
more_trbs_coming = false;
|
||||
|
@ -3392,18 +3237,24 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
length_field,
|
||||
field | TRB_TYPE(TRB_NORMAL));
|
||||
--num_trbs;
|
||||
field);
|
||||
|
||||
running_total += trb_buff_len;
|
||||
|
||||
/* Calculate length for next transfer */
|
||||
addr += trb_buff_len;
|
||||
trb_buff_len = urb->transfer_buffer_length - running_total;
|
||||
if (trb_buff_len > TRB_MAX_BUFF_SIZE)
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE;
|
||||
} while (num_trbs > 0);
|
||||
block_len -= trb_buff_len;
|
||||
|
||||
check_trb_math(urb, num_trbs, running_total);
|
||||
if (sg) {
|
||||
if (block_len == 0) {
|
||||
/* New sg entry */
|
||||
--num_sgs;
|
||||
if (num_sgs == 0)
|
||||
break;
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_trb_math(urb, running_total);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||
start_cycle, start_trb);
|
||||
return 0;
|
||||
|
@ -3532,23 +3383,6 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
|
||||
struct urb *urb, int i)
|
||||
{
|
||||
int num_trbs = 0;
|
||||
u64 addr, td_len;
|
||||
|
||||
addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
|
||||
td_len = urb->iso_frame_desc[i].length;
|
||||
|
||||
num_trbs = DIV_ROUND_UP(td_len + (addr & (TRB_MAX_BUFF_SIZE - 1)),
|
||||
TRB_MAX_BUFF_SIZE);
|
||||
if (num_trbs == 0)
|
||||
num_trbs++;
|
||||
|
||||
return num_trbs;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -3746,7 +3580,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
last_burst_pkt_count = xhci_get_last_burst_packet_count(xhci,
|
||||
urb, total_pkt_count);
|
||||
|
||||
trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
|
||||
trbs_per_td = count_isoc_trbs_needed(urb, i);
|
||||
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
|
||||
urb->stream_id, trbs_per_td, urb, i, mem_flags);
|
||||
|
@ -3807,8 +3641,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
field |= TRB_BEI;
|
||||
}
|
||||
/* Calculate TRB length */
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE -
|
||||
(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
if (trb_buff_len > td_remain_len)
|
||||
trb_buff_len = td_remain_len;
|
||||
|
||||
|
@ -3897,8 +3730,6 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
struct xhci_ring *ep_ring;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
int start_frame;
|
||||
int xhci_interval;
|
||||
int ep_interval;
|
||||
int num_tds, num_trbs, i;
|
||||
int ret;
|
||||
struct xhci_virt_ep *xep;
|
||||
|
@ -3912,7 +3743,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
num_trbs = 0;
|
||||
num_tds = urb->number_of_packets;
|
||||
for (i = 0; i < num_tds; i++)
|
||||
num_trbs += count_isoc_trbs_needed(xhci, urb, i);
|
||||
num_trbs += count_isoc_trbs_needed(urb, i);
|
||||
|
||||
/* Check the ring to guarantee there is enough room for the whole urb.
|
||||
* Do not insert any td of the urb to the ring if the check failed.
|
||||
|
@ -3926,26 +3757,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
* Check interval value. This should be done before we start to
|
||||
* calculate the start frame value.
|
||||
*/
|
||||
xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info));
|
||||
ep_interval = urb->interval;
|
||||
/* Convert to microframes */
|
||||
if (urb->dev->speed == USB_SPEED_LOW ||
|
||||
urb->dev->speed == USB_SPEED_FULL)
|
||||
ep_interval *= 8;
|
||||
/* FIXME change this to a warning and a suggestion to use the new API
|
||||
* to set the polling interval (once the API is added).
|
||||
*/
|
||||
if (xhci_interval != ep_interval) {
|
||||
dev_dbg_ratelimited(&urb->dev->dev,
|
||||
"Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n",
|
||||
ep_interval, ep_interval == 1 ? "" : "s",
|
||||
xhci_interval, xhci_interval == 1 ? "" : "s");
|
||||
urb->interval = xhci_interval;
|
||||
/* Convert back to frames for LS/FS devices */
|
||||
if (urb->dev->speed == USB_SPEED_LOW ||
|
||||
urb->dev->speed == USB_SPEED_FULL)
|
||||
urb->interval /= 8;
|
||||
}
|
||||
check_interval(xhci, urb, ep_ctx);
|
||||
|
||||
/* Calculate the start frame and put it in urb->start_frame. */
|
||||
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
|
||||
|
|
|
@ -1459,47 +1459,6 @@ free_priv:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Get the right ring for the given URB.
|
||||
* If the endpoint supports streams, boundary check the URB's stream ID.
|
||||
* If the endpoint doesn't support streams, return the singular endpoint ring.
|
||||
*/
|
||||
static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned int slot_id;
|
||||
unsigned int ep_index;
|
||||
unsigned int stream_id;
|
||||
struct xhci_virt_ep *ep;
|
||||
|
||||
slot_id = urb->dev->slot_id;
|
||||
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
|
||||
stream_id = urb->stream_id;
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
/* Common case: no streams */
|
||||
if (!(ep->ep_state & EP_HAS_STREAMS))
|
||||
return ep->ring;
|
||||
|
||||
if (stream_id == 0) {
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has streams, "
|
||||
"but URB has no stream ID.\n",
|
||||
slot_id, ep_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stream_id < ep->stream_info->num_streams)
|
||||
return ep->stream_info->stream_rings[stream_id];
|
||||
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has "
|
||||
"stream IDs 1 to %u allocated, "
|
||||
"but stream ID %u is requested.\n",
|
||||
slot_id, ep_index,
|
||||
ep->stream_info->num_streams - 1,
|
||||
stream_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the URB's TD from the endpoint ring. This may cause the HC to stop
|
||||
* USB transfers, potentially stopping in the middle of a TRB buffer. The HC
|
||||
|
|
|
@ -1338,6 +1338,9 @@ union xhci_trb {
|
|||
/* TRB buffer pointers can't cross 64KB boundaries */
|
||||
#define TRB_MAX_BUFF_SHIFT 16
|
||||
#define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT)
|
||||
/* How much data is left before the 64KB boundary? */
|
||||
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
|
||||
(addr & (TRB_MAX_BUFF_SIZE - 1)))
|
||||
|
||||
struct xhci_segment {
|
||||
union xhci_trb *trbs;
|
||||
|
@ -1965,4 +1968,15 @@ struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_container_
|
|||
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);
|
||||
struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_index);
|
||||
|
||||
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id);
|
||||
static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb)
|
||||
{
|
||||
return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
|
||||
xhci_get_endpoint_index(&urb->ep->desc),
|
||||
urb->stream_id);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_XHCI_HCD_H */
|
||||
|
|
|
@ -268,3 +268,29 @@ config USB_CHAOSKEY
|
|||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called chaoskey.
|
||||
|
||||
config UCSI
|
||||
tristate "USB Type-C Connector System Software Interface driver"
|
||||
depends on ACPI
|
||||
help
|
||||
UCSI driver is meant to be used as a convenience tool for desktop and
|
||||
server systems that are not equipped to handle USB in device mode. It
|
||||
will always select USB host role for the USB Type-C ports on systems
|
||||
that provide UCSI interface.
|
||||
|
||||
USB Type-C Connector System Software Interface (UCSI) is a
|
||||
specification for an interface that allows the Operating System to
|
||||
control the USB Type-C ports on a system. Things the need controlling
|
||||
include the USB Data Role (host or device), and when USB Power
|
||||
Delivery is supported, the Power Role (source or sink). With USB
|
||||
Type-C connectors, when two dual role capable devices are attached
|
||||
together, the data role is selected randomly. Therefore it is
|
||||
important to give the OS a way to select the role. Otherwise the user
|
||||
would have to unplug and replug in order in order to attempt to swap
|
||||
the data and power roles.
|
||||
|
||||
The UCSI specification can be downloaded from:
|
||||
http://www.intel.com/content/www/us/en/io/universal-serial-bus/usb-type-c-ucsi-spec.html
|
||||
|
||||
To compile the driver as a module, choose M here: the module will be
|
||||
called ucsi.
|
||||
|
|
|
@ -26,6 +26,7 @@ obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
|
|||
obj-$(CONFIG_USB_YUREX) += yurex.o
|
||||
obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
|
||||
obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
|
||||
obj-$(CONFIG_UCSI) += ucsi.o
|
||||
|
||||
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
|
||||
obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
|
||||
|
|
|
@ -2420,7 +2420,7 @@ static int sisusb_open(struct inode *inode, struct file *file)
|
|||
|
||||
if (!sisusb->devinit) {
|
||||
if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH ||
|
||||
sisusb->sisusb_dev->speed == USB_SPEED_SUPER) {
|
||||
sisusb->sisusb_dev->speed >= USB_SPEED_SUPER) {
|
||||
if (sisusb_init_gfxdevice(sisusb, 0)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
dev_err(&sisusb->sisusb_dev->dev,
|
||||
|
@ -3127,7 +3127,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
|||
|
||||
sisusb->present = 1;
|
||||
|
||||
if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) {
|
||||
if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {
|
||||
int initscreen = 1;
|
||||
#ifdef INCL_SISUSB_CON
|
||||
if (sisusb_first_vc > 0 && sisusb_last_vc > 0 &&
|
||||
|
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* USB Type-C Connector System Software Interface driver
|
||||
*
|
||||
* Copyright (C) 2016, Intel Corporation
|
||||
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.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 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "ucsi.h"
|
||||
|
||||
/* Double the time defined by MIN_TIME_TO_RESPOND_WITH_BUSY */
|
||||
#define UCSI_TIMEOUT_MS 20
|
||||
|
||||
enum ucsi_status {
|
||||
UCSI_IDLE = 0,
|
||||
UCSI_BUSY,
|
||||
UCSI_ERROR,
|
||||
};
|
||||
|
||||
struct ucsi_connector {
|
||||
int num;
|
||||
struct ucsi *ucsi;
|
||||
struct work_struct work;
|
||||
struct ucsi_connector_capability cap;
|
||||
};
|
||||
|
||||
struct ucsi {
|
||||
struct device *dev;
|
||||
struct ucsi_data __iomem *data;
|
||||
|
||||
enum ucsi_status status;
|
||||
struct completion complete;
|
||||
struct ucsi_capability cap;
|
||||
struct ucsi_connector *connector;
|
||||
|
||||
/* device lock */
|
||||
spinlock_t dev_lock;
|
||||
|
||||
/* PPM Communication lock */
|
||||
struct mutex ppm_lock;
|
||||
|
||||
/* PPM communication flags */
|
||||
unsigned long flags;
|
||||
#define EVENT_PENDING 0
|
||||
#define COMMAND_PENDING 1
|
||||
};
|
||||
|
||||
static int ucsi_acpi_cmd(struct ucsi *ucsi, struct ucsi_control *ctrl)
|
||||
{
|
||||
uuid_le uuid = UUID_LE(0x6f8398c2, 0x7ca4, 0x11e4,
|
||||
0xad, 0x36, 0x63, 0x10, 0x42, 0xb5, 0x00, 0x8f);
|
||||
union acpi_object *obj;
|
||||
|
||||
ucsi->data->ctrl.raw_cmd = ctrl->raw_cmd;
|
||||
|
||||
obj = acpi_evaluate_dsm(ACPI_HANDLE(ucsi->dev), uuid.b, 1, 1, NULL);
|
||||
if (!obj) {
|
||||
dev_err(ucsi->dev, "%s: failed to evaluate _DSM\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ACPI_FREE(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct ucsi *ucsi = data;
|
||||
struct ucsi_cci *cci;
|
||||
|
||||
spin_lock(&ucsi->dev_lock);
|
||||
|
||||
ucsi->status = UCSI_IDLE;
|
||||
cci = &ucsi->data->cci;
|
||||
|
||||
/*
|
||||
* REVISIT: This is not documented behavior, but all known PPMs ACK
|
||||
* asynchronous events by sending notification with cleared CCI.
|
||||
*/
|
||||
if (!ucsi->data->raw_cci) {
|
||||
if (test_bit(EVENT_PENDING, &ucsi->flags))
|
||||
complete(&ucsi->complete);
|
||||
else
|
||||
dev_WARN(ucsi->dev, "spurious notification\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (test_bit(COMMAND_PENDING, &ucsi->flags)) {
|
||||
if (cci->busy) {
|
||||
ucsi->status = UCSI_BUSY;
|
||||
complete(&ucsi->complete);
|
||||
|
||||
goto out_unlock;
|
||||
} else if (cci->ack_complete || cci->cmd_complete) {
|
||||
/* Error Indication is only valid with commands */
|
||||
if (cci->error && cci->cmd_complete)
|
||||
ucsi->status = UCSI_ERROR;
|
||||
|
||||
ucsi->data->ctrl.raw_cmd = 0;
|
||||
complete(&ucsi->complete);
|
||||
}
|
||||
}
|
||||
|
||||
if (cci->connector_change) {
|
||||
struct ucsi_connector *con;
|
||||
|
||||
/*
|
||||
* This is workaround for buggy PPMs that create asynchronous
|
||||
* event notifications before OPM has enabled them.
|
||||
*/
|
||||
if (!ucsi->connector)
|
||||
goto out_unlock;
|
||||
|
||||
con = ucsi->connector + (cci->connector_change - 1);
|
||||
|
||||
/*
|
||||
* PPM will not clear the connector specific bit in Connector
|
||||
* Change Indication field of CCI until the driver has ACK it,
|
||||
* and the driver can not ACK it before it has been processed.
|
||||
* The PPM will not generate new events before the first has
|
||||
* been acknowledged, even if they are for an other connector.
|
||||
* So only one event at a time.
|
||||
*/
|
||||
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
|
||||
schedule_work(&con->work);
|
||||
}
|
||||
out_unlock:
|
||||
spin_unlock(&ucsi->dev_lock);
|
||||
}
|
||||
|
||||
static int ucsi_ack(struct ucsi *ucsi, u8 cmd)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
int ret;
|
||||
|
||||
ctrl.cmd.cmd = UCSI_ACK_CC_CI;
|
||||
ctrl.cmd.length = 0;
|
||||
ctrl.cmd.data = cmd;
|
||||
ret = ucsi_acpi_cmd(ucsi, &ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Waiting for ACK also with ACK CMD for now */
|
||||
ret = wait_for_completion_timeout(&ucsi->complete,
|
||||
msecs_to_jiffies(UCSI_TIMEOUT_MS));
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_run_cmd(struct ucsi *ucsi, struct ucsi_control *ctrl,
|
||||
void *data, size_t size)
|
||||
{
|
||||
u16 err_value = 0;
|
||||
int ret;
|
||||
|
||||
set_bit(COMMAND_PENDING, &ucsi->flags);
|
||||
|
||||
ret = ucsi_acpi_cmd(ucsi, ctrl);
|
||||
if (ret)
|
||||
goto err_clear_flag;
|
||||
|
||||
ret = wait_for_completion_timeout(&ucsi->complete,
|
||||
msecs_to_jiffies(UCSI_TIMEOUT_MS));
|
||||
if (!ret) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clear_flag;
|
||||
}
|
||||
|
||||
switch (ucsi->status) {
|
||||
case UCSI_IDLE:
|
||||
if (data)
|
||||
memcpy(data, ucsi->data->message_in, size);
|
||||
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
|
||||
break;
|
||||
case UCSI_BUSY:
|
||||
/* The caller decides whether to cancel or not */
|
||||
ret = -EBUSY;
|
||||
goto err_clear_flag;
|
||||
case UCSI_ERROR:
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
|
||||
if (ret)
|
||||
goto err_clear_flag;
|
||||
|
||||
ctrl->cmd.cmd = UCSI_GET_ERROR_STATUS;
|
||||
ctrl->cmd.length = 0;
|
||||
ctrl->cmd.data = 0;
|
||||
ret = ucsi_acpi_cmd(ucsi, ctrl);
|
||||
if (ret)
|
||||
goto err_clear_flag;
|
||||
|
||||
ret = wait_for_completion_timeout(&ucsi->complete,
|
||||
msecs_to_jiffies(UCSI_TIMEOUT_MS));
|
||||
if (!ret) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clear_flag;
|
||||
}
|
||||
|
||||
memcpy(&err_value, ucsi->data->message_in, sizeof(err_value));
|
||||
|
||||
/* Something has really gone wrong */
|
||||
if (WARN_ON(ucsi->status == UCSI_ERROR)) {
|
||||
ret = -ENODEV;
|
||||
goto err_clear_flag;
|
||||
}
|
||||
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
|
||||
if (ret)
|
||||
goto err_clear_flag;
|
||||
|
||||
switch (err_value) {
|
||||
case UCSI_ERROR_INCOMPATIBLE_PARTNER:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
case UCSI_ERROR_CC_COMMUNICATION_ERR:
|
||||
ret = -ECOMM;
|
||||
break;
|
||||
case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
|
||||
ret = -EIO;
|
||||
break;
|
||||
case UCSI_ERROR_DEAD_BATTERY:
|
||||
dev_warn(ucsi->dev, "Dead battery condition!\n");
|
||||
ret = -EPERM;
|
||||
break;
|
||||
/* The following mean a bug in this driver */
|
||||
case UCSI_ERROR_INVALID_CON_NUM:
|
||||
case UCSI_ERROR_UNREGONIZED_CMD:
|
||||
case UCSI_ERROR_INVALID_CMD_ARGUMENT:
|
||||
default:
|
||||
dev_warn(ucsi->dev,
|
||||
"%s: possible UCSI driver bug - error %hu\n",
|
||||
__func__, err_value);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ctrl->raw_cmd = 0;
|
||||
err_clear_flag:
|
||||
clear_bit(COMMAND_PENDING, &ucsi->flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ucsi_connector_change(struct work_struct *work)
|
||||
{
|
||||
struct ucsi_connector *con = container_of(work, struct ucsi_connector,
|
||||
work);
|
||||
struct ucsi_connector_status constat;
|
||||
struct ucsi *ucsi = con->ucsi;
|
||||
struct ucsi_control ctrl;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ucsi->ppm_lock);
|
||||
|
||||
ctrl.cmd.cmd = UCSI_GET_CONNECTOR_STATUS;
|
||||
ctrl.cmd.length = 0;
|
||||
ctrl.cmd.data = con->num;
|
||||
ret = ucsi_run_cmd(con->ucsi, &ctrl, &constat, sizeof(constat));
|
||||
if (ret) {
|
||||
dev_err(ucsi->dev, "%s: failed to read connector status (%d)\n",
|
||||
__func__, ret);
|
||||
goto out_ack_event;
|
||||
}
|
||||
|
||||
/* Ignoring disconnections and Alternate Modes */
|
||||
if (!constat.connected || !(constat.change &
|
||||
(UCSI_CONSTAT_PARTNER_CHANGE | UCSI_CONSTAT_CONNECT_CHANGE)) ||
|
||||
constat.partner_flags & UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE)
|
||||
goto out_ack_event;
|
||||
|
||||
/* If the partner got USB Host role, attempting swap */
|
||||
if (constat.partner_type & UCSI_CONSTAT_PARTNER_TYPE_DFP) {
|
||||
ctrl.uor.cmd = UCSI_SET_UOR;
|
||||
ctrl.uor.con_num = con->num;
|
||||
ctrl.uor.role = UCSI_UOR_ROLE_DFP;
|
||||
|
||||
ret = ucsi_run_cmd(con->ucsi, &ctrl, NULL, 0);
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "%s: failed to swap role (%d)\n",
|
||||
__func__, ret);
|
||||
}
|
||||
out_ack_event:
|
||||
ucsi_ack(ucsi, UCSI_ACK_EVENT);
|
||||
clear_bit(EVENT_PENDING, &ucsi->flags);
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
}
|
||||
|
||||
static int ucsi_reset_ppm(struct ucsi *ucsi)
|
||||
{
|
||||
int timeout = UCSI_TIMEOUT_MS;
|
||||
struct ucsi_control ctrl;
|
||||
int ret;
|
||||
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.cmd.cmd = UCSI_PPM_RESET;
|
||||
ret = ucsi_acpi_cmd(ucsi, &ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* There is no quarantee the PPM will ever set the RESET_COMPLETE bit */
|
||||
while (!ucsi->data->cci.reset_complete && timeout--)
|
||||
usleep_range(1000, 2000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_init(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_connector *con;
|
||||
struct ucsi_control ctrl;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
init_completion(&ucsi->complete);
|
||||
spin_lock_init(&ucsi->dev_lock);
|
||||
mutex_init(&ucsi->ppm_lock);
|
||||
|
||||
/* Reset the PPM */
|
||||
ret = ucsi_reset_ppm(ucsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* REVISIT: Executing second reset to WA an issue seen on some of the
|
||||
* Broxton based platforms, where the first reset puts the PPM into a
|
||||
* state where it's unable to recognise some of the commands.
|
||||
*/
|
||||
ret = ucsi_reset_ppm(ucsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&ucsi->ppm_lock);
|
||||
|
||||
/* Enable basic notifications */
|
||||
ctrl.cmd.cmd = UCSI_SET_NOTIFICATION_ENABLE;
|
||||
ctrl.cmd.length = 0;
|
||||
ctrl.cmd.data = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
|
||||
ret = ucsi_run_cmd(ucsi, &ctrl, NULL, 0);
|
||||
if (ret)
|
||||
goto err_reset;
|
||||
|
||||
/* Get PPM capabilities */
|
||||
ctrl.cmd.cmd = UCSI_GET_CAPABILITY;
|
||||
ret = ucsi_run_cmd(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap));
|
||||
if (ret)
|
||||
goto err_reset;
|
||||
|
||||
if (!ucsi->cap.num_connectors) {
|
||||
ret = -ENODEV;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
ucsi->connector = devm_kcalloc(ucsi->dev, ucsi->cap.num_connectors,
|
||||
sizeof(*ucsi->connector), GFP_KERNEL);
|
||||
if (!ucsi->connector) {
|
||||
ret = -ENOMEM;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
for (i = 1, con = ucsi->connector; i < ucsi->cap.num_connectors + 1;
|
||||
i++, con++) {
|
||||
/* Get connector capability */
|
||||
ctrl.cmd.cmd = UCSI_GET_CONNECTOR_CAPABILITY;
|
||||
ctrl.cmd.data = i;
|
||||
ret = ucsi_run_cmd(ucsi, &ctrl, &con->cap, sizeof(con->cap));
|
||||
if (ret)
|
||||
goto err_reset;
|
||||
|
||||
con->num = i;
|
||||
con->ucsi = ucsi;
|
||||
INIT_WORK(&con->work, ucsi_connector_change);
|
||||
}
|
||||
|
||||
/* Enable all notifications */
|
||||
ctrl.cmd.cmd = UCSI_SET_NOTIFICATION_ENABLE;
|
||||
ctrl.cmd.data = UCSI_ENABLE_NTFY_ALL;
|
||||
ret = ucsi_run_cmd(ucsi, &ctrl, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err_reset;
|
||||
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
return 0;
|
||||
err_reset:
|
||||
ucsi_reset_ppm(ucsi);
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
acpi_status status;
|
||||
struct ucsi *ucsi;
|
||||
int ret;
|
||||
|
||||
ucsi = devm_kzalloc(&pdev->dev, sizeof(*ucsi), GFP_KERNEL);
|
||||
if (!ucsi)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: ACPI has claimed the memory region as it's also an Operation
|
||||
* Region. It's not possible to request it in the driver.
|
||||
*/
|
||||
ucsi->data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!ucsi->data)
|
||||
return -ENOMEM;
|
||||
|
||||
ucsi->dev = &pdev->dev;
|
||||
|
||||
status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
|
||||
ACPI_ALL_NOTIFY,
|
||||
ucsi_acpi_notify, ucsi);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
ret = ucsi_init(ucsi);
|
||||
if (ret) {
|
||||
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
|
||||
ACPI_ALL_NOTIFY,
|
||||
ucsi_acpi_notify);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ucsi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_acpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ucsi *ucsi = platform_get_drvdata(pdev);
|
||||
|
||||
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
|
||||
ACPI_ALL_NOTIFY, ucsi_acpi_notify);
|
||||
|
||||
/* Make sure there are no events in the middle of being processed */
|
||||
if (wait_on_bit_timeout(&ucsi->flags, EVENT_PENDING,
|
||||
TASK_UNINTERRUPTIBLE,
|
||||
msecs_to_jiffies(UCSI_TIMEOUT_MS)))
|
||||
dev_WARN(ucsi->dev, "%s: Events still pending\n", __func__);
|
||||
|
||||
ucsi_reset_ppm(ucsi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id ucsi_acpi_match[] = {
|
||||
{ "PNP0CA0", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
|
||||
|
||||
static struct platform_driver ucsi_acpi_platform_driver = {
|
||||
.driver = {
|
||||
.name = "ucsi_acpi",
|
||||
.acpi_match_table = ACPI_PTR(ucsi_acpi_match),
|
||||
},
|
||||
.probe = ucsi_acpi_probe,
|
||||
.remove = ucsi_acpi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ucsi_acpi_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("USB Type-C System Software Interface (UCSI) driver");
|
|
@ -0,0 +1,215 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Command Status and Connector Change Indication (CCI) data structure */
|
||||
struct ucsi_cci {
|
||||
unsigned int RESERVED1:1;
|
||||
unsigned int connector_change:7;
|
||||
u8 data_length;
|
||||
unsigned int RESERVED9:9;
|
||||
unsigned int not_supported:1;
|
||||
unsigned int cancel_complete:1;
|
||||
unsigned int reset_complete:1;
|
||||
unsigned int busy:1;
|
||||
unsigned int ack_complete:1;
|
||||
unsigned int error:1;
|
||||
unsigned int cmd_complete:1;
|
||||
} __packed;
|
||||
|
||||
/* Default fields in CONTROL data structure */
|
||||
struct ucsi_command {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u64 data:48;
|
||||
} __packed;
|
||||
|
||||
/* Set USB Operation Mode Command structure */
|
||||
struct ucsi_uor_cmd {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u64 con_num:7;
|
||||
u64 role:3;
|
||||
#define UCSI_UOR_ROLE_DFP BIT(0)
|
||||
#define UCSI_UOR_ROLE_UFP BIT(1)
|
||||
#define UCSI_UOR_ROLE_DRP BIT(2)
|
||||
u64 data:38;
|
||||
} __packed;
|
||||
|
||||
struct ucsi_control {
|
||||
union {
|
||||
u64 raw_cmd;
|
||||
struct ucsi_command cmd;
|
||||
struct ucsi_uor_cmd uor;
|
||||
};
|
||||
};
|
||||
|
||||
struct ucsi_data {
|
||||
u16 version;
|
||||
u16 RESERVED;
|
||||
union {
|
||||
u32 raw_cci;
|
||||
struct ucsi_cci cci;
|
||||
};
|
||||
struct ucsi_control ctrl;
|
||||
u32 message_in[4];
|
||||
u32 message_out[4];
|
||||
} __packed;
|
||||
|
||||
/* Commands */
|
||||
#define UCSI_PPM_RESET 0x01
|
||||
#define UCSI_CANCEL 0x02
|
||||
#define UCSI_CONNECTOR_RESET 0x03
|
||||
#define UCSI_ACK_CC_CI 0x04
|
||||
#define UCSI_SET_NOTIFICATION_ENABLE 0x05
|
||||
#define UCSI_GET_CAPABILITY 0x06
|
||||
#define UCSI_GET_CONNECTOR_CAPABILITY 0x07
|
||||
#define UCSI_SET_UOM 0x08
|
||||
#define UCSI_SET_UOR 0x09
|
||||
#define UCSI_SET_PDM 0x0A
|
||||
#define UCSI_SET_PDR 0x0B
|
||||
#define UCSI_GET_ALTERNATE_MODES 0x0C
|
||||
#define UCSI_GET_CAM_SUPPORTED 0x0D
|
||||
#define UCSI_GET_CURRENT_CAM 0x0E
|
||||
#define UCSI_SET_NEW_CAM 0x0F
|
||||
#define UCSI_GET_PDOS 0x10
|
||||
#define UCSI_GET_CABLE_PROPERTY 0x11
|
||||
#define UCSI_GET_CONNECTOR_STATUS 0x12
|
||||
#define UCSI_GET_ERROR_STATUS 0x13
|
||||
|
||||
/* ACK_CC_CI commands */
|
||||
#define UCSI_ACK_EVENT 1
|
||||
#define UCSI_ACK_CMD 2
|
||||
|
||||
/* Bits for SET_NOTIFICATION_ENABLE command */
|
||||
#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0)
|
||||
#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1)
|
||||
#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(2)
|
||||
#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(5)
|
||||
#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(6)
|
||||
#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(7)
|
||||
#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(8)
|
||||
#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(9)
|
||||
#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(11)
|
||||
#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(12)
|
||||
#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(14)
|
||||
#define UCSI_ENABLE_NTFY_ERROR BIT(15)
|
||||
#define UCSI_ENABLE_NTFY_ALL 0xdbe7
|
||||
|
||||
/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
|
||||
#define UCSI_ERROR_UNREGONIZED_CMD BIT(0)
|
||||
#define UCSI_ERROR_INVALID_CON_NUM BIT(1)
|
||||
#define UCSI_ERROR_INVALID_CMD_ARGUMENT BIT(2)
|
||||
#define UCSI_ERROR_INCOMPATIBLE_PARTNER BIT(3)
|
||||
#define UCSI_ERROR_CC_COMMUNICATION_ERR BIT(4)
|
||||
#define UCSI_ERROR_DEAD_BATTERY BIT(5)
|
||||
#define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL BIT(6)
|
||||
|
||||
/* Data structure filled by PPM in response to GET_CAPABILITY command. */
|
||||
struct ucsi_capability {
|
||||
u32 attributes;
|
||||
#define UCSI_CAP_ATTR_DISABLE_STATE BIT(0)
|
||||
#define UCSI_CAP_ATTR_BATTERY_CHARGING BIT(1)
|
||||
#define UCSI_CAP_ATTR_USB_PD BIT(2)
|
||||
#define UCSI_CAP_ATTR_TYPEC_CURRENT BIT(6)
|
||||
#define UCSI_CAP_ATTR_POWER_AC_SUPPLY BIT(8)
|
||||
#define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
|
||||
#define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
|
||||
u8 num_connectors;
|
||||
u32 features:24;
|
||||
#define UCSI_CAP_SET_UOM BIT(0)
|
||||
#define UCSI_CAP_SET_PDM BIT(1)
|
||||
#define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
|
||||
#define UCSI_CAP_ALT_MODE_OVERRIDE BIT(3)
|
||||
#define UCSI_CAP_PDO_DETAILS BIT(4)
|
||||
#define UCSI_CAP_CABLE_DETAILS BIT(5)
|
||||
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
|
||||
#define UCSI_CAP_PD_RESET BIT(7)
|
||||
u8 num_alt_modes;
|
||||
u8 RESERVED;
|
||||
u16 bc_version;
|
||||
u16 pd_version;
|
||||
u16 typec_version;
|
||||
} __packed;
|
||||
|
||||
/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */
|
||||
struct ucsi_connector_capability {
|
||||
u8 op_mode;
|
||||
#define UCSI_CONCAP_OPMODE_DFP BIT(0)
|
||||
#define UCSI_CONCAP_OPMODE_UFP BIT(1)
|
||||
#define UCSI_CONCAP_OPMODE_DRP BIT(2)
|
||||
#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY BIT(3)
|
||||
#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY BIT(4)
|
||||
#define UCSI_CONCAP_OPMODE_USB2 BIT(5)
|
||||
#define UCSI_CONCAP_OPMODE_USB3 BIT(6)
|
||||
#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7)
|
||||
u8 provider:1;
|
||||
u8 consumer:1;
|
||||
} __packed;
|
||||
|
||||
/* Data structure filled by PPM in response to GET_CABLE_PROPERTY command. */
|
||||
struct ucsi_cable_property {
|
||||
u16 speed_supported;
|
||||
u8 current_capability;
|
||||
u8 vbus_in_cable:1;
|
||||
u8 active_cable:1;
|
||||
u8 directionality:1;
|
||||
u8 plug_type:2;
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
|
||||
u8 mode_support:1;
|
||||
u8 RESERVED_2:2;
|
||||
u8 latency:4;
|
||||
u8 RESERVED_4:4;
|
||||
} __packed;
|
||||
|
||||
/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
|
||||
struct ucsi_connector_status {
|
||||
u16 change;
|
||||
#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1)
|
||||
#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2)
|
||||
#define UCSI_CONSTAT_PDOS_CHANGE BIT(5)
|
||||
#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6)
|
||||
#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7)
|
||||
#define UCSI_CONSTAT_CAM_CHANGE BIT(8)
|
||||
#define UCSI_CONSTAT_BC_CHANGE BIT(9)
|
||||
#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11)
|
||||
#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12)
|
||||
#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14)
|
||||
#define UCSI_CONSTAT_ERROR BIT(15)
|
||||
u16 pwr_op_mode:3;
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_BC 2
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_PD 3
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_3 4
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
|
||||
u16 connected:1;
|
||||
u16 pwr_dir:1;
|
||||
u16 partner_flags:8;
|
||||
#define UCSI_CONSTAT_PARTNER_FLAG_USB BIT(0)
|
||||
#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE BIT(1)
|
||||
u16 partner_type:3;
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_NO_UFP 3 /* Powered Cable */
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
|
||||
u32 request_data_obj;
|
||||
u8 bc_status:2;
|
||||
#define UCSI_CONSTAT_BC_NOT_CHARGING 0
|
||||
#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
|
||||
#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
|
||||
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
|
||||
u8 provider_cap_limit_reason:4;
|
||||
#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
|
||||
#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
|
||||
u8 RESERVED:2;
|
||||
} __packed;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
@ -287,6 +287,9 @@ static struct urb *usbtest_alloc_urb(
|
|||
if (usb_pipein(pipe))
|
||||
urb->transfer_flags |= URB_SHORT_NOT_OK;
|
||||
|
||||
if ((bytes + offset) == 0)
|
||||
return urb;
|
||||
|
||||
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|
||||
urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
|
||||
GFP_KERNEL, &urb->transfer_dma);
|
||||
|
@ -529,6 +532,7 @@ static struct scatterlist *
|
|||
alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
unsigned int n_size = 0;
|
||||
unsigned i;
|
||||
unsigned size = max;
|
||||
unsigned maxpacket =
|
||||
|
@ -561,7 +565,8 @@ alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
|
|||
break;
|
||||
case 1:
|
||||
for (j = 0; j < size; j++)
|
||||
*buf++ = (u8) ((j % maxpacket) % 63);
|
||||
*buf++ = (u8) (((j + n_size) % maxpacket) % 63);
|
||||
n_size += size;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,10 +240,7 @@ static int phy_8x16_read_devicetree(struct phy_8x16 *qphy)
|
|||
|
||||
qphy->switch_gpio = devm_gpiod_get_optional(dev, "switch",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(qphy->switch_gpio))
|
||||
return PTR_ERR(qphy->switch_gpio);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(qphy->switch_gpio);
|
||||
}
|
||||
|
||||
static int phy_8x16_reboot_notify(struct notifier_block *this,
|
||||
|
|
|
@ -799,8 +799,10 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
|||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
|
||||
struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
|
||||
struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
|
||||
|
||||
return info->dma_map_ctrl(pkt, map);
|
||||
return info->dma_map_ctrl(chan->device->dev, pkt, map);
|
||||
}
|
||||
|
||||
static void usbhsf_dma_complete(void *arg);
|
||||
|
@ -881,12 +883,12 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
|||
if (!fifo)
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
if (usbhsf_dma_map(pkt) < 0)
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
ret = usbhsf_fifo_select(pipe, fifo, 0);
|
||||
if (ret < 0)
|
||||
goto usbhsf_pio_prepare_push_unmap;
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
if (usbhsf_dma_map(pkt) < 0)
|
||||
goto usbhsf_pio_prepare_push_unselect;
|
||||
|
||||
pkt->trans = len;
|
||||
|
||||
|
@ -896,8 +898,8 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
|||
|
||||
return 0;
|
||||
|
||||
usbhsf_pio_prepare_push_unmap:
|
||||
usbhsf_dma_unmap(pkt);
|
||||
usbhsf_pio_prepare_push_unselect:
|
||||
usbhsf_fifo_unselect(pipe, fifo);
|
||||
usbhsf_pio_prepare_push:
|
||||
/*
|
||||
* change handler to PIO
|
||||
|
|
|
@ -191,13 +191,12 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep,
|
|||
/*
|
||||
* dma map/unmap
|
||||
*/
|
||||
static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
||||
static int usbhsg_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt,
|
||||
int map)
|
||||
{
|
||||
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
|
||||
struct usb_request *req = &ureq->req;
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
|
||||
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
|
||||
enum dma_data_direction dir;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -207,13 +206,13 @@ static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
|||
/* it can not use scatter/gather */
|
||||
WARN_ON(req->num_sgs);
|
||||
|
||||
ret = usb_gadget_map_request(&gpriv->gadget, req, dir);
|
||||
ret = usb_gadget_map_request_by_dev(dma_dev, req, dir);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pkt->dma = req->dma;
|
||||
} else {
|
||||
usb_gadget_unmap_request(&gpriv->gadget, req, dir);
|
||||
usb_gadget_unmap_request_by_dev(dma_dev, req, dir);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -929,7 +929,8 @@ static int usbhsh_dcp_queue_push(struct usb_hcd *hcd,
|
|||
/*
|
||||
* dma map functions
|
||||
*/
|
||||
static int usbhsh_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
||||
static int usbhsh_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt,
|
||||
int map)
|
||||
{
|
||||
if (map) {
|
||||
struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
|
||||
|
|
|
@ -391,9 +391,8 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len)
|
|||
/*
|
||||
* pipe setup
|
||||
*/
|
||||
static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
||||
int is_host,
|
||||
int dir_in)
|
||||
static int usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, int is_host,
|
||||
int dir_in, u16 *pipecfg)
|
||||
{
|
||||
u16 type = 0;
|
||||
u16 bfre = 0;
|
||||
|
@ -451,14 +450,14 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
|||
|
||||
/* EPNUM */
|
||||
epnum = 0; /* see usbhs_pipe_config_update() */
|
||||
|
||||
return type |
|
||||
*pipecfg = type |
|
||||
bfre |
|
||||
dblb |
|
||||
cntmd |
|
||||
dir |
|
||||
shtnak |
|
||||
epnum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
|
||||
|
@ -655,7 +654,8 @@ static void usbhsp_put_pipe(struct usbhs_pipe *pipe)
|
|||
}
|
||||
|
||||
void usbhs_pipe_init(struct usbhs_priv *priv,
|
||||
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map))
|
||||
int (*dma_map_ctrl)(struct device *dma_dev,
|
||||
struct usbhs_pkt *pkt, int map))
|
||||
{
|
||||
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
|
||||
struct usbhs_pipe *pipe;
|
||||
|
@ -702,7 +702,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in);
|
||||
if (usbhsp_setup_pipecfg(pipe, is_host, dir_in, &pipecfg)) {
|
||||
dev_err(dev, "can't setup pipe\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pipebuf = usbhsp_setup_pipebuff(pipe);
|
||||
|
||||
usbhsp_pipe_select(pipe);
|
||||
|
|
|
@ -47,7 +47,8 @@ struct usbhs_pipe_info {
|
|||
struct usbhs_pipe *pipe;
|
||||
int size; /* array size of "pipe" */
|
||||
|
||||
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map);
|
||||
int (*dma_map_ctrl)(struct device *dma_dev, struct usbhs_pkt *pkt,
|
||||
int map);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -84,7 +85,8 @@ int usbhs_pipe_is_running(struct usbhs_pipe *pipe);
|
|||
void usbhs_pipe_running(struct usbhs_pipe *pipe, int running);
|
||||
|
||||
void usbhs_pipe_init(struct usbhs_priv *priv,
|
||||
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
|
||||
int (*dma_map_ctrl)(struct device *dma_dev,
|
||||
struct usbhs_pkt *pkt, int map));
|
||||
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_clear(struct usbhs_pipe *pipe);
|
||||
int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
|
||||
|
|
|
@ -331,6 +331,42 @@ struct cp210x_comm_status {
|
|||
*/
|
||||
#define PURGE_ALL 0x000f
|
||||
|
||||
/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
|
||||
struct cp210x_flow_ctl {
|
||||
__le32 ulControlHandshake;
|
||||
__le32 ulFlowReplace;
|
||||
__le32 ulXonLimit;
|
||||
__le32 ulXoffLimit;
|
||||
} __packed;
|
||||
|
||||
/* cp210x_flow_ctl::ulControlHandshake */
|
||||
#define CP210X_SERIAL_DTR_MASK GENMASK(1, 0)
|
||||
#define CP210X_SERIAL_DTR_SHIFT(_mode) (_mode)
|
||||
#define CP210X_SERIAL_CTS_HANDSHAKE BIT(3)
|
||||
#define CP210X_SERIAL_DSR_HANDSHAKE BIT(4)
|
||||
#define CP210X_SERIAL_DCD_HANDSHAKE BIT(5)
|
||||
#define CP210X_SERIAL_DSR_SENSITIVITY BIT(6)
|
||||
|
||||
/* values for cp210x_flow_ctl::ulControlHandshake::CP210X_SERIAL_DTR_MASK */
|
||||
#define CP210X_SERIAL_DTR_INACTIVE 0
|
||||
#define CP210X_SERIAL_DTR_ACTIVE 1
|
||||
#define CP210X_SERIAL_DTR_FLOW_CTL 2
|
||||
|
||||
/* cp210x_flow_ctl::ulFlowReplace */
|
||||
#define CP210X_SERIAL_AUTO_TRANSMIT BIT(0)
|
||||
#define CP210X_SERIAL_AUTO_RECEIVE BIT(1)
|
||||
#define CP210X_SERIAL_ERROR_CHAR BIT(2)
|
||||
#define CP210X_SERIAL_NULL_STRIPPING BIT(3)
|
||||
#define CP210X_SERIAL_BREAK_CHAR BIT(4)
|
||||
#define CP210X_SERIAL_RTS_MASK GENMASK(7, 6)
|
||||
#define CP210X_SERIAL_RTS_SHIFT(_mode) (_mode << 6)
|
||||
#define CP210X_SERIAL_XOFF_CONTINUE BIT(31)
|
||||
|
||||
/* values for cp210x_flow_ctl::ulFlowReplace::CP210X_SERIAL_RTS_MASK */
|
||||
#define CP210X_SERIAL_RTS_INACTIVE 0
|
||||
#define CP210X_SERIAL_RTS_ACTIVE 1
|
||||
#define CP210X_SERIAL_RTS_FLOW_CTL 2
|
||||
|
||||
/*
|
||||
* Reads a variable-sized block of CP210X_ registers, identified by req.
|
||||
* Returns data into buf in native USB byte order.
|
||||
|
@ -698,9 +734,10 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
|
|||
{
|
||||
struct device *dev = &port->dev;
|
||||
unsigned int cflag;
|
||||
u8 modem_ctl[16];
|
||||
struct cp210x_flow_ctl flow_ctl;
|
||||
u32 baud;
|
||||
u16 bits;
|
||||
u32 ctl_hs;
|
||||
|
||||
cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud);
|
||||
|
||||
|
@ -796,9 +833,10 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
|
|||
break;
|
||||
}
|
||||
|
||||
cp210x_read_reg_block(port, CP210X_GET_FLOW, modem_ctl,
|
||||
sizeof(modem_ctl));
|
||||
if (modem_ctl[0] & 0x08) {
|
||||
cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
|
||||
sizeof(flow_ctl));
|
||||
ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
|
||||
if (ctl_hs & CP210X_SERIAL_CTS_HANDSHAKE) {
|
||||
dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
|
||||
cflag |= CRTSCTS;
|
||||
} else {
|
||||
|
@ -867,7 +905,6 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
|||
struct device *dev = &port->dev;
|
||||
unsigned int cflag, old_cflag;
|
||||
u16 bits;
|
||||
u8 modem_ctl[16];
|
||||
|
||||
cflag = tty->termios.c_cflag;
|
||||
old_cflag = old_termios->c_cflag;
|
||||
|
@ -951,35 +988,44 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
|||
}
|
||||
|
||||
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
|
||||
struct cp210x_flow_ctl flow_ctl;
|
||||
u32 ctl_hs;
|
||||
u32 flow_repl;
|
||||
|
||||
/* Only bytes 0, 4 and 7 out of first 8 have functional bits */
|
||||
|
||||
cp210x_read_reg_block(port, CP210X_GET_FLOW, modem_ctl,
|
||||
sizeof(modem_ctl));
|
||||
dev_dbg(dev, "%s - read modem controls = %02x .. .. .. %02x .. .. %02x\n",
|
||||
__func__, modem_ctl[0], modem_ctl[4], modem_ctl[7]);
|
||||
cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
|
||||
sizeof(flow_ctl));
|
||||
ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
|
||||
flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace);
|
||||
dev_dbg(dev, "%s - read ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
|
||||
__func__, ctl_hs, flow_repl);
|
||||
|
||||
ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE;
|
||||
ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE;
|
||||
ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY;
|
||||
ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
|
||||
ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE);
|
||||
if (cflag & CRTSCTS) {
|
||||
modem_ctl[0] &= ~0x7B;
|
||||
modem_ctl[0] |= 0x09;
|
||||
modem_ctl[4] = 0x80;
|
||||
/* FIXME - why clear reserved bits just read? */
|
||||
modem_ctl[5] = 0;
|
||||
modem_ctl[6] = 0;
|
||||
modem_ctl[7] = 0;
|
||||
ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE;
|
||||
|
||||
flow_repl &= ~CP210X_SERIAL_RTS_MASK;
|
||||
flow_repl |= CP210X_SERIAL_RTS_SHIFT(
|
||||
CP210X_SERIAL_RTS_FLOW_CTL);
|
||||
dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
|
||||
} else {
|
||||
modem_ctl[0] &= ~0x7B;
|
||||
modem_ctl[0] |= 0x01;
|
||||
/* FIXME - OR here instead of assignment looks wrong */
|
||||
modem_ctl[4] |= 0x40;
|
||||
ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
|
||||
|
||||
flow_repl &= ~CP210X_SERIAL_RTS_MASK;
|
||||
flow_repl |= CP210X_SERIAL_RTS_SHIFT(
|
||||
CP210X_SERIAL_RTS_ACTIVE);
|
||||
dev_dbg(dev, "%s - flow control = NONE\n", __func__);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s - write modem controls = %02x .. .. .. %02x .. .. %02x\n",
|
||||
__func__, modem_ctl[0], modem_ctl[4], modem_ctl[7]);
|
||||
cp210x_write_reg_block(port, CP210X_SET_FLOW, modem_ctl,
|
||||
sizeof(modem_ctl));
|
||||
dev_dbg(dev, "%s - write ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
|
||||
__func__, ctl_hs, flow_repl);
|
||||
flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs);
|
||||
flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
|
||||
cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl,
|
||||
sizeof(flow_ctl));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -93,27 +93,27 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial);
|
|||
static void ftdi_USB_UIRT_setup(struct ftdi_private *priv);
|
||||
static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv);
|
||||
|
||||
static struct ftdi_sio_quirk ftdi_jtag_quirk = {
|
||||
static const struct ftdi_sio_quirk ftdi_jtag_quirk = {
|
||||
.probe = ftdi_jtag_probe,
|
||||
};
|
||||
|
||||
static struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
|
||||
static const struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
|
||||
.probe = ftdi_NDI_device_setup,
|
||||
};
|
||||
|
||||
static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
|
||||
static const struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
|
||||
.port_probe = ftdi_USB_UIRT_setup,
|
||||
};
|
||||
|
||||
static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
|
||||
static const struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
|
||||
.port_probe = ftdi_HE_TIRA1_setup,
|
||||
};
|
||||
|
||||
static struct ftdi_sio_quirk ftdi_stmclite_quirk = {
|
||||
static const struct ftdi_sio_quirk ftdi_stmclite_quirk = {
|
||||
.probe = ftdi_stmclite_probe,
|
||||
};
|
||||
|
||||
static struct ftdi_sio_quirk ftdi_8u2232c_quirk = {
|
||||
static const struct ftdi_sio_quirk ftdi_8u2232c_quirk = {
|
||||
.probe = ftdi_8u2232c_probe,
|
||||
};
|
||||
|
||||
|
@ -1775,7 +1775,7 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
|
|||
static int ftdi_sio_probe(struct usb_serial *serial,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct ftdi_sio_quirk *quirk =
|
||||
const struct ftdi_sio_quirk *quirk =
|
||||
(struct ftdi_sio_quirk *)id->driver_info;
|
||||
|
||||
if (quirk && quirk->probe) {
|
||||
|
@ -1792,7 +1792,7 @@ static int ftdi_sio_probe(struct usb_serial *serial,
|
|||
static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv;
|
||||
struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
|
||||
const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
|
||||
|
||||
|
||||
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
|
||||
|
|
|
@ -2849,14 +2849,16 @@ static int edge_startup(struct usb_serial *serial)
|
|||
/* not set up yet, so do it now */
|
||||
edge_serial->interrupt_read_urb =
|
||||
usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!edge_serial->interrupt_read_urb)
|
||||
return -ENOMEM;
|
||||
if (!edge_serial->interrupt_read_urb) {
|
||||
response = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
edge_serial->interrupt_in_buffer =
|
||||
kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!edge_serial->interrupt_in_buffer) {
|
||||
usb_free_urb(edge_serial->interrupt_read_urb);
|
||||
return -ENOMEM;
|
||||
response = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
edge_serial->interrupt_in_endpoint =
|
||||
endpoint->bEndpointAddress;
|
||||
|
@ -2884,14 +2886,16 @@ static int edge_startup(struct usb_serial *serial)
|
|||
/* not set up yet, so do it now */
|
||||
edge_serial->read_urb =
|
||||
usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!edge_serial->read_urb)
|
||||
return -ENOMEM;
|
||||
if (!edge_serial->read_urb) {
|
||||
response = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
edge_serial->bulk_in_buffer =
|
||||
kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!edge_serial->bulk_in_buffer) {
|
||||
usb_free_urb(edge_serial->read_urb);
|
||||
return -ENOMEM;
|
||||
response = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
edge_serial->bulk_in_endpoint =
|
||||
endpoint->bEndpointAddress;
|
||||
|
@ -2917,9 +2921,22 @@ static int edge_startup(struct usb_serial *serial)
|
|||
}
|
||||
}
|
||||
|
||||
if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
|
||||
dev_err(ddev, "Error - the proper endpoints were not found!\n");
|
||||
return -ENODEV;
|
||||
if (response || !interrupt_in_found || !bulk_in_found ||
|
||||
!bulk_out_found) {
|
||||
if (!response) {
|
||||
dev_err(ddev, "expected endpoints not found\n");
|
||||
response = -ENODEV;
|
||||
}
|
||||
|
||||
usb_free_urb(edge_serial->interrupt_read_urb);
|
||||
kfree(edge_serial->interrupt_in_buffer);
|
||||
|
||||
usb_free_urb(edge_serial->read_urb);
|
||||
kfree(edge_serial->bulk_in_buffer);
|
||||
|
||||
kfree(edge_serial);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/* start interrupt read for this edgeport this interrupt will
|
||||
|
@ -2942,16 +2959,9 @@ static void edge_disconnect(struct usb_serial *serial)
|
|||
{
|
||||
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
|
||||
|
||||
/* stop reads and writes on all ports */
|
||||
/* free up our endpoint stuff */
|
||||
if (edge_serial->is_epic) {
|
||||
usb_kill_urb(edge_serial->interrupt_read_urb);
|
||||
usb_free_urb(edge_serial->interrupt_read_urb);
|
||||
kfree(edge_serial->interrupt_in_buffer);
|
||||
|
||||
usb_kill_urb(edge_serial->read_urb);
|
||||
usb_free_urb(edge_serial->read_urb);
|
||||
kfree(edge_serial->bulk_in_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2964,6 +2974,16 @@ static void edge_release(struct usb_serial *serial)
|
|||
{
|
||||
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
|
||||
|
||||
if (edge_serial->is_epic) {
|
||||
usb_kill_urb(edge_serial->interrupt_read_urb);
|
||||
usb_free_urb(edge_serial->interrupt_read_urb);
|
||||
kfree(edge_serial->interrupt_in_buffer);
|
||||
|
||||
usb_kill_urb(edge_serial->read_urb);
|
||||
usb_free_urb(edge_serial->read_urb);
|
||||
kfree(edge_serial->bulk_in_buffer);
|
||||
}
|
||||
|
||||
kfree(edge_serial);
|
||||
}
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ static int keyspan_write(struct tty_struct *tty,
|
|||
return count;
|
||||
}
|
||||
|
||||
dev_dbg(&port->dev, "%s - endpoint %d flip %d\n",
|
||||
dev_dbg(&port->dev, "%s - endpoint %x flip %d\n",
|
||||
__func__, usb_pipeendpoint(this_urb->pipe), flip);
|
||||
|
||||
if (this_urb->status == -EINPROGRESS) {
|
||||
|
@ -300,7 +300,7 @@ static void usa26_indat_callback(struct urb *urb)
|
|||
endpoint = usb_pipeendpoint(urb->pipe);
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
|
||||
__func__, status, endpoint);
|
||||
return;
|
||||
}
|
||||
|
@ -393,7 +393,8 @@ static void usa26_instat_callback(struct urb *urb)
|
|||
serial = urb->context;
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
if (urb->actual_length != 9) {
|
||||
|
@ -452,7 +453,7 @@ static void usa28_indat_callback(struct urb *urb)
|
|||
|
||||
do {
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
|
||||
__func__, status, usb_pipeendpoint(urb->pipe));
|
||||
return;
|
||||
}
|
||||
|
@ -511,7 +512,8 @@ static void usa28_instat_callback(struct urb *urb)
|
|||
serial = urb->context;
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -591,7 +593,8 @@ static void usa49_instat_callback(struct urb *urb)
|
|||
serial = urb->context;
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -646,7 +649,7 @@ static void usa49_indat_callback(struct urb *urb)
|
|||
endpoint = usb_pipeendpoint(urb->pipe);
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
|
||||
__func__, status, endpoint);
|
||||
return;
|
||||
}
|
||||
|
@ -698,7 +701,8 @@ static void usa49wg_indat_callback(struct urb *urb)
|
|||
serial = urb->context;
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -774,7 +778,7 @@ static void usa90_indat_callback(struct urb *urb)
|
|||
endpoint = usb_pipeendpoint(urb->pipe);
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status %d on endpoint %x\n",
|
||||
__func__, status, endpoint);
|
||||
return;
|
||||
}
|
||||
|
@ -847,7 +851,8 @@ static void usa90_instat_callback(struct urb *urb)
|
|||
serial = urb->context;
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
if (urb->actual_length < 14) {
|
||||
|
@ -912,7 +917,8 @@ static void usa67_instat_callback(struct urb *urb)
|
|||
serial = urb->context;
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1082,12 +1088,6 @@ static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void stop_urb(struct urb *urb)
|
||||
{
|
||||
if (urb && urb->status == -EINPROGRESS)
|
||||
usb_kill_urb(urb);
|
||||
}
|
||||
|
||||
static void keyspan_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct keyspan_port_private *p_priv = usb_get_serial_port_data(port);
|
||||
|
@ -1114,10 +1114,10 @@ static void keyspan_close(struct usb_serial_port *port)
|
|||
p_priv->out_flip = 0;
|
||||
p_priv->in_flip = 0;
|
||||
|
||||
stop_urb(p_priv->inack_urb);
|
||||
usb_kill_urb(p_priv->inack_urb);
|
||||
for (i = 0; i < 2; i++) {
|
||||
stop_urb(p_priv->in_urbs[i]);
|
||||
stop_urb(p_priv->out_urbs[i]);
|
||||
usb_kill_urb(p_priv->in_urbs[i]);
|
||||
usb_kill_urb(p_priv->out_urbs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1221,8 +1221,8 @@ static struct usb_endpoint_descriptor const *find_ep(struct usb_serial const *se
|
|||
if (ep->bEndpointAddress == endpoint)
|
||||
return ep;
|
||||
}
|
||||
dev_warn(&serial->interface->dev, "found no endpoint descriptor for "
|
||||
"endpoint %x\n", endpoint);
|
||||
dev_warn(&serial->interface->dev, "found no endpoint descriptor for endpoint %x\n",
|
||||
endpoint);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1237,7 +1237,8 @@ static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
|
|||
if (endpoint == -1)
|
||||
return NULL; /* endpoint not needed */
|
||||
|
||||
dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %d.\n", __func__, endpoint);
|
||||
dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %x\n",
|
||||
__func__, endpoint);
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
|
||||
if (!urb)
|
||||
return NULL;
|
||||
|
@ -1572,7 +1573,8 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
|
|||
return -1;
|
||||
}
|
||||
|
||||
dev_dbg(&port->dev, "%s - endpoint %d\n", __func__, usb_pipeendpoint(this_urb->pipe));
|
||||
dev_dbg(&port->dev, "%s - endpoint %x\n",
|
||||
__func__, usb_pipeendpoint(this_urb->pipe));
|
||||
|
||||
/* Save reset port val for resend.
|
||||
Don't overwrite resend for open/close condition. */
|
||||
|
@ -1838,7 +1840,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
|
|||
return -1;
|
||||
}
|
||||
|
||||
dev_dbg(&port->dev, "%s - endpoint %d (%d)\n",
|
||||
dev_dbg(&port->dev, "%s - endpoint %x (%d)\n",
|
||||
__func__, usb_pipeendpoint(this_urb->pipe), device_port);
|
||||
|
||||
/* Save reset port val for resend.
|
||||
|
@ -2365,9 +2367,9 @@ static void keyspan_disconnect(struct usb_serial *serial)
|
|||
|
||||
s_priv = usb_get_serial_data(serial);
|
||||
|
||||
stop_urb(s_priv->instat_urb);
|
||||
stop_urb(s_priv->glocont_urb);
|
||||
stop_urb(s_priv->indat_urb);
|
||||
usb_kill_urb(s_priv->instat_urb);
|
||||
usb_kill_urb(s_priv->glocont_urb);
|
||||
usb_kill_urb(s_priv->indat_urb);
|
||||
}
|
||||
|
||||
static void keyspan_release(struct usb_serial *serial)
|
||||
|
@ -2376,6 +2378,10 @@ static void keyspan_release(struct usb_serial *serial)
|
|||
|
||||
s_priv = usb_get_serial_data(serial);
|
||||
|
||||
/* Make sure to unlink the URBs submitted in attach. */
|
||||
usb_kill_urb(s_priv->instat_urb);
|
||||
usb_kill_urb(s_priv->indat_urb);
|
||||
|
||||
usb_free_urb(s_priv->instat_urb);
|
||||
usb_free_urb(s_priv->indat_urb);
|
||||
usb_free_urb(s_priv->glocont_urb);
|
||||
|
@ -2491,11 +2497,11 @@ static int keyspan_port_remove(struct usb_serial_port *port)
|
|||
|
||||
p_priv = usb_get_serial_port_data(port);
|
||||
|
||||
stop_urb(p_priv->inack_urb);
|
||||
stop_urb(p_priv->outcont_urb);
|
||||
usb_kill_urb(p_priv->inack_urb);
|
||||
usb_kill_urb(p_priv->outcont_urb);
|
||||
for (i = 0; i < 2; i++) {
|
||||
stop_urb(p_priv->in_urbs[i]);
|
||||
stop_urb(p_priv->out_urbs[i]);
|
||||
usb_kill_urb(p_priv->in_urbs[i]);
|
||||
usb_kill_urb(p_priv->out_urbs[i]);
|
||||
}
|
||||
|
||||
usb_free_urb(p_priv->inack_urb);
|
||||
|
|
|
@ -1259,6 +1259,15 @@ static int mxuport_attach(struct usb_serial *serial)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mxuport_release(struct usb_serial *serial)
|
||||
{
|
||||
struct usb_serial_port *port0 = serial->port[0];
|
||||
struct usb_serial_port *port1 = serial->port[1];
|
||||
|
||||
usb_serial_generic_close(port1);
|
||||
usb_serial_generic_close(port0);
|
||||
}
|
||||
|
||||
static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port)
|
||||
{
|
||||
struct mxuport_port *mxport = usb_get_serial_port_data(port);
|
||||
|
@ -1361,6 +1370,7 @@ static struct usb_serial_driver mxuport_device = {
|
|||
.probe = mxuport_probe,
|
||||
.port_probe = mxuport_port_probe,
|
||||
.attach = mxuport_attach,
|
||||
.release = mxuport_release,
|
||||
.calc_num_ports = mxuport_calc_num_ports,
|
||||
.open = mxuport_open,
|
||||
.close = mxuport_close,
|
||||
|
|
|
@ -375,18 +375,22 @@ static void option_instat_callback(struct urb *urb);
|
|||
#define HAIER_PRODUCT_CE81B 0x10f8
|
||||
#define HAIER_PRODUCT_CE100 0x2009
|
||||
|
||||
/* Cinterion (formerly Siemens) products */
|
||||
/* Gemalto's Cinterion products (formerly Siemens) */
|
||||
#define SIEMENS_VENDOR_ID 0x0681
|
||||
#define CINTERION_VENDOR_ID 0x1e2d
|
||||
#define CINTERION_PRODUCT_HC25_MDM 0x0047
|
||||
#define CINTERION_PRODUCT_HC25_MDMNET 0x0040
|
||||
#define CINTERION_PRODUCT_HC28_MDM 0x004C
|
||||
#define CINTERION_PRODUCT_HC25_MDM 0x0047
|
||||
#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
|
||||
#define CINTERION_PRODUCT_HC28_MDM 0x004C
|
||||
#define CINTERION_PRODUCT_EU3_E 0x0051
|
||||
#define CINTERION_PRODUCT_EU3_P 0x0052
|
||||
#define CINTERION_PRODUCT_PH8 0x0053
|
||||
#define CINTERION_PRODUCT_AHXX 0x0055
|
||||
#define CINTERION_PRODUCT_PLXX 0x0060
|
||||
#define CINTERION_PRODUCT_PH8_2RMNET 0x0082
|
||||
#define CINTERION_PRODUCT_PH8_AUDIO 0x0083
|
||||
#define CINTERION_PRODUCT_AHXX_2RMNET 0x0084
|
||||
#define CINTERION_PRODUCT_AHXX_AUDIO 0x0085
|
||||
|
||||
/* Olivetti products */
|
||||
#define OLIVETTI_VENDOR_ID 0x0b3c
|
||||
|
@ -633,6 +637,10 @@ static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = {
|
|||
.reserved = BIT(1) | BIT(2) | BIT(3),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info cinterion_rmnet2_blacklist = {
|
||||
.reserved = BIT(4) | BIT(5),
|
||||
};
|
||||
|
||||
static const struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
|
||||
|
@ -1602,7 +1610,79 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
|
||||
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff45, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff46, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff47, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff48, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff49, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4a, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4b, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4c, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4d, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4e, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4f, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff50, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff51, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff52, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff53, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff54, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff55, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff56, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff57, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff58, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff59, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5a, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5b, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5c, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5d, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5e, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5f, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff60, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff61, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff62, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff63, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff64, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff65, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff66, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff67, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff68, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff69, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6a, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6b, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6c, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6d, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6e, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6f, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff70, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff71, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff72, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff73, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff74, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff75, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff76, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff77, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff78, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff79, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7a, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7b, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7c, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7d, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7e, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7f, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff80, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff81, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff82, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff83, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff84, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff85, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff86, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff87, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff88, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff89, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8a, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8b, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8c, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8d, 0xff, 0xff, 0xff) },
|
||||
|
@ -1613,6 +1693,61 @@ static const struct usb_device_id option_ids[] = {
|
|||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff9f, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa0, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa1, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa2, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa3, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa4, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa5, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa6, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa7, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa8, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa9, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaa, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffab, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffac, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffae, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaf, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb0, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb1, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb2, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb3, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb4, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb5, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb6, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb7, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb8, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb9, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffba, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbb, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbc, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbd, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbe, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbf, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc0, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc1, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc2, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc3, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc4, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc5, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc6, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc7, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc8, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc9, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffca, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcb, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcc, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcd, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffce, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcf, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd0, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd1, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd2, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd3, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd4, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd5, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff6, 0xff, 0xff, 0xff) },
|
||||
|
@ -1712,6 +1847,12 @@ static const struct usb_device_id option_ids[] = {
|
|||
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) },
|
||||
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
|
||||
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
|
||||
.driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
|
||||
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
|
||||
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
|
||||
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
|
||||
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) },
|
||||
|
|
|
@ -141,6 +141,7 @@ static void qt2_release(struct usb_serial *serial)
|
|||
|
||||
serial_priv = usb_get_serial_data(serial);
|
||||
|
||||
usb_kill_urb(serial_priv->read_urb);
|
||||
usb_free_urb(serial_priv->read_urb);
|
||||
kfree(serial_priv->read_buffer);
|
||||
kfree(serial_priv);
|
||||
|
|
|
@ -80,6 +80,7 @@ struct ti_device {
|
|||
int td_open_port_count;
|
||||
struct usb_serial *td_serial;
|
||||
int td_is_3410;
|
||||
bool td_rs485_only;
|
||||
int td_urb_error;
|
||||
};
|
||||
|
||||
|
@ -160,6 +161,11 @@ static const struct usb_device_id ti_id_table_3410[] = {
|
|||
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
|
||||
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
@ -193,6 +199,11 @@ static const struct usb_device_id ti_id_table_combined[] = {
|
|||
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
|
||||
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
|
||||
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
@ -277,6 +288,11 @@ MODULE_FIRMWARE("mts_gsm.fw");
|
|||
MODULE_FIRMWARE("mts_edge.fw");
|
||||
MODULE_FIRMWARE("mts_mt9234mu.fw");
|
||||
MODULE_FIRMWARE("mts_mt9234zba.fw");
|
||||
MODULE_FIRMWARE("moxa/moxa-1110.fw");
|
||||
MODULE_FIRMWARE("moxa/moxa-1130.fw");
|
||||
MODULE_FIRMWARE("moxa/moxa-1131.fw");
|
||||
MODULE_FIRMWARE("moxa/moxa-1150.fw");
|
||||
MODULE_FIRMWARE("moxa/moxa-1151.fw");
|
||||
|
||||
module_param(closing_wait, int, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(closing_wait,
|
||||
|
@ -292,6 +308,9 @@ static int ti_startup(struct usb_serial *serial)
|
|||
{
|
||||
struct ti_device *tdev;
|
||||
struct usb_device *dev = serial->dev;
|
||||
struct usb_host_interface *cur_altsetting;
|
||||
int num_endpoints;
|
||||
u16 vid, pid;
|
||||
int status;
|
||||
|
||||
dev_dbg(&dev->dev,
|
||||
|
@ -315,8 +334,22 @@ static int ti_startup(struct usb_serial *serial)
|
|||
dev_dbg(&dev->dev, "%s - device type is %s\n", __func__,
|
||||
tdev->td_is_3410 ? "3410" : "5052");
|
||||
|
||||
/* if we have only 1 configuration, download firmware */
|
||||
if (dev->descriptor.bNumConfigurations == 1) {
|
||||
vid = le16_to_cpu(dev->descriptor.idVendor);
|
||||
pid = le16_to_cpu(dev->descriptor.idProduct);
|
||||
if (vid == MXU1_VENDOR_ID) {
|
||||
switch (pid) {
|
||||
case MXU1_1130_PRODUCT_ID:
|
||||
case MXU1_1131_PRODUCT_ID:
|
||||
tdev->td_rs485_only = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cur_altsetting = serial->interface->cur_altsetting;
|
||||
num_endpoints = cur_altsetting->desc.bNumEndpoints;
|
||||
|
||||
/* if we have only 1 configuration and 1 endpoint, download firmware */
|
||||
if (dev->descriptor.bNumConfigurations == 1 && num_endpoints == 1) {
|
||||
status = ti_download_firmware(tdev);
|
||||
|
||||
if (status != 0)
|
||||
|
@ -371,7 +404,11 @@ static int ti_port_probe(struct usb_serial_port *port)
|
|||
port->port.closing_wait = msecs_to_jiffies(10 * closing_wait);
|
||||
tport->tp_port = port;
|
||||
tport->tp_tdev = usb_get_serial_data(port->serial);
|
||||
tport->tp_uart_mode = 0; /* default is RS232 */
|
||||
|
||||
if (tport->tp_tdev->td_rs485_only)
|
||||
tport->tp_uart_mode = TI_UART_485_RECEIVER_DISABLED;
|
||||
else
|
||||
tport->tp_uart_mode = TI_UART_232;
|
||||
|
||||
usb_set_serial_port_data(port, tport);
|
||||
|
||||
|
@ -1450,6 +1487,16 @@ static int ti_download_firmware(struct ti_device *tdev)
|
|||
const struct firmware *fw_p;
|
||||
char buf[32];
|
||||
|
||||
if (le16_to_cpu(dev->descriptor.idVendor) == MXU1_VENDOR_ID) {
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"moxa/moxa-%04x.fw",
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
status = request_firmware(&fw_p, buf, &dev->dev);
|
||||
goto check_firmware;
|
||||
}
|
||||
|
||||
/* try ID specific firmware first, then try generic firmware */
|
||||
sprintf(buf, "ti_usb-v%04x-p%04x.fw",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
|
@ -1487,6 +1534,8 @@ static int ti_download_firmware(struct ti_device *tdev)
|
|||
}
|
||||
status = request_firmware(&fw_p, buf, &dev->dev);
|
||||
}
|
||||
|
||||
check_firmware:
|
||||
if (status) {
|
||||
dev_err(&dev->dev, "%s - firmware not found\n", __func__);
|
||||
return -ENOENT;
|
||||
|
|
|
@ -60,6 +60,14 @@
|
|||
#define HONEYWELL_VENDOR_ID 0x10ac
|
||||
#define HONEYWELL_HGI80_PRODUCT_ID 0x0102 /* Honeywell HGI80 */
|
||||
|
||||
/* Moxa UPORT 11x0 vendor and product IDs */
|
||||
#define MXU1_VENDOR_ID 0x110a
|
||||
#define MXU1_1110_PRODUCT_ID 0x1110
|
||||
#define MXU1_1130_PRODUCT_ID 0x1130
|
||||
#define MXU1_1131_PRODUCT_ID 0x1131
|
||||
#define MXU1_1150_PRODUCT_ID 0x1150
|
||||
#define MXU1_1151_PRODUCT_ID 0x1151
|
||||
|
||||
/* Commands */
|
||||
#define TI_GET_VERSION 0x01
|
||||
#define TI_GET_PORT_STATUS 0x02
|
||||
|
|
|
@ -96,7 +96,8 @@ static int allocate_minors(struct usb_serial *serial, int num_ports)
|
|||
mutex_lock(&table_lock);
|
||||
for (i = 0; i < num_ports; ++i) {
|
||||
port = serial->port[i];
|
||||
minor = idr_alloc(&serial_minors, port, 0, 0, GFP_KERNEL);
|
||||
minor = idr_alloc(&serial_minors, port, 0,
|
||||
USB_SERIAL_TTY_MINORS, GFP_KERNEL);
|
||||
if (minor < 0)
|
||||
goto error;
|
||||
port->minor = minor;
|
||||
|
@ -815,7 +816,7 @@ static int usb_serial_probe(struct usb_interface *interface,
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
|
||||
#if IS_ENABLED(CONFIG_USB_SERIAL_PL2303)
|
||||
/* BEGIN HORRIBLE HACK FOR PL2303 */
|
||||
/* this is needed due to the looney way its endpoints are set up */
|
||||
if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
|
||||
|
|
|
@ -829,8 +829,10 @@ static int alauda_write_lba(struct us_data *us, u16 lba,
|
|||
|
||||
pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset];
|
||||
if (pba == 1) {
|
||||
/* Maybe it is impossible to write to PBA 1.
|
||||
Fake success, but don't do anything. */
|
||||
/*
|
||||
* Maybe it is impossible to write to PBA 1.
|
||||
* Fake success, but don't do anything.
|
||||
*/
|
||||
printk(KERN_WARNING
|
||||
"alauda_write_lba: avoid writing to pba 1\n");
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
@ -977,10 +979,12 @@ static int alauda_read_data(struct us_data *us, unsigned long address,
|
|||
usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n",
|
||||
pages, lba, page);
|
||||
|
||||
/* This is not really an error. It just means
|
||||
that the block has never been written.
|
||||
Instead of returning USB_STOR_TRANSPORT_ERROR
|
||||
it is better to return all zero data. */
|
||||
/*
|
||||
* This is not really an error. It just means
|
||||
* that the block has never been written.
|
||||
* Instead of returning USB_STOR_TRANSPORT_ERROR
|
||||
* it is better to return all zero data.
|
||||
*/
|
||||
|
||||
memset(buffer, 0, len);
|
||||
} else {
|
||||
|
@ -1222,8 +1226,10 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|||
}
|
||||
|
||||
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
|
||||
/* sure. whatever. not like we can stop the user from popping
|
||||
the media out of the device (no locking doors, etc) */
|
||||
/*
|
||||
* sure. whatever. not like we can stop the user from popping
|
||||
* the media out of the device (no locking doors, etc)
|
||||
*/
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue