USB / Thunderbolt changes for 6.8-rc1

Here is the big set of USB and Thunderbolt changes for 6.8-rc1.
 Included in here are the following:
   - Thunderbolt subsystem and driver updates for USB 4 hardware and
     issues reported by real devices
   - xhci driver updates
   - dwc3 driver updates
   - uvc_video gadget driver updates
   - typec driver updates
   - gadget string functions cleaned up
   - other small changes
 
 All of these have been in the linux-next tree for a while with no
 reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZaedng8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yndHACfX3SA2ipK5umpMsWOoLMCBV6VyrwAn3t+FPd/
 z4mNiCuNUhbEnU7RinK0
 =k/E9
 -----END PGP SIGNATURE-----

Merge tag 'usb-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB / Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt changes for 6.8-rc1.
  Included in here are the following:

   - Thunderbolt subsystem and driver updates for USB 4 hardware and
     issues reported by real devices

   - xhci driver updates

   - dwc3 driver updates

   - uvc_video gadget driver updates

   - typec driver updates

   - gadget string functions cleaned up

   - other small changes

  All of these have been in the linux-next tree for a while with no
  reported issues"

* tag 'usb-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (169 commits)
  usb: typec: tipd: fix use of device-specific init function
  usb: typec: tipd: Separate reset for TPS6598x
  usb: mon: Fix atomicity violation in mon_bin_vma_fault
  usb: gadget: uvc: Remove nested locking
  usb: gadget: uvc: Fix use are free during STREAMOFF
  usb: typec: class: fix typec_altmode_put_partner to put plugs
  dt-bindings: usb: dwc3: Limit num-hc-interrupters definition
  dt-bindings: usb: xhci: Add num-hc-interrupters definition
  xhci: add support to allocate several interrupters
  USB: core: Use device_driver directly in struct usb_driver and usb_device_driver
  arm64: dts: mediatek: mt8195: Add 'rx-fifo-depth' for cherry
  usb: xhci-mtk: fix a short packet issue of gen1 isoc-in transfer
  dt-bindings: usb: mtk-xhci: add a property for Gen1 isoc-in transfer issue
  arm64: dts: qcom: msm8996: Remove PNoC clock from MSS
  arm64: dts: qcom: msm8996: Remove AGGRE2 clock from SLPI
  arm64: dts: qcom: msm8998: Remove AGGRE2 clock from SLPI
  arm64: dts: qcom: msm8939: Drop RPM bus clocks
  arm64: dts: qcom: sdm630: Drop RPM bus clocks
  arm64: dts: qcom: qcs404: Drop RPM bus clocks
  arm64: dts: qcom: msm8996: Drop RPM bus clocks
  ...
This commit is contained in:
Linus Torvalds 2024-01-18 11:43:55 -08:00
commit 8c94ccc7cd
142 changed files with 3945 additions and 1549 deletions

View File

@ -6933,6 +6933,9 @@
pause after every control message);
o = USB_QUIRK_HUB_SLOW_RESET (Hub needs extra
delay after resetting its port);
p = USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT
(Reduce timeout of the SET_ADDRESS
request from 5000 ms to 500 ms);
Example: quirks=0781:5580:bk,0a5c:5834:gij
usbhid.mousepoll=

View File

@ -66,7 +66,6 @@ properties:
Particularly, if use an output GPIO to control a VBUS regulator, should
model it as a regulator. See bindings/regulator/fixed-regulator.yaml
# The following are optional properties for "usb-c-connector".
power-role:
description: Determines the power role that the Type C connector will
support. "dual" refers to Dual Role Port (DRP).
@ -119,30 +118,6 @@ properties:
# The following are optional properties for "usb-c-connector" with power
# delivery support.
source-pdos:
description: An array of u32 with each entry providing supported power
source data object(PDO), the detailed bit definitions of PDO can be found
in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2
Source_Capabilities Message, the order of each entry(PDO) should follow
the PD spec chapter 6.4.1. Required for power source and power dual role.
User can specify the source PDO array via PDO_FIXED/BATT/VAR/PPS_APDO()
defined in dt-bindings/usb/pd.h.
minItems: 1
maxItems: 7
$ref: /schemas/types.yaml#/definitions/uint32-array
sink-pdos:
description: An array of u32 with each entry providing supported power sink
data object(PDO), the detailed bit definitions of PDO can be found in
"Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3
Sink Capabilities Message, the order of each entry(PDO) should follow the
PD spec chapter 6.4.1. Required for power sink and power dual role. User
can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined
in dt-bindings/usb/pd.h.
minItems: 1
maxItems: 7
$ref: /schemas/types.yaml#/definitions/uint32-array
sink-vdos:
description: An array of u32 with each entry, a Vendor Defined Message Object (VDO),
providing additional information corresponding to the product, the detailed bit
@ -166,10 +141,43 @@ properties:
maxItems: 6
$ref: /schemas/types.yaml#/definitions/uint32-array
op-sink-microwatt:
description: Sink required operating power in microwatt, if source can't
offer the power, Capability Mismatch is set. Required for power sink and
power dual role.
accessory-mode-audio:
type: boolean
description: Whether the device supports Audio Adapter Accessory Mode. This
is only necessary if there are no other means to discover supported
alternative modes (e.g. through the UCSI firmware interface).
accessory-mode-debug:
type: boolean
description: Whether the device supports Debug Accessory Mode. This
is only necessary if there are no other means to discover supported
alternative modes (e.g. through the UCSI firmware interface).
altmodes:
type: object
description: List of Alternative Modes supported by the schematics on the
particular device. This is only necessary if there are no other means to
discover supported alternative modes (e.g. through the UCSI firmware
interface).
additionalProperties: false
patternProperties:
"^(displayport)$":
type: object
description:
A single USB-C Alternative Mode as supported by the USB-C connector logic.
additionalProperties: false
properties:
svid:
$ref: /schemas/types.yaml#/definitions/uint16
description: Unique value assigned by USB-IF to the Vendor / AltMode.
enum: [ 0xff01 ]
vdo:
$ref: /schemas/types.yaml#/definitions/uint32
description: VDO returned by Discover Modes USB PD command.
port:
$ref: /schemas/graph.yaml#/properties/port
@ -231,6 +239,20 @@ properties:
SNK_READY for non-pd link.
type: boolean
capabilities:
description: A child node to contain all the selectable USB Power Delivery capabilities.
type: object
patternProperties:
"^caps-[0-9]+$":
description: Child nodes under "capabilities" node. Each node contains a selectable USB
Power Delivery capability.
type: object
$ref: "#/$defs/capabilities"
unevaluatedProperties: false
additionalProperties: false
dependencies:
sink-vdos-v1: [ sink-vdos ]
sink-vdos: [ sink-vdos-v1 ]
@ -238,7 +260,42 @@ dependencies:
required:
- compatible
$defs:
capabilities:
type: object
properties:
source-pdos:
description: An array of u32 with each entry providing supported power
source data object(PDO), the detailed bit definitions of PDO can be found
in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2
Source_Capabilities Message, the order of each entry(PDO) should follow
the PD spec chapter 6.4.1. Required for power source and power dual role.
User can specify the source PDO array via PDO_FIXED/BATT/VAR/PPS_APDO()
defined in dt-bindings/usb/pd.h.
minItems: 1
maxItems: 7
$ref: /schemas/types.yaml#/definitions/uint32-array
sink-pdos:
description: An array of u32 with each entry providing supported power sink
data object(PDO), the detailed bit definitions of PDO can be found in
"Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3
Sink Capabilities Message, the order of each entry(PDO) should follow the
PD spec chapter 6.4.1. Required for power sink and power dual role. User
can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined
in dt-bindings/usb/pd.h.
minItems: 1
maxItems: 7
$ref: /schemas/types.yaml#/definitions/uint32-array
op-sink-microwatt:
description: Sink required operating power in microwatt, if source can't
offer the power, Capability Mismatch is set. Required for power sink and
power dual role.
allOf:
- $ref: "#/$defs/capabilities"
- if:
properties:
compatible:
@ -267,7 +324,7 @@ anyOf:
- typec-power-opmode
- new-source-frs-typec-current
additionalProperties: false
unevaluatedProperties: false
examples:
# Micro-USB connector with HS lines routed via controller (MUIC).
@ -289,6 +346,13 @@ examples:
compatible = "usb-c-connector";
label = "USB-C";
altmodes {
displayport {
svid = /bits/ 16 <0xff01>;
vdo = <0x00001c46>;
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -9,9 +9,6 @@ title: USB xHCI Controller
maintainers:
- Mathias Nyman <mathias.nyman@intel.com>
allOf:
- $ref: usb-xhci.yaml#
properties:
compatible:
oneOf:
@ -25,6 +22,11 @@ properties:
- marvell,armada-380-xhci
- marvell,armada-8k-xhci
- const: generic-xhci
- description: Broadcom SoCs with power domains
items:
- enum:
- brcm,bcm2711-xhci
- const: brcm,xhci-brcm-v2
- description: Broadcom STB SoCs with xHCI
enum:
- brcm,xhci-brcm-v2
@ -49,6 +51,9 @@ properties:
- const: core
- const: reg
power-domains:
maxItems: 1
unevaluatedProperties: false
required:
@ -56,6 +61,20 @@ required:
- reg
- interrupts
allOf:
- $ref: usb-xhci.yaml#
- if:
properties:
compatible:
contains:
const: brcm,bcm2711-xhci
then:
required:
- power-domains
else:
properties:
power-domains: false
examples:
- |
usb@f0931000 {

View File

@ -29,6 +29,11 @@ properties:
description:
the regulator that provides 3.3V core power to the hub.
peer-hub:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to the peer hub on the controller.
required:
- compatible
- reg

View File

@ -124,6 +124,17 @@ properties:
defined in the xHCI spec on MTK's controller.
default: 5000
rx-fifo-depth:
$ref: /schemas/types.yaml#/definitions/uint32
description:
It is a quirk used to work around Gen1 isoc-in endpoint transfer issue
that still send out unexpected ACK after device finishes the burst
transfer with a short packet and cause an exception, specially on a 4K
camera device, it happens on controller before about IPM v1.6.0;
the side-effect is that it may cause performance drop about 10%,
including bulk transfer, prefer to use 3k here. The size is in bytes.
enum: [1024, 2048, 3072, 4096]
# the following properties are only used for case 1
wakeup-source:
description: enable USB remote wakeup, see power/wakeup-source.txt

View File

@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/usb/nxp,ptn5110.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PTN5110 Typec Port Cotroller
title: NXP PTN5110 Type-C Port Controller
maintainers:
- Li Jun <jun.li@nxp.com>

View File

@ -46,6 +46,8 @@ properties:
- qcom,sm8350-dwc3
- qcom,sm8450-dwc3
- qcom,sm8550-dwc3
- qcom,sm8650-dwc3
- qcom,x1e80100-dwc3
- const: qcom,dwc3
reg:
@ -97,12 +99,29 @@ properties:
- const: apps-usb
interrupts:
minItems: 1
maxItems: 4
description: |
Different types of interrupts are used based on HS PHY used on target:
- pwr_event: Used for wakeup based on other power events.
- hs_phY_irq: Apart from DP/DM/QUSB2 PHY interrupts, there is
hs_phy_irq which is not triggered by default and its
functionality is mutually exclusive to that of
{dp/dm}_hs_phy_irq and qusb2_phy_irq.
- qusb2_phy: SoCs with QUSB2 PHY do not have separate DP/DM IRQs and
expose only a single IRQ whose behavior can be modified
by the QUSB2PHY_INTR_CTRL register. The required DPSE/
DMSE configuration is done in QUSB2PHY_INTR_CTRL register
of PHY address space.
- {dp/dm}_hs_phy_irq: These IRQ's directly reflect changes on the DP/
DM pads of the SoC. These are used for wakeup
only on SoCs with non-QUSB2 targets with
exception of SDM670/SDM845/SM6350.
- ss_phy_irq: Used for remote wakeup in Super Speed mode of operation.
minItems: 2
maxItems: 5
interrupt-names:
minItems: 1
maxItems: 4
minItems: 2
maxItems: 5
qcom,select-utmi-as-pipe-clk:
description:
@ -263,6 +282,7 @@ allOf:
contains:
enum:
- qcom,sc8280xp-dwc3
- qcom,x1e80100-dwc3
then:
properties:
clocks:
@ -288,8 +308,8 @@ allOf:
then:
properties:
clocks:
minItems: 5
maxItems: 6
minItems: 4
maxItems: 5
clock-names:
oneOf:
- items:
@ -298,13 +318,11 @@ allOf:
- const: iface
- const: sleep
- const: mock_utmi
- const: bus
- items:
- const: cfg_noc
- const: core
- const: sleep
- const: mock_utmi
- const: bus
- if:
properties:
@ -318,6 +336,7 @@ allOf:
- qcom,sm8250-dwc3
- qcom,sm8450-dwc3
- qcom,sm8550-dwc3
- qcom,sm8650-dwc3
then:
properties:
clocks:
@ -357,131 +376,96 @@ allOf:
compatible:
contains:
enum:
- qcom,ipq4019-dwc3
- qcom,ipq5018-dwc3
- qcom,ipq6018-dwc3
- qcom,ipq8064-dwc3
- qcom,ipq8074-dwc3
- qcom,msm8994-dwc3
- qcom,msm8953-dwc3
- qcom,msm8998-dwc3
then:
properties:
interrupts:
minItems: 2
maxItems: 3
interrupt-names:
items:
- const: pwr_event
- const: qusb2_phy
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8996-dwc3
- qcom,qcs404-dwc3
- qcom,sdm660-dwc3
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3
then:
properties:
interrupts:
minItems: 3
maxItems: 4
interrupt-names:
items:
- const: pwr_event
- const: qusb2_phy
- const: hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,ipq5332-dwc3
- qcom,x1e80100-dwc3
then:
properties:
interrupts:
maxItems: 4
interrupt-names:
items:
- const: pwr_event
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,ipq4019-dwc3
- qcom,ipq8064-dwc3
- qcom,msm8994-dwc3
- qcom,sa8775p-dwc3
- qcom,sc7180-dwc3
- qcom,sc7280-dwc3
- qcom,sc8280xp-dwc3
- qcom,sdm670-dwc3
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
- qcom,sdx65-dwc3
- qcom,sdx75-dwc3
- qcom,sm4250-dwc3
- qcom,sm6125-dwc3
- qcom,sm6350-dwc3
- qcom,sm8150-dwc3
- qcom,sm8250-dwc3
- qcom,sm8350-dwc3
- qcom,sm8450-dwc3
- qcom,sm8550-dwc3
- qcom,sm8650-dwc3
then:
properties:
interrupts:
items:
- description: The interrupt that is asserted
when a wakeup event is received on USB2 bus.
- description: The interrupt that is asserted
when a wakeup event is received on USB3 bus.
- description: Wakeup event on DM line.
- description: Wakeup event on DP line.
interrupt-names:
items:
- const: hs_phy_irq
- const: ss_phy_irq
- const: dm_hs_phy_irq
- const: dp_hs_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8953-dwc3
- qcom,msm8996-dwc3
- qcom,msm8998-dwc3
- qcom,sm6115-dwc3
then:
properties:
interrupts:
maxItems: 2
interrupt-names:
items:
- const: hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,sdm660-dwc3
then:
properties:
interrupts:
minItems: 1
maxItems: 2
interrupt-names:
minItems: 1
items:
- const: hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,sc7280-dwc3
then:
properties:
interrupts:
minItems: 3
maxItems: 4
interrupt-names:
minItems: 3
items:
- const: hs_phy_irq
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,sc8280xp-dwc3
then:
properties:
interrupts:
maxItems: 4
minItems: 4
maxItems: 5
interrupt-names:
items:
- const: pwr_event
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,sa8775p-dwc3
then:
properties:
interrupts:
minItems: 3
maxItems: 4
interrupt-names:
minItems: 3
items:
- const: pwr_event
- const: hs_phy_irq
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- const: ss_phy_irq
@ -519,12 +503,13 @@ examples:
<&gcc GCC_USB30_PRIM_MASTER_CLK>;
assigned-clock-rates = <19200000>, <150000000>;
interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 486 IRQ_TYPE_LEVEL_HIGH>,
interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 489 IRQ_TYPE_EDGE_BOTH>,
<GIC_SPI 488 IRQ_TYPE_EDGE_BOTH>,
<GIC_SPI 489 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "hs_phy_irq", "ss_phy_irq",
"dm_hs_phy_irq", "dp_hs_phy_irq";
<GIC_SPI 486 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "pwr_event", "hs_phy_irq",
"dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq";
power-domains = <&gcc USB30_PRIM_GDSC>;

View File

@ -0,0 +1,102 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/qcom,wcd939x-usbss.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm WCD9380/WCD9385 USB SubSystem Altmode/Analog Audio Switch
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
Qualcomm WCD9390/WCD9395 is a standalone Hi-Fi audio codec IC with a
functionally separate USB SubSystem for Altmode/Analog Audio Switch
accessible over an I2C interface.
The Audio Headphone and Microphone data path between the Codec and the
USB-C Mux subsystems are external to the IC, thus requiring DT port-endpoint
graph description to handle USB-C altmode & orientation switching for Audio
Accessory Mode.
properties:
compatible:
oneOf:
- const: qcom,wcd9390-usbss
- items:
- const: qcom,wcd9395-usbss
- const: qcom,wcd9390-usbss
reg:
maxItems: 1
reset-gpios:
maxItems: 1
vdd-supply:
description: USBSS VDD power supply
mode-switch:
description: Flag the port as possible handle of altmode switching
type: boolean
orientation-switch:
description: Flag the port as possible handler of orientation switching
type: boolean
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description:
A port node to link the WCD939x USB SubSystem to a TypeC controller for the
purpose of handling altmode muxing and orientation switching.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description:
A port node to link the WCD939x USB SubSystem to the Codec SubSystem for the
purpose of handling USB-C Audio Accessory Mode muxing and orientation switching.
required:
- compatible
- reg
- ports
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
typec-mux@42 {
compatible = "qcom,wcd9390-usbss";
reg = <0x42>;
vdd-supply = <&vreg_bob>;
mode-switch;
orientation-switch;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
wcd9390_usbss_sbu: endpoint {
remote-endpoint = <&typec_sbu>;
};
};
port@1 {
reg = <1>;
wcd9390_usbss_codec: endpoint {
remote-endpoint = <&wcd9390_codec_usbss>;
};
};
};
};
};
...

View File

@ -19,7 +19,7 @@ properties:
- items:
- enum:
- renesas,usbhs-r7s9210 # RZ/A2
- renesas,usbhs-r9a07g043 # RZ/G2UL
- renesas,usbhs-r9a07g043 # RZ/G2UL and RZ/Five
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
- renesas,usbhs-r9a07g054 # RZ/V2L
- const: renesas,rza2-usbhs

View File

@ -432,6 +432,10 @@ properties:
items:
enum: [1, 4, 8, 16, 32, 64, 128, 256]
num-hc-interrupters:
maximum: 8
default: 1
port:
$ref: /schemas/graph.yaml#/properties/port
description:

View File

@ -38,6 +38,10 @@ properties:
- const: main
- const: patch-address
reset-gpios:
description: GPIO used for the HRESET pin.
maxItems: 1
wakeup-source: true
interrupts:
@ -90,6 +94,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -106,6 +111,7 @@ examples:
pinctrl-names = "default";
pinctrl-0 = <&typec_pins>;
reset-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
typec_con: connector {
compatible = "usb-c-connector";

View File

@ -29,6 +29,12 @@ properties:
description: Interrupt moderation interval
default: 5000
num-hc-interrupters:
description: Maximum number of interrupters to allocate
$ref: /schemas/types.yaml#/definitions/uint16
minimum: 1
maximum: 1024
additionalProperties: true
examples:

View File

@ -448,15 +448,17 @@ Function-specific configfs interface
The function name to use when creating the function directory is "ncm".
The NCM function provides these attributes in its function directory:
=============== ==================================================
ifname network device interface name associated with this
function instance
qmult queue length multiplier for high and super speed
host_addr MAC address of host's end of this
Ethernet over USB link
dev_addr MAC address of device's end of this
Ethernet over USB link
=============== ==================================================
=============== ==================================================
ifname network device interface name associated with this
function instance
qmult queue length multiplier for high and super speed
host_addr MAC address of host's end of this
Ethernet over USB link
dev_addr MAC address of device's end of this
Ethernet over USB link
max_segment_size Segment size required for P2P connections. This
will set MTU to (max_segment_size - 14 bytes)
=============== ==================================================
and after creating the functions/ncm.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected.

View File

@ -81,9 +81,6 @@ feature must be kept in the implementation.
Potential future improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Report more events (suspend, resume, etc.) through
``USB_RAW_IOCTL_EVENT_FETCH``.
- Support ``O_NONBLOCK`` I/O. This would be another mode of operation, where
Raw Gadget would not wait until the completion of each USB request.

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "bcm2835-rpi.dtsi"
#include <dt-bindings/power/raspberrypi-power.h>
#include <dt-bindings/reset/raspberrypi,firmware-reset.h>
/ {
@ -76,3 +77,7 @@
&vchiq {
interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
};
&xhci {
power-domains = <&power RPI_POWER_DOMAIN_USB>;
};

View File

@ -604,6 +604,20 @@
};
};
xhci: usb@7e9c0000 {
compatible = "brcm,bcm2711-xhci", "brcm,xhci-brcm-v2";
reg = <0x0 0x7e9c0000 0x100000>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
/* DWC2 and this IP block share the same USB PHY,
* enabling both at the same time results in lockups.
* So keep this node disabled and let the bootloader
* decide which interface should be enabled.
*/
status = "disabled";
};
v3d: gpu@7ec00000 {
compatible = "brcm,2711-v3d";
reg = <0x0 0x7ec00000 0x4000>,

View File

@ -1294,6 +1294,7 @@
&xhci0 {
status = "okay";
rx-fifo-depth = <3072>;
vusb33-supply = <&mt6359_vusb_ldo_reg>;
vbus-supply = <&usb_vbus>;
};
@ -1301,6 +1302,7 @@
&xhci1 {
status = "okay";
rx-fifo-depth = <3072>;
vusb33-supply = <&mt6359_vusb_ldo_reg>;
vbus-supply = <&usb_vbus>;
};

View File

@ -540,9 +540,6 @@
compatible = "qcom,msm8916-bimc";
reg = <0x00400000 0x62000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
};
tsens: thermal-sensor@4a9000 {
@ -575,18 +572,12 @@
compatible = "qcom,msm8916-pcnoc";
reg = <0x00500000 0x11000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_PCNOC_CLK>,
<&rpmcc RPM_SMD_PCNOC_A_CLK>;
};
snoc: interconnect@580000 {
compatible = "qcom,msm8916-snoc";
reg = <0x00580000 0x14000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
<&rpmcc RPM_SMD_SNOC_A_CLK>;
};
stm: stm@802000 {

View File

@ -602,9 +602,6 @@
bimc: interconnect@400000 {
compatible = "qcom,msm8939-bimc";
reg = <0x00400000 0x62000>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
#interconnect-cells = <1>;
};
@ -648,25 +645,16 @@
pcnoc: interconnect@500000 {
compatible = "qcom,msm8939-pcnoc";
reg = <0x00500000 0x11000>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_PCNOC_CLK>,
<&rpmcc RPM_SMD_PCNOC_A_CLK>;
#interconnect-cells = <1>;
};
snoc: interconnect@580000 {
compatible = "qcom,msm8939-snoc";
reg = <0x00580000 0x14080>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
<&rpmcc RPM_SMD_SNOC_A_CLK>;
#interconnect-cells = <1>;
snoc_mm: interconnect-snoc {
compatible = "qcom,msm8939-snoc-mm";
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SYSMMNOC_CLK>,
<&rpmcc RPM_SMD_SYSMMNOC_A_CLK>;
#interconnect-cells = <1>;
};
};

View File

@ -838,9 +838,6 @@
compatible = "qcom,msm8996-bimc";
reg = <0x00408000 0x5a000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
};
tsens0: thermal-sensor@4a9000 {
@ -891,18 +888,12 @@
compatible = "qcom,msm8996-cnoc";
reg = <0x00500000 0x1000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_CNOC_CLK>,
<&rpmcc RPM_SMD_CNOC_A_CLK>;
};
snoc: interconnect@524000 {
compatible = "qcom,msm8996-snoc";
reg = <0x00524000 0x1c000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
<&rpmcc RPM_SMD_SNOC_A_CLK>;
};
a0noc: interconnect@543000 {
@ -922,19 +913,14 @@
compatible = "qcom,msm8996-a1noc";
reg = <0x00562000 0x5000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_AGGR1_NOC_CLK>,
<&rpmcc RPM_SMD_AGGR1_NOC_A_CLK>;
};
a2noc: interconnect@583000 {
compatible = "qcom,msm8996-a2noc";
reg = <0x00583000 0x7000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a", "aggre2_ufs_axi", "ufs_axi";
clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>,
<&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>,
<&gcc GCC_AGGRE2_UFS_AXI_CLK>,
clock-names = "aggre2_ufs_axi", "ufs_axi";
clocks = <&gcc GCC_AGGRE2_UFS_AXI_CLK>,
<&gcc GCC_UFS_AXI_CLK>;
};
@ -942,19 +928,14 @@
compatible = "qcom,msm8996-mnoc";
reg = <0x005a4000 0x1c000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a", "iface";
clocks = <&rpmcc RPM_SMD_MMAXI_CLK>,
<&rpmcc RPM_SMD_MMAXI_A_CLK>,
<&mmcc AHB_CLK_SRC>;
clock-names = "iface";
clocks = <&mmcc AHB_CLK_SRC>;
};
pnoc: interconnect@5c0000 {
compatible = "qcom,msm8996-pnoc";
reg = <0x005c0000 0x3000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_PCNOC_CLK>,
<&rpmcc RPM_SMD_PCNOC_A_CLK>;
};
tcsr_mutex: hwlock@740000 {
@ -2486,9 +2467,8 @@
"handover",
"stop-ack";
clocks = <&xo_board>,
<&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
clock-names = "xo", "aggre2";
clocks = <&xo_board>;
clock-names = "xo";
memory-region = <&slpi_mem>;
@ -2533,10 +2513,15 @@
<&gcc GCC_MSS_GPLL0_DIV_CLK>,
<&gcc GCC_MSS_SNOC_AXI_CLK>,
<&gcc GCC_MSS_MNOC_BIMC_AXI_CLK>,
<&rpmcc RPM_SMD_PCNOC_CLK>,
<&rpmcc RPM_SMD_QDSS_CLK>;
clock-names = "iface", "bus", "mem", "xo", "gpll0_mss",
"snoc_axi", "mnoc_axi", "pnoc", "qdss";
clock-names = "iface",
"bus",
"mem",
"xo",
"gpll0_mss",
"snoc_axi",
"mnoc_axi",
"qdss";
resets = <&gcc GCC_MSS_RESTART>;
reset-names = "mss_restart";

View File

@ -1605,9 +1605,8 @@
px-supply = <&vreg_lvs2a_1p8>;
clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>,
<&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
clock-names = "xo", "aggre2";
clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>;
clock-names = "xo";
memory-region = <&slpi_mem>;

View File

@ -558,9 +558,6 @@
reg = <0x00400000 0x80000>;
compatible = "qcom,qcs404-bimc";
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
};
tsens: thermal-sensor@4a9000 {
@ -601,18 +598,12 @@
reg = <0x00500000 0x15080>;
compatible = "qcom,qcs404-pcnoc";
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_PNOC_CLK>,
<&rpmcc RPM_SMD_PNOC_A_CLK>;
};
snoc: interconnect@580000 {
reg = <0x00580000 0x23080>;
compatible = "qcom,qcs404-snoc";
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
<&rpmcc RPM_SMD_SNOC_A_CLK>;
};
remoteproc_cdsp: remoteproc@b00000 {

View File

@ -1454,7 +1454,7 @@
altmodes {
displayport {
svid = <0xff01>;
svid = /bits/ 16 <0xff01>;
vdo = <0x00001c46>;
};
};

View File

@ -606,9 +606,6 @@
compatible = "qcom,sdm660-bimc";
reg = <0x01008000 0x78000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
};
restart@10ac000 {
@ -620,28 +617,17 @@
compatible = "qcom,sdm660-cnoc";
reg = <0x01500000 0x10000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_CNOC_CLK>,
<&rpmcc RPM_SMD_CNOC_A_CLK>;
};
snoc: interconnect@1626000 {
compatible = "qcom,sdm660-snoc";
reg = <0x01626000 0x7090>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
<&rpmcc RPM_SMD_SNOC_A_CLK>;
};
anoc2_smmu: iommu@16c0000 {
compatible = "qcom,sdm630-smmu-v2", "qcom,smmu-v2";
reg = <0x016c0000 0x40000>;
assigned-clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
assigned-clock-rates = <1000>;
clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
clock-names = "bus";
#global-interrupts = <2>;
#iommu-cells = <1>;
@ -686,16 +672,12 @@
compatible = "qcom,sdm660-a2noc";
reg = <0x01704000 0xc100>;
#interconnect-cells = <1>;
clock-names = "bus",
"bus_a",
"ipa",
clock-names = "ipa",
"ufs_axi",
"aggre2_ufs_axi",
"aggre2_usb3_axi",
"cfg_noc_usb2_axi";
clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>,
<&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>,
<&rpmcc RPM_SMD_IPA_CLK>,
clocks = <&rpmcc RPM_SMD_IPA_CLK>,
<&gcc GCC_UFS_AXI_CLK>,
<&gcc GCC_AGGRE2_UFS_AXI_CLK>,
<&gcc GCC_AGGRE2_USB3_AXI_CLK>,
@ -706,10 +688,8 @@
compatible = "qcom,sdm660-mnoc";
reg = <0x01745000 0xa010>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a", "iface";
clocks = <&rpmcc RPM_SMD_MMSSNOC_AXI_CLK>,
<&rpmcc RPM_SMD_MMSSNOC_AXI_CLK_A>,
<&mmcc AHB_CLK_SRC>;
clock-names = "iface";
clocks = <&mmcc AHB_CLK_SRC>;
};
tsens: thermal-sensor@10ae000 {
@ -1186,7 +1166,9 @@
clocks = <&gcc GCC_GPU_CFG_AHB_CLK>,
<&gcc GCC_BIMC_GFX_CLK>,
<&gcc GCC_GPU_BIMC_GFX_CLK>;
clock-names = "iface", "mem", "mem_iface";
clock-names = "iface",
"mem",
"mem_iface";
#global-interrupts = <2>;
#iommu-cells = <1>;
@ -1288,20 +1270,16 @@
<&gcc GCC_USB30_MASTER_CLK>,
<&gcc GCC_AGGRE2_USB3_AXI_CLK>,
<&gcc GCC_USB30_SLEEP_CLK>,
<&gcc GCC_USB30_MOCK_UTMI_CLK>,
<&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
<&gcc GCC_USB30_MOCK_UTMI_CLK>;
clock-names = "cfg_noc",
"core",
"iface",
"sleep",
"mock_utmi",
"bus";
"mock_utmi";
assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
<&gcc GCC_USB30_MASTER_CLK>,
<&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
assigned-clock-rates = <19200000>, <120000000>,
<19200000>;
<&gcc GCC_USB30_MASTER_CLK>;
assigned-clock-rates = <19200000>, <120000000>;
interrupts = <GIC_SPI 347 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>;
@ -2204,10 +2182,9 @@
clocks = <&mmcc MNOC_AHB_CLK>,
<&mmcc BIMC_SMMU_AHB_CLK>,
<&rpmcc RPM_SMD_MMSSNOC_AXI_CLK>,
<&mmcc BIMC_SMMU_AXI_CLK>;
clock-names = "iface-mm", "iface-smmu",
"bus-mm", "bus-smmu";
"bus-smmu";
#global-interrupts = <2>;
#iommu-cells = <1>;
@ -2324,12 +2301,6 @@
compatible = "qcom,sdm660-gnoc";
reg = <0x17900000 0xe000>;
#interconnect-cells = <1>;
/*
* This one apparently features no clocks,
* so let's not mess with the driver needlessly
*/
clock-names = "bus", "bus_a";
clocks = <&xo_board>, <&xo_board>;
};
apcs_glb: mailbox@17911000 {

View File

@ -4796,10 +4796,8 @@ static struct usb_driver btusb_driver = {
.disable_hub_initiated_lpm = 1,
#ifdef CONFIG_DEV_COREDUMP
.drvwrap = {
.driver = {
.coredump = btusb_coredump,
},
.driver = {
.coredump = btusb_coredump,
},
#endif
};

View File

@ -1143,7 +1143,7 @@ static void __exit peak_usb_exit(void)
int err;
/* last chance do send any synchronous commands here */
err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL,
err = driver_for_each_device(&peak_usb_driver.driver, NULL,
NULL, peak_usb_do_device_exit);
if (err)
pr_err("%s: failed to stop all can devices (err %d)\n",

View File

@ -10069,7 +10069,7 @@ static struct usb_driver rtl8152_driver = {
.disable_hub_initiated_lpm = 1,
};
static int rtl8152_cfgselector_probe(struct usb_device *udev)
static int rtl8152_cfgselector_choose_configuration(struct usb_device *udev)
{
struct usb_host_config *c;
int i, num_configs;
@ -10096,19 +10096,13 @@ static int rtl8152_cfgselector_probe(struct usb_device *udev)
if (i == num_configs)
return -ENODEV;
if (usb_set_configuration(udev, c->desc.bConfigurationValue)) {
dev_err(&udev->dev, "Failed to set configuration %d\n",
c->desc.bConfigurationValue);
return -ENODEV;
}
return 0;
return c->desc.bConfigurationValue;
}
static struct usb_device_driver rtl8152_cfgselector_driver = {
.name = MODULENAME "-cfgselector",
.probe = rtl8152_cfgselector_probe,
.id_table = rtl8152_table,
.name = MODULENAME "-cfgselector",
.choose_configuration = rtl8152_cfgselector_choose_configuration,
.id_table = rtl8152_table,
.generic_subclass = 1,
.supports_autosuspend = 1,
};

View File

@ -1581,7 +1581,7 @@ static int brcmf_usb_reset_device(struct device *dev, void *notused)
void brcmf_usb_exit(void)
{
struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
struct device_driver *drv = &brcmf_usbdrvr.driver;
int ret;
brcmf_dbg(USB, "Enter\n");

View File

@ -687,7 +687,7 @@ static struct usb_driver mwifiex_usb_driver = {
.suspend = mwifiex_usb_suspend,
.resume = mwifiex_usb_resume,
.soft_unbind = 1,
.drvwrap.driver = {
.driver = {
.coredump = mwifiex_usb_coredump,
},
};

View File

@ -136,7 +136,7 @@ static const struct software_node altmodes_node = {
};
static const struct property_entry dp_altmode_properties[] = {
PROPERTY_ENTRY_U32("svid", 0xff01),
PROPERTY_ENTRY_U16("svid", 0xff01),
PROPERTY_ENTRY_U32("vdo", 0x0c0086),
{ }
};

View File

@ -307,7 +307,7 @@ static const struct attribute_group *domain_attr_groups[] = {
NULL,
};
struct bus_type tb_bus_type = {
const struct bus_type tb_bus_type = {
.name = "thunderbolt",
.match = tb_service_match,
.probe = tb_service_probe,

View File

@ -1020,7 +1020,7 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
1, 10, 2000);
1, 10, 250);
if (ret)
return ret;

View File

@ -1517,6 +1517,10 @@ static struct pci_device_id nhi_ids[] = {
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI0),
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) },

View File

@ -90,6 +90,8 @@ extern const struct tb_nhi_ops icl_nhi_ops;
#define PCI_DEVICE_ID_INTEL_TGL_H_NHI1 0x9a21
#define PCI_DEVICE_ID_INTEL_RPL_NHI0 0xa73e
#define PCI_DEVICE_ID_INTEL_RPL_NHI1 0xa76d
#define PCI_DEVICE_ID_INTEL_LNL_NHI0 0xa833
#define PCI_DEVICE_ID_INTEL_LNL_NHI1 0xa834
#define PCI_CLASS_SERIAL_USB_USB4 0x0c0340

View File

@ -941,22 +941,6 @@ int tb_port_get_link_generation(struct tb_port *port)
}
}
static const char *width_name(enum tb_link_width width)
{
switch (width) {
case TB_LINK_WIDTH_SINGLE:
return "symmetric, single lane";
case TB_LINK_WIDTH_DUAL:
return "symmetric, dual lanes";
case TB_LINK_WIDTH_ASYM_TX:
return "asymmetric, 3 transmitters, 1 receiver";
case TB_LINK_WIDTH_ASYM_RX:
return "asymmetric, 3 receivers, 1 transmitter";
default:
return "unknown";
}
}
/**
* tb_port_get_link_width() - Get current link width
* @port: Port to check (USB4 or CIO)
@ -2769,7 +2753,7 @@ static void tb_switch_link_init(struct tb_switch *sw)
return;
tb_sw_dbg(sw, "current link speed %u.0 Gb/s\n", sw->link_speed);
tb_sw_dbg(sw, "current link width %s\n", width_name(sw->link_width));
tb_sw_dbg(sw, "current link width %s\n", tb_width_name(sw->link_width));
bonded = sw->link_width >= TB_LINK_WIDTH_DUAL;
@ -2789,6 +2773,19 @@ static void tb_switch_link_init(struct tb_switch *sw)
if (down->dual_link_port)
down->dual_link_port->bonded = bonded;
tb_port_update_credits(down);
if (tb_port_get_link_generation(up) < 4)
return;
/*
* Set the Gen 4 preferred link width. This is what the router
* prefers when the link is brought up. If the router does not
* support asymmetric link configuration, this also will be set
* to TB_LINK_WIDTH_DUAL.
*/
sw->preferred_link_width = sw->link_width;
tb_sw_dbg(sw, "preferred link width %s\n",
tb_width_name(sw->preferred_link_width));
}
/**
@ -3029,7 +3026,7 @@ int tb_switch_set_link_width(struct tb_switch *sw, enum tb_link_width width)
tb_switch_update_link_attributes(sw);
tb_sw_dbg(sw, "link width set to %s\n", width_name(width));
tb_sw_dbg(sw, "link width set to %s\n", tb_width_name(width));
return ret;
}

View File

@ -513,8 +513,6 @@ static void tb_port_unconfigure_xdomain(struct tb_port *port)
usb4_port_unconfigure_xdomain(port);
else
tb_lc_unconfigure_xdomain(port);
tb_port_enable(port->dual_link_port);
}
static void tb_scan_xdomain(struct tb_port *port)
@ -1087,15 +1085,14 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
struct tb_port *dst_port, int requested_up,
int requested_down)
{
bool clx = false, clx_disabled = false, downstream;
struct tb_switch *sw;
bool clx, downstream;
struct tb_port *up;
int ret = 0;
if (!asym_threshold)
return 0;
/* Disable CL states before doing any transitions */
downstream = tb_port_path_direction_downstream(src_port, dst_port);
/* Pick up router deepest in the hierarchy */
if (downstream)
@ -1103,11 +1100,10 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
else
sw = src_port->sw;
clx = tb_disable_clx(sw);
tb_for_each_upstream_port_on_path(src_port, dst_port, up) {
struct tb_port *down = tb_switch_downstream_port(up->sw);
enum tb_link_width width_up, width_down;
int consumed_up, consumed_down;
enum tb_link_width width;
ret = tb_consumed_dp_bandwidth(tb, src_port, dst_port, up,
&consumed_up, &consumed_down);
@ -1128,7 +1124,8 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
if (consumed_down + requested_down < asym_threshold)
continue;
width = TB_LINK_WIDTH_ASYM_RX;
width_up = TB_LINK_WIDTH_ASYM_RX;
width_down = TB_LINK_WIDTH_ASYM_TX;
} else {
/* Upstream, the opposite of above */
if (consumed_down + requested_down >= TB_ASYM_MIN) {
@ -1138,22 +1135,34 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
if (consumed_up + requested_up < asym_threshold)
continue;
width = TB_LINK_WIDTH_ASYM_TX;
width_up = TB_LINK_WIDTH_ASYM_TX;
width_down = TB_LINK_WIDTH_ASYM_RX;
}
if (up->sw->link_width == width)
if (up->sw->link_width == width_up)
continue;
if (!tb_port_width_supported(up, width))
if (!tb_port_width_supported(up, width_up) ||
!tb_port_width_supported(down, width_down))
continue;
/*
* Disable CL states before doing any transitions. We
* delayed it until now that we know there is a real
* transition taking place.
*/
if (!clx_disabled) {
clx = tb_disable_clx(sw);
clx_disabled = true;
}
tb_sw_dbg(up->sw, "configuring asymmetric link\n");
/*
* Here requested + consumed > threshold so we need to
* transtion the link into asymmetric now.
*/
ret = tb_switch_set_link_width(up->sw, width);
ret = tb_switch_set_link_width(up->sw, width_up);
if (ret) {
tb_sw_warn(up->sw, "failed to set link width\n");
break;
@ -1174,24 +1183,24 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
* @dst_port: Destination adapter
* @requested_up: New lower bandwidth request upstream (Mb/s)
* @requested_down: New lower bandwidth request downstream (Mb/s)
* @keep_asym: Keep asymmetric link if preferred
*
* Goes over each link from @src_port to @dst_port and tries to
* transition the link to symmetric if the currently consumed bandwidth
* allows.
* allows and link asymmetric preference is ignored (if @keep_asym is %false).
*/
static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
struct tb_port *dst_port, int requested_up,
int requested_down)
int requested_down, bool keep_asym)
{
bool clx = false, clx_disabled = false, downstream;
struct tb_switch *sw;
bool clx, downstream;
struct tb_port *up;
int ret = 0;
if (!asym_threshold)
return 0;
/* Disable CL states before doing any transitions */
downstream = tb_port_path_direction_downstream(src_port, dst_port);
/* Pick up router deepest in the hierarchy */
if (downstream)
@ -1199,8 +1208,6 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
else
sw = src_port->sw;
clx = tb_disable_clx(sw);
tb_for_each_upstream_port_on_path(src_port, dst_port, up) {
int consumed_up, consumed_down;
@ -1233,6 +1240,25 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
if (up->sw->link_width == TB_LINK_WIDTH_DUAL)
continue;
/*
* Here consumed < threshold so we can transition the
* link to symmetric.
*
* However, if the router prefers asymmetric link we
* honor that (unless @keep_asym is %false).
*/
if (keep_asym &&
up->sw->preferred_link_width > TB_LINK_WIDTH_DUAL) {
tb_sw_dbg(up->sw, "keeping preferred asymmetric link\n");
continue;
}
/* Disable CL states before doing any transitions */
if (!clx_disabled) {
clx = tb_disable_clx(sw);
clx_disabled = true;
}
tb_sw_dbg(up->sw, "configuring symmetric link\n");
ret = tb_switch_set_link_width(up->sw, TB_LINK_WIDTH_DUAL);
@ -1280,7 +1306,7 @@ static void tb_configure_link(struct tb_port *down, struct tb_port *up,
struct tb_port *host_port;
host_port = tb_port_at(tb_route(sw), tb->root_switch);
tb_configure_sym(tb, host_port, up, 0, 0);
tb_configure_sym(tb, host_port, up, 0, 0, false);
}
/* Set the link configured */
@ -1465,7 +1491,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
* If bandwidth on a link is < asym_threshold
* transition the link to symmetric.
*/
tb_configure_sym(tb, src_port, dst_port, 0, 0);
tb_configure_sym(tb, src_port, dst_port, 0, 0, true);
/* Now we can allow the domain to runtime suspend again */
pm_runtime_mark_last_busy(&dst_port->sw->dev);
pm_runtime_put_autosuspend(&dst_port->sw->dev);
@ -1901,7 +1927,7 @@ static void tb_dp_resource_available(struct tb *tb, struct tb_port *port)
return;
}
tb_port_dbg(port, "DP %s resource available\n",
tb_port_dbg(port, "DP %s resource available after hotplug\n",
tb_port_is_dpin(port) ? "IN" : "OUT");
list_add_tail(&port->list, &tcm->dp_resources);
@ -2287,7 +2313,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
* If bandwidth on a link is < asym_threshold transition
* the link to symmetric.
*/
tb_configure_sym(tb, in, out, *requested_up, *requested_down);
tb_configure_sym(tb, in, out, *requested_up, *requested_down, true);
/*
* If requested bandwidth is less or equal than what is
* currently allocated to that tunnel we simply change
@ -2330,7 +2356,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
ret = tb_configure_asym(tb, in, out, *requested_up,
*requested_down);
if (ret) {
tb_configure_sym(tb, in, out, 0, 0);
tb_configure_sym(tb, in, out, 0, 0, true);
return ret;
}
@ -2338,7 +2364,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
requested_down);
if (ret) {
tb_tunnel_warn(tunnel, "failed to allocate bandwidth\n");
tb_configure_sym(tb, in, out, 0, 0);
tb_configure_sym(tb, in, out, 0, 0, true);
}
} else {
ret = -ENOBUFS;

View File

@ -125,6 +125,7 @@ struct tb_switch_tmu {
* @device_name: Name of the device (or %NULL if not known)
* @link_speed: Speed of the link in Gb/s
* @link_width: Width of the upstream facing link
* @preferred_link_width: Router preferred link width (only set for Gen 4 links)
* @link_usb4: Upstream link is USB4
* @generation: Switch Thunderbolt generation
* @cap_plug_events: Offset to the plug events capability (%0 if not found)
@ -178,6 +179,7 @@ struct tb_switch {
const char *device_name;
unsigned int link_speed;
enum tb_link_width link_width;
enum tb_link_width preferred_link_width;
bool link_usb4;
unsigned int generation;
int cap_plug_events;
@ -568,6 +570,22 @@ static inline struct tb_port *tb_port_at(u64 route, struct tb_switch *sw)
return &sw->ports[port];
}
static inline const char *tb_width_name(enum tb_link_width width)
{
switch (width) {
case TB_LINK_WIDTH_SINGLE:
return "symmetric, single lane";
case TB_LINK_WIDTH_DUAL:
return "symmetric, dual lanes";
case TB_LINK_WIDTH_ASYM_TX:
return "asymmetric, 3 transmitters, 1 receiver";
case TB_LINK_WIDTH_ASYM_RX:
return "asymmetric, 3 receivers, 1 transmitter";
default:
return "unknown";
}
}
/**
* tb_port_has_remote() - Does the port have switch connected downstream
* @port: Port to check

View File

@ -894,7 +894,7 @@ static int tb_switch_tmu_change_mode(struct tb_switch *sw)
ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request);
if (ret)
return ret;
goto out;
/* Program the new mode and the downstream router lane adapter */
switch (sw->tmu.mode_request) {

View File

@ -173,16 +173,28 @@ static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable)
int ret;
/* Only supported of both routers are at least USB4 v2 */
if (tb_port_get_link_generation(port) < 4)
if ((usb4_switch_version(tunnel->src_port->sw) < 2) ||
(usb4_switch_version(tunnel->dst_port->sw) < 2))
return 0;
if (enable && tb_port_get_link_generation(port) < 4)
return 0;
ret = usb4_pci_port_set_ext_encapsulation(tunnel->src_port, enable);
if (ret)
return ret;
/*
* Downstream router could be unplugged so disable of encapsulation
* in upstream router is still possible.
*/
ret = usb4_pci_port_set_ext_encapsulation(tunnel->dst_port, enable);
if (ret)
return ret;
if (ret) {
if (enable)
return ret;
if (ret != -ENODEV)
return ret;
}
tb_tunnel_dbg(tunnel, "extended encapsulation %s\n",
str_enabled_disabled(enable));
@ -199,14 +211,21 @@ static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate)
return res;
}
res = tb_pci_port_enable(tunnel->src_port, activate);
if (activate)
res = tb_pci_port_enable(tunnel->dst_port, activate);
else
res = tb_pci_port_enable(tunnel->src_port, activate);
if (res)
return res;
if (tb_port_is_pcie_up(tunnel->dst_port)) {
res = tb_pci_port_enable(tunnel->dst_port, activate);
if (activate) {
res = tb_pci_port_enable(tunnel->src_port, activate);
if (res)
return res;
} else {
/* Downstream router could be unplugged */
tb_pci_port_enable(tunnel->dst_port, activate);
}
return activate ? 0 : tb_pci_set_ext_encapsulation(tunnel, activate);
@ -1067,8 +1086,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
return 0;
}
static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes,
int timeout_msec)
static int tb_dp_wait_dprx(struct tb_tunnel *tunnel, int timeout_msec)
{
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
struct tb_port *in = tunnel->src_port;
@ -1087,15 +1105,13 @@ static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes,
return ret;
if (val & DP_COMMON_CAP_DPRX_DONE) {
*rate = tb_dp_cap_get_rate(val);
*lanes = tb_dp_cap_get_lanes(val);
tb_tunnel_dbg(tunnel, "DPRX read done\n");
return 0;
}
usleep_range(100, 150);
} while (ktime_before(ktime_get(), timeout));
tb_tunnel_dbg(tunnel, "DPRX read timeout\n");
return -ETIMEDOUT;
}
@ -1110,6 +1126,7 @@ static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate,
switch (cap) {
case DP_LOCAL_CAP:
case DP_REMOTE_CAP:
case DP_COMMON_CAP:
break;
default:
@ -1182,7 +1199,7 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
* reduced one). Otherwise return the remote (possibly
* reduced) caps.
*/
ret = tb_dp_read_dprx(tunnel, &rate, &lanes, 150);
ret = tb_dp_wait_dprx(tunnel, 150);
if (ret) {
if (ret == -ETIMEDOUT)
ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP,
@ -1190,6 +1207,9 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
if (ret)
return ret;
}
ret = tb_dp_read_cap(tunnel, DP_COMMON_CAP, &rate, &lanes);
if (ret)
return ret;
} else if (sw->generation >= 2) {
ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, &rate, &lanes);
if (ret)
@ -1313,8 +1333,6 @@ static void tb_dp_dump(struct tb_tunnel *tunnel)
"DP IN maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
rate, lanes, tb_dp_bandwidth(rate, lanes));
out = tunnel->dst_port;
if (tb_port_read(out, &dp_cap, TB_CFG_PORT,
out->cap_adap + DP_LOCAL_CAP, 1))
return;

View File

@ -1462,6 +1462,11 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd)
tb_port_disable(port->dual_link_port);
}
dev_dbg(&xd->dev, "current link speed %u.0 Gb/s\n",
xd->link_speed);
dev_dbg(&xd->dev, "current link width %s\n",
tb_width_name(xd->link_width));
if (device_add(&xd->dev)) {
dev_err(&xd->dev, "failed to add XDomain device\n");
return -ENODEV;
@ -1895,6 +1900,50 @@ struct device_type tb_xdomain_type = {
};
EXPORT_SYMBOL_GPL(tb_xdomain_type);
static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down)
{
if (!down->dual_link_port)
return;
/*
* Gen 4 links come up already as bonded so only update the port
* structures here.
*/
if (tb_port_get_link_generation(down) >= 4) {
down->bonded = true;
down->dual_link_port->bonded = true;
} else {
xd->bonding_possible = true;
}
}
static void tb_xdomain_link_exit(struct tb_xdomain *xd)
{
struct tb_port *down = tb_xdomain_downstream_port(xd);
if (!down->dual_link_port)
return;
if (tb_port_get_link_generation(down) >= 4) {
down->bonded = false;
down->dual_link_port->bonded = false;
} else if (xd->link_width > TB_LINK_WIDTH_SINGLE) {
/*
* Just return port structures back to way they were and
* update credits. No need to update userspace because
* the XDomain is removed soon anyway.
*/
tb_port_lane_bonding_disable(down);
tb_port_update_credits(down);
} else if (down->dual_link_port) {
/*
* Re-enable the lane 1 adapter we disabled at the end
* of tb_xdomain_get_properties().
*/
tb_port_enable(down->dual_link_port);
}
}
/**
* tb_xdomain_alloc() - Allocate new XDomain object
* @tb: Domain where the XDomain belongs
@ -1945,7 +1994,8 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent,
goto err_free_local_uuid;
} else {
xd->needs_uuid = true;
xd->bonding_possible = !!down->dual_link_port;
tb_xdomain_link_init(xd, down);
}
device_initialize(&xd->dev);
@ -2014,6 +2064,8 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
device_for_each_child_reverse(&xd->dev, xd, unregister_service);
tb_xdomain_link_exit(xd);
/*
* Undo runtime PM here explicitly because it is possible that
* the XDomain was never added to the bus and thus device_del()

View File

@ -2493,6 +2493,9 @@ static int send_break(struct tty_struct *tty, unsigned int duration)
if (!retval) {
msleep_interruptible(duration);
retval = tty->ops->break_ctl(tty, 0);
} else if (retval == -EOPNOTSUPP) {
/* some drivers can tell only dynamically */
retval = 0;
}
tty_write_unlock(tty);

View File

@ -546,7 +546,7 @@ MODULE_PARM_DESC(annex,
#define uea_wait(sc, cond, timeo) \
({ \
int _r = wait_event_interruptible_timeout(sc->sync_q, \
int _r = wait_event_freezable_timeout(sc->sync_q, \
(cond) || kthread_should_stop(), timeo); \
if (kthread_should_stop()) \
_r = -ENODEV; \
@ -1896,7 +1896,6 @@ static int uea_kthread(void *data)
ret = sc->stat(sc);
if (ret != -EAGAIN)
uea_wait(sc, 0, msecs_to_jiffies(1000));
try_to_freeze();
}
uea_leaves(INS_TO_USBDEV(sc));
return ret;
@ -2252,7 +2251,7 @@ static ssize_t stat_status_show(struct device *dev, struct device_attribute *att
sc = dev_to_uea(dev);
if (!sc)
goto out;
ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
ret = sysfs_emit(buf, "%08x\n", sc->stats.phy.state);
out:
mutex_unlock(&uea_mutex);
return ret;
@ -2318,19 +2317,19 @@ static ssize_t stat_human_status_show(struct device *dev,
switch (modem_state) {
case 0:
ret = sprintf(buf, "Modem is booting\n");
ret = sysfs_emit(buf, "Modem is booting\n");
break;
case 1:
ret = sprintf(buf, "Modem is initializing\n");
ret = sysfs_emit(buf, "Modem is initializing\n");
break;
case 2:
ret = sprintf(buf, "Modem is operational\n");
ret = sysfs_emit(buf, "Modem is operational\n");
break;
case 3:
ret = sprintf(buf, "Modem synchronization failed\n");
ret = sysfs_emit(buf, "Modem synchronization failed\n");
break;
default:
ret = sprintf(buf, "Modem state is unknown\n");
ret = sysfs_emit(buf, "Modem state is unknown\n");
break;
}
out:
@ -2364,7 +2363,7 @@ static ssize_t stat_delin_show(struct device *dev, struct device_attribute *attr
delin = "LOSS";
}
ret = sprintf(buf, "%s\n", delin);
ret = sysfs_emit(buf, "%s\n", delin);
out:
mutex_unlock(&uea_mutex);
return ret;
@ -2384,7 +2383,7 @@ static ssize_t stat_##name##_show(struct device *dev, \
sc = dev_to_uea(dev); \
if (!sc) \
goto out; \
ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \
ret = sysfs_emit(buf, "%08x\n", sc->stats.phy.name); \
if (reset) \
sc->stats.phy.name = 0; \
out: \

View File

@ -1119,6 +1119,8 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
dma_addr_t trb_dma;
u32 togle_pcs = 1;
int sg_iter = 0;
int num_trb_req;
int trb_burst;
int num_trb;
int address;
u32 control;
@ -1126,16 +1128,15 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
u16 total_tdl = 0;
struct scatterlist *s = NULL;
bool sg_supported = !!(request->num_mapped_sgs);
u32 ioc = request->no_interrupt ? 0 : TRB_IOC;
num_trb_req = sg_supported ? request->num_mapped_sgs : 1;
/* ISO transfer require each SOF have a TD, each TD include some TRBs */
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
num_trb = priv_ep->interval;
num_trb = priv_ep->interval * num_trb_req;
else
num_trb = sg_supported ? request->num_mapped_sgs : 1;
if (num_trb > priv_ep->free_trbs) {
priv_ep->flags |= EP_RING_FULL;
return -ENOBUFS;
}
num_trb = num_trb_req;
priv_req = to_cdns3_request(request);
address = priv_ep->endpoint.desc->bEndpointAddress;
@ -1184,14 +1185,31 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) |
TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
/*
* ISO require LINK TRB must be first one of TD.
* Fill LINK TRBs for left trb space to simply software process logic.
*/
while (priv_ep->enqueue) {
*trb = *link_trb;
trace_cdns3_prepare_trb(priv_ep, trb);
cdns3_ep_inc_enq(priv_ep);
trb = priv_ep->trb_pool + priv_ep->enqueue;
priv_req->trb = trb;
}
}
}
if (num_trb > priv_ep->free_trbs) {
priv_ep->flags |= EP_RING_FULL;
return -ENOBUFS;
}
if (priv_dev->dev_ver <= DEV_VER_V2)
togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
if (sg_supported)
s = request->sg;
/* set incorrect Cycle Bit for first trb*/
control = priv_ep->pcs ? 0 : TRB_CYCLE;
trb->length = 0;
@ -1209,6 +1227,9 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
do {
u32 length;
if (!(sg_iter % num_trb_req) && sg_supported)
s = request->sg;
/* fill TRB */
control |= TRB_TYPE(TRB_NORMAL);
if (sg_supported) {
@ -1223,7 +1244,36 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
total_tdl += DIV_ROUND_UP(length,
priv_ep->endpoint.maxpacket);
trb->length |= cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) |
trb_burst = priv_ep->trb_burst_size;
/*
* Supposed DMA cross 4k bounder problem should be fixed at DEV_VER_V2, but still
* met problem when do ISO transfer if sg enabled.
*
* Data pattern likes below when sg enabled, package size is 1k and mult is 2
* [UVC Header(8B) ] [data(3k - 8)] ...
*
* The received data at offset 0xd000 will get 0xc000 data, len 0x70. Error happen
* as below pattern:
* 0xd000: wrong
* 0xe000: wrong
* 0xf000: correct
* 0x10000: wrong
* 0x11000: wrong
* 0x12000: correct
* ...
*
* But it is still unclear about why error have not happen below 0xd000, it should
* cross 4k bounder. But anyway, the below code can fix this problem.
*
* To avoid DMA cross 4k bounder at ISO transfer, reduce burst len according to 16.
*/
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_dev->dev_ver <= DEV_VER_V2)
if (ALIGN_DOWN(trb->buffer, SZ_4K) !=
ALIGN_DOWN(trb->buffer + length, SZ_4K))
trb_burst = 16;
trb->length |= cpu_to_le32(TRB_BURST_LEN(trb_burst) |
TRB_LEN(length));
pcs = priv_ep->pcs ? TRB_CYCLE : 0;
@ -1235,11 +1285,11 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
control |= pcs;
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) {
control |= TRB_IOC | TRB_ISP;
control |= ioc | TRB_ISP;
} else {
/* for last element in TD or in SG list */
if (sg_iter == (num_trb - 1) && sg_iter != 0)
control |= pcs | TRB_IOC | TRB_ISP;
control |= pcs | ioc | TRB_ISP;
}
if (sg_iter)
@ -1250,7 +1300,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
if (sg_supported) {
trb->control |= cpu_to_le32(TRB_ISP);
/* Don't set chain bit for last TRB */
if (sg_iter < num_trb - 1)
if ((sg_iter % num_trb_req) < num_trb_req - 1)
trb->control |= cpu_to_le32(TRB_CHAIN);
s = sg_next(s);
@ -1270,7 +1320,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
priv_req->num_of_trb = num_trb;
if (sg_iter == 1)
trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP);
trb->control |= cpu_to_le32(ioc | TRB_ISP);
if (priv_dev->dev_ver < DEV_VER_V2 &&
(priv_ep->flags & EP_TDLCHK_EN)) {
@ -1508,6 +1558,12 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
/* The TRB was changed as link TRB, and the request was handled at ep_dequeue */
while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) {
/* ISO ep_traddr may stop at LINK TRB */
if (priv_ep->dequeue == cdns3_get_dma_pos(priv_dev, priv_ep) &&
priv_ep->type == USB_ENDPOINT_XFER_ISOC)
break;
trace_cdns3_complete_trb(priv_ep, trb);
cdns3_ep_inc_deq(priv_ep);
trb = priv_ep->trb_pool + priv_ep->dequeue;
@ -1540,6 +1596,10 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
}
if (request_handled) {
/* TRBs are duplicated by priv_ep->interval time for ISO IN */
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_ep->dir)
request->actual /= priv_ep->interval;
cdns3_gadget_giveback(priv_ep, priv_req, 0);
request_handled = false;
transfer_end = false;
@ -2035,11 +2095,10 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
u32 max_packet_size = 0;
u8 maxburst = 0;
u32 max_packet_size = priv_ep->wMaxPacketSize;
u8 maxburst = priv_ep->bMaxBurst;
u32 ep_cfg = 0;
u8 buffering;
u8 mult = 0;
int ret;
buffering = priv_dev->ep_buf_size - 1;
@ -2061,8 +2120,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
break;
default:
ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
mult = priv_dev->ep_iso_burst - 1;
buffering = mult + 1;
buffering = (priv_ep->bMaxBurst + 1) * (priv_ep->mult + 1) - 1;
}
switch (priv_dev->gadget.speed) {
@ -2073,17 +2131,8 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
max_packet_size = is_iso_ep ? 1024 : 512;
break;
case USB_SPEED_SUPER:
/* It's limitation that driver assumes in driver. */
mult = 0;
max_packet_size = 1024;
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
maxburst = priv_dev->ep_iso_burst - 1;
buffering = (mult + 1) *
(maxburst + 1);
if (priv_ep->interval > 1)
buffering++;
} else {
if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
max_packet_size = 1024;
maxburst = priv_dev->ep_buf_size - 1;
}
break;
@ -2112,7 +2161,6 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
if (priv_dev->dev_ver < DEV_VER_V2)
priv_ep->trb_burst_size = 16;
mult = min_t(u8, mult, EP_CFG_MULT_MAX);
buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX);
maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX);
@ -2146,7 +2194,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
}
ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
EP_CFG_MULT(mult) |
EP_CFG_MULT(priv_ep->mult) | /* must match EP setting */
EP_CFG_BUFFERING(buffering) |
EP_CFG_MAXBURST(maxburst);
@ -2236,6 +2284,13 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
priv_ep->type = usb_endpoint_type(desc);
priv_ep->flags |= EP_CLAIMED;
priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
priv_ep->wMaxPacketSize = usb_endpoint_maxp(desc);
priv_ep->mult = USB_EP_MAXP_MULT(priv_ep->wMaxPacketSize);
priv_ep->wMaxPacketSize &= USB_ENDPOINT_MAXP_MASK;
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && comp_desc) {
priv_ep->mult = USB_SS_MULT(comp_desc->bmAttributes) - 1;
priv_ep->bMaxBurst = comp_desc->bMaxBurst;
}
spin_unlock_irqrestore(&priv_dev->lock, flags);
return &priv_ep->endpoint;
@ -3019,22 +3074,40 @@ static int cdns3_gadget_check_config(struct usb_gadget *gadget)
struct cdns3_endpoint *priv_ep;
struct usb_ep *ep;
int n_in = 0;
int iso = 0;
int out = 1;
int total;
int n;
list_for_each_entry(ep, &gadget->ep_list, ep_list) {
priv_ep = ep_to_cdns3_ep(ep);
if ((priv_ep->flags & EP_CLAIMED) && (ep->address & USB_DIR_IN))
n_in++;
if (!(priv_ep->flags & EP_CLAIMED))
continue;
n = (priv_ep->mult + 1) * (priv_ep->bMaxBurst + 1);
if (ep->address & USB_DIR_IN) {
/*
* ISO transfer: DMA start move data when get ISO, only transfer
* data as min(TD size, iso). No benefit for allocate bigger
* internal memory than 'iso'.
*/
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
iso += n;
else
n_in++;
} else {
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
out = max_t(int, out, n);
}
}
/* 2KB are reserved for EP0, 1KB for out*/
total = 2 + n_in + 1;
total = 2 + n_in + out + iso;
if (total > priv_dev->onchip_buffers)
return -ENOMEM;
priv_dev->ep_buf_size = priv_dev->ep_iso_burst =
(priv_dev->onchip_buffers - 2) / (n_in + 1);
priv_dev->ep_buf_size = (priv_dev->onchip_buffers - 2 - iso) / (n_in + out);
return 0;
}

View File

@ -1168,6 +1168,9 @@ struct cdns3_endpoint {
u8 dir;
u8 num;
u8 type;
u8 mult;
u8 bMaxBurst;
u16 wMaxPacketSize;
int interval;
int free_trbs;

View File

@ -87,16 +87,20 @@ static int cdns3_plat_probe(struct platform_device *pdev)
cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
if (cdns->dev_irq < 0)
return cdns->dev_irq;
return dev_err_probe(dev, cdns->dev_irq,
"Failed to get peripheral IRQ\n");
regs = devm_platform_ioremap_resource_byname(pdev, "dev");
if (IS_ERR(regs))
return PTR_ERR(regs);
return dev_err_probe(dev, PTR_ERR(regs),
"Failed to get dev base\n");
cdns->dev_regs = regs;
cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
if (cdns->otg_irq < 0)
return cdns->otg_irq;
return dev_err_probe(dev, cdns->otg_irq,
"Failed to get otg IRQ\n");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
if (!res) {
@ -119,7 +123,8 @@ static int cdns3_plat_probe(struct platform_device *pdev)
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
if (IS_ERR(cdns->usb2_phy))
return PTR_ERR(cdns->usb2_phy);
return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy),
"Failed to get cdn3,usb2-phy\n");
ret = phy_init(cdns->usb2_phy);
if (ret)
@ -127,7 +132,8 @@ static int cdns3_plat_probe(struct platform_device *pdev)
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
if (IS_ERR(cdns->usb3_phy))
return PTR_ERR(cdns->usb3_phy);
return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy),
"Failed to get cdn3,usb3-phy\n");
ret = phy_init(cdns->usb3_phy);
if (ret)

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/**
/*
* cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller
*
* Copyright (C) 2023 StarFive Technology Co., Ltd.

View File

@ -187,202 +187,202 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
switch (type) {
case TRB_LINK:
ret = snprintf(str, size,
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
field1, field0, GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_TC ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
field1, field0, GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_TC ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_TRANSFER:
case TRB_COMPLETION:
case TRB_PORT_STATUS:
case TRB_HC_EVENT:
ret = snprintf(str, size,
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
" len %ld slot %ld flags %c:%c",
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
cdnsp_trb_type_string(type), field1, field0,
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
" len %ld slot %ld flags %c:%c",
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
cdnsp_trb_type_string(type), field1, field0,
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_MFINDEX_WRAP:
ret = snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SETUP:
ret = snprintf(str, size,
"type '%s' bRequestType %02x bRequest %02x "
"wValue %02x%02x wIndex %02x%02x wLength %d "
"length %ld TD size %ld intr %ld Setup ID %ld "
"flags %c:%c:%c",
cdnsp_trb_type_string(type),
field0 & 0xff,
(field0 & 0xff00) >> 8,
(field0 & 0xff000000) >> 24,
(field0 & 0xff0000) >> 16,
(field1 & 0xff00) >> 8,
field1 & 0xff,
(field1 & 0xff000000) >> 16 |
(field1 & 0xff0000) >> 16,
TRB_LEN(field2), GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
TRB_SETUPID_TO_TYPE(field3),
field3 & TRB_IDT ? 'D' : 'd',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"type '%s' bRequestType %02x bRequest %02x "
"wValue %02x%02x wIndex %02x%02x wLength %d "
"length %ld TD size %ld intr %ld Setup ID %ld "
"flags %c:%c:%c",
cdnsp_trb_type_string(type),
field0 & 0xff,
(field0 & 0xff00) >> 8,
(field0 & 0xff000000) >> 24,
(field0 & 0xff0000) >> 16,
(field1 & 0xff00) >> 8,
field1 & 0xff,
(field1 & 0xff000000) >> 16 |
(field1 & 0xff0000) >> 16,
TRB_LEN(field2), GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
TRB_SETUPID_TO_TYPE(field3),
field3 & TRB_IDT ? 'D' : 'd',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DATA:
ret = snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld TD size %ld "
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_IDT ? 'D' : 'i',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"type '%s' Buffer %08x%08x length %ld TD size %ld "
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_IDT ? 'D' : 'i',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STATUS:
ret = snprintf(str, size,
"Buffer %08x%08x length %ld TD size %ld intr"
"%ld type '%s' flags %c:%c:%c:%c",
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"Buffer %08x%08x length %ld TD size %ld intr"
"%ld type '%s' flags %c:%c:%c:%c",
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_NORMAL:
case TRB_ISOC:
case TRB_EVENT_DATA:
case TRB_TR_NOOP:
ret = snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld "
"TD size %ld intr %ld "
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_BEI ? 'B' : 'b',
field3 & TRB_IDT ? 'T' : 't',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c',
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
ret = scnprintf(str, size,
"type '%s' Buffer %08x%08x length %ld "
"TD size %ld intr %ld "
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_BEI ? 'B' : 'b',
field3 & TRB_IDT ? 'T' : 't',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c',
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
break;
case TRB_CMD_NOOP:
case TRB_ENABLE_SLOT:
ret = snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DISABLE_SLOT:
ret = snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ADDR_DEV:
ret = snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_BSR ? 'B' : 'b',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_BSR ? 'B' : 'b',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_CONFIG_EP:
ret = snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_DC ? 'D' : 'd',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_DC ? 'D' : 'd',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_EVAL_CONTEXT:
ret = snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_EP:
case TRB_HALT_ENDPOINT:
ret = snprintf(str, size,
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STOP_RING:
ret = snprintf(str, size,
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
ret = snprintf(str, size,
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:
ret = snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ENDPOINT_NRDY:
temp = TRB_TO_HOST_STREAM(field2);
ret = snprintf(str, size,
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), temp,
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
temp == STREAM_REJECTED ? "(REJECTED)" : "",
TRB_TO_DEV_STREAM(field0),
field3 & TRB_STAT ? 'S' : 's',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = scnprintf(str, size,
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), temp,
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
temp == STREAM_REJECTED ? "(REJECTED)" : "",
TRB_TO_DEV_STREAM(field0),
field3 & TRB_STAT ? 'S' : 's',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
default:
ret = snprintf(str, size,
"type '%s' -> raw %08x %08x %08x %08x",
cdnsp_trb_type_string(type),
field0, field1, field2, field3);
ret = scnprintf(str, size,
"type '%s' -> raw %08x %08x %08x %08x",
cdnsp_trb_type_string(type),
field0, field1, field2, field3);
}
if (ret >= size)
pr_info("CDNSP: buffer overflowed.\n");
if (ret == size - 1)
pr_info("CDNSP: buffer may be truncated.\n");
return str;
}
@ -465,32 +465,32 @@ static inline const char *cdnsp_decode_portsc(char *str, size_t size,
{
int ret;
ret = snprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ",
portsc & PORT_POWER ? "Powered" : "Powered-off",
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
portsc & PORT_PED ? "Enabled" : "Disabled",
cdnsp_portsc_link_state_string(portsc),
DEV_PORT_SPEED(portsc));
ret = scnprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ",
portsc & PORT_POWER ? "Powered" : "Powered-off",
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
portsc & PORT_PED ? "Enabled" : "Disabled",
cdnsp_portsc_link_state_string(portsc),
DEV_PORT_SPEED(portsc));
if (portsc & PORT_RESET)
ret += snprintf(str + ret, size - ret, "In-Reset ");
ret += scnprintf(str + ret, size - ret, "In-Reset ");
ret += snprintf(str + ret, size - ret, "Change: ");
ret += scnprintf(str + ret, size - ret, "Change: ");
if (portsc & PORT_CSC)
ret += snprintf(str + ret, size - ret, "CSC ");
ret += scnprintf(str + ret, size - ret, "CSC ");
if (portsc & PORT_WRC)
ret += snprintf(str + ret, size - ret, "WRC ");
ret += scnprintf(str + ret, size - ret, "WRC ");
if (portsc & PORT_RC)
ret += snprintf(str + ret, size - ret, "PRC ");
ret += scnprintf(str + ret, size - ret, "PRC ");
if (portsc & PORT_PLC)
ret += snprintf(str + ret, size - ret, "PLC ");
ret += scnprintf(str + ret, size - ret, "PLC ");
if (portsc & PORT_CEC)
ret += snprintf(str + ret, size - ret, "CEC ");
ret += snprintf(str + ret, size - ret, "Wake: ");
ret += scnprintf(str + ret, size - ret, "CEC ");
ret += scnprintf(str + ret, size - ret, "Wake: ");
if (portsc & PORT_WKCONN_E)
ret += snprintf(str + ret, size - ret, "WCE ");
ret += scnprintf(str + ret, size - ret, "WCE ");
if (portsc & PORT_WKDISC_E)
ret += snprintf(str + ret, size - ret, "WDE ");
ret += scnprintf(str + ret, size - ret, "WDE ");
return str;
}
@ -562,20 +562,20 @@ static inline const char *cdnsp_decode_ep_context(char *str, size_t size,
avg = EP_AVG_TRB_LENGTH(tx_info);
ret = snprintf(str, size, "State %s mult %d max P. Streams %d %s",
cdnsp_ep_state_string(ep_state), mult,
max_pstr, lsa ? "LSA " : "");
ret = scnprintf(str, size, "State %s mult %d max P. Streams %d %s",
cdnsp_ep_state_string(ep_state), mult,
max_pstr, lsa ? "LSA " : "");
ret += snprintf(str + ret, size - ret,
"interval %d us max ESIT payload %d CErr %d ",
(1 << interval) * 125, esit, cerr);
ret += scnprintf(str + ret, size - ret,
"interval %d us max ESIT payload %d CErr %d ",
(1 << interval) * 125, esit, cerr);
ret += snprintf(str + ret, size - ret,
"Type %s %sburst %d maxp %d deq %016llx ",
cdnsp_ep_type_string(ep_type), hid ? "HID" : "",
burst, maxp, deq);
ret += scnprintf(str + ret, size - ret,
"Type %s %sburst %d maxp %d deq %016llx ",
cdnsp_ep_type_string(ep_type), hid ? "HID" : "",
burst, maxp, deq);
ret += snprintf(str + ret, size - ret, "avg trb len %d", avg);
ret += scnprintf(str + ret, size - ret, "avg trb len %d", avg);
return str;
}

View File

@ -96,6 +96,7 @@ struct ci_hdrc_imx_data {
struct usb_phy *phy;
struct platform_device *ci_pdev;
struct clk *clk;
struct clk *clk_wakeup;
struct imx_usbmisc_data *usbmisc_data;
bool supports_runtime_pm;
bool override_phy_control;
@ -199,7 +200,7 @@ static int imx_get_clks(struct device *dev)
data->clk_ipg = devm_clk_get(dev, "ipg");
if (IS_ERR(data->clk_ipg)) {
/* If the platform only needs one clocks */
/* If the platform only needs one primary clock */
data->clk = devm_clk_get(dev, NULL);
if (IS_ERR(data->clk)) {
ret = PTR_ERR(data->clk);
@ -208,6 +209,13 @@ static int imx_get_clks(struct device *dev)
PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
return ret;
}
/* Get wakeup clock. Not all of the platforms need to
* handle this clock. So make it optional.
*/
data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup_clk");
if (IS_ERR(data->clk_wakeup))
ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup),
"Failed to get wakeup clk\n");
return ret;
}
@ -423,6 +431,10 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (ret)
goto disable_hsic_regulator;
ret = clk_prepare_enable(data->clk_wakeup);
if (ret)
goto err_wakeup_clk;
data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0);
if (IS_ERR(data->phy)) {
ret = PTR_ERR(data->phy);
@ -504,6 +516,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
err_clk:
clk_disable_unprepare(data->clk_wakeup);
err_wakeup_clk:
imx_disable_unprepare_clks(dev);
disable_hsic_regulator:
if (data->hsic_pad_regulator)
@ -530,6 +544,7 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev)
usb_phy_shutdown(data->phy);
if (data->ci_pdev) {
imx_disable_unprepare_clks(&pdev->dev);
clk_disable_unprepare(data->clk_wakeup);
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
if (data->hsic_pad_regulator)

View File

@ -523,6 +523,13 @@ static irqreturn_t ci_irq_handler(int irq, void *data)
u32 otgsc = 0;
if (ci->in_lpm) {
/*
* If we already have a wakeup irq pending there,
* let's just return to wait resume finished firstly.
*/
if (ci->wakeup_int)
return IRQ_HANDLED;
disable_irq_nosync(irq);
ci->wakeup_int = true;
pm_runtime_get(ci->dev);
@ -862,7 +869,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
if (ret)
return ERR_PTR(ret);
id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);
id = ida_alloc(&ci_ida, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
@ -892,7 +899,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
err:
platform_device_put(pdev);
put_id:
ida_simple_remove(&ci_ida, id);
ida_free(&ci_ida, id);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(ci_hdrc_add_device);
@ -901,7 +908,7 @@ void ci_hdrc_remove_device(struct platform_device *pdev)
{
int id = pdev->id;
platform_device_unregister(pdev);
ida_simple_remove(&ci_ida, id);
ida_free(&ci_ida, id);
}
EXPORT_SYMBOL_GPL(ci_hdrc_remove_device);

View File

@ -688,7 +688,8 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if ((TD_STATUS_ACTIVE & tmptoken) != 0) {
int n = hw_ep_bit(hwep->num, hwep->dir);
if (ci->rev == CI_REVISION_24)
if (ci->rev == CI_REVISION_24 ||
ci->rev == CI_REVISION_22)
if (!hw_read(ci, OP_ENDPTSTAT, BIT(n)))
reprime_dtd(ci, hwep, node);
hwreq->req.status = -EALREADY;

View File

@ -916,6 +916,9 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)
struct acm *acm = tty->driver_data;
int retval;
if (!(acm->ctrl_caps & USB_CDC_CAP_BRK))
return -EOPNOTSUPP;
retval = acm_send_break(acm, state ? 0xffff : 0);
if (retval < 0)
dev_dbg(&acm->control->dev,

View File

@ -189,13 +189,13 @@ static int usb_create_newid_files(struct usb_driver *usb_drv)
goto exit;
if (usb_drv->probe != NULL) {
error = driver_create_file(&usb_drv->drvwrap.driver,
error = driver_create_file(&usb_drv->driver,
&driver_attr_new_id);
if (error == 0) {
error = driver_create_file(&usb_drv->drvwrap.driver,
error = driver_create_file(&usb_drv->driver,
&driver_attr_remove_id);
if (error)
driver_remove_file(&usb_drv->drvwrap.driver,
driver_remove_file(&usb_drv->driver,
&driver_attr_new_id);
}
}
@ -209,9 +209,9 @@ static void usb_remove_newid_files(struct usb_driver *usb_drv)
return;
if (usb_drv->probe != NULL) {
driver_remove_file(&usb_drv->drvwrap.driver,
driver_remove_file(&usb_drv->driver,
&driver_attr_remove_id);
driver_remove_file(&usb_drv->drvwrap.driver,
driver_remove_file(&usb_drv->driver,
&driver_attr_new_id);
}
}
@ -290,7 +290,10 @@ static int usb_probe_device(struct device *dev)
* specialised device drivers prior to setting the
* use_generic_driver bit.
*/
error = udriver->probe(udev);
if (udriver->probe)
error = udriver->probe(udev);
else if (!udriver->generic_subclass)
error = -EINVAL;
if (error == -ENODEV && udriver != &usb_generic_driver &&
(udriver->id_table || udriver->match)) {
udev->use_generic_driver = 1;
@ -549,7 +552,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
dev->driver = &driver->drvwrap.driver;
dev->driver = &driver->driver;
usb_set_intfdata(iface, data);
iface->needs_binding = 0;
@ -612,7 +615,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
struct device *dev = &iface->dev;
/* this should never happen, don't release something that's not ours */
if (!dev->driver || dev->driver != &driver->drvwrap.driver)
if (!dev->driver || dev->driver != &driver->driver)
return;
/* don't release from within disconnect() */
@ -947,7 +950,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
int ret;
/* Don't reprobe if current driver isn't usb_generic_driver */
if (dev->driver != &usb_generic_driver.drvwrap.driver)
if (dev->driver != &usb_generic_driver.driver)
return 0;
udev = to_usb_device(dev);
@ -961,6 +964,11 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
return 0;
}
bool is_usb_device_driver(const struct device_driver *drv)
{
return drv->probe == usb_probe_device;
}
/**
* usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
@ -980,15 +988,14 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
if (usb_disabled())
return -ENODEV;
new_udriver->drvwrap.for_devices = 1;
new_udriver->drvwrap.driver.name = new_udriver->name;
new_udriver->drvwrap.driver.bus = &usb_bus_type;
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
new_udriver->drvwrap.driver.owner = owner;
new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;
new_udriver->driver.name = new_udriver->name;
new_udriver->driver.bus = &usb_bus_type;
new_udriver->driver.probe = usb_probe_device;
new_udriver->driver.remove = usb_unbind_device;
new_udriver->driver.owner = owner;
new_udriver->driver.dev_groups = new_udriver->dev_groups;
retval = driver_register(&new_udriver->drvwrap.driver);
retval = driver_register(&new_udriver->driver);
if (!retval) {
pr_info("%s: registered new device driver %s\n",
@ -1020,7 +1027,7 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver)
pr_info("%s: deregistering device driver %s\n",
usbcore_name, udriver->name);
driver_unregister(&udriver->drvwrap.driver);
driver_unregister(&udriver->driver);
}
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
@ -1048,18 +1055,17 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
if (usb_disabled())
return -ENODEV;
new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
new_driver->driver.name = new_driver->name;
new_driver->driver.bus = &usb_bus_type;
new_driver->driver.probe = usb_probe_interface;
new_driver->driver.remove = usb_unbind_interface;
new_driver->driver.owner = owner;
new_driver->driver.mod_name = mod_name;
new_driver->driver.dev_groups = new_driver->dev_groups;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver);
retval = driver_register(&new_driver->driver);
if (retval)
goto out;
@ -1074,7 +1080,7 @@ out:
return retval;
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
driver_unregister(&new_driver->driver);
pr_err("%s: error %d registering interface driver %s\n",
usbcore_name, retval, new_driver->name);
@ -1099,7 +1105,7 @@ void usb_deregister(struct usb_driver *driver)
usbcore_name, driver->name);
usb_remove_newid_files(driver);
driver_unregister(&driver->drvwrap.driver);
driver_unregister(&driver->driver);
usb_free_dynids(driver);
}
EXPORT_SYMBOL_GPL(usb_deregister);

View File

@ -59,10 +59,26 @@ int usb_choose_configuration(struct usb_device *udev)
int num_configs;
int insufficient_power = 0;
struct usb_host_config *c, *best;
struct usb_device_driver *udriver;
/*
* If a USB device (not an interface) doesn't have a driver then the
* kernel has no business trying to select or install a configuration
* for it.
*/
if (!udev->dev.driver)
return -1;
udriver = to_usb_device_driver(udev->dev.driver);
if (usb_device_is_owned(udev))
return 0;
if (udriver->choose_configuration) {
i = udriver->choose_configuration(udev);
if (i >= 0)
return i;
}
best = NULL;
c = udev->config;
num_configs = udev->descriptor.bNumConfigurations;

View File

@ -47,12 +47,24 @@
#define USB_VENDOR_TEXAS_INSTRUMENTS 0x0451
#define USB_PRODUCT_TUSB8041_USB3 0x8140
#define USB_PRODUCT_TUSB8041_USB2 0x8142
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
#define HUB_QUIRK_DISABLE_AUTOSUSPEND 0x02
#define USB_VENDOR_MICROCHIP 0x0424
#define USB_PRODUCT_USB4913 0x4913
#define USB_PRODUCT_USB4914 0x4914
#define USB_PRODUCT_USB4915 0x4915
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND BIT(0)
#define HUB_QUIRK_DISABLE_AUTOSUSPEND BIT(1)
#define HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL BIT(2)
#define USB_TP_TRANSMISSION_DELAY 40 /* ns */
#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */
#define USB_PING_RESPONSE_TIME 400 /* ns */
#define USB_REDUCE_FRAME_INTR_BINTERVAL 9
/*
* The SET_ADDRESS request timeout will be 500 ms when
* USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT quirk flag is set.
*/
#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
@ -1904,6 +1916,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
usb_autopm_get_interface_no_resume(intf);
}
if ((id->driver_info & HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL) &&
desc->endpoint[0].desc.bInterval > USB_REDUCE_FRAME_INTR_BINTERVAL) {
desc->endpoint[0].desc.bInterval =
USB_REDUCE_FRAME_INTR_BINTERVAL;
/* Tell the HCD about the interrupt ep's new bInterval */
usb_set_interface(hdev, 0, 0);
}
if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs);
@ -4626,7 +4646,12 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit);
static int hub_set_address(struct usb_device *udev, int devnum)
{
int retval;
unsigned int timeout_ms = USB_CTRL_SET_TIMEOUT;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
if (hub->hdev->quirks & USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT)
timeout_ms = USB_SHORT_SET_ADDRESS_REQ_TIMEOUT;
/*
* The host controller will choose the device address,
@ -4639,11 +4664,11 @@ static int hub_set_address(struct usb_device *udev, int devnum)
if (udev->state != USB_STATE_DEFAULT)
return -EINVAL;
if (hcd->driver->address_device)
retval = hcd->driver->address_device(hcd, udev);
retval = hcd->driver->address_device(hcd, udev, timeout_ms);
else
retval = usb_control_msg(udev, usb_sndaddr0pipe(),
USB_REQ_SET_ADDRESS, 0, devnum, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
NULL, 0, timeout_ms);
if (retval == 0) {
update_devnum(udev, devnum);
/* Device now using proper address. */
@ -5895,6 +5920,21 @@ static const struct usb_device_id hub_id_table[] = {
.idVendor = USB_VENDOR_TEXAS_INSTRUMENTS,
.idProduct = USB_PRODUCT_TUSB8041_USB3,
.driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT,
.idVendor = USB_VENDOR_MICROCHIP,
.idProduct = USB_PRODUCT_USB4913,
.driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL},
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT,
.idVendor = USB_VENDOR_MICROCHIP,
.idProduct = USB_PRODUCT_USB4914,
.driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL},
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT,
.idVendor = USB_VENDOR_MICROCHIP,
.idProduct = USB_PRODUCT_USB4915,
.driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,

View File

@ -138,6 +138,9 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
case 'o':
flags |= USB_QUIRK_HUB_SLOW_RESET;
break;
case 'p':
flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT;
break;
/* Ignore unrecognized flag characters */
}
}
@ -527,6 +530,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM },
/* APTIV AUTOMOTIVE HUB */
{ USB_DEVICE(0x2c48, 0x0132), .driver_info =
USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT },
/* DJI CineSSD */
{ USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },

View File

@ -431,7 +431,7 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
struct device *dev;
argb.minor = minor;
argb.drv = &drv->drvwrap.driver;
argb.drv = &drv->driver;
dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface);

View File

@ -175,13 +175,7 @@ static inline int is_root_hub(struct usb_device *udev)
return (udev->parent == NULL);
}
/* Do the same for device drivers and interface drivers. */
static inline int is_usb_device_driver(struct device_driver *drv)
{
return container_of(drv, struct usbdrv_wrap, driver)->
for_devices;
}
extern bool is_usb_device_driver(const struct device_driver *drv);
/* for labeling diagnostics */
extern const char *usbcore_name;

View File

@ -130,6 +130,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->lpm_clock_gating = false;
p->besl = false;
p->hird_threshold_en = false;
p->no_clock_gating = true;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)

View File

@ -277,48 +277,11 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
/*
* We're resetting only the device side because, if we're in host mode,
* XHCI driver will reset the host block. If dwc3 was configured for
* host-only mode or current role is host, then we can return early.
* host-only mode, then we can return early.
*/
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
/*
* If the dr_mode is host and the dwc->current_dr_role is not the
* corresponding DWC3_GCTL_PRTCAP_HOST, then the dwc3_core_init_mode
* isn't executed yet. Ensure the phy is ready before the controller
* updates the GCTL.PRTCAPDIR or other settings by soft-resetting
* the phy.
*
* Note: GUSB3PIPECTL[n] and GUSB2PHYCFG[n] are port settings where n
* is port index. If this is a multiport host, then we need to reset
* all active ports.
*/
if (dwc->dr_mode == USB_DR_MODE_HOST) {
u32 usb3_port;
u32 usb2_port;
usb3_port = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
usb3_port |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port);
usb2_port = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
usb2_port |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port);
/* Small delay for phy reset assertion */
usleep_range(1000, 2000);
usb3_port &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port);
usb2_port &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port);
/* Wait for clock synchronization */
msleep(50);
return 0;
}
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
reg &= ~DWC3_DCTL_RUN_STOP;
@ -1367,6 +1330,18 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_config_threshold(dwc);
/*
* Modify this for all supported Super Speed ports when
* multiport support is added.
*/
if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET &&
(DWC3_IP_IS(DWC31)) &&
dwc->maximum_speed == USB_SPEED_SUPER) {
reg = dwc3_readl(dwc->regs, DWC3_LLUCTL);
reg |= DWC3_LLUCTL_FORCE_GEN1;
dwc3_writel(dwc->regs, DWC3_LLUCTL, reg);
}
return 0;
err_power_off_phy:
@ -2340,12 +2315,15 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
ret = dwc3_resume_common(dwc, PMSG_RESUME);
if (ret)
return ret;
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
ret = dwc3_resume_common(dwc, PMSG_RESUME);
if (ret) {
pm_runtime_set_suspended(dev);
return ret;
}
pm_runtime_enable(dev);
return 0;

View File

@ -172,6 +172,8 @@
#define DWC3_OEVTEN 0xcc0C
#define DWC3_OSTS 0xcc10
#define DWC3_LLUCTL 0xd024
/* Bit fields */
/* Global SoC Bus Configuration INCRx Register 0 */
@ -374,6 +376,7 @@
/* Global HWPARAMS4 Register */
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
#define DWC3_EXT_BUFF_CONTROL BIT(21)
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14)
@ -657,6 +660,9 @@
#define DWC3_OSTS_VBUSVLD BIT(1)
#define DWC3_OSTS_CONIDSTS BIT(0)
/* Force Gen1 speed on Gen2 link */
#define DWC3_LLUCTL_FORCE_GEN1 BIT(10)
/* Structures */
struct dwc3_trb;

View File

@ -363,8 +363,10 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
}
ret = clk_prepare_enable(dwc3_imx->hsio_clk);
if (ret)
if (ret) {
clk_disable_unprepare(dwc3_imx->suspend_clk);
return ret;
}
ret = dwc3_imx8mp_resume(dwc3_imx, PMSG_RESUME);

View File

@ -57,7 +57,7 @@ struct dwc3_acpi_pdata {
u32 qscratch_base_offset;
u32 qscratch_base_size;
u32 dwc3_core_base_size;
int hs_phy_irq_index;
int qusb2_phy_irq_index;
int dp_hs_phy_irq_index;
int dm_hs_phy_irq_index;
int ss_phy_irq_index;
@ -73,7 +73,7 @@ struct dwc3_qcom {
int num_clocks;
struct reset_control *resets;
int hs_phy_irq;
int qusb2_phy_irq;
int dp_hs_phy_irq;
int dm_hs_phy_irq;
int ss_phy_irq;
@ -372,7 +372,7 @@ static void dwc3_qcom_disable_wakeup_irq(int irq)
static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
{
dwc3_qcom_disable_wakeup_irq(qcom->hs_phy_irq);
dwc3_qcom_disable_wakeup_irq(qcom->qusb2_phy_irq);
if (qcom->usb2_speed == USB_SPEED_LOW) {
dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
@ -389,7 +389,7 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
{
dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq, 0);
dwc3_qcom_enable_wakeup_irq(qcom->qusb2_phy_irq, 0);
/*
* Configure DP/DM line interrupts based on the USB2 device attached to
@ -542,19 +542,19 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
int irq;
int ret;
irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq",
pdata ? pdata->hs_phy_irq_index : -1);
irq = dwc3_qcom_get_irq(pdev, "qusb2_phy",
pdata ? pdata->qusb2_phy_irq_index : -1);
if (irq > 0) {
/* Keep wakeup interrupts disabled until suspend */
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
qcom_dwc3_resume_irq,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"qcom_dwc3 HS", qcom);
"qcom_dwc3 QUSB2", qcom);
if (ret) {
dev_err(qcom->dev, "hs_phy_irq failed: %d\n", ret);
dev_err(qcom->dev, "qusb2_phy_irq failed: %d\n", ret);
return ret;
}
qcom->hs_phy_irq = irq;
qcom->qusb2_phy_irq = irq;
}
irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq",
@ -1058,7 +1058,7 @@ static const struct dwc3_acpi_pdata sdm845_acpi_pdata = {
.qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET,
.qscratch_base_size = SDM845_QSCRATCH_SIZE,
.dwc3_core_base_size = SDM845_DWC3_CORE_SIZE,
.hs_phy_irq_index = 1,
.qusb2_phy_irq_index = 1,
.dp_hs_phy_irq_index = 4,
.dm_hs_phy_irq_index = 3,
.ss_phy_irq_index = 2
@ -1068,7 +1068,7 @@ static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = {
.qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET,
.qscratch_base_size = SDM845_QSCRATCH_SIZE,
.dwc3_core_base_size = SDM845_DWC3_CORE_SIZE,
.hs_phy_irq_index = 1,
.qusb2_phy_irq_index = 1,
.dp_hs_phy_irq_index = 4,
.dm_hs_phy_irq_index = 3,
.ss_phy_irq_index = 2,

View File

@ -293,11 +293,15 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
goto err_clk_put;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_suspend_ignore_children(dev, false);
pm_runtime_get_sync(dev);
ret = devm_pm_runtime_enable(dev);
if (ret < 0)
goto err_pm_set_suspended;
return 0;
pm_suspend_ignore_children(dev, false);
return pm_runtime_resume_and_get(dev);
err_pm_set_suspended:
pm_runtime_set_suspended(dev);
err_clk_put:
clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
@ -315,7 +319,6 @@ static void dwc3_xlnx_remove(struct platform_device *pdev)
clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
priv_data->num_clocks = 0;
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
}

View File

@ -238,7 +238,10 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
struct dwc3_request *req;
req = next_request(&dep->pending_list);
dwc3_gadget_giveback(dep, req, -ECONNRESET);
if (!dwc->connected)
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
else
dwc3_gadget_giveback(dep, req, -ECONNRESET);
}
dwc->eps[0]->trb_enqueue = 0;

View File

@ -673,6 +673,12 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1);
}
if (dep->endpoint.fifo_mode) {
if (!(dwc->hwparams.hwparams4 & DWC3_EXT_BUFF_CONTROL))
return -EINVAL;
params.param1 |= DWC3_DEPCFG_EBC_HWO_NOWB | DWC3_DEPCFG_USE_EBC;
}
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}
@ -2103,7 +2109,17 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
list_for_each_entry(r, &dep->pending_list, list) {
if (r == req) {
dwc3_gadget_giveback(dep, req, -ECONNRESET);
/*
* Explicitly check for EP0/1 as dequeue for those
* EPs need to be handled differently. Control EP
* only deals with one USB req, and giveback will
* occur during dwc3_ep0_stall_and_restart(). EP0
* requests are never added to started_list.
*/
if (dep->number > 1)
dwc3_gadget_giveback(dep, req, -ECONNRESET);
else
dwc3_ep0_reset_state(dwc);
goto out;
}
}
@ -3973,6 +3989,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
dwc3_ep0_reset_state(dwc);
/*
* Request PM idle to address condition where usage count is
* already decremented to zero, but waiting for the disconnect
* interrupt to set dwc->connected to FALSE.
*/
pm_request_idle(dwc->dev);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)

View File

@ -26,6 +26,8 @@ struct dwc3;
#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13)
#define DWC3_DEPCFG_EBC_HWO_NOWB BIT(14)
#define DWC3_DEPCFG_USE_EBC BIT(15)
#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)

View File

