USB/PHY updates for 4.16-rc1
Here is the big USB and PHY driver update for 4.16-rc1. Along with the normally expected XHCI, MUSB, and Gadget driver patches, there are some PHY driver fixes, license cleanups, sysfs attribute cleanups, usbip changes, and a raft of other smaller fixes and additions. Full details are in the shortlog. All of these have been in the linux-next tree for a long time with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWnL0Bg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymg8gCeLg/FMtc0S/xRR/56N/sbthEebcUAnROr9Sg3 55hDLdkyi93o9R86YOAJ =8d2q -----END PGP SIGNATURE----- Merge tag 'usb-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB/PHY updates from Greg KH: "Here is the big USB and PHY driver update for 4.16-rc1. Along with the normally expected XHCI, MUSB, and Gadget driver patches, there are some PHY driver fixes, license cleanups, sysfs attribute cleanups, usbip changes, and a raft of other smaller fixes and additions. Full details are in the shortlog. All of these have been in the linux-next tree for a long time with no reported issues" * tag 'usb-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (137 commits) USB: serial: pl2303: new device id for Chilitag USB: misc: fix up some remaining DEVICE_ATTR() usages USB: musb: fix up one odd DEVICE_ATTR() usage USB: atm: fix up some remaining DEVICE_ATTR() usage USB: move many drivers to use DEVICE_ATTR_WO USB: move many drivers to use DEVICE_ATTR_RO USB: move many drivers to use DEVICE_ATTR_RW USB: misc: chaoskey: Use true and false for boolean values USB: storage: remove old wording about how to submit a change USB: storage: remove invalid URL from drivers usb: ehci-omap: don't complain on -EPROBE_DEFER when no PHY found usbip: list: don't list devices attached to vhci_hcd usbip: prevent bind loops on devices attached to vhci_hcd USB: serial: remove redundant initializations of 'mos_parport' usb/gadget: Fix "high bandwidth" check in usb_gadget_ep_match_desc() usb: gadget: compress return logic into one line usbip: vhci_hcd: update 'status' file header and format USB: serial: simple: add Motorola Tetra driver CDC-ACM: apply quirk for card reader usb: option: Add support for FS040U modem ...
This commit is contained in:
commit
e4ee8b85b7
160 changed files with 3874 additions and 1453 deletions
25
Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
Normal file
25
Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
Normal file
|
@ -0,0 +1,25 @@
|
|||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc
|
||||
Date: June 2017
|
||||
Contact: Lu Baolu <baolu.lu@linux.intel.com>
|
||||
Description:
|
||||
xHCI compatible USB host controllers (i.e. super-speed
|
||||
USB3 controllers) are often implemented with the Debug
|
||||
Capability (DbC). It can present a debug device which
|
||||
is fully compliant with the USB framework and provides
|
||||
the equivalent of a very high performance full-duplex
|
||||
serial link for debug purpose.
|
||||
|
||||
The DbC debug device shares a root port with xHCI host.
|
||||
When the DbC is enabled, the root port will be assigned
|
||||
to the Debug Capability. Otherwise, it will be assigned
|
||||
to xHCI.
|
||||
|
||||
Writing "enable" to this attribute will enable the DbC
|
||||
functionality and the shared root port will be assigned
|
||||
to the DbC device. Writing "disable" to this attribute
|
||||
will disable the DbC functionality and the shared root
|
||||
port will roll back to the xHCI.
|
||||
|
||||
Reading this attribute gives the state of the DbC. It
|
||||
can be one of the following states: disabled, enabled,
|
||||
initialized, connected, configured and stalled.
|
|
@ -47,6 +47,8 @@ Optional properties:
|
|||
from P0 to P1/P2/P3 without delay.
|
||||
- snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check
|
||||
during HS transmit.
|
||||
- snps,dis_metastability_quirk: when set, disable metastability workaround.
|
||||
CAUTION: use only if you are absolutely sure of it.
|
||||
- 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
|
||||
|
|
|
@ -35,10 +35,14 @@ Required properties:
|
|||
- phys : a list of phandle + phy specifier pairs
|
||||
|
||||
Optional properties:
|
||||
- mediatek,wakeup-src : 1: ip sleep wakeup mode; 2: line state wakeup
|
||||
mode;
|
||||
- mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
|
||||
control register, it depends on "mediatek,wakeup-src".
|
||||
- wakeup-source : enable USB remote wakeup;
|
||||
- mediatek,syscon-wakeup : phandle to syscon used to access the register
|
||||
of the USB wakeup glue layer between xHCI and SPM; it depends on
|
||||
"wakeup-source", and has two arguments:
|
||||
- the first one : register base address of the glue layer in syscon;
|
||||
- the second one : hardware version of the glue layer
|
||||
- 1 : used by mt8173 etc
|
||||
- 2 : used by mt2712 etc
|
||||
- mediatek,u3p-dis-msk : mask to disable u3ports, bit0 for u3port0,
|
||||
bit1 for u3port1, ... etc;
|
||||
- vbus-supply : reference to the VBUS regulator;
|
||||
|
@ -46,6 +50,7 @@ Optional properties:
|
|||
- pinctrl-names : a pinctrl state named "default" must be defined
|
||||
- pinctrl-0 : pin control group
|
||||
See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||
- imod-interval-ns: default interrupt moderation interval is 5000ns
|
||||
|
||||
Example:
|
||||
usb30: usb@11270000 {
|
||||
|
@ -64,8 +69,9 @@ usb30: usb@11270000 {
|
|||
vusb33-supply = <&mt6397_vusb_reg>;
|
||||
vbus-supply = <&usb_p1_vbus>;
|
||||
usb3-lpm-capable;
|
||||
mediatek,syscon-wakeup = <&pericfg>;
|
||||
mediatek,wakeup-src = <1>;
|
||||
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
|
||||
wakeup-source;
|
||||
imod-interval-ns = <10000>;
|
||||
};
|
||||
|
||||
2nd: dual-role mode with xHCI driver
|
||||
|
|
|
@ -42,9 +42,14 @@ Optional properties:
|
|||
- enable-manual-drd : supports manual dual-role switch via debugfs; usually
|
||||
used when receptacle is TYPE-A and also wants to support dual-role
|
||||
mode.
|
||||
- mediatek,enable-wakeup : supports ip sleep wakeup used by host mode
|
||||
- mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
|
||||
control register, it depends on "mediatek,enable-wakeup".
|
||||
- wakeup-source: enable USB remote wakeup of host mode.
|
||||
- mediatek,syscon-wakeup : phandle to syscon used to access the register
|
||||
of the USB wakeup glue layer between SSUSB and SPM; it depends on
|
||||
"wakeup-source", and has two arguments:
|
||||
- the first one : register base address of the glue layer in syscon;
|
||||
- the second one : hardware version of the glue layer
|
||||
- 1 : used by mt8173 etc
|
||||
- 2 : used by mt2712 etc
|
||||
- mediatek,u3p-dis-msk : mask to disable u3ports, bit0 for u3port0,
|
||||
bit1 for u3port1, ... etc;
|
||||
|
||||
|
@ -71,8 +76,8 @@ ssusb: usb@11271000 {
|
|||
vbus-supply = <&usb_p0_vbus>;
|
||||
extcon = <&extcon_usb>;
|
||||
dr_mode = "otg";
|
||||
mediatek,enable-wakeup;
|
||||
mediatek,syscon-wakeup = <&pericfg>;
|
||||
wakeup-source;
|
||||
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
|
|
@ -13,8 +13,10 @@ Required properties:
|
|||
- "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
|
||||
- "renesas,usbhs-r8a7796" for r8a7796 (R-Car M3-W) compatible device
|
||||
- "renesas,usbhs-r8a77995" for r8a77995 (R-Car D3) compatible device
|
||||
- "renesas,usbhs-r7s72100" for r7s72100 (RZ/A1) compatible device
|
||||
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 or RZ/G1 compatible devices
|
||||
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
|
||||
- "renesas,rza1-usbhs" for RZ/A1 compatible device
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first followed
|
||||
|
|
|
@ -4,8 +4,49 @@ Usually, we only use device tree for hard wired USB device.
|
|||
The reference binding doc is from:
|
||||
http://www.devicetree.org/open-firmware/bindings/usb/usb-1_0.ps
|
||||
|
||||
Four types of device-tree nodes are defined: "host-controller nodes"
|
||||
representing USB host controllers, "device nodes" representing USB devices,
|
||||
"interface nodes" representing USB interfaces and "combined nodes"
|
||||
representing simple USB devices.
|
||||
|
||||
Required properties:
|
||||
A combined node shall be used instead of a device node and an interface node
|
||||
for devices of class 0 or 9 (hub) with a single configuration and a single
|
||||
interface.
|
||||
|
||||
A "hub node" is a combined node or an interface node that represents a USB
|
||||
hub.
|
||||
|
||||
|
||||
Required properties for device nodes:
|
||||
- compatible: "usbVID,PID", where VID is the vendor id and PID the product id.
|
||||
The textual representation of VID and PID shall be in lower case hexadecimal
|
||||
with leading zeroes suppressed. The other compatible strings from the above
|
||||
standard binding could also be used, but a device adhering to this binding
|
||||
may leave out all except for "usbVID,PID".
|
||||
- reg: the number of the USB hub port or the USB host-controller port to which
|
||||
this device is attached. The range is 1-255.
|
||||
|
||||
|
||||
Required properties for device nodes with interface nodes:
|
||||
- #address-cells: shall be 2
|
||||
- #size-cells: shall be 0
|
||||
|
||||
|
||||
Required properties for interface nodes:
|
||||
- compatible: "usbifVID,PID.configCN.IN", where VID is the vendor id, PID is
|
||||
the product id, CN is the configuration value and IN is the interface
|
||||
number. The textual representation of VID, PID, CN and IN shall be in lower
|
||||
case hexadecimal with leading zeroes suppressed. The other compatible
|
||||
strings from the above standard binding could also be used, but a device
|
||||
adhering to this binding may leave out all except for
|
||||
"usbifVID,PID.configCN.IN".
|
||||
- reg: the interface number and configuration value
|
||||
|
||||
The configuration component is not included in the textual representation of
|
||||
an interface-node unit address for configuration 1.
|
||||
|
||||
|
||||
Required properties for combined nodes:
|
||||
- compatible: "usbVID,PID", where VID is the vendor id and PID the product id.
|
||||
The textual representation of VID and PID shall be in lower case hexadecimal
|
||||
with leading zeroes suppressed. The other compatible strings from the above
|
||||
|
@ -31,8 +72,31 @@ Example:
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hub@1 { /* hub connected to port 1 */
|
||||
hub@1 { /* hub connected to port 1 */
|
||||
compatible = "usb5e3,608";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
device@2 { /* device connected to port 2 */
|
||||
compatible = "usb123,4567";
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
device@3 { /* device connected to port 3 */
|
||||
compatible = "usb123,abcd";
|
||||
reg = <3>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
|
||||
interface@0 { /* interface 0 of configuration 1 */
|
||||
compatible = "usbif123,abcd.config1.0";
|
||||
reg = <0 1>;
|
||||
};
|
||||
|
||||
interface@0,2 { /* interface 0 of configuration 2 */
|
||||
compatible = "usbif123,abcd.config2.0";
|
||||
reg = <0 2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,12 +7,14 @@ Required properties:
|
|||
- "marvell,armada3700-xhci" for Armada 37xx SoCs
|
||||
- "marvell,armada-375-xhci" for Armada 375 SoCs
|
||||
- "marvell,armada-380-xhci" for Armada 38x SoCs
|
||||
- "renesas,xhci-r8a7743" for r8a7743 SoC
|
||||
- "renesas,xhci-r8a7790" for r8a7790 SoC
|
||||
- "renesas,xhci-r8a7791" for r8a7791 SoC
|
||||
- "renesas,xhci-r8a7793" for r8a7793 SoC
|
||||
- "renesas,xhci-r8a7795" for r8a7795 SoC
|
||||
- "renesas,xhci-r8a7796" for r8a7796 SoC
|
||||
- "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 compatible device
|
||||
- "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 or RZ/G1 compatible
|
||||
device
|
||||
- "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 compatible device
|
||||
- "xhci-platform" (deprecated)
|
||||
|
||||
|
@ -29,6 +31,7 @@ Optional properties:
|
|||
- usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
|
||||
- usb3-lpm-capable: determines if platform is USB3 LPM capable
|
||||
- quirk-broken-port-ped: set if the controller has broken port disable mechanism
|
||||
- imod-interval-ns: default interrupt moderation interval is 5000ns
|
||||
|
||||
Example:
|
||||
usb@f0931000 {
|
||||
|
|
|
@ -274,7 +274,6 @@ USBIP_CMD_SUBMIT: Submit an URB
|
|||
URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no
|
||||
URB_ISO_ASAP | 0x00000002 | no | no | no | yes
|
||||
URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes
|
||||
URB_NO_FSBR | 0x00000020 | yes | no | no | no
|
||||
URB_ZERO_PACKET | 0x00000040 | no | no | only out | no
|
||||
URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes
|
||||
URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes
|
||||
|
|
|
@ -417,7 +417,7 @@ static struct clk_lookup da830_clks[] = {
|
|||
CLK("davinci-mcasp.0", NULL, &mcasp0_clk),
|
||||
CLK("davinci-mcasp.1", NULL, &mcasp1_clk),
|
||||
CLK("davinci-mcasp.2", NULL, &mcasp2_clk),
|
||||
CLK("musb-da8xx", "usb20", &usb20_clk),
|
||||
CLK("musb-da8xx", NULL, &usb20_clk),
|
||||
CLK("cppi41-dmaengine", NULL, &cppi41_clk),
|
||||
CLK(NULL, "aemif", &aemif_clk),
|
||||
CLK(NULL, "aintc", &aintc_clk),
|
||||
|
@ -426,7 +426,7 @@ static struct clk_lookup da830_clks[] = {
|
|||
CLK("davinci_mdio.0", "fck", &emac_clk),
|
||||
CLK(NULL, "gpio", &gpio_clk),
|
||||
CLK("i2c_davinci.2", NULL, &i2c1_clk),
|
||||
CLK("ohci-da8xx", "usb11", &usb11_clk),
|
||||
CLK("ohci-da8xx", NULL, &usb11_clk),
|
||||
CLK(NULL, "emif3", &emif3_clk),
|
||||
CLK(NULL, "arm", &arm_clk),
|
||||
CLK(NULL, "rmii", &rmii_clk),
|
||||
|
|
|
@ -563,8 +563,8 @@ static struct clk_lookup da850_clks[] = {
|
|||
CLK("da830-mmc.1", NULL, &mmcsd1_clk),
|
||||
CLK("ti-aemif", NULL, &aemif_clk),
|
||||
CLK("davinci-nand.0", "aemif", &aemif_nand_clk),
|
||||
CLK("ohci-da8xx", "usb11", &usb11_clk),
|
||||
CLK("musb-da8xx", "usb20", &usb20_clk),
|
||||
CLK("ohci-da8xx", NULL, &usb11_clk),
|
||||
CLK("musb-da8xx", NULL, &usb20_clk),
|
||||
CLK("cppi41-dmaengine", NULL, &cppi41_clk),
|
||||
CLK("spi_davinci.0", NULL, &spi0_clk),
|
||||
CLK("spi_davinci.1", NULL, &spi1_clk),
|
||||
|
|
|
@ -505,7 +505,7 @@
|
|||
vbus-supply = <&usb_p0_vbus>;
|
||||
extcon = <&extcon_usb>;
|
||||
dr_mode = "otg";
|
||||
mediatek,enable-wakeup;
|
||||
wakeup-source;
|
||||
pinctrl-names = "default", "id_float", "id_ground";
|
||||
pinctrl-0 = <&usb_id_pins_float>;
|
||||
pinctrl-1 = <&usb_id_pins_float>;
|
||||
|
|
|
@ -731,15 +731,9 @@
|
|||
<&u3port0 PHY_TYPE_USB3>,
|
||||
<&u2port1 PHY_TYPE_USB2>;
|
||||
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
|
||||
clocks = <&topckgen CLK_TOP_USB30_SEL>,
|
||||
<&clk26m>,
|
||||
<&pericfg CLK_PERI_USB0>,
|
||||
<&pericfg CLK_PERI_USB1>;
|
||||
clock-names = "sys_ck",
|
||||
"ref_ck",
|
||||
"wakeup_deb_p0",
|
||||
"wakeup_deb_p1";
|
||||
mediatek,syscon-wakeup = <&pericfg>;
|
||||
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
|
||||
clock-names = "sys_ck", "ref_ck";
|
||||
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */
|
||||
#define USB_CTRL_EBRIDGE 0x0c
|
||||
#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */
|
||||
#define USB_CTRL_OBRIDGE 0x10
|
||||
#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK 0x08000000
|
||||
#define USB_CTRL_MDIO 0x14
|
||||
#define USB_CTRL_MDIO2 0x18
|
||||
#define USB_CTRL_UTMI_CTL_1 0x2c
|
||||
|
@ -71,6 +73,7 @@
|
|||
#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK 0x20000000 /* option */
|
||||
#define USB_CTRL_USB30_PCTL 0x70
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK 0x00000002
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_IDDQ_OVERRIDE_MASK 0x00008000
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK 0x00020000
|
||||
#define USB_CTRL_USB_DEVICE_CTL1 0x90
|
||||
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 /* option */
|
||||
|
@ -116,7 +119,6 @@ enum {
|
|||
USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR,
|
||||
USB_CTRL_SETUP_OC3_DISABLE_SELECTOR,
|
||||
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR,
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_SELECTOR,
|
||||
USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR,
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_SELECTOR,
|
||||
|
@ -203,7 +205,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_MASK,
|
||||
|
@ -225,7 +226,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK,
|
||||
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
|
||||
|
@ -247,7 +247,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_MASK,
|
||||
|
@ -269,7 +268,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK,
|
||||
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
|
||||
|
@ -291,7 +289,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_MASK,
|
||||
|
@ -313,7 +310,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
|
||||
0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */
|
||||
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
|
||||
0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */
|
||||
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
|
||||
0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */
|
||||
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
|
||||
|
@ -335,7 +331,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
|
||||
0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */
|
||||
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_MASK,
|
||||
|
@ -357,7 +352,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
|
||||
0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */
|
||||
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
|
||||
0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */
|
||||
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
|
||||
|
@ -379,7 +373,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_MASK,
|
||||
|
@ -401,7 +394,6 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
|||
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
|
||||
USB_CTRL_SETUP_OC3_DISABLE_MASK,
|
||||
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
|
||||
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
|
||||
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
|
||||
USB_CTRL_USB_PM_USB_PWRDN_MASK,
|
||||
|
@ -926,6 +918,7 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
|
|||
USB_CTRL_UNSET_FAMILY(params, USB_PM, BDC_SOFT_RESETB);
|
||||
break;
|
||||
default:
|
||||
USB_CTRL_UNSET_FAMILY(params, USB_PM, BDC_SOFT_RESETB);
|
||||
USB_CTRL_SET_FAMILY(params, USB_PM, BDC_SOFT_RESETB);
|
||||
break;
|
||||
}
|
||||
|
@ -952,13 +945,17 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
|
|||
* Don't enable this so the memory controller doesn't read
|
||||
* into memory holes. NOTE: This bit is low true on 7366C0.
|
||||
*/
|
||||
USB_CTRL_SET_FAMILY(params, EBRIDGE, ESTOP_SCB_REQ);
|
||||
USB_CTRL_SET(ctrl, EBRIDGE, ESTOP_SCB_REQ);
|
||||
|
||||
/* Setup the endian bits */
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
reg &= ~USB_CTRL_SETUP_ENDIAN_BITS;
|
||||
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN);
|
||||
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
|
||||
if (params->selected_family == BRCM_FAMILY_7271A0)
|
||||
/* Enable LS keep alive fix for certain keyboards */
|
||||
USB_CTRL_SET(ctrl, OBRIDGE, LS_KEEP_ALIVE);
|
||||
}
|
||||
|
||||
void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
|
||||
|
@ -1003,6 +1000,7 @@ void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params)
|
|||
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
brcmusb_xhci_soft_reset(params, 1);
|
||||
USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
|
||||
}
|
||||
|
||||
void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
|
||||
|
|
|
@ -338,9 +338,9 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
|
|||
ARRAY_SIZE(brcm_dr_mode_to_name),
|
||||
mode, &priv->ini.mode);
|
||||
}
|
||||
if (of_property_read_bool(dn, "brcm,has_xhci"))
|
||||
if (of_property_read_bool(dn, "brcm,has-xhci"))
|
||||
priv->has_xhci = true;
|
||||
if (of_property_read_bool(dn, "brcm,has_eohci"))
|
||||
if (of_property_read_bool(dn, "brcm,has-eohci"))
|
||||
priv->has_eohci = true;
|
||||
|
||||
err = brcm_usb_phy_dvr_init(dev, priv, dn);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
|
@ -440,9 +441,9 @@ static void u2_phy_instance_init(struct mtk_tphy *tphy,
|
|||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
/* switch to USB function. (system register, force ip into usb mode) */
|
||||
/* switch to USB function, and enable usb pll */
|
||||
tmp = readl(com + U3P_U2PHYDTM0);
|
||||
tmp &= ~P2C_FORCE_UART_EN;
|
||||
tmp &= ~(P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM);
|
||||
tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
|
||||
writel(tmp, com + U3P_U2PHYDTM0);
|
||||
|
||||
|
@ -502,10 +503,8 @@ static void u2_phy_instance_power_on(struct mtk_tphy *tphy,
|
|||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
/* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */
|
||||
tmp = readl(com + U3P_U2PHYDTM0);
|
||||
tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL);
|
||||
tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
|
||||
tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
|
||||
writel(tmp, com + U3P_U2PHYDTM0);
|
||||
|
||||
/* OTG Enable */
|
||||
|
@ -540,7 +539,6 @@ static void u2_phy_instance_power_off(struct mtk_tphy *tphy,
|
|||
|
||||
tmp = readl(com + U3P_U2PHYDTM0);
|
||||
tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
|
||||
tmp |= P2C_FORCE_SUSPENDM;
|
||||
writel(tmp, com + U3P_U2PHYDTM0);
|
||||
|
||||
/* OTG Disable */
|
||||
|
@ -548,18 +546,16 @@ static void u2_phy_instance_power_off(struct mtk_tphy *tphy,
|
|||
tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
|
||||
writel(tmp, com + U3P_USBPHYACR6);
|
||||
|
||||
/* let suspendm=0, set utmi into analog power down */
|
||||
tmp = readl(com + U3P_U2PHYDTM0);
|
||||
tmp &= ~P2C_RG_SUSPENDM;
|
||||
writel(tmp, com + U3P_U2PHYDTM0);
|
||||
udelay(1);
|
||||
|
||||
tmp = readl(com + U3P_U2PHYDTM1);
|
||||
tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
|
||||
tmp |= P2C_RG_SESSEND;
|
||||
writel(tmp, com + U3P_U2PHYDTM1);
|
||||
|
||||
if (tphy->pdata->avoid_rx_sen_degradation && index) {
|
||||
tmp = readl(com + U3P_U2PHYDTM0);
|
||||
tmp &= ~(P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM);
|
||||
writel(tmp, com + U3P_U2PHYDTM0);
|
||||
|
||||
tmp = readl(com + U3D_U2PHYDCR0);
|
||||
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, com + U3D_U2PHYDCR0);
|
||||
|
@ -1000,7 +996,6 @@ MODULE_DEVICE_TABLE(of, mtk_tphy_id_table);
|
|||
|
||||
static int mtk_tphy_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;
|
||||
|
@ -1010,15 +1005,14 @@ static int mtk_tphy_probe(struct platform_device *pdev)
|
|||
struct resource res;
|
||||
int port, retval;
|
||||
|
||||
match = of_match_node(mtk_tphy_id_table, pdev->dev.of_node);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
tphy = devm_kzalloc(dev, sizeof(*tphy), GFP_KERNEL);
|
||||
if (!tphy)
|
||||
return -ENOMEM;
|
||||
|
||||
tphy->pdata = match->data;
|
||||
tphy->pdata = of_device_get_match_data(dev);
|
||||
if (!tphy->pdata)
|
||||
return -EINVAL;
|
||||
|
||||
tphy->nphys = of_get_child_count(np);
|
||||
tphy->phys = devm_kcalloc(dev, tphy->nphys,
|
||||
sizeof(*tphy->phys), GFP_KERNEL);
|
||||
|
@ -1028,9 +1022,10 @@ static int mtk_tphy_probe(struct platform_device *pdev)
|
|||
tphy->dev = dev;
|
||||
platform_set_drvdata(pdev, tphy);
|
||||
|
||||
if (tphy->pdata->version == MTK_PHY_V1) {
|
||||
sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
/* SATA phy of V1 needn't it if not shared with PCIe or USB */
|
||||
if (sif_res && tphy->pdata->version == MTK_PHY_V1) {
|
||||
/* get banks shared by multiple phys */
|
||||
sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tphy->sif_base = devm_ioremap_resource(dev, sif_res);
|
||||
if (IS_ERR(tphy->sif_base)) {
|
||||
dev_err(dev, "failed to remap sif regs\n");
|
||||
|
|
|
@ -90,7 +90,17 @@
|
|||
#define PHYCLKRST_COMMONONN BIT(0)
|
||||
|
||||
#define EXYNOS5_DRD_PHYREG0 0x14
|
||||
#define PHYREG0_SSC_REF_CLK_SEL BIT(21)
|
||||
#define PHYREG0_SSC_RANGE BIT(20)
|
||||
#define PHYREG0_CR_WRITE BIT(19)
|
||||
#define PHYREG0_CR_READ BIT(18)
|
||||
#define PHYREG0_CR_DATA_IN(_x) ((_x) << 2)
|
||||
#define PHYREG0_CR_CAP_DATA BIT(1)
|
||||
#define PHYREG0_CR_CAP_ADDR BIT(0)
|
||||
|
||||
#define EXYNOS5_DRD_PHYREG1 0x18
|
||||
#define PHYREG1_CR_DATA_OUT(_x) ((_x) << 1)
|
||||
#define PHYREG1_CR_ACK BIT(0)
|
||||
|
||||
#define EXYNOS5_DRD_PHYPARAM0 0x1c
|
||||
|
||||
|
@ -119,6 +129,25 @@
|
|||
#define EXYNOS5_DRD_PHYRESUME 0x34
|
||||
#define EXYNOS5_DRD_LINKPORT 0x44
|
||||
|
||||
/* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */
|
||||
#define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN (0x15)
|
||||
#define LOSLEVEL_OVRD_IN_LOS_BIAS_5420 (0x5 << 13)
|
||||
#define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT (0x0 << 13)
|
||||
#define LOSLEVEL_OVRD_IN_EN (0x1 << 10)
|
||||
#define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT (0x9 << 0)
|
||||
|
||||
#define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN (0x12)
|
||||
#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420 (0x5 << 13)
|
||||
#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT (0x4 << 13)
|
||||
|
||||
#define EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG (0x1010)
|
||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M (0x4 << 4)
|
||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M (0x8 << 4)
|
||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_25M_26M (0x8 << 4)
|
||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M (0x20 << 4)
|
||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4)
|
||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4)
|
||||
|
||||
#define KHZ 1000
|
||||
#define MHZ (KHZ * KHZ)
|
||||
|
||||
|
@ -527,6 +556,151 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
|
||||
u32 val, u32 cmd)
|
||||
{
|
||||
u32 usec = 100;
|
||||
unsigned int result;
|
||||
|
||||
writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
|
||||
|
||||
do {
|
||||
result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
|
||||
if (result & PHYREG1_CR_ACK)
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
} while (usec-- > 0);
|
||||
|
||||
if (!usec) {
|
||||
dev_err(phy_drd->dev,
|
||||
"CRPORT handshake timeout1 (0x%08x)\n", val);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
usec = 100;
|
||||
|
||||
writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
|
||||
|
||||
do {
|
||||
result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
|
||||
if (!(result & PHYREG1_CR_ACK))
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
} while (usec-- > 0);
|
||||
|
||||
if (!usec) {
|
||||
dev_err(phy_drd->dev,
|
||||
"CRPORT handshake timeout2 (0x%08x)\n", val);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crport_ctrl_write(struct exynos5_usbdrd_phy *phy_drd,
|
||||
u32 addr, u32 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Write Address */
|
||||
writel(PHYREG0_CR_DATA_IN(addr),
|
||||
phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
|
||||
ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(addr),
|
||||
PHYREG0_CR_CAP_ADDR);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Write Data */
|
||||
writel(PHYREG0_CR_DATA_IN(data),
|
||||
phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
|
||||
ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
|
||||
PHYREG0_CR_CAP_DATA);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
|
||||
PHYREG0_CR_WRITE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calibrate few PHY parameters using CR_PORT register to meet
|
||||
* SuperSpeed requirements on Exynos5420 and Exynos5800 systems,
|
||||
* which have 28nm USB 3.0 DRD PHY.
|
||||
*/
|
||||
static int exynos5420_usbdrd_phy_calibrate(struct exynos5_usbdrd_phy *phy_drd)
|
||||
{
|
||||
unsigned int temp;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Change los_bias to (0x5) for 28nm PHY from a
|
||||
* default value (0x0); los_level is set as default
|
||||
* (0x9) as also reflected in los_level[30:26] bits
|
||||
* of PHYPARAM0 register.
|
||||
*/
|
||||
temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 |
|
||||
LOSLEVEL_OVRD_IN_EN |
|
||||
LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT;
|
||||
ret = crport_ctrl_write(phy_drd,
|
||||
EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN,
|
||||
temp);
|
||||
if (ret) {
|
||||
dev_err(phy_drd->dev,
|
||||
"Failed setting Loss-of-Signal level for SuperSpeed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning,
|
||||
* to raise Tx signal level from its default value of (0x4)
|
||||
*/
|
||||
temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420;
|
||||
ret = crport_ctrl_write(phy_drd,
|
||||
EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN,
|
||||
temp);
|
||||
if (ret) {
|
||||
dev_err(phy_drd->dev,
|
||||
"Failed setting Tx-Vboost-Level for SuperSpeed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set proper time to wait for RxDetect measurement, for
|
||||
* desired reference clock of PHY, by tuning the CR_PORT
|
||||
* register LANE0.TX_DEBUG which is internal to PHY.
|
||||
* This fixes issue with few USB 3.0 devices, which are
|
||||
* not detected (not even generate interrupts on the bus
|
||||
* on insertion) without this change.
|
||||
* e.g. Samsung SUM-TSB16S 3.0 USB drive.
|
||||
*/
|
||||
switch (phy_drd->extrefclk) {
|
||||
case EXYNOS5_FSEL_50MHZ:
|
||||
temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M;
|
||||
break;
|
||||
case EXYNOS5_FSEL_20MHZ:
|
||||
case EXYNOS5_FSEL_19MHZ2:
|
||||
temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M;
|
||||
break;
|
||||
case EXYNOS5_FSEL_24MHZ:
|
||||
default:
|
||||
temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = crport_ctrl_write(phy_drd,
|
||||
EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG,
|
||||
temp);
|
||||
if (ret)
|
||||
dev_err(phy_drd->dev,
|
||||
"Fail to set RxDet measurement time for SuperSpeed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
|
@ -538,11 +712,20 @@ static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
|
|||
return phy_drd->phys[args->args[0]].phy;
|
||||
}
|
||||
|
||||
static int exynos5_usbdrd_phy_calibrate(struct phy *phy)
|
||||
{
|
||||
struct phy_usb_instance *inst = phy_get_drvdata(phy);
|
||||
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
|
||||
|
||||
return exynos5420_usbdrd_phy_calibrate(phy_drd);
|
||||
}
|
||||
|
||||
static const struct phy_ops exynos5_usbdrd_phy_ops = {
|
||||
.init = exynos5_usbdrd_phy_init,
|
||||
.exit = exynos5_usbdrd_phy_exit,
|
||||
.power_on = exynos5_usbdrd_phy_power_on,
|
||||
.power_off = exynos5_usbdrd_phy_power_off,
|
||||
.calibrate = exynos5_usbdrd_phy_calibrate,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
@ -196,18 +196,16 @@ static void cxacru_poll_status(struct work_struct *work);
|
|||
|
||||
/* Card info exported through sysfs */
|
||||
#define CXACRU__ATTR_INIT(_name) \
|
||||
static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
|
||||
static DEVICE_ATTR_RO(_name)
|
||||
|
||||
#define CXACRU_CMD_INIT(_name) \
|
||||
static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
|
||||
cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
|
||||
static DEVICE_ATTR_RW(_name)
|
||||
|
||||
#define CXACRU_SET_INIT(_name) \
|
||||
static DEVICE_ATTR(_name, S_IWUSR, \
|
||||
NULL, cxacru_sysfs_store_##_name)
|
||||
static DEVICE_ATTR_WO(_name)
|
||||
|
||||
#define CXACRU_ATTR_INIT(_value, _type, _name) \
|
||||
static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
|
||||
static ssize_t _name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct cxacru_data *instance = to_usbatm_driver_data(\
|
||||
|
@ -302,7 +300,7 @@ static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
|
|||
* MAC_ADDRESS_LOW = 0x33221100
|
||||
* Where 00-55 are bytes 0-5 of the MAC.
|
||||
*/
|
||||
static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
|
||||
static ssize_t mac_address_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cxacru_data *instance = to_usbatm_driver_data(
|
||||
|
@ -315,7 +313,7 @@ static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
|
|||
instance->usbatm->atm_dev->esi);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
|
||||
static ssize_t adsl_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
static char *str[] = { "running", "stopped" };
|
||||
|
@ -332,7 +330,7 @@ static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
|
||||
static ssize_t adsl_state_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct cxacru_data *instance = to_usbatm_driver_data(
|
||||
|
@ -435,7 +433,7 @@ static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
|
|||
|
||||
/* CM_REQUEST_CARD_DATA_GET times out, so no show attribute */
|
||||
|
||||
static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
|
||||
static ssize_t adsl_config_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct cxacru_data *instance = to_usbatm_driver_data(
|
||||
|
|
|
@ -2280,7 +2280,7 @@ static struct uea_softc *dev_to_uea(struct device *dev)
|
|||
return usbatm->driver_data;
|
||||
}
|
||||
|
||||
static ssize_t read_status(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t stat_status_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
@ -2296,7 +2296,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t reboot(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t stat_status_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
@ -2313,9 +2313,9 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(stat_status, S_IWUSR | S_IRUGO, read_status, reboot);
|
||||
static DEVICE_ATTR_RW(stat_status);
|
||||
|
||||
static ssize_t read_human_status(struct device *dev,
|
||||
static ssize_t stat_human_status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
@ -2376,9 +2376,9 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(stat_human_status, S_IRUGO, read_human_status, NULL);
|
||||
static DEVICE_ATTR_RO(stat_human_status);
|
||||
|
||||
static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t stat_delin_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
@ -2408,11 +2408,11 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(stat_delin, S_IRUGO, read_delin, NULL);
|
||||
static DEVICE_ATTR_RO(stat_delin);
|
||||
|
||||
#define UEA_ATTR(name, reset) \
|
||||
\
|
||||
static ssize_t read_##name(struct device *dev, \
|
||||
static ssize_t stat_##name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
int ret = -ENODEV; \
|
||||
|
@ -2430,7 +2430,7 @@ out: \
|
|||
return ret; \
|
||||
} \
|
||||
\
|
||||
static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL)
|
||||
static DEVICE_ATTR_RO(stat_##name)
|
||||
|
||||
UEA_ATTR(mflags, 1);
|
||||
UEA_ATTR(vidcpe, 0);
|
||||
|
|
|
@ -29,7 +29,7 @@ static const struct tegra_udc_soc_info tegra20_udc_soc_info = {
|
|||
};
|
||||
|
||||
static const struct tegra_udc_soc_info tegra30_udc_soc_info = {
|
||||
.flags = 0,
|
||||
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
|
||||
};
|
||||
|
||||
static const struct tegra_udc_soc_info tegra114_udc_soc_info = {
|
||||
|
|
|
@ -835,7 +835,7 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t role_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
@ -846,7 +846,7 @@ static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ci_role_store(struct device *dev,
|
||||
static ssize_t role_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
@ -877,7 +877,7 @@ static ssize_t ci_role_store(struct device *dev,
|
|||
|
||||
return (ret == 0) ? n : ret;
|
||||
}
|
||||
static DEVICE_ATTR(role, 0644, ci_role_show, ci_role_store);
|
||||
static DEVICE_ATTR_RW(role);
|
||||
|
||||
static struct attribute *ci_attrs[] = {
|
||||
&dev_attr_role.attr,
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
/* Add for otg: interact with user space app */
|
||||
static ssize_t
|
||||
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *next;
|
||||
unsigned size, t;
|
||||
|
@ -45,7 +45,7 @@ get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
||||
a_bus_req_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
@ -75,10 +75,10 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req);
|
||||
static DEVICE_ATTR_RW(a_bus_req);
|
||||
|
||||
static ssize_t
|
||||
get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
a_bus_drop_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *next;
|
||||
unsigned size, t;
|
||||
|
@ -94,7 +94,7 @@ get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
set_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
||||
a_bus_drop_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
@ -115,11 +115,10 @@ set_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop,
|
||||
set_a_bus_drop);
|
||||
static DEVICE_ATTR_RW(a_bus_drop);
|
||||
|
||||
static ssize_t
|
||||
get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
b_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *next;
|
||||
unsigned size, t;
|
||||
|
@ -135,7 +134,7 @@ get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
set_b_bus_req(struct device *dev, struct device_attribute *attr,
|
||||
b_bus_req_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
@ -160,10 +159,10 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req);
|
||||
static DEVICE_ATTR_RW(b_bus_req);
|
||||
|
||||
static ssize_t
|
||||
set_a_clr_err(struct device *dev, struct device_attribute *attr,
|
||||
a_clr_err_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
@ -180,7 +179,7 @@ set_a_clr_err(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
|
||||
static DEVICE_ATTR_WO(a_clr_err);
|
||||
|
||||
static struct attribute *inputs_attrs[] = {
|
||||
&dev_attr_a_bus_req.attr,
|
||||
|
|
|
@ -235,7 +235,7 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
|
|||
/*
|
||||
* attributes exported through sysfs
|
||||
*/
|
||||
static ssize_t show_caps
|
||||
static ssize_t bmCapabilities_show
|
||||
(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -243,9 +243,9 @@ static ssize_t show_caps
|
|||
|
||||
return sprintf(buf, "%d", acm->ctrl_caps);
|
||||
}
|
||||
static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
|
||||
static DEVICE_ATTR_RO(bmCapabilities);
|
||||
|
||||
static ssize_t show_country_codes
|
||||
static ssize_t wCountryCodes_show
|
||||
(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -255,9 +255,9 @@ static ssize_t show_country_codes
|
|||
return acm->country_code_size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
|
||||
static DEVICE_ATTR_RO(wCountryCodes);
|
||||
|
||||
static ssize_t show_country_rel_date
|
||||
static ssize_t iCountryCodeRelDate_show
|
||||
(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -266,7 +266,7 @@ static ssize_t show_country_rel_date
|
|||
return sprintf(buf, "%d", acm->country_rel_date);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
|
||||
static DEVICE_ATTR_RO(iCountryCodeRelDate);
|
||||
/*
|
||||
* Interrupt handlers for various ACM device responses
|
||||
*/
|
||||
|
@ -425,7 +425,7 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
|
|||
|
||||
res = usb_submit_urb(acm->read_urbs[index], mem_flags);
|
||||
if (res) {
|
||||
if (res != -EPERM) {
|
||||
if (res != -EPERM && res != -ENODEV) {
|
||||
dev_err(&acm->data->dev,
|
||||
"urb %d failed submission with %d\n",
|
||||
index, res);
|
||||
|
@ -1752,6 +1752,9 @@ static const struct usb_device_id acm_ids[] = {
|
|||
{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
|
||||
.driver_info = SINGLE_RX_URB, /* firmware bug */
|
||||
},
|
||||
{ USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
|
||||
.driver_info = SINGLE_RX_URB,
|
||||
},
|
||||
{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
|
|
|
@ -1066,7 +1066,7 @@ static struct usb_class_driver usblp_class = {
|
|||
.minor_base = USBLP_MINOR_BASE,
|
||||
};
|
||||
|
||||
static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usblp *usblp = usb_get_intfdata(intf);
|
||||
|
@ -1078,7 +1078,7 @@ static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribut
|
|||
return sprintf(buf, "%s", usblp->device_id_string+2);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL);
|
||||
static DEVICE_ATTR_RO(ieee1284_id);
|
||||
|
||||
static int usblp_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
|
|
|
@ -1681,8 +1681,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
u |= URB_ISO_ASAP;
|
||||
if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in)
|
||||
u |= URB_SHORT_NOT_OK;
|
||||
if (uurb->flags & USBDEVFS_URB_NO_FSBR)
|
||||
u |= URB_NO_FSBR;
|
||||
if (uurb->flags & USBDEVFS_URB_ZERO_PACKET)
|
||||
u |= URB_ZERO_PACKET;
|
||||
if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT)
|
||||
|
|
|
@ -342,8 +342,8 @@ static int usb_probe_interface(struct device *dev)
|
|||
if (driver->disable_hub_initiated_lpm) {
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error) {
|
||||
dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n",
|
||||
__func__, driver->name);
|
||||
error = lpm_disable_error;
|
||||
goto err;
|
||||
}
|
||||
|
@ -537,8 +537,8 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
if (driver->disable_hub_initiated_lpm) {
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n",
|
||||
__func__, driver->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
@ -1070,7 +1070,7 @@ static void usb_rebind_intf(struct usb_interface *intf)
|
|||
if (!intf->dev.power.is_prepared) {
|
||||
intf->needs_binding = 0;
|
||||
rc = device_attach(&intf->dev);
|
||||
if (rc < 0)
|
||||
if (rc < 0 && rc != -EPROBE_DEFER)
|
||||
dev_warn(&intf->dev, "rebind failed: %d\n", rc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
|
||||
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
|
||||
|
||||
#define USB_TP_TRANSMISSION_DELAY 40 /* ns */
|
||||
#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */
|
||||
|
||||
/* Protect struct usb_device->state and ->children members
|
||||
* Note: Both are also protected by ->dev.sem, except that ->state can
|
||||
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
|
||||
|
@ -1049,12 +1052,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
ret = hcd->driver->update_hub_device(hcd, hdev,
|
||||
&hub->tt, GFP_NOIO);
|
||||
if (ret < 0) {
|
||||
dev_err(hub->intfdev, "Host not "
|
||||
"accepting hub info "
|
||||
"update.\n");
|
||||
dev_err(hub->intfdev, "LS/FS devices "
|
||||
"and hubs may not work "
|
||||
"under this hub\n.");
|
||||
dev_err(hub->intfdev,
|
||||
"Host not accepting hub info update\n");
|
||||
dev_err(hub->intfdev,
|
||||
"LS/FS devices and hubs may not work under this hub\n");
|
||||
}
|
||||
}
|
||||
hub_power_on(hub, true);
|
||||
|
@ -1352,6 +1353,20 @@ static int hub_configure(struct usb_hub *hub,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accumulate wHubDelay + 40ns for every hub in the tree of devices.
|
||||
* The resulting value will be used for SetIsochDelay() request.
|
||||
*/
|
||||
if (hub_is_superspeed(hdev) || hub_is_superspeedplus(hdev)) {
|
||||
u32 delay = __le16_to_cpu(hub->descriptor->u.ss.wHubDelay);
|
||||
|
||||
if (hdev->parent)
|
||||
delay += hdev->parent->hub_delay;
|
||||
|
||||
delay += USB_TP_TRANSMISSION_DELAY;
|
||||
hdev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX);
|
||||
}
|
||||
|
||||
maxchild = hub->descriptor->bNbrPorts;
|
||||
dev_info(hub_dev, "%d port%s detected\n", maxchild,
|
||||
(maxchild == 1) ? "" : "s");
|
||||
|
@ -3157,7 +3172,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
usb_set_usb2_hardware_lpm(udev, 0);
|
||||
|
||||
if (usb_disable_ltm(udev)) {
|
||||
dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
|
||||
dev_err(&udev->dev, "Failed to disable LTM before suspend\n");
|
||||
status = -ENOMEM;
|
||||
if (PMSG_IS_AUTO(msg))
|
||||
goto err_ltm;
|
||||
|
@ -4599,7 +4614,20 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
if (retval >= 0)
|
||||
retval = -EMSGSIZE;
|
||||
} else {
|
||||
u32 delay;
|
||||
|
||||
retval = 0;
|
||||
|
||||
delay = udev->parent->hub_delay;
|
||||
udev->hub_delay = min_t(u32, delay,
|
||||
USB_TP_TRANSMISSION_DELAY_MAX);
|
||||
retval = usb_set_isoch_delay(udev);
|
||||
if (retval) {
|
||||
dev_dbg(&udev->dev,
|
||||
"Failed set isoch delay, error %d\n",
|
||||
retval);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -5484,13 +5512,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
*/
|
||||
ret = usb_unlocked_disable_lpm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n", __func__);
|
||||
goto re_enumerate_no_bos;
|
||||
}
|
||||
ret = usb_disable_ltm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LTM\n.",
|
||||
__func__);
|
||||
dev_err(&udev->dev, "%s Failed to disable LTM\n", __func__);
|
||||
goto re_enumerate_no_bos;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,11 +137,17 @@ static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data,
|
|||
if (!led_np)
|
||||
return false;
|
||||
|
||||
/* Get node of port being added */
|
||||
port_np = usb_of_get_child_node(usb_dev->dev.of_node, port1);
|
||||
/*
|
||||
* Get node of port being added
|
||||
*
|
||||
* FIXME: This is really the device node of the connected device
|
||||
*/
|
||||
port_np = usb_of_get_device_node(usb_dev, port1);
|
||||
if (!port_np)
|
||||
return false;
|
||||
|
||||
of_node_put(port_np);
|
||||
|
||||
/* Amount of trigger sources for this LED */
|
||||
count = of_count_phandle_with_args(led_np, "trigger-sources",
|
||||
"#trigger-source-cells");
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h> /* for usbcore internals */
|
||||
#include <linux/usb/of.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
@ -776,7 +777,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf)
|
|||
* deal with strings at all. Set string_langid to -1 in order to
|
||||
* prevent any string to be retrieved from the device */
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "string descriptor 0 read error: %d\n",
|
||||
dev_info(&dev->dev, "string descriptor 0 read error: %d\n",
|
||||
err);
|
||||
dev->string_langid = -1;
|
||||
return -EPIPE;
|
||||
|
@ -915,6 +916,30 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_set_isoch_delay - informs the device of the packet transmit delay
|
||||
* @dev: the device whose delay is to be informed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Since this is an optional request, we don't bother if it fails.
|
||||
*/
|
||||
int usb_set_isoch_delay(struct usb_device *dev)
|
||||
{
|
||||
/* skip hub devices */
|
||||
if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
return 0;
|
||||
|
||||
/* skip non-SS/non-SSP devices */
|
||||
if (dev->speed < USB_SPEED_SUPER)
|
||||
return 0;
|
||||
|
||||
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
USB_REQ_SET_ISOCH_DELAY,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
cpu_to_le16(dev->hub_delay), 0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_get_status - issues a GET_STATUS call
|
||||
* @dev: the device whose status is being checked
|
||||
|
@ -1355,7 +1380,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
* so that the xHCI driver can recalculate the U1/U2 timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM\n", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -1499,7 +1524,7 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
* that the xHCI driver can recalculate the U1/U2 timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -1583,6 +1608,7 @@ static void usb_release_interface(struct device *dev)
|
|||
|
||||
kref_put(&intfc->ref, usb_release_interface_cache);
|
||||
usb_put_dev(interface_to_usbdev(intf));
|
||||
of_node_put(dev->of_node);
|
||||
kfree(intf);
|
||||
}
|
||||
|
||||
|
@ -1846,7 +1872,7 @@ free_interfaces:
|
|||
* timeouts.
|
||||
*/
|
||||
if (dev->actconfig && usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
ret = -ENOMEM;
|
||||
goto free_interfaces;
|
||||
|
@ -1868,6 +1894,7 @@ free_interfaces:
|
|||
struct usb_interface_cache *intfc;
|
||||
struct usb_interface *intf;
|
||||
struct usb_host_interface *alt;
|
||||
u8 ifnum;
|
||||
|
||||
cp->interface[i] = intf = new_interfaces[i];
|
||||
intfc = cp->intf_cache[i];
|
||||
|
@ -1886,11 +1913,17 @@ free_interfaces:
|
|||
if (!alt)
|
||||
alt = &intf->altsetting[0];
|
||||
|
||||
intf->intf_assoc =
|
||||
find_iad(dev, cp, alt->desc.bInterfaceNumber);
|
||||
ifnum = alt->desc.bInterfaceNumber;
|
||||
intf->intf_assoc = find_iad(dev, cp, ifnum);
|
||||
intf->cur_altsetting = alt;
|
||||
usb_enable_interface(dev, intf, true);
|
||||
intf->dev.parent = &dev->dev;
|
||||
if (usb_of_has_combined_node(dev)) {
|
||||
device_set_of_node_from_dev(&intf->dev, &dev->dev);
|
||||
} else {
|
||||
intf->dev.of_node = usb_of_get_interface_node(dev,
|
||||
configuration, ifnum);
|
||||
}
|
||||
intf->dev.driver = NULL;
|
||||
intf->dev.bus = &usb_bus_type;
|
||||
intf->dev.type = &usb_if_device_type;
|
||||
|
@ -1905,9 +1938,8 @@ free_interfaces:
|
|||
intf->minor = -1;
|
||||
device_initialize(&intf->dev);
|
||||
pm_runtime_no_callbacks(&intf->dev);
|
||||
dev_set_name(&intf->dev, "%d-%s:%d.%d",
|
||||
dev->bus->busnum, dev->devpath,
|
||||
configuration, alt->desc.bInterfaceNumber);
|
||||
dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum,
|
||||
dev->devpath, configuration, ifnum);
|
||||
usb_get_dev(dev);
|
||||
}
|
||||
kfree(new_interfaces);
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* of.c The helpers for hcd device tree support
|
||||
*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Author: Peter Chen <peter.chen@freescale.com>
|
||||
* Author: Peter Chen <peter.chen@freescale.com>
|
||||
* Copyright (C) 2017 Johan Hovold <johan@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
|
@ -11,31 +12,99 @@
|
|||
#include <linux/usb/of.h>
|
||||
|
||||
/**
|
||||
* usb_of_get_child_node - Find the device node match port number
|
||||
* @parent: the parent device node
|
||||
* @portnum: the port number which device is connecting
|
||||
* usb_of_get_device_node() - get a USB device node
|
||||
* @hub: hub to which device is connected
|
||||
* @port1: one-based index of port
|
||||
*
|
||||
* Find the node from device tree according to its port number.
|
||||
* Look up the node of a USB device given its parent hub device and one-based
|
||||
* port number.
|
||||
*
|
||||
* Return: A pointer to the node with incremented refcount if found, or
|
||||
* %NULL otherwise.
|
||||
*/
|
||||
struct device_node *usb_of_get_child_node(struct device_node *parent,
|
||||
int portnum)
|
||||
struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1)
|
||||
{
|
||||
struct device_node *node;
|
||||
u32 port;
|
||||
u32 reg;
|
||||
|
||||
for_each_child_of_node(parent, node) {
|
||||
if (!of_property_read_u32(node, "reg", &port)) {
|
||||
if (port == portnum)
|
||||
return node;
|
||||
}
|
||||
for_each_child_of_node(hub->dev.of_node, node) {
|
||||
if (of_property_read_u32(node, "reg", ®))
|
||||
continue;
|
||||
|
||||
if (reg == port1)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_of_get_child_node);
|
||||
EXPORT_SYMBOL_GPL(usb_of_get_device_node);
|
||||
|
||||
/**
|
||||
* usb_of_has_combined_node() - determine whether a device has a combined node
|
||||
* @udev: USB device
|
||||
*
|
||||
* Determine whether a USB device has a so called combined node which is
|
||||
* shared with its sole interface. This is the case if and only if the device
|
||||
* has a node and its decriptors report the following:
|
||||
*
|
||||
* 1) bDeviceClass is 0 or 9, and
|
||||
* 2) bNumConfigurations is 1, and
|
||||
* 3) bNumInterfaces is 1.
|
||||
*
|
||||
* Return: True iff the device has a device node and its descriptors match the
|
||||
* criteria for a combined node.
|
||||
*/
|
||||
bool usb_of_has_combined_node(struct usb_device *udev)
|
||||
{
|
||||
struct usb_device_descriptor *ddesc = &udev->descriptor;
|
||||
struct usb_config_descriptor *cdesc;
|
||||
|
||||
if (!udev->dev.of_node)
|
||||
return false;
|
||||
|
||||
switch (ddesc->bDeviceClass) {
|
||||
case USB_CLASS_PER_INTERFACE:
|
||||
case USB_CLASS_HUB:
|
||||
if (ddesc->bNumConfigurations == 1) {
|
||||
cdesc = &udev->config->desc;
|
||||
if (cdesc->bNumInterfaces == 1)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_of_has_combined_node);
|
||||
|
||||
/**
|
||||
* usb_of_get_interface_node() - get a USB interface node
|
||||
* @udev: USB device of interface
|
||||
* @config: configuration value
|
||||
* @ifnum: interface number
|
||||
*
|
||||
* Look up the node of a USB interface given its USB device, configuration
|
||||
* value and interface number.
|
||||
*
|
||||
* Return: A pointer to the node with incremented refcount if found, or
|
||||
* %NULL otherwise.
|
||||
*/
|
||||
struct device_node *
|
||||
usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum)
|
||||
{
|
||||
struct device_node *node;
|
||||
u32 reg[2];
|
||||
|
||||
for_each_child_of_node(udev->dev.of_node, node) {
|
||||
if (of_property_read_u32_array(node, "reg", reg, 2))
|
||||
continue;
|
||||
|
||||
if (reg[0] == ifnum && reg[1] == config)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_of_get_interface_node);
|
||||
|
||||
/**
|
||||
* usb_of_get_companion_dev - Find the companion device
|
||||
|
|
|
@ -479,9 +479,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
if (is_out)
|
||||
allowed |= URB_ZERO_PACKET;
|
||||
/* FALLTHROUGH */
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
allowed |= URB_NO_FSBR; /* only affects UHCI */
|
||||
/* FALLTHROUGH */
|
||||
default: /* all non-iso endpoints */
|
||||
if (!is_out)
|
||||
allowed |= URB_SHORT_NOT_OK;
|
||||
|
|
|
@ -645,8 +645,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
raw_port = usb_hcd_find_raw_port_number(usb_hcd,
|
||||
port1);
|
||||
}
|
||||
dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node,
|
||||
raw_port);
|
||||
dev->dev.of_node = usb_of_get_device_node(parent, raw_port);
|
||||
|
||||
/* hub driver sets up TT records */
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ extern int usb_remove_device(struct usb_device *udev);
|
|||
|
||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
unsigned int size);
|
||||
extern int usb_set_isoch_delay(struct usb_device *dev);
|
||||
extern int usb_get_bos_descriptor(struct usb_device *dev);
|
||||
extern void usb_release_bos_descriptor(struct usb_device *dev);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
|
|
|
@ -929,6 +929,7 @@ struct dwc2_hsotg {
|
|||
int irq;
|
||||
struct clk *clk;
|
||||
struct reset_control *reset;
|
||||
struct reset_control *reset_ecc;
|
||||
|
||||
unsigned int queuing_high_bandwidth:1;
|
||||
unsigned int srp_success:1;
|
||||
|
@ -971,6 +972,7 @@ struct dwc2_hsotg {
|
|||
} flags;
|
||||
|
||||
struct list_head non_periodic_sched_inactive;
|
||||
struct list_head non_periodic_sched_waiting;
|
||||
struct list_head non_periodic_sched_active;
|
||||
struct list_head *non_periodic_qh_ptr;
|
||||
struct list_head periodic_sched_inactive;
|
||||
|
|
|
@ -659,6 +659,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
|
|||
list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive,
|
||||
qh_list_entry)
|
||||
dev_dbg(hsotg->dev, " %p\n", qh);
|
||||
dev_dbg(hsotg->dev, " NP waiting sched:\n");
|
||||
list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting,
|
||||
qh_list_entry)
|
||||
dev_dbg(hsotg->dev, " %p\n", qh);
|
||||
dev_dbg(hsotg->dev, " NP active sched:\n");
|
||||
list_for_each_entry(qh, &hsotg->non_periodic_sched_active,
|
||||
qh_list_entry)
|
||||
|
@ -1818,6 +1822,7 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
|
|||
static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive);
|
||||
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready);
|
||||
|
@ -4998,6 +5003,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
|
|||
|
||||
/* Free memory for QH/QTD lists */
|
||||
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive);
|
||||
dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready);
|
||||
|
@ -5159,6 +5165,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
|
|||
|
||||
/* Initialize the non-periodic schedule */
|
||||
INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive);
|
||||
INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting);
|
||||
INIT_LIST_HEAD(&hsotg->non_periodic_sched_active);
|
||||
|
||||
/* Initialize the periodic schedule */
|
||||
|
|
|
@ -314,12 +314,16 @@ struct dwc2_hs_transfer_time {
|
|||
* descriptor and indicates original XferSize value for the
|
||||
* descriptor
|
||||
* @unreserve_timer: Timer for releasing periodic reservation.
|
||||
* @wait_timer: Timer used to wait before re-queuing.
|
||||
* @dwc2_tt: Pointer to our tt info (or NULL if no tt).
|
||||
* @ttport: Port number within our tt.
|
||||
* @tt_buffer_dirty True if clear_tt_buffer_complete is pending
|
||||
* @unreserve_pending: True if we planned to unreserve but haven't yet.
|
||||
* @schedule_low_speed: True if we have a low/full speed component (either the
|
||||
* host is in low/full speed mode or do_split).
|
||||
* @want_wait: We should wait before re-queuing; only matters for non-
|
||||
* periodic transfers and is ignored for periodic ones.
|
||||
* @wait_timer_cancel: Set to true to cancel the wait_timer.
|
||||
*
|
||||
* A Queue Head (QH) holds the static characteristics of an endpoint and
|
||||
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
|
||||
|
@ -354,11 +358,14 @@ struct dwc2_qh {
|
|||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
struct timer_list unreserve_timer;
|
||||
struct timer_list wait_timer;
|
||||
struct dwc2_tt *dwc_tt;
|
||||
int ttport;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
unsigned unreserve_pending:1;
|
||||
unsigned schedule_low_speed:1;
|
||||
unsigned want_wait:1;
|
||||
unsigned wait_timer_cancel:1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -389,6 +396,7 @@ struct dwc2_qh {
|
|||
* @n_desc: Number of DMA descriptors for this QTD
|
||||
* @isoc_frame_index_last: Last activated frame (packet) index, used in
|
||||
* descriptor DMA mode only
|
||||
* @num_naks: Number of NAKs received on this QTD.
|
||||
* @urb: URB for this transfer
|
||||
* @qh: Queue head for this QTD
|
||||
* @qtd_list_entry: For linking to the QH's list of QTDs
|
||||
|
@ -419,6 +427,7 @@ struct dwc2_qtd {
|
|||
u8 error_count;
|
||||
u8 n_desc;
|
||||
u16 isoc_frame_index_last;
|
||||
u16 num_naks;
|
||||
struct dwc2_hcd_urb *urb;
|
||||
struct dwc2_qh *qh;
|
||||
struct list_head qtd_list_entry;
|
||||
|
|
|
@ -53,6 +53,12 @@
|
|||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
|
||||
/*
|
||||
* If we get this many NAKs on a split transaction we'll slow down
|
||||
* retransmission. A 1 here means delay after the first NAK.
|
||||
*/
|
||||
#define DWC2_NAKS_BEFORE_DELAY 3
|
||||
|
||||
/* This function is for debug only */
|
||||
static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
|
@ -1201,11 +1207,25 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
|
|||
/*
|
||||
* Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
|
||||
* interrupt. Re-start the SSPLIT transfer.
|
||||
*
|
||||
* Normally for non-periodic transfers we'll retry right away, but to
|
||||
* avoid interrupt storms we'll wait before retrying if we've got
|
||||
* several NAKs. If we didn't do this we'd retry directly from the
|
||||
* interrupt handler and could end up quickly getting another
|
||||
* interrupt (another NAK), which we'd retry.
|
||||
*
|
||||
* Note that in DMA mode software only gets involved to re-send NAKed
|
||||
* transfers for split transactions, so we only need to apply this
|
||||
* delaying logic when handling splits. In non-DMA mode presumably we
|
||||
* might want a similar delay if someone can demonstrate this problem
|
||||
* affects that code path too.
|
||||
*/
|
||||
if (chan->do_split) {
|
||||
if (chan->complete_split)
|
||||
qtd->error_count = 0;
|
||||
qtd->complete_split = 0;
|
||||
qtd->num_naks++;
|
||||
qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY;
|
||||
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
|
||||
goto handle_nak_done;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,9 @@
|
|||
/* Wait this long before releasing periodic reservation */
|
||||
#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
|
||||
|
||||
/* If we get a NAK, wait this long before retrying */
|
||||
#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1))
|
||||
|
||||
/**
|
||||
* dwc2_periodic_channel_available() - Checks that a channel is available for a
|
||||
* periodic transfer
|
||||
|
@ -1440,6 +1443,55 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
|
|||
list_del_init(&qh->qh_list_entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_wait_timer_fn() - Timer function to re-queue after waiting
|
||||
*
|
||||
* As per the spec, a NAK indicates that "a function is temporarily unable to
|
||||
* transmit or receive data, but will eventually be able to do so without need
|
||||
* of host intervention".
|
||||
*
|
||||
* That means that when we encounter a NAK we're supposed to retry.
|
||||
*
|
||||
* ...but if we retry right away (from the interrupt handler that saw the NAK)
|
||||
* then we can end up with an interrupt storm (if the other side keeps NAKing
|
||||
* us) because on slow enough CPUs it could take us longer to get out of the
|
||||
* interrupt routine than it takes for the device to send another NAK. That
|
||||
* leads to a constant stream of NAK interrupts and the CPU locks.
|
||||
*
|
||||
* ...so instead of retrying right away in the case of a NAK we'll set a timer
|
||||
* to retry some time later. This function handles that timer and moves the
|
||||
* qh back to the "inactive" list, then queues transactions.
|
||||
*
|
||||
* @t: Pointer to wait_timer in a qh.
|
||||
*/
|
||||
static void dwc2_wait_timer_fn(struct timer_list *t)
|
||||
{
|
||||
struct dwc2_qh *qh = from_timer(qh, t, wait_timer);
|
||||
struct dwc2_hsotg *hsotg = qh->hsotg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
/*
|
||||
* We'll set wait_timer_cancel to true if we want to cancel this
|
||||
* operation in dwc2_hcd_qh_unlink().
|
||||
*/
|
||||
if (!qh->wait_timer_cancel) {
|
||||
enum dwc2_transaction_type tr_type;
|
||||
|
||||
qh->want_wait = false;
|
||||
|
||||
list_move(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
|
||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_qh_init() - Initializes a QH structure
|
||||
*
|
||||
|
@ -1468,6 +1520,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
/* Initialize QH */
|
||||
qh->hsotg = hsotg;
|
||||
timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0);
|
||||
timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0);
|
||||
qh->ep_type = ep_type;
|
||||
qh->ep_is_in = ep_is_in;
|
||||
|
||||
|
@ -1628,6 +1681,16 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
dwc2_do_unreserve(hsotg, qh);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't have the lock so we can safely wait until the wait timer
|
||||
* finishes. Of course, at this point in time we'd better have set
|
||||
* wait_timer_active to false so if this timer was still pending it
|
||||
* won't do anything anyway, but we want it to finish before we free
|
||||
* memory.
|
||||
*/
|
||||
del_timer_sync(&qh->wait_timer);
|
||||
|
||||
dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
|
||||
|
||||
if (qh->desc_list)
|
||||
|
@ -1663,9 +1726,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
qh->start_active_frame = hsotg->frame_number;
|
||||
qh->next_active_frame = qh->start_active_frame;
|
||||
|
||||
/* Always start in inactive schedule */
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
if (qh->want_wait) {
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_waiting);
|
||||
qh->wait_timer_cancel = false;
|
||||
mod_timer(&qh->wait_timer,
|
||||
jiffies + DWC2_RETRY_WAIT_DELAY + 1);
|
||||
} else {
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1695,6 +1765,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
||||
/* If the wait_timer is pending, this will stop it from acting */
|
||||
qh->wait_timer_cancel = true;
|
||||
|
||||
if (list_empty(&qh->qh_list_entry))
|
||||
/* QH is not in a schedule */
|
||||
return;
|
||||
|
@ -1903,7 +1976,7 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
if (dwc2_qh_is_non_per(qh)) {
|
||||
dwc2_hcd_qh_unlink(hsotg, qh);
|
||||
if (!list_empty(&qh->qtd_list))
|
||||
/* Add back to inactive non-periodic schedule */
|
||||
/* Add back to inactive/waiting non-periodic schedule */
|
||||
dwc2_hcd_qh_add(hsotg, qh);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -221,6 +221,15 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
|
|||
|
||||
reset_control_deassert(hsotg->reset);
|
||||
|
||||
hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc");
|
||||
if (IS_ERR(hsotg->reset_ecc)) {
|
||||
ret = PTR_ERR(hsotg->reset_ecc);
|
||||
dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reset_control_deassert(hsotg->reset_ecc);
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
|
@ -319,6 +328,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
|||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
|
||||
reset_control_assert(hsotg->reset);
|
||||
reset_control_assert(hsotg->reset_ecc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
otg_set_vbus(dwc->usb2_phy->otg, true);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
|
||||
phy_calibrate(dwc->usb2_generic_phy);
|
||||
}
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
|
@ -945,6 +946,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
|||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
phy_calibrate(dwc->usb2_generic_phy);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
|
||||
|
@ -1062,6 +1064,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
|
||||
&dwc->fladj);
|
||||
|
||||
dwc->dis_metastability_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_metastability_quirk");
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
|
|
|
@ -796,7 +796,6 @@ struct dwc3_scratchpad_array {
|
|||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
* @usb3_generic_phy: pointer to USB3 PHY
|
||||
* @ulpi: pointer to ulpi interface
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
* @u2sel: parameter from Set SEL request.
|
||||
* @u2pel: parameter from Set SEL request.
|
||||
* @u1sel: parameter from Set SEL request.
|
||||
|
@ -857,6 +856,7 @@ struct dwc3_scratchpad_array {
|
|||
* 1 - -3.5dB de-emphasis
|
||||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* @dis_metastability_quirk: set to disable metastability quirk.
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
* increments or 0 to disable.
|
||||
*/
|
||||
|
@ -955,7 +955,6 @@ struct dwc3 {
|
|||
enum dwc3_ep0_state ep0state;
|
||||
enum dwc3_link_state link_state;
|
||||
|
||||
u16 isoch_delay;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
u8 u1sel;
|
||||
|
@ -1010,6 +1009,8 @@ struct dwc3 {
|
|||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
unsigned dis_metastability_quirk:1;
|
||||
|
||||
u16 imod_interval;
|
||||
};
|
||||
|
||||
|
|
|
@ -247,6 +247,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
|
|||
case USB_DEVICE_TEST_MODE:
|
||||
s = "Test Mode";
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
s = "U1 Enable";
|
||||
break;
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
s = "U2 Enable";
|
||||
break;
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
s = "LTM Enable";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
} s; }),
|
||||
|
|
|
@ -736,11 +736,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct
|
|||
if (wIndex || wLength)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* REVISIT It's unclear from Databook what to do with this
|
||||
* value. For now, just cache it.
|
||||
*/
|
||||
dwc->isoch_delay = wValue;
|
||||
dwc->gadget.isoch_delay = wValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2005,7 +2005,8 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
|
|||
* STAR#9000525659: Clock Domain Crossing on DCTL in
|
||||
* USB 2.0 Mode
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A) {
|
||||
if (dwc->revision < DWC3_REVISION_220A &&
|
||||
!dwc->dis_metastability_quirk) {
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
} else {
|
||||
switch (speed) {
|
||||
|
@ -3224,7 +3225,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
* is less than super speed because we don't have means, yet, to tell
|
||||
* composite.c that we are USB 2.0 + LPM ECN.
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A)
|
||||
if (dwc->revision < DWC3_REVISION_220A &&
|
||||
!dwc->dis_metastability_quirk)
|
||||
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
|
||||
dwc->revision);
|
||||
|
||||
|
|
|
@ -37,12 +37,12 @@ DECLARE_EVENT_CLASS(dwc3_log_io,
|
|||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_io, dwc3_readl,
|
||||
TP_PROTO(void *base, u32 offset, u32 value),
|
||||
TP_PROTO(void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_io, dwc3_writel,
|
||||
TP_PROTO(void *base, u32 offset, u32 value),
|
||||
TP_PROTO(void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value)
|
||||
);
|
||||
|
||||
|
|
|
@ -328,7 +328,7 @@ static void xdbc_mem_init(void)
|
|||
ep_in = (struct xdbc_ep_context *)&ctx->in;
|
||||
|
||||
ep_in->ep_info1 = 0;
|
||||
ep_in->ep_info2 = cpu_to_le32(EP_TYPE(BULK_OUT_EP) | MAX_PACKET(1024) | MAX_BURST(max_burst));
|
||||
ep_in->ep_info2 = cpu_to_le32(EP_TYPE(BULK_IN_EP) | MAX_PACKET(1024) | MAX_BURST(max_burst));
|
||||
ep_in->deq = cpu_to_le64(xdbc.in_seg.dma | xdbc.in_ring.cycle_state);
|
||||
|
||||
/* Set DbC context and info registers: */
|
||||
|
|
|
@ -266,6 +266,7 @@ static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
}
|
||||
|
||||
static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
|
||||
__releases(&ffs->ev.waitq.lock)
|
||||
{
|
||||
struct usb_request *req = ffs->ep0req;
|
||||
int ret;
|
||||
|
@ -458,6 +459,7 @@ done_spin:
|
|||
/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
|
||||
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
|
||||
size_t n)
|
||||
__releases(&ffs->ev.waitq.lock)
|
||||
{
|
||||
/*
|
||||
* n cannot be bigger than ffs->ev.count, which cannot be bigger than
|
||||
|
@ -543,6 +545,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
|||
break;
|
||||
}
|
||||
|
||||
/* unlocks spinlock */
|
||||
return __ffs_ep0_read_events(ffs, buf,
|
||||
min(n, (size_t)ffs->ev.count));
|
||||
|
||||
|
@ -1246,7 +1249,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
desc = epfile->ep->descs[desc_idx];
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = copy_to_user((void *)value, desc, desc->bLength);
|
||||
ret = copy_to_user((void __user *)value, desc, desc->bLength);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
|
@ -2324,7 +2327,7 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
|
|||
length, pnl, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
|
||||
pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl));
|
||||
if (length != 14 + pnl + pdl) {
|
||||
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
|
||||
length, pnl, pdl, type);
|
||||
|
@ -2878,7 +2881,7 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
|
|||
|
||||
ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
|
||||
ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
|
||||
ext_prop->data_len = le32_to_cpu(*(u32 *)
|
||||
ext_prop->data_len = le32_to_cpu(*(__le32 *)
|
||||
usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
|
||||
length = ext_prop->name_len + ext_prop->data_len + 14;
|
||||
|
||||
|
@ -3700,7 +3703,8 @@ static void ffs_closed(struct ffs_data *ffs)
|
|||
ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
|
||||
ffs_dev_unlock();
|
||||
|
||||
unregister_gadget_item(ci);
|
||||
if (test_bit(FFS_FL_BOUND, &ffs->flags))
|
||||
unregister_gadget_item(ci);
|
||||
return;
|
||||
done:
|
||||
ffs_dev_unlock();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
|
@ -80,19 +81,11 @@
|
|||
#define WRITE_BUF_SIZE 8192 /* TX only */
|
||||
#define GS_CONSOLE_BUF_SIZE 8192
|
||||
|
||||
/* circular buffer */
|
||||
struct gs_buf {
|
||||
unsigned buf_size;
|
||||
char *buf_buf;
|
||||
char *buf_get;
|
||||
char *buf_put;
|
||||
};
|
||||
|
||||
/* console info */
|
||||
struct gscons_info {
|
||||
struct gs_port *port;
|
||||
struct task_struct *console_thread;
|
||||
struct gs_buf con_buf;
|
||||
struct kfifo con_buf;
|
||||
/* protect the buf and busy flag */
|
||||
spinlock_t con_lock;
|
||||
int req_busy;
|
||||
|
@ -122,7 +115,7 @@ struct gs_port {
|
|||
struct list_head write_pool;
|
||||
int write_started;
|
||||
int write_allocated;
|
||||
struct gs_buf port_write_buf;
|
||||
struct kfifo port_write_buf;
|
||||
wait_queue_head_t drain_wait; /* wait while writes drain */
|
||||
bool write_busy;
|
||||
wait_queue_head_t close_wait;
|
||||
|
@ -154,144 +147,6 @@ static struct portmaster {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Circular Buffer */
|
||||
|
||||
/*
|
||||
* gs_buf_alloc
|
||||
*
|
||||
* Allocate a circular buffer and all associated memory.
|
||||
*/
|
||||
static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
|
||||
{
|
||||
gb->buf_buf = kmalloc(size, GFP_KERNEL);
|
||||
if (gb->buf_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
gb->buf_size = size;
|
||||
gb->buf_put = gb->buf_buf;
|
||||
gb->buf_get = gb->buf_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_free
|
||||
*
|
||||
* Free the buffer and all associated memory.
|
||||
*/
|
||||
static void gs_buf_free(struct gs_buf *gb)
|
||||
{
|
||||
kfree(gb->buf_buf);
|
||||
gb->buf_buf = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_clear
|
||||
*
|
||||
* Clear out all data in the circular buffer.
|
||||
*/
|
||||
static void gs_buf_clear(struct gs_buf *gb)
|
||||
{
|
||||
gb->buf_get = gb->buf_put;
|
||||
/* equivalent to a get of all data available */
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_data_avail
|
||||
*
|
||||
* Return the number of bytes of data written into the circular
|
||||
* buffer.
|
||||
*/
|
||||
static unsigned gs_buf_data_avail(struct gs_buf *gb)
|
||||
{
|
||||
return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_space_avail
|
||||
*
|
||||
* Return the number of bytes of space available in the circular
|
||||
* buffer.
|
||||
*/
|
||||
static unsigned gs_buf_space_avail(struct gs_buf *gb)
|
||||
{
|
||||
return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_put
|
||||
*
|
||||
* Copy data data from a user buffer and put it into the circular buffer.
|
||||
* Restrict to the amount of space available.
|
||||
*
|
||||
* Return the number of bytes copied.
|
||||
*/
|
||||
static unsigned
|
||||
gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
len = gs_buf_space_avail(gb);
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
len = gb->buf_buf + gb->buf_size - gb->buf_put;
|
||||
if (count > len) {
|
||||
memcpy(gb->buf_put, buf, len);
|
||||
memcpy(gb->buf_buf, buf+len, count - len);
|
||||
gb->buf_put = gb->buf_buf + count - len;
|
||||
} else {
|
||||
memcpy(gb->buf_put, buf, count);
|
||||
if (count < len)
|
||||
gb->buf_put += count;
|
||||
else /* count == len */
|
||||
gb->buf_put = gb->buf_buf;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_buf_get
|
||||
*
|
||||
* Get data from the circular buffer and copy to the given buffer.
|
||||
* Restrict to the amount of data available.
|
||||
*
|
||||
* Return the number of bytes copied.
|
||||
*/
|
||||
static unsigned
|
||||
gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
len = gs_buf_data_avail(gb);
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
len = gb->buf_buf + gb->buf_size - gb->buf_get;
|
||||
if (count > len) {
|
||||
memcpy(buf, gb->buf_get, len);
|
||||
memcpy(buf+len, gb->buf_buf, count - len);
|
||||
gb->buf_get = gb->buf_buf + count - len;
|
||||
} else {
|
||||
memcpy(buf, gb->buf_get, count);
|
||||
if (count < len)
|
||||
gb->buf_get += count;
|
||||
else /* count == len */
|
||||
gb->buf_get = gb->buf_buf;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* I/O glue between TTY (upper) and USB function (lower) driver layers */
|
||||
|
||||
/*
|
||||
|
@ -346,11 +201,11 @@ gs_send_packet(struct gs_port *port, char *packet, unsigned size)
|
|||
{
|
||||
unsigned len;
|
||||
|
||||
len = gs_buf_data_avail(&port->port_write_buf);
|
||||
len = kfifo_len(&port->port_write_buf);
|
||||
if (len < size)
|
||||
size = len;
|
||||
if (size != 0)
|
||||
size = gs_buf_get(&port->port_write_buf, packet, size);
|
||||
size = kfifo_out(&port->port_write_buf, packet, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -398,7 +253,7 @@ __acquires(&port->port_lock)
|
|||
|
||||
req->length = len;
|
||||
list_del(&req->list);
|
||||
req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
|
||||
req->zero = kfifo_is_empty(&port->port_write_buf);
|
||||
|
||||
pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
port->port_num, len, *((u8 *)req->buf),
|
||||
|
@ -787,10 +642,11 @@ static int gs_open(struct tty_struct *tty, struct file *file)
|
|||
spin_lock_irq(&port->port_lock);
|
||||
|
||||
/* allocate circular buffer on first open */
|
||||
if (port->port_write_buf.buf_buf == NULL) {
|
||||
if (!kfifo_initialized(&port->port_write_buf)) {
|
||||
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
|
||||
status = kfifo_alloc(&port->port_write_buf,
|
||||
WRITE_BUF_SIZE, GFP_KERNEL);
|
||||
spin_lock_irq(&port->port_lock);
|
||||
|
||||
if (status) {
|
||||
|
@ -839,7 +695,7 @@ static int gs_writes_finished(struct gs_port *p)
|
|||
|
||||
/* return true on disconnect or empty buffer */
|
||||
spin_lock_irq(&p->port_lock);
|
||||
cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
|
||||
cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf);
|
||||
spin_unlock_irq(&p->port_lock);
|
||||
|
||||
return cond;
|
||||
|
@ -875,7 +731,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
/* wait for circular write buffer to drain, disconnect, or at
|
||||
* most GS_CLOSE_TIMEOUT seconds; then discard the rest
|
||||
*/
|
||||
if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
|
||||
if (kfifo_len(&port->port_write_buf) > 0 && gser) {
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
wait_event_interruptible_timeout(port->drain_wait,
|
||||
gs_writes_finished(port),
|
||||
|
@ -889,9 +745,9 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
* let the push tasklet fire again until we're re-opened.
|
||||
*/
|
||||
if (gser == NULL)
|
||||
gs_buf_free(&port->port_write_buf);
|
||||
kfifo_free(&port->port_write_buf);
|
||||
else
|
||||
gs_buf_clear(&port->port_write_buf);
|
||||
kfifo_reset(&port->port_write_buf);
|
||||
|
||||
port->port.tty = NULL;
|
||||
|
||||
|
@ -915,7 +771,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
|||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (count)
|
||||
count = gs_buf_put(&port->port_write_buf, buf, count);
|
||||
count = kfifo_in(&port->port_write_buf, buf, count);
|
||||
/* treat count == 0 as flush_chars() */
|
||||
if (port->port_usb)
|
||||
gs_start_tx(port);
|
||||
|
@ -934,7 +790,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
|
|||
port->port_num, tty, ch, __builtin_return_address(0));
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
status = gs_buf_put(&port->port_write_buf, &ch, 1);
|
||||
status = kfifo_put(&port->port_write_buf, ch);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return status;
|
||||
|
@ -961,7 +817,7 @@ static int gs_write_room(struct tty_struct *tty)
|
|||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (port->port_usb)
|
||||
room = gs_buf_space_avail(&port->port_write_buf);
|
||||
room = kfifo_avail(&port->port_write_buf);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
|
||||
|
@ -977,7 +833,7 @@ static int gs_chars_in_buffer(struct tty_struct *tty)
|
|||
int chars = 0;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
chars = gs_buf_data_avail(&port->port_write_buf);
|
||||
chars = kfifo_len(&port->port_write_buf);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
|
||||
|
@ -1148,7 +1004,7 @@ static int gs_console_thread(void *data)
|
|||
ep = port->port_usb->in;
|
||||
|
||||
spin_lock_irq(&info->con_lock);
|
||||
count = gs_buf_data_avail(&info->con_buf);
|
||||
count = kfifo_len(&info->con_buf);
|
||||
size = ep->maxpacket;
|
||||
|
||||
if (count > 0 && !info->req_busy) {
|
||||
|
@ -1156,7 +1012,7 @@ static int gs_console_thread(void *data)
|
|||
if (count < size)
|
||||
size = count;
|
||||
|
||||
xfer = gs_buf_get(&info->con_buf, req->buf, size);
|
||||
xfer = kfifo_out(&info->con_buf, req->buf, size);
|
||||
req->length = xfer;
|
||||
|
||||
spin_unlock(&info->con_lock);
|
||||
|
@ -1192,7 +1048,7 @@ static int gs_console_setup(struct console *co, char *options)
|
|||
info->req_busy = 0;
|
||||
spin_lock_init(&info->con_lock);
|
||||
|
||||
status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE);
|
||||
status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL);
|
||||
if (status) {
|
||||
pr_err("%s: allocate console buffer failed\n", __func__);
|
||||
return status;
|
||||
|
@ -1202,7 +1058,7 @@ static int gs_console_setup(struct console *co, char *options)
|
|||
co, "gs_console");
|
||||
if (IS_ERR(info->console_thread)) {
|
||||
pr_err("%s: cannot create console thread\n", __func__);
|
||||
gs_buf_free(&info->con_buf);
|
||||
kfifo_free(&info->con_buf);
|
||||
return PTR_ERR(info->console_thread);
|
||||
}
|
||||
wake_up_process(info->console_thread);
|
||||
|
@ -1217,7 +1073,7 @@ static void gs_console_write(struct console *co,
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->con_lock, flags);
|
||||
gs_buf_put(&info->con_buf, buf, count);
|
||||
kfifo_in(&info->con_buf, buf, count);
|
||||
spin_unlock_irqrestore(&info->con_lock, flags);
|
||||
|
||||
wake_up_process(info->console_thread);
|
||||
|
@ -1256,7 +1112,7 @@ static void gserial_console_exit(void)
|
|||
unregister_console(&gserial_cons);
|
||||
if (!IS_ERR_OR_NULL(info->console_thread))
|
||||
kthread_stop(info->console_thread);
|
||||
gs_buf_free(&info->con_buf);
|
||||
kfifo_free(&info->con_buf);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -1529,7 +1385,7 @@ void gserial_disconnect(struct gserial *gser)
|
|||
/* finally, free any unused/unusable I/O buffers */
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (port->port.count == 0 && !port->openclose)
|
||||
gs_buf_free(&port->port_write_buf);
|
||||
kfifo_free(&port->port_write_buf);
|
||||
gs_free_requests(gser->out, &port->read_pool, NULL);
|
||||
gs_free_requests(gser->out, &port->read_queue, NULL);
|
||||
gs_free_requests(gser->in, &port->write_pool, NULL);
|
||||
|
|
|
@ -1470,7 +1470,6 @@ delegate:
|
|||
dev->setup_wLength = w_length;
|
||||
dev->setup_out_ready = 0;
|
||||
dev->setup_out_error = 0;
|
||||
value = 0;
|
||||
|
||||
/* read DATA stage for OUT right away */
|
||||
if (unlikely (!dev->setup_in && w_length)) {
|
||||
|
|
|
@ -102,10 +102,8 @@ static int ncm_do_config(struct usb_configuration *c)
|
|||
}
|
||||
|
||||
f_ncm = usb_get_function(f_ncm_inst);
|
||||
if (IS_ERR(f_ncm)) {
|
||||
status = PTR_ERR(f_ncm);
|
||||
return status;
|
||||
}
|
||||
if (IS_ERR(f_ncm))
|
||||
return PTR_ERR(f_ncm);
|
||||
|
||||
status = usb_add_function(c, f_ncm);
|
||||
if (status < 0) {
|
||||
|
|
|
@ -2385,10 +2385,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
goto out_uninit;
|
||||
}
|
||||
if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
|
||||
dev_name(dev), udc) < 0) {
|
||||
dev_err(dev, "error requesting IRQ #%d\n", irq);
|
||||
goto out_uninit;
|
||||
}
|
||||
dev_name(dev), udc) < 0)
|
||||
goto report_request_failure;
|
||||
|
||||
/* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
|
||||
for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
|
||||
|
@ -2398,10 +2396,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
goto out_uninit;
|
||||
}
|
||||
if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
|
||||
dev_name(dev), &udc->iudma[i]) < 0) {
|
||||
dev_err(dev, "error requesting IRQ #%d\n", irq);
|
||||
goto out_uninit;
|
||||
}
|
||||
dev_name(dev), &udc->iudma[i]) < 0)
|
||||
goto report_request_failure;
|
||||
}
|
||||
|
||||
bcm63xx_udc_init_debugfs(udc);
|
||||
|
@ -2413,6 +2409,10 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
out_uninit:
|
||||
bcm63xx_uninit_udc_hw(udc);
|
||||
return rc;
|
||||
|
||||
report_request_failure:
|
||||
dev_err(dev, "error requesting IRQ #%d\n", irq);
|
||||
goto out_uninit;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -912,7 +912,7 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
|
|||
return 0;
|
||||
|
||||
/* "high bandwidth" works only at high speed */
|
||||
if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp(desc) & (3<<11))
|
||||
if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1)
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
|
@ -1417,7 +1417,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
|
|||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static ssize_t usb_udc_srp_store(struct device *dev,
|
||||
static ssize_t srp_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
|
@ -1427,9 +1427,9 @@ static ssize_t usb_udc_srp_store(struct device *dev,
|
|||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store);
|
||||
static DEVICE_ATTR_WO(srp);
|
||||
|
||||
static ssize_t usb_udc_softconn_store(struct device *dev,
|
||||
static ssize_t soft_connect_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
|
@ -1453,7 +1453,7 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
|
|||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store);
|
||||
static DEVICE_ATTR_WO(soft_connect);
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
|
@ -925,20 +925,8 @@ static void dummy_udc_set_speed(struct usb_gadget *_gadget,
|
|||
struct dummy *dum;
|
||||
|
||||
dum = gadget_dev_to_dummy(&_gadget->dev);
|
||||
|
||||
if (mod_data.is_super_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_SUPER, speed);
|
||||
else if (mod_data.is_high_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed);
|
||||
else
|
||||
dum->gadget.speed = USB_SPEED_FULL;
|
||||
|
||||
dum->gadget.speed = speed;
|
||||
dummy_udc_update_ep0(dum);
|
||||
|
||||
if (dum->gadget.speed < speed)
|
||||
dev_dbg(udc_dev(dum), "This device can perform faster"
|
||||
" if you connect it to a %s port...\n",
|
||||
usb_speed_string(speed));
|
||||
}
|
||||
|
||||
static int dummy_udc_start(struct usb_gadget *g,
|
||||
|
@ -2193,8 +2181,6 @@ static int dummy_hub_control(
|
|||
USB_PORT_STAT_LOW_SPEED;
|
||||
break;
|
||||
default:
|
||||
dum_hcd->dum->gadget.speed =
|
||||
USB_SPEED_FULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1543,7 +1543,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
|
|||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
break;
|
||||
case WAIT_FOR_SETUP:
|
||||
ERR("Unexpect ep0 packets\n");
|
||||
ERR("Unexpected ep0 packets\n");
|
||||
break;
|
||||
default:
|
||||
ep0stall(udc);
|
||||
|
|
|
@ -979,8 +979,6 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
|
|||
|
||||
max = ep->fifo_size;
|
||||
do {
|
||||
is_short = 0;
|
||||
|
||||
udccsr = udc_ep_readl(ep, UDCCSR);
|
||||
if (udccsr & UDCCSR_PC) {
|
||||
ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
|
||||
|
@ -1134,7 +1132,6 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
if (unlikely(!_ep))
|
||||
return -EINVAL;
|
||||
|
||||
dev = udc_usb_ep->dev;
|
||||
ep = udc_usb_ep->pxa_ep;
|
||||
if (unlikely(!ep))
|
||||
return -EINVAL;
|
||||
|
|
|
@ -225,6 +225,7 @@ DECLARE_EVENT_CLASS(udc_log_req,
|
|||
__field(unsigned, short_not_ok)
|
||||
__field(int, status)
|
||||
__field(int, ret)
|
||||
__field(struct usb_request *, req)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name);
|
||||
|
@ -238,9 +239,10 @@ DECLARE_EVENT_CLASS(udc_log_req,
|
|||
__entry->short_not_ok = req->short_not_ok;
|
||||
__entry->status = req->status;
|
||||
__entry->ret = ret;
|
||||
__entry->req = req;
|
||||
),
|
||||
TP_printk("%s: length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
|
||||
__get_str(name), __entry->actual, __entry->length,
|
||||
TP_printk("%s: req %p length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
|
||||
__get_str(name),__entry->req, __entry->actual, __entry->length,
|
||||
__entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
|
|
|
@ -963,10 +963,8 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
|
|||
gfp_t gfp_flags)
|
||||
{
|
||||
struct xusb_ep *ep = to_xusb_ep(_ep);
|
||||
struct xusb_udc *udc;
|
||||
struct xusb_req *req;
|
||||
|
||||
udc = ep->udc;
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
|
|
@ -27,6 +27,14 @@ config USB_XHCI_HCD
|
|||
module will be called xhci-hcd.
|
||||
|
||||
if USB_XHCI_HCD
|
||||
config USB_XHCI_DBGCAP
|
||||
bool "xHCI support for debug capability"
|
||||
depends on TTY
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI debug capability. Make
|
||||
sure that your xHCI host supports the extended debug capability and
|
||||
you want a TTY serial device based on the xHCI debug capability
|
||||
before enabling this option. If unsure, say 'N'.
|
||||
|
||||
config USB_XHCI_PCI
|
||||
tristate
|
||||
|
|
|
@ -14,6 +14,11 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
|
|||
xhci-hcd-y := xhci.o xhci-mem.o
|
||||
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
|
||||
xhci-hcd-y += xhci-trace.o
|
||||
|
||||
ifneq ($(CONFIG_USB_XHCI_DBGCAP), )
|
||||
xhci-hcd-y += xhci-dbgcap.o xhci-dbgtty.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USB_XHCI_MTK), )
|
||||
xhci-hcd-y += xhci-mtk-sch.o
|
||||
endif
|
||||
|
|
|
@ -167,7 +167,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
|||
continue;
|
||||
|
||||
ret = PTR_ERR(phy);
|
||||
dev_err(dev, "Can't get PHY device for port %d: %d\n",
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Can't get PHY for port %d: %d\n",
|
||||
i, ret);
|
||||
goto err_phy;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
|
||||
/* Display the ports dedicated to the companion controller */
|
||||
static ssize_t show_companion(struct device *dev,
|
||||
static ssize_t companion_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ static ssize_t show_companion(struct device *dev,
|
|||
* Syntax is "[-]portnum", where a leading '-' sign means
|
||||
* return control of the port to the EHCI controller.
|
||||
*/
|
||||
static ssize_t store_companion(struct device *dev,
|
||||
static ssize_t companion_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
@ -59,13 +59,13 @@ static ssize_t store_companion(struct device *dev,
|
|||
set_owner(ehci, portnum, new_owner);
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
|
||||
static DEVICE_ATTR_RW(companion);
|
||||
|
||||
|
||||
/*
|
||||
* Display / Set uframe_periodic_max
|
||||
*/
|
||||
static ssize_t show_uframe_periodic_max(struct device *dev,
|
||||
static ssize_t uframe_periodic_max_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ static ssize_t show_uframe_periodic_max(struct device *dev,
|
|||
}
|
||||
|
||||
|
||||
static ssize_t store_uframe_periodic_max(struct device *dev,
|
||||
static ssize_t uframe_periodic_max_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ out_unlock:
|
|||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max);
|
||||
static DEVICE_ATTR_RW(uframe_periodic_max);
|
||||
|
||||
|
||||
static inline int create_sysfs_files(struct ehci_hcd *ehci)
|
||||
|
|
|
@ -1865,11 +1865,9 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
|
|||
qh = kzalloc(sizeof(*qh), GFP_ATOMIC);
|
||||
if (!qh)
|
||||
goto done;
|
||||
qh->hw = (struct fotg210_qh_hw *)
|
||||
dma_pool_alloc(fotg210->qh_pool, flags, &dma);
|
||||
qh->hw = dma_pool_zalloc(fotg210->qh_pool, flags, &dma);
|
||||
if (!qh->hw)
|
||||
goto fail;
|
||||
memset(qh->hw, 0, sizeof(*qh->hw));
|
||||
qh->qh_dma = dma;
|
||||
INIT_LIST_HEAD(&qh->qtd_list);
|
||||
|
||||
|
@ -4121,7 +4119,7 @@ static int itd_urb_transaction(struct fotg210_iso_stream *stream,
|
|||
} else {
|
||||
alloc_itd:
|
||||
spin_unlock_irqrestore(&fotg210->lock, flags);
|
||||
itd = dma_pool_alloc(fotg210->itd_pool, mem_flags,
|
||||
itd = dma_pool_zalloc(fotg210->itd_pool, mem_flags,
|
||||
&itd_dma);
|
||||
spin_lock_irqsave(&fotg210->lock, flags);
|
||||
if (!itd) {
|
||||
|
@ -4131,7 +4129,6 @@ alloc_itd:
|
|||
}
|
||||
}
|
||||
|
||||
memset(itd, 0, sizeof(*itd));
|
||||
itd->itd_dma = itd_dma;
|
||||
list_add(&itd->itd_list, &sched->td_list);
|
||||
}
|
||||
|
@ -4696,7 +4693,7 @@ static void scan_isoc(struct fotg210_hcd *fotg210)
|
|||
|
||||
/* Display / Set uframe_periodic_max
|
||||
*/
|
||||
static ssize_t show_uframe_periodic_max(struct device *dev,
|
||||
static ssize_t uframe_periodic_max_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fotg210_hcd *fotg210;
|
||||
|
@ -4708,7 +4705,7 @@ static ssize_t show_uframe_periodic_max(struct device *dev,
|
|||
}
|
||||
|
||||
|
||||
static ssize_t store_uframe_periodic_max(struct device *dev,
|
||||
static ssize_t uframe_periodic_max_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct fotg210_hcd *fotg210;
|
||||
|
@ -4775,8 +4772,7 @@ out_unlock:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max,
|
||||
store_uframe_periodic_max);
|
||||
static DEVICE_ATTR_RW(uframe_periodic_max);
|
||||
|
||||
static inline int create_sysfs_files(struct fotg210_hcd *fotg210)
|
||||
{
|
||||
|
|
|
@ -413,7 +413,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
|
|||
da8xx_ohci = to_da8xx_ohci(hcd);
|
||||
da8xx_ohci->hcd = hcd;
|
||||
|
||||
da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11");
|
||||
da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(da8xx_ohci->usb11_clk)) {
|
||||
error = PTR_ERR(da8xx_ohci->usb11_clk);
|
||||
if (error != -EPROBE_DEFER)
|
||||
|
|
|
@ -600,7 +600,7 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||
uhci->dentry = dentry;
|
||||
#endif
|
||||
|
||||
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
|
||||
uhci->frame = dma_zalloc_coherent(uhci_dev(uhci),
|
||||
UHCI_NUMFRAMES * sizeof(*uhci->frame),
|
||||
&uhci->frame_dma_handle, GFP_KERNEL);
|
||||
if (!uhci->frame) {
|
||||
|
@ -608,7 +608,6 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||
"unable to allocate consistent memory for frame list\n");
|
||||
goto err_alloc_frame;
|
||||
}
|
||||
memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));
|
||||
|
||||
uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
|
||||
GFP_KERNEL);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT)
|
||||
#define PIPE_DEVEP_MASK 0x0007ff00
|
||||
|
@ -447,6 +448,8 @@ struct uhci_hcd {
|
|||
int total_load; /* Sum of array values */
|
||||
short load[MAX_PHASE]; /* Periodic allocations */
|
||||
|
||||
struct clk *clk; /* (optional) clock source */
|
||||
|
||||
/* Reset host controller */
|
||||
void (*reset_hc) (struct uhci_hcd *uhci);
|
||||
int (*check_and_reset_hc) (struct uhci_hcd *uhci);
|
||||
|
|
|
@ -89,6 +89,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
|
|||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
uhci = hcd_to_uhci(hcd);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
|
@ -98,8 +100,6 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
|
|||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
uhci = hcd_to_uhci(hcd);
|
||||
|
||||
uhci->regs = hcd->regs;
|
||||
|
||||
/* Grab some things from the device-tree */
|
||||
|
@ -119,13 +119,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
|
|||
"Enabled Aspeed implementation workarounds\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Get and enable clock if any specified */
|
||||
uhci->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(uhci->clk)) {
|
||||
ret = PTR_ERR(uhci->clk);
|
||||
goto err_rmr;
|
||||
}
|
||||
ret = clk_prepare_enable(uhci->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret);
|
||||
goto err_rmr;
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto err_rmr;
|
||||
goto err_clk;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(uhci->clk);
|
||||
err_rmr:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
@ -135,7 +150,9 @@ err_rmr:
|
|||
static int uhci_hcd_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
|
||||
clk_disable_unprepare(uhci->clk);
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
|
|
@ -73,8 +73,7 @@ static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
|
|||
{
|
||||
struct urb_priv *urbp = urb->hcpriv;
|
||||
|
||||
if (!(urb->transfer_flags & URB_NO_FSBR))
|
||||
urbp->fsbr = 1;
|
||||
urbp->fsbr = 1;
|
||||
}
|
||||
|
||||
static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
|
||||
|
|
|
@ -90,9 +90,7 @@ static uint32_t process_qset(struct whc *whc, struct whc_qset *qset)
|
|||
|
||||
while (qset->ntds) {
|
||||
struct whc_qtd *td;
|
||||
int t;
|
||||
|
||||
t = qset->td_start;
|
||||
td = &qset->qtd[qset->td_start];
|
||||
status = le32_to_cpu(td->status);
|
||||
|
||||
|
|
|
@ -10,267 +10,6 @@
|
|||
|
||||
#include "xhci.h"
|
||||
|
||||
#define XHCI_INIT_VALUE 0x0
|
||||
|
||||
/* Add verbose debugging later, just print everything for now */
|
||||
|
||||
void xhci_dbg_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
xhci_dbg(xhci, "// xHCI capability registers at %p:\n",
|
||||
xhci->cap_regs);
|
||||
temp = readl(&xhci->cap_regs->hc_capbase);
|
||||
xhci_dbg(xhci, "// @%p = 0x%x (CAPLENGTH AND HCIVERSION)\n",
|
||||
&xhci->cap_regs->hc_capbase, temp);
|
||||
xhci_dbg(xhci, "// CAPLENGTH: 0x%x\n",
|
||||
(unsigned int) HC_LENGTH(temp));
|
||||
xhci_dbg(xhci, "// HCIVERSION: 0x%x\n",
|
||||
(unsigned int) HC_VERSION(temp));
|
||||
|
||||
xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs);
|
||||
|
||||
temp = readl(&xhci->cap_regs->run_regs_off);
|
||||
xhci_dbg(xhci, "// @%p = 0x%x RTSOFF\n",
|
||||
&xhci->cap_regs->run_regs_off,
|
||||
(unsigned int) temp & RTSOFF_MASK);
|
||||
xhci_dbg(xhci, "// xHCI runtime registers at %p:\n", xhci->run_regs);
|
||||
|
||||
temp = readl(&xhci->cap_regs->db_off);
|
||||
xhci_dbg(xhci, "// @%p = 0x%x DBOFF\n", &xhci->cap_regs->db_off, temp);
|
||||
xhci_dbg(xhci, "// Doorbell array at %p:\n", xhci->dba);
|
||||
}
|
||||
|
||||
static void xhci_print_cap_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
u32 hci_version;
|
||||
|
||||
xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs);
|
||||
|
||||
temp = readl(&xhci->cap_regs->hc_capbase);
|
||||
hci_version = HC_VERSION(temp);
|
||||
xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, "CAPLENGTH: 0x%x\n",
|
||||
(unsigned int) HC_LENGTH(temp));
|
||||
xhci_dbg(xhci, "HCIVERSION: 0x%x\n", hci_version);
|
||||
|
||||
temp = readl(&xhci->cap_regs->hcs_params1);
|
||||
xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, " Max device slots: %u\n",
|
||||
(unsigned int) HCS_MAX_SLOTS(temp));
|
||||
xhci_dbg(xhci, " Max interrupters: %u\n",
|
||||
(unsigned int) HCS_MAX_INTRS(temp));
|
||||
xhci_dbg(xhci, " Max ports: %u\n",
|
||||
(unsigned int) HCS_MAX_PORTS(temp));
|
||||
|
||||
temp = readl(&xhci->cap_regs->hcs_params2);
|
||||
xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, " Isoc scheduling threshold: %u\n",
|
||||
(unsigned int) HCS_IST(temp));
|
||||
xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n",
|
||||
(unsigned int) HCS_ERST_MAX(temp));
|
||||
|
||||
temp = readl(&xhci->cap_regs->hcs_params3);
|
||||
xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n",
|
||||
(unsigned int) HCS_U1_LATENCY(temp));
|
||||
xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n",
|
||||
(unsigned int) HCS_U2_LATENCY(temp));
|
||||
|
||||
temp = readl(&xhci->cap_regs->hcc_params);
|
||||
xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp);
|
||||
xhci_dbg(xhci, " HC generates %s bit addresses\n",
|
||||
HCC_64BIT_ADDR(temp) ? "64" : "32");
|
||||
xhci_dbg(xhci, " HC %s Contiguous Frame ID Capability\n",
|
||||
HCC_CFC(temp) ? "has" : "hasn't");
|
||||
xhci_dbg(xhci, " HC %s generate Stopped - Short Package event\n",
|
||||
HCC_SPC(temp) ? "can" : "can't");
|
||||
/* FIXME */
|
||||
xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n");
|
||||
|
||||
temp = readl(&xhci->cap_regs->run_regs_off);
|
||||
xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK);
|
||||
|
||||
/* xhci 1.1 controllers have the HCCPARAMS2 register */
|
||||
if (hci_version > 0x100) {
|
||||
temp = readl(&xhci->cap_regs->hcc_params2);
|
||||
xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp);
|
||||
xhci_dbg(xhci, " HC %s Force save context capability",
|
||||
HCC2_FSC(temp) ? "supports" : "doesn't support");
|
||||
xhci_dbg(xhci, " HC %s Large ESIT Payload Capability",
|
||||
HCC2_LEC(temp) ? "supports" : "doesn't support");
|
||||
xhci_dbg(xhci, " HC %s Extended TBC capability",
|
||||
HCC2_ETC(temp) ? "supports" : "doesn't support");
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_print_command_reg(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = readl(&xhci->op_regs->command);
|
||||
xhci_dbg(xhci, "USBCMD 0x%x:\n", temp);
|
||||
xhci_dbg(xhci, " HC is %s\n",
|
||||
(temp & CMD_RUN) ? "running" : "being stopped");
|
||||
xhci_dbg(xhci, " HC has %sfinished hard reset\n",
|
||||
(temp & CMD_RESET) ? "not " : "");
|
||||
xhci_dbg(xhci, " Event Interrupts %s\n",
|
||||
(temp & CMD_EIE) ? "enabled " : "disabled");
|
||||
xhci_dbg(xhci, " Host System Error Interrupts %s\n",
|
||||
(temp & CMD_HSEIE) ? "enabled " : "disabled");
|
||||
xhci_dbg(xhci, " HC has %sfinished light reset\n",
|
||||
(temp & CMD_LRESET) ? "not " : "");
|
||||
}
|
||||
|
||||
static void xhci_print_status(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = readl(&xhci->op_regs->status);
|
||||
xhci_dbg(xhci, "USBSTS 0x%x:\n", temp);
|
||||
xhci_dbg(xhci, " Event ring is %sempty\n",
|
||||
(temp & STS_EINT) ? "not " : "");
|
||||
xhci_dbg(xhci, " %sHost System Error\n",
|
||||
(temp & STS_FATAL) ? "WARNING: " : "No ");
|
||||
xhci_dbg(xhci, " HC is %s\n",
|
||||
(temp & STS_HALT) ? "halted" : "running");
|
||||
}
|
||||
|
||||
static void xhci_print_op_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
xhci_dbg(xhci, "xHCI operational registers at %p:\n", xhci->op_regs);
|
||||
xhci_print_command_reg(xhci);
|
||||
xhci_print_status(xhci);
|
||||
}
|
||||
|
||||
static void xhci_print_ports(struct xhci_hcd *xhci)
|
||||
{
|
||||
__le32 __iomem *addr;
|
||||
int i, j;
|
||||
int ports;
|
||||
char *names[NUM_PORT_REGS] = {
|
||||
"status",
|
||||
"power",
|
||||
"link",
|
||||
"reserved",
|
||||
};
|
||||
|
||||
ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
addr = &xhci->op_regs->port_status_base;
|
||||
for (i = 0; i < ports; i++) {
|
||||
for (j = 0; j < NUM_PORT_REGS; j++) {
|
||||
xhci_dbg(xhci, "%p port %s reg = 0x%x\n",
|
||||
addr, names[j],
|
||||
(unsigned int) readl(addr));
|
||||
addr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num)
|
||||
{
|
||||
struct xhci_intr_reg __iomem *ir_set = &xhci->run_regs->ir_set[set_num];
|
||||
void __iomem *addr;
|
||||
u32 temp;
|
||||
u64 temp_64;
|
||||
|
||||
addr = &ir_set->irq_pending;
|
||||
temp = readl(addr);
|
||||
if (temp == XHCI_INIT_VALUE)
|
||||
return;
|
||||
|
||||
xhci_dbg(xhci, " %p: ir_set[%i]\n", ir_set, set_num);
|
||||
|
||||
xhci_dbg(xhci, " %p: ir_set.pending = 0x%x\n", addr,
|
||||
(unsigned int)temp);
|
||||
|
||||
addr = &ir_set->irq_control;
|
||||
temp = readl(addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.control = 0x%x\n", addr,
|
||||
(unsigned int)temp);
|
||||
|
||||
addr = &ir_set->erst_size;
|
||||
temp = readl(addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_size = 0x%x\n", addr,
|
||||
(unsigned int)temp);
|
||||
|
||||
addr = &ir_set->rsvd;
|
||||
temp = readl(addr);
|
||||
if (temp != XHCI_INIT_VALUE)
|
||||
xhci_dbg(xhci, " WARN: %p: ir_set.rsvd = 0x%x\n",
|
||||
addr, (unsigned int)temp);
|
||||
|
||||
addr = &ir_set->erst_base;
|
||||
temp_64 = xhci_read_64(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_base = @%08llx\n",
|
||||
addr, temp_64);
|
||||
|
||||
addr = &ir_set->erst_dequeue;
|
||||
temp_64 = xhci_read_64(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_dequeue = @%08llx\n",
|
||||
addr, temp_64);
|
||||
}
|
||||
|
||||
void xhci_print_run_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
int i;
|
||||
|
||||
xhci_dbg(xhci, "xHCI runtime registers at %p:\n", xhci->run_regs);
|
||||
temp = readl(&xhci->run_regs->microframe_index);
|
||||
xhci_dbg(xhci, " %p: Microframe index = 0x%x\n",
|
||||
&xhci->run_regs->microframe_index,
|
||||
(unsigned int) temp);
|
||||
for (i = 0; i < 7; i++) {
|
||||
temp = readl(&xhci->run_regs->rsvd[i]);
|
||||
if (temp != XHCI_INIT_VALUE)
|
||||
xhci_dbg(xhci, " WARN: %p: Rsvd[%i] = 0x%x\n",
|
||||
&xhci->run_regs->rsvd[i],
|
||||
i, (unsigned int) temp);
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_print_registers(struct xhci_hcd *xhci)
|
||||
{
|
||||
xhci_print_cap_regs(xhci);
|
||||
xhci_print_op_regs(xhci);
|
||||
xhci_print_ports(xhci);
|
||||
}
|
||||
|
||||
void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
|
||||
{
|
||||
u64 addr = erst->erst_dma_addr;
|
||||
int i;
|
||||
struct xhci_erst_entry *entry;
|
||||
|
||||
for (i = 0; i < erst->num_entries; i++) {
|
||||
entry = &erst->entries[i];
|
||||
xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n",
|
||||
addr,
|
||||
lower_32_bits(le64_to_cpu(entry->seg_addr)),
|
||||
upper_32_bits(le64_to_cpu(entry->seg_addr)),
|
||||
le32_to_cpu(entry->seg_size),
|
||||
le32_to_cpu(entry->rsvd));
|
||||
addr += sizeof(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
|
||||
xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = @%08x\n",
|
||||
lower_32_bits(val));
|
||||
xhci_dbg(xhci, "// xHC command ring deq ptr high bits = @%08x\n",
|
||||
upper_32_bits(val));
|
||||
}
|
||||
|
||||
char *xhci_get_slot_state(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
|
|
996
drivers/usb/host/xhci-dbgcap.c
Normal file
996
drivers/usb/host/xhci-dbgcap.c
Normal file
|
@ -0,0 +1,996 @@
|
|||
/**
|
||||
* xhci-dbgcap.c - xHCI debug capability support
|
||||
*
|
||||
* Copyright (C) 2017 Intel Corporation
|
||||
*
|
||||
* Author: Lu Baolu <baolu.lu@linux.intel.com>
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include "xhci.h"
|
||||
#include "xhci-trace.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
|
||||
static inline void *
|
||||
dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags)
|
||||
{
|
||||
void *vaddr;
|
||||
|
||||
vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
|
||||
size, dma_handle, flags);
|
||||
memset(vaddr, 0, size);
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size,
|
||||
void *cpu_addr, dma_addr_t dma_handle)
|
||||
{
|
||||
if (cpu_addr)
|
||||
dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev,
|
||||
size, cpu_addr, dma_handle);
|
||||
}
|
||||
|
||||
static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
|
||||
{
|
||||
struct usb_string_descriptor *s_desc;
|
||||
u32 string_length;
|
||||
|
||||
/* Serial string: */
|
||||
s_desc = (struct usb_string_descriptor *)strings->serial;
|
||||
utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
|
||||
DBC_MAX_STRING_LENGTH);
|
||||
|
||||
s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
string_length = s_desc->bLength;
|
||||
string_length <<= 8;
|
||||
|
||||
/* Product string: */
|
||||
s_desc = (struct usb_string_descriptor *)strings->product;
|
||||
utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
|
||||
DBC_MAX_STRING_LENGTH);
|
||||
|
||||
s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
string_length += s_desc->bLength;
|
||||
string_length <<= 8;
|
||||
|
||||
/* Manufacture string: */
|
||||
s_desc = (struct usb_string_descriptor *)strings->manufacturer;
|
||||
utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
|
||||
strlen(DBC_STRING_MANUFACTURER),
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
|
||||
DBC_MAX_STRING_LENGTH);
|
||||
|
||||
s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
string_length += s_desc->bLength;
|
||||
string_length <<= 8;
|
||||
|
||||
/* String0: */
|
||||
strings->string0[0] = 4;
|
||||
strings->string0[1] = USB_DT_STRING;
|
||||
strings->string0[2] = 0x09;
|
||||
strings->string0[3] = 0x04;
|
||||
string_length += 4;
|
||||
|
||||
return string_length;
|
||||
}
|
||||
|
||||
static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct dbc_info_context *info;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
u32 dev_info;
|
||||
dma_addr_t deq, dma;
|
||||
unsigned int max_burst;
|
||||
|
||||
dbc = xhci->dbc;
|
||||
if (!dbc)
|
||||
return;
|
||||
|
||||
/* Populate info Context: */
|
||||
info = (struct dbc_info_context *)dbc->ctx->bytes;
|
||||
dma = dbc->string_dma;
|
||||
info->string0 = cpu_to_le64(dma);
|
||||
info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH);
|
||||
info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2);
|
||||
info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3);
|
||||
info->length = cpu_to_le32(string_length);
|
||||
|
||||
/* Populate bulk out endpoint context: */
|
||||
ep_ctx = dbc_bulkout_ctx(dbc);
|
||||
max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control));
|
||||
deq = dbc_bulkout_enq(dbc);
|
||||
ep_ctx->ep_info = 0;
|
||||
ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst);
|
||||
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state);
|
||||
|
||||
/* Populate bulk in endpoint context: */
|
||||
ep_ctx = dbc_bulkin_ctx(dbc);
|
||||
deq = dbc_bulkin_enq(dbc);
|
||||
ep_ctx->ep_info = 0;
|
||||
ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst);
|
||||
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
|
||||
|
||||
/* Set DbC context and info registers: */
|
||||
xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp);
|
||||
|
||||
dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL);
|
||||
writel(dev_info, &dbc->regs->devinfo1);
|
||||
|
||||
dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID);
|
||||
writel(dev_info, &dbc->regs->devinfo2);
|
||||
}
|
||||
|
||||
static void xhci_dbc_giveback(struct dbc_request *req, int status)
|
||||
__releases(&dbc->lock)
|
||||
__acquires(&dbc->lock)
|
||||
{
|
||||
struct dbc_ep *dep = req->dep;
|
||||
struct xhci_dbc *dbc = dep->dbc;
|
||||
struct xhci_hcd *xhci = dbc->xhci;
|
||||
struct device *dev = xhci_to_hcd(dbc->xhci)->self.sysdev;
|
||||
|
||||
list_del_init(&req->list_pending);
|
||||
req->trb_dma = 0;
|
||||
req->trb = NULL;
|
||||
|
||||
if (req->status == -EINPROGRESS)
|
||||
req->status = status;
|
||||
|
||||
trace_xhci_dbc_giveback_request(req);
|
||||
|
||||
dma_unmap_single(dev,
|
||||
req->dma,
|
||||
req->length,
|
||||
dbc_ep_dma_direction(dep));
|
||||
|
||||
/* Give back the transfer request: */
|
||||
spin_unlock(&dbc->lock);
|
||||
req->complete(xhci, req);
|
||||
spin_lock(&dbc->lock);
|
||||
}
|
||||
|
||||
static void xhci_dbc_flush_single_request(struct dbc_request *req)
|
||||
{
|
||||
union xhci_trb *trb = req->trb;
|
||||
|
||||
trb->generic.field[0] = 0;
|
||||
trb->generic.field[1] = 0;
|
||||
trb->generic.field[2] = 0;
|
||||
trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
|
||||
trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP));
|
||||
|
||||
xhci_dbc_giveback(req, -ESHUTDOWN);
|
||||
}
|
||||
|
||||
static void xhci_dbc_flush_endpoint_requests(struct dbc_ep *dep)
|
||||
{
|
||||
struct dbc_request *req, *tmp;
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &dep->list_pending, list_pending)
|
||||
xhci_dbc_flush_single_request(req);
|
||||
}
|
||||
|
||||
static void xhci_dbc_flush_reqests(struct xhci_dbc *dbc)
|
||||
{
|
||||
xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT]);
|
||||
xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN]);
|
||||
}
|
||||
|
||||
struct dbc_request *
|
||||
dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags)
|
||||
{
|
||||
struct dbc_request *req;
|
||||
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
req->dep = dep;
|
||||
INIT_LIST_HEAD(&req->list_pending);
|
||||
INIT_LIST_HEAD(&req->list_pool);
|
||||
req->direction = dep->direction;
|
||||
|
||||
trace_xhci_dbc_alloc_request(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void
|
||||
dbc_free_request(struct dbc_ep *dep, struct dbc_request *req)
|
||||
{
|
||||
trace_xhci_dbc_free_request(req);
|
||||
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1,
|
||||
u32 field2, u32 field3, u32 field4)
|
||||
{
|
||||
union xhci_trb *trb, *next;
|
||||
|
||||
trb = ring->enqueue;
|
||||
trb->generic.field[0] = cpu_to_le32(field1);
|
||||
trb->generic.field[1] = cpu_to_le32(field2);
|
||||
trb->generic.field[2] = cpu_to_le32(field3);
|
||||
trb->generic.field[3] = cpu_to_le32(field4);
|
||||
|
||||
trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic);
|
||||
|
||||
ring->num_trbs_free--;
|
||||
next = ++(ring->enqueue);
|
||||
if (TRB_TYPE_LINK_LE32(next->link.control)) {
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
ring->cycle_state ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep,
|
||||
struct dbc_request *req)
|
||||
{
|
||||
u64 addr;
|
||||
union xhci_trb *trb;
|
||||
unsigned int num_trbs;
|
||||
struct xhci_dbc *dbc = dep->dbc;
|
||||
struct xhci_ring *ring = dep->ring;
|
||||
u32 length, control, cycle;
|
||||
|
||||
num_trbs = count_trbs(req->dma, req->length);
|
||||
WARN_ON(num_trbs != 1);
|
||||
if (ring->num_trbs_free < num_trbs)
|
||||
return -EBUSY;
|
||||
|
||||
addr = req->dma;
|
||||
trb = ring->enqueue;
|
||||
cycle = ring->cycle_state;
|
||||
length = TRB_LEN(req->length);
|
||||
control = TRB_TYPE(TRB_NORMAL) | TRB_IOC;
|
||||
|
||||
if (cycle)
|
||||
control &= cpu_to_le32(~TRB_CYCLE);
|
||||
else
|
||||
control |= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
req->trb = ring->enqueue;
|
||||
req->trb_dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
|
||||
xhci_dbc_queue_trb(ring,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
length, control);
|
||||
|
||||
/*
|
||||
* Add a barrier between writes of trb fields and flipping
|
||||
* the cycle bit:
|
||||
*/
|
||||
wmb();
|
||||
|
||||
if (cycle)
|
||||
trb->generic.field[3] |= cpu_to_le32(TRB_CYCLE);
|
||||
else
|
||||
trb->generic.field[3] &= cpu_to_le32(~TRB_CYCLE);
|
||||
|
||||
writel(DBC_DOOR_BELL_TARGET(dep->direction), &dbc->regs->doorbell);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dbc_ep_do_queue(struct dbc_ep *dep, struct dbc_request *req)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev;
|
||||
struct xhci_dbc *dbc = dep->dbc;
|
||||
struct xhci_hcd *xhci = dbc->xhci;
|
||||
|
||||
dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
|
||||
if (!req->length || !req->buf)
|
||||
return -EINVAL;
|
||||
|
||||
req->actual = 0;
|
||||
req->status = -EINPROGRESS;
|
||||
|
||||
req->dma = dma_map_single(dev,
|
||||
req->buf,
|
||||
req->length,
|
||||
dbc_ep_dma_direction(dep));
|
||||
if (dma_mapping_error(dev, req->dma)) {
|
||||
xhci_err(xhci, "failed to map buffer\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ret = xhci_dbc_queue_bulk_tx(dep, req);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "failed to queue trbs\n");
|
||||
dma_unmap_single(dev,
|
||||
req->dma,
|
||||
req->length,
|
||||
dbc_ep_dma_direction(dep));
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
list_add_tail(&req->list_pending, &dep->list_pending);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
struct xhci_dbc *dbc = dep->dbc;
|
||||
int ret = -ESHUTDOWN;
|
||||
|
||||
spin_lock(&dbc->lock);
|
||||
if (dbc->state == DS_CONFIGURED)
|
||||
ret = dbc_ep_do_queue(dep, req);
|
||||
spin_unlock(&dbc->lock);
|
||||
|
||||
mod_delayed_work(system_wq, &dbc->event_work, 0);
|
||||
|
||||
trace_xhci_dbc_queue_request(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void xhci_dbc_do_eps_init(struct xhci_hcd *xhci, bool direction)
|
||||
{
|
||||
struct dbc_ep *dep;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
dep = &dbc->eps[direction];
|
||||
dep->dbc = dbc;
|
||||
dep->direction = direction;
|
||||
dep->ring = direction ? dbc->ring_in : dbc->ring_out;
|
||||
|
||||
INIT_LIST_HEAD(&dep->list_pending);
|
||||
}
|
||||
|
||||
static void xhci_dbc_eps_init(struct xhci_hcd *xhci)
|
||||
{
|
||||
xhci_dbc_do_eps_init(xhci, BULK_OUT);
|
||||
xhci_dbc_do_eps_init(xhci, BULK_IN);
|
||||
}
|
||||
|
||||
static void xhci_dbc_eps_exit(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps));
|
||||
}
|
||||
|
||||
static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
int ret;
|
||||
dma_addr_t deq;
|
||||
u32 string_length;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
/* Allocate various rings for events and transfers: */
|
||||
dbc->ring_evt = xhci_ring_alloc(xhci, 1, 1, TYPE_EVENT, 0, flags);
|
||||
if (!dbc->ring_evt)
|
||||
goto evt_fail;
|
||||
|
||||
dbc->ring_in = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags);
|
||||
if (!dbc->ring_in)
|
||||
goto in_fail;
|
||||
|
||||
dbc->ring_out = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags);
|
||||
if (!dbc->ring_out)
|
||||
goto out_fail;
|
||||
|
||||
/* Allocate and populate ERST: */
|
||||
ret = xhci_alloc_erst(xhci, dbc->ring_evt, &dbc->erst, flags);
|
||||
if (ret)
|
||||
goto erst_fail;
|
||||
|
||||
/* Allocate context data structure: */
|
||||
dbc->ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags);
|
||||
if (!dbc->ctx)
|
||||
goto ctx_fail;
|
||||
|
||||
/* Allocate the string table: */
|
||||
dbc->string_size = sizeof(struct dbc_str_descs);
|
||||
dbc->string = dbc_dma_alloc_coherent(xhci,
|
||||
dbc->string_size,
|
||||
&dbc->string_dma,
|
||||
flags);
|
||||
if (!dbc->string)
|
||||
goto string_fail;
|
||||
|
||||
/* Setup ERST register: */
|
||||
writel(dbc->erst.erst_size, &dbc->regs->ersts);
|
||||
xhci_write_64(xhci, dbc->erst.erst_dma_addr, &dbc->regs->erstba);
|
||||
deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
|
||||
dbc->ring_evt->dequeue);
|
||||
xhci_write_64(xhci, deq, &dbc->regs->erdp);
|
||||
|
||||
/* Setup strings and contexts: */
|
||||
string_length = xhci_dbc_populate_strings(dbc->string);
|
||||
xhci_dbc_init_contexts(xhci, string_length);
|
||||
|
||||
mmiowb();
|
||||
|
||||
xhci_dbc_eps_init(xhci);
|
||||
dbc->state = DS_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
|
||||
string_fail:
|
||||
xhci_free_container_ctx(xhci, dbc->ctx);
|
||||
dbc->ctx = NULL;
|
||||
ctx_fail:
|
||||
xhci_free_erst(xhci, &dbc->erst);
|
||||
erst_fail:
|
||||
xhci_ring_free(xhci, dbc->ring_out);
|
||||
dbc->ring_out = NULL;
|
||||
out_fail:
|
||||
xhci_ring_free(xhci, dbc->ring_in);
|
||||
dbc->ring_in = NULL;
|
||||
in_fail:
|
||||
xhci_ring_free(xhci, dbc->ring_evt);
|
||||
dbc->ring_evt = NULL;
|
||||
evt_fail:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void xhci_dbc_mem_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
if (!dbc)
|
||||
return;
|
||||
|
||||
xhci_dbc_eps_exit(xhci);
|
||||
|
||||
if (dbc->string) {
|
||||
dbc_dma_free_coherent(xhci,
|
||||
dbc->string_size,
|
||||
dbc->string, dbc->string_dma);
|
||||
dbc->string = NULL;
|
||||
}
|
||||
|
||||
xhci_free_container_ctx(xhci, dbc->ctx);
|
||||
dbc->ctx = NULL;
|
||||
|
||||
xhci_free_erst(xhci, &dbc->erst);
|
||||
xhci_ring_free(xhci, dbc->ring_out);
|
||||
xhci_ring_free(xhci, dbc->ring_in);
|
||||
xhci_ring_free(xhci, dbc->ring_evt);
|
||||
dbc->ring_in = NULL;
|
||||
dbc->ring_out = NULL;
|
||||
dbc->ring_evt = NULL;
|
||||
}
|
||||
|
||||
static int xhci_do_dbc_start(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret;
|
||||
u32 ctrl;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EINVAL;
|
||||
|
||||
writel(0, &dbc->regs->control);
|
||||
ret = xhci_handshake(&dbc->regs->control,
|
||||
DBC_CTRL_DBC_ENABLE,
|
||||
0, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xhci_dbc_mem_init(xhci, GFP_ATOMIC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE,
|
||||
&dbc->regs->control);
|
||||
ret = xhci_handshake(&dbc->regs->control,
|
||||
DBC_CTRL_DBC_ENABLE,
|
||||
DBC_CTRL_DBC_ENABLE, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dbc->state = DS_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xhci_do_dbc_stop(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
if (dbc->state == DS_DISABLED)
|
||||
return;
|
||||
|
||||
writel(0, &dbc->regs->control);
|
||||
xhci_dbc_mem_cleanup(xhci);
|
||||
dbc->state = DS_DISABLED;
|
||||
}
|
||||
|
||||
static int xhci_dbc_start(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
WARN_ON(!dbc);
|
||||
|
||||
pm_runtime_get_sync(xhci_to_hcd(xhci)->self.controller);
|
||||
|
||||
spin_lock(&dbc->lock);
|
||||
ret = xhci_do_dbc_start(xhci);
|
||||
spin_unlock(&dbc->lock);
|
||||
|
||||
if (ret) {
|
||||
pm_runtime_put(xhci_to_hcd(xhci)->self.controller);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return mod_delayed_work(system_wq, &dbc->event_work, 1);
|
||||
}
|
||||
|
||||
static void xhci_dbc_stop(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
struct dbc_port *port = &dbc->port;
|
||||
|
||||
WARN_ON(!dbc);
|
||||
|
||||
cancel_delayed_work_sync(&dbc->event_work);
|
||||
|
||||
if (port->registered)
|
||||
xhci_dbc_tty_unregister_device(xhci);
|
||||
|
||||
spin_lock(&dbc->lock);
|
||||
xhci_do_dbc_stop(xhci);
|
||||
spin_unlock(&dbc->lock);
|
||||
|
||||
pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller);
|
||||
}
|
||||
|
||||
static void
|
||||
dbc_handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
|
||||
{
|
||||
u32 portsc;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
portsc = readl(&dbc->regs->portsc);
|
||||
if (portsc & DBC_PORTSC_CONN_CHANGE)
|
||||
xhci_info(xhci, "DbC port connect change\n");
|
||||
|
||||
if (portsc & DBC_PORTSC_RESET_CHANGE)
|
||||
xhci_info(xhci, "DbC port reset change\n");
|
||||
|
||||
if (portsc & DBC_PORTSC_LINK_CHANGE)
|
||||
xhci_info(xhci, "DbC port link status change\n");
|
||||
|
||||
if (portsc & DBC_PORTSC_CONFIG_CHANGE)
|
||||
xhci_info(xhci, "DbC config error change\n");
|
||||
|
||||
/* Port reset change bit will be cleared in other place: */
|
||||
writel(portsc & ~DBC_PORTSC_RESET_CHANGE, &dbc->regs->portsc);
|
||||
}
|
||||
|
||||
static void dbc_handle_xfer_event(struct xhci_hcd *xhci, union xhci_trb *event)
|
||||
{
|
||||
struct dbc_ep *dep;
|
||||
struct xhci_ring *ring;
|
||||
int ep_id;
|
||||
int status;
|
||||
u32 comp_code;
|
||||
size_t remain_length;
|
||||
struct dbc_request *req = NULL, *r;
|
||||
|
||||
comp_code = GET_COMP_CODE(le32_to_cpu(event->generic.field[2]));
|
||||
remain_length = EVENT_TRB_LEN(le32_to_cpu(event->generic.field[2]));
|
||||
ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3]));
|
||||
dep = (ep_id == EPID_OUT) ?
|
||||
get_out_ep(xhci) : get_in_ep(xhci);
|
||||
ring = dep->ring;
|
||||
|
||||
switch (comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
remain_length = 0;
|
||||
/* FALLTHROUGH */
|
||||
case COMP_SHORT_PACKET:
|
||||
status = 0;
|
||||
break;
|
||||
case COMP_TRB_ERROR:
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
case COMP_STALL_ERROR:
|
||||
xhci_warn(xhci, "tx error %d detected\n", comp_code);
|
||||
status = -comp_code;
|
||||
break;
|
||||
default:
|
||||
xhci_err(xhci, "unknown tx error %d\n", comp_code);
|
||||
status = -comp_code;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Match the pending request: */
|
||||
list_for_each_entry(r, &dep->list_pending, list_pending) {
|
||||
if (r->trb_dma == event->trans_event.buffer) {
|
||||
req = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!req) {
|
||||
xhci_warn(xhci, "no matched request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
|
||||
|
||||
ring->num_trbs_free++;
|
||||
req->actual = req->length - remain_length;
|
||||
xhci_dbc_giveback(req, status);
|
||||
}
|
||||
|
||||
static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
|
||||
{
|
||||
dma_addr_t deq;
|
||||
struct dbc_ep *dep;
|
||||
union xhci_trb *evt;
|
||||
u32 ctrl, portsc;
|
||||
struct xhci_hcd *xhci = dbc->xhci;
|
||||
bool update_erdp = false;
|
||||
|
||||
/* DbC state machine: */
|
||||
switch (dbc->state) {
|
||||
case DS_DISABLED:
|
||||
case DS_INITIALIZED:
|
||||
|
||||
return EVT_ERR;
|
||||
case DS_ENABLED:
|
||||
portsc = readl(&dbc->regs->portsc);
|
||||
if (portsc & DBC_PORTSC_CONN_STATUS) {
|
||||
dbc->state = DS_CONNECTED;
|
||||
xhci_info(xhci, "DbC connected\n");
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
case DS_CONNECTED:
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
if (ctrl & DBC_CTRL_DBC_RUN) {
|
||||
dbc->state = DS_CONFIGURED;
|
||||
xhci_info(xhci, "DbC configured\n");
|
||||
portsc = readl(&dbc->regs->portsc);
|
||||
writel(portsc, &dbc->regs->portsc);
|
||||
return EVT_GSER;
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
case DS_CONFIGURED:
|
||||
/* Handle cable unplug event: */
|
||||
portsc = readl(&dbc->regs->portsc);
|
||||
if (!(portsc & DBC_PORTSC_PORT_ENABLED) &&
|
||||
!(portsc & DBC_PORTSC_CONN_STATUS)) {
|
||||
xhci_info(xhci, "DbC cable unplugged\n");
|
||||
dbc->state = DS_ENABLED;
|
||||
xhci_dbc_flush_reqests(dbc);
|
||||
|
||||
return EVT_DISC;
|
||||
}
|
||||
|
||||
/* Handle debug port reset event: */
|
||||
if (portsc & DBC_PORTSC_RESET_CHANGE) {
|
||||
xhci_info(xhci, "DbC port reset\n");
|
||||
writel(portsc, &dbc->regs->portsc);
|
||||
dbc->state = DS_ENABLED;
|
||||
xhci_dbc_flush_reqests(dbc);
|
||||
|
||||
return EVT_DISC;
|
||||
}
|
||||
|
||||
/* Handle endpoint stall event: */
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
if ((ctrl & DBC_CTRL_HALT_IN_TR) ||
|
||||
(ctrl & DBC_CTRL_HALT_OUT_TR)) {
|
||||
xhci_info(xhci, "DbC Endpoint stall\n");
|
||||
dbc->state = DS_STALLED;
|
||||
|
||||
if (ctrl & DBC_CTRL_HALT_IN_TR) {
|
||||
dep = get_in_ep(xhci);
|
||||
xhci_dbc_flush_endpoint_requests(dep);
|
||||
}
|
||||
|
||||
if (ctrl & DBC_CTRL_HALT_OUT_TR) {
|
||||
dep = get_out_ep(xhci);
|
||||
xhci_dbc_flush_endpoint_requests(dep);
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
}
|
||||
|
||||
/* Clear DbC run change bit: */
|
||||
if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) {
|
||||
writel(ctrl, &dbc->regs->control);
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
}
|
||||
|
||||
break;
|
||||
case DS_STALLED:
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
if (!(ctrl & DBC_CTRL_HALT_IN_TR) &&
|
||||
!(ctrl & DBC_CTRL_HALT_OUT_TR) &&
|
||||
(ctrl & DBC_CTRL_DBC_RUN)) {
|
||||
dbc->state = DS_CONFIGURED;
|
||||
break;
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
default:
|
||||
xhci_err(xhci, "Unknown DbC state %d\n", dbc->state);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle the events in the event ring: */
|
||||
evt = dbc->ring_evt->dequeue;
|
||||
while ((le32_to_cpu(evt->event_cmd.flags) & TRB_CYCLE) ==
|
||||
dbc->ring_evt->cycle_state) {
|
||||
/*
|
||||
* Add a barrier between reading the cycle flag and any
|
||||
* reads of the event's flags/data below:
|
||||
*/
|
||||
rmb();
|
||||
|
||||
trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic);
|
||||
|
||||
switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) {
|
||||
case TRB_TYPE(TRB_PORT_STATUS):
|
||||
dbc_handle_port_status(xhci, evt);
|
||||
break;
|
||||
case TRB_TYPE(TRB_TRANSFER):
|
||||
dbc_handle_xfer_event(xhci, evt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
inc_deq(xhci, dbc->ring_evt);
|
||||
evt = dbc->ring_evt->dequeue;
|
||||
update_erdp = true;
|
||||
}
|
||||
|
||||
/* Update event ring dequeue pointer: */
|
||||
if (update_erdp) {
|
||||
deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
|
||||
dbc->ring_evt->dequeue);
|
||||
xhci_write_64(xhci, deq, &dbc->regs->erdp);
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
}
|
||||
|
||||
static void xhci_dbc_handle_events(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
enum evtreturn evtr;
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
|
||||
xhci = dbc->xhci;
|
||||
|
||||
spin_lock(&dbc->lock);
|
||||
evtr = xhci_dbc_do_handle_events(dbc);
|
||||
spin_unlock(&dbc->lock);
|
||||
|
||||
switch (evtr) {
|
||||
case EVT_GSER:
|
||||
ret = xhci_dbc_tty_register_device(xhci);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "failed to alloc tty device\n");
|
||||
break;
|
||||
}
|
||||
|
||||
xhci_info(xhci, "DbC now attached to /dev/ttyDBC0\n");
|
||||
break;
|
||||
case EVT_DISC:
|
||||
xhci_dbc_tty_unregister_device(xhci);
|
||||
break;
|
||||
case EVT_DONE:
|
||||
break;
|
||||
default:
|
||||
xhci_info(xhci, "stop handling dbc events\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mod_delayed_work(system_wq, &dbc->event_work, 1);
|
||||
}
|
||||
|
||||
static void xhci_do_dbc_exit(struct xhci_hcd *xhci)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
kfree(xhci->dbc);
|
||||
xhci->dbc = NULL;
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
}
|
||||
|
||||
static int xhci_do_dbc_init(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 reg;
|
||||
struct xhci_dbc *dbc;
|
||||
unsigned long flags;
|
||||
void __iomem *base;
|
||||
int dbc_cap_offs;
|
||||
|
||||
base = &xhci->cap_regs->hc_capbase;
|
||||
dbc_cap_offs = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_DEBUG);
|
||||
if (!dbc_cap_offs)
|
||||
return -ENODEV;
|
||||
|
||||
dbc = kzalloc(sizeof(*dbc), GFP_KERNEL);
|
||||
if (!dbc)
|
||||
return -ENOMEM;
|
||||
|
||||
dbc->regs = base + dbc_cap_offs;
|
||||
|
||||
/* We will avoid using DbC in xhci driver if it's in use. */
|
||||
reg = readl(&dbc->regs->control);
|
||||
if (reg & DBC_CTRL_DBC_ENABLE) {
|
||||
kfree(dbc);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
if (xhci->dbc) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
kfree(dbc);
|
||||
return -EBUSY;
|
||||
}
|
||||
xhci->dbc = dbc;
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
dbc->xhci = xhci;
|
||||
INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
|
||||
spin_lock_init(&dbc->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dbc_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const char *p;
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
|
||||
switch (dbc->state) {
|
||||
case DS_DISABLED:
|
||||
p = "disabled";
|
||||
break;
|
||||
case DS_INITIALIZED:
|
||||
p = "initialized";
|
||||
break;
|
||||
case DS_ENABLED:
|
||||
p = "enabled";
|
||||
break;
|
||||
case DS_CONNECTED:
|
||||
p = "connected";
|
||||
break;
|
||||
case DS_CONFIGURED:
|
||||
p = "configured";
|
||||
break;
|
||||
case DS_STALLED:
|
||||
p = "stalled";
|
||||
break;
|
||||
default:
|
||||
p = "unknown";
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", p);
|
||||
}
|
||||
|
||||
static ssize_t dbc_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
|
||||
if (!strncmp(buf, "enable", 6))
|
||||
xhci_dbc_start(xhci);
|
||||
else if (!strncmp(buf, "disable", 7))
|
||||
xhci_dbc_stop(xhci);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(dbc);
|
||||
|
||||
int xhci_dbc_init(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
|
||||
ret = xhci_do_dbc_init(xhci);
|
||||
if (ret)
|
||||
goto init_err3;
|
||||
|
||||
ret = xhci_dbc_tty_register_driver(xhci);
|
||||
if (ret)
|
||||
goto init_err2;
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_dbc);
|
||||
if (ret)
|
||||
goto init_err1;
|
||||
|
||||
return 0;
|
||||
|
||||
init_err1:
|
||||
xhci_dbc_tty_unregister_driver();
|
||||
init_err2:
|
||||
xhci_do_dbc_exit(xhci);
|
||||
init_err3:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xhci_dbc_exit(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
|
||||
if (!xhci->dbc)
|
||||
return;
|
||||
|
||||
device_remove_file(dev, &dev_attr_dbc);
|
||||
xhci_dbc_tty_unregister_driver();
|
||||
xhci_dbc_stop(xhci);
|
||||
xhci_do_dbc_exit(xhci);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int xhci_dbc_suspend(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
if (!dbc)
|
||||
return 0;
|
||||
|
||||
if (dbc->state == DS_CONFIGURED)
|
||||
dbc->resume_required = 1;
|
||||
|
||||
xhci_dbc_stop(xhci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_dbc_resume(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret = 0;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
if (!dbc)
|
||||
return 0;
|
||||
|
||||
if (dbc->resume_required) {
|
||||
dbc->resume_required = 0;
|
||||
xhci_dbc_start(xhci);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
229
drivers/usb/host/xhci-dbgcap.h
Normal file
229
drivers/usb/host/xhci-dbgcap.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
|
||||
/**
|
||||
* xhci-dbgcap.h - xHCI debug capability support
|
||||
*
|
||||
* Copyright (C) 2017 Intel Corporation
|
||||
*
|
||||
* Author: Lu Baolu <baolu.lu@linux.intel.com>
|
||||
*/
|
||||
#ifndef __LINUX_XHCI_DBGCAP_H
|
||||
#define __LINUX_XHCI_DBGCAP_H
|
||||
|
||||
#include <linux/tty.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
struct dbc_regs {
|
||||
__le32 capability;
|
||||
__le32 doorbell;
|
||||
__le32 ersts; /* Event Ring Segment Table Size*/
|
||||
__le32 __reserved_0; /* 0c~0f reserved bits */
|
||||
__le64 erstba; /* Event Ring Segment Table Base Address */
|
||||
__le64 erdp; /* Event Ring Dequeue Pointer */
|
||||
__le32 control;
|
||||
__le32 status;
|
||||
__le32 portsc; /* Port status and control */
|
||||
__le32 __reserved_1; /* 2b~28 reserved bits */
|
||||
__le64 dccp; /* Debug Capability Context Pointer */
|
||||
__le32 devinfo1; /* Device Descriptor Info Register 1 */
|
||||
__le32 devinfo2; /* Device Descriptor Info Register 2 */
|
||||
};
|
||||
|
||||
struct dbc_info_context {
|
||||
__le64 string0;
|
||||
__le64 manufacturer;
|
||||
__le64 product;
|
||||
__le64 serial;
|
||||
__le32 length;
|
||||
__le32 __reserved_0[7];
|
||||
};
|
||||
|
||||
#define DBC_CTRL_DBC_RUN BIT(0)
|
||||
#define DBC_CTRL_PORT_ENABLE BIT(1)
|
||||
#define DBC_CTRL_HALT_OUT_TR BIT(2)
|
||||
#define DBC_CTRL_HALT_IN_TR BIT(3)
|
||||
#define DBC_CTRL_DBC_RUN_CHANGE BIT(4)
|
||||
#define DBC_CTRL_DBC_ENABLE BIT(31)
|
||||
#define DBC_CTRL_MAXBURST(p) (((p) >> 16) & 0xff)
|
||||
#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8)
|
||||
|
||||
#define DBC_MAX_PACKET 1024
|
||||
#define DBC_MAX_STRING_LENGTH 64
|
||||
#define DBC_STRING_MANUFACTURER "Linux Foundation"
|
||||
#define DBC_STRING_PRODUCT "Linux USB Debug Target"
|
||||
#define DBC_STRING_SERIAL "0001"
|
||||
#define DBC_CONTEXT_SIZE 64
|
||||
|
||||
/*
|
||||
* Port status:
|
||||
*/
|
||||
#define DBC_PORTSC_CONN_STATUS BIT(0)
|
||||
#define DBC_PORTSC_PORT_ENABLED BIT(1)
|
||||
#define DBC_PORTSC_CONN_CHANGE BIT(17)
|
||||
#define DBC_PORTSC_RESET_CHANGE BIT(21)
|
||||
#define DBC_PORTSC_LINK_CHANGE BIT(22)
|
||||
#define DBC_PORTSC_CONFIG_CHANGE BIT(23)
|
||||
|
||||
struct dbc_str_descs {
|
||||
char string0[DBC_MAX_STRING_LENGTH];
|
||||
char manufacturer[DBC_MAX_STRING_LENGTH];
|
||||
char product[DBC_MAX_STRING_LENGTH];
|
||||
char serial[DBC_MAX_STRING_LENGTH];
|
||||
};
|
||||
|
||||
#define DBC_PROTOCOL 1 /* GNU Remote Debug Command */
|
||||
#define DBC_VENDOR_ID 0x1d6b /* Linux Foundation 0x1d6b */
|
||||
#define DBC_PRODUCT_ID 0x0010 /* device 0010 */
|
||||
#define DBC_DEVICE_REV 0x0010 /* 0.10 */
|
||||
|
||||
enum dbc_state {
|
||||
DS_DISABLED = 0,
|
||||
DS_INITIALIZED,
|
||||
DS_ENABLED,
|
||||
DS_CONNECTED,
|
||||
DS_CONFIGURED,
|
||||
DS_STALLED,
|
||||
};
|
||||
|
||||
struct dbc_request {
|
||||
void *buf;
|
||||
unsigned int length;
|
||||
dma_addr_t dma;
|
||||
void (*complete)(struct xhci_hcd *xhci,
|
||||
struct dbc_request *req);
|
||||
struct list_head list_pool;
|
||||
int status;
|
||||
unsigned int actual;
|
||||
|
||||
struct dbc_ep *dep;
|
||||
struct list_head list_pending;
|
||||
dma_addr_t trb_dma;
|
||||
union xhci_trb *trb;
|
||||
unsigned direction:1;
|
||||
};
|
||||
|
||||
struct dbc_ep {
|
||||
struct xhci_dbc *dbc;
|
||||
struct list_head list_pending;
|
||||
struct xhci_ring *ring;
|
||||
unsigned direction:1;
|
||||
};
|
||||
|
||||
#define DBC_QUEUE_SIZE 16
|
||||
#define DBC_WRITE_BUF_SIZE 8192
|
||||
|
||||
/*
|
||||
* Private structure for DbC hardware state:
|
||||
*/
|
||||
struct dbc_port {
|
||||
struct tty_port port;
|
||||
spinlock_t port_lock; /* port access */
|
||||
|
||||
struct list_head read_pool;
|
||||
struct list_head read_queue;
|
||||
unsigned int n_read;
|
||||
struct tasklet_struct push;
|
||||
|
||||
struct list_head write_pool;
|
||||
struct kfifo write_fifo;
|
||||
|
||||
bool registered;
|
||||
struct dbc_ep *in;
|
||||
struct dbc_ep *out;
|
||||
};
|
||||
|
||||
struct xhci_dbc {
|
||||
spinlock_t lock; /* device access */
|
||||
struct xhci_hcd *xhci;
|
||||
struct dbc_regs __iomem *regs;
|
||||
struct xhci_ring *ring_evt;
|
||||
struct xhci_ring *ring_in;
|
||||
struct xhci_ring *ring_out;
|
||||
struct xhci_erst erst;
|
||||
struct xhci_container_ctx *ctx;
|
||||
|
||||
struct dbc_str_descs *string;
|
||||
dma_addr_t string_dma;
|
||||
size_t string_size;
|
||||
|
||||
enum dbc_state state;
|
||||
struct delayed_work event_work;
|
||||
unsigned resume_required:1;
|
||||
struct dbc_ep eps[2];
|
||||
|
||||
struct dbc_port port;
|
||||
};
|
||||
|
||||
#define dbc_bulkout_ctx(d) \
|
||||
((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE))
|
||||
#define dbc_bulkin_ctx(d) \
|
||||
((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE * 2))
|
||||
#define dbc_bulkout_enq(d) \
|
||||
xhci_trb_virt_to_dma((d)->ring_out->enq_seg, (d)->ring_out->enqueue)
|
||||
#define dbc_bulkin_enq(d) \
|
||||
xhci_trb_virt_to_dma((d)->ring_in->enq_seg, (d)->ring_in->enqueue)
|
||||
#define dbc_epctx_info2(t, p, b) \
|
||||
cpu_to_le32(EP_TYPE(t) | MAX_PACKET(p) | MAX_BURST(b))
|
||||
#define dbc_ep_dma_direction(d) \
|
||||
((d)->direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE)
|
||||
|
||||
#define BULK_OUT 0
|
||||
#define BULK_IN 1
|
||||
#define EPID_OUT 2
|
||||
#define EPID_IN 3
|
||||
|
||||
enum evtreturn {
|
||||
EVT_ERR = -1,
|
||||
EVT_DONE,
|
||||
EVT_GSER,
|
||||
EVT_DISC,
|
||||
};
|
||||
|
||||
static inline struct dbc_ep *get_in_ep(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
return &dbc->eps[BULK_IN];
|
||||
}
|
||||
|
||||
static inline struct dbc_ep *get_out_ep(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
return &dbc->eps[BULK_OUT];
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_DBGCAP
|
||||
int xhci_dbc_init(struct xhci_hcd *xhci);
|
||||
void xhci_dbc_exit(struct xhci_hcd *xhci);
|
||||
int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci);
|
||||
void xhci_dbc_tty_unregister_driver(void);
|
||||
int xhci_dbc_tty_register_device(struct xhci_hcd *xhci);
|
||||
void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci);
|
||||
struct dbc_request *dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags);
|
||||
void dbc_free_request(struct dbc_ep *dep, struct dbc_request *req);
|
||||
int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, gfp_t gfp_flags);
|
||||
#ifdef CONFIG_PM
|
||||
int xhci_dbc_suspend(struct xhci_hcd *xhci);
|
||||
int xhci_dbc_resume(struct xhci_hcd *xhci);
|
||||
#endif /* CONFIG_PM */
|
||||
#else
|
||||
static inline int xhci_dbc_init(struct xhci_hcd *xhci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void xhci_dbc_exit(struct xhci_hcd *xhci)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int xhci_dbc_suspend(struct xhci_hcd *xhci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int xhci_dbc_resume(struct xhci_hcd *xhci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_XHCI_DBGCAP */
|
||||
#endif /* __LINUX_XHCI_DBGCAP_H */
|
497
drivers/usb/host/xhci-dbgtty.c
Normal file
497
drivers/usb/host/xhci-dbgtty.c
Normal file
|
@ -0,0 +1,497 @@
|
|||
/**
|
||||
* xhci-dbgtty.c - tty glue for xHCI debug capability
|
||||
*
|
||||
* Copyright (C) 2017 Intel Corporation
|
||||
*
|
||||
* Author: Lu Baolu <baolu.lu@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
||||
#include "xhci.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
|
||||
static unsigned int
|
||||
dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = kfifo_len(&port->write_fifo);
|
||||
if (len < size)
|
||||
size = len;
|
||||
if (size != 0)
|
||||
size = kfifo_out(&port->write_fifo, packet, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int dbc_start_tx(struct dbc_port *port)
|
||||
__releases(&port->port_lock)
|
||||
__acquires(&port->port_lock)
|
||||
{
|
||||
int len;
|
||||
struct dbc_request *req;
|
||||
int status = 0;
|
||||
bool do_tty_wake = false;
|
||||
struct list_head *pool = &port->write_pool;
|
||||
|
||||
while (!list_empty(pool)) {
|
||||
req = list_entry(pool->next, struct dbc_request, list_pool);
|
||||
len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET);
|
||||
if (len == 0)
|
||||
break;
|
||||
do_tty_wake = true;
|
||||
|
||||
req->length = len;
|
||||
list_del(&req->list_pool);
|
||||
|
||||
spin_unlock(&port->port_lock);
|
||||
status = dbc_ep_queue(port->out, req, GFP_ATOMIC);
|
||||
spin_lock(&port->port_lock);
|
||||
|
||||
if (status) {
|
||||
list_add(&req->list_pool, pool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_tty_wake && port->port.tty)
|
||||
tty_wakeup(port->port.tty);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbc_start_rx(struct dbc_port *port)
|
||||
__releases(&port->port_lock)
|
||||
__acquires(&port->port_lock)
|
||||
{
|
||||
struct dbc_request *req;
|
||||
int status;
|
||||
struct list_head *pool = &port->read_pool;
|
||||
|
||||
while (!list_empty(pool)) {
|
||||
if (!port->port.tty)
|
||||
break;
|
||||
|
||||
req = list_entry(pool->next, struct dbc_request, list_pool);
|
||||
list_del(&req->list_pool);
|
||||
req->length = DBC_MAX_PACKET;
|
||||
|
||||
spin_unlock(&port->port_lock);
|
||||
status = dbc_ep_queue(port->in, req, GFP_ATOMIC);
|
||||
spin_lock(&port->port_lock);
|
||||
|
||||
if (status) {
|
||||
list_add(&req->list_pool, pool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dbc_read_complete(struct xhci_hcd *xhci, struct dbc_request *req)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
struct dbc_port *port = &dbc->port;
|
||||
|
||||
spin_lock(&port->port_lock);
|
||||
list_add_tail(&req->list_pool, &port->read_queue);
|
||||
tasklet_schedule(&port->push);
|
||||
spin_unlock(&port->port_lock);
|
||||
}
|
||||
|
||||
static void dbc_write_complete(struct xhci_hcd *xhci, struct dbc_request *req)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
struct dbc_port *port = &dbc->port;
|
||||
|
||||
spin_lock(&port->port_lock);
|
||||
list_add(&req->list_pool, &port->write_pool);
|
||||
switch (req->status) {
|
||||
case 0:
|
||||
dbc_start_tx(port);
|
||||
break;
|
||||
case -ESHUTDOWN:
|
||||
break;
|
||||
default:
|
||||
xhci_warn(xhci, "unexpected write complete status %d\n",
|
||||
req->status);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&port->port_lock);
|
||||
}
|
||||
|
||||
static void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
dbc_free_request(dep, req);
|
||||
}
|
||||
|
||||
static int
|
||||
xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head,
|
||||
void (*fn)(struct xhci_hcd *, struct dbc_request *))
|
||||
{
|
||||
int i;
|
||||
struct dbc_request *req;
|
||||
|
||||
for (i = 0; i < DBC_QUEUE_SIZE; i++) {
|
||||
req = dbc_alloc_request(dep, GFP_ATOMIC);
|
||||
if (!req)
|
||||
break;
|
||||
|
||||
req->length = DBC_MAX_PACKET;
|
||||
req->buf = kmalloc(req->length, GFP_KERNEL);
|
||||
if (!req->buf) {
|
||||
xhci_dbc_free_req(dep, req);
|
||||
break;
|
||||
}
|
||||
|
||||
req->complete = fn;
|
||||
list_add_tail(&req->list_pool, head);
|
||||
}
|
||||
|
||||
return list_empty(head) ? -ENOMEM : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_dbc_free_requests(struct dbc_ep *dep, struct list_head *head)
|
||||
{
|
||||
struct dbc_request *req;
|
||||
|
||||
while (!list_empty(head)) {
|
||||
req = list_entry(head->next, struct dbc_request, list_pool);
|
||||
list_del(&req->list_pool);
|
||||
xhci_dbc_free_req(dep, req);
|
||||
}
|
||||
}
|
||||
|
||||
static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
{
|
||||
struct dbc_port *port = driver->driver_state;
|
||||
|
||||
tty->driver_data = port;
|
||||
|
||||
return tty_port_install(&port->port, driver, tty);
|
||||
}
|
||||
|
||||
static int dbc_tty_open(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
|
||||
return tty_port_open(&port->port, tty, file);
|
||||
}
|
||||
|
||||
static void dbc_tty_close(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
|
||||
tty_port_close(&port->port, tty, file);
|
||||
}
|
||||
|
||||
static int dbc_tty_write(struct tty_struct *tty,
|
||||
const unsigned char *buf,
|
||||
int count)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (count)
|
||||
count = kfifo_in(&port->write_fifo, buf, count);
|
||||
dbc_start_tx(port);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
status = kfifo_put(&port->write_fifo, ch);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbc_tty_flush_chars(struct tty_struct *tty)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
dbc_start_tx(port);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
|
||||
static int dbc_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int room = 0;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
room = kfifo_avail(&port->write_fifo);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
static int dbc_tty_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int chars = 0;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
chars = kfifo_len(&port->write_fifo);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
static void dbc_tty_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct dbc_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
tasklet_schedule(&port->push);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
|
||||
static const struct tty_operations dbc_tty_ops = {
|
||||
.install = dbc_tty_install,
|
||||
.open = dbc_tty_open,
|
||||
.close = dbc_tty_close,
|
||||
.write = dbc_tty_write,
|
||||
.put_char = dbc_tty_put_char,
|
||||
.flush_chars = dbc_tty_flush_chars,
|
||||
.write_room = dbc_tty_write_room,
|
||||
.chars_in_buffer = dbc_tty_chars_in_buffer,
|
||||
.unthrottle = dbc_tty_unthrottle,
|
||||
};
|
||||
|
||||
static struct tty_driver *dbc_tty_driver;
|
||||
|
||||
int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci)
|
||||
{
|
||||
int status;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
|
||||
TTY_DRIVER_DYNAMIC_DEV);
|
||||
if (IS_ERR(dbc_tty_driver)) {
|
||||
status = PTR_ERR(dbc_tty_driver);
|
||||
dbc_tty_driver = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
dbc_tty_driver->driver_name = "dbc_serial";
|
||||
dbc_tty_driver->name = "ttyDBC";
|
||||
|
||||
dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
dbc_tty_driver->init_termios = tty_std_termios;
|
||||
dbc_tty_driver->init_termios.c_cflag =
|
||||
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
dbc_tty_driver->init_termios.c_ispeed = 9600;
|
||||
dbc_tty_driver->init_termios.c_ospeed = 9600;
|
||||
dbc_tty_driver->driver_state = &dbc->port;
|
||||
|
||||
tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
|
||||
|
||||
status = tty_register_driver(dbc_tty_driver);
|
||||
if (status) {
|
||||
xhci_err(xhci,
|
||||
"can't register dbc tty driver, err %d\n", status);
|
||||
put_tty_driver(dbc_tty_driver);
|
||||
dbc_tty_driver = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void xhci_dbc_tty_unregister_driver(void)
|
||||
{
|
||||
tty_unregister_driver(dbc_tty_driver);
|
||||
put_tty_driver(dbc_tty_driver);
|
||||
dbc_tty_driver = NULL;
|
||||
}
|
||||
|
||||
static void dbc_rx_push(unsigned long _port)
|
||||
{
|
||||
struct dbc_request *req;
|
||||
struct tty_struct *tty;
|
||||
bool do_push = false;
|
||||
bool disconnect = false;
|
||||
struct dbc_port *port = (void *)_port;
|
||||
struct list_head *queue = &port->read_queue;
|
||||
|
||||
spin_lock_irq(&port->port_lock);
|
||||
tty = port->port.tty;
|
||||
while (!list_empty(queue)) {
|
||||
req = list_first_entry(queue, struct dbc_request, list_pool);
|
||||
|
||||
if (tty && tty_throttled(tty))
|
||||
break;
|
||||
|
||||
switch (req->status) {
|
||||
case 0:
|
||||
break;
|
||||
case -ESHUTDOWN:
|
||||
disconnect = true;
|
||||
break;
|
||||
default:
|
||||
pr_warn("ttyDBC0: unexpected RX status %d\n",
|
||||
req->status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (req->actual) {
|
||||
char *packet = req->buf;
|
||||
unsigned int n, size = req->actual;
|
||||
int count;
|
||||
|
||||
n = port->n_read;
|
||||
if (n) {
|
||||
packet += n;
|
||||
size -= n;
|
||||
}
|
||||
|
||||
count = tty_insert_flip_string(&port->port, packet,
|
||||
size);
|
||||
if (count)
|
||||
do_push = true;
|
||||
if (count != size) {
|
||||
port->n_read += count;
|
||||
break;
|
||||
}
|
||||
port->n_read = 0;
|
||||
}
|
||||
|
||||
list_move(&req->list_pool, &port->read_pool);
|
||||
}
|
||||
|
||||
if (do_push)
|
||||
tty_flip_buffer_push(&port->port);
|
||||
|
||||
if (!list_empty(queue) && tty) {
|
||||
if (!tty_throttled(tty)) {
|
||||
if (do_push)
|
||||
tasklet_schedule(&port->push);
|
||||
else
|
||||
pr_warn("ttyDBC0: RX not scheduled?\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!disconnect)
|
||||
dbc_start_rx(port);
|
||||
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
}
|
||||
|
||||
static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty)
|
||||
{
|
||||
struct dbc_port *port = container_of(_port, struct dbc_port, port);
|
||||
|
||||
spin_lock_irq(&port->port_lock);
|
||||
dbc_start_rx(port);
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tty_port_operations dbc_port_ops = {
|
||||
.activate = dbc_port_activate,
|
||||
};
|
||||
|
||||
static void
|
||||
xhci_dbc_tty_init_port(struct xhci_hcd *xhci, struct dbc_port *port)
|
||||
{
|
||||
tty_port_init(&port->port);
|
||||
spin_lock_init(&port->port_lock);
|
||||
tasklet_init(&port->push, dbc_rx_push, (unsigned long)port);
|
||||
INIT_LIST_HEAD(&port->read_pool);
|
||||
INIT_LIST_HEAD(&port->read_queue);
|
||||
INIT_LIST_HEAD(&port->write_pool);
|
||||
|
||||
port->in = get_in_ep(xhci);
|
||||
port->out = get_out_ep(xhci);
|
||||
port->port.ops = &dbc_port_ops;
|
||||
port->n_read = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xhci_dbc_tty_exit_port(struct dbc_port *port)
|
||||
{
|
||||
tasklet_kill(&port->push);
|
||||
tty_port_destroy(&port->port);
|
||||
}
|
||||
|
||||
int xhci_dbc_tty_register_device(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret;
|
||||
struct device *tty_dev;
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
struct dbc_port *port = &dbc->port;
|
||||
|
||||
xhci_dbc_tty_init_port(xhci, port);
|
||||
tty_dev = tty_port_register_device(&port->port,
|
||||
dbc_tty_driver, 0, NULL);
|
||||
ret = IS_ERR_OR_NULL(tty_dev);
|
||||
if (ret)
|
||||
goto register_fail;
|
||||
|
||||
ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto buf_alloc_fail;
|
||||
|
||||
ret = xhci_dbc_alloc_requests(port->in, &port->read_pool,
|
||||
dbc_read_complete);
|
||||
if (ret)
|
||||
goto request_fail;
|
||||
|
||||
ret = xhci_dbc_alloc_requests(port->out, &port->write_pool,
|
||||
dbc_write_complete);
|
||||
if (ret)
|
||||
goto request_fail;
|
||||
|
||||
port->registered = true;
|
||||
|
||||
return 0;
|
||||
|
||||
request_fail:
|
||||
xhci_dbc_free_requests(port->in, &port->read_pool);
|
||||
xhci_dbc_free_requests(port->out, &port->write_pool);
|
||||
kfifo_free(&port->write_fifo);
|
||||
|
||||
buf_alloc_fail:
|
||||
tty_unregister_device(dbc_tty_driver, 0);
|
||||
|
||||
register_fail:
|
||||
xhci_dbc_tty_exit_port(port);
|
||||
|
||||
xhci_err(xhci, "can't register tty port, err %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
struct dbc_port *port = &dbc->port;
|
||||
|
||||
tty_unregister_device(dbc_tty_driver, 0);
|
||||
xhci_dbc_tty_exit_port(port);
|
||||
port->registered = false;
|
||||
|
||||
kfifo_free(&port->write_fifo);
|
||||
xhci_dbc_free_requests(get_out_ep(xhci), &port->read_pool);
|
||||
xhci_dbc_free_requests(get_out_ep(xhci), &port->read_queue);
|
||||
xhci_dbc_free_requests(get_in_ep(xhci), &port->write_pool);
|
||||
}
|
|
@ -388,7 +388,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
|
|||
|
||||
trace_xhci_stop_device(virt_dev);
|
||||
|
||||
cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
|
||||
cmd = xhci_alloc_command(xhci, true, GFP_NOIO);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -404,8 +404,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
|
|||
if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING)
|
||||
continue;
|
||||
|
||||
command = xhci_alloc_command(xhci, false, false,
|
||||
GFP_NOWAIT);
|
||||
command = xhci_alloc_command(xhci, false, GFP_NOWAIT);
|
||||
if (!command) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
ret = -ENOMEM;
|
||||
|
@ -1077,6 +1076,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
retval = -ENODEV;
|
||||
break;
|
||||
}
|
||||
trace_xhci_get_port_status(wIndex, temp);
|
||||
status = xhci_get_port_status(hcd, bus_state, port_array,
|
||||
wIndex, temp, flags);
|
||||
if (status == 0xffffffff)
|
||||
|
@ -1443,6 +1443,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
retval = -ENODEV;
|
||||
break;
|
||||
}
|
||||
trace_xhci_hub_status_data(i, temp);
|
||||
|
||||
if ((temp & mask) != 0 ||
|
||||
(bus_state->port_c_suspend & 1 << i) ||
|
||||
(bus_state->resume_done[i] && time_after_eq(
|
||||
|
|
|
@ -357,7 +357,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
|
|||
* Set the end flag and the cycle toggle bit on the last segment.
|
||||
* See section 4.9.1 and figures 15 and 16.
|
||||
*/
|
||||
static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
||||
unsigned int num_segs, unsigned int cycle_state,
|
||||
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags)
|
||||
{
|
||||
|
@ -454,7 +454,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
int type, gfp_t flags)
|
||||
{
|
||||
struct xhci_container_ctx *ctx;
|
||||
|
@ -479,7 +479,7 @@ static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci
|
|||
return ctx;
|
||||
}
|
||||
|
||||
static void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
|
@ -650,7 +650,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
|||
|
||||
/* Allocate everything needed to free the stream rings later */
|
||||
stream_info->free_streams_command =
|
||||
xhci_alloc_command(xhci, true, true, mem_flags);
|
||||
xhci_alloc_command_with_ctx(xhci, true, mem_flags);
|
||||
if (!stream_info->free_streams_command)
|
||||
goto cleanup_ctx;
|
||||
|
||||
|
@ -1715,8 +1715,7 @@ static void scratchpad_free(struct xhci_hcd *xhci)
|
|||
}
|
||||
|
||||
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
||||
bool allocate_in_ctx, bool allocate_completion,
|
||||
gfp_t mem_flags)
|
||||
bool allocate_completion, gfp_t mem_flags)
|
||||
{
|
||||
struct xhci_command *command;
|
||||
|
||||
|
@ -1724,21 +1723,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
|||
if (!command)
|
||||
return NULL;
|
||||
|
||||
if (allocate_in_ctx) {
|
||||
command->in_ctx =
|
||||
xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT,
|
||||
mem_flags);
|
||||
if (!command->in_ctx) {
|
||||
kfree(command);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (allocate_completion) {
|
||||
command->completion =
|
||||
kzalloc(sizeof(struct completion), mem_flags);
|
||||
if (!command->completion) {
|
||||
xhci_free_container_ctx(xhci, command->in_ctx);
|
||||
kfree(command);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1750,6 +1738,25 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
|||
return command;
|
||||
}
|
||||
|
||||
struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci,
|
||||
bool allocate_completion, gfp_t mem_flags)
|
||||
{
|
||||
struct xhci_command *command;
|
||||
|
||||
command = xhci_alloc_command(xhci, allocate_completion, mem_flags);
|
||||
if (!command)
|
||||
return NULL;
|
||||
|
||||
command->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT,
|
||||
mem_flags);
|
||||
if (!command->in_ctx) {
|
||||
kfree(command->completion);
|
||||
kfree(command);
|
||||
return NULL;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
void xhci_urb_free_priv(struct urb_priv *urb_priv)
|
||||
{
|
||||
kfree(urb_priv);
|
||||
|
@ -1764,21 +1771,58 @@ void xhci_free_command(struct xhci_hcd *xhci,
|
|||
kfree(command);
|
||||
}
|
||||
|
||||
int xhci_alloc_erst(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *evt_ring,
|
||||
struct xhci_erst *erst,
|
||||
gfp_t flags)
|
||||
{
|
||||
size_t size;
|
||||
unsigned int val;
|
||||
struct xhci_segment *seg;
|
||||
struct xhci_erst_entry *entry;
|
||||
|
||||
size = sizeof(struct xhci_erst_entry) * evt_ring->num_segs;
|
||||
erst->entries = dma_zalloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
|
||||
size, &erst->erst_dma_addr, flags);
|
||||
if (!erst->entries)
|
||||
return -ENOMEM;
|
||||
|
||||
erst->num_entries = evt_ring->num_segs;
|
||||
|
||||
seg = evt_ring->first_seg;
|
||||
for (val = 0; val < evt_ring->num_segs; val++) {
|
||||
entry = &erst->entries[val];
|
||||
entry->seg_addr = cpu_to_le64(seg->dma);
|
||||
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
|
||||
entry->rsvd = 0;
|
||||
seg = seg->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
|
||||
{
|
||||
size_t size;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
|
||||
size = sizeof(struct xhci_erst_entry) * (erst->num_entries);
|
||||
if (erst->entries)
|
||||
dma_free_coherent(dev, size,
|
||||
erst->entries,
|
||||
erst->erst_dma_addr);
|
||||
erst->entries = NULL;
|
||||
}
|
||||
|
||||
void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
int size;
|
||||
int i, j, num_ports;
|
||||
|
||||
cancel_delayed_work_sync(&xhci->cmd_timer);
|
||||
|
||||
/* Free the Event Ring Segment Table and the actual Event Ring */
|
||||
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
|
||||
if (xhci->erst.entries)
|
||||
dma_free_coherent(dev, size,
|
||||
xhci->erst.entries, xhci->erst.erst_dma_addr);
|
||||
xhci->erst.entries = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST");
|
||||
xhci_free_erst(xhci, &xhci->erst);
|
||||
|
||||
if (xhci->event_ring)
|
||||
xhci_ring_free(xhci, xhci->event_ring);
|
||||
xhci->event_ring = NULL;
|
||||
|
@ -2315,9 +2359,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
unsigned int val, val2;
|
||||
u64 val_64;
|
||||
struct xhci_segment *seg;
|
||||
u32 page_size, temp;
|
||||
int i;
|
||||
u32 page_size, temp;
|
||||
int i, ret;
|
||||
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
|
||||
|
@ -2421,9 +2464,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Setting command ring address to 0x%016llx", val_64);
|
||||
xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
|
||||
xhci_dbg_cmd_ptrs(xhci);
|
||||
|
||||
xhci->lpm_command = xhci_alloc_command(xhci, true, true, flags);
|
||||
xhci->lpm_command = xhci_alloc_command_with_ctx(xhci, true, flags);
|
||||
if (!xhci->lpm_command)
|
||||
goto fail;
|
||||
|
||||
|
@ -2439,8 +2481,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
"// Doorbell array is located at offset 0x%x"
|
||||
" from cap regs base addr", val);
|
||||
xhci->dba = (void __iomem *) xhci->cap_regs + val;
|
||||
xhci_dbg_regs(xhci);
|
||||
xhci_print_run_regs(xhci);
|
||||
/* Set ir_set to interrupt register set 0 */
|
||||
xhci->ir_set = &xhci->run_regs->ir_set[0];
|
||||
|
||||
|
@ -2456,32 +2496,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
if (xhci_check_trb_in_td_math(xhci) < 0)
|
||||
goto fail;
|
||||
|
||||
xhci->erst.entries = dma_alloc_coherent(dev,
|
||||
sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
|
||||
flags);
|
||||
if (!xhci->erst.entries)
|
||||
ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags);
|
||||
if (ret)
|
||||
goto fail;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Allocated event ring segment table at 0x%llx",
|
||||
(unsigned long long)dma);
|
||||
|
||||
memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS);
|
||||
xhci->erst.num_entries = ERST_NUM_SEGS;
|
||||
xhci->erst.erst_dma_addr = dma;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx",
|
||||
xhci->erst.num_entries,
|
||||
xhci->erst.entries,
|
||||
(unsigned long long)xhci->erst.erst_dma_addr);
|
||||
|
||||
/* set ring base address and size for each segment table entry */
|
||||
for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) {
|
||||
struct xhci_erst_entry *entry = &xhci->erst.entries[val];
|
||||
entry->seg_addr = cpu_to_le64(seg->dma);
|
||||
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
|
||||
entry->rsvd = 0;
|
||||
seg = seg->next;
|
||||
}
|
||||
|
||||
/* set ERST count with the number of entries in the segment table */
|
||||
val = readl(&xhci->ir_set->erst_size);
|
||||
|
@ -2507,7 +2524,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
xhci_set_hc_event_deq(xhci);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Wrote ERST address to ir_set 0.");
|
||||
xhci_print_ir_set(xhci, 0);
|
||||
|
||||
/*
|
||||
* XXX: Might need to set the Interrupter Moderation Register to
|
||||
|
|
|
@ -57,26 +57,21 @@
|
|||
/* u2_phy_pll register */
|
||||
#define CTRL_U2_FORCE_PLL_STB BIT(28)
|
||||
|
||||
#define PERI_WK_CTRL0 0x400
|
||||
#define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */
|
||||
#define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/
|
||||
#define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1)
|
||||
#define UWK_CTL1_1P_LS_E BIT(0)
|
||||
/* usb remote wakeup registers in syscon */
|
||||
/* mt8173 etc */
|
||||
#define PERI_WK_CTRL1 0x4
|
||||
#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
|
||||
#define WC1_IS_EN BIT(25)
|
||||
#define WC1_IS_P BIT(6) /* polarity for ip sleep */
|
||||
|
||||
#define PERI_WK_CTRL1 0x404
|
||||
#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
|
||||
#define UWK_CTL1_IS_E BIT(25)
|
||||
#define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21)
|
||||
#define UWK_CTL1_0P_LS_E BIT(20)
|
||||
#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
|
||||
#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
|
||||
#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
|
||||
#define UWK_CTL1_0P_LS_P BIT(7)
|
||||
#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
|
||||
/* mt2712 etc */
|
||||
#define PERI_SSUSB_SPM_CTRL 0x0
|
||||
#define SSC_IP_SLEEP_EN BIT(4)
|
||||
#define SSC_SPM_INT_EN BIT(1)
|
||||
|
||||
enum ssusb_wakeup_src {
|
||||
SSUSB_WK_IP_SLEEP = 1,
|
||||
SSUSB_WK_LINE_STATE = 2,
|
||||
enum ssusb_uwk_vers {
|
||||
SSUSB_UWK_V1 = 1,
|
||||
SSUSB_UWK_V2,
|
||||
};
|
||||
|
||||
static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
|
||||
|
@ -296,112 +291,58 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk)
|
|||
}
|
||||
|
||||
/* only clocks can be turn off for ip-sleep wakeup mode */
|
||||
static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk)
|
||||
static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable)
|
||||
{
|
||||
u32 tmp;
|
||||
struct regmap *pericfg = mtk->pericfg;
|
||||
u32 reg, msk, val;
|
||||
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_IS_P;
|
||||
tmp &= ~(UWK_CTL1_IS_C(0xf));
|
||||
tmp |= UWK_CTL1_IS_C(0x8);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
|
||||
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
|
||||
__func__, tmp);
|
||||
}
|
||||
|
||||
static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_IS_E;
|
||||
regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* for line-state wakeup mode, phy's power should not power-down
|
||||
* and only support cable plug in/out
|
||||
*/
|
||||
static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
u32 tmp;
|
||||
struct regmap *pericfg = mtk->pericfg;
|
||||
|
||||
/* line-state of u2-port0 */
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_0P_LS_P;
|
||||
tmp &= ~(UWK_CTL1_0P_LS_C(0xf));
|
||||
tmp |= UWK_CTL1_0P_LS_C(0x8);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp);
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E);
|
||||
|
||||
/* line-state of u2-port1 */
|
||||
regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
|
||||
tmp &= ~(UWK_CTL1_1P_LS_C(0xf));
|
||||
tmp |= UWK_CTL1_1P_LS_C(0x8);
|
||||
regmap_write(pericfg, PERI_WK_CTRL0, tmp);
|
||||
regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E);
|
||||
}
|
||||
|
||||
static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
u32 tmp;
|
||||
struct regmap *pericfg = mtk->pericfg;
|
||||
|
||||
/* line-state of u2-port0 */
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_0P_LS_E;
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp);
|
||||
|
||||
/* line-state of u2-port1 */
|
||||
regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
|
||||
tmp &= ~UWK_CTL1_1P_LS_E;
|
||||
regmap_write(pericfg, PERI_WK_CTRL0, tmp);
|
||||
}
|
||||
|
||||
static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
|
||||
usb_wakeup_ip_sleep_en(mtk);
|
||||
else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
|
||||
usb_wakeup_line_state_en(mtk);
|
||||
}
|
||||
|
||||
static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
|
||||
usb_wakeup_ip_sleep_dis(mtk);
|
||||
else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
|
||||
usb_wakeup_line_state_dis(mtk);
|
||||
switch (mtk->uwk_vers) {
|
||||
case SSUSB_UWK_V1:
|
||||
reg = mtk->uwk_reg_base + PERI_WK_CTRL1;
|
||||
msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
|
||||
val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
|
||||
break;
|
||||
case SSUSB_UWK_V2:
|
||||
reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
|
||||
msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
|
||||
val = enable ? msk : 0;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
regmap_update_bits(mtk->uwk, reg, msk, val);
|
||||
}
|
||||
|
||||
static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct device *dev = mtk->dev;
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* wakeup function is optional, so it is not an error if this property
|
||||
* does not exist, and in such case, no need to get relative
|
||||
* properties anymore.
|
||||
*/
|
||||
of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src);
|
||||
if (!mtk->wakeup_src)
|
||||
/* Wakeup function is optional */
|
||||
mtk->uwk_en = of_property_read_bool(dn, "wakeup-source");
|
||||
if (!mtk->uwk_en)
|
||||
return 0;
|
||||
|
||||
mtk->pericfg = syscon_regmap_lookup_by_phandle(dn,
|
||||
"mediatek,syscon-wakeup");
|
||||
if (IS_ERR(mtk->pericfg)) {
|
||||
dev_err(dev, "fail to get pericfg regs\n");
|
||||
return PTR_ERR(mtk->pericfg);
|
||||
}
|
||||
ret = of_parse_phandle_with_fixed_args(dn,
|
||||
"mediatek,syscon-wakeup", 2, 0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
mtk->uwk_reg_base = args.args[0];
|
||||
mtk->uwk_vers = args.args[1];
|
||||
mtk->uwk = syscon_node_to_regmap(args.np);
|
||||
of_node_put(args.np);
|
||||
dev_info(mtk->dev, "uwk - reg:0x%x, version:%d\n",
|
||||
mtk->uwk_reg_base, mtk->uwk_vers);
|
||||
|
||||
return PTR_ERR_OR_ZERO(mtk->uwk);
|
||||
|
||||
}
|
||||
|
||||
static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable)
|
||||
{
|
||||
if (mtk->uwk_en)
|
||||
usb_wakeup_ip_sleep_set(mtk, enable);
|
||||
}
|
||||
|
||||
static int xhci_mtk_setup(struct usb_hcd *hcd);
|
||||
|
@ -583,8 +524,10 @@ static int xhci_mtk_probe(struct platform_device *pdev)
|
|||
&mtk->u3p_dis_msk);
|
||||
|
||||
ret = usb_wakeup_of_property_parse(mtk, node);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to parse uwk property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mtk->num_phys = of_count_phandle_with_args(node,
|
||||
"phys", "#phy-cells");
|
||||
|
@ -674,6 +617,15 @@ static int xhci_mtk_probe(struct platform_device *pdev)
|
|||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
xhci->main_hcd = hcd;
|
||||
|
||||
/*
|
||||
* imod_interval is the interrupt moderation value in nanoseconds.
|
||||
* The increment interval is 8 times as much as that defined in
|
||||
* the xHCI spec on MTK's controller.
|
||||
*/
|
||||
xhci->imod_interval = 5000;
|
||||
device_property_read_u32(dev, "imod-interval-ns", &xhci->imod_interval);
|
||||
|
||||
xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
|
||||
dev_name(dev), hcd);
|
||||
if (!xhci->shared_hcd) {
|
||||
|
@ -768,7 +720,7 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev)
|
|||
xhci_mtk_host_disable(mtk);
|
||||
xhci_mtk_phy_power_off(mtk);
|
||||
xhci_mtk_clks_disable(mtk);
|
||||
usb_wakeup_enable(mtk);
|
||||
usb_wakeup_set(mtk, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -778,7 +730,7 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev)
|
|||
struct usb_hcd *hcd = mtk->hcd;
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
usb_wakeup_disable(mtk);
|
||||
usb_wakeup_set(mtk, false);
|
||||
xhci_mtk_clks_enable(mtk);
|
||||
xhci_mtk_phy_power_on(mtk);
|
||||
xhci_mtk_host_enable(mtk);
|
||||
|
|
|
@ -122,8 +122,12 @@ struct xhci_hcd_mtk {
|
|||
struct regmap *pericfg;
|
||||
struct phy **phys;
|
||||
int num_phys;
|
||||
int wakeup_src;
|
||||
bool lpm_support;
|
||||
/* usb remote wakeup */
|
||||
bool uwk_en;
|
||||
struct regmap *uwk;
|
||||
u32 uwk_reg_base;
|
||||
u32 uwk_vers;
|
||||
};
|
||||
|
||||
static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)
|
||||
|
|
|
@ -237,6 +237,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
|||
if (!xhci->sbrn)
|
||||
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
|
||||
|
||||
/* imod_interval is the interrupt moderation value in nanoseconds. */
|
||||
xhci->imod_interval = 40000;
|
||||
|
||||
retval = xhci_gen_setup(hcd, xhci_pci_quirks);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
|
|
@ -269,6 +269,11 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
|
||||
xhci->quirks |= XHCI_BROKEN_PORT_PED;
|
||||
|
||||
/* imod_interval is the interrupt moderation value in nanoseconds. */
|
||||
xhci->imod_interval = 40000;
|
||||
device_property_read_u32(sysdev, "imod-interval-ns",
|
||||
&xhci->imod_interval);
|
||||
|
||||
hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
|
||||
if (IS_ERR(hcd->usb_phy)) {
|
||||
ret = PTR_ERR(hcd->usb_phy);
|
||||
|
|
|
@ -153,7 +153,7 @@ static void next_trb(struct xhci_hcd *xhci,
|
|||
* See Cycle bit rules. SW is the consumer for the event ring only.
|
||||
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
|
||||
*/
|
||||
static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
/* event ring doesn't have link trbs, check for last trb */
|
||||
if (ring->type == TYPE_EVENT) {
|
||||
|
@ -1141,7 +1141,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
|||
if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
|
||||
struct xhci_command *command;
|
||||
|
||||
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
|
||||
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
@ -1821,7 +1821,7 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
|
|||
{
|
||||
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
struct xhci_command *command;
|
||||
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
|
||||
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
@ -1878,12 +1878,10 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
|
|||
static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
struct xhci_ring *ep_ring, int *status)
|
||||
{
|
||||
struct urb_priv *urb_priv;
|
||||
struct urb *urb = NULL;
|
||||
|
||||
/* Clean up the endpoint's TD list */
|
||||
urb = td->urb;
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
/* if a bounce buffer was used to align this td then unmap it */
|
||||
xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);
|
||||
|
@ -1994,7 +1992,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||
struct xhci_virt_ep *ep, int *status)
|
||||
{
|
||||
struct xhci_virt_device *xdev;
|
||||
struct xhci_ring *ep_ring;
|
||||
unsigned int slot_id;
|
||||
int ep_index;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
@ -2006,7 +2003,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
|
||||
xdev = xhci->devs[slot_id];
|
||||
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
|
||||
ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
|
||||
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
|
||||
requested = td->urb->transfer_buffer_length;
|
||||
|
@ -2965,7 +2961,7 @@ static int prepare_transfer(struct xhci_hcd *xhci,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int count_trbs(u64 addr, u64 len)
|
||||
unsigned int count_trbs(u64 addr, u64 len)
|
||||
{
|
||||
unsigned int num_trbs;
|
||||
|
||||
|
@ -4044,7 +4040,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
|
|||
}
|
||||
|
||||
/* This function gets called from contexts where it cannot sleep */
|
||||
cmd = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
|
||||
cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <linux/tracepoint.h>
|
||||
#include "xhci.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
|
||||
#define XHCI_MSG_MAX 500
|
||||
|
||||
|
@ -155,6 +156,21 @@ DEFINE_EVENT(xhci_log_trb, xhci_queue_trb,
|
|||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event,
|
||||
TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer,
|
||||
TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue,
|
||||
TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xhci_log_virt_dev,
|
||||
TP_PROTO(struct xhci_virt_device *vdev),
|
||||
TP_ARGS(vdev),
|
||||
|
@ -478,6 +494,59 @@ DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status,
|
|||
TP_ARGS(portnum, portsc)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_log_portsc, xhci_get_port_status,
|
||||
TP_PROTO(u32 portnum, u32 portsc),
|
||||
TP_ARGS(portnum, portsc)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data,
|
||||
TP_PROTO(u32 portnum, u32 portsc),
|
||||
TP_ARGS(portnum, portsc)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xhci_dbc_log_request,
|
||||
TP_PROTO(struct dbc_request *req),
|
||||
TP_ARGS(req),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct dbc_request *, req)
|
||||
__field(bool, dir)
|
||||
__field(unsigned int, actual)
|
||||
__field(unsigned int, length)
|
||||
__field(int, status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->req = req;
|
||||
__entry->dir = req->direction;
|
||||
__entry->actual = req->actual;
|
||||
__entry->length = req->length;
|
||||
__entry->status = req->status;
|
||||
),
|
||||
TP_printk("%s: req %p length %u/%u ==> %d",
|
||||
__entry->dir ? "bulk-in" : "bulk-out",
|
||||
__entry->req, __entry->actual,
|
||||
__entry->length, __entry->status
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_alloc_request,
|
||||
TP_PROTO(struct dbc_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_free_request,
|
||||
TP_PROTO(struct dbc_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_queue_request,
|
||||
TP_PROTO(struct dbc_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_giveback_request,
|
||||
TP_PROTO(struct dbc_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
#endif /* __XHCI_TRACE_H */
|
||||
|
||||
/* this part must be outside header guard */
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "xhci-trace.h"
|
||||
#include "xhci-mtk.h"
|
||||
#include "xhci-debugfs.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Sarah Sharp"
|
||||
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
|
||||
|
@ -573,10 +574,6 @@ int xhci_run(struct usb_hcd *hcd)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
xhci_dbg_cmd_ptrs(xhci);
|
||||
|
||||
xhci_dbg(xhci, "ERST memory map follows:\n");
|
||||
xhci_dbg_erst(xhci, &xhci->erst);
|
||||
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
|
||||
temp_64 &= ~ERST_PTR_MASK;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
|
@ -586,11 +583,7 @@ int xhci_run(struct usb_hcd *hcd)
|
|||
"// Set the interrupt modulation register");
|
||||
temp = readl(&xhci->ir_set->irq_control);
|
||||
temp &= ~ER_IRQ_INTERVAL_MASK;
|
||||
/*
|
||||
* the increment interval is 8 times as much as that defined
|
||||
* in xHCI spec on MTK's controller
|
||||
*/
|
||||
temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
|
||||
temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
|
||||
writel(temp, &xhci->ir_set->irq_control);
|
||||
|
||||
/* Set the HCD state before we enable the irqs */
|
||||
|
@ -605,12 +598,11 @@ int xhci_run(struct usb_hcd *hcd)
|
|||
"// Enabling event ring interrupter %p by writing 0x%x to irq_pending",
|
||||
xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp));
|
||||
writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending);
|
||||
xhci_print_ir_set(xhci, 0);
|
||||
|
||||
if (xhci->quirks & XHCI_NEC_HOST) {
|
||||
struct xhci_command *command;
|
||||
|
||||
command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, false, GFP_KERNEL);
|
||||
if (!command)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -622,6 +614,8 @@ int xhci_run(struct usb_hcd *hcd)
|
|||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Finished xhci_run for USB2 roothub");
|
||||
|
||||
xhci_dbc_init(xhci);
|
||||
|
||||
xhci_debugfs_init(xhci);
|
||||
|
||||
return 0;
|
||||
|
@ -654,6 +648,8 @@ static void xhci_stop(struct usb_hcd *hcd)
|
|||
|
||||
xhci_debugfs_exit(xhci);
|
||||
|
||||
xhci_dbc_exit(xhci);
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
xhci->xhc_state |= XHCI_STATE_HALTED;
|
||||
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
|
||||
|
@ -681,7 +677,6 @@ static void xhci_stop(struct usb_hcd *hcd)
|
|||
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
|
||||
temp = readl(&xhci->ir_set->irq_pending);
|
||||
writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending);
|
||||
xhci_print_ir_set(xhci, 0);
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
|
||||
xhci_mem_cleanup(xhci);
|
||||
|
@ -870,6 +865,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
|
|||
xhci->shared_hcd->state != HC_STATE_SUSPENDED)
|
||||
return -EINVAL;
|
||||
|
||||
xhci_dbc_suspend(xhci);
|
||||
|
||||
/* Clear root port wake on bits if wakeup not allowed. */
|
||||
if (!do_wakeup)
|
||||
xhci_disable_port_wake_on_bits(xhci);
|
||||
|
@ -1014,7 +1011,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
|||
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
|
||||
temp = readl(&xhci->ir_set->irq_pending);
|
||||
writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending);
|
||||
xhci_print_ir_set(xhci, 0);
|
||||
|
||||
xhci_dbg(xhci, "cleaning up memory\n");
|
||||
xhci_mem_cleanup(xhci);
|
||||
|
@ -1065,6 +1061,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
|||
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
xhci_dbc_resume(xhci);
|
||||
|
||||
done:
|
||||
if (retval == 0) {
|
||||
/* Resume root hubs only when have pending events. */
|
||||
|
@ -1243,7 +1241,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
|
|||
* changes max packet sizes.
|
||||
*/
|
||||
|
||||
command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
if (!command)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1498,7 +1496,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
|||
* the first cancellation to be handled.
|
||||
*/
|
||||
if (!(ep->ep_state & EP_STOP_CMD_PENDING)) {
|
||||
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
|
||||
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
|
@ -2683,7 +2681,7 @@ static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
|
||||
virt_dev = xhci->devs[udev->slot_id];
|
||||
|
||||
command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
if (!command)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -2838,12 +2836,10 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
|
|||
unsigned int stream_id, struct xhci_td *td)
|
||||
{
|
||||
struct xhci_dequeue_state deq_state;
|
||||
struct xhci_virt_ep *ep;
|
||||
struct usb_device *udev = td->urb->dev;
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
|
||||
"Cleaning up stalled endpoint ring");
|
||||
ep = &xhci->devs[udev->slot_id]->eps[ep_index];
|
||||
/* We need to move the HW's dequeue pointer past this TD,
|
||||
* or it will attempt to resend it on the next doorbell ring.
|
||||
*/
|
||||
|
@ -3092,7 +3088,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
|
||||
config_cmd = xhci_alloc_command_with_ctx(xhci, true, mem_flags);
|
||||
if (!config_cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -3363,7 +3359,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
|||
unsigned int slot_id;
|
||||
struct xhci_virt_device *virt_dev;
|
||||
struct xhci_command *reset_device_cmd;
|
||||
int last_freed_endpoint;
|
||||
struct xhci_slot_ctx *slot_ctx;
|
||||
int old_active_eps = 0;
|
||||
|
||||
|
@ -3416,7 +3411,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
|||
* reset as part of error handling, so use GFP_NOIO instead of
|
||||
* GFP_KERNEL.
|
||||
*/
|
||||
reset_device_cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
|
||||
reset_device_cmd = xhci_alloc_command(xhci, true, GFP_NOIO);
|
||||
if (!reset_device_cmd) {
|
||||
xhci_dbg(xhci, "Couldn't allocate command structure.\n");
|
||||
return -ENOMEM;
|
||||
|
@ -3478,7 +3473,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
|||
}
|
||||
|
||||
/* Everything but endpoint 0 is disabled, so free the rings. */
|
||||
last_freed_endpoint = 1;
|
||||
for (i = 1; i < 31; i++) {
|
||||
struct xhci_virt_ep *ep = &virt_dev->eps[i];
|
||||
|
||||
|
@ -3493,7 +3487,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
|||
if (ep->ring) {
|
||||
xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
|
||||
xhci_free_endpoint_ring(xhci, virt_dev, i);
|
||||
last_freed_endpoint = i;
|
||||
}
|
||||
if (!list_empty(&virt_dev->eps[i].bw_endpoint_list))
|
||||
xhci_drop_ep_from_interval_table(xhci,
|
||||
|
@ -3566,7 +3559,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
|
|||
u32 state;
|
||||
int ret = 0;
|
||||
|
||||
command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, false, GFP_KERNEL);
|
||||
if (!command)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -3628,7 +3621,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
int ret, slot_id;
|
||||
struct xhci_command *command;
|
||||
|
||||
command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
if (!command)
|
||||
return 0;
|
||||
|
||||
|
@ -3761,7 +3754,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
}
|
||||
}
|
||||
|
||||
command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
|
||||
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
if (!command) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
|
@ -4680,7 +4673,7 @@ static int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
|
||||
config_cmd = xhci_alloc_command_with_ctx(xhci, true, mem_flags);
|
||||
if (!config_cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -4828,7 +4821,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
|
|||
xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
|
||||
if (xhci->hci_version > 0x100)
|
||||
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
|
||||
xhci_print_registers(xhci);
|
||||
|
||||
xhci->quirks |= quirks;
|
||||
|
||||
|
|
|
@ -1717,6 +1717,8 @@ struct xhci_hcd {
|
|||
u8 max_interrupters;
|
||||
u8 max_ports;
|
||||
u8 isoc_threshold;
|
||||
/* imod_interval in ns (I * 250ns) */
|
||||
u32 imod_interval;
|
||||
int event_ring_max;
|
||||
/* 4KB min, 128MB max */
|
||||
int page_size;
|
||||
|
@ -1856,6 +1858,7 @@ struct xhci_hcd {
|
|||
struct dentry *debugfs_slots;
|
||||
struct list_head regset_list;
|
||||
|
||||
void *dbc;
|
||||
/* platform-specific data -- must come last */
|
||||
unsigned long priv[0] __aligned(sizeof(s64));
|
||||
};
|
||||
|
@ -1924,12 +1927,6 @@ static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
|
|||
}
|
||||
|
||||
/* xHCI debugging */
|
||||
void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num);
|
||||
void xhci_print_registers(struct xhci_hcd *xhci);
|
||||
void xhci_dbg_regs(struct xhci_hcd *xhci);
|
||||
void xhci_print_run_regs(struct xhci_hcd *xhci);
|
||||
void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst);
|
||||
void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci);
|
||||
char *xhci_get_slot_state(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
|
||||
|
@ -1965,9 +1962,17 @@ void xhci_slot_copy(struct xhci_hcd *xhci,
|
|||
int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev,
|
||||
struct usb_device *udev, struct usb_host_endpoint *ep,
|
||||
gfp_t mem_flags);
|
||||
struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
||||
unsigned int num_segs, unsigned int cycle_state,
|
||||
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags);
|
||||
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
|
||||
int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
unsigned int num_trbs, gfp_t flags);
|
||||
unsigned int num_trbs, gfp_t flags);
|
||||
int xhci_alloc_erst(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *evt_ring,
|
||||
struct xhci_erst *erst,
|
||||
gfp_t flags);
|
||||
void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst);
|
||||
void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *virt_dev,
|
||||
unsigned int ep_index);
|
||||
|
@ -1992,11 +1997,16 @@ struct xhci_ring *xhci_stream_id_to_ring(
|
|||
unsigned int ep_index,
|
||||
unsigned int stream_id);
|
||||
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
||||
bool allocate_in_ctx, bool allocate_completion,
|
||||
gfp_t mem_flags);
|
||||
bool allocate_completion, gfp_t mem_flags);
|
||||
struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci,
|
||||
bool allocate_completion, gfp_t mem_flags);
|
||||
void xhci_urb_free_priv(struct urb_priv *urb_priv);
|
||||
void xhci_free_command(struct xhci_hcd *xhci,
|
||||
struct xhci_command *command);
|
||||
struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
int type, gfp_t flags);
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
|
||||
/* xHCI host controller glue */
|
||||
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
|
||||
|
@ -2070,6 +2080,8 @@ void xhci_handle_command_timeout(struct work_struct *work);
|
|||
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
|
||||
unsigned int ep_index, unsigned int stream_id);
|
||||
void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
|
||||
void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring);
|
||||
unsigned int count_trbs(u64 addr, u64 len);
|
||||
|
||||
/* xHCI roothub code */
|
||||
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||
|
|
|
@ -183,10 +183,10 @@ static int chaoskey_probe(struct usb_interface *interface,
|
|||
dev->in_ep = in_ep;
|
||||
|
||||
if (le16_to_cpu(udev->descriptor.idVendor) != ALEA_VENDOR_ID)
|
||||
dev->reads_started = 1;
|
||||
dev->reads_started = true;
|
||||
|
||||
dev->size = size;
|
||||
dev->present = 1;
|
||||
dev->present = true;
|
||||
|
||||
init_waitqueue_head(&dev->wait_q);
|
||||
|
||||
|
@ -239,7 +239,7 @@ static void chaoskey_disconnect(struct usb_interface *interface)
|
|||
usb_set_intfdata(interface, NULL);
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
dev->present = 0;
|
||||
dev->present = false;
|
||||
usb_poison_urb(dev->urb);
|
||||
|
||||
if (!dev->open) {
|
||||
|
|
|
@ -144,7 +144,7 @@ error:
|
|||
}
|
||||
|
||||
/* attribute callback handler (write) */
|
||||
static ssize_t set_port0_handler(struct device *dev,
|
||||
static ssize_t port0_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
@ -152,7 +152,7 @@ static ssize_t set_port0_handler(struct device *dev,
|
|||
}
|
||||
|
||||
/* attribute callback handler (write) */
|
||||
static ssize_t set_port1_handler(struct device *dev,
|
||||
static ssize_t port1_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
@ -178,22 +178,22 @@ static ssize_t read_port(struct device *dev, struct device_attribute *attr,
|
|||
}
|
||||
|
||||
/* attribute callback handler (read) */
|
||||
static ssize_t get_port0_handler(struct device *dev,
|
||||
static ssize_t port0_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0);
|
||||
}
|
||||
|
||||
/* attribute callback handler (read) */
|
||||
static ssize_t get_port1_handler(struct device *dev,
|
||||
static ssize_t port1_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR, get_port0_handler, set_port0_handler);
|
||||
static DEVICE_ATTR_RW(port0);
|
||||
|
||||
static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR, get_port1_handler, set_port1_handler);
|
||||
static DEVICE_ATTR_RW(port1);
|
||||
|
||||
|
||||
static int cypress_probe(struct usb_interface *interface,
|
||||
|
|
|
@ -78,7 +78,7 @@ static int vendor_command(struct usb_device *dev, unsigned char request,
|
|||
#define BRIGHTNESS 0x2c /* RAM location for brightness value */
|
||||
#define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */
|
||||
|
||||
static ssize_t show_brightness(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
|
||||
|
@ -86,7 +86,7 @@ static ssize_t show_brightness(struct device *dev, struct device_attribute *attr
|
|||
return sprintf(buf, "%i", cytherm->brightness);
|
||||
}
|
||||
|
||||
static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -121,15 +121,13 @@ static ssize_t set_brightness(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
show_brightness, set_brightness);
|
||||
static DEVICE_ATTR_RW(brightness);
|
||||
|
||||
|
||||
#define TEMP 0x33 /* RAM location for temperature */
|
||||
#define SIGN 0x34 /* RAM location for temperature sign */
|
||||
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -161,19 +159,12 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char
|
|||
return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1,
|
||||
5*(temp - ((temp >> 1) << 1)));
|
||||
}
|
||||
|
||||
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(temp, S_IRUGO, show_temp, set_temp);
|
||||
static DEVICE_ATTR_RO(temp);
|
||||
|
||||
|
||||
#define BUTTON 0x7a
|
||||
|
||||
static ssize_t show_button(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t button_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -200,17 +191,10 @@ static ssize_t show_button(struct device *dev, struct device_attribute *attr, ch
|
|||
else
|
||||
return sprintf(buf, "0");
|
||||
}
|
||||
static DEVICE_ATTR_RO(button);
|
||||
|
||||
|
||||
static ssize_t set_button(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(button, S_IRUGO, show_button, set_button);
|
||||
|
||||
|
||||
static ssize_t show_port0(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t port0_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
|
||||
|
@ -234,7 +218,7 @@ static ssize_t show_port0(struct device *dev, struct device_attribute *attr, cha
|
|||
}
|
||||
|
||||
|
||||
static ssize_t set_port0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
static ssize_t port0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
|
||||
|
@ -263,10 +247,9 @@ static ssize_t set_port0(struct device *dev, struct device_attribute *attr, cons
|
|||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(port0);
|
||||
|
||||
static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR | S_IWGRP, show_port0, set_port0);
|
||||
|
||||
static ssize_t show_port1(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t port1_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
|
||||
|
@ -290,7 +273,7 @@ static ssize_t show_port1(struct device *dev, struct device_attribute *attr, cha
|
|||
}
|
||||
|
||||
|
||||
static ssize_t set_port1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
static ssize_t port1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
|
||||
|
@ -319,9 +302,7 @@ static ssize_t set_port1(struct device *dev, struct device_attribute *attr, cons
|
|||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR | S_IWGRP, show_port1, set_port1);
|
||||
|
||||
static DEVICE_ATTR_RW(port1);
|
||||
|
||||
|
||||
static int cytherm_probe(struct usb_interface *interface,
|
||||
|
|
|
@ -30,7 +30,7 @@ struct trancevibrator {
|
|||
unsigned int speed;
|
||||
};
|
||||
|
||||
static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -39,7 +39,7 @@ static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
|
|||
return sprintf(buf, "%d\n", tv->speed);
|
||||
}
|
||||
|
||||
static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -70,7 +70,7 @@ static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR, show_speed, set_speed);
|
||||
static DEVICE_ATTR_RW(speed);
|
||||
|
||||
static int tv_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
|
|
|
@ -165,7 +165,7 @@ static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
|
|||
}
|
||||
|
||||
#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \
|
||||
static ssize_t show_attr_##name(struct device *dev, \
|
||||
static ssize_t name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_interface *intf = to_usb_interface(dev); \
|
||||
|
@ -174,7 +174,7 @@ static ssize_t show_attr_##name(struct device *dev, \
|
|||
return sprintf(buf, "%u\n", mydev->name); \
|
||||
} \
|
||||
\
|
||||
static ssize_t set_attr_##name(struct device *dev, \
|
||||
static ssize_t name##_store(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
struct usb_interface *intf = to_usb_interface(dev); \
|
||||
|
@ -185,9 +185,9 @@ static ssize_t set_attr_##name(struct device *dev, \
|
|||
\
|
||||
return count; \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_attr_##name, set_attr_##name);
|
||||
static DEVICE_ATTR_RW(name);
|
||||
|
||||
static ssize_t show_attr_text(struct device *dev,
|
||||
static ssize_t text_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -196,7 +196,7 @@ static ssize_t show_attr_text(struct device *dev,
|
|||
return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
|
||||
}
|
||||
|
||||
static ssize_t set_attr_text(struct device *dev,
|
||||
static ssize_t text_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -216,9 +216,9 @@ static ssize_t set_attr_text(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(text, S_IRUGO | S_IWUSR, show_attr_text, set_attr_text);
|
||||
static DEVICE_ATTR_RW(text);
|
||||
|
||||
static ssize_t show_attr_decimals(struct device *dev,
|
||||
static ssize_t decimals_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -240,7 +240,7 @@ static ssize_t show_attr_decimals(struct device *dev,
|
|||
return sizeof(mydev->decimals) + 1;
|
||||
}
|
||||
|
||||
static ssize_t set_attr_decimals(struct device *dev,
|
||||
static ssize_t decimals_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -265,9 +265,9 @@ static ssize_t set_attr_decimals(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(decimals, S_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals);
|
||||
static DEVICE_ATTR_RW(decimals);
|
||||
|
||||
static ssize_t show_attr_textmode(struct device *dev,
|
||||
static ssize_t textmode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -293,7 +293,7 @@ static ssize_t show_attr_textmode(struct device *dev,
|
|||
return strlen(buf);
|
||||
}
|
||||
|
||||
static ssize_t set_attr_textmode(struct device *dev,
|
||||
static ssize_t textmode_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
@ -309,7 +309,7 @@ static ssize_t set_attr_textmode(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode);
|
||||
static DEVICE_ATTR_RW(textmode);
|
||||
|
||||
|
||||
MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
|
||||
|
|
|
@ -1710,6 +1710,35 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int test_toggle_sync(struct usbtest_dev *tdev, int ep, struct urb *urb)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* clear initial data toggle to DATA0 */
|
||||
retval = usb_clear_halt(urb->dev, urb->pipe);
|
||||
if (retval < 0) {
|
||||
ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* transfer 3 data packets, should be DATA0, DATA1, DATA0 */
|
||||
retval = simple_io(tdev, urb, 1, 0, 0, __func__);
|
||||
if (retval != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* clear halt resets device side data toggle, host should react to it */
|
||||
retval = usb_clear_halt(urb->dev, urb->pipe);
|
||||
if (retval < 0) {
|
||||
ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* host should use DATA0 again after clear halt */
|
||||
retval = simple_io(tdev, urb, 1, 0, 0, __func__);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int halt_simple(struct usbtest_dev *dev)
|
||||
{
|
||||
int ep;
|
||||
|
@ -1742,6 +1771,33 @@ done:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int toggle_sync_simple(struct usbtest_dev *dev)
|
||||
{
|
||||
int ep;
|
||||
int retval = 0;
|
||||
struct urb *urb;
|
||||
struct usb_device *udev = testdev_to_usbdev(dev);
|
||||
unsigned maxp = get_maxpacket(udev, dev->out_pipe);
|
||||
|
||||
/*
|
||||
* Create a URB that causes a transfer of uneven amount of data packets
|
||||
* This way the clear toggle has an impact on the data toggle sequence.
|
||||
* Use 2 maxpacket length packets and one zero packet.
|
||||
*/
|
||||
urb = simple_alloc_urb(udev, 0, 2 * maxp, 0);
|
||||
if (urb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
urb->transfer_flags |= URB_ZERO_PACKET;
|
||||
|
||||
ep = usb_pipeendpoint(dev->out_pipe);
|
||||
urb->pipe = dev->out_pipe;
|
||||
retval = test_toggle_sync(dev, ep, urb);
|
||||
|
||||
simple_free_urb(urb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Control OUT tests use the vendor control requests from Intel's
|
||||
|
@ -2524,6 +2580,20 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param)
|
|||
retval = test_queue(dev, param,
|
||||
dev->in_pipe, NULL, 0);
|
||||
break;
|
||||
/* Test data Toggle/seq_nr clear between bulk out transfers */
|
||||
case 29:
|
||||
if (dev->out_pipe == 0)
|
||||
break;
|
||||
retval = 0;
|
||||
dev_info(&intf->dev, "TEST 29: Clear toggle between bulk writes %d times\n",
|
||||
param->iterations);
|
||||
for (i = param->iterations; retval == 0 && i > 0; --i)
|
||||
retval = toggle_sync_simple(dev);
|
||||
|
||||
if (retval)
|
||||
ERROR(dev, "toggle sync failed, iterations left %d\n",
|
||||
i);
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
|
|
@ -229,7 +229,10 @@ struct otg_switch_mtk {
|
|||
* @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to
|
||||
* disable u3port0, bit1==1 to disable u3port1,... etc
|
||||
* @dbgfs_root: only used when supports manual dual-role switch via debugfs
|
||||
* @wakeup_en: it's true when supports remote wakeup in host mode
|
||||
* @uwk_en: it's true when supports remote wakeup in host mode
|
||||
* @uwk: syscon including usb wakeup glue layer between SSUSB IP and SPM
|
||||
* @uwk_reg_base: the base address of the wakeup glue layer in @uwk
|
||||
* @uwk_vers: the version of the wakeup glue layer
|
||||
*/
|
||||
struct ssusb_mtk {
|
||||
struct device *dev;
|
||||
|
@ -253,8 +256,10 @@ struct ssusb_mtk {
|
|||
int u3p_dis_msk;
|
||||
struct dentry *dbgfs_root;
|
||||
/* usb wakeup for host mode */
|
||||
bool wakeup_en;
|
||||
struct regmap *pericfg;
|
||||
bool uwk_en;
|
||||
struct regmap *uwk;
|
||||
u32 uwk_reg_base;
|
||||
u32 uwk_vers;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,8 +18,7 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
|
|||
struct device_node *dn);
|
||||
int ssusb_host_enable(struct ssusb_mtk *ssusb);
|
||||
int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
|
||||
int ssusb_wakeup_enable(struct ssusb_mtk *ssusb);
|
||||
void ssusb_wakeup_disable(struct ssusb_mtk *ssusb);
|
||||
void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -49,12 +48,7 @@ static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
|
||||
static inline void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable)
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,66 +18,77 @@
|
|||
#include "mtu3.h"
|
||||
#include "mtu3_dr.h"
|
||||
|
||||
#define PERI_WK_CTRL1 0x404
|
||||
#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
|
||||
#define UWK_CTL1_IS_E BIT(25)
|
||||
#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
|
||||
#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
|
||||
#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
|
||||
#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
|
||||
/* mt8173 etc */
|
||||
#define PERI_WK_CTRL1 0x4
|
||||
#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
|
||||
#define WC1_IS_EN BIT(25)
|
||||
#define WC1_IS_P BIT(6) /* polarity for ip sleep */
|
||||
|
||||
/* mt2712 etc */
|
||||
#define PERI_SSUSB_SPM_CTRL 0x0
|
||||
#define SSC_IP_SLEEP_EN BIT(4)
|
||||
#define SSC_SPM_INT_EN BIT(1)
|
||||
|
||||
enum ssusb_uwk_vers {
|
||||
SSUSB_UWK_V1 = 1,
|
||||
SSUSB_UWK_V2,
|
||||
};
|
||||
|
||||
/*
|
||||
* ip-sleep wakeup mode:
|
||||
* all clocks can be turn off, but power domain should be kept on
|
||||
*/
|
||||
static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb)
|
||||
static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable)
|
||||
{
|
||||
u32 tmp;
|
||||
struct regmap *pericfg = ssusb->pericfg;
|
||||
u32 reg, msk, val;
|
||||
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_IS_P;
|
||||
tmp &= ~(UWK_CTL1_IS_C(0xf));
|
||||
tmp |= UWK_CTL1_IS_C(0x8);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
|
||||
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
|
||||
__func__, tmp);
|
||||
}
|
||||
|
||||
static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_IS_E;
|
||||
regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp);
|
||||
switch (ssusb->uwk_vers) {
|
||||
case SSUSB_UWK_V1:
|
||||
reg = ssusb->uwk_reg_base + PERI_WK_CTRL1;
|
||||
msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
|
||||
val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
|
||||
break;
|
||||
case SSUSB_UWK_V2:
|
||||
reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
|
||||
msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
|
||||
val = enable ? msk : 0;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
regmap_update_bits(ssusb->uwk, reg, msk, val);
|
||||
}
|
||||
|
||||
int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct device *dev = ssusb->dev;
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Wakeup function is optional, so it is not an error if this property
|
||||
* does not exist, and in such case, no need to get relative
|
||||
* properties anymore.
|
||||
*/
|
||||
ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup");
|
||||
if (!ssusb->wakeup_en)
|
||||
/* wakeup function is optional */
|
||||
ssusb->uwk_en = of_property_read_bool(dn, "wakeup-source");
|
||||
if (!ssusb->uwk_en)
|
||||
return 0;
|
||||
|
||||
ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,
|
||||
"mediatek,syscon-wakeup");
|
||||
if (IS_ERR(ssusb->pericfg)) {
|
||||
dev_err(dev, "fail to get pericfg regs\n");
|
||||
return PTR_ERR(ssusb->pericfg);
|
||||
}
|
||||
ret = of_parse_phandle_with_fixed_args(dn,
|
||||
"mediatek,syscon-wakeup", 2, 0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
ssusb->uwk_reg_base = args.args[0];
|
||||
ssusb->uwk_vers = args.args[1];
|
||||
ssusb->uwk = syscon_node_to_regmap(args.np);
|
||||
of_node_put(args.np);
|
||||
dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n",
|
||||
ssusb->uwk_reg_base, ssusb->uwk_vers);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ssusb->uwk);
|
||||
}
|
||||
|
||||
void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable)
|
||||
{
|
||||
if (ssusb->uwk_en)
|
||||
ssusb_wakeup_ip_sleep_set(ssusb, enable);
|
||||
}
|
||||
|
||||
static void host_ports_num_get(struct ssusb_mtk *ssusb)
|
||||
|
@ -235,17 +246,3 @@ void ssusb_host_exit(struct ssusb_mtk *ssusb)
|
|||
of_platform_depopulate(ssusb->dev);
|
||||
ssusb_host_cleanup(ssusb);
|
||||
}
|
||||
|
||||
int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
if (ssusb->wakeup_en)
|
||||
ssusb_wakeup_ip_sleep_en(ssusb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
if (ssusb->wakeup_en)
|
||||
ssusb_wakeup_ip_sleep_dis(ssusb);
|
||||
}
|
||||
|
|
|
@ -282,8 +282,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
|
|||
|
||||
/* if host role is supported */
|
||||
ret = ssusb_wakeup_of_property_parse(ssusb, node);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to parse uwk property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* optional property, ignore the error if it does not exist */
|
||||
of_property_read_u32(node, "mediatek,u3p-dis-msk",
|
||||
|
@ -308,7 +310,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
|
|||
otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
|
||||
if (IS_ERR(otg_sx->edev)) {
|
||||
dev_err(ssusb->dev, "couldn't get extcon device\n");
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(otg_sx->edev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,7 +459,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)
|
|||
ssusb_host_disable(ssusb, true);
|
||||
ssusb_phy_power_off(ssusb);
|
||||
ssusb_clks_disable(ssusb);
|
||||
ssusb_wakeup_enable(ssusb);
|
||||
ssusb_wakeup_set(ssusb, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -473,7 +475,7 @@ static int __maybe_unused mtu3_resume(struct device *dev)
|
|||
if (!ssusb->is_host)
|
||||
return 0;
|
||||
|
||||
ssusb_wakeup_disable(ssusb);
|
||||
ssusb_wakeup_set(ssusb, false);
|
||||
ret = ssusb_clks_enable(ssusb);
|
||||
if (ret)
|
||||
goto clks_err;
|
||||
|
|
|
@ -520,7 +520,7 @@ static int da8xx_probe(struct platform_device *pdev)
|
|||
if (!glue)
|
||||
return -ENOMEM;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, "usb20");
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
return PTR_ERR(clk);
|
||||
|
|
|
@ -1687,7 +1687,7 @@ EXPORT_SYMBOL_GPL(musb_mailbox);
|
|||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static ssize_t
|
||||
musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
unsigned long flags;
|
||||
|
@ -1701,7 +1701,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
musb_mode_store(struct device *dev, struct device_attribute *attr,
|
||||
mode_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
|
@ -1721,10 +1721,10 @@ musb_mode_store(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return (status == 0) ? n : status;
|
||||
}
|
||||
static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store);
|
||||
static DEVICE_ATTR_RW(mode);
|
||||
|
||||
static ssize_t
|
||||
musb_vbus_store(struct device *dev, struct device_attribute *attr,
|
||||
vbus_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
|
@ -1748,7 +1748,7 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
unsigned long flags;
|
||||
|
@ -1773,13 +1773,12 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
return sprintf(buf, "Vbus %s, timeout %lu msec\n",
|
||||
vbus ? "on" : "off", val);
|
||||
}
|
||||
static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
|
||||
static DEVICE_ATTR_RW(vbus);
|
||||
|
||||
/* Gadget drivers can't know that a host is connected so they might want
|
||||
* to start SRP, but users can. This allows userspace to trigger SRP.
|
||||
*/
|
||||
static ssize_t
|
||||
musb_srp_store(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t srp_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
|
@ -1796,7 +1795,7 @@ musb_srp_store(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
|
||||
static DEVICE_ATTR_WO(srp);
|
||||
|
||||
static struct attribute *musb_attributes[] = {
|
||||
&dev_attr_mode.attr,
|
||||
|
|
|
@ -195,7 +195,6 @@ static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in)
|
|||
static void
|
||||
musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
{
|
||||
u16 frame;
|
||||
u32 len;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
struct urb *urb = next_urb(qh);
|
||||
|
@ -244,7 +243,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
|||
case USB_ENDPOINT_XFER_ISOC:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
musb_dbg(musb, "check whether there's still time for periodic Tx");
|
||||
frame = musb_readw(mbase, MUSB_FRAME);
|
||||
/* FIXME this doesn't implement that scheduling policy ...
|
||||
* or handle framecounter wrapping
|
||||
*/
|
||||
|
@ -1781,7 +1779,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
|||
struct musb_qh *qh = hw_ep->in_qh;
|
||||
size_t xfer_len;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
int pipe;
|
||||
u16 rx_csr, val;
|
||||
bool iso_err = false;
|
||||
bool done = false;
|
||||
|
@ -1810,8 +1807,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
|||
return;
|
||||
}
|
||||
|
||||
pipe = urb->pipe;
|
||||
|
||||
trace_musb_urb_rx(musb, urb);
|
||||
|
||||
/* check for errors, concurrent stall & unlink is not really
|
||||
|
|
|
@ -519,7 +519,7 @@ static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
|
@ -527,7 +527,7 @@ get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
||||
a_bus_req_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
|
@ -559,11 +559,10 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req,
|
||||
set_a_bus_req);
|
||||
static DEVICE_ATTR_RW(a_bus_req);
|
||||
|
||||
static ssize_t
|
||||
set_a_clr_err(struct device *dev, struct device_attribute *attr,
|
||||
a_clr_err_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
|
@ -587,10 +586,10 @@ set_a_clr_err(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
|
||||
static DEVICE_ATTR_WO(a_clr_err);
|
||||
|
||||
static ssize_t
|
||||
get_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
||||
a_bus_drop_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
|
@ -599,7 +598,7 @@ get_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
set_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
||||
a_bus_drop_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
|
@ -630,8 +629,7 @@ set_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR,
|
||||
get_a_bus_drop, set_a_bus_drop);
|
||||
static DEVICE_ATTR_RW(a_bus_drop);
|
||||
|
||||
static struct attribute *inputs_attrs[] = {
|
||||
&dev_attr_a_bus_req.attr,
|
||||
|
|
|
@ -59,13 +59,13 @@ static const unsigned int tahvo_cable[] = {
|
|||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static ssize_t vbus_state_show(struct device *device,
|
||||
static ssize_t vbus_show(struct device *device,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tahvo_usb *tu = dev_get_drvdata(device);
|
||||
return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
|
||||
}
|
||||
static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL);
|
||||
static DEVICE_ATTR_RO(vbus);
|
||||
|
||||
static void check_vbus_state(struct tahvo_usb *tu)
|
||||
{
|
||||
|
@ -310,7 +310,7 @@ static ssize_t otg_mode_store(struct device *device,
|
|||
|
||||
return r;
|
||||
}
|
||||
static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store);
|
||||
static DEVICE_ATTR_RW(otg_mode);
|
||||
|
||||
static struct attribute *tahvo_attributes[] = {
|
||||
&dev_attr_vbus.attr,
|
||||
|
|
|
@ -168,7 +168,7 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t twl6030_usb_vbus_show(struct device *dev,
|
||||
static ssize_t vbus_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct twl6030_usb *twl = dev_get_drvdata(dev);
|
||||
|
@ -194,7 +194,7 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev,
|
|||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
|
||||
static DEVICE_ATTR_RO(vbus);
|
||||
|
||||
static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
|
||||
{
|
||||
|
|
|
@ -323,6 +323,14 @@ static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
|
|||
return *phy == match_data;
|
||||
}
|
||||
|
||||
static void usb_charger_init(struct usb_phy *usb_phy)
|
||||
{
|
||||
usb_phy->chg_type = UNKNOWN_TYPE;
|
||||
usb_phy->chg_state = USB_CHARGER_DEFAULT;
|
||||
usb_phy_set_default_current(usb_phy);
|
||||
INIT_WORK(&usb_phy->chg_work, usb_phy_notify_charger_work);
|
||||
}
|
||||
|
||||
static int usb_add_extcon(struct usb_phy *x)
|
||||
{
|
||||
int ret;
|
||||
|
@ -406,10 +414,6 @@ static int usb_add_extcon(struct usb_phy *x)
|
|||
}
|
||||
}
|
||||
|
||||
usb_phy_set_default_current(x);
|
||||
INIT_WORK(&x->chg_work, usb_phy_notify_charger_work);
|
||||
x->chg_type = UNKNOWN_TYPE;
|
||||
x->chg_state = USB_CHARGER_DEFAULT;
|
||||
if (x->type_nb.notifier_call)
|
||||
__usb_phy_get_charger_type(x);
|
||||
|
||||
|
@ -704,6 +708,7 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
usb_charger_init(x);
|
||||
ret = usb_add_extcon(x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -749,6 +754,7 @@ int usb_add_phy_dev(struct usb_phy *x)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
usb_charger_init(x);
|
||||
ret = usb_add_extcon(x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
|
||||
|
||||
renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o
|
||||
renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o rza.o
|
||||
|
||||
ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),)
|
||||
renesas_usbhs-y += mod_host.o
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "common.h"
|
||||
#include "rcar2.h"
|
||||
#include "rcar3.h"
|
||||
#include "rza.h"
|
||||
|
||||
/*
|
||||
* image of renesas_usbhs
|
||||
|
@ -488,6 +489,10 @@ static const struct of_device_id usbhs_of_match[] = {
|
|||
.compatible = "renesas,rcar-gen3-usbhs",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN3,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rza1-usbhs",
|
||||
.data = (void *)USBHS_TYPE_RZA1,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usbhs_of_match);
|
||||
|
@ -520,6 +525,11 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
|
|||
dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
|
||||
}
|
||||
|
||||
if (dparam->type == USBHS_TYPE_RZA1) {
|
||||
dparam->pipe_configs = usbhsc_new_pipe;
|
||||
dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -581,6 +591,18 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
break;
|
||||
case USBHS_TYPE_RCAR_GEN3_WITH_PLL:
|
||||
priv->pfunc = usbhs_rcar3_with_pll_ops;
|
||||
if (!IS_ERR_OR_NULL(priv->edev)) {
|
||||
priv->nb.notifier_call = priv->pfunc.notifier;
|
||||
ret = devm_extcon_register_notifier(&pdev->dev,
|
||||
priv->edev,
|
||||
EXTCON_USB_HOST,
|
||||
&priv->nb);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "no notifier registered\n");
|
||||
}
|
||||
break;
|
||||
case USBHS_TYPE_RZA1:
|
||||
priv->pfunc = usbhs_rza1_ops;
|
||||
break;
|
||||
default:
|
||||
if (!info->platform_callback.get_id) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue