mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 21:02:19 +00:00
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:
commit
8c94ccc7cd
@ -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=
|
||||
|
@ -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>;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>;
|
||||
|
||||
|
102
Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml
Normal file
102
Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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";
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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>,
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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";
|
||||
|
@ -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>;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -1454,7 +1454,7 @@
|
||||
|
||||
altmodes {
|
||||
displayport {
|
||||
svid = <0xff01>;
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
vdo = <0x00001c46>;
|
||||
};
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
@ -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),
|
||||
{ }
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) },
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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: \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1168,6 +1168,9 @@ struct cdns3_endpoint {
|
||||
u8 dir;
|
||||
u8 num;
|
||||
u8 type;
|
||||
u8 mult;
|
||||
u8 bMaxBurst;
|
||||
u16 wMaxPacketSize;
|
||||
int interval;
|
||||
|
||||
int free_trbs;
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 },
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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, ¶ms);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -31,6 +31,8 @@ struct f_ncm_opts {
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
|
||||
u16 max_segment_size;
|
||||
};
|
||||
|
||||
#endif /* U_NCM_H */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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; \
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ enum dbc_state {
|
||||
DS_CONNECTED,
|
||||
DS_CONFIGURED,
|
||||
DS_STALLED,
|
||||
DS_MAX
|
||||
};
|
||||
|
||||
struct dbc_ep {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user