@ -404,9 +404,9 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
else if (td->hw_alt_next != list_end)
mark = '/';
}
temp = snprintf(next, size,
"\n\t%p%c%s len=%d %08x urb %p",
td, mark, ({ char *tmp;
temp = scnprintf(next, size,
"\n\t%p%c%s len=%d %08x urb %p",
td, mark, ({ char *tmp;
switch ((scratch>>8)&0x03) {
case 0:
tmp = "out";
@ -424,15 +424,11 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
(scratch >> 16) & 0x7fff,
scratch,
td->urb);
if (size < temp)
temp = size;
size -= temp;
next += temp;
}
temp = snprintf(next, size, "\n");
if (size < temp)
temp = size;
temp = scnprintf(next, size, "\n");
size -= temp;
next += temp;

View File

@ -1094,10 +1094,10 @@ static int fotg210_udc_stop(struct usb_gadget *g)
/**
* fotg210_vbus_session - Called by external transceiver to enable/disable udc
* @_gadget: usb gadget
* @g: usb gadget
* @is_active: 0 if should disable UDC VBUS, 1 if should enable
*
* Returns 0
* Returns: %0
*/
static int fotg210_vbus_session(struct usb_gadget *g, int is_active)
{
@ -1122,7 +1122,7 @@ static const struct usb_gadget_ops fotg210_gadget_ops = {
*
* Called by the USB Phy when a cable connect or disconnect is sensed.
*
* Returns NOTIFY_OK or NOTIFY_DONE
* Returns: NOTIFY_OK or NOTIFY_DONE
*/
static int fotg210_phy_event(struct notifier_block *nb, unsigned long action,
void *data)

View File

@ -606,10 +606,11 @@ static struct config_group *function_make(
char *instance_name;
int ret;
ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
if (ret >= MAX_NAME_LEN)
if (strlen(name) >= MAX_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
scnprintf(buf, MAX_NAME_LEN, "%s", name);
func_name = buf;
instance_name = strchr(func_name, '.');
if (!instance_name) {
@ -701,10 +702,12 @@ static struct config_group *config_desc_make(
int ret;
gi = container_of(group, struct gadget_info, configs_group);
ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
if (ret >= MAX_NAME_LEN)
if (strlen(name) >= MAX_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
scnprintf(buf, MAX_NAME_LEN, "%s", name);
num_str = strchr(buf, '.');
if (!num_str) {
pr_err("Unable to locate . in name.bConfigurationValue\n");
@ -812,7 +815,7 @@ static ssize_t gadget_string_s_show(struct config_item *item, char *page)
struct gadget_string *string = to_gadget_string(item);
int ret;
ret = snprintf(page, sizeof(string->string), "%s\n", string->string);
ret = sysfs_emit(page, "%s\n", string->string);
return ret;
}

View File

@ -2931,9 +2931,8 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
ARRAY_SIZE(desc->CompatibleID) +
ARRAY_SIZE(desc->SubCompatibleID));
memcpy(t->os_desc->ext_compat_id, &desc->IDs,
sizeof_field(struct usb_ext_compat_desc, IDs));
length = sizeof(*desc);
}
break;

View File

@ -1177,11 +1177,11 @@ F_MIDI_OPT(out_ports, true, MAX_PORTS);
static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
{
struct f_midi_opts *opts = to_f_midi_opts(item);
int result;
ssize_t result;
mutex_lock(&opts->lock);
if (opts->id) {
result = strlcpy(page, opts->id, PAGE_SIZE);
result = strscpy(page, opts->id, PAGE_SIZE);
} else {
page[0] = 0;
result = 0;

View File

@ -103,6 +103,16 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* Delay for the transmit to wait before sending an unfilled NTB frame. */
#define TX_TIMEOUT_NSECS 300000
/*
* Although max mtu as dictated by u_ether is 15412 bytes, setting
* max_segment_sizeto 15426 would not be efficient. If user chooses segment
* size to be (>= 8192), then we can't aggregate more than one buffer in each
* NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
* maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
* at least 2 packets to be aggregated reducing wastage of NTB buffer space
*/
#define MAX_DATAGRAM_SIZE 8000
#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
USB_CDC_NCM_NTB32_SUPPORTED)
@ -179,7 +189,6 @@ static struct usb_cdc_ether_desc ecm_desc = {
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
.wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN),
.wNumberMCFilters = cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
@ -1166,11 +1175,15 @@ static int ncm_unwrap_ntb(struct gether *port,
struct sk_buff *skb2;
int ret = -EINVAL;
unsigned ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
unsigned frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize);
unsigned frame_max;
const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
int dgram_counter;
int to_process = skb->len;
struct f_ncm_opts *ncm_opts;
ncm_opts = container_of(port->func.fi, struct f_ncm_opts, func_inst);
frame_max = ncm_opts->max_segment_size;
parse_ntb:
tmp = (__le16 *)ntb_ptr;
@ -1430,8 +1443,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
mutex_lock(&ncm_opts->lock);
gether_set_gadget(ncm_opts->net, cdev->gadget);
if (!ncm_opts->bound)
if (!ncm_opts->bound) {
ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
status = gether_register_netdev(ncm_opts->net);
}
mutex_unlock(&ncm_opts->lock);
if (status)
@ -1474,6 +1489,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm_data_intf.bInterfaceNumber = status;
ncm_union_desc.bSlaveInterface0 = status;
ecm_desc.wMaxSegmentSize = ncm_opts->max_segment_size;
status = -ENODEV;
/* allocate instance-specific endpoints */
@ -1576,11 +1593,56 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page)
{
struct f_ncm_opts *opts = to_f_ncm_opts(item);
u16 segment_size;
mutex_lock(&opts->lock);
segment_size = opts->max_segment_size;
mutex_unlock(&opts->lock);
return sysfs_emit(page, "%u\n", segment_size);
}
static ssize_t ncm_opts_max_segment_size_store(struct config_item *item,
const char *page, size_t len)
{
struct f_ncm_opts *opts = to_f_ncm_opts(item);
u16 segment_size;
int ret;
mutex_lock(&opts->lock);
if (opts->refcnt) {
ret = -EBUSY;
goto out;
}
ret = kstrtou16(page, 0, &segment_size);
if (ret)
goto out;
if (segment_size > MAX_DATAGRAM_SIZE) {
ret = -EINVAL;
goto out;
}
opts->max_segment_size = segment_size;
ret = len;
out:
mutex_unlock(&opts->lock);
return ret;
}
CONFIGFS_ATTR(ncm_opts_, max_segment_size);
static struct configfs_attribute *ncm_attrs[] = {
&ncm_opts_attr_dev_addr,
&ncm_opts_attr_host_addr,
&ncm_opts_attr_qmult,
&ncm_opts_attr_ifname,
&ncm_opts_attr_max_segment_size,
NULL,
};
@ -1623,6 +1685,7 @@ static struct usb_function_instance *ncm_alloc_inst(void)
kfree(opts);
return ERR_CAST(net);
}
opts->max_segment_size = cpu_to_le16(ETH_FRAME_LEN);
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
descs[0] = &opts->ncm_os_desc;

View File

@ -1504,8 +1504,8 @@ static ssize_t tcm_usbg_tpg_nexus_show(struct config_item *item, char *page)
ret = -ENODEV;
goto out;
}
ret = snprintf(page, PAGE_SIZE, "%s\n",
tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
ret = sysfs_emit(page, "%s\n",
tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
out:
mutex_unlock(&tpg->tpg_mutex);
return ret;

View File

@ -292,6 +292,77 @@ static struct usb_descriptor_header *f_audio_desc[] = {
NULL,
};
/* Standard ISO OUT Endpoint Descriptor */
static struct usb_endpoint_descriptor ss_as_out_ep_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
| USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = {
.bLength = sizeof(ss_as_out_ep_desc_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
.bmAttributes = 0,
/* wBytesPerInterval = DYNAMIC */
};
/* Standard ISO OUT Endpoint Descriptor */
static struct usb_endpoint_descriptor ss_as_in_ep_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = {
.bLength = sizeof(ss_as_in_ep_desc_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
.bmAttributes = 0,
/* wBytesPerInterval = DYNAMIC */
};
static struct usb_descriptor_header *f_audio_ss_desc[] = {
(struct usb_descriptor_header *)&ac_interface_desc,
(struct usb_descriptor_header *)&ac_header_desc,
(struct usb_descriptor_header *)&usb_out_it_desc,
(struct usb_descriptor_header *)&io_out_ot_desc,
(struct usb_descriptor_header *)&io_in_it_desc,
(struct usb_descriptor_header *)&usb_in_ot_desc,
(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
(struct usb_descriptor_header *)&as_out_header_desc,
(struct usb_descriptor_header *)&as_out_type_i_desc,
//(struct usb_descriptor_header *)&as_out_ep_desc,
(struct usb_descriptor_header *)&ss_as_out_ep_desc,
(struct usb_descriptor_header *)&ss_as_out_ep_desc_comp,
(struct usb_descriptor_header *)&as_iso_out_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
(struct usb_descriptor_header *)&as_in_header_desc,
(struct usb_descriptor_header *)&as_in_type_i_desc,
//(struct usb_descriptor_header *)&as_in_ep_desc,
(struct usb_descriptor_header *)&ss_as_in_ep_desc,
(struct usb_descriptor_header *)&ss_as_in_ep_desc_comp,
(struct usb_descriptor_header *)&as_iso_in_desc,
NULL,
};
enum {
STR_AC_IF,
STR_USB_OUT_IT,
@ -1352,6 +1423,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
if (!ep)
goto err_free_fu;
ss_as_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress;
audio->out_ep = ep;
audio->out_ep->desc = &as_out_ep_desc;
}
@ -1360,6 +1432,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
if (!ep)
goto err_free_fu;
ss_as_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress;
audio->in_ep = ep;
audio->in_ep->desc = &as_in_ep_desc;
}
@ -1367,8 +1440,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
setup_descriptor(audio_opts);
/* copy descriptors, and track endpoint copies */
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
NULL);
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, f_audio_ss_desc,
f_audio_ss_desc);
if (status)
goto err_free_fu;
@ -1561,7 +1634,7 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@ -1579,7 +1652,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
ret = snprintf(opts->name, min(sizeof(opts->name), len), \
ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
"%s", page); \
\
end: \
@ -1685,7 +1758,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->req_number = UAC1_DEF_REQ_NUM;
snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
return &opts->func_inst;
}

View File

@ -2045,7 +2045,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@ -2063,7 +2063,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
ret = snprintf(opts->name, min(sizeof(opts->name), len), \
ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
"%s", page); \
\
end: \
@ -2187,7 +2187,7 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = FBACK_FAST_MAX;
snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE;
opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE;

View File

@ -263,10 +263,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
return 0;
}
void uvc_function_setup_continue(struct uvc_device *uvc)
void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
if (disable_ep && uvc->video.ep)
usb_ep_disable(uvc->video.ep);
usb_composite_setup_continue(cdev);
}
@ -337,15 +340,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_STREAMING)
return 0;
if (uvc->video.ep)
usb_ep_disable(uvc->video.ep);
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMOFF;
v4l2_event_queue(&uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_CONNECTED;
return 0;
return USB_GADGET_DELAYED_STATUS;
case 1:
if (uvc->state != UVC_STATE_CONNECTED)
@ -722,13 +721,29 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
}
uvc->enable_interrupt_ep = opts->enable_interrupt_ep;
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
/*
* gadget_is_{super|dual}speed() API check UDC controller capitblity. It should pass down
* highest speed endpoint descriptor to UDC controller. So UDC controller driver can reserve
* enough resource at check_config(), especially mult and maxburst. So UDC driver (such as
* cdns3) can know need at least (mult + 1) * (maxburst + 1) * wMaxPacketSize internal
* memory for this uvc functions. This is the only straightforward method to resolve the UDC
* resource allocation issue in the current gadget framework.
*/
if (gadget_is_superspeed(c->cdev->gadget))
ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
&uvc_ss_streaming_comp);
else if (gadget_is_dualspeed(cdev->gadget))
ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
else
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
if (!ep) {
uvcg_info(f, "Unable to allocate streaming EP\n");
goto error;
}
uvc->video.ep = ep;
uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
@ -960,7 +975,8 @@ static void uvc_free(struct usb_function *f)
struct uvc_device *uvc = to_uvc(f);
struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
func_inst);
config_item_put(&uvc->header->item);
if (!opts->header)
config_item_put(&uvc->header->item);
--opts->refcnt;
kfree(uvc);
}
@ -1052,25 +1068,29 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
streaming = config_group_find_item(&opts->func_inst.group, "streaming");
if (!streaming)
goto err_config;
if (opts->header) {
uvc->header = opts->header;
} else {
streaming = config_group_find_item(&opts->func_inst.group, "streaming");
if (!streaming)
goto err_config;
header = config_group_find_item(to_config_group(streaming), "header");
config_item_put(streaming);
if (!header)
goto err_config;
header = config_group_find_item(to_config_group(streaming), "header");
config_item_put(streaming);
if (!header)
goto err_config;
h = config_group_find_item(to_config_group(header), "h");
config_item_put(header);
if (!h)
goto err_config;
h = config_group_find_item(to_config_group(header), "h");
config_item_put(header);
if (!h)
goto err_config;
uvc->header = to_uvcg_streaming_header(h);
if (!uvc->header->linked) {
mutex_unlock(&opts->lock);
kfree(uvc);
return ERR_PTR(-EBUSY);
uvc->header = to_uvcg_streaming_header(h);
if (!uvc->header->linked) {
mutex_unlock(&opts->lock);
kfree(uvc);
return ERR_PTR(-EBUSY);
}
}
uvc->desc.extension_units = &opts->extension_units;

View File

@ -11,7 +11,7 @@
struct uvc_device;
void uvc_function_setup_continue(struct uvc_device *uvc);
void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep);
void uvc_function_connect(struct uvc_device *uvc);

View File

@ -1163,6 +1163,8 @@ struct net_device *gether_connect(struct gether *link)
if (netif_running(dev->net))
eth_start(dev, GFP_ATOMIC);
netif_device_attach(dev->net);
/* on error, disable any endpoints */
} else {
(void) usb_ep_disable(link->out_ep);

View File

@ -31,6 +31,8 @@ struct f_ncm_opts {
*/
struct mutex lock;
int refcnt;
u16 max_segment_size;
};
#endif /* U_NCM_H */

View File

@ -98,6 +98,12 @@ struct f_uvc_opts {
*/
struct mutex lock;
int refcnt;
/*
* Only for legacy gadget. Shall be NULL for configfs-composed gadgets,
* which is guaranteed by alloc_inst implementation of f_uvc doing kzalloc.
*/
struct uvcg_streaming_header *header;
};
#endif /* U_UVC_H */

View File

@ -81,6 +81,7 @@ struct uvc_request {
struct sg_table sgt;
u8 header[UVCG_REQUEST_HEADER_LEN];
struct uvc_buffer *last_buf;
struct list_head list;
};
struct uvc_video {
@ -101,9 +102,18 @@ struct uvc_video {
unsigned int uvc_num_requests;
/* Requests */
bool is_enabled; /* tracks whether video stream is enabled */
unsigned int req_size;
struct uvc_request *ureq;
struct list_head ureqs; /* all uvc_requests allocated by uvc_video */
/* USB requests that the video pump thread can encode into */
struct list_head req_free;
/*
* USB requests video pump thread has already encoded into. These are
* ready to be queued to the endpoint.
*/
struct list_head req_ready;
spinlock_t req_lock;
unsigned int req_int_count;
@ -177,7 +187,7 @@ struct uvc_file_handle {
* Functions
*/
extern void uvc_function_setup_continue(struct uvc_device *uvc);
extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep);
extern void uvc_function_connect(struct uvc_device *uvc);
extern void uvc_function_disconnect(struct uvc_device *uvc);

View File

@ -3414,7 +3414,7 @@ static ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\
int result; \
\
mutex_lock(&opts->lock); \
result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\
result = scnprintf(page, sizeof(opts->aname), "%s", opts->aname);\
mutex_unlock(&opts->lock); \
\
return result; \

View File

@ -443,7 +443,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
return -EINVAL;
/* Enable UVC video. */
ret = uvcg_video_enable(video, 1);
ret = uvcg_video_enable(video);
if (ret < 0)
return ret;
@ -451,7 +451,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
* Complete the alternate setting selection setup phase now that
* userspace is ready to provide video frames.
*/
uvc_function_setup_continue(uvc);
uvc_function_setup_continue(uvc, 0);
uvc->state = UVC_STATE_STREAMING;
return 0;
@ -463,11 +463,18 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
int ret = 0;
if (type != video->queue.queue.type)
return -EINVAL;
return uvcg_video_enable(video, 0);
ret = uvcg_video_disable(video);
if (ret < 0)
return ret;
uvc->state = UVC_STATE_CONNECTED;
uvc_function_setup_continue(uvc, 1);
return 0;
}
static int
@ -500,7 +507,7 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
static void uvc_v4l2_disable(struct uvc_device *uvc)
{
uvc_function_disconnect(uvc);
uvcg_video_enable(&uvc->video, 0);
uvcg_video_disable(&uvc->video);
uvcg_free_buffers(&uvc->video.queue);
uvc->func_connected = false;
wake_up_interruptible(&uvc->func_connected_queue);
@ -647,4 +654,3 @@ const struct v4l2_file_operations uvc_v4l2_fops = {
.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
#endif
};

View File

@ -227,6 +227,28 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
* Request handling
*/
/*
* Callers must take care to hold req_lock when this function may be called
* from multiple threads. For example, when frames are streaming to the host.
*/
static void
uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep)
{
sg_free_table(&ureq->sgt);
if (ureq->req && ep) {
usb_ep_free_request(ep, ureq->req);
ureq->req = NULL;
}
kfree(ureq->req_buffer);
ureq->req_buffer = NULL;
if (!list_empty(&ureq->list))
list_del_init(&ureq->list);
kfree(ureq);
}
static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
{
int ret;
@ -247,14 +269,127 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
return ret;
}
/* This function must be called with video->req_lock held. */
static int uvcg_video_usb_req_queue(struct uvc_video *video,
struct usb_request *req, bool queue_to_ep)
{
bool is_bulk = video->max_payload_size;
struct list_head *list = NULL;
if (!video->is_enabled)
return -ENODEV;
if (queue_to_ep) {
struct uvc_request *ureq = req->context;
/*
* With USB3 handling more requests at a higher speed, we can't
* afford to generate an interrupt for every request. Decide to
* interrupt:
*
* - When no more requests are available in the free queue, as
* this may be our last chance to refill the endpoint's
* request queue.
*
* - When this is request is the last request for the video
* buffer, as we want to start sending the next video buffer
* ASAP in case it doesn't get started already in the next
* iteration of this loop.
*
* - Four times over the length of the requests queue (as
* indicated by video->uvc_num_requests), as a trade-off
* between latency and interrupt load.
*/
if (list_empty(&video->req_free) || ureq->last_buf ||
!(video->req_int_count %
DIV_ROUND_UP(video->uvc_num_requests, 4))) {
video->req_int_count = 0;
req->no_interrupt = 0;
} else {
req->no_interrupt = 1;
}
video->req_int_count++;
return uvcg_video_ep_queue(video, req);
}
/*
* If we're not queuing to the ep, for isoc we're queuing
* to the req_ready list, otherwise req_free.
*/
list = is_bulk ? &video->req_free : &video->req_ready;
list_add_tail(&req->list, list);
return 0;
}
/*
* Must only be called from uvcg_video_enable - since after that we only want to
* queue requests to the endpoint from the uvc_video_complete complete handler.
* This function is needed in order to 'kick start' the flow of requests from
* gadget driver to the usb controller.
*/
static void uvc_video_ep_queue_initial_requests(struct uvc_video *video)
{
struct usb_request *req = NULL;
unsigned long flags = 0;
unsigned int count = 0;
int ret = 0;
/*
* We only queue half of the free list since we still want to have
* some free usb_requests in the free list for the video_pump async_wq
* thread to encode uvc buffers into. Otherwise we could get into a
* situation where the free list does not have any usb requests to
* encode into - we always end up queueing 0 length requests to the
* end point.
*/
unsigned int half_list_size = video->uvc_num_requests / 2;
spin_lock_irqsave(&video->req_lock, flags);
/*
* Take these requests off the free list and queue them all to the
* endpoint. Since we queue 0 length requests with the req_lock held,
* there isn't any 'data' race involved here with the complete handler.
*/
while (count < half_list_size) {
req = list_first_entry(&video->req_free, struct usb_request,
list);
list_del(&req->list);
req->length = 0;
ret = uvcg_video_ep_queue(video, req);
if (ret < 0) {
uvcg_queue_cancel(&video->queue, 0);
break;
}
count++;
}
spin_unlock_irqrestore(&video->req_lock, flags);
}
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_request *ureq = req->context;
struct uvc_video *video = ureq->video;
struct uvc_video_queue *queue = &video->queue;
struct uvc_device *uvc = video->uvc;
struct uvc_buffer *last_buf;
unsigned long flags;
bool is_bulk = video->max_payload_size;
int ret = 0;
spin_lock_irqsave(&video->req_lock, flags);
if (!video->is_enabled) {
/*
* When is_enabled is false, uvcg_video_disable() ensures
* that in-flight uvc_buffers are returned, so we can
* safely call free_request without worrying about
* last_buf.
*/
uvc_video_free_request(ureq, ep);
spin_unlock_irqrestore(&video->req_lock, flags);
return;
}
last_buf = ureq->last_buf;
ureq->last_buf = NULL;
spin_unlock_irqrestore(&video->req_lock, flags);
switch (req->status) {
case 0:
@ -277,44 +412,85 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
uvcg_queue_cancel(queue, 0);
}
if (ureq->last_buf) {
uvcg_complete_buffer(&video->queue, ureq->last_buf);
ureq->last_buf = NULL;
if (last_buf) {
spin_lock_irqsave(&queue->irqlock, flags);
uvcg_complete_buffer(queue, last_buf);
spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
/*
* Video stream might have been disabled while we were
* processing the current usb_request. So make sure
* we're still streaming before queueing the usb_request
* back to req_free
*/
if (video->is_enabled) {
/*
* Here we check whether any request is available in the ready
* list. If it is, queue it to the ep and add the current
* usb_request to the req_free list - for video_pump to fill in.
* Otherwise, just use the current usb_request to queue a 0
* length request to the ep. Since we always add to the req_free
* list if we dequeue from the ready list, there will never
* be a situation where the req_free list is completely out of
* requests and cannot recover.
*/
struct usb_request *to_queue = req;
if (uvc->state == UVC_STATE_STREAMING)
queue_work(video->async_wq, &video->pump);
to_queue->length = 0;
if (!list_empty(&video->req_ready)) {
to_queue = list_first_entry(&video->req_ready,
struct usb_request, list);
list_del(&to_queue->list);
list_add_tail(&req->list, &video->req_free);
/*
* Queue work to the wq as well since it is possible that a
* buffer may not have been completely encoded with the set of
* in-flight usb requests for whih the complete callbacks are
* firing.
* In that case, if we do not queue work to the worker thread,
* the buffer will never be marked as complete - and therefore
* not be returned to userpsace. As a result,
* dequeue -> queue -> dequeue flow of uvc buffers will not
* happen.
*/
queue_work(video->async_wq, &video->pump);
}
/*
* Queue to the endpoint. The actual queueing to ep will
* only happen on one thread - the async_wq for bulk endpoints
* and this thread for isoc endpoints.
*/
ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk);
if (ret < 0) {
/*
* Endpoint error, but the stream is still enabled.
* Put request back in req_free for it to be cleaned
* up later.
*/
list_add_tail(&to_queue->list, &video->req_free);
}
} else {
uvc_video_free_request(ureq, ep);
ret = 0;
}
spin_unlock_irqrestore(&video->req_lock, flags);
if (ret < 0)
uvcg_queue_cancel(queue, 0);
}
static int
uvc_video_free_requests(struct uvc_video *video)
{
unsigned int i;
struct uvc_request *ureq, *temp;
if (video->ureq) {
for (i = 0; i < video->uvc_num_requests; ++i) {
sg_free_table(&video->ureq[i].sgt);
if (video->ureq[i].req) {
usb_ep_free_request(video->ep, video->ureq[i].req);
video->ureq[i].req = NULL;
}
if (video->ureq[i].req_buffer) {
kfree(video->ureq[i].req_buffer);
video->ureq[i].req_buffer = NULL;
}
}
kfree(video->ureq);
video->ureq = NULL;
}
list_for_each_entry_safe(ureq, temp, &video->ureqs, list)
uvc_video_free_request(ureq, video->ep);
INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
INIT_LIST_HEAD(&video->req_ready);
video->req_size = 0;
return 0;
}
@ -322,6 +498,7 @@ uvc_video_free_requests(struct uvc_video *video)
static int
uvc_video_alloc_requests(struct uvc_video *video)
{
struct uvc_request *ureq;
unsigned int req_size;
unsigned int i;
int ret = -ENOMEM;
@ -332,29 +509,33 @@ uvc_video_alloc_requests(struct uvc_video *video)
* max_t(unsigned int, video->ep->maxburst, 1)
* (video->ep->mult);
video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL);
if (video->ureq == NULL)
return -ENOMEM;
for (i = 0; i < video->uvc_num_requests; ++i) {
video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL);
if (video->ureq[i].req_buffer == NULL)
for (i = 0; i < video->uvc_num_requests; i++) {
ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL);
if (ureq == NULL)
goto error;
video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
if (video->ureq[i].req == NULL)
INIT_LIST_HEAD(&ureq->list);
list_add_tail(&ureq->list, &video->ureqs);
ureq->req_buffer = kmalloc(req_size, GFP_KERNEL);
if (ureq->req_buffer == NULL)
goto error;
video->ureq[i].req->buf = video->ureq[i].req_buffer;
video->ureq[i].req->length = 0;
video->ureq[i].req->complete = uvc_video_complete;
video->ureq[i].req->context = &video->ureq[i];
video->ureq[i].video = video;
video->ureq[i].last_buf = NULL;
ureq->req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
if (ureq->req == NULL)
goto error;
list_add_tail(&video->ureq[i].req->list, &video->req_free);
ureq->req->buf = ureq->req_buffer;
ureq->req->length = 0;
ureq->req->complete = uvc_video_complete;
ureq->req->context = ureq;
ureq->video = video;
ureq->last_buf = NULL;
list_add_tail(&ureq->req->list, &video->req_free);
/* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
sg_alloc_table(&video->ureq[i].sgt,
sg_alloc_table(&ureq->sgt,
DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
PAGE_SIZE) + 2, GFP_KERNEL);
}
@ -387,16 +568,18 @@ static void uvcg_video_pump(struct work_struct *work)
struct usb_request *req = NULL;
struct uvc_buffer *buf;
unsigned long flags;
bool buf_done;
int ret;
int ret = 0;
while (true) {
if (!video->ep->enabled)
return;
while (video->ep->enabled) {
/*
* Retrieve the first available USB request, protected by the
* request lock.
* Check is_enabled and retrieve the first available USB
* request, protected by the request lock.
*/
spin_lock_irqsave(&video->req_lock, flags);
if (list_empty(&video->req_free)) {
if (!video->is_enabled || list_empty(&video->req_free)) {
spin_unlock_irqrestore(&video->req_lock, flags);
return;
}
@ -414,15 +597,6 @@ static void uvcg_video_pump(struct work_struct *work)
if (buf != NULL) {
video->encode(req, video, buf);
buf_done = buf->state == UVC_BUF_STATE_DONE;
} else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) {
/*
* No video buffer available; the queue is still connected and
* we're transferring over ISOC. Queue a 0 length request to
* prevent missed ISOC transfers.
*/
req->length = 0;
buf_done = false;
} else {
/*
* Either the queue has been disconnected or no video buffer
@ -433,62 +607,123 @@ static void uvcg_video_pump(struct work_struct *work)
break;
}
/*
* With USB3 handling more requests at a higher speed, we can't
* afford to generate an interrupt for every request. Decide to
* interrupt:
*
* - When no more requests are available in the free queue, as
* this may be our last chance to refill the endpoint's
* request queue.
*
* - When this is request is the last request for the video
* buffer, as we want to start sending the next video buffer
* ASAP in case it doesn't get started already in the next
* iteration of this loop.
*
* - Four times over the length of the requests queue (as
* indicated by video->uvc_num_requests), as a trade-off
* between latency and interrupt load.
*/
if (list_empty(&video->req_free) || buf_done ||
!(video->req_int_count %
DIV_ROUND_UP(video->uvc_num_requests, 4))) {
video->req_int_count = 0;
req->no_interrupt = 0;
} else {
req->no_interrupt = 1;
}
/* Queue the USB request */
ret = uvcg_video_ep_queue(video, req);
spin_unlock_irqrestore(&queue->irqlock, flags);
spin_lock_irqsave(&video->req_lock, flags);
/* For bulk end points we queue from the worker thread
* since we would preferably not want to wait on requests
* to be ready, in the uvcg_video_complete() handler.
* For isoc endpoints we add the request to the ready list
* and only queue it to the endpoint from the complete handler.
*/
ret = uvcg_video_usb_req_queue(video, req, is_bulk);
spin_unlock_irqrestore(&video->req_lock, flags);
if (ret < 0) {
uvcg_queue_cancel(queue, 0);
break;
}
/* Endpoint now owns the request */
/* The request is owned by the endpoint / ready list. */
req = NULL;
video->req_int_count++;
}
if (!req)
return;
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
if (video->is_enabled)
list_add_tail(&req->list, &video->req_free);
else
uvc_video_free_request(req->context, video->ep);
spin_unlock_irqrestore(&video->req_lock, flags);
return;
}
/*
* Enable or disable the video stream.
* Disable the video stream
*/
int uvcg_video_enable(struct uvc_video *video, int enable)
int
uvcg_video_disable(struct uvc_video *video)
{
unsigned long flags;
struct list_head inflight_bufs;
struct usb_request *req, *temp;
struct uvc_buffer *buf, *btemp;
struct uvc_request *ureq, *utemp;
if (video->ep == NULL) {
uvcg_info(&video->uvc->func,
"Video disable failed, device is uninitialized.\n");
return -ENODEV;
}
INIT_LIST_HEAD(&inflight_bufs);
spin_lock_irqsave(&video->req_lock, flags);
video->is_enabled = false;
/*
* Remove any in-flight buffers from the uvc_requests
* because we want to return them before cancelling the
* queue. This ensures that we aren't stuck waiting for
* all complete callbacks to come through before disabling
* vb2 queue.
*/
list_for_each_entry(ureq, &video->ureqs, list) {
if (ureq->last_buf) {
list_add_tail(&ureq->last_buf->queue, &inflight_bufs);
ureq->last_buf = NULL;
}
}
spin_unlock_irqrestore(&video->req_lock, flags);
cancel_work_sync(&video->pump);
uvcg_queue_cancel(&video->queue, 0);
spin_lock_irqsave(&video->req_lock, flags);
/*
* Remove all uvc_requests from ureqs with list_del_init
* This lets uvc_video_free_request correctly identify
* if the uvc_request is attached to a list or not when freeing
* memory.
*/
list_for_each_entry_safe(ureq, utemp, &video->ureqs, list)
list_del_init(&ureq->list);
list_for_each_entry_safe(req, temp, &video->req_free, list) {
list_del(&req->list);
uvc_video_free_request(req->context, video->ep);
}
list_for_each_entry_safe(req, temp, &video->req_ready, list) {
list_del(&req->list);
uvc_video_free_request(req->context, video->ep);
}
INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
INIT_LIST_HEAD(&video->req_ready);
video->req_size = 0;
spin_unlock_irqrestore(&video->req_lock, flags);
/*
* Return all the video buffers before disabling the queue.
*/
spin_lock_irqsave(&video->queue.irqlock, flags);
list_for_each_entry_safe(buf, btemp, &inflight_bufs, queue) {
list_del(&buf->queue);
uvcg_complete_buffer(&video->queue, buf);
}
spin_unlock_irqrestore(&video->queue.irqlock, flags);
uvcg_queue_enable(&video->queue, 0);
return 0;
}
/*
* Enable the video stream.
*/
int uvcg_video_enable(struct uvc_video *video)
{
unsigned int i;
int ret;
if (video->ep == NULL) {
@ -497,18 +732,13 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
return -ENODEV;
}
if (!enable) {
cancel_work_sync(&video->pump);
uvcg_queue_cancel(&video->queue, 0);
for (i = 0; i < video->uvc_num_requests; ++i)
if (video->ureq && video->ureq[i].req)
usb_ep_dequeue(video->ep, video->ureq[i].req);
uvc_video_free_requests(video);
uvcg_queue_enable(&video->queue, 0);
return 0;
}
/*
* Safe to access request related fields without req_lock because
* this is the only thread currently active, and no other
* request handling thread will become active until this function
* returns.
*/
video->is_enabled = true;
if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
return ret;
@ -525,7 +755,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
video->req_int_count = 0;
queue_work(video->async_wq, &video->pump);
uvc_video_ep_queue_initial_requests(video);
return ret;
}
@ -535,7 +765,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
*/
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{
video->is_enabled = false;
INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
INIT_LIST_HEAD(&video->req_ready);
spin_lock_init(&video->req_lock);
INIT_WORK(&video->pump, uvcg_video_pump);

View File

@ -14,7 +14,8 @@
struct uvc_video;
int uvcg_video_enable(struct uvc_video *video, int enable);
int uvcg_video_enable(struct uvc_video *video);
int uvcg_video_disable(struct uvc_video *video);
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);

View File

@ -12,6 +12,7 @@
#include <linux/usb/video.h>
#include "u_uvc.h"
#include "uvc_configfs.h"
USB_GADGET_COMPOSITE_OPTIONS();
@ -84,8 +85,6 @@ static struct usb_device_descriptor webcam_device_descriptor = {
.bNumConfigurations = 0, /* dynamic */
};
DECLARE_UVC_HEADER_DESCRIPTOR(1);
static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
.bLength = UVC_DT_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
@ -158,72 +157,179 @@ static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
.bmaControls[1][0] = 4,
};
static const struct uvc_format_uncompressed uvc_format_yuv = {
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
.bFormatIndex = 1,
.bNumFrameDescriptors = 2,
.guidFormat =
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
.bBitsPerPixel = 16,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
static const struct uvcg_color_matching uvcg_color_matching = {
.desc = {
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_COLORFORMAT,
.bColorPrimaries = 1,
.bTransferCharacteristics = 1,
.bMatrixCoefficients = 4,
},
};
static struct uvcg_uncompressed uvcg_format_yuv = {
.fmt = {
.type = UVCG_UNCOMPRESSED,
/* add to .frames and fill .num_frames at runtime */
.color_matching = (struct uvcg_color_matching *)&uvcg_color_matching,
},
.desc = {
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
.bFormatIndex = 1,
.bNumFrameDescriptors = 2,
.guidFormat = {
'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
},
.bBitsPerPixel = 16,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
},
};
static struct uvcg_format_ptr uvcg_format_ptr_yuv = {
.fmt = &uvcg_format_yuv.fmt,
};
DECLARE_UVC_FRAME_UNCOMPRESSED(1);
DECLARE_UVC_FRAME_UNCOMPRESSED(3);
#define UVCG_WIDTH_360P 640
#define UVCG_HEIGHT_360P 360
#define UVCG_MIN_BITRATE_360P 18432000
#define UVCG_MAX_BITRATE_360P 55296000
#define UVCG_MAX_VIDEO_FB_SZ_360P 460800
#define UVCG_FRM_INTERV_0_360P 666666
#define UVCG_FRM_INTERV_1_360P 1000000
#define UVCG_FRM_INTERV_2_360P 5000000
#define UVCG_DEFAULT_FRM_INTERV_360P UVCG_FRM_INTERV_0_360P
static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 1,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(640),
.wHeight = cpu_to_le16(360),
.dwMinBitRate = cpu_to_le32(18432000),
.dwMaxBitRate = cpu_to_le32(55296000),
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
.dwDefaultFrameInterval = cpu_to_le32(666666),
.wWidth = cpu_to_le16(UVCG_WIDTH_360P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_360P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
.bFrameIntervalType = 3,
.dwFrameInterval[0] = cpu_to_le32(666666),
.dwFrameInterval[1] = cpu_to_le32(1000000),
.dwFrameInterval[2] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P),
.dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P),
.dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P),
};
static u32 uvcg_frame_yuv_360p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_360P,
[1] = UVCG_FRM_INTERV_1_360P,
[2] = UVCG_FRM_INTERV_2_360P,
};
static const struct uvcg_frame uvcg_frame_yuv_360p = {
.fmt_type = UVCG_UNCOMPRESSED,
.frame = {
.b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED,
.b_frame_index = 1,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_360P,
.w_height = UVCG_HEIGHT_360P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_360P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_360P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P,
.b_frame_interval_type = 3,
},
.dw_frame_interval = uvcg_frame_yuv_360p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = {
.frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p,
};
#define UVCG_WIDTH_720P 1280
#define UVCG_HEIGHT_720P 720
#define UVCG_MIN_BITRATE_720P 29491200
#define UVCG_MAX_BITRATE_720P 29491200
#define UVCG_MAX_VIDEO_FB_SZ_720P 1843200
#define UVCG_FRM_INTERV_0_720P 5000000
#define UVCG_DEFAULT_FRM_INTERV_720P UVCG_FRM_INTERV_0_720P
static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 2,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(1280),
.wHeight = cpu_to_le16(720),
.dwMinBitRate = cpu_to_le32(29491200),
.dwMaxBitRate = cpu_to_le32(29491200),
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
.dwDefaultFrameInterval = cpu_to_le32(5000000),
.wWidth = cpu_to_le16(UVCG_WIDTH_720P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_720P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
.bFrameIntervalType = 1,
.dwFrameInterval[0] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P),
};
static const struct uvc_format_mjpeg uvc_format_mjpg = {
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
.bFormatIndex = 2,
.bNumFrameDescriptors = 2,
.bmFlags = 0,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
static u32 uvcg_frame_yuv_720p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_720P,
};
static const struct uvcg_frame uvcg_frame_yuv_720p = {
.fmt_type = UVCG_UNCOMPRESSED,
.frame = {
.b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED,
.b_frame_index = 2,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_720P,
.w_height = UVCG_HEIGHT_720P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_720P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_720P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P,
.b_frame_interval_type = 1,
},
.dw_frame_interval = uvcg_frame_yuv_720p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = {
.frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p,
};
static struct uvcg_mjpeg uvcg_format_mjpeg = {
.fmt = {
.type = UVCG_MJPEG,
/* add to .frames and fill .num_frames at runtime */
.color_matching = (struct uvcg_color_matching *)&uvcg_color_matching,
},
.desc = {
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
.bFormatIndex = 2,
.bNumFrameDescriptors = 2,
.bmFlags = 0,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
},
};
static struct uvcg_format_ptr uvcg_format_ptr_mjpeg = {
.fmt = &uvcg_format_mjpeg.fmt,
};
DECLARE_UVC_FRAME_MJPEG(1);
@ -235,16 +341,45 @@ static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 1,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(640),
.wHeight = cpu_to_le16(360),
.dwMinBitRate = cpu_to_le32(18432000),
.dwMaxBitRate = cpu_to_le32(55296000),
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
.dwDefaultFrameInterval = cpu_to_le32(666666),
.wWidth = cpu_to_le16(UVCG_WIDTH_360P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_360P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
.bFrameIntervalType = 3,
.dwFrameInterval[0] = cpu_to_le32(666666),
.dwFrameInterval[1] = cpu_to_le32(1000000),
.dwFrameInterval[2] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P),
.dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P),
.dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P),
};
static u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_360P,
[1] = UVCG_FRM_INTERV_1_360P,
[2] = UVCG_FRM_INTERV_2_360P,
};
static const struct uvcg_frame uvcg_frame_mjpeg_360p = {
.fmt_type = UVCG_MJPEG,
.frame = {
.b_length = UVC_DT_FRAME_MJPEG_SIZE(3),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_MJPEG,
.b_frame_index = 1,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_360P,
.w_height = UVCG_HEIGHT_360P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_360P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_360P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P,
.b_frame_interval_type = 3,
},
.dw_frame_interval = uvcg_frame_mjpeg_360p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = {
.frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p,
};
static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
@ -253,23 +388,44 @@ static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 2,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(1280),
.wHeight = cpu_to_le16(720),
.dwMinBitRate = cpu_to_le32(29491200),
.dwMaxBitRate = cpu_to_le32(29491200),
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
.dwDefaultFrameInterval = cpu_to_le32(5000000),
.wWidth = cpu_to_le16(UVCG_WIDTH_720P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_720P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
.bFrameIntervalType = 1,
.dwFrameInterval[0] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P),
};
static const struct uvc_color_matching_descriptor uvc_color_matching = {
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_COLORFORMAT,
.bColorPrimaries = 1,
.bTransferCharacteristics = 1,
.bMatrixCoefficients = 4,
static u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_720P,
};
static const struct uvcg_frame uvcg_frame_mjpeg_720p = {
.fmt_type = UVCG_MJPEG,
.frame = {
.b_length = UVC_DT_FRAME_MJPEG_SIZE(1),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_MJPEG,
.b_frame_index = 2,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_720P,
.w_height = UVCG_HEIGHT_720P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_720P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_720P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P,
.b_frame_interval_type = 1,
},
.dw_frame_interval = uvcg_frame_mjpeg_720p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = {
.frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p,
};
static struct uvcg_streaming_header uvcg_streaming_header = {
};
static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
@ -290,40 +446,40 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
@ -387,6 +543,23 @@ webcam_bind(struct usb_composite_dev *cdev)
uvc_opts->hs_streaming = uvc_hs_streaming_cls;
uvc_opts->ss_streaming = uvc_ss_streaming_cls;
INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames);
list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames);
list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames);
uvcg_format_yuv.fmt.num_frames = 2;
INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames);
list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames);
list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames);
uvcg_format_mjpeg.fmt.num_frames = 2;
INIT_LIST_HEAD(&uvcg_streaming_header.formats);
list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats);
list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats);
uvcg_streaming_header.num_fmt = 2;
uvc_opts->header = &uvcg_streaming_header;
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/

