forked from Minki/linux
USB/Thunderbolt changes for 6.1-rc1
Here is the big set of USB and Thunderbolt driver changes for 6.1-rc1. Nothing major in here, lots of little things with new devices supported and updates for a few drivers. Highlights include: - thunderbolt/USB4 devices supported a bit better than before, and some new ids to enable new hardware devices - USB gadget uvc updates for newer video formats and better v4l integration (the v4l portions were acked by those maintainers) - typec updates for tiny issues and more typec drivers for new chips. - xhci tiny updates for minor issues - big usb-serial ftdi_sio driver update to handle new devices better - lots of tiny dwc3 fixes and updates for the IP block that is showing up everywhere these days - dts updates for new devices being supported - other tiny janitorial and cleanups fixes for lots of different USB drivers. Full details are in the shortlog. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCY0BN9g8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yljdgCdH4Fev4Nvf2Nokfv0q0krcRB3YpUAn1aZfpb/ EbgxsxhPnTJg+mmXyLms =IxUy -----END PGP SIGNATURE----- Merge tag 'usb-6.1-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 driver changes for 6.1-rc1. Nothing major in here, lots of little things with new devices supported and updates for a few drivers. Highlights include: - thunderbolt/USB4 devices supported a bit better than before, and some new ids to enable new hardware devices - USB gadget uvc updates for newer video formats and better v4l integration (the v4l portions were acked by those maintainers) - typec updates for tiny issues and more typec drivers for new chips. - xhci tiny updates for minor issues - big usb-serial ftdi_sio driver update to handle new devices better - lots of tiny dwc3 fixes and updates for the IP block that is showing up everywhere these days - dts updates for new devices being supported - other tiny janitorial and cleanups fixes for lots of different USB drivers. Full details are in the shortlog. All of these have been in linux-next for a while with no reported issues" * tag 'usb-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (169 commits) usb: gadget: uvc: don't put item still in use usb: gadget: uvc: Fix argument to sizeof() in uvc_register_video() usb: host: ehci-exynos: switch to using gpiod API Revert "usb: dwc3: Don't switch OTG -> peripheral if extcon is present" Revert "USB: fixup for merge issue with "usb: dwc3: Don't switch OTG -> peripheral if extcon is present"" dt-bindings: usb: Convert FOTG210 to dt schema usb: mtu3: fix failed runtime suspend in host only mode USB: omap_udc: Fix spelling mistake: "tranceiver_ctrl" -> "transceiver_ctrl" usb: typec: ucsi_ccg: Disable UCSI ALT support on Tegra usb: typec: Replace custom implementation of device_match_fwnode() usb: typec: ucsi: Don't warn on probe deferral usb: add quirks for Lenovo OneLink+ Dock MAINTAINERS: switch dwc3 to Thinh usb: idmouse: fix an uninit-value in idmouse_open USB: PHY: JZ4770: Switch to use dev_err_probe() helper usb: phy: generic: Switch to use dev_err_probe() helper usb: ulpi: use DEFINE_SHOW_ATTRIBUTE to simplify ulpi_regs usb: cdns3: remove dead code usb: cdc-wdm: Use skb_put_data() instead of skb_put/memcpy pair usb: musb: sunxi: Switch to use dev_err_probe() helper ...
This commit is contained in:
commit
d3dcbe24a0
@ -153,7 +153,7 @@ Date: Jan 2020
|
||||
KernelVersion: 5.5
|
||||
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
Description: This attribute reports number of RX lanes the device is
|
||||
using simultaneusly through its upstream port.
|
||||
using simultaneously through its upstream port.
|
||||
|
||||
What: /sys/bus/thunderbolt/devices/.../tx_speed
|
||||
Date: Jan 2020
|
||||
@ -167,7 +167,7 @@ Date: Jan 2020
|
||||
KernelVersion: 5.5
|
||||
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
Description: This attribute reports number of TX lanes the device is
|
||||
using simultaneusly through its upstream port.
|
||||
using simultaneously through its upstream port.
|
||||
|
||||
What: /sys/bus/thunderbolt/devices/.../vendor
|
||||
Date: Sep 2017
|
||||
|
@ -15,10 +15,10 @@ Required properties:
|
||||
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
|
||||
|
||||
Optional properties:
|
||||
- fsl,tx-cal-45-dn-ohms: Integer [30-55]. Resistance (in ohms) of switchable
|
||||
- fsl,tx-cal-45-dn-ohms: Integer [35-54]. Resistance (in ohms) of switchable
|
||||
high-speed trimming resistor connected in parallel with the 45 ohm resistor
|
||||
that terminates the DN output signal. Default: 45
|
||||
- fsl,tx-cal-45-dp-ohms: Integer [30-55]. Resistance (in ohms) of switchable
|
||||
- fsl,tx-cal-45-dp-ohms: Integer [35-54]. Resistance (in ohms) of switchable
|
||||
high-speed trimming resistor connected in parallel with the 45 ohm resistor
|
||||
that terminates the DP output signal. Default: 45
|
||||
- fsl,tx-d-cal: Integer [79-119]. Current trimming value (as a percentage) of
|
||||
|
@ -23,6 +23,8 @@ properties:
|
||||
connector:
|
||||
type: object
|
||||
$ref: ../connector/usb-connector.yaml
|
||||
unevaluatedProperties: false
|
||||
|
||||
description:
|
||||
Properties for usb c connector.
|
||||
|
||||
|
@ -67,6 +67,7 @@ properties:
|
||||
|
||||
vhub-strings:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
@ -78,6 +79,7 @@ properties:
|
||||
patternProperties:
|
||||
'^string@[0-9a-f]+$':
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: string descriptors of the specific language
|
||||
|
||||
properties:
|
||||
|
@ -32,6 +32,7 @@ properties:
|
||||
- enum:
|
||||
- rockchip,px30-usb
|
||||
- rockchip,rk3036-usb
|
||||
- rockchip,rk3128-usb
|
||||
- rockchip,rk3188-usb
|
||||
- rockchip,rk3228-usb
|
||||
- rockchip,rk3288-usb
|
||||
|
@ -1,35 +0,0 @@
|
||||
Faraday FOTG Host controller
|
||||
|
||||
This OTG-capable USB host controller is found in Cortina Systems
|
||||
Gemini and other SoC products.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of:
|
||||
"faraday,fotg210"
|
||||
"cortina,gemini-usb", "faraday,fotg210"
|
||||
- reg: should contain one register range i.e. start and length
|
||||
- interrupts: description of the interrupt line
|
||||
|
||||
Optional properties:
|
||||
- clocks: should contain the IP block clock
|
||||
- clock-names: should be "PCLK" for the IP block clock
|
||||
|
||||
Required properties for "cortina,gemini-usb" compatible:
|
||||
- syscon: a phandle to the system controller to access PHY registers
|
||||
|
||||
Optional properties for "cortina,gemini-usb" compatible:
|
||||
- cortina,gemini-mini-b: boolean property that indicates that a Mini-B
|
||||
OTG connector is in use
|
||||
- wakeup-source: see power/wakeup-source.txt
|
||||
|
||||
Example for Gemini:
|
||||
|
||||
usb@68000000 {
|
||||
compatible = "cortina,gemini-usb", "faraday,fotg210";
|
||||
reg = <0x68000000 0x1000>;
|
||||
interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cc 12>;
|
||||
clock-names = "PCLK";
|
||||
syscon = <&syscon>;
|
||||
wakeup-source;
|
||||
};
|
77
Documentation/devicetree/bindings/usb/faraday,fotg210.yaml
Normal file
77
Documentation/devicetree/bindings/usb/faraday,fotg210.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2022 Linaro Ltd.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/faraday,fotg210.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Faraday Technology FOTG210 HS OTG USB 2.0 controller Bindings
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-drd.yaml#
|
||||
- $ref: usb-hcd.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: faraday,fotg210
|
||||
- items:
|
||||
- const: cortina,gemini-usb
|
||||
- const: faraday,fotg210
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: PCLK
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: a phandle to the global Gemini system controller on
|
||||
Gemini systems
|
||||
|
||||
dr_mode: true
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
|
||||
phy-names:
|
||||
const: usb2-phy
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clock/cortina,gemini-clock.h>
|
||||
#include <dt-bindings/reset/cortina,gemini-reset.h>
|
||||
usb0: usb@68000000 {
|
||||
compatible = "cortina,gemini-usb", "faraday,fotg210";
|
||||
reg = <0x68000000 0x1000>;
|
||||
interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
|
||||
resets = <&syscon GEMINI_RESET_USB0>;
|
||||
clocks = <&syscon GEMINI_CLK_GATE_USB0>;
|
||||
clock-names = "PCLK";
|
||||
syscon = <&syscon>;
|
||||
dr_mode = "host";
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/usb/mediatek,mt6370-tcpc.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: MediatTek MT6370 Type-C Port Switch and Power Delivery controller
|
||||
|
||||
maintainers:
|
||||
- ChiYuan Huang <cy_huang@richtek.com>
|
||||
|
||||
description: |
|
||||
MediaTek MT6370 is a multi-functional device.
|
||||
It integrates charger, ADC, flash, RGB indicators,
|
||||
regulators (DSV/VIBLDO), and TypeC Port Switch with Power Delivery controller.
|
||||
This document only describes MT6370 Type-C Port Switch and
|
||||
Power Delivery controller.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt6370-tcpc
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
connector:
|
||||
type: object
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
@ -5,7 +5,9 @@ EHCI:
|
||||
-----
|
||||
|
||||
Required properties:
|
||||
- compatible: "nuvoton,npcm750-ehci"
|
||||
- compatible: should be one of
|
||||
"nuvoton,npcm750-ehci"
|
||||
"nuvoton,npcm845-ehci"
|
||||
- interrupts: Should contain the EHCI interrupt
|
||||
- reg: Physical address and length of the register set for the device
|
||||
|
||||
|
@ -26,6 +26,7 @@ properties:
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sdm660-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sdx65-dwc3
|
||||
@ -175,6 +176,7 @@ allOf:
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sm6350-dwc3
|
||||
@ -294,6 +296,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sm6115-dwc3
|
||||
- qcom,sm6125-dwc3
|
||||
- qcom,sm8150-dwc3
|
||||
- qcom,sm8250-dwc3
|
||||
@ -344,11 +347,11 @@ allOf:
|
||||
- qcom,msm8994-dwc3
|
||||
- qcom,qcs404-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sdx65-dwc3
|
||||
- qcom,sm4250-dwc3
|
||||
- qcom,sm6115-dwc3
|
||||
- qcom,sm6125-dwc3
|
||||
- qcom,sm6350-dwc3
|
||||
- qcom,sm8150-dwc3
|
||||
@ -380,6 +383,7 @@ allOf:
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,sm6115-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
|
@ -11,27 +11,55 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r8a774a1-usb3-peri # RZ/G2M
|
||||
- renesas,r8a774b1-usb3-peri # RZ/G2N
|
||||
- renesas,r8a774c0-usb3-peri # RZ/G2E
|
||||
- renesas,r8a774e1-usb3-peri # RZ/G2H
|
||||
- renesas,r8a7795-usb3-peri # R-Car H3
|
||||
- renesas,r8a7796-usb3-peri # R-Car M3-W
|
||||
- renesas,r8a77961-usb3-peri # R-Car M3-W+
|
||||
- renesas,r8a77965-usb3-peri # R-Car M3-N
|
||||
- renesas,r8a77990-usb3-peri # R-Car E3
|
||||
- const: renesas,rcar-gen3-usb3-peri
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a774a1-usb3-peri # RZ/G2M
|
||||
- renesas,r8a774b1-usb3-peri # RZ/G2N
|
||||
- renesas,r8a774c0-usb3-peri # RZ/G2E
|
||||
- renesas,r8a774e1-usb3-peri # RZ/G2H
|
||||
- renesas,r8a7795-usb3-peri # R-Car H3
|
||||
- renesas,r8a7796-usb3-peri # R-Car M3-W
|
||||
- renesas,r8a77961-usb3-peri # R-Car M3-W+
|
||||
- renesas,r8a77965-usb3-peri # R-Car M3-N
|
||||
- renesas,r8a77990-usb3-peri # R-Car E3
|
||||
- const: renesas,rcar-gen3-usb3-peri
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a09g011-usb3-peri # RZ/V2M
|
||||
- const: renesas,rzv2m-usb3-peri
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Combined interrupt for DMA, SYS and ERR
|
||||
- description: Dual Role Device (DRD)
|
||||
- description: Battery Charging
|
||||
- description: Global Purpose Input
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: all_p
|
||||
- const: drd
|
||||
- const: bc
|
||||
- const: gpi
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Main clock
|
||||
- description: Register access clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: aclk
|
||||
- const: reg
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
@ -43,7 +71,15 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Peripheral reset
|
||||
- description: DRD reset
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: aresetn_p
|
||||
- const: drd_reset
|
||||
|
||||
usb-role-switch:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
@ -78,6 +114,39 @@ required:
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rzv2m-usb3-peri
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
minItems: 2
|
||||
interrupts:
|
||||
minItems: 4
|
||||
interrupt-names:
|
||||
minItems: 4
|
||||
resets:
|
||||
minItems: 2
|
||||
required:
|
||||
- clock-names
|
||||
- interrupt-names
|
||||
- resets
|
||||
- reset-names
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
100
Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml
Normal file
100
Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml
Normal file
@ -0,0 +1,100 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/usb/richtek,rt1711h.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Richtek RT1711H Type-C Port Switch and Power Delivery controller
|
||||
|
||||
maintainers:
|
||||
- Gene Chen <gene_chen@richtek.com>
|
||||
|
||||
description: |
|
||||
The RT1711H is a USB Type-C controller that complies with the latest
|
||||
USB Type-C and PD standards. It does the USB Type-C detection including attach
|
||||
and orientation. It integrates the physical layer of the USB BMC power
|
||||
delivery protocol to allow up to 100W of power. The BMC PD block enables full
|
||||
support for alternative interfaces of the Type-C specification.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- richtek,rt1711h
|
||||
- richtek,rt1715
|
||||
description:
|
||||
RT1711H support PD20, RT1715 support PD30 except Fast Role Swap.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source:
|
||||
type: boolean
|
||||
|
||||
connector:
|
||||
type: object
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
description:
|
||||
Properties for usb c connector.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- connector
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/usb/pd.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rt1711h@4e {
|
||||
compatible = "richtek,rt1711h";
|
||||
reg = <0x4e>;
|
||||
interrupts-extended = <&gpio26 3 IRQ_TYPE_LEVEL_LOW>;
|
||||
wakeup-source;
|
||||
|
||||
connector {
|
||||
compatible = "usb-c-connector";
|
||||
label = "USB-C";
|
||||
data-role = "dual";
|
||||
power-role = "dual";
|
||||
try-power-role = "sink";
|
||||
source-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>;
|
||||
sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>;
|
||||
op-sink-microwatt = <10000000>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_hs>;
|
||||
};
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_ss>;
|
||||
};
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
endpoint {
|
||||
remote-endpoint = <&dp_aux>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -234,6 +234,18 @@ properties:
|
||||
avoid -EPROTO errors with usbhid on some devices (Hikey 970).
|
||||
type: boolean
|
||||
|
||||
snps,gfladj-refclk-lpm-sel-quirk:
|
||||
description:
|
||||
When set, run the SOF/ITP counter based on ref_clk.
|
||||
type: boolean
|
||||
|
||||
snps,resume-hs-terminations:
|
||||
description:
|
||||
Fix the issue of HS terminations CRC error on resume by enabling this
|
||||
quirk. When set, all the termsel, xcvrsel, opmode becomes 0 during end
|
||||
of resume. This option is to support certain legacy ULPI PHYs.
|
||||
type: boolean
|
||||
|
||||
snps,is-utmi-l1-suspend:
|
||||
description:
|
||||
True when DWC3 asserts output signal utmi_l1_suspend_n, false when
|
||||
|
@ -33,6 +33,7 @@ properties:
|
||||
connector:
|
||||
type: object
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@ -74,9 +75,14 @@ examples:
|
||||
data-role = "dual";
|
||||
typec-power-opmode = "default";
|
||||
|
||||
port {
|
||||
typec_con_ep: endpoint {
|
||||
remote-endpoint = <&usbotg_hs_ep>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
typec_con_ep: endpoint {
|
||||
remote-endpoint = <&usbotg_hs_ep>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ properties:
|
||||
connector:
|
||||
type: object
|
||||
$ref: ../connector/usb-connector.yaml#
|
||||
unevaluatedProperties: false
|
||||
description:
|
||||
The managed USB Type-C connector. Since WUSB3801 does not support
|
||||
Power Delivery, the node should have the "pd-disable" property.
|
||||
|
@ -340,13 +340,12 @@ USBIP_CMD_SUBMIT:
|
||||
| 0 | 20 | usbip_header_basic, 'command' shall be 0x00000001 |
|
||||
+-----------+--------+---------------------------------------------------+
|
||||
| 0x14 | 4 | transfer_flags: possible values depend on the |
|
||||
| | | URB transfer_flags (refer to URB doc in |
|
||||
| | | Documentation/driver-api/usb/URB.rst) |
|
||||
| | | but with URB_NO_TRANSFER_DMA_MAP masked. Refer to |
|
||||
| | | function usbip_pack_cmd_submit and function |
|
||||
| | | tweak_transfer_flags in drivers/usb/usbip/ |
|
||||
| | | usbip_common.c. The following fields may also ref |
|
||||
| | | to function usbip_pack_cmd_submit and URB doc |
|
||||
| | | USBIP_URB transfer_flags. |
|
||||
| | | Refer to include/uapi/linux/usbip.h and |
|
||||
| | | Documentation/driver-api/usb/URB.rst. |
|
||||
| | | Refer to usbip_pack_cmd_submit() and |
|
||||
| | | tweak_transfer_flags() in drivers/usb/usbip/ |
|
||||
| | | usbip_common.c. |
|
||||
+-----------+--------+---------------------------------------------------+
|
||||
| 0x18 | 4 | transfer_buffer_length: |
|
||||
| | | use URB transfer_buffer_length |
|
||||
|
@ -5921,10 +5921,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
|
||||
F: drivers/usb/dwc2/
|
||||
|
||||
DESIGNWARE USB3 DRD IP DRIVER
|
||||
M: Felipe Balbi <balbi@kernel.org>
|
||||
M: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
|
||||
F: drivers/usb/dwc3/
|
||||
|
||||
DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER
|
||||
|
@ -421,7 +421,14 @@ static struct s3c2410_platform_nand __initdata gta02_nand_info = {
|
||||
/* Get PMU to set USB current limit accordingly. */
|
||||
static struct s3c2410_udc_mach_info gta02_udc_cfg __initdata = {
|
||||
.vbus_draw = gta02_udc_vbus_draw,
|
||||
.pullup_pin = GTA02_GPIO_USB_PULLUP,
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table gta02_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOB", 9, "pullup", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
/* USB */
|
||||
@ -555,6 +562,7 @@ static void __init gta02_machine_init(void)
|
||||
s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
|
||||
S3C_GPIO_PULL_NONE);
|
||||
|
||||
gpiod_add_lookup_table(>a02_udc_gpio_table);
|
||||
gpiod_add_lookup_table(>a02_audio_gpio_table);
|
||||
gpiod_add_lookup_table(>a02_mmc_gpio_table);
|
||||
platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices));
|
||||
|
@ -167,9 +167,15 @@ static struct gpio_chip h1940_latch_gpiochip = {
|
||||
};
|
||||
|
||||
static struct s3c2410_udc_mach_info h1940_udc_cfg __initdata = {
|
||||
.vbus_pin = S3C2410_GPG(5),
|
||||
.vbus_pin_inverted = 1,
|
||||
.pullup_pin = H1940_LATCH_USB_DP,
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table h1940_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOG", 5, "vbus", GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP("H1940_LATCH", 7, "pullup", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct s3c2410_ts_mach_info h1940_ts_cfg __initdata = {
|
||||
@ -725,6 +731,7 @@ static void __init h1940_init(void)
|
||||
u32 tmp;
|
||||
|
||||
s3c24xx_fb_set_platdata(&h1940_fb_info);
|
||||
gpiod_add_lookup_table(&h1940_udc_gpio_table);
|
||||
gpiod_add_lookup_table(&h1940_mmc_gpio_table);
|
||||
gpiod_add_lookup_table(&h1940_audio_gpio_table);
|
||||
gpiod_add_lookup_table(&h1940_bat_gpio_table);
|
||||
|
@ -493,7 +493,14 @@ static struct platform_device *jive_devices[] __initdata = {
|
||||
};
|
||||
|
||||
static struct s3c2410_udc_mach_info jive_udc_cfg __initdata = {
|
||||
.vbus_pin = S3C2410_GPG(1), /* detect is on GPG1 */
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table jive_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOG", 1, "vbus", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
/* Jive power management device */
|
||||
@ -669,6 +676,7 @@ static void __init jive_machine_init(void)
|
||||
|
||||
pm_power_off = jive_power_off;
|
||||
|
||||
gpiod_add_lookup_table(&jive_udc_gpio_table);
|
||||
gpiod_add_lookup_table(&jive_lcdspi_gpiod_table);
|
||||
gpiod_add_lookup_table(&jive_wm8750_gpiod_table);
|
||||
platform_add_devices(jive_devices, ARRAY_SIZE(jive_devices));
|
||||
|
@ -93,9 +93,15 @@ static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata = {
|
||||
/* USB device UDC support */
|
||||
|
||||
static struct s3c2410_udc_mach_info mini2440_udc_cfg __initdata = {
|
||||
.pullup_pin = S3C2410_GPC(5),
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table mini2440_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOC", 5, "pullup", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
/* LCD timing and setup */
|
||||
|
||||
@ -755,6 +761,7 @@ static void __init mini2440_init(void)
|
||||
s3c24xx_fb_set_platdata(&mini2440_fb_info);
|
||||
}
|
||||
|
||||
gpiod_add_lookup_table(&mini2440_udc_gpio_table);
|
||||
s3c24xx_udc_set_platdata(&mini2440_udc_cfg);
|
||||
gpiod_add_lookup_table(&mini2440_mmc_gpio_table);
|
||||
s3c24xx_mci_set_platdata(&mini2440_mmc_cfg);
|
||||
|
@ -84,9 +84,15 @@ static struct s3c2410_uartcfg n30_uartcfgs[] = {
|
||||
};
|
||||
|
||||
static struct s3c2410_udc_mach_info n30_udc_cfg __initdata = {
|
||||
.vbus_pin = S3C2410_GPG(1),
|
||||
.vbus_pin_inverted = 0,
|
||||
.pullup_pin = S3C2410_GPB(3),
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table n30_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOG", 1, "vbus", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("GPIOB", 3, "pullup", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_keys_button n30_buttons[] = {
|
||||
@ -595,6 +601,7 @@ static void __init n30_init(void)
|
||||
WARN_ON(gpio_request(S3C2410_GPG(4), "mmc power"));
|
||||
|
||||
s3c24xx_fb_set_platdata(&n30_fb_info);
|
||||
gpiod_add_lookup_table(&n30_udc_gpio_table);
|
||||
s3c24xx_udc_set_platdata(&n30_udc_cfg);
|
||||
gpiod_add_lookup_table(&n30_mci_gpio_table);
|
||||
s3c24xx_mci_set_platdata(&n30_mci_cfg);
|
||||
|
@ -643,9 +643,15 @@ static struct s3c2410_platform_nand rx1950_nand_info = {
|
||||
};
|
||||
|
||||
static struct s3c2410_udc_mach_info rx1950_udc_cfg __initdata = {
|
||||
.vbus_pin = S3C2410_GPG(5),
|
||||
.vbus_pin_inverted = 1,
|
||||
.pullup_pin = S3C2410_GPJ(5),
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table rx1950_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOG", 5, "vbus", GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP("GPIOJ", 5, "pullup", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct s3c2410_ts_mach_info rx1950_ts_cfg __initdata = {
|
||||
@ -847,6 +853,7 @@ static void __init rx1950_init_machine(void)
|
||||
gpio_direction_output(S3C2410_GPJ(6), 0);
|
||||
|
||||
pwm_add_table(rx1950_pwm_lookup, ARRAY_SIZE(rx1950_pwm_lookup));
|
||||
gpiod_add_lookup_table(&rx1950_udc_gpio_table);
|
||||
gpiod_add_lookup_table(&rx1950_audio_gpio_table);
|
||||
gpiod_add_lookup_table(&rx1950_bat_gpio_table);
|
||||
/* Configure the I2S pins (GPE0...GPE4) in correct mode */
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial_s3c.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -74,9 +74,15 @@ static struct s3c2410_uartcfg smdk2413_uartcfgs[] __initdata = {
|
||||
|
||||
|
||||
static struct s3c2410_udc_mach_info smdk2413_udc_cfg __initdata = {
|
||||
.pullup_pin = S3C2410_GPF(2),
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table smdk2413_udc_gpio_table = {
|
||||
.dev_id = "s3c2410-usbgadget",
|
||||
.table = {
|
||||
GPIO_LOOKUP("GPIOF", 2, "pullup", GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *smdk2413_devices[] __initdata = {
|
||||
&s3c_device_ohci,
|
||||
@ -115,7 +121,7 @@ static void __init smdk2413_machine_init(void)
|
||||
S3C2410_MISCCR_USBSUSPND0 |
|
||||
S3C2410_MISCCR_USBSUSPND1, 0x0);
|
||||
|
||||
|
||||
gpiod_add_lookup_table(&smdk2413_udc_gpio_table);
|
||||
s3c24xx_udc_set_platdata(&smdk2413_udc_cfg);
|
||||
s3c_i2c0_set_platdata(NULL);
|
||||
/* Configure the I2S pins (GPE0...GPE4) in correct mode */
|
||||
|
@ -1299,7 +1299,7 @@
|
||||
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
|
||||
phys = <&usb3_phy0>, <&usb3_phy0>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
snps,dis-u2-freeclk-exists-quirk;
|
||||
snps,gfladj-refclk-lpm-sel-quirk;
|
||||
};
|
||||
|
||||
};
|
||||
@ -1341,7 +1341,7 @@
|
||||
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
|
||||
phys = <&usb3_phy1>, <&usb3_phy1>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
snps,dis-u2-freeclk-exists-quirk;
|
||||
snps,gfladj-refclk-lpm-sel-quirk;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -425,12 +425,14 @@ static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
|
||||
|
||||
static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
|
||||
{
|
||||
u16 frame_size = le16_to_cpu(
|
||||
fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
|
||||
int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
|
||||
frame_size, i, j, ret;
|
||||
struct usb_host_interface *alt = fc_usb->uintf->cur_altsetting;
|
||||
u16 frame_size;
|
||||
int bufsize, i, j, ret;
|
||||
int buffer_offset = 0;
|
||||
|
||||
frame_size = usb_endpoint_maxp(&alt->endpoint[0].desc);
|
||||
bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size;
|
||||
|
||||
deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
|
||||
B2C2_USB_NUM_ISO_URB,
|
||||
B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
|
||||
@ -501,17 +503,21 @@ urb_error:
|
||||
|
||||
static int flexcop_usb_init(struct flexcop_usb *fc_usb)
|
||||
{
|
||||
/* use the alternate setting with the larges buffer */
|
||||
int ret = usb_set_interface(fc_usb->udev, 0, 1);
|
||||
struct usb_host_interface *alt;
|
||||
int ret;
|
||||
|
||||
/* use the alternate setting with the largest buffer */
|
||||
ret = usb_set_interface(fc_usb->udev, 0, 1);
|
||||
if (ret) {
|
||||
err("set interface failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1)
|
||||
alt = fc_usb->uintf->cur_altsetting;
|
||||
|
||||
if (alt->desc.bNumEndpoints < 1)
|
||||
return -ENODEV;
|
||||
if (!usb_endpoint_is_isoc_in(&fc_usb->uintf->cur_altsetting->endpoint[0].desc))
|
||||
if (!usb_endpoint_is_isoc_in(&alt->endpoint[0].desc))
|
||||
return -ENODEV;
|
||||
|
||||
switch (fc_usb->udev->speed) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-uvc.h>
|
||||
|
||||
#include "uvcvideo.h"
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-uvc.h>
|
||||
|
||||
#include "uvcvideo.h"
|
||||
|
||||
@ -34,198 +35,6 @@ static unsigned int uvc_quirks_param = -1;
|
||||
unsigned int uvc_dbg_param;
|
||||
unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Video formats
|
||||
*/
|
||||
|
||||
static struct uvc_format_desc uvc_fmts[] = {
|
||||
{
|
||||
.name = "YUV 4:2:2 (YUYV)",
|
||||
.guid = UVC_GUID_FORMAT_YUY2,
|
||||
.fcc = V4L2_PIX_FMT_YUYV,
|
||||
},
|
||||
{
|
||||
.name = "YUV 4:2:2 (YUYV)",
|
||||
.guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
|
||||
.fcc = V4L2_PIX_FMT_YUYV,
|
||||
},
|
||||
{
|
||||
.name = "YUV 4:2:0 (NV12)",
|
||||
.guid = UVC_GUID_FORMAT_NV12,
|
||||
.fcc = V4L2_PIX_FMT_NV12,
|
||||
},
|
||||
{
|
||||
.name = "MJPEG",
|
||||
.guid = UVC_GUID_FORMAT_MJPEG,
|
||||
.fcc = V4L2_PIX_FMT_MJPEG,
|
||||
},
|
||||
{
|
||||
.name = "YVU 4:2:0 (YV12)",
|
||||
.guid = UVC_GUID_FORMAT_YV12,
|
||||
.fcc = V4L2_PIX_FMT_YVU420,
|
||||
},
|
||||
{
|
||||
.name = "YUV 4:2:0 (I420)",
|
||||
.guid = UVC_GUID_FORMAT_I420,
|
||||
.fcc = V4L2_PIX_FMT_YUV420,
|
||||
},
|
||||
{
|
||||
.name = "YUV 4:2:0 (M420)",
|
||||
.guid = UVC_GUID_FORMAT_M420,
|
||||
.fcc = V4L2_PIX_FMT_M420,
|
||||
},
|
||||
{
|
||||
.name = "YUV 4:2:2 (UYVY)",
|
||||
.guid = UVC_GUID_FORMAT_UYVY,
|
||||
.fcc = V4L2_PIX_FMT_UYVY,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 8-bit (Y800)",
|
||||
.guid = UVC_GUID_FORMAT_Y800,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 8-bit (Y8 )",
|
||||
.guid = UVC_GUID_FORMAT_Y8,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 8-bit (D3DFMT_L8)",
|
||||
.guid = UVC_GUID_FORMAT_D3DFMT_L8,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.name = "IR 8-bit (L8_IR)",
|
||||
.guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 10-bit (Y10 )",
|
||||
.guid = UVC_GUID_FORMAT_Y10,
|
||||
.fcc = V4L2_PIX_FMT_Y10,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 12-bit (Y12 )",
|
||||
.guid = UVC_GUID_FORMAT_Y12,
|
||||
.fcc = V4L2_PIX_FMT_Y12,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 16-bit (Y16 )",
|
||||
.guid = UVC_GUID_FORMAT_Y16,
|
||||
.fcc = V4L2_PIX_FMT_Y16,
|
||||
},
|
||||
{
|
||||
.name = "BGGR Bayer (BY8 )",
|
||||
.guid = UVC_GUID_FORMAT_BY8,
|
||||
.fcc = V4L2_PIX_FMT_SBGGR8,
|
||||
},
|
||||
{
|
||||
.name = "BGGR Bayer (BA81)",
|
||||
.guid = UVC_GUID_FORMAT_BA81,
|
||||
.fcc = V4L2_PIX_FMT_SBGGR8,
|
||||
},
|
||||
{
|
||||
.name = "GBRG Bayer (GBRG)",
|
||||
.guid = UVC_GUID_FORMAT_GBRG,
|
||||
.fcc = V4L2_PIX_FMT_SGBRG8,
|
||||
},
|
||||
{
|
||||
.name = "GRBG Bayer (GRBG)",
|
||||
.guid = UVC_GUID_FORMAT_GRBG,
|
||||
.fcc = V4L2_PIX_FMT_SGRBG8,
|
||||
},
|
||||
{
|
||||
.name = "RGGB Bayer (RGGB)",
|
||||
.guid = UVC_GUID_FORMAT_RGGB,
|
||||
.fcc = V4L2_PIX_FMT_SRGGB8,
|
||||
},
|
||||
{
|
||||
.name = "RGB565",
|
||||
.guid = UVC_GUID_FORMAT_RGBP,
|
||||
.fcc = V4L2_PIX_FMT_RGB565,
|
||||
},
|
||||
{
|
||||
.name = "BGR 8:8:8 (BGR3)",
|
||||
.guid = UVC_GUID_FORMAT_BGR3,
|
||||
.fcc = V4L2_PIX_FMT_BGR24,
|
||||
},
|
||||
{
|
||||
.name = "H.264",
|
||||
.guid = UVC_GUID_FORMAT_H264,
|
||||
.fcc = V4L2_PIX_FMT_H264,
|
||||
},
|
||||
{
|
||||
.name = "H.265",
|
||||
.guid = UVC_GUID_FORMAT_H265,
|
||||
.fcc = V4L2_PIX_FMT_HEVC,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 8 L/R (Y8I)",
|
||||
.guid = UVC_GUID_FORMAT_Y8I,
|
||||
.fcc = V4L2_PIX_FMT_Y8I,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 12 L/R (Y12I)",
|
||||
.guid = UVC_GUID_FORMAT_Y12I,
|
||||
.fcc = V4L2_PIX_FMT_Y12I,
|
||||
},
|
||||
{
|
||||
.name = "Depth data 16-bit (Z16)",
|
||||
.guid = UVC_GUID_FORMAT_Z16,
|
||||
.fcc = V4L2_PIX_FMT_Z16,
|
||||
},
|
||||
{
|
||||
.name = "Bayer 10-bit (SRGGB10P)",
|
||||
.guid = UVC_GUID_FORMAT_RW10,
|
||||
.fcc = V4L2_PIX_FMT_SRGGB10P,
|
||||
},
|
||||
{
|
||||
.name = "Bayer 16-bit (SBGGR16)",
|
||||
.guid = UVC_GUID_FORMAT_BG16,
|
||||
.fcc = V4L2_PIX_FMT_SBGGR16,
|
||||
},
|
||||
{
|
||||
.name = "Bayer 16-bit (SGBRG16)",
|
||||
.guid = UVC_GUID_FORMAT_GB16,
|
||||
.fcc = V4L2_PIX_FMT_SGBRG16,
|
||||
},
|
||||
{
|
||||
.name = "Bayer 16-bit (SRGGB16)",
|
||||
.guid = UVC_GUID_FORMAT_RG16,
|
||||
.fcc = V4L2_PIX_FMT_SRGGB16,
|
||||
},
|
||||
{
|
||||
.name = "Bayer 16-bit (SGRBG16)",
|
||||
.guid = UVC_GUID_FORMAT_GR16,
|
||||
.fcc = V4L2_PIX_FMT_SGRBG16,
|
||||
},
|
||||
{
|
||||
.name = "Depth data 16-bit (Z16)",
|
||||
.guid = UVC_GUID_FORMAT_INVZ,
|
||||
.fcc = V4L2_PIX_FMT_Z16,
|
||||
},
|
||||
{
|
||||
.name = "Greyscale 10-bit (Y10 )",
|
||||
.guid = UVC_GUID_FORMAT_INVI,
|
||||
.fcc = V4L2_PIX_FMT_Y10,
|
||||
},
|
||||
{
|
||||
.name = "IR:Depth 26-bit (INZI)",
|
||||
.guid = UVC_GUID_FORMAT_INZI,
|
||||
.fcc = V4L2_PIX_FMT_INZI,
|
||||
},
|
||||
{
|
||||
.name = "4-bit Depth Confidence (Packed)",
|
||||
.guid = UVC_GUID_FORMAT_CNF4,
|
||||
.fcc = V4L2_PIX_FMT_CNF4,
|
||||
},
|
||||
{
|
||||
.name = "HEVC",
|
||||
.guid = UVC_GUID_FORMAT_HEVC,
|
||||
.fcc = V4L2_PIX_FMT_HEVC,
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Utility functions
|
||||
*/
|
||||
@ -245,19 +54,6 @@ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
|
||||
{
|
||||
unsigned int len = ARRAY_SIZE(uvc_fmts);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
|
||||
return &uvc_fmts[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum v4l2_colorspace uvc_colorspace(const u8 primaries)
|
||||
{
|
||||
static const enum v4l2_colorspace colorprimaries[] = {
|
||||
@ -329,90 +125,6 @@ static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients)
|
||||
return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */
|
||||
}
|
||||
|
||||
/*
|
||||
* Simplify a fraction using a simple continued fraction decomposition. The
|
||||
* idea here is to convert fractions such as 333333/10000000 to 1/30 using
|
||||
* 32 bit arithmetic only. The algorithm is not perfect and relies upon two
|
||||
* arbitrary parameters to remove non-significative terms from the simple
|
||||
* continued fraction decomposition. Using 8 and 333 for n_terms and threshold
|
||||
* respectively seems to give nice results.
|
||||
*/
|
||||
void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
|
||||
unsigned int n_terms, unsigned int threshold)
|
||||
{
|
||||
u32 *an;
|
||||
u32 x, y, r;
|
||||
unsigned int i, n;
|
||||
|
||||
an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL);
|
||||
if (an == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Convert the fraction to a simple continued fraction. See
|
||||
* https://en.wikipedia.org/wiki/Continued_fraction
|
||||
* Stop if the current term is bigger than or equal to the given
|
||||
* threshold.
|
||||
*/
|
||||
x = *numerator;
|
||||
y = *denominator;
|
||||
|
||||
for (n = 0; n < n_terms && y != 0; ++n) {
|
||||
an[n] = x / y;
|
||||
if (an[n] >= threshold) {
|
||||
if (n < 2)
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
|
||||
r = x - an[n] * y;
|
||||
x = y;
|
||||
y = r;
|
||||
}
|
||||
|
||||
/* Expand the simple continued fraction back to an integer fraction. */
|
||||
x = 0;
|
||||
y = 1;
|
||||
|
||||
for (i = n; i > 0; --i) {
|
||||
r = y;
|
||||
y = an[i-1] * y + x;
|
||||
x = r;
|
||||
}
|
||||
|
||||
*numerator = y;
|
||||
*denominator = x;
|
||||
kfree(an);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a fraction to a frame interval in 100ns multiples. The idea here is
|
||||
* to compute numerator / denominator * 10000000 using 32 bit fixed point
|
||||
* arithmetic only.
|
||||
*/
|
||||
u32 uvc_fraction_to_interval(u32 numerator, u32 denominator)
|
||||
{
|
||||
u32 multiplier;
|
||||
|
||||
/* Saturate the result if the operation would overflow. */
|
||||
if (denominator == 0 ||
|
||||
numerator/denominator >= ((u32)-1)/10000000)
|
||||
return (u32)-1;
|
||||
|
||||
/*
|
||||
* Divide both the denominator and the multiplier by two until
|
||||
* numerator * multiplier doesn't overflow. If anyone knows a better
|
||||
* algorithm please let me know.
|
||||
*/
|
||||
multiplier = 10000000;
|
||||
while (numerator > ((u32)-1)/multiplier) {
|
||||
multiplier /= 2;
|
||||
denominator /= 2;
|
||||
}
|
||||
|
||||
return denominator ? numerator * multiplier / denominator : 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Terminal and unit management
|
||||
*/
|
||||
|
@ -386,7 +386,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
|
||||
mutex_unlock(&stream->mutex);
|
||||
|
||||
denominator = 10000000;
|
||||
uvc_simplify_fraction(&numerator, &denominator, 8, 333);
|
||||
v4l2_simplify_fraction(&numerator, &denominator, 8, 333);
|
||||
|
||||
memset(parm, 0, sizeof(*parm));
|
||||
parm->type = stream->type;
|
||||
@ -427,7 +427,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
|
||||
else
|
||||
timeperframe = parm->parm.output.timeperframe;
|
||||
|
||||
interval = uvc_fraction_to_interval(timeperframe.numerator,
|
||||
interval = v4l2_fraction_to_interval(timeperframe.numerator,
|
||||
timeperframe.denominator);
|
||||
uvc_dbg(stream->dev, FORMAT, "Setting frame interval to %u/%u (%u)\n",
|
||||
timeperframe.numerator, timeperframe.denominator, interval);
|
||||
@ -481,7 +481,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
|
||||
/* Return the actual frame period. */
|
||||
timeperframe.numerator = probe.dwFrameInterval;
|
||||
timeperframe.denominator = 10000000;
|
||||
uvc_simplify_fraction(&timeperframe.numerator,
|
||||
v4l2_simplify_fraction(&timeperframe.numerator,
|
||||
&timeperframe.denominator, 8, 333);
|
||||
|
||||
if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
||||
@ -1275,7 +1275,7 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
|
||||
fival->discrete.numerator =
|
||||
frame->dwFrameInterval[index];
|
||||
fival->discrete.denominator = 10000000;
|
||||
uvc_simplify_fraction(&fival->discrete.numerator,
|
||||
v4l2_simplify_fraction(&fival->discrete.numerator,
|
||||
&fival->discrete.denominator, 8, 333);
|
||||
} else {
|
||||
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
|
||||
@ -1285,11 +1285,11 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
|
||||
fival->stepwise.max.denominator = 10000000;
|
||||
fival->stepwise.step.numerator = frame->dwFrameInterval[2];
|
||||
fival->stepwise.step.denominator = 10000000;
|
||||
uvc_simplify_fraction(&fival->stepwise.min.numerator,
|
||||
v4l2_simplify_fraction(&fival->stepwise.min.numerator,
|
||||
&fival->stepwise.min.denominator, 8, 333);
|
||||
uvc_simplify_fraction(&fival->stepwise.max.numerator,
|
||||
v4l2_simplify_fraction(&fival->stepwise.max.numerator,
|
||||
&fival->stepwise.max.denominator, 8, 333);
|
||||
uvc_simplify_fraction(&fival->stepwise.step.numerator,
|
||||
v4l2_simplify_fraction(&fival->stepwise.step.numerator,
|
||||
&fival->stepwise.step.denominator, 8, 333);
|
||||
}
|
||||
|
||||
|
@ -41,144 +41,6 @@
|
||||
#define UVC_EXT_GPIO_UNIT 0x7ffe
|
||||
#define UVC_EXT_GPIO_UNIT_ID 0x100
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* GUIDs
|
||||
*/
|
||||
#define UVC_GUID_UVC_CAMERA \
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
#define UVC_GUID_UVC_OUTPUT \
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}
|
||||
#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
|
||||
#define UVC_GUID_UVC_PROCESSING \
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}
|
||||
#define UVC_GUID_UVC_SELECTOR \
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
|
||||
#define UVC_GUID_EXT_GPIO_CONTROLLER \
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03}
|
||||
|
||||
#define UVC_GUID_FORMAT_MJPEG \
|
||||
{ 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_YUY2 \
|
||||
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_YUY2_ISIGHT \
|
||||
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_NV12 \
|
||||
{ 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_YV12 \
|
||||
{ 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_I420 \
|
||||
{ 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_UYVY \
|
||||
{ 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y800 \
|
||||
{ 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y8 \
|
||||
{ 'Y', '8', ' ', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y10 \
|
||||
{ 'Y', '1', '0', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y12 \
|
||||
{ 'Y', '1', '2', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y16 \
|
||||
{ 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_BY8 \
|
||||
{ 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_BA81 \
|
||||
{ 'B', 'A', '8', '1', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_GBRG \
|
||||
{ 'G', 'B', 'R', 'G', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_GRBG \
|
||||
{ 'G', 'R', 'B', 'G', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_RGGB \
|
||||
{ 'R', 'G', 'G', 'B', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_BG16 \
|
||||
{ 'B', 'G', '1', '6', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_GB16 \
|
||||
{ 'G', 'B', '1', '6', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_RG16 \
|
||||
{ 'R', 'G', '1', '6', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_GR16 \
|
||||
{ 'G', 'R', '1', '6', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_RGBP \
|
||||
{ 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_BGR3 \
|
||||
{ 0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, \
|
||||
0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}
|
||||
#define UVC_GUID_FORMAT_M420 \
|
||||
{ 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
|
||||
#define UVC_GUID_FORMAT_H264 \
|
||||
{ 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_H265 \
|
||||
{ 'H', '2', '6', '5', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y8I \
|
||||
{ 'Y', '8', 'I', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Y12I \
|
||||
{ 'Y', '1', '2', 'I', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_Z16 \
|
||||
{ 'Z', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_RW10 \
|
||||
{ 'R', 'W', '1', '0', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_INVZ \
|
||||
{ 'I', 'N', 'V', 'Z', 0x90, 0x2d, 0x58, 0x4a, \
|
||||
0x92, 0x0b, 0x77, 0x3f, 0x1f, 0x2c, 0x55, 0x6b}
|
||||
#define UVC_GUID_FORMAT_INZI \
|
||||
{ 'I', 'N', 'Z', 'I', 0x66, 0x1a, 0x42, 0xa2, \
|
||||
0x90, 0x65, 0xd0, 0x18, 0x14, 0xa8, 0xef, 0x8a}
|
||||
#define UVC_GUID_FORMAT_INVI \
|
||||
{ 'I', 'N', 'V', 'I', 0xdb, 0x57, 0x49, 0x5e, \
|
||||
0x8e, 0x3f, 0xf4, 0x79, 0x53, 0x2b, 0x94, 0x6f}
|
||||
#define UVC_GUID_FORMAT_CNF4 \
|
||||
{ 'C', ' ', ' ', ' ', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
|
||||
#define UVC_GUID_FORMAT_D3DFMT_L8 \
|
||||
{0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
#define UVC_GUID_FORMAT_KSMEDIA_L8_IR \
|
||||
{0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
|
||||
#define UVC_GUID_FORMAT_HEVC \
|
||||
{ 'H', 'E', 'V', 'C', 0x00, 0x00, 0x10, 0x00, \
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Driver specific constants.
|
||||
*/
|
||||
@ -283,12 +145,6 @@ struct uvc_control {
|
||||
struct uvc_fh *handle; /* File handle that last changed the control. */
|
||||
};
|
||||
|
||||
struct uvc_format_desc {
|
||||
char *name;
|
||||
u8 guid[16];
|
||||
u32 fcc;
|
||||
};
|
||||
|
||||
/*
|
||||
* The term 'entity' refers to both UVC units and UVC terminals.
|
||||
*
|
||||
@ -911,9 +767,6 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
||||
struct uvc_xu_control_query *xqry);
|
||||
|
||||
/* Utility functions */
|
||||
void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
|
||||
unsigned int n_terms, unsigned int threshold);
|
||||
u32 uvc_fraction_to_interval(u32 numerator, u32 denominator);
|
||||
struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
|
||||
u8 epaddr);
|
||||
u16 uvc_endpoint_max_bpi(struct usb_device *dev, struct usb_host_endpoint *ep);
|
||||
|
@ -484,3 +484,89 @@ s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul,
|
||||
return freq > 0 ? freq : -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_get_link_freq);
|
||||
|
||||
/*
|
||||
* Simplify a fraction using a simple continued fraction decomposition. The
|
||||
* idea here is to convert fractions such as 333333/10000000 to 1/30 using
|
||||
* 32 bit arithmetic only. The algorithm is not perfect and relies upon two
|
||||
* arbitrary parameters to remove non-significative terms from the simple
|
||||
* continued fraction decomposition. Using 8 and 333 for n_terms and threshold
|
||||
* respectively seems to give nice results.
|
||||
*/
|
||||
void v4l2_simplify_fraction(u32 *numerator, u32 *denominator,
|
||||
unsigned int n_terms, unsigned int threshold)
|
||||
{
|
||||
u32 *an;
|
||||
u32 x, y, r;
|
||||
unsigned int i, n;
|
||||
|
||||
an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL);
|
||||
if (an == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Convert the fraction to a simple continued fraction. See
|
||||
* https://en.wikipedia.org/wiki/Continued_fraction
|
||||
* Stop if the current term is bigger than or equal to the given
|
||||
* threshold.
|
||||
*/
|
||||
x = *numerator;
|
||||
y = *denominator;
|
||||
|
||||
for (n = 0; n < n_terms && y != 0; ++n) {
|
||||
an[n] = x / y;
|
||||
if (an[n] >= threshold) {
|
||||
if (n < 2)
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
|
||||
r = x - an[n] * y;
|
||||
x = y;
|
||||
y = r;
|
||||
}
|
||||
|
||||
/* Expand the simple continued fraction back to an integer fraction. */
|
||||
x = 0;
|
||||
y = 1;
|
||||
|
||||
for (i = n; i > 0; --i) {
|
||||
r = y;
|
||||
y = an[i-1] * y + x;
|
||||
x = r;
|
||||
}
|
||||
|
||||
*numerator = y;
|
||||
*denominator = x;
|
||||
kfree(an);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_simplify_fraction);
|
||||
|
||||
/*
|
||||
* Convert a fraction to a frame interval in 100ns multiples. The idea here is
|
||||
* to compute numerator / denominator * 10000000 using 32 bit fixed point
|
||||
* arithmetic only.
|
||||
*/
|
||||
u32 v4l2_fraction_to_interval(u32 numerator, u32 denominator)
|
||||
{
|
||||
u32 multiplier;
|
||||
|
||||
/* Saturate the result if the operation would overflow. */
|
||||
if (denominator == 0 ||
|
||||
numerator/denominator >= ((u32)-1)/10000000)
|
||||
return (u32)-1;
|
||||
|
||||
/*
|
||||
* Divide both the denominator and the multiplier by two until
|
||||
* numerator * multiplier doesn't overflow. If anyone knows a better
|
||||
* algorithm please let me know.
|
||||
*/
|
||||
multiplier = 10000000;
|
||||
while (numerator > ((u32)-1)/multiplier) {
|
||||
multiplier /= 2;
|
||||
denominator /= 2;
|
||||
}
|
||||
|
||||
return denominator ? numerator * multiplier / denominator : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_fraction_to_interval);
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -638,7 +638,7 @@ static void tegra186_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl)
|
||||
mutex_unlock(&padctl->lock);
|
||||
}
|
||||
|
||||
static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
|
||||
static void tegra186_utmi_pad_power_on(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
@ -656,6 +656,8 @@ static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "power on UTMI pad %u\n", index);
|
||||
|
||||
tegra186_utmi_bias_pad_power_on(padctl);
|
||||
|
||||
udelay(2);
|
||||
@ -669,7 +671,7 @@ static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
}
|
||||
|
||||
static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
|
||||
static void tegra186_utmi_pad_power_down(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
@ -679,6 +681,8 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
|
||||
if (!phy)
|
||||
return;
|
||||
|
||||
dev_dbg(padctl->dev, "power down UTMI pad %u\n", index);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
value |= USB2_OTG_PD;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
@ -849,15 +853,14 @@ static int tegra186_utmi_phy_power_on(struct phy *phy)
|
||||
value |= RPD_CTRL(priv->calib.rpd_ctrl);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
|
||||
/* TODO: pad power saving */
|
||||
tegra_phy_xusb_utmi_pad_power_on(phy);
|
||||
tegra186_utmi_pad_power_on(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_utmi_phy_power_off(struct phy *phy)
|
||||
{
|
||||
/* TODO: pad power saving */
|
||||
tegra_phy_xusb_utmi_pad_power_down(phy);
|
||||
tegra186_utmi_pad_power_down(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1483,6 +1486,8 @@ static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
|
||||
.suspend_noirq = tegra186_xusb_padctl_suspend_noirq,
|
||||
.resume_noirq = tegra186_xusb_padctl_resume_noirq,
|
||||
.vbus_override = tegra186_xusb_padctl_vbus_override,
|
||||
.utmi_pad_power_on = tegra186_utmi_pad_power_on,
|
||||
.utmi_pad_power_down = tegra186_utmi_pad_power_down,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -1459,6 +1459,26 @@ int tegra_phy_xusb_utmi_port_reset(struct phy *phy)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset);
|
||||
|
||||
void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
|
||||
if (padctl->soc->ops->utmi_pad_power_on)
|
||||
padctl->soc->ops->utmi_pad_power_on(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on);
|
||||
|
||||
void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
|
||||
if (padctl->soc->ops->utmi_pad_power_down)
|
||||
padctl->soc->ops->utmi_pad_power_down(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_down);
|
||||
|
||||
int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
|
||||
unsigned int port)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2015, Google Inc.
|
||||
*/
|
||||
|
||||
@ -412,6 +412,8 @@ struct tegra_xusb_padctl_ops {
|
||||
unsigned int index, bool enable);
|
||||
int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set);
|
||||
int (*utmi_port_reset)(struct phy *phy);
|
||||
void (*utmi_pad_power_on)(struct phy *phy);
|
||||
void (*utmi_pad_power_down)(struct phy *phy);
|
||||
};
|
||||
|
||||
struct tegra_xusb_padctl_soc {
|
||||
|
@ -27,6 +27,16 @@ config USB4_DEBUGFS_WRITE
|
||||
Only enable this if you know what you are doing! Never enable
|
||||
this for production systems or distro kernels.
|
||||
|
||||
config USB4_DEBUGFS_MARGINING
|
||||
bool "Expose receiver lane margining operations under USB4 ports (DANGEROUS)"
|
||||
depends on DEBUG_FS
|
||||
depends on USB4_DEBUGFS_WRITE
|
||||
help
|
||||
Enables hardware and software based receiver lane margining support
|
||||
under each USB4 port. Used for electrical quality and robustness
|
||||
validation during manufacturing. Should not be enabled by distro
|
||||
kernels.
|
||||
|
||||
config USB4_KUNIT_TEST
|
||||
bool "KUnit tests" if !KUNIT_ALL_TESTS
|
||||
depends on USB4 && KUNIT=y
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "tb.h"
|
||||
#include "sb_regs.h"
|
||||
|
||||
#define PORT_CAP_PCIE_LEN 1
|
||||
#define PORT_CAP_POWER_LEN 2
|
||||
@ -187,6 +188,828 @@ static ssize_t switch_regs_write(struct file *file, const char __user *user_buf,
|
||||
#define DEBUGFS_MODE 0400
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)
|
||||
/**
|
||||
* struct tb_margining - Lane margining support
|
||||
* @caps: Port lane margining capabilities
|
||||
* @results: Last lane margining results
|
||||
* @lanes: %0, %1 or %7 (all)
|
||||
* @min_ber_level: Minimum supported BER level contour value
|
||||
* @max_ber_level: Maximum supported BER level contour value
|
||||
* @ber_level: Current BER level contour value
|
||||
* @voltage_steps: Number of mandatory voltage steps
|
||||
* @max_voltage_offset: Maximum mandatory voltage offset (in mV)
|
||||
* @time_steps: Number of time margin steps
|
||||
* @max_time_offset: Maximum time margin offset (in mUI)
|
||||
* @software: %true if software margining is used instead of hardware
|
||||
* @time: %true if time margining is used instead of voltage
|
||||
* @right_high: %false if left/low margin test is performed, %true if
|
||||
* right/high
|
||||
*/
|
||||
struct tb_margining {
|
||||
u32 caps[2];
|
||||
u32 results[2];
|
||||
unsigned int lanes;
|
||||
unsigned int min_ber_level;
|
||||
unsigned int max_ber_level;
|
||||
unsigned int ber_level;
|
||||
unsigned int voltage_steps;
|
||||
unsigned int max_voltage_offset;
|
||||
unsigned int time_steps;
|
||||
unsigned int max_time_offset;
|
||||
bool software;
|
||||
bool time;
|
||||
bool right_high;
|
||||
};
|
||||
|
||||
static bool supports_software(const struct usb4_port *usb4)
|
||||
{
|
||||
return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
|
||||
}
|
||||
|
||||
static bool supports_hardware(const struct usb4_port *usb4)
|
||||
{
|
||||
return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW;
|
||||
}
|
||||
|
||||
static bool both_lanes(const struct usb4_port *usb4)
|
||||
{
|
||||
return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_2_LANES;
|
||||
}
|
||||
|
||||
static unsigned int independent_voltage_margins(const struct usb4_port *usb4)
|
||||
{
|
||||
return (usb4->margining->caps[0] & USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK) >>
|
||||
USB4_MARGIN_CAP_0_VOLTAGE_INDP_SHIFT;
|
||||
}
|
||||
|
||||
static bool supports_time(const struct usb4_port *usb4)
|
||||
{
|
||||
return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_TIME;
|
||||
}
|
||||
|
||||
/* Only applicable if supports_time() returns true */
|
||||
static unsigned int independent_time_margins(const struct usb4_port *usb4)
|
||||
{
|
||||
return (usb4->margining->caps[1] & USB4_MARGIN_CAP_1_TIME_INDP_MASK) >>
|
||||
USB4_MARGIN_CAP_1_TIME_INDP_SHIFT;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
margining_ber_level_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
unsigned int val;
|
||||
int ret = 0;
|
||||
char *buf;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (usb4->margining->software) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
buf = validate_and_copy_from_user(user_buf, &count);
|
||||
if (IS_ERR(buf)) {
|
||||
ret = PTR_ERR(buf);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
buf[count - 1] = '\0';
|
||||
|
||||
ret = kstrtouint(buf, 10, &val);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
if (val < usb4->margining->min_ber_level ||
|
||||
val > usb4->margining->max_ber_level) {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
usb4->margining->ber_level = val;
|
||||
|
||||
out_free:
|
||||
free_page((unsigned long)buf);
|
||||
out_unlock:
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
|
||||
static void ber_level_show(struct seq_file *s, unsigned int val)
|
||||
{
|
||||
if (val % 2)
|
||||
seq_printf(s, "3 * 1e%d (%u)\n", -12 + (val + 1) / 2, val);
|
||||
else
|
||||
seq_printf(s, "1e%d (%u)\n", -12 + val / 2, val);
|
||||
}
|
||||
|
||||
static int margining_ber_level_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
|
||||
if (usb4->margining->software)
|
||||
return -EINVAL;
|
||||
ber_level_show(s, usb4->margining->ber_level);
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_ber_level);
|
||||
|
||||
static int margining_caps_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
u32 cap0, cap1;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* Dump the raw caps first */
|
||||
cap0 = usb4->margining->caps[0];
|
||||
seq_printf(s, "0x%08x\n", cap0);
|
||||
cap1 = usb4->margining->caps[1];
|
||||
seq_printf(s, "0x%08x\n", cap1);
|
||||
|
||||
seq_printf(s, "# software margining: %s\n",
|
||||
supports_software(usb4) ? "yes" : "no");
|
||||
if (supports_hardware(usb4)) {
|
||||
seq_puts(s, "# hardware margining: yes\n");
|
||||
seq_puts(s, "# minimum BER level contour: ");
|
||||
ber_level_show(s, usb4->margining->min_ber_level);
|
||||
seq_puts(s, "# maximum BER level contour: ");
|
||||
ber_level_show(s, usb4->margining->max_ber_level);
|
||||
} else {
|
||||
seq_puts(s, "# hardware margining: no\n");
|
||||
}
|
||||
|
||||
seq_printf(s, "# both lanes simultaneously: %s\n",
|
||||
both_lanes(usb4) ? "yes" : "no");
|
||||
seq_printf(s, "# voltage margin steps: %u\n",
|
||||
usb4->margining->voltage_steps);
|
||||
seq_printf(s, "# maximum voltage offset: %u mV\n",
|
||||
usb4->margining->max_voltage_offset);
|
||||
|
||||
switch (independent_voltage_margins(usb4)) {
|
||||
case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
|
||||
seq_puts(s, "# returns minimum between high and low voltage margins\n");
|
||||
break;
|
||||
case USB4_MARGIN_CAP_0_VOLTAGE_HL:
|
||||
seq_puts(s, "# returns high or low voltage margin\n");
|
||||
break;
|
||||
case USB4_MARGIN_CAP_0_VOLTAGE_BOTH:
|
||||
seq_puts(s, "# returns both high and low margins\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (supports_time(usb4)) {
|
||||
seq_puts(s, "# time margining: yes\n");
|
||||
seq_printf(s, "# time margining is destructive: %s\n",
|
||||
cap1 & USB4_MARGIN_CAP_1_TIME_DESTR ? "yes" : "no");
|
||||
|
||||
switch (independent_time_margins(usb4)) {
|
||||
case USB4_MARGIN_CAP_1_TIME_MIN:
|
||||
seq_puts(s, "# returns minimum between left and right time margins\n");
|
||||
break;
|
||||
case USB4_MARGIN_CAP_1_TIME_LR:
|
||||
seq_puts(s, "# returns left or right margin\n");
|
||||
break;
|
||||
case USB4_MARGIN_CAP_1_TIME_BOTH:
|
||||
seq_puts(s, "# returns both left and right margins\n");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(s, "# time margin steps: %u\n",
|
||||
usb4->margining->time_steps);
|
||||
seq_printf(s, "# maximum time offset: %u mUI\n",
|
||||
usb4->margining->max_time_offset);
|
||||
} else {
|
||||
seq_puts(s, "# time margining: no\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RO(margining_caps);
|
||||
|
||||
static ssize_t
|
||||
margining_lanes_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
int ret = 0;
|
||||
char *buf;
|
||||
|
||||
buf = validate_and_copy_from_user(user_buf, &count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
buf[count - 1] = '\0';
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!strcmp(buf, "0")) {
|
||||
usb4->margining->lanes = 0;
|
||||
} else if (!strcmp(buf, "1")) {
|
||||
usb4->margining->lanes = 1;
|
||||
} else if (!strcmp(buf, "all")) {
|
||||
/* Needs to be supported */
|
||||
if (both_lanes(usb4))
|
||||
usb4->margining->lanes = 7;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
out_free:
|
||||
free_page((unsigned long)buf);
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
|
||||
static int margining_lanes_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
unsigned int lanes;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
lanes = usb4->margining->lanes;
|
||||
if (both_lanes(usb4)) {
|
||||
if (!lanes)
|
||||
seq_puts(s, "[0] 1 all\n");
|
||||
else if (lanes == 1)
|
||||
seq_puts(s, "0 [1] all\n");
|
||||
else
|
||||
seq_puts(s, "0 1 [all]\n");
|
||||
} else {
|
||||
if (!lanes)
|
||||
seq_puts(s, "[0] 1\n");
|
||||
else
|
||||
seq_puts(s, "0 [1]\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_lanes);
|
||||
|
||||
static ssize_t margining_mode_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
int ret = 0;
|
||||
char *buf;
|
||||
|
||||
buf = validate_and_copy_from_user(user_buf, &count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
buf[count - 1] = '\0';
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!strcmp(buf, "software")) {
|
||||
if (supports_software(usb4))
|
||||
usb4->margining->software = true;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
} else if (!strcmp(buf, "hardware")) {
|
||||
if (supports_hardware(usb4))
|
||||
usb4->margining->software = false;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
out_free:
|
||||
free_page((unsigned long)buf);
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static int margining_mode_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
const struct tb_port *port = s->private;
|
||||
const struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
const char *space = "";
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (supports_software(usb4)) {
|
||||
if (usb4->margining->software)
|
||||
seq_puts(s, "[software]");
|
||||
else
|
||||
seq_puts(s, "software");
|
||||
space = " ";
|
||||
}
|
||||
if (supports_hardware(usb4)) {
|
||||
if (usb4->margining->software)
|
||||
seq_printf(s, "%shardware", space);
|
||||
else
|
||||
seq_printf(s, "%s[hardware]", space);
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
seq_puts(s, "\n");
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_mode);
|
||||
|
||||
static int margining_run_write(void *data, u64 val)
|
||||
{
|
||||
struct tb_port *port = data;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb_switch *sw = port->sw;
|
||||
struct tb_margining *margining;
|
||||
struct tb *tb = sw->tb;
|
||||
int ret;
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(&sw->dev);
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out_rpm_put;
|
||||
}
|
||||
|
||||
/*
|
||||
* CL states may interfere with lane margining so inform the user know
|
||||
* and bail out.
|
||||
*/
|
||||
if (tb_port_is_clx_enabled(port, TB_CL1 | TB_CL2)) {
|
||||
tb_port_warn(port,
|
||||
"CL states are enabled, Disable them with clx=0 and re-connect\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
margining = usb4->margining;
|
||||
|
||||
if (margining->software) {
|
||||
tb_port_dbg(port, "running software %s lane margining for lanes %u\n",
|
||||
margining->time ? "time" : "voltage", margining->lanes);
|
||||
ret = usb4_port_sw_margin(port, margining->lanes, margining->time,
|
||||
margining->right_high,
|
||||
USB4_MARGIN_SW_COUNTER_CLEAR);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = usb4_port_sw_margin_errors(port, &margining->results[0]);
|
||||
} else {
|
||||
tb_port_dbg(port, "running hardware %s lane margining for lanes %u\n",
|
||||
margining->time ? "time" : "voltage", margining->lanes);
|
||||
/* Clear the results */
|
||||
margining->results[0] = 0;
|
||||
margining->results[1] = 0;
|
||||
ret = usb4_port_hw_margin(port, margining->lanes,
|
||||
margining->ber_level, margining->time,
|
||||
margining->right_high, margining->results);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&tb->lock);
|
||||
out_rpm_put:
|
||||
pm_runtime_mark_last_busy(&sw->dev);
|
||||
pm_runtime_put_autosuspend(&sw->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(margining_run_fops, NULL, margining_run_write,
|
||||
"%llu\n");
|
||||
|
||||
static ssize_t margining_results_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* Just clear the results */
|
||||
usb4->margining->results[0] = 0;
|
||||
usb4->margining->results[1] = 0;
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void voltage_margin_show(struct seq_file *s,
|
||||
const struct tb_margining *margining, u8 val)
|
||||
{
|
||||
unsigned int tmp, voltage;
|
||||
|
||||
tmp = val & USB4_MARGIN_HW_RES_1_MARGIN_MASK;
|
||||
voltage = tmp * margining->max_voltage_offset / margining->voltage_steps;
|
||||
seq_printf(s, "%u mV (%u)", voltage, tmp);
|
||||
if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
|
||||
seq_puts(s, " exceeds maximum");
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
|
||||
static void time_margin_show(struct seq_file *s,
|
||||
const struct tb_margining *margining, u8 val)
|
||||
{
|
||||
unsigned int tmp, interval;
|
||||
|
||||
tmp = val & USB4_MARGIN_HW_RES_1_MARGIN_MASK;
|
||||
interval = tmp * margining->max_time_offset / margining->time_steps;
|
||||
seq_printf(s, "%u mUI (%u)", interval, tmp);
|
||||
if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
|
||||
seq_puts(s, " exceeds maximum");
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
|
||||
static int margining_results_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb_margining *margining;
|
||||
struct tb *tb = port->sw->tb;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
margining = usb4->margining;
|
||||
/* Dump the raw results first */
|
||||
seq_printf(s, "0x%08x\n", margining->results[0]);
|
||||
/* Only the hardware margining has two result dwords */
|
||||
if (!margining->software) {
|
||||
unsigned int val;
|
||||
|
||||
seq_printf(s, "0x%08x\n", margining->results[1]);
|
||||
|
||||
if (margining->time) {
|
||||
if (!margining->lanes || margining->lanes == 7) {
|
||||
val = margining->results[1];
|
||||
seq_puts(s, "# lane 0 right time margin: ");
|
||||
time_margin_show(s, margining, val);
|
||||
val = margining->results[1] >>
|
||||
USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT;
|
||||
seq_puts(s, "# lane 0 left time margin: ");
|
||||
time_margin_show(s, margining, val);
|
||||
}
|
||||
if (margining->lanes == 1 || margining->lanes == 7) {
|
||||
val = margining->results[1] >>
|
||||
USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT;
|
||||
seq_puts(s, "# lane 1 right time margin: ");
|
||||
time_margin_show(s, margining, val);
|
||||
val = margining->results[1] >>
|
||||
USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT;
|
||||
seq_puts(s, "# lane 1 left time margin: ");
|
||||
time_margin_show(s, margining, val);
|
||||
}
|
||||
} else {
|
||||
if (!margining->lanes || margining->lanes == 7) {
|
||||
val = margining->results[1];
|
||||
seq_puts(s, "# lane 0 high voltage margin: ");
|
||||
voltage_margin_show(s, margining, val);
|
||||
val = margining->results[1] >>
|
||||
USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT;
|
||||
seq_puts(s, "# lane 0 low voltage margin: ");
|
||||
voltage_margin_show(s, margining, val);
|
||||
}
|
||||
if (margining->lanes == 1 || margining->lanes == 7) {
|
||||
val = margining->results[1] >>
|
||||
USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT;
|
||||
seq_puts(s, "# lane 1 high voltage margin: ");
|
||||
voltage_margin_show(s, margining, val);
|
||||
val = margining->results[1] >>
|
||||
USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT;
|
||||
seq_puts(s, "# lane 1 low voltage margin: ");
|
||||
voltage_margin_show(s, margining, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_results);
|
||||
|
||||
static ssize_t margining_test_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
int ret = 0;
|
||||
char *buf;
|
||||
|
||||
buf = validate_and_copy_from_user(user_buf, &count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
buf[count - 1] = '\0';
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!strcmp(buf, "time") && supports_time(usb4))
|
||||
usb4->margining->time = true;
|
||||
else if (!strcmp(buf, "voltage"))
|
||||
usb4->margining->time = false;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
out_free:
|
||||
free_page((unsigned long)buf);
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static int margining_test_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (supports_time(usb4)) {
|
||||
if (usb4->margining->time)
|
||||
seq_puts(s, "voltage [time]\n");
|
||||
else
|
||||
seq_puts(s, "[voltage] time\n");
|
||||
} else {
|
||||
seq_puts(s, "[voltage]\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_test);
|
||||
|
||||
static ssize_t margining_margin_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
int ret = 0;
|
||||
char *buf;
|
||||
|
||||
buf = validate_and_copy_from_user(user_buf, &count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
buf[count - 1] = '\0';
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (usb4->margining->time) {
|
||||
if (!strcmp(buf, "left"))
|
||||
usb4->margining->right_high = false;
|
||||
else if (!strcmp(buf, "right"))
|
||||
usb4->margining->right_high = true;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
if (!strcmp(buf, "low"))
|
||||
usb4->margining->right_high = false;
|
||||
else if (!strcmp(buf, "high"))
|
||||
usb4->margining->right_high = true;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
out_free:
|
||||
free_page((unsigned long)buf);
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static int margining_margin_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_port *port = s->private;
|
||||
struct usb4_port *usb4 = port->usb4;
|
||||
struct tb *tb = port->sw->tb;
|
||||
|
||||
if (mutex_lock_interruptible(&tb->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (usb4->margining->time) {
|
||||
if (usb4->margining->right_high)
|
||||
seq_puts(s, "left [right]\n");
|
||||
else
|
||||
seq_puts(s, "[left] right\n");
|
||||
} else {
|
||||
if (usb4->margining->right_high)
|
||||
seq_puts(s, "low [high]\n");
|
||||
else
|
||||
seq_puts(s, "[low] high\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_margin);
|
||||
|
||||
static void margining_port_init(struct tb_port *port)
|
||||
{
|
||||
struct tb_margining *margining;
|
||||
struct dentry *dir, *parent;
|
||||
struct usb4_port *usb4;
|
||||
char dir_name[10];
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
usb4 = port->usb4;
|
||||
if (!usb4)
|
||||
return;
|
||||
|
||||
snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
|
||||
parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
|
||||
|
||||
margining = kzalloc(sizeof(*margining), GFP_KERNEL);
|
||||
if (!margining)
|
||||
return;
|
||||
|
||||
ret = usb4_port_margining_caps(port, margining->caps);
|
||||
if (ret) {
|
||||
kfree(margining);
|
||||
return;
|
||||
}
|
||||
|
||||
usb4->margining = margining;
|
||||
|
||||
/* Set the initial mode */
|
||||
if (supports_software(usb4))
|
||||
margining->software = true;
|
||||
|
||||
val = (margining->caps[0] & USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK) >>
|
||||
USB4_MARGIN_CAP_0_VOLTAGE_STEPS_SHIFT;
|
||||
margining->voltage_steps = val;
|
||||
val = (margining->caps[0] & USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK) >>
|
||||
USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_SHIFT;
|
||||
margining->max_voltage_offset = 74 + val * 2;
|
||||
|
||||
if (supports_time(usb4)) {
|
||||
val = (margining->caps[1] & USB4_MARGIN_CAP_1_TIME_STEPS_MASK) >>
|
||||
USB4_MARGIN_CAP_1_TIME_STEPS_SHIFT;
|
||||
margining->time_steps = val;
|
||||
val = (margining->caps[1] & USB4_MARGIN_CAP_1_TIME_OFFSET_MASK) >>
|
||||
USB4_MARGIN_CAP_1_TIME_OFFSET_SHIFT;
|
||||
/*
|
||||
* Store it as mUI (milli Unit Interval) because we want
|
||||
* to keep it as integer.
|
||||
*/
|
||||
margining->max_time_offset = 200 + 10 * val;
|
||||
}
|
||||
|
||||
dir = debugfs_create_dir("margining", parent);
|
||||
if (supports_hardware(usb4)) {
|
||||
val = (margining->caps[1] & USB4_MARGIN_CAP_1_MIN_BER_MASK) >>
|
||||
USB4_MARGIN_CAP_1_MIN_BER_SHIFT;
|
||||
margining->min_ber_level = val;
|
||||
val = (margining->caps[1] & USB4_MARGIN_CAP_1_MAX_BER_MASK) >>
|
||||
USB4_MARGIN_CAP_1_MAX_BER_SHIFT;
|
||||
margining->max_ber_level = val;
|
||||
|
||||
/* Set the default to minimum */
|
||||
margining->ber_level = margining->min_ber_level;
|
||||
|
||||
debugfs_create_file("ber_level_contour", 0400, dir, port,
|
||||
&margining_ber_level_fops);
|
||||
}
|
||||
debugfs_create_file("caps", 0400, dir, port, &margining_caps_fops);
|
||||
debugfs_create_file("lanes", 0600, dir, port, &margining_lanes_fops);
|
||||
debugfs_create_file("mode", 0600, dir, port, &margining_mode_fops);
|
||||
debugfs_create_file("run", 0600, dir, port, &margining_run_fops);
|
||||
debugfs_create_file("results", 0600, dir, port, &margining_results_fops);
|
||||
debugfs_create_file("test", 0600, dir, port, &margining_test_fops);
|
||||
if (independent_voltage_margins(usb4) ||
|
||||
(supports_time(usb4) && independent_time_margins(usb4)))
|
||||
debugfs_create_file("margin", 0600, dir, port, &margining_margin_fops);
|
||||
}
|
||||
|
||||
static void margining_port_remove(struct tb_port *port)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char dir_name[10];
|
||||
|
||||
if (!port->usb4)
|
||||
return;
|
||||
|
||||
snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
|
||||
parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
|
||||
debugfs_remove_recursive(debugfs_lookup("margining", parent));
|
||||
|
||||
kfree(port->usb4->margining);
|
||||
port->usb4->margining = NULL;
|
||||
}
|
||||
|
||||
static void margining_switch_init(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *upstream, *downstream;
|
||||
struct tb_switch *parent_sw;
|
||||
u64 route = tb_route(sw);
|
||||
|
||||
if (!route)
|
||||
return;
|
||||
|
||||
upstream = tb_upstream_port(sw);
|
||||
parent_sw = tb_switch_parent(sw);
|
||||
downstream = tb_port_at(route, parent_sw);
|
||||
|
||||
margining_port_init(downstream);
|
||||
margining_port_init(upstream);
|
||||
}
|
||||
|
||||
static void margining_switch_remove(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_switch *parent_sw;
|
||||
struct tb_port *downstream;
|
||||
u64 route = tb_route(sw);
|
||||
|
||||
if (!route)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Upstream is removed with the router itself but we need to
|
||||
* remove the downstream port margining directory.
|
||||
*/
|
||||
parent_sw = tb_switch_parent(sw);
|
||||
downstream = tb_port_at(route, parent_sw);
|
||||
margining_port_remove(downstream);
|
||||
}
|
||||
|
||||
static void margining_xdomain_init(struct tb_xdomain *xd)
|
||||
{
|
||||
struct tb_switch *parent_sw;
|
||||
struct tb_port *downstream;
|
||||
|
||||
parent_sw = tb_xdomain_parent(xd);
|
||||
downstream = tb_port_at(xd->route, parent_sw);
|
||||
|
||||
margining_port_init(downstream);
|
||||
}
|
||||
|
||||
static void margining_xdomain_remove(struct tb_xdomain *xd)
|
||||
{
|
||||
struct tb_switch *parent_sw;
|
||||
struct tb_port *downstream;
|
||||
|
||||
parent_sw = tb_xdomain_parent(xd);
|
||||
downstream = tb_port_at(xd->route, parent_sw);
|
||||
margining_port_remove(downstream);
|
||||
}
|
||||
#else
|
||||
static inline void margining_switch_init(struct tb_switch *sw) { }
|
||||
static inline void margining_switch_remove(struct tb_switch *sw) { }
|
||||
static inline void margining_xdomain_init(struct tb_xdomain *xd) { }
|
||||
static inline void margining_xdomain_remove(struct tb_xdomain *xd) { }
|
||||
#endif
|
||||
|
||||
static int port_clear_all_counters(struct tb_port *port)
|
||||
{
|
||||
u32 *buf;
|
||||
@ -689,6 +1512,8 @@ void tb_switch_debugfs_init(struct tb_switch *sw)
|
||||
debugfs_create_file("counters", 0600, debugfs_dir, port,
|
||||
&counters_fops);
|
||||
}
|
||||
|
||||
margining_switch_init(sw);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -699,9 +1524,20 @@ void tb_switch_debugfs_init(struct tb_switch *sw)
|
||||
*/
|
||||
void tb_switch_debugfs_remove(struct tb_switch *sw)
|
||||
{
|
||||
margining_switch_remove(sw);
|
||||
debugfs_remove_recursive(sw->debugfs_dir);
|
||||
}
|
||||
|
||||
void tb_xdomain_debugfs_init(struct tb_xdomain *xd)
|
||||
{
|
||||
margining_xdomain_init(xd);
|
||||
}
|
||||
|
||||
void tb_xdomain_debugfs_remove(struct tb_xdomain *xd)
|
||||
{
|
||||
margining_xdomain_remove(xd);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_service_debugfs_init() - Add debugfs directory for service
|
||||
* @svc: Thunderbolt service pointer
|
||||
|
@ -144,11 +144,9 @@ static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
|
||||
if (!uuid_is_null(&uuids[i]))
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
|
||||
&uuids[i]);
|
||||
ret += sysfs_emit_at(buf, ret, "%pUb", &uuids[i]);
|
||||
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s",
|
||||
i < tb->nboot_acl - 1 ? "," : "\n");
|
||||
ret += sysfs_emit_at(buf, ret, "%s", i < tb->nboot_acl - 1 ? "," : "\n");
|
||||
}
|
||||
|
||||
out:
|
||||
@ -247,7 +245,7 @@ static ssize_t deauthorization_show(struct device *dev,
|
||||
tb->security_level == TB_SECURITY_SECURE)
|
||||
deauthorization = !!tb->cm_ops->disapprove_switch;
|
||||
|
||||
return sprintf(buf, "%d\n", deauthorization);
|
||||
return sysfs_emit(buf, "%d\n", deauthorization);
|
||||
}
|
||||
static DEVICE_ATTR_RO(deauthorization);
|
||||
|
||||
@ -270,7 +268,7 @@ static ssize_t security_show(struct device *dev, struct device_attribute *attr,
|
||||
if (tb->security_level < ARRAY_SIZE(tb_security_names))
|
||||
name = tb_security_names[tb->security_level];
|
||||
|
||||
return sprintf(buf, "%s\n", name);
|
||||
return sysfs_emit(buf, "%s\n", name);
|
||||
}
|
||||
static DEVICE_ATTR_RO(security);
|
||||
|
||||
|
@ -2518,6 +2518,9 @@ struct tb *icm_probe(struct tb_nhi *nhi)
|
||||
case PCI_DEVICE_ID_INTEL_ADL_NHI1:
|
||||
case PCI_DEVICE_ID_INTEL_RPL_NHI0:
|
||||
case PCI_DEVICE_ID_INTEL_RPL_NHI1:
|
||||
case PCI_DEVICE_ID_INTEL_MTL_M_NHI0:
|
||||
case PCI_DEVICE_ID_INTEL_MTL_P_NHI0:
|
||||
case PCI_DEVICE_ID_INTEL_MTL_P_NHI1:
|
||||
icm->is_supported = icm_tgl_is_supported;
|
||||
icm->driver_ready = icm_icl_driver_ready;
|
||||
icm->set_uuid = icm_icl_set_uuid;
|
||||
|
@ -1184,6 +1184,7 @@ static void nhi_check_iommu(struct tb_nhi *nhi)
|
||||
static int nhi_init_msi(struct tb_nhi *nhi)
|
||||
{
|
||||
struct pci_dev *pdev = nhi->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int res, irq, nvec;
|
||||
|
||||
/* In case someone left them on. */
|
||||
@ -1214,10 +1215,8 @@ static int nhi_init_msi(struct tb_nhi *nhi)
|
||||
|
||||
res = devm_request_irq(&pdev->dev, irq, nhi_msi,
|
||||
IRQF_NO_SUSPEND, "thunderbolt", nhi);
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "request_irq failed, aborting\n");
|
||||
return res;
|
||||
}
|
||||
if (res)
|
||||
return dev_err_probe(dev, res, "request_irq failed, aborting\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1258,26 +1257,21 @@ static struct tb *nhi_select_cm(struct tb_nhi *nhi)
|
||||
|
||||
static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tb_nhi *nhi;
|
||||
struct tb *tb;
|
||||
int res;
|
||||
|
||||
if (!nhi_imr_valid(pdev)) {
|
||||
dev_warn(&pdev->dev, "firmware image not valid, aborting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!nhi_imr_valid(pdev))
|
||||
return dev_err_probe(dev, -ENODEV, "firmware image not valid, aborting\n");
|
||||
|
||||
res = pcim_enable_device(pdev);
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "cannot enable PCI device, aborting\n");
|
||||
return res;
|
||||
}
|
||||
if (res)
|
||||
return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n");
|
||||
|
||||
res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt");
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n");
|
||||
return res;
|
||||
}
|
||||
if (res)
|
||||
return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n");
|
||||
|
||||
nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL);
|
||||
if (!nhi)
|
||||
@ -1288,7 +1282,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
/* cannot fail - table is allocated in pcim_iomap_regions */
|
||||
nhi->iobase = pcim_iomap_table(pdev)[0];
|
||||
nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
|
||||
dev_dbg(&pdev->dev, "total paths: %d\n", nhi->hop_count);
|
||||
dev_dbg(dev, "total paths: %d\n", nhi->hop_count);
|
||||
|
||||
nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
|
||||
sizeof(*nhi->tx_rings), GFP_KERNEL);
|
||||
@ -1301,18 +1295,14 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
nhi_check_iommu(nhi);
|
||||
|
||||
res = nhi_init_msi(nhi);
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
|
||||
return res;
|
||||
}
|
||||
if (res)
|
||||
return dev_err_probe(dev, res, "cannot enable MSI, aborting\n");
|
||||
|
||||
spin_lock_init(&nhi->lock);
|
||||
|
||||
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "failed to set DMA mask\n");
|
||||
return res;
|
||||
}
|
||||
if (res)
|
||||
return dev_err_probe(dev, res, "failed to set DMA mask\n");
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
@ -1323,13 +1313,11 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
}
|
||||
|
||||
tb = nhi_select_cm(nhi);
|
||||
if (!tb) {
|
||||
dev_err(&nhi->pdev->dev,
|
||||
if (!tb)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"failed to determine connection manager, aborting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_dbg(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n");
|
||||
dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
|
||||
|
||||
res = tb_domain_add(tb);
|
||||
if (res) {
|
||||
@ -1433,6 +1421,7 @@ static struct pci_device_id nhi_ids[] = {
|
||||
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1),
|
||||
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
|
||||
/* Thunderbolt 4 */
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0),
|
||||
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1),
|
||||
@ -1449,6 +1438,12 @@ static struct pci_device_id nhi_ids[] = {
|
||||
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1),
|
||||
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_M_NHI0),
|
||||
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI0),
|
||||
.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 },
|
||||
|
||||
/* Any USB4 compliant host */
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },
|
||||
|
@ -75,6 +75,9 @@ extern const struct tb_nhi_ops icl_nhi_ops;
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef
|
||||
#define PCI_DEVICE_ID_INTEL_ADL_NHI0 0x463e
|
||||
#define PCI_DEVICE_ID_INTEL_ADL_NHI1 0x466d
|
||||
#define PCI_DEVICE_ID_INTEL_MTL_M_NHI0 0x7eb2
|
||||
#define PCI_DEVICE_ID_INTEL_MTL_P_NHI0 0x7ec2
|
||||
#define PCI_DEVICE_ID_INTEL_MTL_P_NHI1 0x7ec3
|
||||
#define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d
|
||||
#define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17
|
||||
#define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b
|
||||
|
@ -12,19 +12,315 @@
|
||||
|
||||
#include "tb.h"
|
||||
|
||||
/* Intel specific NVM offsets */
|
||||
#define INTEL_NVM_DEVID 0x05
|
||||
#define INTEL_NVM_VERSION 0x08
|
||||
#define INTEL_NVM_CSS 0x10
|
||||
#define INTEL_NVM_FLASH_SIZE 0x45
|
||||
|
||||
/* ASMedia specific NVM offsets */
|
||||
#define ASMEDIA_NVM_DATE 0x1c
|
||||
#define ASMEDIA_NVM_VERSION 0x28
|
||||
|
||||
static DEFINE_IDA(nvm_ida);
|
||||
|
||||
/**
|
||||
* struct tb_nvm_vendor_ops - Vendor specific NVM operations
|
||||
* @read_version: Reads out NVM version from the flash
|
||||
* @validate: Validates the NVM image before update (optional)
|
||||
* @write_headers: Writes headers before the rest of the image (optional)
|
||||
*/
|
||||
struct tb_nvm_vendor_ops {
|
||||
int (*read_version)(struct tb_nvm *nvm);
|
||||
int (*validate)(struct tb_nvm *nvm);
|
||||
int (*write_headers)(struct tb_nvm *nvm);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_nvm_vendor - Vendor to &struct tb_nvm_vendor_ops mapping
|
||||
* @vendor: Vendor ID
|
||||
* @vops: Vendor specific NVM operations
|
||||
*
|
||||
* Maps vendor ID to NVM vendor operations. If there is no mapping then
|
||||
* NVM firmware upgrade is disabled for the device.
|
||||
*/
|
||||
struct tb_nvm_vendor {
|
||||
u16 vendor;
|
||||
const struct tb_nvm_vendor_ops *vops;
|
||||
};
|
||||
|
||||
static int intel_switch_nvm_version(struct tb_nvm *nvm)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(nvm->dev);
|
||||
u32 val, nvm_size, hdr_size;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If the switch is in safe-mode the only accessible portion of
|
||||
* the NVM is the non-active one where userspace is expected to
|
||||
* write new functional NVM.
|
||||
*/
|
||||
if (sw->safe_mode)
|
||||
return 0;
|
||||
|
||||
ret = tb_switch_nvm_read(sw, INTEL_NVM_FLASH_SIZE, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K;
|
||||
nvm_size = (SZ_1M << (val & 7)) / 8;
|
||||
nvm_size = (nvm_size - hdr_size) / 2;
|
||||
|
||||
ret = tb_switch_nvm_read(sw, INTEL_NVM_VERSION, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvm->major = (val >> 16) & 0xff;
|
||||
nvm->minor = (val >> 8) & 0xff;
|
||||
nvm->active_size = nvm_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_switch_nvm_validate(struct tb_nvm *nvm)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(nvm->dev);
|
||||
unsigned int image_size, hdr_size;
|
||||
u16 ds_size, device_id;
|
||||
u8 *buf = nvm->buf;
|
||||
|
||||
image_size = nvm->buf_data_size;
|
||||
|
||||
/*
|
||||
* FARB pointer must point inside the image and must at least
|
||||
* contain parts of the digital section we will be reading here.
|
||||
*/
|
||||
hdr_size = (*(u32 *)buf) & 0xffffff;
|
||||
if (hdr_size + INTEL_NVM_DEVID + 2 >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Digital section start should be aligned to 4k page */
|
||||
if (!IS_ALIGNED(hdr_size, SZ_4K))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Read digital section size and check that it also fits inside
|
||||
* the image.
|
||||
*/
|
||||
ds_size = *(u16 *)(buf + hdr_size);
|
||||
if (ds_size >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (sw->safe_mode)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Make sure the device ID in the image matches the one
|
||||
* we read from the switch config space.
|
||||
*/
|
||||
device_id = *(u16 *)(buf + hdr_size + INTEL_NVM_DEVID);
|
||||
if (device_id != sw->config.device_id)
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip headers in the image */
|
||||
nvm->buf_data_start = buf + hdr_size;
|
||||
nvm->buf_data_size = image_size - hdr_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_switch_nvm_write_headers(struct tb_nvm *nvm)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(nvm->dev);
|
||||
|
||||
if (sw->generation < 3) {
|
||||
int ret;
|
||||
|
||||
/* Write CSS headers first */
|
||||
ret = dma_port_flash_write(sw->dma_port,
|
||||
DMA_PORT_CSS_ADDRESS, nvm->buf + INTEL_NVM_CSS,
|
||||
DMA_PORT_CSS_MAX_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tb_nvm_vendor_ops intel_switch_nvm_ops = {
|
||||
.read_version = intel_switch_nvm_version,
|
||||
.validate = intel_switch_nvm_validate,
|
||||
.write_headers = intel_switch_nvm_write_headers,
|
||||
};
|
||||
|
||||
static int asmedia_switch_nvm_version(struct tb_nvm *nvm)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(nvm->dev);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = tb_switch_nvm_read(sw, ASMEDIA_NVM_VERSION, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvm->major = (val << 16) & 0xff0000;
|
||||
nvm->major |= val & 0x00ff00;
|
||||
nvm->major |= (val >> 16) & 0x0000ff;
|
||||
|
||||
ret = tb_switch_nvm_read(sw, ASMEDIA_NVM_DATE, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvm->minor = (val << 16) & 0xff0000;
|
||||
nvm->minor |= val & 0x00ff00;
|
||||
nvm->minor |= (val >> 16) & 0x0000ff;
|
||||
|
||||
/* ASMedia NVM size is fixed to 512k */
|
||||
nvm->active_size = SZ_512K;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tb_nvm_vendor_ops asmedia_switch_nvm_ops = {
|
||||
.read_version = asmedia_switch_nvm_version,
|
||||
};
|
||||
|
||||
/* Router vendor NVM support table */
|
||||
static const struct tb_nvm_vendor switch_nvm_vendors[] = {
|
||||
{ 0x174c, &asmedia_switch_nvm_ops },
|
||||
{ PCI_VENDOR_ID_INTEL, &intel_switch_nvm_ops },
|
||||
{ 0x8087, &intel_switch_nvm_ops },
|
||||
};
|
||||
|
||||
static int intel_retimer_nvm_version(struct tb_nvm *nvm)
|
||||
{
|
||||
struct tb_retimer *rt = tb_to_retimer(nvm->dev);
|
||||
u32 val, nvm_size;
|
||||
int ret;
|
||||
|
||||
ret = tb_retimer_nvm_read(rt, INTEL_NVM_VERSION, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvm->major = (val >> 16) & 0xff;
|
||||
nvm->minor = (val >> 8) & 0xff;
|
||||
|
||||
ret = tb_retimer_nvm_read(rt, INTEL_NVM_FLASH_SIZE, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvm_size = (SZ_1M << (val & 7)) / 8;
|
||||
nvm_size = (nvm_size - SZ_16K) / 2;
|
||||
nvm->active_size = nvm_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_retimer_nvm_validate(struct tb_nvm *nvm)
|
||||
{
|
||||
struct tb_retimer *rt = tb_to_retimer(nvm->dev);
|
||||
unsigned int image_size, hdr_size;
|
||||
u8 *buf = nvm->buf;
|
||||
u16 ds_size, device;
|
||||
|
||||
image_size = nvm->buf_data_size;
|
||||
|
||||
/*
|
||||
* FARB pointer must point inside the image and must at least
|
||||
* contain parts of the digital section we will be reading here.
|
||||
*/
|
||||
hdr_size = (*(u32 *)buf) & 0xffffff;
|
||||
if (hdr_size + INTEL_NVM_DEVID + 2 >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Digital section start should be aligned to 4k page */
|
||||
if (!IS_ALIGNED(hdr_size, SZ_4K))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Read digital section size and check that it also fits inside
|
||||
* the image.
|
||||
*/
|
||||
ds_size = *(u16 *)(buf + hdr_size);
|
||||
if (ds_size >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Make sure the device ID in the image matches the retimer
|
||||
* hardware.
|
||||
*/
|
||||
device = *(u16 *)(buf + hdr_size + INTEL_NVM_DEVID);
|
||||
if (device != rt->device)
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip headers in the image */
|
||||
nvm->buf_data_start = buf + hdr_size;
|
||||
nvm->buf_data_size = image_size - hdr_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tb_nvm_vendor_ops intel_retimer_nvm_ops = {
|
||||
.read_version = intel_retimer_nvm_version,
|
||||
.validate = intel_retimer_nvm_validate,
|
||||
};
|
||||
|
||||
/* Retimer vendor NVM support table */
|
||||
static const struct tb_nvm_vendor retimer_nvm_vendors[] = {
|
||||
{ 0x8087, &intel_retimer_nvm_ops },
|
||||
};
|
||||
|
||||
/**
|
||||
* tb_nvm_alloc() - Allocate new NVM structure
|
||||
* @dev: Device owning the NVM
|
||||
*
|
||||
* Allocates new NVM structure with unique @id and returns it. In case
|
||||
* of error returns ERR_PTR().
|
||||
* of error returns ERR_PTR(). Specifically returns %-EOPNOTSUPP if the
|
||||
* NVM format of the @dev is not known by the kernel.
|
||||
*/
|
||||
struct tb_nvm *tb_nvm_alloc(struct device *dev)
|
||||
{
|
||||
const struct tb_nvm_vendor_ops *vops = NULL;
|
||||
struct tb_nvm *nvm;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (tb_is_switch(dev)) {
|
||||
const struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(switch_nvm_vendors); i++) {
|
||||
const struct tb_nvm_vendor *v = &switch_nvm_vendors[i];
|
||||
|
||||
if (v->vendor == sw->config.vendor_id) {
|
||||
vops = v->vops;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vops) {
|
||||
tb_sw_dbg(sw, "router NVM format of vendor %#x unknown\n",
|
||||
sw->config.vendor_id);
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
} else if (tb_is_retimer(dev)) {
|
||||
const struct tb_retimer *rt = tb_to_retimer(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(retimer_nvm_vendors); i++) {
|
||||
const struct tb_nvm_vendor *v = &retimer_nvm_vendors[i];
|
||||
|
||||
if (v->vendor == rt->vendor) {
|
||||
vops = v->vops;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vops) {
|
||||
dev_dbg(dev, "retimer NVM format of vendor %#x unknown\n",
|
||||
rt->vendor);
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
} else {
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
|
||||
if (!nvm)
|
||||
@ -38,14 +334,85 @@ struct tb_nvm *tb_nvm_alloc(struct device *dev)
|
||||
|
||||
nvm->id = ret;
|
||||
nvm->dev = dev;
|
||||
nvm->vops = vops;
|
||||
|
||||
return nvm;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_nvm_read_version() - Read and populate NVM version
|
||||
* @nvm: NVM structure
|
||||
*
|
||||
* Uses vendor specific means to read out and fill in the existing
|
||||
* active NVM version. Returns %0 in case of success and negative errno
|
||||
* otherwise.
|
||||
*/
|
||||
int tb_nvm_read_version(struct tb_nvm *nvm)
|
||||
{
|
||||
const struct tb_nvm_vendor_ops *vops = nvm->vops;
|
||||
|
||||
if (vops && vops->read_version)
|
||||
return vops->read_version(nvm);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_nvm_validate() - Validate new NVM image
|
||||
* @nvm: NVM structure
|
||||
*
|
||||
* Runs vendor specific validation over the new NVM image and if all
|
||||
* checks pass returns %0. As side effect updates @nvm->buf_data_start
|
||||
* and @nvm->buf_data_size fields to match the actual data to be written
|
||||
* to the NVM.
|
||||
*
|
||||
* If the validation does not pass then returns negative errno.
|
||||
*/
|
||||
int tb_nvm_validate(struct tb_nvm *nvm)
|
||||
{
|
||||
const struct tb_nvm_vendor_ops *vops = nvm->vops;
|
||||
unsigned int image_size;
|
||||
u8 *buf = nvm->buf;
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
if (!vops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Just do basic image size checks */
|
||||
image_size = nvm->buf_data_size;
|
||||
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Set the default data start in the buffer. The validate method
|
||||
* below can change this if needed.
|
||||
*/
|
||||
nvm->buf_data_start = buf;
|
||||
|
||||
return vops->validate ? vops->validate(nvm) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_nvm_write_headers() - Write headers before the rest of the image
|
||||
* @nvm: NVM structure
|
||||
*
|
||||
* If the vendor NVM format requires writing headers before the rest of
|
||||
* the image, this function does that. Can be called even if the device
|
||||
* does not need this.
|
||||
*
|
||||
* Returns %0 in case of success and negative errno otherwise.
|
||||
*/
|
||||
int tb_nvm_write_headers(struct tb_nvm *nvm)
|
||||
{
|
||||
const struct tb_nvm_vendor_ops *vops = nvm->vops;
|
||||
|
||||
return vops->write_headers ? vops->write_headers(nvm) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_nvm_add_active() - Adds active NVMem device to NVM
|
||||
* @nvm: NVM structure
|
||||
* @size: Size of the active NVM in bytes
|
||||
* @reg_read: Pointer to the function to read the NVM (passed directly to the
|
||||
* NVMem device)
|
||||
*
|
||||
@ -54,7 +421,7 @@ struct tb_nvm *tb_nvm_alloc(struct device *dev)
|
||||
* needed. The first parameter passed to @reg_read is @nvm structure.
|
||||
* Returns %0 in success and negative errno otherwise.
|
||||
*/
|
||||
int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read)
|
||||
int tb_nvm_add_active(struct tb_nvm *nvm, nvmem_reg_read_t reg_read)
|
||||
{
|
||||
struct nvmem_config config;
|
||||
struct nvmem_device *nvmem;
|
||||
@ -67,7 +434,7 @@ int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read
|
||||
config.id = nvm->id;
|
||||
config.stride = 4;
|
||||
config.word_size = 4;
|
||||
config.size = size;
|
||||
config.size = nvm->active_size;
|
||||
config.dev = nvm->dev;
|
||||
config.owner = THIS_MODULE;
|
||||
config.priv = nvm;
|
||||
@ -109,17 +476,17 @@ int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
|
||||
/**
|
||||
* tb_nvm_add_non_active() - Adds non-active NVMem device to NVM
|
||||
* @nvm: NVM structure
|
||||
* @size: Size of the non-active NVM in bytes
|
||||
* @reg_write: Pointer to the function to write the NVM (passed directly
|
||||
* to the NVMem device)
|
||||
*
|
||||
* Registers new non-active NVmem device for @nvm. The @reg_write is called
|
||||
* directly from NVMem so it must handle possible concurrent access if
|
||||
* needed. The first parameter passed to @reg_write is @nvm structure.
|
||||
* The size of the NVMem device is set to %NVM_MAX_SIZE.
|
||||
*
|
||||
* Returns %0 in success and negative errno otherwise.
|
||||
*/
|
||||
int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
|
||||
nvmem_reg_write_t reg_write)
|
||||
int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write)
|
||||
{
|
||||
struct nvmem_config config;
|
||||
struct nvmem_device *nvmem;
|
||||
@ -132,7 +499,7 @@ int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
|
||||
config.id = nvm->id;
|
||||
config.stride = 4;
|
||||
config.word_size = 4;
|
||||
config.size = size;
|
||||
config.size = NVM_MAX_SIZE;
|
||||
config.dev = nvm->dev;
|
||||
config.owner = THIS_MODULE;
|
||||
config.priv = nvm;
|
||||
|
@ -16,8 +16,23 @@
|
||||
|
||||
#define TB_MAX_RETIMER_INDEX 6
|
||||
|
||||
static int tb_retimer_nvm_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
/**
|
||||
* tb_retimer_nvm_read() - Read contents of retimer NVM
|
||||
* @rt: Retimer device
|
||||
* @address: NVM address (in bytes) to start reading
|
||||
* @buf: Data read from NVM is stored here
|
||||
* @size: Number of bytes to read
|
||||
*
|
||||
* Reads retimer NVM and copies the contents to @buf. Returns %0 if the
|
||||
* read was successful and negative errno in case of failure.
|
||||
*/
|
||||
int tb_retimer_nvm_read(struct tb_retimer *rt, unsigned int address, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
return usb4_port_retimer_nvm_read(rt->port, rt->index, address, buf, size);
|
||||
}
|
||||
|
||||
static int nvm_read(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
struct tb_nvm *nvm = priv;
|
||||
struct tb_retimer *rt = tb_to_retimer(nvm->dev);
|
||||
@ -30,7 +45,7 @@ static int tb_retimer_nvm_read(void *priv, unsigned int offset, void *val,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = usb4_port_retimer_nvm_read(rt->port, rt->index, offset, val, bytes);
|
||||
ret = tb_retimer_nvm_read(rt, offset, val, bytes);
|
||||
mutex_unlock(&rt->tb->lock);
|
||||
|
||||
out:
|
||||
@ -40,8 +55,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tb_retimer_nvm_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
struct tb_nvm *nvm = priv;
|
||||
struct tb_retimer *rt = tb_to_retimer(nvm->dev);
|
||||
@ -59,34 +73,23 @@ static int tb_retimer_nvm_write(void *priv, unsigned int offset, void *val,
|
||||
static int tb_retimer_nvm_add(struct tb_retimer *rt)
|
||||
{
|
||||
struct tb_nvm *nvm;
|
||||
u32 val, nvm_size;
|
||||
int ret;
|
||||
|
||||
nvm = tb_nvm_alloc(&rt->dev);
|
||||
if (IS_ERR(nvm))
|
||||
return PTR_ERR(nvm);
|
||||
if (IS_ERR(nvm)) {
|
||||
ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm);
|
||||
goto err_nvm;
|
||||
}
|
||||
|
||||
ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_VERSION, &val,
|
||||
sizeof(val));
|
||||
ret = tb_nvm_read_version(nvm);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
nvm->major = val >> 16;
|
||||
nvm->minor = val >> 8;
|
||||
|
||||
ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_FLASH_SIZE,
|
||||
&val, sizeof(val));
|
||||
ret = tb_nvm_add_active(nvm, nvm_read);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
nvm_size = (SZ_1M << (val & 7)) / 8;
|
||||
nvm_size = (nvm_size - SZ_16K) / 2;
|
||||
|
||||
ret = tb_nvm_add_active(nvm, nvm_size, tb_retimer_nvm_read);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, tb_retimer_nvm_write);
|
||||
ret = tb_nvm_add_non_active(nvm, nvm_write);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
@ -94,59 +97,33 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)
|
||||
return 0;
|
||||
|
||||
err_nvm:
|
||||
tb_nvm_free(nvm);
|
||||
dev_dbg(&rt->dev, "NVM upgrade disabled\n");
|
||||
if (!IS_ERR(nvm))
|
||||
tb_nvm_free(nvm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
|
||||
{
|
||||
unsigned int image_size, hdr_size;
|
||||
const u8 *buf = rt->nvm->buf;
|
||||
u16 ds_size, device;
|
||||
unsigned int image_size;
|
||||
const u8 *buf;
|
||||
int ret;
|
||||
|
||||
ret = tb_nvm_validate(rt->nvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf = rt->nvm->buf_data_start;
|
||||
image_size = rt->nvm->buf_data_size;
|
||||
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* FARB pointer must point inside the image and must at least
|
||||
* contain parts of the digital section we will be reading here.
|
||||
*/
|
||||
hdr_size = (*(u32 *)buf) & 0xffffff;
|
||||
if (hdr_size + NVM_DEVID + 2 >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Digital section start should be aligned to 4k page */
|
||||
if (!IS_ALIGNED(hdr_size, SZ_4K))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Read digital section size and check that it also fits inside
|
||||
* the image.
|
||||
*/
|
||||
ds_size = *(u16 *)(buf + hdr_size);
|
||||
if (ds_size >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Make sure the device ID in the image matches the retimer
|
||||
* hardware.
|
||||
*/
|
||||
device = *(u16 *)(buf + hdr_size + NVM_DEVID);
|
||||
if (device != rt->device)
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip headers in the image */
|
||||
buf += hdr_size;
|
||||
image_size -= hdr_size;
|
||||
|
||||
ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
|
||||
image_size);
|
||||
if (!ret)
|
||||
rt->nvm->flushed = true;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
rt->nvm->flushed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only)
|
||||
@ -185,7 +162,7 @@ static ssize_t device_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_retimer *rt = tb_to_retimer(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", rt->device);
|
||||
return sysfs_emit(buf, "%#x\n", rt->device);
|
||||
}
|
||||
static DEVICE_ATTR_RO(device);
|
||||
|
||||
@ -200,8 +177,10 @@ static ssize_t nvm_authenticate_show(struct device *dev,
|
||||
|
||||
if (!rt->nvm)
|
||||
ret = -EAGAIN;
|
||||
else if (rt->no_nvm_upgrade)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
ret = sprintf(buf, "%#x\n", rt->auth_status);
|
||||
ret = sysfs_emit(buf, "%#x\n", rt->auth_status);
|
||||
|
||||
mutex_unlock(&rt->tb->lock);
|
||||
|
||||
@ -276,7 +255,7 @@ static ssize_t nvm_version_show(struct device *dev,
|
||||
if (!rt->nvm)
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
ret = sprintf(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
|
||||
ret = sysfs_emit(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
|
||||
|
||||
mutex_unlock(&rt->tb->lock);
|
||||
return ret;
|
||||
@ -288,7 +267,7 @@ static ssize_t vendor_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_retimer *rt = tb_to_retimer(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", rt->vendor);
|
||||
return sysfs_emit(buf, "%#x\n", rt->vendor);
|
||||
}
|
||||
static DEVICE_ATTR_RO(vendor);
|
||||
|
||||
|
@ -26,10 +26,68 @@ enum usb4_sb_opcode {
|
||||
USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */
|
||||
USB4_SB_OPCODE_NVM_AUTH_WRITE = 0x48545541, /* "AUTH" */
|
||||
USB4_SB_OPCODE_NVM_READ = 0x52524641, /* "AFRR" */
|
||||
USB4_SB_OPCODE_READ_LANE_MARGINING_CAP = 0x50434452, /* "RDCP" */
|
||||
USB4_SB_OPCODE_RUN_HW_LANE_MARGINING = 0x474d4852, /* "RHMG" */
|
||||
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING = 0x474d5352, /* "RSMG" */
|
||||
USB4_SB_OPCODE_READ_SW_MARGIN_ERR = 0x57534452, /* "RDSW" */
|
||||
};
|
||||
|
||||
#define USB4_SB_METADATA 0x09
|
||||
#define USB4_SB_METADATA_NVM_AUTH_WRITE_MASK GENMASK(5, 0)
|
||||
#define USB4_SB_DATA 0x12
|
||||
|
||||
/* USB4_SB_OPCODE_READ_LANE_MARGINING_CAP */
|
||||
#define USB4_MARGIN_CAP_0_MODES_HW BIT(0)
|
||||
#define USB4_MARGIN_CAP_0_MODES_SW BIT(1)
|
||||
#define USB4_MARGIN_CAP_0_2_LANES BIT(2)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK GENMASK(4, 3)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_INDP_SHIFT 3
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_MIN 0x0
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_HL 0x1
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_BOTH 0x2
|
||||
#define USB4_MARGIN_CAP_0_TIME BIT(5)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_SHIFT 6
|
||||
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
|
||||
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_SHIFT 13
|
||||
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
|
||||
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
|
||||
#define USB4_MARGIN_CAP_1_TIME_INDP_SHIFT 9
|
||||
#define USB4_MARGIN_CAP_1_TIME_MIN 0x0
|
||||
#define USB4_MARGIN_CAP_1_TIME_LR 0x1
|
||||
#define USB4_MARGIN_CAP_1_TIME_BOTH 0x2
|
||||
#define USB4_MARGIN_CAP_1_TIME_STEPS_MASK GENMASK(15, 11)
|
||||
#define USB4_MARGIN_CAP_1_TIME_STEPS_SHIFT 11
|
||||
#define USB4_MARGIN_CAP_1_TIME_OFFSET_MASK GENMASK(20, 16)
|
||||
#define USB4_MARGIN_CAP_1_TIME_OFFSET_SHIFT 16
|
||||
#define USB4_MARGIN_CAP_1_MIN_BER_MASK GENMASK(25, 21)
|
||||
#define USB4_MARGIN_CAP_1_MIN_BER_SHIFT 21
|
||||
#define USB4_MARGIN_CAP_1_MAX_BER_MASK GENMASK(30, 26)
|
||||
#define USB4_MARGIN_CAP_1_MAX_BER_SHIFT 26
|
||||
#define USB4_MARGIN_CAP_1_MAX_BER_SHIFT 26
|
||||
|
||||
/* USB4_SB_OPCODE_RUN_HW_LANE_MARGINING */
|
||||
#define USB4_MARGIN_HW_TIME BIT(3)
|
||||
#define USB4_MARGIN_HW_RH BIT(4)
|
||||
#define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5)
|
||||
#define USB4_MARGIN_HW_BER_SHIFT 5
|
||||
|
||||
/* Applicable to all margin values */
|
||||
#define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0)
|
||||
#define USB4_MARGIN_HW_RES_1_EXCEEDS BIT(7)
|
||||
/* Different lane margin shifts */
|
||||
#define USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT 8
|
||||
#define USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT 16
|
||||
#define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24
|
||||
|
||||
/* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */
|
||||
#define USB4_MARGIN_SW_TIME BIT(3)
|
||||
#define USB4_MARGIN_SW_RH BIT(4)
|
||||
#define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13)
|
||||
#define USB4_MARGIN_SW_COUNTER_SHIFT 13
|
||||
#define USB4_MARGIN_SW_COUNTER_NOP 0x0
|
||||
#define USB4_MARGIN_SW_COUNTER_CLEAR 0x1
|
||||
#define USB4_MARGIN_SW_COUNTER_START 0x2
|
||||
#define USB4_MARGIN_SW_COUNTER_STOP 0x3
|
||||
|
||||
#endif
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
/* Switch NVM support */
|
||||
|
||||
#define NVM_CSS 0x10
|
||||
|
||||
struct nvm_auth_status {
|
||||
struct list_head list;
|
||||
uuid_t uuid;
|
||||
@ -102,70 +100,30 @@ static void nvm_clear_auth_status(const struct tb_switch *sw)
|
||||
|
||||
static int nvm_validate_and_write(struct tb_switch *sw)
|
||||
{
|
||||
unsigned int image_size, hdr_size;
|
||||
const u8 *buf = sw->nvm->buf;
|
||||
u16 ds_size;
|
||||
unsigned int image_size;
|
||||
const u8 *buf;
|
||||
int ret;
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
ret = tb_nvm_validate(sw->nvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tb_nvm_write_headers(sw->nvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf = sw->nvm->buf_data_start;
|
||||
image_size = sw->nvm->buf_data_size;
|
||||
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* FARB pointer must point inside the image and must at least
|
||||
* contain parts of the digital section we will be reading here.
|
||||
*/
|
||||
hdr_size = (*(u32 *)buf) & 0xffffff;
|
||||
if (hdr_size + NVM_DEVID + 2 >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Digital section start should be aligned to 4k page */
|
||||
if (!IS_ALIGNED(hdr_size, SZ_4K))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Read digital section size and check that it also fits inside
|
||||
* the image.
|
||||
*/
|
||||
ds_size = *(u16 *)(buf + hdr_size);
|
||||
if (ds_size >= image_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (!sw->safe_mode) {
|
||||
u16 device_id;
|
||||
|
||||
/*
|
||||
* Make sure the device ID in the image matches the one
|
||||
* we read from the switch config space.
|
||||
*/
|
||||
device_id = *(u16 *)(buf + hdr_size + NVM_DEVID);
|
||||
if (device_id != sw->config.device_id)
|
||||
return -EINVAL;
|
||||
|
||||
if (sw->generation < 3) {
|
||||
/* Write CSS headers first */
|
||||
ret = dma_port_flash_write(sw->dma_port,
|
||||
DMA_PORT_CSS_ADDRESS, buf + NVM_CSS,
|
||||
DMA_PORT_CSS_MAX_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Skip headers in the image */
|
||||
buf += hdr_size;
|
||||
image_size -= hdr_size;
|
||||
}
|
||||
|
||||
if (tb_switch_is_usb4(sw))
|
||||
ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
|
||||
else
|
||||
ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size);
|
||||
if (!ret)
|
||||
sw->nvm->flushed = true;
|
||||
return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sw->nvm->flushed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvm_authenticate_host_dma_port(struct tb_switch *sw)
|
||||
@ -300,14 +258,6 @@ static inline bool nvm_upgradeable(struct tb_switch *sw)
|
||||
return nvm_readable(sw);
|
||||
}
|
||||
|
||||
static inline int nvm_read(struct tb_switch *sw, unsigned int address,
|
||||
void *buf, size_t size)
|
||||
{
|
||||
if (tb_switch_is_usb4(sw))
|
||||
return usb4_switch_nvm_read(sw, address, buf, size);
|
||||
return dma_port_flash_read(sw->dma_port, address, buf, size);
|
||||
}
|
||||
|
||||
static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
|
||||
{
|
||||
int ret;
|
||||
@ -335,8 +285,26 @@ static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
/**
|
||||
* tb_switch_nvm_read() - Read router NVM
|
||||
* @sw: Router whose NVM to read
|
||||
* @address: Start address on the NVM
|
||||
* @buf: Buffer where the read data is copied
|
||||
* @size: Size of the buffer in bytes
|
||||
*
|
||||
* Reads from router NVM and returns the requested data in @buf. Locking
|
||||
* is up to the caller. Returns %0 in success and negative errno in case
|
||||
* of failure.
|
||||
*/
|
||||
int tb_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
if (tb_switch_is_usb4(sw))
|
||||
return usb4_switch_nvm_read(sw, address, buf, size);
|
||||
return dma_port_flash_read(sw->dma_port, address, buf, size);
|
||||
}
|
||||
|
||||
static int nvm_read(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
struct tb_nvm *nvm = priv;
|
||||
struct tb_switch *sw = tb_to_switch(nvm->dev);
|
||||
@ -349,7 +317,7 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nvm_read(sw, offset, val, bytes);
|
||||
ret = tb_switch_nvm_read(sw, offset, val, bytes);
|
||||
mutex_unlock(&sw->tb->lock);
|
||||
|
||||
out:
|
||||
@ -359,8 +327,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
struct tb_nvm *nvm = priv;
|
||||
struct tb_switch *sw = tb_to_switch(nvm->dev);
|
||||
@ -384,28 +351,20 @@ static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
|
||||
static int tb_switch_nvm_add(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_nvm *nvm;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!nvm_readable(sw))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The NVM format of non-Intel hardware is not known so
|
||||
* currently restrict NVM upgrade for Intel hardware. We may
|
||||
* relax this in the future when we learn other NVM formats.
|
||||
*/
|
||||
if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL &&
|
||||
sw->config.vendor_id != 0x8087) {
|
||||
dev_info(&sw->dev,
|
||||
"NVM format of vendor %#x is not known, disabling NVM upgrade\n",
|
||||
sw->config.vendor_id);
|
||||
return 0;
|
||||
nvm = tb_nvm_alloc(&sw->dev);
|
||||
if (IS_ERR(nvm)) {
|
||||
ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm);
|
||||
goto err_nvm;
|
||||
}
|
||||
|
||||
nvm = tb_nvm_alloc(&sw->dev);
|
||||
if (IS_ERR(nvm))
|
||||
return PTR_ERR(nvm);
|
||||
ret = tb_nvm_read_version(nvm);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
/*
|
||||
* If the switch is in safe-mode the only accessible portion of
|
||||
@ -413,31 +372,13 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
|
||||
* write new functional NVM.
|
||||
*/
|
||||
if (!sw->safe_mode) {
|
||||
u32 nvm_size, hdr_size;
|
||||
|
||||
ret = nvm_read(sw, NVM_FLASH_SIZE, &val, sizeof(val));
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K;
|
||||
nvm_size = (SZ_1M << (val & 7)) / 8;
|
||||
nvm_size = (nvm_size - hdr_size) / 2;
|
||||
|
||||
ret = nvm_read(sw, NVM_VERSION, &val, sizeof(val));
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
nvm->major = val >> 16;
|
||||
nvm->minor = val >> 8;
|
||||
|
||||
ret = tb_nvm_add_active(nvm, nvm_size, tb_switch_nvm_read);
|
||||
ret = tb_nvm_add_active(nvm, nvm_read);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
}
|
||||
|
||||
if (!sw->no_nvm_upgrade) {
|
||||
ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE,
|
||||
tb_switch_nvm_write);
|
||||
ret = tb_nvm_add_non_active(nvm, nvm_write);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
}
|
||||
@ -446,7 +387,11 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
|
||||
return 0;
|
||||
|
||||
err_nvm:
|
||||
tb_nvm_free(nvm);
|
||||
tb_sw_dbg(sw, "NVM upgrade disabled\n");
|
||||
sw->no_nvm_upgrade = true;
|
||||
if (!IS_ERR(nvm))
|
||||
tb_nvm_free(nvm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1229,6 +1174,135 @@ int tb_port_update_credits(struct tb_port *port)
|
||||
return tb_port_do_update_credits(port->dual_link_port);
|
||||
}
|
||||
|
||||
static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
|
||||
{
|
||||
u32 phy;
|
||||
int ret;
|
||||
|
||||
ret = tb_port_read(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (secondary)
|
||||
phy |= LANE_ADP_CS_1_PMS;
|
||||
else
|
||||
phy &= ~LANE_ADP_CS_1_PMS;
|
||||
|
||||
return tb_port_write(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
}
|
||||
|
||||
static int tb_port_pm_secondary_enable(struct tb_port *port)
|
||||
{
|
||||
return __tb_port_pm_secondary_set(port, true);
|
||||
}
|
||||
|
||||
static int tb_port_pm_secondary_disable(struct tb_port *port)
|
||||
{
|
||||
return __tb_port_pm_secondary_set(port, false);
|
||||
}
|
||||
|
||||
/* Called for USB4 or Titan Ridge routers only */
|
||||
static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask)
|
||||
{
|
||||
u32 val, mask = 0;
|
||||
bool ret;
|
||||
|
||||
/* Don't enable CLx in case of two single-lane links */
|
||||
if (!port->bonded && port->dual_link_port)
|
||||
return false;
|
||||
|
||||
/* Don't enable CLx in case of inter-domain link */
|
||||
if (port->xdomain)
|
||||
return false;
|
||||
|
||||
if (tb_switch_is_usb4(port->sw)) {
|
||||
if (!usb4_port_clx_supported(port))
|
||||
return false;
|
||||
} else if (!tb_lc_is_clx_supported(port)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clx_mask & TB_CL1) {
|
||||
/* CL0s and CL1 are enabled and supported together */
|
||||
mask |= LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT;
|
||||
}
|
||||
if (clx_mask & TB_CL2)
|
||||
mask |= LANE_ADP_CS_0_CL2_SUPPORT;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_0, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & mask);
|
||||
}
|
||||
|
||||
static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable)
|
||||
{
|
||||
u32 phy, mask;
|
||||
int ret;
|
||||
|
||||
/* CL0s and CL1 are enabled and supported together */
|
||||
if (clx == TB_CL1)
|
||||
mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
|
||||
else
|
||||
/* For now we support only CL0s and CL1. Not CL2 */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
phy |= mask;
|
||||
else
|
||||
phy &= ~mask;
|
||||
|
||||
return tb_port_write(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
}
|
||||
|
||||
static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx)
|
||||
{
|
||||
return __tb_port_clx_set(port, clx, false);
|
||||
}
|
||||
|
||||
static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx)
|
||||
{
|
||||
return __tb_port_clx_set(port, clx, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_port_is_clx_enabled() - Is given CL state enabled
|
||||
* @port: USB4 port to check
|
||||
* @clx_mask: Mask of CL states to check
|
||||
*
|
||||
* Returns true if any of the given CL states is enabled for @port.
|
||||
*/
|
||||
bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx_mask)
|
||||
{
|
||||
u32 val, mask = 0;
|
||||
int ret;
|
||||
|
||||
if (!tb_port_clx_supported(port, clx_mask))
|
||||
return false;
|
||||
|
||||
if (clx_mask & TB_CL1)
|
||||
mask |= LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
|
||||
if (clx_mask & TB_CL2)
|
||||
mask |= LANE_ADP_CS_1_CL2_ENABLE;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & mask);
|
||||
}
|
||||
|
||||
static int tb_port_start_lane_initialization(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
@ -1620,7 +1694,7 @@ static ssize_t authorized_show(struct device *dev,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sw->authorized);
|
||||
return sysfs_emit(buf, "%u\n", sw->authorized);
|
||||
}
|
||||
|
||||
static int disapprove_switch(struct device *dev, void *not_used)
|
||||
@ -1730,7 +1804,7 @@ static ssize_t boot_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sw->boot);
|
||||
return sysfs_emit(buf, "%u\n", sw->boot);
|
||||
}
|
||||
static DEVICE_ATTR_RO(boot);
|
||||
|
||||
@ -1739,7 +1813,7 @@ static ssize_t device_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", sw->device);
|
||||
return sysfs_emit(buf, "%#x\n", sw->device);
|
||||
}
|
||||
static DEVICE_ATTR_RO(device);
|
||||
|
||||
@ -1748,7 +1822,7 @@ device_name_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", sw->device_name ? sw->device_name : "");
|
||||
return sysfs_emit(buf, "%s\n", sw->device_name ?: "");
|
||||
}
|
||||
static DEVICE_ATTR_RO(device_name);
|
||||
|
||||
@ -1757,7 +1831,7 @@ generation_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sw->generation);
|
||||
return sysfs_emit(buf, "%u\n", sw->generation);
|
||||
}
|
||||
static DEVICE_ATTR_RO(generation);
|
||||
|
||||
@ -1771,9 +1845,9 @@ static ssize_t key_show(struct device *dev, struct device_attribute *attr,
|
||||
return restart_syscall();
|
||||
|
||||
if (sw->key)
|
||||
ret = sprintf(buf, "%*phN\n", TB_SWITCH_KEY_SIZE, sw->key);
|
||||
ret = sysfs_emit(buf, "%*phN\n", TB_SWITCH_KEY_SIZE, sw->key);
|
||||
else
|
||||
ret = sprintf(buf, "\n");
|
||||
ret = sysfs_emit(buf, "\n");
|
||||
|
||||
mutex_unlock(&sw->tb->lock);
|
||||
return ret;
|
||||
@ -1818,7 +1892,7 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%u.0 Gb/s\n", sw->link_speed);
|
||||
return sysfs_emit(buf, "%u.0 Gb/s\n", sw->link_speed);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1833,7 +1907,7 @@ static ssize_t lanes_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sw->link_width);
|
||||
return sysfs_emit(buf, "%u\n", sw->link_width);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1850,7 +1924,7 @@ static ssize_t nvm_authenticate_show(struct device *dev,
|
||||
u32 status;
|
||||
|
||||
nvm_get_auth_status(sw, &status);
|
||||
return sprintf(buf, "%#x\n", status);
|
||||
return sysfs_emit(buf, "%#x\n", status);
|
||||
}
|
||||
|
||||
static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf,
|
||||
@ -1866,6 +1940,11 @@ static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf,
|
||||
goto exit_rpm;
|
||||
}
|
||||
|
||||
if (sw->no_nvm_upgrade) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
/* If NVMem devices are not yet added */
|
||||
if (!sw->nvm) {
|
||||
ret = -EAGAIN;
|
||||
@ -1954,7 +2033,7 @@ static ssize_t nvm_version_show(struct device *dev,
|
||||
else if (!sw->nvm)
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
ret = sprintf(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
|
||||
ret = sysfs_emit(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
|
||||
|
||||
mutex_unlock(&sw->tb->lock);
|
||||
|
||||
@ -1967,7 +2046,7 @@ static ssize_t vendor_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", sw->vendor);
|
||||
return sysfs_emit(buf, "%#x\n", sw->vendor);
|
||||
}
|
||||
static DEVICE_ATTR_RO(vendor);
|
||||
|
||||
@ -1976,7 +2055,7 @@ vendor_name_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", sw->vendor_name ? sw->vendor_name : "");
|
||||
return sysfs_emit(buf, "%s\n", sw->vendor_name ?: "");
|
||||
}
|
||||
static DEVICE_ATTR_RO(vendor_name);
|
||||
|
||||
@ -1985,7 +2064,7 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_switch *sw = tb_to_switch(dev);
|
||||
|
||||
return sprintf(buf, "%pUb\n", sw->uuid);
|
||||
return sysfs_emit(buf, "%pUb\n", sw->uuid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(unique_id);
|
||||
|
||||
@ -2822,6 +2901,26 @@ static void tb_switch_credits_init(struct tb_switch *sw)
|
||||
tb_sw_info(sw, "failed to determine preferred buffer allocation, using defaults\n");
|
||||
}
|
||||
|
||||
static int tb_switch_port_hotplug_enable(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *port;
|
||||
|
||||
if (tb_switch_is_icm(sw))
|
||||
return 0;
|
||||
|
||||
tb_switch_for_each_port(sw, port) {
|
||||
int res;
|
||||
|
||||
if (!port->cap_usb4)
|
||||
continue;
|
||||
|
||||
res = usb4_port_hotplug_enable(port);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_add() - Add a switch to the domain
|
||||
* @sw: Switch to add
|
||||
@ -2891,6 +2990,10 @@ int tb_switch_add(struct tb_switch *sw)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tb_switch_port_hotplug_enable(sw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_add(&sw->dev);
|
||||
if (ret) {
|
||||
dev_err(&sw->dev, "failed to add device: %d\n", ret);
|
||||
@ -3362,35 +3465,6 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
|
||||
{
|
||||
u32 phy;
|
||||
int ret;
|
||||
|
||||
ret = tb_port_read(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (secondary)
|
||||
phy |= LANE_ADP_CS_1_PMS;
|
||||
else
|
||||
phy &= ~LANE_ADP_CS_1_PMS;
|
||||
|
||||
return tb_port_write(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
}
|
||||
|
||||
static int tb_port_pm_secondary_enable(struct tb_port *port)
|
||||
{
|
||||
return __tb_port_pm_secondary_set(port, true);
|
||||
}
|
||||
|
||||
static int tb_port_pm_secondary_disable(struct tb_port *port)
|
||||
{
|
||||
return __tb_port_pm_secondary_set(port, false);
|
||||
}
|
||||
|
||||
static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_switch *parent = tb_switch_parent(sw);
|
||||
@ -3409,83 +3483,6 @@ static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
|
||||
return tb_port_pm_secondary_disable(down);
|
||||
}
|
||||
|
||||
/* Called for USB4 or Titan Ridge routers only */
|
||||
static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx)
|
||||
{
|
||||
u32 mask, val;
|
||||
bool ret;
|
||||
|
||||
/* Don't enable CLx in case of two single-lane links */
|
||||
if (!port->bonded && port->dual_link_port)
|
||||
return false;
|
||||
|
||||
/* Don't enable CLx in case of inter-domain link */
|
||||
if (port->xdomain)
|
||||
return false;
|
||||
|
||||
if (tb_switch_is_usb4(port->sw)) {
|
||||
if (!usb4_port_clx_supported(port))
|
||||
return false;
|
||||
} else if (!tb_lc_is_clx_supported(port)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (clx) {
|
||||
case TB_CL1:
|
||||
/* CL0s and CL1 are enabled and supported together */
|
||||
mask = LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT;
|
||||
break;
|
||||
|
||||
/* For now we support only CL0s and CL1. Not CL2 */
|
||||
case TB_CL2:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_0, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & mask);
|
||||
}
|
||||
|
||||
static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable)
|
||||
{
|
||||
u32 phy, mask;
|
||||
int ret;
|
||||
|
||||
/* CL0s and CL1 are enabled and supported together */
|
||||
if (clx == TB_CL1)
|
||||
mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
|
||||
else
|
||||
/* For now we support only CL0s and CL1. Not CL2 */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
phy |= mask;
|
||||
else
|
||||
phy &= ~mask;
|
||||
|
||||
return tb_port_write(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
}
|
||||
|
||||
static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx)
|
||||
{
|
||||
return __tb_port_clx_set(port, clx, false);
|
||||
}
|
||||
|
||||
static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx)
|
||||
{
|
||||
return __tb_port_clx_set(port, clx, true);
|
||||
}
|
||||
|
||||
static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx)
|
||||
{
|
||||
struct tb_switch *parent = tb_switch_parent(sw);
|
||||
|
@ -105,6 +105,32 @@ static void tb_remove_dp_resources(struct tb_switch *sw)
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_discover_dp_resource(struct tb *tb, struct tb_port *port)
|
||||
{
|
||||
struct tb_cm *tcm = tb_priv(tb);
|
||||
struct tb_port *p;
|
||||
|
||||
list_for_each_entry(p, &tcm->dp_resources, list) {
|
||||
if (p == port)
|
||||
return;
|
||||
}
|
||||
|
||||
tb_port_dbg(port, "DP %s resource available discovered\n",
|
||||
tb_port_is_dpin(port) ? "IN" : "OUT");
|
||||
list_add_tail(&port->list, &tcm->dp_resources);
|
||||
}
|
||||
|
||||
static void tb_discover_dp_resources(struct tb *tb)
|
||||
{
|
||||
struct tb_cm *tcm = tb_priv(tb);
|
||||
struct tb_tunnel *tunnel;
|
||||
|
||||
list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
|
||||
if (tb_tunnel_is_dp(tunnel))
|
||||
tb_discover_dp_resource(tb, tunnel->dst_port);
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_switch_discover_tunnels(struct tb_switch *sw,
|
||||
struct list_head *list,
|
||||
bool alloc_hopids)
|
||||
@ -1416,8 +1442,11 @@ static int tb_start(struct tb *tb)
|
||||
* ICM firmware upgrade needs running firmware and in native
|
||||
* mode that is not available so disable firmware upgrade of the
|
||||
* root switch.
|
||||
*
|
||||
* However, USB4 routers support NVM firmware upgrade if they
|
||||
* implement the necessary router operations.
|
||||
*/
|
||||
tb->root_switch->no_nvm_upgrade = true;
|
||||
tb->root_switch->no_nvm_upgrade = !tb_switch_is_usb4(tb->root_switch);
|
||||
/* All USB4 routers support runtime PM */
|
||||
tb->root_switch->rpm = tb_switch_is_usb4(tb->root_switch);
|
||||
|
||||
@ -1446,6 +1475,8 @@ static int tb_start(struct tb *tb)
|
||||
tb_scan_switch(tb->root_switch);
|
||||
/* Find out tunnels created by the boot firmware */
|
||||
tb_discover_tunnels(tb);
|
||||
/* Add DP resources from the DP tunnels created by the boot firmware */
|
||||
tb_discover_dp_resources(tb);
|
||||
/*
|
||||
* If the boot firmware did not create USB 3.x tunnels create them
|
||||
* now for the whole topology.
|
||||
|
@ -23,11 +23,6 @@
|
||||
#define NVM_MAX_SIZE SZ_512K
|
||||
#define NVM_DATA_DWORDS 16
|
||||
|
||||
/* Intel specific NVM offsets */
|
||||
#define NVM_DEVID 0x05
|
||||
#define NVM_VERSION 0x08
|
||||
#define NVM_FLASH_SIZE 0x45
|
||||
|
||||
/**
|
||||
* struct tb_nvm - Structure holding NVM information
|
||||
* @dev: Owner of the NVM
|
||||
@ -35,28 +30,35 @@
|
||||
* @minor: Minor version number of the active NVM portion
|
||||
* @id: Identifier used with both NVM portions
|
||||
* @active: Active portion NVMem device
|
||||
* @active_size: Size in bytes of the active NVM
|
||||
* @non_active: Non-active portion NVMem device
|
||||
* @buf: Buffer where the NVM image is stored before it is written to
|
||||
* the actual NVM flash device
|
||||
* @buf_data_start: Where the actual image starts after skipping
|
||||
* possible headers
|
||||
* @buf_data_size: Number of bytes actually consumed by the new NVM
|
||||
* image
|
||||
* @authenticating: The device is authenticating the new NVM
|
||||
* @flushed: The image has been flushed to the storage area
|
||||
* @vops: Router vendor specific NVM operations (optional)
|
||||
*
|
||||
* The user of this structure needs to handle serialization of possible
|
||||
* concurrent access.
|
||||
*/
|
||||
struct tb_nvm {
|
||||
struct device *dev;
|
||||
u8 major;
|
||||
u8 minor;
|
||||
u32 major;
|
||||
u32 minor;
|
||||
int id;
|
||||
struct nvmem_device *active;
|
||||
size_t active_size;
|
||||
struct nvmem_device *non_active;
|
||||
void *buf;
|
||||
void *buf_data_start;
|
||||
size_t buf_data_size;
|
||||
bool authenticating;
|
||||
bool flushed;
|
||||
const struct tb_nvm_vendor_ops *vops;
|
||||
};
|
||||
|
||||
enum tb_nvm_write_ops {
|
||||
@ -113,8 +115,8 @@ struct tb_switch_tmu {
|
||||
enum tb_clx {
|
||||
TB_CLX_DISABLE,
|
||||
/* CL0s and CL1 are enabled and supported together */
|
||||
TB_CL1,
|
||||
TB_CL2,
|
||||
TB_CL1 = BIT(0),
|
||||
TB_CL2 = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -279,12 +281,16 @@ struct tb_port {
|
||||
* @can_offline: Does the port have necessary platform support to moved
|
||||
* it into offline mode and back
|
||||
* @offline: The port is currently in offline mode
|
||||
* @margining: Pointer to margining structure if enabled
|
||||
*/
|
||||
struct usb4_port {
|
||||
struct device dev;
|
||||
struct tb_port *port;
|
||||
bool can_offline;
|
||||
bool offline;
|
||||
#ifdef CONFIG_USB4_DEBUGFS_MARGINING
|
||||
struct tb_margining *margining;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@ -296,6 +302,7 @@ struct usb4_port {
|
||||
* @device: Device ID of the retimer
|
||||
* @port: Pointer to the lane 0 adapter
|
||||
* @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise)
|
||||
* @no_nvm_upgrade: Prevent NVM upgrade of this retimer
|
||||
* @auth_status: Status of last NVM authentication
|
||||
*/
|
||||
struct tb_retimer {
|
||||
@ -306,6 +313,7 @@ struct tb_retimer {
|
||||
u32 device;
|
||||
struct tb_port *port;
|
||||
struct tb_nvm *nvm;
|
||||
bool no_nvm_upgrade;
|
||||
u32 auth_status;
|
||||
};
|
||||
|
||||
@ -737,11 +745,13 @@ static inline void tb_domain_put(struct tb *tb)
|
||||
}
|
||||
|
||||
struct tb_nvm *tb_nvm_alloc(struct device *dev);
|
||||
int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read);
|
||||
int tb_nvm_read_version(struct tb_nvm *nvm);
|
||||
int tb_nvm_validate(struct tb_nvm *nvm);
|
||||
int tb_nvm_write_headers(struct tb_nvm *nvm);
|
||||
int tb_nvm_add_active(struct tb_nvm *nvm, nvmem_reg_read_t reg_read);
|
||||
int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
|
||||
size_t bytes);
|
||||
int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
|
||||
nvmem_reg_write_t reg_write);
|
||||
int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write);
|
||||
void tb_nvm_free(struct tb_nvm *nvm);
|
||||
void tb_nvm_exit(void);
|
||||
|
||||
@ -755,6 +765,8 @@ int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
|
||||
unsigned int retries, write_block_fn write_next_block,
|
||||
void *write_block_data);
|
||||
|
||||
int tb_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
|
||||
size_t size);
|
||||
struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
|
||||
u64 route);
|
||||
struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb,
|
||||
@ -1035,6 +1047,7 @@ void tb_port_lane_bonding_disable(struct tb_port *port);
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, int width,
|
||||
int timeout_msec);
|
||||
int tb_port_update_credits(struct tb_port *port);
|
||||
bool tb_port_is_clx_enabled(struct tb_port *port, enum tb_clx clx);
|
||||
|
||||
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
|
||||
int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
|
||||
@ -1132,6 +1145,13 @@ void tb_xdomain_remove(struct tb_xdomain *xd);
|
||||
struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
|
||||
u8 depth);
|
||||
|
||||
static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd)
|
||||
{
|
||||
return tb_to_switch(xd->dev.parent);
|
||||
}
|
||||
|
||||
int tb_retimer_nvm_read(struct tb_retimer *rt, unsigned int address, void *buf,
|
||||
size_t size);
|
||||
int tb_retimer_scan(struct tb_port *port, bool add);
|
||||
void tb_retimer_remove_all(struct tb_port *port);
|
||||
|
||||
@ -1174,6 +1194,7 @@ int usb4_switch_add_ports(struct tb_switch *sw);
|
||||
void usb4_switch_remove_ports(struct tb_switch *sw);
|
||||
|
||||
int usb4_port_unlock(struct tb_port *port);
|
||||
int usb4_port_hotplug_enable(struct tb_port *port);
|
||||
int usb4_port_configure(struct tb_port *port);
|
||||
void usb4_port_unconfigure(struct tb_port *port);
|
||||
int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd);
|
||||
@ -1182,6 +1203,13 @@ int usb4_port_router_offline(struct tb_port *port);
|
||||
int usb4_port_router_online(struct tb_port *port);
|
||||
int usb4_port_enumerate_retimers(struct tb_port *port);
|
||||
bool usb4_port_clx_supported(struct tb_port *port);
|
||||
int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
|
||||
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
|
||||
unsigned int ber_level, bool timing, bool right_high,
|
||||
u32 *results);
|
||||
int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
|
||||
bool right_high, u32 counter);
|
||||
int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors);
|
||||
|
||||
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
|
||||
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
|
||||
@ -1264,6 +1292,8 @@ void tb_debugfs_init(void);
|
||||
void tb_debugfs_exit(void);
|
||||
void tb_switch_debugfs_init(struct tb_switch *sw);
|
||||
void tb_switch_debugfs_remove(struct tb_switch *sw);
|
||||
void tb_xdomain_debugfs_init(struct tb_xdomain *xd);
|
||||
void tb_xdomain_debugfs_remove(struct tb_xdomain *xd);
|
||||
void tb_service_debugfs_init(struct tb_service *svc);
|
||||
void tb_service_debugfs_remove(struct tb_service *svc);
|
||||
#else
|
||||
@ -1271,6 +1301,8 @@ static inline void tb_debugfs_init(void) { }
|
||||
static inline void tb_debugfs_exit(void) { }
|
||||
static inline void tb_switch_debugfs_init(struct tb_switch *sw) { }
|
||||
static inline void tb_switch_debugfs_remove(struct tb_switch *sw) { }
|
||||
static inline void tb_xdomain_debugfs_init(struct tb_xdomain *xd) { }
|
||||
static inline void tb_xdomain_debugfs_remove(struct tb_xdomain *xd) { }
|
||||
static inline void tb_service_debugfs_init(struct tb_service *svc) { }
|
||||
static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
|
||||
#endif
|
||||
|
@ -308,6 +308,7 @@ struct tb_regs_port_header {
|
||||
#define ADP_CS_5 0x05
|
||||
#define ADP_CS_5_LCA_MASK GENMASK(28, 22)
|
||||
#define ADP_CS_5_LCA_SHIFT 22
|
||||
#define ADP_CS_5_DHP BIT(31)
|
||||
|
||||
/* TMU adapter registers */
|
||||
#define TMU_ADP_CS_3 0x03
|
||||
@ -324,6 +325,7 @@ struct tb_regs_port_header {
|
||||
#define LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL 0x2
|
||||
#define LANE_ADP_CS_0_CL0S_SUPPORT BIT(26)
|
||||
#define LANE_ADP_CS_0_CL1_SUPPORT BIT(27)
|
||||
#define LANE_ADP_CS_0_CL2_SUPPORT BIT(28)
|
||||
#define LANE_ADP_CS_1 0x01
|
||||
#define LANE_ADP_CS_1_TARGET_SPEED_MASK GENMASK(3, 0)
|
||||
#define LANE_ADP_CS_1_TARGET_SPEED_GEN3 0xc
|
||||
@ -333,6 +335,7 @@ struct tb_regs_port_header {
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_DUAL 0x3
|
||||
#define LANE_ADP_CS_1_CL0S_ENABLE BIT(10)
|
||||
#define LANE_ADP_CS_1_CL1_ENABLE BIT(11)
|
||||
#define LANE_ADP_CS_1_CL2_ENABLE BIT(12)
|
||||
#define LANE_ADP_CS_1_LD BIT(14)
|
||||
#define LANE_ADP_CS_1_LB BIT(15)
|
||||
#define LANE_ADP_CS_1_CURRENT_SPEED_MASK GENMASK(19, 16)
|
||||
|
@ -1046,6 +1046,26 @@ int usb4_port_unlock(struct tb_port *port)
|
||||
return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_4, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_hotplug_enable() - Enables hotplug for a port
|
||||
* @port: USB4 port to operate on
|
||||
*
|
||||
* Enables hot plug events on a given port. This is only intended
|
||||
* to be used on lane, DP-IN, and DP-OUT adapters.
|
||||
*/
|
||||
int usb4_port_hotplug_enable(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT, ADP_CS_5, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_CS_5_DHP;
|
||||
return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_5, 1);
|
||||
}
|
||||
|
||||
static int usb4_port_set_configured(struct tb_port *port, bool configured)
|
||||
{
|
||||
int ret;
|
||||
@ -1386,6 +1406,126 @@ bool usb4_port_clx_supported(struct tb_port *port)
|
||||
return !!(val & PORT_CS_18_CPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_margining_caps() - Read USB4 port marginig capabilities
|
||||
* @port: USB4 port
|
||||
* @caps: Array with at least two elements to hold the results
|
||||
*
|
||||
* Reads the USB4 port lane margining capabilities into @caps.
|
||||
*/
|
||||
int usb4_port_margining_caps(struct tb_port *port, u32 *caps)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_OPCODE_READ_LANE_MARGINING_CAP, 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_DATA, caps, sizeof(*caps) * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_hw_margin() - Run hardware lane margining on port
|
||||
* @port: USB4 port
|
||||
* @lanes: Which lanes to run (must match the port capabilities). Can be
|
||||
* %0, %1 or %7.
|
||||
* @ber_level: BER level contour value
|
||||
* @timing: Perform timing margining instead of voltage
|
||||
* @right_high: Use Right/high margin instead of left/low
|
||||
* @results: Array with at least two elements to hold the results
|
||||
*
|
||||
* Runs hardware lane margining on USB4 port and returns the result in
|
||||
* @results.
|
||||
*/
|
||||
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
|
||||
unsigned int ber_level, bool timing, bool right_high,
|
||||
u32 *results)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = lanes;
|
||||
if (timing)
|
||||
val |= USB4_MARGIN_HW_TIME;
|
||||
if (right_high)
|
||||
val |= USB4_MARGIN_HW_RH;
|
||||
if (ber_level)
|
||||
val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) &
|
||||
USB4_MARGIN_HW_BER_MASK;
|
||||
|
||||
ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_METADATA, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_OPCODE_RUN_HW_LANE_MARGINING, 2500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_DATA, results, sizeof(*results) * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_sw_margin() - Run software lane margining on port
|
||||
* @port: USB4 port
|
||||
* @lanes: Which lanes to run (must match the port capabilities). Can be
|
||||
* %0, %1 or %7.
|
||||
* @timing: Perform timing margining instead of voltage
|
||||
* @right_high: Use Right/high margin instead of left/low
|
||||
* @counter: What to do with the error counter
|
||||
*
|
||||
* Runs software lane margining on USB4 port. Read back the error
|
||||
* counters by calling usb4_port_sw_margin_errors(). Returns %0 in
|
||||
* success and negative errno otherwise.
|
||||
*/
|
||||
int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
|
||||
bool right_high, u32 counter)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = lanes;
|
||||
if (timing)
|
||||
val |= USB4_MARGIN_SW_TIME;
|
||||
if (right_high)
|
||||
val |= USB4_MARGIN_SW_RH;
|
||||
val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) &
|
||||
USB4_MARGIN_SW_COUNTER_MASK;
|
||||
|
||||
ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_METADATA, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_sw_margin_errors() - Read the software margining error counters
|
||||
* @port: USB4 port
|
||||
* @errors: Error metadata is copied here.
|
||||
*
|
||||
* This reads back the software margining error counters from the port.
|
||||
* Returns %0 in success and negative errno otherwise.
|
||||
*/
|
||||
int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_OPCODE_READ_SW_MARGIN_ERR, 150);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
|
||||
USB4_SB_METADATA, errors, sizeof(*errors));
|
||||
}
|
||||
|
||||
static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
|
||||
enum usb4_sb_opcode opcode,
|
||||
int timeout_msec)
|
||||
|
@ -877,7 +877,7 @@ static ssize_t key_show(struct device *dev, struct device_attribute *attr,
|
||||
* It should be null terminated but anything else is pretty much
|
||||
* allowed.
|
||||
*/
|
||||
return sprintf(buf, "%*pE\n", (int)strlen(svc->key), svc->key);
|
||||
return sysfs_emit(buf, "%*pE\n", (int)strlen(svc->key), svc->key);
|
||||
}
|
||||
static DEVICE_ATTR_RO(key);
|
||||
|
||||
@ -903,7 +903,7 @@ static ssize_t prtcid_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_service *svc = container_of(dev, struct tb_service, dev);
|
||||
|
||||
return sprintf(buf, "%u\n", svc->prtcid);
|
||||
return sysfs_emit(buf, "%u\n", svc->prtcid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(prtcid);
|
||||
|
||||
@ -912,7 +912,7 @@ static ssize_t prtcvers_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_service *svc = container_of(dev, struct tb_service, dev);
|
||||
|
||||
return sprintf(buf, "%u\n", svc->prtcvers);
|
||||
return sysfs_emit(buf, "%u\n", svc->prtcvers);
|
||||
}
|
||||
static DEVICE_ATTR_RO(prtcvers);
|
||||
|
||||
@ -921,7 +921,7 @@ static ssize_t prtcrevs_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_service *svc = container_of(dev, struct tb_service, dev);
|
||||
|
||||
return sprintf(buf, "%u\n", svc->prtcrevs);
|
||||
return sysfs_emit(buf, "%u\n", svc->prtcrevs);
|
||||
}
|
||||
static DEVICE_ATTR_RO(prtcrevs);
|
||||
|
||||
@ -930,7 +930,7 @@ static ssize_t prtcstns_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_service *svc = container_of(dev, struct tb_service, dev);
|
||||
|
||||
return sprintf(buf, "0x%08x\n", svc->prtcstns);
|
||||
return sysfs_emit(buf, "0x%08x\n", svc->prtcstns);
|
||||
}
|
||||
static DEVICE_ATTR_RO(prtcstns);
|
||||
|
||||
@ -1131,11 +1131,6 @@ static int populate_properties(struct tb_xdomain *xd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd)
|
||||
{
|
||||
return tb_to_switch(xd->dev.parent);
|
||||
}
|
||||
|
||||
static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd)
|
||||
{
|
||||
bool change = false;
|
||||
@ -1440,6 +1435,8 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd)
|
||||
if (xd->vendor_name && xd->device_name)
|
||||
dev_info(&xd->dev, "%s %s\n", xd->vendor_name,
|
||||
xd->device_name);
|
||||
|
||||
tb_xdomain_debugfs_init(xd);
|
||||
} else {
|
||||
kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE);
|
||||
}
|
||||
@ -1664,7 +1661,7 @@ static ssize_t device_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", xd->device);
|
||||
return sysfs_emit(buf, "%#x\n", xd->device);
|
||||
}
|
||||
static DEVICE_ATTR_RO(device);
|
||||
|
||||
@ -1676,7 +1673,7 @@ device_name_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
|
||||
if (mutex_lock_interruptible(&xd->lock))
|
||||
return -ERESTARTSYS;
|
||||
ret = sprintf(buf, "%s\n", xd->device_name ? xd->device_name : "");
|
||||
ret = sysfs_emit(buf, "%s\n", xd->device_name ?: "");
|
||||
mutex_unlock(&xd->lock);
|
||||
|
||||
return ret;
|
||||
@ -1688,7 +1685,7 @@ static ssize_t maxhopid_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
|
||||
|
||||
return sprintf(buf, "%d\n", xd->remote_max_hopid);
|
||||
return sysfs_emit(buf, "%d\n", xd->remote_max_hopid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(maxhopid);
|
||||
|
||||
@ -1697,7 +1694,7 @@ static ssize_t vendor_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", xd->vendor);
|
||||
return sysfs_emit(buf, "%#x\n", xd->vendor);
|
||||
}
|
||||
static DEVICE_ATTR_RO(vendor);
|
||||
|
||||
@ -1709,7 +1706,7 @@ vendor_name_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
|
||||
if (mutex_lock_interruptible(&xd->lock))
|
||||
return -ERESTARTSYS;
|
||||
ret = sprintf(buf, "%s\n", xd->vendor_name ? xd->vendor_name : "");
|
||||
ret = sysfs_emit(buf, "%s\n", xd->vendor_name ?: "");
|
||||
mutex_unlock(&xd->lock);
|
||||
|
||||
return ret;
|
||||
@ -1721,7 +1718,7 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
|
||||
|
||||
return sprintf(buf, "%pUb\n", xd->remote_uuid);
|
||||
return sysfs_emit(buf, "%pUb\n", xd->remote_uuid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(unique_id);
|
||||
|
||||
@ -1730,7 +1727,7 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
|
||||
|
||||
return sprintf(buf, "%u.0 Gb/s\n", xd->link_speed);
|
||||
return sysfs_emit(buf, "%u.0 Gb/s\n", xd->link_speed);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL);
|
||||
@ -1741,7 +1738,7 @@ static ssize_t lanes_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
|
||||
|
||||
return sprintf(buf, "%u\n", xd->link_width);
|
||||
return sysfs_emit(buf, "%u\n", xd->link_width);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL);
|
||||
@ -1940,6 +1937,8 @@ static int unregister_service(struct device *dev, void *data)
|
||||
*/
|
||||
void tb_xdomain_remove(struct tb_xdomain *xd)
|
||||
{
|
||||
tb_xdomain_debugfs_remove(xd);
|
||||
|
||||
stop_handshake(xd);
|
||||
|
||||
device_for_each_child_reverse(&xd->dev, xd, unregister_service);
|
||||
|
@ -1026,7 +1026,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
|
||||
/* public fields */
|
||||
|
||||
instance->driver = driver;
|
||||
strlcpy(instance->driver_name, driver->driver_name,
|
||||
strscpy(instance->driver_name, driver->driver_name,
|
||||
sizeof(instance->driver_name));
|
||||
|
||||
instance->usb_dev = usb_dev;
|
||||
|
@ -110,8 +110,6 @@ static int cdns3_plat_probe(struct platform_device *pdev)
|
||||
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
|
||||
if (cdns->wakeup_irq == -EPROBE_DEFER)
|
||||
return cdns->wakeup_irq;
|
||||
else if (cdns->wakeup_irq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (cdns->wakeup_irq < 0) {
|
||||
dev_dbg(dev, "couldn't get wakeup irq\n");
|
||||
|
@ -34,26 +34,26 @@ config USB_CHIPIDEA_HOST
|
||||
ChipIdea driver.
|
||||
|
||||
config USB_CHIPIDEA_PCI
|
||||
tristate "Enable PCI glue driver" if EMBEDDED
|
||||
tristate "Enable PCI glue driver" if EXPERT
|
||||
depends on USB_PCI
|
||||
depends on NOP_USB_XCEIV
|
||||
default USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_MSM
|
||||
tristate "Enable MSM hsusb glue driver" if EMBEDDED
|
||||
tristate "Enable MSM hsusb glue driver" if EXPERT
|
||||
default USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_IMX
|
||||
tristate "Enable i.MX USB glue driver" if EMBEDDED
|
||||
tristate "Enable i.MX USB glue driver" if EXPERT
|
||||
depends on OF
|
||||
default USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_GENERIC
|
||||
tristate "Enable generic USB2 glue driver" if EMBEDDED
|
||||
tristate "Enable generic USB2 glue driver" if EXPERT
|
||||
default USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_TEGRA
|
||||
tristate "Enable Tegra USB glue driver" if EMBEDDED
|
||||
tristate "Enable Tegra USB glue driver" if EXPERT
|
||||
depends on OF
|
||||
default USB_CHIPIDEA
|
||||
|
||||
|
@ -30,6 +30,7 @@ static const struct ci_hdrc_platform_data ci_default_pdata = {
|
||||
|
||||
static const struct ci_hdrc_platform_data ci_zynq_pdata = {
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_PHY_VBUS_CONTROL,
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_platform_data ci_zevio_pdata = {
|
||||
|
@ -63,6 +63,13 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
|
||||
priv->enabled = enable;
|
||||
}
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL) {
|
||||
if (enable)
|
||||
usb_phy_vbus_on(ci->usb_phy);
|
||||
else
|
||||
usb_phy_vbus_off(ci->usb_phy);
|
||||
}
|
||||
|
||||
if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) {
|
||||
/*
|
||||
* Marvell 28nm HSIC PHY requires forcing the port to HS mode.
|
||||
|
@ -471,6 +471,10 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL)
|
||||
usb_phy_vbus_on(ci->usb_phy);
|
||||
|
||||
/* Disable data pulse irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIE, 0);
|
||||
|
||||
@ -480,6 +484,9 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
|
||||
if (ci->platdata->reg_vbus)
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL)
|
||||
usb_phy_vbus_off(ci->usb_phy);
|
||||
|
||||
fsm->a_bus_drop = 1;
|
||||
fsm->a_bus_req = 0;
|
||||
}
|
||||
|
@ -958,7 +958,7 @@ static void wdm_wwan_rx(struct wdm_device *desc, int length)
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
memcpy(skb_put(skb, length), desc->inbuf, length);
|
||||
skb_put_data(skb, desc->inbuf, length);
|
||||
wwan_port_rx(port, skb);
|
||||
|
||||
/* inbuf has been copied, it is safe to check for outstanding data */
|
||||
|
@ -208,30 +208,28 @@ static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
|
||||
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_decode_ctrl - Returns human readable representation of control request.
|
||||
* @str: buffer to return a human-readable representation of control request.
|
||||
* This buffer should have about 200 bytes.
|
||||
* @size: size of str buffer.
|
||||
* @bRequestType: matches the USB bmRequestType field
|
||||
* @bRequest: matches the USB bRequest field
|
||||
* @wValue: matches the USB wValue field (CPU byte order)
|
||||
* @wIndex: matches the USB wIndex field (CPU byte order)
|
||||
* @wLength: matches the USB wLength field (CPU byte order)
|
||||
*
|
||||
* Function returns decoded, formatted and human-readable description of
|
||||
* control request packet.
|
||||
*
|
||||
* The usage scenario for this is for tracepoints, so function as a return
|
||||
* use the same value as in parameters. This approach allows to use this
|
||||
* function in TP_printk
|
||||
*
|
||||
* Important: wValue, wIndex, wLength parameters before invoking this function
|
||||
* should be processed by le16_to_cpu macro.
|
||||
*/
|
||||
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength)
|
||||
static void usb_decode_ctrl_generic(char *str, size_t size, __u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength)
|
||||
{
|
||||
u8 recip = bRequestType & USB_RECIP_MASK;
|
||||
u8 type = bRequestType & USB_TYPE_MASK;
|
||||
|
||||
snprintf(str, size,
|
||||
"Type=%s Recipient=%s Dir=%s bRequest=%u wValue=%u wIndex=%u wLength=%u",
|
||||
(type == USB_TYPE_STANDARD) ? "Standard" :
|
||||
(type == USB_TYPE_VENDOR) ? "Vendor" :
|
||||
(type == USB_TYPE_CLASS) ? "Class" : "Unknown",
|
||||
(recip == USB_RECIP_DEVICE) ? "Device" :
|
||||
(recip == USB_RECIP_INTERFACE) ? "Interface" :
|
||||
(recip == USB_RECIP_ENDPOINT) ? "Endpoint" : "Unknown",
|
||||
(bRequestType & USB_DIR_IN) ? "IN" : "OUT",
|
||||
bRequest, wValue, wIndex, wLength);
|
||||
}
|
||||
|
||||
static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength)
|
||||
{
|
||||
switch (bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
@ -272,14 +270,48 @@ const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
|
||||
usb_decode_set_isoch_delay(wValue, str, size);
|
||||
break;
|
||||
default:
|
||||
snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
bRequestType, bRequest,
|
||||
(u8)(cpu_to_le16(wValue) & 0xff),
|
||||
(u8)(cpu_to_le16(wValue) >> 8),
|
||||
(u8)(cpu_to_le16(wIndex) & 0xff),
|
||||
(u8)(cpu_to_le16(wIndex) >> 8),
|
||||
(u8)(cpu_to_le16(wLength) & 0xff),
|
||||
(u8)(cpu_to_le16(wLength) >> 8));
|
||||
usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
|
||||
wValue, wIndex, wLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_decode_ctrl - Returns human readable representation of control request.
|
||||
* @str: buffer to return a human-readable representation of control request.
|
||||
* This buffer should have about 200 bytes.
|
||||
* @size: size of str buffer.
|
||||
* @bRequestType: matches the USB bmRequestType field
|
||||
* @bRequest: matches the USB bRequest field
|
||||
* @wValue: matches the USB wValue field (CPU byte order)
|
||||
* @wIndex: matches the USB wIndex field (CPU byte order)
|
||||
* @wLength: matches the USB wLength field (CPU byte order)
|
||||
*
|
||||
* Function returns decoded, formatted and human-readable description of
|
||||
* control request packet.
|
||||
*
|
||||
* The usage scenario for this is for tracepoints, so function as a return
|
||||
* use the same value as in parameters. This approach allows to use this
|
||||
* function in TP_printk
|
||||
*
|
||||
* Important: wValue, wIndex, wLength parameters before invoking this function
|
||||
* should be processed by le16_to_cpu macro.
|
||||
*/
|
||||
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength)
|
||||
{
|
||||
switch (bRequestType & USB_TYPE_MASK) {
|
||||
case USB_TYPE_STANDARD:
|
||||
usb_decode_ctrl_standard(str, size, bRequestType, bRequest,
|
||||
wValue, wIndex, wLength);
|
||||
break;
|
||||
case USB_TYPE_VENDOR:
|
||||
case USB_TYPE_CLASS:
|
||||
default:
|
||||
usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
|
||||
wValue, wIndex, wLength);
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
|
@ -233,7 +233,7 @@ err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulpi_regs_read(struct seq_file *seq, void *data)
|
||||
static int ulpi_regs_show(struct seq_file *seq, void *data)
|
||||
{
|
||||
struct ulpi *ulpi = seq->private;
|
||||
|
||||
@ -269,21 +269,7 @@ static int ulpi_regs_read(struct seq_file *seq, void *data)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulpi_regs_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
struct ulpi *ulpi = inode->i_private;
|
||||
|
||||
return single_open(f, ulpi_regs_read, ulpi);
|
||||
}
|
||||
|
||||
static const struct file_operations ulpi_regs_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ulpi_regs_open,
|
||||
.release = single_release,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(ulpi_regs);
|
||||
|
||||
#define ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL)
|
||||
|
||||
@ -316,7 +302,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
||||
}
|
||||
|
||||
root = debugfs_create_dir(dev_name(dev), ULPI_ROOT);
|
||||
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_ops);
|
||||
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
|
||||
|
||||
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
||||
ulpi->id.vendor, ulpi->id.product);
|
||||
|
@ -208,10 +208,8 @@ static int usb_conn_probe(struct platform_device *pdev)
|
||||
if (PTR_ERR(info->vbus) == -ENODEV)
|
||||
info->vbus = NULL;
|
||||
|
||||
if (IS_ERR(info->vbus)) {
|
||||
ret = PTR_ERR(info->vbus);
|
||||
return dev_err_probe(dev, ret, "failed to get vbus :%d\n", ret);
|
||||
}
|
||||
if (IS_ERR(info->vbus))
|
||||
return dev_err_probe(dev, PTR_ERR(info->vbus), "failed to get vbus\n");
|
||||
|
||||
info->role_sw = usb_role_switch_get(dev);
|
||||
if (IS_ERR(info->role_sw))
|
||||
|
@ -1434,7 +1434,7 @@ static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
|
||||
if (!intf || !intf->dev.driver)
|
||||
ret = -ENODATA;
|
||||
else {
|
||||
strlcpy(gd.driver, intf->dev.driver->name,
|
||||
strscpy(gd.driver, intf->dev.driver->name,
|
||||
sizeof(gd.driver));
|
||||
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
|
||||
}
|
||||
|
@ -157,7 +157,6 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
/**
|
||||
* usb_hcd_pci_probe - initialize PCI-based HCDs
|
||||
* @dev: USB Host Controller being probed
|
||||
* @id: pci hotplug id connecting controller to HCD framework
|
||||
* @driver: USB HC driver handle
|
||||
*
|
||||
* Context: task context, might sleep
|
||||
@ -170,8 +169,7 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
*
|
||||
* Return: 0 if successful.
|
||||
*/
|
||||
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
|
||||
const struct hc_driver *driver)
|
||||
int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
@ -180,9 +178,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
|
||||
if (!driver)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1474,7 +1474,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
||||
urb->sg,
|
||||
urb->num_sgs,
|
||||
dir);
|
||||
if (n <= 0)
|
||||
if (!n)
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
urb->transfer_flags |= URB_DMA_MAP_SG;
|
||||
@ -2158,21 +2158,14 @@ static struct urb *request_single_step_set_feature_urb(
|
||||
{
|
||||
struct urb *urb;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return NULL;
|
||||
|
||||
urb->pipe = usb_rcvctrlpipe(udev, 0);
|
||||
ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
|
||||
[usb_pipeendpoint(urb->pipe)];
|
||||
if (!ep) {
|
||||
usb_free_urb(urb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
urb->ep = ep;
|
||||
urb->ep = &udev->ep0;
|
||||
urb->dev = udev;
|
||||
urb->setup_packet = (void *)dr;
|
||||
urb->transfer_buffer = buf;
|
||||
|
@ -388,6 +388,15 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* Kingston DataTraveler 3.0 */
|
||||
{ USB_DEVICE(0x0951, 0x1666), .driver_info = USB_QUIRK_NO_LPM },
|
||||
|
||||
/* NVIDIA Jetson devices in Force Recovery mode */
|
||||
{ USB_DEVICE(0x0955, 0x7018), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x0955, 0x7019), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x0955, 0x7418), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x0955, 0x7721), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x0955, 0x7c18), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x0955, 0x7e19), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x0955, 0x7f21), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* X-Rite/Gretag-Macbeth Eye-One Pro display colorimeter */
|
||||
{ USB_DEVICE(0x0971, 0x2000), .driver_info = USB_QUIRK_NO_SET_INTF },
|
||||
|
||||
@ -437,6 +446,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
|
||||
|
||||
/* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */
|
||||
{ USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x17ef, 0x1019), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
|
||||
{ USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },
|
||||
|
||||
|
@ -3,36 +3,6 @@
|
||||
* core.c - DesignWare HS OTG Controller common routines
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,36 +3,6 @@
|
||||
* core.h - DesignWare HS OTG Controller common declarations
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __DWC2_CORE_H__
|
||||
|
@ -3,36 +3,6 @@
|
||||
* core_intr.c - DesignWare HS OTG Controller common interrupt handling
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,36 +3,6 @@
|
||||
* hcd.c - DesignWare HS OTG Controller host-mode routines
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,37 +3,8 @@
|
||||
* hcd.h - DesignWare HS OTG Controller host-mode declarations
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __DWC2_HCD_H__
|
||||
#define __DWC2_HCD_H__
|
||||
|
||||
|
@ -3,36 +3,6 @@
|
||||
* hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,36 +3,6 @@
|
||||
* hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,36 +3,6 @@
|
||||
* hcd_queue.c - DesignWare HS OTG Controller host queuing routines
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,36 +3,6 @@
|
||||
* hw.h - DesignWare HS OTG Controller hardware definitions
|
||||
*
|
||||
* Copyright 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __DWC2_HW_H__
|
||||
|
@ -1,36 +1,6 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* Copyright (C) 2004-2016 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -3,36 +3,6 @@
|
||||
* pci.c - DesignWare HS OTG Controller PCI driver
|
||||
*
|
||||
* Copyright (C) 2004-2013 Synopsys, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -3,36 +3,6 @@
|
||||
* platform.c - DesignWare HS OTG Controller platform driver
|
||||
*
|
||||
* Copyright (C) Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
@ -86,7 +85,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
|
||||
* mode. If the controller supports DRD but the dr_mode is not
|
||||
* specified or set to OTG, then set the mode to peripheral.
|
||||
*/
|
||||
if (mode == USB_DR_MODE_OTG && !dwc->edev &&
|
||||
if (mode == USB_DR_MODE_OTG &&
|
||||
(!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
|
||||
!device_property_read_bool(dwc->dev, "usb-role-switch")) &&
|
||||
!DWC3_VER_IS_PRIOR(DWC3, 330A))
|
||||
@ -408,6 +407,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
|
||||
reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
|
||||
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
|
||||
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
|
||||
|
||||
if (dwc->gfladj_refclk_lpm_sel)
|
||||
reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
|
||||
}
|
||||
|
||||
@ -789,7 +792,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
else
|
||||
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
|
||||
if (dwc->dis_u2_freeclk_exists_quirk)
|
||||
if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
@ -1180,6 +1183,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* When configured in HOST mode, after issuing U3/L2 exit controller
|
||||
* fails to send proper CRC checksum in CRC5 feild. Because of this
|
||||
* behaviour Transaction Error is generated, resulting in reset and
|
||||
* re-enumeration of usb device attached. All the termsel, xcvrsel,
|
||||
* opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1
|
||||
* will correct this problem. This option is to support certain
|
||||
* legacy ULPI PHYs.
|
||||
*/
|
||||
if (dwc->resume_hs_terminations) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
|
||||
reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
|
||||
|
||||
@ -1523,8 +1541,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
"snps,dis-del-phy-power-chg-quirk");
|
||||
dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
|
||||
"snps,dis-tx-ipgap-linecheck-quirk");
|
||||
dwc->resume_hs_terminations = device_property_read_bool(dev,
|
||||
"snps,resume-hs-terminations");
|
||||
dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
|
||||
"snps,parkmode-disable-ss-quirk");
|
||||
dwc->gfladj_refclk_lpm_sel = device_property_read_bool(dev,
|
||||
"snps,gfladj-refclk-lpm-sel-quirk");
|
||||
|
||||
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
|
||||
"snps,tx_de_emphasis_quirk");
|
||||
@ -1668,46 +1690,6 @@ static void dwc3_check_params(struct dwc3 *dwc)
|
||||
}
|
||||
}
|
||||
|
||||
static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
struct device_node *np_phy;
|
||||
struct extcon_dev *edev = NULL;
|
||||
const char *name;
|
||||
|
||||
if (device_property_read_bool(dev, "extcon"))
|
||||
return extcon_get_edev_by_phandle(dev, 0);
|
||||
|
||||
/*
|
||||
* Device tree platforms should get extcon via phandle.
|
||||
* On ACPI platforms, we get the name from a device property.
|
||||
* This device property is for kernel internal use only and
|
||||
* is expected to be set by the glue code.
|
||||
*/
|
||||
if (device_property_read_string(dev, "linux,extcon-name", &name) == 0)
|
||||
return extcon_get_extcon_dev(name);
|
||||
|
||||
/*
|
||||
* Try to get an extcon device from the USB PHY controller's "port"
|
||||
* node. Check if it has the "port" node first, to avoid printing the
|
||||
* error message from underlying code, as it's a valid case: extcon
|
||||
* device (and "port" node) may be missing in case of "usb-role-switch"
|
||||
* or OTG mode.
|
||||
*/
|
||||
np_phy = of_parse_phandle(dev->of_node, "phys", 0);
|
||||
if (of_graph_is_present(np_phy)) {
|
||||
struct device_node *np_conn;
|
||||
|
||||
np_conn = of_graph_get_remote_node(np_phy, -1, -1);
|
||||
if (np_conn)
|
||||
edev = extcon_find_edev_by_node(np_conn);
|
||||
of_node_put(np_conn);
|
||||
}
|
||||
of_node_put(np_phy);
|
||||
|
||||
return edev;
|
||||
}
|
||||
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -1753,8 +1735,10 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dwc3_get_properties(dwc);
|
||||
|
||||
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
|
||||
if (IS_ERR(dwc->reset))
|
||||
return PTR_ERR(dwc->reset);
|
||||
if (IS_ERR(dwc->reset)) {
|
||||
ret = PTR_ERR(dwc->reset);
|
||||
goto put_usb_psy;
|
||||
}
|
||||
|
||||
if (dev->of_node) {
|
||||
/*
|
||||
@ -1764,45 +1748,57 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
* check for them to retain backwards compatibility.
|
||||
*/
|
||||
dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
|
||||
if (IS_ERR(dwc->bus_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
|
||||
"could not get bus clock\n");
|
||||
if (IS_ERR(dwc->bus_clk)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
|
||||
"could not get bus clock\n");
|
||||
goto put_usb_psy;
|
||||
}
|
||||
|
||||
if (dwc->bus_clk == NULL) {
|
||||
dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
|
||||
if (IS_ERR(dwc->bus_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
|
||||
"could not get bus clock\n");
|
||||
if (IS_ERR(dwc->bus_clk)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
|
||||
"could not get bus clock\n");
|
||||
goto put_usb_psy;
|
||||
}
|
||||
}
|
||||
|
||||
dwc->ref_clk = devm_clk_get_optional(dev, "ref");
|
||||
if (IS_ERR(dwc->ref_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
|
||||
"could not get ref clock\n");
|
||||
if (IS_ERR(dwc->ref_clk)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
|
||||
"could not get ref clock\n");
|
||||
goto put_usb_psy;
|
||||
}
|
||||
|
||||
if (dwc->ref_clk == NULL) {
|
||||
dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
|
||||
if (IS_ERR(dwc->ref_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
|
||||
"could not get ref clock\n");
|
||||
if (IS_ERR(dwc->ref_clk)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
|
||||
"could not get ref clock\n");
|
||||
goto put_usb_psy;
|
||||
}
|
||||
}
|
||||
|
||||
dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
|
||||
if (IS_ERR(dwc->susp_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
|
||||
"could not get suspend clock\n");
|
||||
if (IS_ERR(dwc->susp_clk)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
|
||||
"could not get suspend clock\n");
|
||||
goto put_usb_psy;
|
||||
}
|
||||
|
||||
if (dwc->susp_clk == NULL) {
|
||||
dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
|
||||
if (IS_ERR(dwc->susp_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
|
||||
"could not get suspend clock\n");
|
||||
if (IS_ERR(dwc->susp_clk)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
|
||||
"could not get suspend clock\n");
|
||||
goto put_usb_psy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(dwc->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto put_usb_psy;
|
||||
|
||||
ret = dwc3_clk_enable(dwc);
|
||||
if (ret)
|
||||
@ -1844,13 +1840,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
dwc->edev = dwc3_get_extcon(dwc);
|
||||
if (IS_ERR(dwc->edev)) {
|
||||
ret = PTR_ERR(dwc->edev);
|
||||
dev_err_probe(dwc->dev, ret, "failed to get extcon\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
ret = dwc3_get_dr_mode(dwc);
|
||||
if (ret)
|
||||
goto err3;
|
||||
@ -1909,7 +1898,7 @@ disable_clks:
|
||||
dwc3_clk_disable(dwc);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
|
||||
put_usb_psy:
|
||||
if (dwc->usb_psy)
|
||||
power_supply_put(dwc->usb_psy);
|
||||
|
||||
@ -1977,9 +1966,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
if (pm_runtime_suspended(dwc->dev))
|
||||
break;
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_suspend(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
dwc3_core_exit(dwc);
|
||||
break;
|
||||
@ -2040,9 +2027,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
return ret;
|
||||
|
||||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_resume(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
|
||||
|
@ -263,6 +263,7 @@
|
||||
#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
|
||||
#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10)
|
||||
|
||||
/* Global Status Register */
|
||||
#define DWC3_GSTS_OTG_IP BIT(10)
|
||||
@ -391,6 +392,7 @@
|
||||
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
|
||||
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
|
||||
#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
|
||||
#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23)
|
||||
#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
|
||||
#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
|
||||
|
||||
@ -1096,6 +1098,8 @@ struct dwc3_scratchpad_array {
|
||||
* change quirk.
|
||||
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
|
||||
* check during HS transmit.
|
||||
* @resume-hs-terminations: Set if we enable quirk for fixing improper crc
|
||||
* generation after resume from suspend.
|
||||
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
|
||||
* instances in park mode.
|
||||
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
|
||||
@ -1311,7 +1315,9 @@ struct dwc3 {
|
||||
unsigned dis_u2_freeclk_exists_quirk:1;
|
||||
unsigned dis_del_phy_power_chg_quirk:1;
|
||||
unsigned dis_tx_ipgap_linecheck_quirk:1;
|
||||
unsigned resume_hs_terminations:1;
|
||||
unsigned parkmode_disable_ss_quirk:1;
|
||||
unsigned gfladj_refclk_lpm_sel:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
@ -1560,6 +1566,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
|
||||
u32 param);
|
||||
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
|
||||
void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
|
@ -278,7 +278,7 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
scnprintf(str + len, size - len,
|
||||
"Transfer In Progress [%d] (%c%c%c)",
|
||||
"Transfer In Progress [%08x] (%c%c%c)",
|
||||
event->parameters,
|
||||
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
|
||||
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
|
||||
@ -286,7 +286,7 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
len += scnprintf(str + len, size - len,
|
||||
"Transfer Not Ready [%d]%s",
|
||||
"Transfer Not Ready [%08x]%s",
|
||||
event->parameters,
|
||||
status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
|
||||
" (Active)" : " (Not Active)");
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
@ -438,6 +439,51 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
struct device_node *np_phy;
|
||||
struct extcon_dev *edev = NULL;
|
||||
const char *name;
|
||||
|
||||
if (device_property_read_bool(dev, "extcon"))
|
||||
return extcon_get_edev_by_phandle(dev, 0);
|
||||
|
||||
/*
|
||||
* Device tree platforms should get extcon via phandle.
|
||||
* On ACPI platforms, we get the name from a device property.
|
||||
* This device property is for kernel internal use only and
|
||||
* is expected to be set by the glue code.
|
||||
*/
|
||||
if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) {
|
||||
edev = extcon_get_extcon_dev(name);
|
||||
if (!edev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return edev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to get an extcon device from the USB PHY controller's "port"
|
||||
* node. Check if it has the "port" node first, to avoid printing the
|
||||
* error message from underlying code, as it's a valid case: extcon
|
||||
* device (and "port" node) may be missing in case of "usb-role-switch"
|
||||
* or OTG mode.
|
||||
*/
|
||||
np_phy = of_parse_phandle(dev->of_node, "phys", 0);
|
||||
if (of_graph_is_present(np_phy)) {
|
||||
struct device_node *np_conn;
|
||||
|
||||
np_conn = of_graph_get_remote_node(np_phy, -1, -1);
|
||||
if (np_conn)
|
||||
edev = extcon_find_edev_by_node(np_conn);
|
||||
of_node_put(np_conn);
|
||||
}
|
||||
of_node_put(np_phy);
|
||||
|
||||
return edev;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
|
||||
#define ROLE_SWITCH 1
|
||||
static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
|
||||
@ -542,6 +588,10 @@ int dwc3_drd_init(struct dwc3 *dwc)
|
||||
device_property_read_bool(dwc->dev, "usb-role-switch"))
|
||||
return dwc3_setup_role_switch(dwc);
|
||||
|
||||
dwc->edev = dwc3_get_extcon(dwc);
|
||||
if (IS_ERR(dwc->edev))
|
||||
return PTR_ERR(dwc->edev);
|
||||
|
||||
if (dwc->edev) {
|
||||
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
|
||||
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
|
@ -40,9 +40,10 @@
|
||||
#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
|
||||
#define PCI_DEVICE_ID_INTEL_TGPH 0x43ee
|
||||
#define PCI_DEVICE_ID_INTEL_JSP 0x4dee
|
||||
#define PCI_DEVICE_ID_INTEL_ADL 0x465e
|
||||
#define PCI_DEVICE_ID_INTEL_ADLP 0x51ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADLM 0x54ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADL 0x460e
|
||||
#define PCI_DEVICE_ID_INTEL_ADL_PCH 0x51ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADLN 0x465e
|
||||
#define PCI_DEVICE_ID_INTEL_ADLN_PCH 0x54ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
|
||||
#define PCI_DEVICE_ID_INTEL_RPL 0x460e
|
||||
#define PCI_DEVICE_ID_INTEL_RPLS 0x7a61
|
||||
@ -448,10 +449,13 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLP),
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_PCH),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLM),
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLN),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLN_PCH),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLS),
|
||||
|
@ -243,6 +243,7 @@ static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
|
||||
*/
|
||||
static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
|
||||
{
|
||||
enum usb_device_speed max_speed;
|
||||
struct device *dev = qcom->dev;
|
||||
int ret;
|
||||
|
||||
@ -252,7 +253,7 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
|
||||
qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr");
|
||||
if (IS_ERR(qcom->icc_path_ddr)) {
|
||||
dev_err(dev, "failed to get usb-ddr path: %ld\n",
|
||||
PTR_ERR(qcom->icc_path_ddr));
|
||||
PTR_ERR(qcom->icc_path_ddr));
|
||||
return PTR_ERR(qcom->icc_path_ddr);
|
||||
}
|
||||
|
||||
@ -263,21 +264,20 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
|
||||
return PTR_ERR(qcom->icc_path_apps);
|
||||
}
|
||||
|
||||
if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER ||
|
||||
usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN)
|
||||
max_speed = usb_get_maximum_speed(&qcom->dwc3->dev);
|
||||
if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
|
||||
else
|
||||
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
|
||||
} else {
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
|
||||
|
||||
USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
|
||||
}
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = icc_set_bw(qcom->icc_path_apps,
|
||||
APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
|
||||
ret = icc_set_bw(qcom->icc_path_apps, APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret);
|
||||
return ret;
|
||||
@ -1007,10 +1007,6 @@ static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
|
||||
|
||||
static const struct of_device_id dwc3_qcom_of_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ .compatible = "qcom,msm8996-dwc3" },
|
||||
{ .compatible = "qcom,msm8998-dwc3" },
|
||||
{ .compatible = "qcom,sdm660-dwc3" },
|
||||
{ .compatible = "qcom,sdm845-dwc3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
|
||||
|
@ -47,6 +47,7 @@ struct dwc3_xlnx {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
int (*pltfm_init)(struct dwc3_xlnx *data);
|
||||
struct phy *usb3_phy;
|
||||
};
|
||||
|
||||
static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
|
||||
@ -100,13 +101,12 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
||||
struct device *dev = priv_data->dev;
|
||||
struct reset_control *crst, *hibrst, *apbrst;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct phy *usb3_phy;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
|
||||
if (IS_ERR(usb3_phy)) {
|
||||
ret = PTR_ERR(usb3_phy);
|
||||
priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
|
||||
if (IS_ERR(priv_data->usb3_phy)) {
|
||||
ret = PTR_ERR(priv_data->usb3_phy);
|
||||
dev_err_probe(dev, ret,
|
||||
"failed to get USB3 PHY\n");
|
||||
goto err;
|
||||
@ -121,7 +121,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
||||
* in use but the usb3-phy entry is missing from the device tree.
|
||||
* Therefore, skip these operations in this case.
|
||||
*/
|
||||
if (!usb3_phy)
|
||||
if (!priv_data->usb3_phy)
|
||||
goto skip_usb3_phy;
|
||||
|
||||
crst = devm_reset_control_get_exclusive(dev, "usb_crst");
|
||||
@ -166,9 +166,9 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = phy_init(usb3_phy);
|
||||
ret = phy_init(priv_data->usb3_phy);
|
||||
if (ret < 0) {
|
||||
phy_exit(usb3_phy);
|
||||
phy_exit(priv_data->usb3_phy);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -196,9 +196,9 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = phy_power_on(usb3_phy);
|
||||
ret = phy_power_on(priv_data->usb3_phy);
|
||||
if (ret < 0) {
|
||||
phy_exit(usb3_phy);
|
||||
phy_exit(priv_data->usb3_phy);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -322,7 +322,7 @@ static int dwc3_xlnx_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_xlnx_suspend_common(struct device *dev)
|
||||
static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
|
||||
|
||||
@ -331,7 +331,7 @@ static int __maybe_unused dwc3_xlnx_suspend_common(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_xlnx_resume_common(struct device *dev)
|
||||
static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
|
||||
|
||||
@ -346,8 +346,45 @@ static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(dwc3_xlnx_dev_pm_ops, dwc3_xlnx_suspend_common,
|
||||
dwc3_xlnx_resume_common, dwc3_xlnx_runtime_idle);
|
||||
static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
|
||||
|
||||
phy_exit(priv_data->usb3_phy);
|
||||
|
||||
/* Disable the clocks */
|
||||
clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_xlnx_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_init(priv_data->usb3_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(priv_data->usb3_phy);
|
||||
if (ret < 0) {
|
||||
phy_exit(priv_data->usb3_phy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend,
|
||||
dwc3_xlnx_runtime_resume, dwc3_xlnx_runtime_idle)
|
||||
};
|
||||
|
||||
static struct platform_driver dwc3_xlnx_driver = {
|
||||
.probe = dwc3_xlnx_probe,
|
||||
|
@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->endpoint.desc || !dwc->pullups_connected) {
|
||||
if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
|
||||
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
|
||||
dep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
@ -293,7 +293,10 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
|
||||
continue;
|
||||
|
||||
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
|
||||
dwc3_stop_active_transfer(dwc3_ep, true, true);
|
||||
if (dwc->connected)
|
||||
dwc3_stop_active_transfer(dwc3_ep, true, true);
|
||||
else
|
||||
dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@ -815,7 +818,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
||||
int ret = -EINVAL;
|
||||
u32 len;
|
||||
|
||||
if (!dwc->gadget_driver || !dwc->connected)
|
||||
if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
|
||||
goto out;
|
||||
|
||||
trace_dwc3_ctrl_req(ctrl);
|
||||
@ -1118,6 +1121,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
{
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
if (!dwc->softconnect || !dwc->connected)
|
||||
return;
|
||||
/*
|
||||
* We already have a DATA transfer in the controller's cache,
|
||||
* if we receive a XferNotReady(DATA) we will ignore it, unless
|
||||
|
@ -366,7 +366,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
||||
|
||||
dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
|
||||
|
||||
if (!(cmd & DWC3_DEPCMD_CMDACT)) {
|
||||
if (!(cmd & DWC3_DEPCMD_CMDACT) ||
|
||||
(DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
|
||||
!(cmd & DWC3_DEPCMD_CMDIOC))) {
|
||||
ret = 0;
|
||||
goto skip_status;
|
||||
}
|
||||
@ -965,29 +967,33 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
|
||||
dwc3_stop_active_transfer(dep, true, false);
|
||||
|
||||
/* If endxfer is delayed, avoid unmapping requests */
|
||||
if (dep->flags & DWC3_EP_DELAY_STOP)
|
||||
return;
|
||||
|
||||
/* - giveback all requests to gadget driver */
|
||||
while (!list_empty(&dep->started_list)) {
|
||||
req = next_request(&dep->started_list);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
}
|
||||
|
||||
while (!list_empty(&dep->pending_list)) {
|
||||
req = next_request(&dep->pending_list);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
}
|
||||
|
||||
while (!list_empty(&dep->cancelled_list)) {
|
||||
req = next_request(&dep->cancelled_list);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1005,6 +1011,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
|
||||
trace_dwc3_gadget_ep_disable(dep);
|
||||
|
||||
@ -1022,11 +1029,19 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
||||
dep->endpoint.desc = NULL;
|
||||
}
|
||||
|
||||
dwc3_remove_requests(dwc, dep);
|
||||
dwc3_remove_requests(dwc, dep, -ECONNRESET);
|
||||
|
||||
dep->stream_capable = false;
|
||||
dep->type = 0;
|
||||
dep->flags &= DWC3_EP_TXFIFO_RESIZED;
|
||||
mask = DWC3_EP_TXFIFO_RESIZED;
|
||||
/*
|
||||
* dwc3_remove_requests() can exit early if DWC3 EP delayed stop is
|
||||
* set. Do not clear DEP flags, so that the end transfer command will
|
||||
* be reattempted during the next SETUP stage.
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_DELAY_STOP)
|
||||
mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
|
||||
dep->flags &= mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2340,7 +2355,7 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
dwc3_remove_requests(dwc, dep);
|
||||
dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2440,7 +2455,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
||||
{
|
||||
u32 reg;
|
||||
u32 timeout = 500;
|
||||
u32 timeout = 2000;
|
||||
|
||||
if (pm_runtime_suspended(dwc->dev))
|
||||
return 0;
|
||||
@ -2473,6 +2488,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
|
||||
do {
|
||||
usleep_range(1000, 2000);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg &= DWC3_DSTS_DEVCTRLHLT;
|
||||
} while (--timeout && !(!is_on ^ !reg));
|
||||
@ -2501,6 +2517,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
|
||||
if (dwc->ep0state != EP0_SETUP_PHASE) {
|
||||
int ret;
|
||||
|
||||
if (dwc->delayed_status)
|
||||
dwc3_ep0_send_delayed_status(dwc);
|
||||
|
||||
reinit_completion(&dwc->ep0_in_setup);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
@ -2568,6 +2587,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
||||
return 0;
|
||||
}
|
||||
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
|
||||
if (!is_on) {
|
||||
ret = dwc3_gadget_soft_disconnect(dwc);
|
||||
} else {
|
||||
@ -2718,6 +2739,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
dep->flags = 0;
|
||||
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
@ -2725,6 +2747,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
dep->flags = 0;
|
||||
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
@ -3568,7 +3591,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
|
||||
* streams are updated, and the device controller will not be
|
||||
* triggered to generate ERDY to move the next stream data. To
|
||||
* workaround this and maintain compatibility with various
|
||||
* hosts, force to reinitate the stream until the host is ready
|
||||
* hosts, force to reinitiate the stream until the host is ready
|
||||
* instead of waiting for the host to prime the endpoint.
|
||||
*/
|
||||
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
|
||||
@ -3596,11 +3619,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
dep = dwc->eps[epnum];
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
|
||||
if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
|
||||
return;
|
||||
|
||||
/* Handle only EPCMDCMPLT when EP disabled */
|
||||
if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
|
||||
if ((event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) &&
|
||||
!(epnum <= 1 && event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3695,7 +3719,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
* timeout. Delay issuing the End Transfer command until the Setup TRB is
|
||||
* prepared.
|
||||
*/
|
||||
if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
|
||||
if (dwc->ep0state != EP0_SETUP_PHASE) {
|
||||
dep->flags |= DWC3_EP_DELAY_STOP;
|
||||
return;
|
||||
}
|
||||
@ -3763,13 +3787,24 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
||||
reg &= ~DWC3_DCTL_INITU2ENA;
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
|
||||
dwc->connected = false;
|
||||
|
||||
dwc3_disconnect_gadget(dwc);
|
||||
|
||||
dwc->gadget->speed = USB_SPEED_UNKNOWN;
|
||||
dwc->setup_packet_pending = false;
|
||||
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
|
||||
|
||||
dwc->connected = false;
|
||||
if (dwc->ep0state != EP0_SETUP_PHASE) {
|
||||
unsigned int dir;
|
||||
|
||||
dir = !!dwc->ep0_expect_in;
|
||||
if (dwc->ep0state == EP0_DATA_PHASE)
|
||||
dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
|
||||
else
|
||||
dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||
@ -3867,6 +3902,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
u8 lanes = 1;
|
||||
u8 speed;
|
||||
|
||||
if (!dwc->softconnect)
|
||||
return;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
dwc->speed = speed;
|
||||
@ -4129,7 +4167,7 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
|
||||
unsigned int is_ss = evtinfo & BIT(4);
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revison 2.20a with hibernation support
|
||||
* WORKAROUND: DWC3 revision 2.20a with hibernation support
|
||||
* have a known issue which can cause USB CV TD.9.23 to fail
|
||||
* randomly.
|
||||
*
|
||||
@ -4507,12 +4545,17 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!dwc->gadget_driver)
|
||||
return 0;
|
||||
|
||||
dwc3_gadget_run_stop(dwc, false, false);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_disconnect_gadget(dwc);
|
||||
__dwc3_gadget_stop(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
__entry->enqueue = dep->trb_enqueue;
|
||||
__entry->dequeue = dep->trb_dequeue;
|
||||
),
|
||||
TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
|
||||
TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
|
||||
__get_str(name), __entry->trb, __entry->enqueue,
|
||||
__entry->dequeue, __entry->bph, __entry->bpl,
|
||||
({char *s;
|
||||
@ -267,6 +267,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
s = "";
|
||||
} s; }),
|
||||
DWC3_TRB_SIZE_LENGTH(__entry->size), __entry->ctrl,
|
||||
DWC3_TRB_CTRL_GET_SID_SOFN(__entry->ctrl),
|
||||
__entry->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c',
|
||||
|
@ -2645,10 +2645,10 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
|
||||
unsigned i = 0;
|
||||
vla_group(d);
|
||||
vla_item(d, struct usb_gadget_strings *, stringtabs,
|
||||
lang_count + 1);
|
||||
size_add(lang_count, 1));
|
||||
vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
|
||||
vla_item(d, struct usb_string, strings,
|
||||
lang_count*(needed_count+1));
|
||||
size_mul(lang_count, (needed_count + 1)));
|
||||
|
||||
char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
|
||||
|
||||
@ -3700,7 +3700,7 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name)
|
||||
|
||||
existing = _ffs_do_find_dev(name);
|
||||
if (!existing)
|
||||
strlcpy(dev->name, name, ARRAY_SIZE(dev->name));
|
||||
strscpy(dev->name, name, ARRAY_SIZE(dev->name));
|
||||
else if (existing != dev)
|
||||
ret = -EBUSY;
|
||||
|
||||
|
@ -2662,11 +2662,16 @@ static ssize_t forced_eject_store(struct device *dev,
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(nofua);
|
||||
/* mode wil be set in fsg_lun_attr_is_visible() */
|
||||
static DEVICE_ATTR(ro, 0, ro_show, ro_store);
|
||||
static DEVICE_ATTR(file, 0, file_show, file_store);
|
||||
static DEVICE_ATTR_WO(forced_eject);
|
||||
|
||||
/*
|
||||
* Mode of the ro and file attribute files will be overridden in
|
||||
* fsg_lun_dev_is_visible() depending on if this is a cdrom, or if it is a
|
||||
* removable device.
|
||||
*/
|
||||
static DEVICE_ATTR_RW(ro);
|
||||
static DEVICE_ATTR_RW(file);
|
||||
|
||||
/****************************** FSG COMMON ******************************/
|
||||
|
||||
static void fsg_lun_release(struct device *dev)
|
||||
|
@ -450,39 +450,35 @@ struct ndp_parser_opts {
|
||||
unsigned next_ndp_index;
|
||||
};
|
||||
|
||||
#define INIT_NDP16_OPTS { \
|
||||
.nth_sign = USB_CDC_NCM_NTH16_SIGN, \
|
||||
.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \
|
||||
.nth_size = sizeof(struct usb_cdc_ncm_nth16), \
|
||||
.ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \
|
||||
.dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \
|
||||
.ndplen_align = 4, \
|
||||
.dgram_item_len = 1, \
|
||||
.block_length = 1, \
|
||||
.ndp_index = 1, \
|
||||
.reserved1 = 0, \
|
||||
.reserved2 = 0, \
|
||||
.next_ndp_index = 1, \
|
||||
}
|
||||
static const struct ndp_parser_opts ndp16_opts = {
|
||||
.nth_sign = USB_CDC_NCM_NTH16_SIGN,
|
||||
.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,
|
||||
.nth_size = sizeof(struct usb_cdc_ncm_nth16),
|
||||
.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),
|
||||
.dpe_size = sizeof(struct usb_cdc_ncm_dpe16),
|
||||
.ndplen_align = 4,
|
||||
.dgram_item_len = 1,
|
||||
.block_length = 1,
|
||||
.ndp_index = 1,
|
||||
.reserved1 = 0,
|
||||
.reserved2 = 0,
|
||||
.next_ndp_index = 1,
|
||||
};
|
||||
|
||||
|
||||
#define INIT_NDP32_OPTS { \
|
||||
.nth_sign = USB_CDC_NCM_NTH32_SIGN, \
|
||||
.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \
|
||||
.nth_size = sizeof(struct usb_cdc_ncm_nth32), \
|
||||
.ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \
|
||||
.dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \
|
||||
.ndplen_align = 8, \
|
||||
.dgram_item_len = 2, \
|
||||
.block_length = 2, \
|
||||
.ndp_index = 2, \
|
||||
.reserved1 = 1, \
|
||||
.reserved2 = 2, \
|
||||
.next_ndp_index = 2, \
|
||||
}
|
||||
|
||||
static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
|
||||
static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
|
||||
static const struct ndp_parser_opts ndp32_opts = {
|
||||
.nth_sign = USB_CDC_NCM_NTH32_SIGN,
|
||||
.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,
|
||||
.nth_size = sizeof(struct usb_cdc_ncm_nth32),
|
||||
.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),
|
||||
.dpe_size = sizeof(struct usb_cdc_ncm_dpe32),
|
||||
.ndplen_align = 8,
|
||||
.dgram_item_len = 2,
|
||||
.block_length = 2,
|
||||
.ndp_index = 2,
|
||||
.reserved1 = 1,
|
||||
.reserved2 = 2,
|
||||
.next_ndp_index = 2,
|
||||
};
|
||||
|
||||
static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ struct printer_dev {
|
||||
u8 printer_cdev_open;
|
||||
wait_queue_head_t wait;
|
||||
unsigned q_len;
|
||||
char *pnp_string; /* We don't own memory! */
|
||||
char **pnp_string; /* We don't own memory! */
|
||||
struct usb_function function;
|
||||
};
|
||||
|
||||
@ -1000,16 +1000,16 @@ static int printer_func_setup(struct usb_function *f,
|
||||
if ((wIndex>>8) != dev->interface)
|
||||
break;
|
||||
|
||||
if (!dev->pnp_string) {
|
||||
if (!*dev->pnp_string) {
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
value = strlen(dev->pnp_string);
|
||||
value = strlen(*dev->pnp_string);
|
||||
buf[0] = (value >> 8) & 0xFF;
|
||||
buf[1] = value & 0xFF;
|
||||
memcpy(buf + 2, dev->pnp_string, value);
|
||||
memcpy(buf + 2, *dev->pnp_string, value);
|
||||
DBG(dev, "1284 PNP String: %x %s\n", value,
|
||||
dev->pnp_string);
|
||||
*dev->pnp_string);
|
||||
break;
|
||||
|
||||
case GET_PORT_STATUS: /* Get Port Status */
|
||||
@ -1475,7 +1475,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
|
||||
kref_init(&dev->kref);
|
||||
++opts->refcnt;
|
||||
dev->minor = opts->minor;
|
||||
dev->pnp_string = opts->pnp_string;
|
||||
dev->pnp_string = &opts->pnp_string;
|
||||
dev->q_len = opts->q_len;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
|
@ -2306,7 +2306,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
|
||||
|
||||
DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc);
|
||||
|
||||
static int tcm_init(void)
|
||||
static int __init tcm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -2322,7 +2322,7 @@ static int tcm_init(void)
|
||||
}
|
||||
module_init(tcm_init);
|
||||
|
||||
static void tcm_exit(void)
|
||||
static void __exit tcm_exit(void)
|
||||
{
|
||||
target_unregister_template(&usbg_ops);
|
||||
usb_function_unregister(&tcmusb_func);
|
||||
|
@ -421,7 +421,7 @@ uvc_register_video(struct uvc_device *uvc)
|
||||
int ret;
|
||||
|
||||
/* TODO reference counting. */
|
||||
memset(&uvc->vdev, 0, sizeof(uvc->video));
|
||||
memset(&uvc->vdev, 0, sizeof(uvc->vdev));
|
||||
uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
|
||||
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
|
||||
uvc->vdev.fops = &uvc_v4l2_fops;
|
||||
@ -430,7 +430,7 @@ uvc_register_video(struct uvc_device *uvc)
|
||||
uvc->vdev.vfl_dir = VFL_DIR_TX;
|
||||
uvc->vdev.lock = &uvc->video.mutex;
|
||||
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
|
||||
strlcpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));
|
||||
strscpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));
|
||||
|
||||
video_set_drvdata(&uvc->vdev, uvc);
|
||||
|
||||
@ -888,6 +888,7 @@ 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);
|
||||
--opts->refcnt;
|
||||
kfree(uvc);
|
||||
}
|
||||
@ -897,10 +898,14 @@ static void uvc_function_unbind(struct usb_configuration *c,
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
long wait_ret = 1;
|
||||
|
||||
uvcg_info(f, "%s()\n", __func__);
|
||||
|
||||
if (video->async_wq)
|
||||
destroy_workqueue(video->async_wq);
|
||||
|
||||
/*
|
||||
* If we know we're connected via v4l2, then there should be a cleanup
|
||||
* of the device from userspace either via UVC_EVENT_DISCONNECT or
|
||||
@ -941,6 +946,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
|
||||
struct uvc_device *uvc;
|
||||
struct f_uvc_opts *opts;
|
||||
struct uvc_descriptor_header **strm_cls;
|
||||
struct config_item *streaming, *header, *h;
|
||||
|
||||
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
|
||||
if (uvc == NULL)
|
||||
@ -973,6 +979,28 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
|
||||
uvc->desc.fs_streaming = opts->fs_streaming;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
uvc->header = to_uvcg_streaming_header(h);
|
||||
if (!uvc->header->linked) {
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(uvc);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
@ -988,6 +1016,11 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
|
||||
uvc->func.bind_deactivated = true;
|
||||
|
||||
return &uvc->func;
|
||||
|
||||
err_config:
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(uvc);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
|
||||
|
@ -869,7 +869,7 @@ EXPORT_SYMBOL_GPL(rndis_msg_parser);
|
||||
|
||||
static inline int rndis_get_nr(void)
|
||||
{
|
||||
return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL);
|
||||
return ida_simple_get(&rndis_ida, 0, 1000, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void rndis_put_nr(int nr)
|
||||
@ -1105,7 +1105,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
|
||||
"used : %s\n"
|
||||
"state : %s\n"
|
||||
"medium : 0x%08X\n"
|
||||
"speed : %d\n"
|
||||
"speed : %u\n"
|
||||
"cable : %s\n"
|
||||
"vendor ID : 0x%08X\n"
|
||||
"vendor : %s\n",
|
||||
|
@ -144,10 +144,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
|
||||
strlcpy(p->driver, "g_ether", sizeof(p->driver));
|
||||
strlcpy(p->version, UETH__VERSION, sizeof(p->version));
|
||||
strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
|
||||
strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
|
||||
strscpy(p->driver, "g_ether", sizeof(p->driver));
|
||||
strscpy(p->version, UETH__VERSION, sizeof(p->version));
|
||||
strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
|
||||
strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
|
||||
}
|
||||
|
||||
/* REVISIT can also support:
|
||||
|
@ -1443,7 +1443,7 @@ void gserial_resume(struct gserial *gser)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_resume);
|
||||
|
||||
static int userial_init(void)
|
||||
static int __init userial_init(void)
|
||||
{
|
||||
struct tty_driver *driver;
|
||||
unsigned i;
|
||||
@ -1496,7 +1496,7 @@ fail:
|
||||
}
|
||||
module_init(userial_init);
|
||||
|
||||
static void userial_cleanup(void)
|
||||
static void __exit userial_cleanup(void)
|
||||
{
|
||||
tty_unregister_driver(gs_tty_driver);
|
||||
tty_driver_kref_put(gs_tty_driver);
|
||||
|
@ -88,6 +88,7 @@ struct uvc_video {
|
||||
struct usb_ep *ep;
|
||||
|
||||
struct work_struct pump;
|
||||
struct workqueue_struct *async_wq;
|
||||
|
||||
/* Frame parameters */
|
||||
u8 bpp;
|
||||
@ -133,6 +134,8 @@ struct uvc_device {
|
||||
bool func_connected;
|
||||
wait_queue_head_t func_connected_queue;
|
||||
|
||||
struct uvcg_streaming_header *header;
|
||||
|
||||
/* Descriptors */
|
||||
struct {
|
||||
const struct uvc_descriptor_header * const *fs_control;
|
||||
|
@ -18,12 +18,161 @@
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-uvc.h>
|
||||
|
||||
#include "f_uvc.h"
|
||||
#include "uvc.h"
|
||||
#include "uvc_queue.h"
|
||||
#include "uvc_video.h"
|
||||
#include "uvc_v4l2.h"
|
||||
#include "uvc_configfs.h"
|
||||
|
||||
static struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat)
|
||||
{
|
||||
char guid[16] = UVC_GUID_FORMAT_MJPEG;
|
||||
struct uvc_format_desc *format;
|
||||
struct uvcg_uncompressed *unc;
|
||||
|
||||
if (uformat->type == UVCG_UNCOMPRESSED) {
|
||||
unc = to_uvcg_uncompressed(&uformat->group.cg_item);
|
||||
if (!unc)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
memcpy(guid, unc->desc.guidFormat, sizeof(guid));
|
||||
}
|
||||
|
||||
format = uvc_format_by_guid(guid);
|
||||
if (!format)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
static int uvc_v4l2_get_bytesperline(struct uvcg_format *uformat,
|
||||
struct uvcg_frame *uframe)
|
||||
{
|
||||
struct uvcg_uncompressed *u;
|
||||
|
||||
if (uformat->type == UVCG_UNCOMPRESSED) {
|
||||
u = to_uvcg_uncompressed(&uformat->group.cg_item);
|
||||
if (!u)
|
||||
return 0;
|
||||
|
||||
return u->desc.bBitsPerPixel * uframe->frame.w_width / 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvc_get_frame_size(struct uvcg_format *uformat,
|
||||
struct uvcg_frame *uframe)
|
||||
{
|
||||
unsigned int bpl = uvc_v4l2_get_bytesperline(uformat, uframe);
|
||||
|
||||
return bpl ? bpl * uframe->frame.w_height :
|
||||
uframe->frame.dw_max_video_frame_buffer_size;
|
||||
}
|
||||
|
||||
static struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index)
|
||||
{
|
||||
struct uvcg_format_ptr *format;
|
||||
struct uvcg_format *uformat = NULL;
|
||||
int i = 1;
|
||||
|
||||
list_for_each_entry(format, &uvc->header->formats, entry) {
|
||||
if (index == i) {
|
||||
uformat = format->fmt;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return uformat;
|
||||
}
|
||||
|
||||
static struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc,
|
||||
struct uvcg_format *uformat,
|
||||
int index)
|
||||
{
|
||||
struct uvcg_format_ptr *format;
|
||||
struct uvcg_frame_ptr *frame;
|
||||
struct uvcg_frame *uframe = NULL;
|
||||
|
||||
list_for_each_entry(format, &uvc->header->formats, entry) {
|
||||
if (format->fmt->type != uformat->type)
|
||||
continue;
|
||||
list_for_each_entry(frame, &format->fmt->frames, entry) {
|
||||
if (index == frame->frm->frame.b_frame_index) {
|
||||
uframe = frame->frm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uframe;
|
||||
}
|
||||
|
||||
static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
|
||||
u32 pixelformat)
|
||||
{
|
||||
struct uvcg_format_ptr *format;
|
||||
struct uvcg_format *uformat = NULL;
|
||||
|
||||
list_for_each_entry(format, &uvc->header->formats, entry) {
|
||||
struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
|
||||
|
||||
if (fmtdesc->fcc == pixelformat) {
|
||||
uformat = format->fmt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return uformat;
|
||||
}
|
||||
|
||||
static struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc,
|
||||
struct uvcg_format *uformat,
|
||||
u16 rw, u16 rh)
|
||||
{
|
||||
struct uvc_video *video = &uvc->video;
|
||||
struct uvcg_format_ptr *format;
|
||||
struct uvcg_frame_ptr *frame;
|
||||
struct uvcg_frame *uframe = NULL;
|
||||
unsigned int d, maxd;
|
||||
|
||||
/* Find the closest image size. The distance between image sizes is
|
||||
* the size in pixels of the non-overlapping regions between the
|
||||
* requested size and the frame-specified size.
|
||||
*/
|
||||
maxd = (unsigned int)-1;
|
||||
|
||||
list_for_each_entry(format, &uvc->header->formats, entry) {
|
||||
if (format->fmt->type != uformat->type)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(frame, &format->fmt->frames, entry) {
|
||||
u16 w, h;
|
||||
|
||||
w = frame->frm->frame.w_width;
|
||||
h = frame->frm->frame.w_height;
|
||||
|
||||
d = min(w, rw) * min(h, rh);
|
||||
d = w*h + rw*rh - 2*d;
|
||||
if (d < maxd) {
|
||||
maxd = d;
|
||||
uframe = frame->frm;
|
||||
}
|
||||
|
||||
if (maxd == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uframe)
|
||||
uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh);
|
||||
|
||||
return uframe;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Requests handling
|
||||
@ -67,9 +216,9 @@ uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
|
||||
strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
|
||||
strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
|
||||
strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
|
||||
strscpy(cap->driver, "g_uvc", sizeof(cap->driver));
|
||||
strscpy(cap->card, cdev->gadget->name, sizeof(cap->card));
|
||||
strscpy(cap->bus_info, dev_name(&cdev->gadget->dev),
|
||||
sizeof(cap->bus_info));
|
||||
return 0;
|
||||
}
|
||||
@ -134,6 +283,139 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
struct uvcg_format *uformat;
|
||||
struct uvcg_frame *uframe;
|
||||
u8 *fcc;
|
||||
|
||||
if (fmt->type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
fcc = (u8 *)&fmt->fmt.pix.pixelformat;
|
||||
uvcg_dbg(&uvc->func, "Trying format 0x%08x (%c%c%c%c): %ux%u\n",
|
||||
fmt->fmt.pix.pixelformat,
|
||||
fcc[0], fcc[1], fcc[2], fcc[3],
|
||||
fmt->fmt.pix.width, fmt->fmt.pix.height);
|
||||
|
||||
uformat = find_format_by_pix(uvc, fmt->fmt.pix.pixelformat);
|
||||
if (!uformat)
|
||||
return -EINVAL;
|
||||
|
||||
uframe = find_closest_frame_by_size(uvc, uformat,
|
||||
fmt->fmt.pix.width, fmt->fmt.pix.height);
|
||||
if (!uframe)
|
||||
return -EINVAL;
|
||||
|
||||
fmt->fmt.pix.width = uframe->frame.w_width;
|
||||
fmt->fmt.pix.height = uframe->frame.w_height;
|
||||
fmt->fmt.pix.field = V4L2_FIELD_NONE;
|
||||
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
|
||||
fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
|
||||
fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
|
||||
fmt->fmt.pix.priv = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_enum_frameintervals(struct file *file, void *fh,
|
||||
struct v4l2_frmivalenum *fival)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvcg_format *uformat = NULL;
|
||||
struct uvcg_frame *uframe = NULL;
|
||||
struct uvcg_frame_ptr *frame;
|
||||
|
||||
uformat = find_format_by_pix(uvc, fival->pixel_format);
|
||||
if (!uformat)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(frame, &uformat->frames, entry) {
|
||||
if (frame->frm->frame.w_width == fival->width &&
|
||||
frame->frm->frame.w_height == fival->height) {
|
||||
uframe = frame->frm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!uframe)
|
||||
return -EINVAL;
|
||||
|
||||
if (fival->index >= uframe->frame.b_frame_interval_type)
|
||||
return -EINVAL;
|
||||
|
||||
fival->discrete.numerator =
|
||||
uframe->dw_frame_interval[fival->index];
|
||||
|
||||
/* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */
|
||||
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
|
||||
fival->discrete.denominator = 10000000;
|
||||
v4l2_simplify_fraction(&fival->discrete.numerator,
|
||||
&fival->discrete.denominator, 8, 333);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_enum_framesizes(struct file *file, void *fh,
|
||||
struct v4l2_frmsizeenum *fsize)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvcg_format *uformat = NULL;
|
||||
struct uvcg_frame *uframe = NULL;
|
||||
|
||||
uformat = find_format_by_pix(uvc, fsize->pixel_format);
|
||||
if (!uformat)
|
||||
return -EINVAL;
|
||||
|
||||
if (fsize->index >= uformat->num_frames)
|
||||
return -EINVAL;
|
||||
|
||||
uframe = find_frame_by_index(uvc, uformat, fsize->index + 1);
|
||||
if (!uframe)
|
||||
return -EINVAL;
|
||||
|
||||
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
|
||||
fsize->discrete.width = uframe->frame.w_width;
|
||||
fsize->discrete.height = uframe->frame.w_height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_format_desc *fmtdesc;
|
||||
struct uvcg_format *uformat;
|
||||
|
||||
if (f->index >= uvc->header->num_fmt)
|
||||
return -EINVAL;
|
||||
|
||||
uformat = find_format_by_index(uvc, f->index + 1);
|
||||
if (!uformat)
|
||||
return -EINVAL;
|
||||
|
||||
if (uformat->type != UVCG_UNCOMPRESSED)
|
||||
f->flags |= V4L2_FMT_FLAG_COMPRESSED;
|
||||
|
||||
fmtdesc = to_uvc_format(uformat);
|
||||
f->pixelformat = fmtdesc->fcc;
|
||||
|
||||
strscpy(f->description, fmtdesc->name, sizeof(f->description));
|
||||
f->description[strlen(fmtdesc->name) - 1] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
|
||||
{
|
||||
@ -170,7 +452,7 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
|
||||
return ret;
|
||||
|
||||
if (uvc->state == UVC_STATE_STREAMING)
|
||||
schedule_work(&video->pump);
|
||||
queue_work(video->async_wq, &video->pump);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -298,8 +580,12 @@ uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
|
||||
|
||||
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
|
||||
.vidioc_querycap = uvc_v4l2_querycap,
|
||||
.vidioc_try_fmt_vid_out = uvc_v4l2_try_format,
|
||||
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
|
||||
.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
|
||||
.vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals,
|
||||
.vidioc_enum_framesizes = uvc_v4l2_enum_framesizes,
|
||||
.vidioc_enum_fmt_vid_out = uvc_v4l2_enum_format,
|
||||
.vidioc_reqbufs = uvc_v4l2_reqbufs,
|
||||
.vidioc_querybuf = uvc_v4l2_querybuf,
|
||||
.vidioc_qbuf = uvc_v4l2_qbuf,
|
||||
|
@ -277,7 +277,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
|
||||
if (uvc->state == UVC_STATE_STREAMING)
|
||||
schedule_work(&video->pump);
|
||||
queue_work(video->async_wq, &video->pump);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -485,7 +485,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
||||
|
||||
video->req_int_count = 0;
|
||||
|
||||
schedule_work(&video->pump);
|
||||
queue_work(video->async_wq, &video->pump);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -499,6 +499,11 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
|
||||
spin_lock_init(&video->req_lock);
|
||||
INIT_WORK(&video->pump, uvcg_video_pump);
|
||||
|
||||
/* Allocate a work queue for asynchronous video pump handler. */
|
||||
video->async_wq = alloc_workqueue("uvcgadget", WQ_UNBOUND | WQ_HIGHPRI, 0);
|
||||
if (!video->async_wq)
|
||||
return -EINVAL;
|
||||
|
||||
video->uvc = uvc;
|
||||
video->fcc = V4L2_PIX_FMT_YUYV;
|
||||
video->bpp = 16;
|
||||
|
@ -994,7 +994,7 @@ static const struct usb_gadget_ops at91_udc_ops = {
|
||||
.udc_stop = at91_stop,
|
||||
|
||||
/*
|
||||
* VBUS-powered devices may also also want to support bigger
|
||||
* VBUS-powered devices may also want to support bigger
|
||||
* power budgets after an appropriate SET_CONFIGURATION.
|
||||
*/
|
||||
/* .vbus_power = at91_vbus_power, */
|
||||
@ -1779,12 +1779,14 @@ static void at91udc_of_init(struct at91_udc *udc, struct device_node *np)
|
||||
if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0)
|
||||
board->vbus_polled = 1;
|
||||
|
||||
board->vbus_pin = gpiod_get_from_of_node(np, "atmel,vbus-gpio", 0,
|
||||
GPIOD_IN, "udc_vbus");
|
||||
board->vbus_pin = fwnode_gpiod_get_index(of_fwnode_handle(np),
|
||||
"atmel,vbus", 0, GPIOD_IN,
|
||||
"udc_vbus");
|
||||
if (IS_ERR(board->vbus_pin))
|
||||
board->vbus_pin = NULL;
|
||||
|
||||
board->pullup_pin = gpiod_get_from_of_node(np, "atmel,pullup-gpio", 0,
|
||||
board->pullup_pin = fwnode_gpiod_get_index(of_fwnode_handle(np),
|
||||
"atmel,pullup", 0,
|
||||
GPIOD_ASIS, "udc_pullup");
|
||||
if (IS_ERR(board->pullup_pin))
|
||||
board->pullup_pin = NULL;
|
||||
|
@ -91,7 +91,7 @@ module_param(dma_mode, ushort, 0644);
|
||||
* mode 2 == ep-a 1k, ep-b 1k, ep-c 512db
|
||||
* mode 3 == ep-a 1k, ep-b disabled, ep-c 512db
|
||||
*/
|
||||
static ushort fifo_mode = 0;
|
||||
static ushort fifo_mode;
|
||||
module_param(fifo_mode, ushort, 0644);
|
||||
|
||||
/*
|
||||
@ -100,7 +100,7 @@ module_param(fifo_mode, ushort, 0644);
|
||||
* USB suspend requests will be ignored. This is acceptable for
|
||||
* self-powered devices. For bus powered devices set this to 1.
|
||||
*/
|
||||
static ushort enable_suspend = 0;
|
||||
static ushort enable_suspend;
|
||||
module_param(enable_suspend, ushort, 0644);
|
||||
|
||||
static void assert_out_naking(struct net2272_ep *ep, const char *where)
|
||||
|
@ -2234,7 +2234,7 @@ static int proc_otg_show(struct seq_file *s)
|
||||
char *ctrl_name = "(UNKNOWN)";
|
||||
|
||||
tmp = omap_readl(OTG_REV);
|
||||
ctrl_name = "tranceiver_ctrl";
|
||||
ctrl_name = "transceiver_ctrl";
|
||||
trans = omap_readw(USB_TRANSCEIVER_CTRL);
|
||||
seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
|
||||
tmp >> 4, tmp & 0xf, ctrl_name, trans);
|
||||
@ -2558,7 +2558,7 @@ omap_ep_setup(char *name, u8 addr, u8 type,
|
||||
|
||||
/* set up driver data structures */
|
||||
BUG_ON(strlen(name) >= sizeof ep->name);
|
||||
strlcpy(ep->name, name, sizeof ep->name);
|
||||
strscpy(ep->name, name, sizeof(ep->name));
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
INIT_LIST_HEAD(&ep->iso);
|
||||
ep->bEndpointAddress = addr;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user