View File

@ -1924,7 +1924,7 @@ err_unprepare_fclk:
return retval;
}
static int at91udc_remove(struct platform_device *pdev)
static void at91udc_remove(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
unsigned long flags;
@ -1932,8 +1932,11 @@ static int at91udc_remove(struct platform_device *pdev)
DBG("remove\n");
usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
if (udc->driver) {
dev_err(&pdev->dev,
"Driver still in use but removing anyhow\n");
return;
}
spin_lock_irqsave(&udc->lock, flags);
pullup(udc, 0);
@ -1943,8 +1946,6 @@ static int at91udc_remove(struct platform_device *pdev)
remove_debug_file(udc);
clk_unprepare(udc->fclk);
clk_unprepare(udc->iclk);
return 0;
}
#ifdef CONFIG_PM
@ -2001,7 +2002,7 @@ static int at91udc_resume(struct platform_device *pdev)
static struct platform_driver at91_udc_driver = {
.probe = at91udc_probe,
.remove = at91udc_remove,
.remove_new = at91udc_remove,
.shutdown = at91udc_shutdown,
.suspend = at91udc_suspend,
.resume = at91udc_resume,

View File

@ -94,7 +94,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf,
inode_lock(file_inode(file));
list_for_each_entry_safe(req, tmp_req, queue, queue) {
len = snprintf(tmpbuf, sizeof(tmpbuf),
len = scnprintf(tmpbuf, sizeof(tmpbuf),
"%8p %08x %c%c%c %5d %c%c%c\n",
req->req.buf, req->req.length,
req->req.no_interrupt ? 'i' : 'I',
@ -104,7 +104,6 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf,
req->submitted ? 'F' : 'f',
req->using_dma ? 'D' : 'd',
req->last_transaction ? 'L' : 'l');
len = min(len, sizeof(tmpbuf));
if (len > nbytes)
break;

View File

@ -16,34 +16,34 @@ static inline const char *cdns2_decode_usb_irq(char *str, size_t size,
{
int ret;
ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
ret = scnprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
if (usb_irq & USBIRQ_SOF)
ret += snprintf(str + ret, size - ret, "SOF ");
ret += scnprintf(str + ret, size - ret, "SOF ");
if (usb_irq & USBIRQ_SUTOK)
ret += snprintf(str + ret, size - ret, "SUTOK ");
ret += scnprintf(str + ret, size - ret, "SUTOK ");
if (usb_irq & USBIRQ_SUDAV)
ret += snprintf(str + ret, size - ret, "SETUP ");
ret += scnprintf(str + ret, size - ret, "SETUP ");
if (usb_irq & USBIRQ_SUSPEND)
ret += snprintf(str + ret, size - ret, "Suspend ");
ret += scnprintf(str + ret, size - ret, "Suspend ");
if (usb_irq & USBIRQ_URESET)
ret += snprintf(str + ret, size - ret, "Reset ");
ret += scnprintf(str + ret, size - ret, "Reset ");
if (usb_irq & USBIRQ_HSPEED)
ret += snprintf(str + ret, size - ret, "HS ");
ret += scnprintf(str + ret, size - ret, "HS ");
if (usb_irq & USBIRQ_LPM)
ret += snprintf(str + ret, size - ret, "LPM ");
ret += scnprintf(str + ret, size - ret, "LPM ");
ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
ret += scnprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
if (ext_irq & EXTIRQ_WAKEUP)
ret += snprintf(str + ret, size - ret, "Wakeup ");
ret += scnprintf(str + ret, size - ret, "Wakeup ");
if (ext_irq & EXTIRQ_VBUSFAULT_FALL)
ret += snprintf(str + ret, size - ret, "VBUS_FALL ");
ret += scnprintf(str + ret, size - ret, "VBUS_FALL ");
if (ext_irq & EXTIRQ_VBUSFAULT_RISE)
ret += snprintf(str + ret, size - ret, "VBUS_RISE ");
ret += scnprintf(str + ret, size - ret, "VBUS_RISE ");
if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
@ -54,28 +54,28 @@ static inline const char *cdns2_decode_dma_irq(char *str, size_t size,
{
int ret;
ret = snprintf(str, size, "ISTS: %08x, %s: %08x ",
ep_ists, ep_name, ep_sts);
ret = scnprintf(str, size, "ISTS: %08x, %s: %08x ",
ep_ists, ep_name, ep_sts);
if (ep_sts & DMA_EP_STS_IOC)
ret += snprintf(str + ret, size - ret, "IOC ");
ret += scnprintf(str + ret, size - ret, "IOC ");
if (ep_sts & DMA_EP_STS_ISP)
ret += snprintf(str + ret, size - ret, "ISP ");
ret += scnprintf(str + ret, size - ret, "ISP ");
if (ep_sts & DMA_EP_STS_DESCMIS)
ret += snprintf(str + ret, size - ret, "DESCMIS ");
ret += scnprintf(str + ret, size - ret, "DESCMIS ");
if (ep_sts & DMA_EP_STS_TRBERR)
ret += snprintf(str + ret, size - ret, "TRBERR ");
ret += scnprintf(str + ret, size - ret, "TRBERR ");
if (ep_sts & DMA_EP_STS_OUTSMM)
ret += snprintf(str + ret, size - ret, "OUTSMM ");
ret += scnprintf(str + ret, size - ret, "OUTSMM ");
if (ep_sts & DMA_EP_STS_ISOERR)
ret += snprintf(str + ret, size - ret, "ISOERR ");
ret += scnprintf(str + ret, size - ret, "ISOERR ");
if (ep_sts & DMA_EP_STS_DBUSY)
ret += snprintf(str + ret, size - ret, "DBUSY ");
ret += scnprintf(str + ret, size - ret, "DBUSY ");
if (DMA_EP_STS_CCS(ep_sts))
ret += snprintf(str + ret, size - ret, "CCS ");
ret += scnprintf(str + ret, size - ret, "CCS ");
if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
@ -105,43 +105,43 @@ static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep,
int ret;
int i;
ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name);
ret = scnprintf(str, size, "\n\t\tTR for %s:", pep->name);
trb = &trbs[ring->dequeue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += snprintf(str + ret, size - ret,
"\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
ring->dequeue, trb, &dma);
ret += scnprintf(str + ret, size - ret,
"\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
ring->dequeue, trb, &dma);
trb = &trbs[ring->enqueue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += snprintf(str + ret, size - ret,
"\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
ring->enqueue, trb, &dma);
ret += scnprintf(str + ret, size - ret,
"\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
ring->enqueue, trb, &dma);
ret += snprintf(str + ret, size - ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
ring->free_trbs, ring->ccs, ring->pcs);
ret += scnprintf(str + ret, size - ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
ring->free_trbs, ring->ccs, ring->pcs);
if (TRBS_PER_SEGMENT > 40) {
ret += snprintf(str + ret, size - ret,
"\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
ret += scnprintf(str + ret, size - ret,
"\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
return str;
}
dma = ring->dma;
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &trbs[i];
ret += snprintf(str + ret, size - ret,
"\t\t@%pad %08x %08x %08x\n", &dma,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
ret += scnprintf(str + ret, size - ret,
"\t\t@%pad %08x %08x %08x\n", &dma,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
dma += sizeof(*trb);
}
if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
@ -166,36 +166,36 @@ static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags,
switch (type) {
case TRB_LINK:
ret = snprintf(str, size,
"LINK %08x type '%s' flags %c:%c:%c%c:%c",
buffer, cdns2_trb_type_string(type),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_TOGGLE ? 'T' : 't',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
ret = scnprintf(str, size,
"LINK %08x type '%s' flags %c:%c:%c%c:%c",
buffer, cdns2_trb_type_string(type),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_TOGGLE ? 'T' : 't',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
case TRB_NORMAL:
ret = snprintf(str, size,
"type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
"flags %c:%c:%c%c:%c",
cdns2_trb_type_string(type),
buffer, TRB_LEN(length),
TRB_FIELD_TO_BURST(length),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_ISP ? 'I' : 'i',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
ret = scnprintf(str, size,
"type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
"flags %c:%c:%c%c:%c",
cdns2_trb_type_string(type),
buffer, TRB_LEN(length),
TRB_FIELD_TO_BURST(length),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_ISP ? 'I' : 'i',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
default:
ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x",
cdns2_trb_type_string(type),
buffer, length, flags);
ret = scnprintf(str, size, "type '%s' -> raw %08x %08x %08x",
cdns2_trb_type_string(type),
buffer, length, flags);
}
if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}

View File

@ -1360,7 +1360,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
udc->ep0_dir = USB_DIR_IN;
/* Borrow the per device status_req */
req = udc->status_req;
/* Fill in the reqest structure */
/* Fill in the request structure */
*((u16 *) req->req.buf) = cpu_to_le16(tmp);
req->ep = ep;
@ -2532,15 +2532,18 @@ err_kfree:
/* Driver removal function
* Free resources and finish pending transactions
*/
static int fsl_udc_remove(struct platform_device *pdev)
static void fsl_udc_remove(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
DECLARE_COMPLETION_ONSTACK(done);
if (!udc_controller)
return -ENODEV;
if (!udc_controller) {
dev_err(&pdev->dev,
"Driver still in use but removing anyhow\n");
return;
}
udc_controller->done = &done;
usb_del_gadget_udc(&udc_controller->gadget);
@ -2568,8 +2571,6 @@ static int fsl_udc_remove(struct platform_device *pdev)
*/
if (pdata->exit)
pdata->exit(pdev);
return 0;
}
/*-----------------------------------------------------------------
@ -2667,7 +2668,7 @@ static const struct platform_device_id fsl_udc_devtype[] = {
MODULE_DEVICE_TABLE(platform, fsl_udc_devtype);
static struct platform_driver udc_driver = {
.probe = fsl_udc_probe,
.remove = fsl_udc_remove,
.remove_new = fsl_udc_remove,
.id_table = fsl_udc_devtype,
/* these suspend and resume are not usb suspend and resume */
.suspend = fsl_udc_suspend,

View File

@ -2089,15 +2089,18 @@ static void gr_ep_remove(struct gr_udc *dev, int num, int is_in)
ep->tailbuf, ep->tailbuf_paddr);
}
static int gr_remove(struct platform_device *pdev)
static void gr_remove(struct platform_device *pdev)
{
struct gr_udc *dev = platform_get_drvdata(pdev);
int i;
if (dev->added)
usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
if (dev->driver)
return -EBUSY;
if (dev->driver) {
dev_err(&pdev->dev,
"Driver still in use but removing anyhow\n");
return;
}
gr_dfs_delete(dev);
dma_pool_destroy(dev->desc_pool);
@ -2110,8 +2113,6 @@ static int gr_remove(struct platform_device *pdev)
gr_ep_remove(dev, i, 0);
for (i = 0; i < dev->nepi; i++)
gr_ep_remove(dev, i, 1);
return 0;
}
static int gr_request_irq(struct gr_udc *dev, int irq)
{
@ -2248,7 +2249,7 @@ static struct platform_driver gr_driver = {
.of_match_table = gr_match,
},
.probe = gr_probe,
.remove = gr_remove,
.remove_new = gr_remove,
};
module_platform_driver(gr_driver);

View File

@ -3174,13 +3174,16 @@ i2c_fail:
return retval;
}
static int lpc32xx_udc_remove(struct platform_device *pdev)
static void lpc32xx_udc_remove(struct platform_device *pdev)
{
struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
if (udc->driver) {
dev_err(&pdev->dev,
"Driver still in use but removing anyhow\n");
return;
}
udc_clk_set(udc, 1);
udc_disable(udc);
@ -3194,8 +3197,6 @@ static int lpc32xx_udc_remove(struct platform_device *pdev)
udc->udca_v_base, udc->udca_p_base);
clk_disable_unprepare(udc->usb_slv_clk);
return 0;
}
#ifdef CONFIG_PM
@ -3255,7 +3256,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match);
static struct platform_driver lpc32xx_udc_driver = {
.probe = lpc32xx_udc_probe,
.remove = lpc32xx_udc_remove,
.remove_new = lpc32xx_udc_remove,
.shutdown = lpc32xx_udc_shutdown,
.suspend = lpc32xx_udc_suspend,
.resume = lpc32xx_udc_resume,

View File

@ -1451,7 +1451,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
req = udc->status_req;
/* fill in the reqest structure */
/* fill in the request structure */
if (empty == false) {
*((u16 *) req->req.buf) = cpu_to_le16(status);
req->req.length = 2;

View File

@ -2397,12 +2397,15 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev)
pullup_off();
}
static int pxa25x_udc_remove(struct platform_device *pdev)
static void pxa25x_udc_remove(struct platform_device *pdev)
{
struct pxa25x_udc *dev = platform_get_drvdata(pdev);
if (dev->driver)
return -EBUSY;
if (dev->driver) {
dev_err(&pdev->dev,
"Driver still in use but removing anyhow\n");
return;
}
usb_del_gadget_udc(&dev->gadget);
dev->pullup = 0;
@ -2414,7 +2417,6 @@ static int pxa25x_udc_remove(struct platform_device *pdev)
dev->transceiver = NULL;
the_controller = NULL;
return 0;
}
/*-------------------------------------------------------------------------*/
@ -2472,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev)
static struct platform_driver udc_driver = {
.shutdown = pxa25x_udc_shutdown,
.probe = pxa25x_udc_probe,
.remove = pxa25x_udc_remove,
.remove_new = pxa25x_udc_remove,
.suspend = pxa25x_udc_suspend,
.resume = pxa25x_udc_resume,
.driver = {

View File

@ -1158,12 +1158,12 @@ dump_eps(struct usb_hcd *hcd)
end = dp + sizeof(ubuf);
*dp = '\0';
list_for_each_entry(urb, &ep->urb_list, urb_list) {
ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb,
usb_pipetype(urb->pipe),
usb_urb_dir_in(urb) ? "IN" : "OUT",
urb->actual_length,
urb->transfer_buffer_length);
if (ret < 0 || ret >= end - dp)
ret = scnprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb,
usb_pipetype(urb->pipe),
usb_urb_dir_in(urb) ? "IN" : "OUT",
urb->actual_length,
urb->transfer_buffer_length);
if (ret == end - dp - 1)
break; /* error or buffer full */
dp += ret;
}
@ -1255,9 +1255,9 @@ max3421_handle_irqs(struct usb_hcd *hcd)
end = sbuf + sizeof(sbuf);
*dp = '\0';
for (i = 0; i < 16; ++i) {
int ret = snprintf(dp, end - dp, " %lu",
max3421_hcd->err_stat[i]);
if (ret < 0 || ret >= end - dp)
int ret = scnprintf(dp, end - dp, " %lu",
max3421_hcd->err_stat[i]);
if (ret == end - dp - 1)
break; /* error or buffer full */
dp += ret;
}

View File

@ -6,9 +6,24 @@
*
* Author: Lu Baolu <baolu.lu@linux.intel.com>
*/
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kstrtox.h>
#include <linux/list.h>
#include <linux/nls.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <asm/byteorder.h>
#include "xhci.h"
#include "xhci-trace.h"
@ -28,7 +43,7 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring)
if (!ring)
return;
if (ring->first_seg && ring->first_seg->trbs) {
if (ring->first_seg) {
dma_free_coherent(dev, TRB_SEGMENT_SIZE,
ring->first_seg->trbs,
ring->first_seg->dma);
@ -374,13 +389,13 @@ static void xhci_dbc_eps_init(struct xhci_dbc *dbc)
static void xhci_dbc_eps_exit(struct xhci_dbc *dbc)
{
memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps));
memset(dbc->eps, 0, sizeof_field(struct xhci_dbc, eps));
}
static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring,
struct xhci_erst *erst, gfp_t flags)
{
erst->entries = dma_alloc_coherent(dev, sizeof(struct xhci_erst_entry),
erst->entries = dma_alloc_coherent(dev, sizeof(*erst->entries),
&erst->erst_dma_addr, flags);
if (!erst->entries)
return -ENOMEM;
@ -394,9 +409,8 @@ static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring,
static void dbc_erst_free(struct device *dev, struct xhci_erst *erst)
{
if (erst->entries)
dma_free_coherent(dev, sizeof(struct xhci_erst_entry),
erst->entries, erst->erst_dma_addr);
dma_free_coherent(dev, sizeof(*erst->entries), erst->entries,
erst->erst_dma_addr);
erst->entries = NULL;
}
@ -495,7 +509,7 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
goto ctx_fail;
/* Allocate the string table: */
dbc->string_size = sizeof(struct dbc_str_descs);
dbc->string_size = sizeof(*dbc->string);
dbc->string = dma_alloc_coherent(dev, dbc->string_size,
&dbc->string_dma, flags);
if (!dbc->string)
@ -543,11 +557,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
xhci_dbc_eps_exit(dbc);
if (dbc->string) {
dma_free_coherent(dbc->dev, dbc->string_size,
dbc->string, dbc->string_dma);
dbc->string = NULL;
}
dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma);
dbc->string = NULL;
dbc_free_ctx(dbc->dev, dbc->ctx);
dbc->ctx = NULL;
@ -597,7 +608,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc)
static int xhci_do_dbc_stop(struct xhci_dbc *dbc)
{
if (dbc->state == DS_DISABLED)
return -1;
return -EINVAL;
writel(0, &dbc->regs->control);
dbc->state = DS_DISABLED;
@ -650,11 +661,11 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
spin_lock_irqsave(&dbc->lock, flags);
ret = xhci_do_dbc_stop(dbc);
spin_unlock_irqrestore(&dbc->lock, flags);
if (ret)
return;
if (!ret) {
xhci_dbc_mem_cleanup(dbc);
pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
}
xhci_dbc_mem_cleanup(dbc);
pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
}
static void
@ -914,41 +925,29 @@ static void xhci_dbc_handle_events(struct work_struct *work)
mod_delayed_work(system_wq, &dbc->event_work, 1);
}
static const char * const dbc_state_strings[DS_MAX] = {
[DS_DISABLED] = "disabled",
[DS_INITIALIZED] = "initialized",
[DS_ENABLED] = "enabled",
[DS_CONNECTED] = "connected",
[DS_CONFIGURED] = "configured",
[DS_STALLED] = "stalled",
};
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";
}
if (dbc->state >= ARRAY_SIZE(dbc_state_strings))
return sysfs_emit(buf, "unknown\n");
return sprintf(buf, "%s\n", p);
return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]);
}
static ssize_t dbc_store(struct device *dev,
@ -961,9 +960,9 @@ static ssize_t dbc_store(struct device *dev,
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
if (!strncmp(buf, "enable", 6))
if (sysfs_streq(buf, "enable"))
xhci_dbc_start(dbc);
else if (!strncmp(buf, "disable", 7))
else if (sysfs_streq(buf, "disable"))
xhci_dbc_stop(dbc);
else
return -EINVAL;
@ -981,7 +980,7 @@ static ssize_t dbc_idVendor_show(struct device *dev,
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
return sprintf(buf, "%04x\n", dbc->idVendor);
return sysfs_emit(buf, "%04x\n", dbc->idVendor);
}
static ssize_t dbc_idVendor_store(struct device *dev,
@ -993,9 +992,11 @@ static ssize_t dbc_idVendor_store(struct device *dev,
void __iomem *ptr;
u16 value;
u32 dev_info;
int ret;
if (kstrtou16(buf, 0, &value))
return -EINVAL;
ret = kstrtou16(buf, 0, &value);
if (ret)
return ret;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
@ -1021,7 +1022,7 @@ static ssize_t dbc_idProduct_show(struct device *dev,
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
return sprintf(buf, "%04x\n", dbc->idProduct);
return sysfs_emit(buf, "%04x\n", dbc->idProduct);
}
static ssize_t dbc_idProduct_store(struct device *dev,
@ -1033,9 +1034,11 @@ static ssize_t dbc_idProduct_store(struct device *dev,
void __iomem *ptr;
u32 dev_info;
u16 value;
int ret;
if (kstrtou16(buf, 0, &value))
return -EINVAL;
ret = kstrtou16(buf, 0, &value);
if (ret)
return ret;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
@ -1060,7 +1063,7 @@ static ssize_t dbc_bcdDevice_show(struct device *dev,
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
return sprintf(buf, "%04x\n", dbc->bcdDevice);
return sysfs_emit(buf, "%04x\n", dbc->bcdDevice);
}
static ssize_t dbc_bcdDevice_store(struct device *dev,
@ -1072,9 +1075,11 @@ static ssize_t dbc_bcdDevice_store(struct device *dev,
void __iomem *ptr;
u32 dev_info;
u16 value;
int ret;
if (kstrtou16(buf, 0, &value))
return -EINVAL;
ret = kstrtou16(buf, 0, &value);
if (ret)
return ret;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
@ -1100,7 +1105,7 @@ static ssize_t dbc_bInterfaceProtocol_show(struct device *dev,
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
return sprintf(buf, "%02x\n", dbc->bInterfaceProtocol);
return sysfs_emit(buf, "%02x\n", dbc->bInterfaceProtocol);
}
static ssize_t dbc_bInterfaceProtocol_store(struct device *dev,
@ -1114,9 +1119,13 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev,
u8 value;
int ret;
/* bInterfaceProtocol is 8 bit, but xhci only supports values 0 and 1 */
/* bInterfaceProtocol is 8 bit, but... */
ret = kstrtou8(buf, 0, &value);
if (ret || value > 1)
if (ret)
return ret;
/* ...xhci only supports values 0 and 1 */
if (value > 1)
return -EINVAL;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
@ -1139,7 +1148,7 @@ static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice);
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
static struct attribute *dbc_dev_attributes[] = {
static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc.attr,
&dev_attr_dbc_idVendor.attr,
&dev_attr_dbc_idProduct.attr,
@ -1147,10 +1156,7 @@ static struct attribute *dbc_dev_attributes[] = {
&dev_attr_dbc_bInterfaceProtocol.attr,
NULL
};
static const struct attribute_group dbc_dev_attrib_grp = {
.attrs = dbc_dev_attributes,
};
ATTRIBUTE_GROUPS(dbc_dev);
struct xhci_dbc *
xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *driver)
@ -1176,7 +1182,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
spin_lock_init(&dbc->lock);
ret = sysfs_create_group(&dev->kobj, &dbc_dev_attrib_grp);
ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups);
if (ret)
goto err;
@ -1195,7 +1201,7 @@ void xhci_dbc_remove(struct xhci_dbc *dbc)
xhci_dbc_stop(dbc);
/* remove sysfs files */
sysfs_remove_group(&dbc->dev->kobj, &dbc_dev_attrib_grp);
sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups);
kfree(dbc);
}

View File

@ -82,6 +82,7 @@ enum dbc_state {
DS_CONNECTED,
DS_CONFIGURED,
DS_STALLED,
DS_MAX
};
struct dbc_ep {

View File

@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
"command-ring",
xhci->debugfs_root);
xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring,
xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring,
"event-ring",
xhci->debugfs_root);

View File

@ -323,6 +323,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring,
*/
ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
}
EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
/* Allocate segments and link them for a ring */
static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
@ -1739,6 +1740,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
}
command->status = 0;
/* set default timeout to 5000 ms */
command->timeout_ms = XHCI_CMD_DEFAULT_TIMEOUT;
INIT_LIST_HEAD(&command->cmd_list);
return command;
}
@ -1853,6 +1856,31 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
kfree(ir);
}
void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned int intr_num;
/* interrupter 0 is primary interrupter, don't touch it */
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
/* fixme, should we check xhci->interrupter[intr_num] == ir */
/* fixme locking */
spin_lock_irq(&xhci->lock);
intr_num = ir->intr_num;
xhci_remove_interrupter(xhci, ir);
xhci->interrupters[intr_num] = NULL;
spin_unlock_irq(&xhci->lock);
xhci_free_interrupter(xhci, ir);
}
EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
@ -1860,10 +1888,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
cancel_delayed_work_sync(&xhci->cmd_timer);
xhci_remove_interrupter(xhci, xhci->interrupter);
xhci_free_interrupter(xhci, xhci->interrupter);
xhci->interrupter = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");
for (i = 0; i < xhci->max_interrupters; i++) {
if (xhci->interrupters[i]) {
xhci_remove_interrupter(xhci, xhci->interrupters[i]);
xhci_free_interrupter(xhci, xhci->interrupters[i]);
xhci->interrupters[i] = NULL;
}
}
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters");
if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring);
@ -1933,6 +1965,7 @@ no_bw:
for (i = 0; i < xhci->num_port_caps; i++)
kfree(xhci->port_caps[i].psi);
kfree(xhci->port_caps);
kfree(xhci->interrupters);
xhci->num_port_caps = 0;
xhci->usb2_rhub.ports = NULL;
@ -1941,6 +1974,7 @@ no_bw:
xhci->rh_bw = NULL;
xhci->ext_caps = NULL;
xhci->port_caps = NULL;
xhci->interrupters = NULL;
xhci->page_size = 0;
xhci->page_shift = 0;
@ -2246,18 +2280,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
}
static struct xhci_interrupter *
xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags)
xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_interrupter *ir;
unsigned int num_segs;
unsigned int num_segs = segs;
int ret;
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
if (!ir)
return NULL;
num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2),
/* number of ring segments should be greater than 0 */
if (segs <= 0)
num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2),
ERST_MAX_SEGS);
ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0,
@ -2292,6 +2328,13 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
return -EINVAL;
}
if (xhci->interrupters[intr_num]) {
xhci_warn(xhci, "Interrupter %d\n already set up", intr_num);
return -EINVAL;
}
xhci->interrupters[intr_num] = ir;
ir->intr_num = intr_num;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
/* set ERST count with the number of entries in the segment table */
@ -2311,10 +2354,52 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
return 0;
}
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir;
unsigned int i;
int err = -ENOSPC;
if (!xhci->interrupters || xhci->max_interrupters <= 1)
return NULL;
ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL);
if (!ir)
return NULL;
spin_lock_irq(&xhci->lock);
/* Find available secondary interrupter, interrupter 0 is reserved for primary */
for (i = 1; i < xhci->max_interrupters; i++) {
if (xhci->interrupters[i] == NULL) {
err = xhci_add_interrupter(xhci, ir, i);
break;
}
}
spin_unlock_irq(&xhci->lock);
if (err) {
xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n",
xhci->max_interrupters);
xhci_free_interrupter(xhci, ir);
return NULL;
}
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
i, xhci->max_interrupters);
return ir;
}
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
dma_addr_t dma;
struct xhci_interrupter *ir;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
dma_addr_t dma;
unsigned int val, val2;
u64 val_64;
u32 page_size, temp;
@ -2438,11 +2523,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
/* Allocate and set up primary interrupter 0 with an event ring. */
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Allocating primary event ring");
xhci->interrupter = xhci_alloc_interrupter(xhci, flags);
if (!xhci->interrupter)
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
flags, dev_to_node(dev));
ir = xhci_alloc_interrupter(xhci, 0, flags);
if (!ir)
goto fail;
if (xhci_add_interrupter(xhci, xhci->interrupter, 0))
if (xhci_add_interrupter(xhci, ir, 0))
goto fail;
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;

View File

@ -7,6 +7,7 @@
* Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
#include <linux/bitfield.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@ -73,6 +74,9 @@
#define FRMCNT_LEV1_RANG (0x12b << 8)
#define FRMCNT_LEV1_RANG_MASK GENMASK(19, 8)
#define HSCH_CFG1 0x960
#define SCH3_RXFIFO_DEPTH_MASK GENMASK(21, 20)
#define SS_GEN2_EOF_CFG 0x990
#define SSG2EOF_OFFSET 0x3c
@ -114,6 +118,8 @@
#define SSC_IP_SLEEP_EN BIT(4)
#define SSC_SPM_INT_EN BIT(1)
#define SCH_FIFO_TO_KB(x) ((x) >> 10)
enum ssusb_uwk_vers {
SSUSB_UWK_V1 = 1,
SSUSB_UWK_V2,
@ -165,6 +171,35 @@ static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk)
writel(value, hcd->regs + SS_GEN2_EOF_CFG);
}
/*
* workaround: usb3.2 gen1 isoc rx hw issue
* host send out unexpected ACK afer device fininsh a burst transfer with
* a short packet.
*/
static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk)
{
struct usb_hcd *hcd = mtk->hcd;
u32 value;
if (!mtk->rxfifo_depth)
return;
value = readl(hcd->regs + HSCH_CFG1);
value &= ~SCH3_RXFIFO_DEPTH_MASK;
value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK,
SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1);
writel(value, hcd->regs + HSCH_CFG1);
}
static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk)
{
/* workaround only for mt8195 */
xhci_mtk_set_frame_interval(mtk);
/* workaround for SoCs using SSUSB about before IPM v1.6.0 */
xhci_mtk_rxfifo_depth_set(mtk);
}
static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
{
struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
@ -448,8 +483,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
if (ret)
return ret;
/* workaround only for mt8195 */
xhci_mtk_set_frame_interval(mtk);
xhci_mtk_init_quirk(mtk);
}
ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
@ -527,6 +561,8 @@ static int xhci_mtk_probe(struct platform_device *pdev)
of_property_read_u32(node, "mediatek,u2p-dis-msk",
&mtk->u2p_dis_msk);
of_property_read_u32(node, "rx-fifo-depth", &mtk->rxfifo_depth);
ret = usb_wakeup_of_property_parse(mtk, node);
if (ret) {
dev_err(dev, "failed to parse uwk property\n");

Some files were not shown because too many files have changed in this diff Show More