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:
Linus Torvalds 2022-10-07 16:48:26 -07:00
commit d3dcbe24a0
191 changed files with 4600 additions and 2276 deletions

View File

@ -153,7 +153,7 @@ Date: Jan 2020
KernelVersion: 5.5 KernelVersion: 5.5
Contact: Mika Westerberg <mika.westerberg@linux.intel.com> Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute reports number of RX lanes the device is 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 What: /sys/bus/thunderbolt/devices/.../tx_speed
Date: Jan 2020 Date: Jan 2020
@ -167,7 +167,7 @@ Date: Jan 2020
KernelVersion: 5.5 KernelVersion: 5.5
Contact: Mika Westerberg <mika.westerberg@linux.intel.com> Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute reports number of TX lanes the device is 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 What: /sys/bus/thunderbolt/devices/.../vendor
Date: Sep 2017 Date: Sep 2017

View File

@ -15,10 +15,10 @@ Required properties:
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series - fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
Optional properties: 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 high-speed trimming resistor connected in parallel with the 45 ohm resistor
that terminates the DN output signal. Default: 45 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 high-speed trimming resistor connected in parallel with the 45 ohm resistor
that terminates the DP output signal. Default: 45 that terminates the DP output signal. Default: 45
- fsl,tx-d-cal: Integer [79-119]. Current trimming value (as a percentage) of - fsl,tx-d-cal: Integer [79-119]. Current trimming value (as a percentage) of

View File

@ -23,6 +23,8 @@ properties:
connector: connector:
type: object type: object
$ref: ../connector/usb-connector.yaml $ref: ../connector/usb-connector.yaml
unevaluatedProperties: false
description: description:
Properties for usb c connector. Properties for usb c connector.

View File

@ -67,6 +67,7 @@ properties:
vhub-strings: vhub-strings:
type: object type: object
additionalProperties: false
properties: properties:
'#address-cells': '#address-cells':
@ -78,6 +79,7 @@ properties:
patternProperties: patternProperties:
'^string@[0-9a-f]+$': '^string@[0-9a-f]+$':
type: object type: object
additionalProperties: false
description: string descriptors of the specific language description: string descriptors of the specific language
properties: properties:

View File

@ -32,6 +32,7 @@ properties:
- enum: - enum:
- rockchip,px30-usb - rockchip,px30-usb
- rockchip,rk3036-usb - rockchip,rk3036-usb
- rockchip,rk3128-usb
- rockchip,rk3188-usb - rockchip,rk3188-usb
- rockchip,rk3228-usb - rockchip,rk3228-usb
- rockchip,rk3288-usb - rockchip,rk3288-usb

View File

@ -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;
};

View 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";
};

View File

@ -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

View File

@ -5,7 +5,9 @@ EHCI:
----- -----
Required properties: Required properties:
- compatible: "nuvoton,npcm750-ehci" - compatible: should be one of
"nuvoton,npcm750-ehci"
"nuvoton,npcm845-ehci"
- interrupts: Should contain the EHCI interrupt - interrupts: Should contain the EHCI interrupt
- reg: Physical address and length of the register set for the device - reg: Physical address and length of the register set for the device

View File

@ -26,6 +26,7 @@ properties:
- qcom,sc7280-dwc3 - qcom,sc7280-dwc3
- qcom,sc8280xp-dwc3 - qcom,sc8280xp-dwc3
- qcom,sdm660-dwc3 - qcom,sdm660-dwc3
- qcom,sdm670-dwc3
- qcom,sdm845-dwc3 - qcom,sdm845-dwc3
- qcom,sdx55-dwc3 - qcom,sdx55-dwc3
- qcom,sdx65-dwc3 - qcom,sdx65-dwc3
@ -175,6 +176,7 @@ allOf:
- qcom,msm8998-dwc3 - qcom,msm8998-dwc3
- qcom,sc7180-dwc3 - qcom,sc7180-dwc3
- qcom,sc7280-dwc3 - qcom,sc7280-dwc3
- qcom,sdm670-dwc3
- qcom,sdm845-dwc3 - qcom,sdm845-dwc3
- qcom,sdx55-dwc3 - qcom,sdx55-dwc3
- qcom,sm6350-dwc3 - qcom,sm6350-dwc3
@ -294,6 +296,7 @@ allOf:
compatible: compatible:
contains: contains:
enum: enum:
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3 - qcom,sm6125-dwc3
- qcom,sm8150-dwc3 - qcom,sm8150-dwc3
- qcom,sm8250-dwc3 - qcom,sm8250-dwc3
@ -344,11 +347,11 @@ allOf:
- qcom,msm8994-dwc3 - qcom,msm8994-dwc3
- qcom,qcs404-dwc3 - qcom,qcs404-dwc3
- qcom,sc7180-dwc3 - qcom,sc7180-dwc3
- qcom,sdm670-dwc3
- qcom,sdm845-dwc3 - qcom,sdm845-dwc3
- qcom,sdx55-dwc3 - qcom,sdx55-dwc3
- qcom,sdx65-dwc3 - qcom,sdx65-dwc3
- qcom,sm4250-dwc3 - qcom,sm4250-dwc3
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3 - qcom,sm6125-dwc3
- qcom,sm6350-dwc3 - qcom,sm6350-dwc3
- qcom,sm8150-dwc3 - qcom,sm8150-dwc3
@ -380,6 +383,7 @@ allOf:
- qcom,msm8953-dwc3 - qcom,msm8953-dwc3
- qcom,msm8996-dwc3 - qcom,msm8996-dwc3
- qcom,msm8998-dwc3 - qcom,msm8998-dwc3
- qcom,sm6115-dwc3
then: then:
properties: properties:
interrupts: interrupts:

View File

@ -11,7 +11,8 @@ maintainers:
properties: properties:
compatible: compatible:
items: oneOf:
- items:
- enum: - enum:
- renesas,r8a774a1-usb3-peri # RZ/G2M - renesas,r8a774a1-usb3-peri # RZ/G2M
- renesas,r8a774b1-usb3-peri # RZ/G2N - renesas,r8a774b1-usb3-peri # RZ/G2N
@ -24,14 +25,41 @@ properties:
- renesas,r8a77990-usb3-peri # R-Car E3 - renesas,r8a77990-usb3-peri # R-Car E3
- const: renesas,rcar-gen3-usb3-peri - const: renesas,rcar-gen3-usb3-peri
- items:
- enum:
- renesas,r9a09g011-usb3-peri # RZ/V2M
- const: renesas,rzv2m-usb3-peri
reg: reg:
maxItems: 1 maxItems: 1
interrupts: 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: clocks:
maxItems: 1 minItems: 1
items:
- description: Main clock
- description: Register access clock
clock-names:
minItems: 1
items:
- const: aclk
- const: reg
phys: phys:
maxItems: 1 maxItems: 1
@ -43,7 +71,15 @@ properties:
maxItems: 1 maxItems: 1
resets: resets:
maxItems: 1 minItems: 1
items:
- description: Peripheral reset
- description: DRD reset
reset-names:
items:
- const: aresetn_p
- const: drd_reset
usb-role-switch: usb-role-switch:
$ref: /schemas/types.yaml#/definitions/flag $ref: /schemas/types.yaml#/definitions/flag
@ -78,6 +114,39 @@ required:
- interrupts - interrupts
- clocks - 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 additionalProperties: false
examples: examples:

View 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>;
};
};
};
};
};
};
...

View File

@ -234,6 +234,18 @@ properties:
avoid -EPROTO errors with usbhid on some devices (Hikey 970). avoid -EPROTO errors with usbhid on some devices (Hikey 970).
type: boolean 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: snps,is-utmi-l1-suspend:
description: description:
True when DWC3 asserts output signal utmi_l1_suspend_n, false when True when DWC3 asserts output signal utmi_l1_suspend_n, false when

View File

@ -33,6 +33,7 @@ properties:
connector: connector:
type: object type: object
$ref: /schemas/connector/usb-connector.yaml# $ref: /schemas/connector/usb-connector.yaml#
unevaluatedProperties: false
properties: properties:
compatible: compatible:
@ -74,7 +75,11 @@ examples:
data-role = "dual"; data-role = "dual";
typec-power-opmode = "default"; typec-power-opmode = "default";
port { ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
typec_con_ep: endpoint { typec_con_ep: endpoint {
remote-endpoint = <&usbotg_hs_ep>; remote-endpoint = <&usbotg_hs_ep>;
}; };
@ -82,4 +87,5 @@ examples:
}; };
}; };
}; };
};
... ...

View File

@ -28,6 +28,7 @@ properties:
connector: connector:
type: object type: object
$ref: ../connector/usb-connector.yaml# $ref: ../connector/usb-connector.yaml#
unevaluatedProperties: false
description: description:
The managed USB Type-C connector. Since WUSB3801 does not support The managed USB Type-C connector. Since WUSB3801 does not support
Power Delivery, the node should have the "pd-disable" property. Power Delivery, the node should have the "pd-disable" property.

View File

@ -340,13 +340,12 @@ USBIP_CMD_SUBMIT:
| 0 | 20 | usbip_header_basic, 'command' shall be 0x00000001 | | 0 | 20 | usbip_header_basic, 'command' shall be 0x00000001 |
+-----------+--------+---------------------------------------------------+ +-----------+--------+---------------------------------------------------+
| 0x14 | 4 | transfer_flags: possible values depend on the | | 0x14 | 4 | transfer_flags: possible values depend on the |
| | | URB transfer_flags (refer to URB doc in | | | | USBIP_URB transfer_flags. |
| | | Documentation/driver-api/usb/URB.rst) | | | | Refer to include/uapi/linux/usbip.h and |
| | | but with URB_NO_TRANSFER_DMA_MAP masked. Refer to | | | | Documentation/driver-api/usb/URB.rst. |
| | | function usbip_pack_cmd_submit and function | | | | Refer to usbip_pack_cmd_submit() and |
| | | tweak_transfer_flags in drivers/usb/usbip/ | | | | tweak_transfer_flags() in drivers/usb/usbip/ |
| | | usbip_common.c. The following fields may also ref | | | | usbip_common.c. |
| | | to function usbip_pack_cmd_submit and URB doc |
+-----------+--------+---------------------------------------------------+ +-----------+--------+---------------------------------------------------+
| 0x18 | 4 | transfer_buffer_length: | | 0x18 | 4 | transfer_buffer_length: |
| | | use URB transfer_buffer_length | | | | use URB transfer_buffer_length |

View File

@ -5921,10 +5921,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
F: drivers/usb/dwc2/ F: drivers/usb/dwc2/
DESIGNWARE USB3 DRD IP DRIVER DESIGNWARE USB3 DRD IP DRIVER
M: Felipe Balbi <balbi@kernel.org> M: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
L: linux-usb@vger.kernel.org L: linux-usb@vger.kernel.org
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
F: drivers/usb/dwc3/ F: drivers/usb/dwc3/
DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER

View File

@ -421,7 +421,14 @@ static struct s3c2410_platform_nand __initdata gta02_nand_info = {
/* Get PMU to set USB current limit accordingly. */ /* Get PMU to set USB current limit accordingly. */
static struct s3c2410_udc_mach_info gta02_udc_cfg __initdata = { static struct s3c2410_udc_mach_info gta02_udc_cfg __initdata = {
.vbus_draw = gta02_udc_vbus_draw, .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 */ /* 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_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
S3C_GPIO_PULL_NONE); S3C_GPIO_PULL_NONE);
gpiod_add_lookup_table(&gta02_udc_gpio_table);
gpiod_add_lookup_table(&gta02_audio_gpio_table); gpiod_add_lookup_table(&gta02_audio_gpio_table);
gpiod_add_lookup_table(&gta02_mmc_gpio_table); gpiod_add_lookup_table(&gta02_mmc_gpio_table);
platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices)); platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices));

View File

@ -167,9 +167,15 @@ static struct gpio_chip h1940_latch_gpiochip = {
}; };
static struct s3c2410_udc_mach_info h1940_udc_cfg __initdata = { 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 = { static struct s3c2410_ts_mach_info h1940_ts_cfg __initdata = {
@ -725,6 +731,7 @@ static void __init h1940_init(void)
u32 tmp; u32 tmp;
s3c24xx_fb_set_platdata(&h1940_fb_info); 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_mmc_gpio_table);
gpiod_add_lookup_table(&h1940_audio_gpio_table); gpiod_add_lookup_table(&h1940_audio_gpio_table);
gpiod_add_lookup_table(&h1940_bat_gpio_table); gpiod_add_lookup_table(&h1940_bat_gpio_table);

View File

@ -493,7 +493,14 @@ static struct platform_device *jive_devices[] __initdata = {
}; };
static struct s3c2410_udc_mach_info jive_udc_cfg __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 */ /* Jive power management device */
@ -669,6 +676,7 @@ static void __init jive_machine_init(void)
pm_power_off = jive_power_off; 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_lcdspi_gpiod_table);
gpiod_add_lookup_table(&jive_wm8750_gpiod_table); gpiod_add_lookup_table(&jive_wm8750_gpiod_table);
platform_add_devices(jive_devices, ARRAY_SIZE(jive_devices)); platform_add_devices(jive_devices, ARRAY_SIZE(jive_devices));

View File

@ -93,9 +93,15 @@ static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata = {
/* USB device UDC support */ /* USB device UDC support */
static struct s3c2410_udc_mach_info mini2440_udc_cfg __initdata = { 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 */ /* LCD timing and setup */
@ -755,6 +761,7 @@ static void __init mini2440_init(void)
s3c24xx_fb_set_platdata(&mini2440_fb_info); s3c24xx_fb_set_platdata(&mini2440_fb_info);
} }
gpiod_add_lookup_table(&mini2440_udc_gpio_table);
s3c24xx_udc_set_platdata(&mini2440_udc_cfg); s3c24xx_udc_set_platdata(&mini2440_udc_cfg);
gpiod_add_lookup_table(&mini2440_mmc_gpio_table); gpiod_add_lookup_table(&mini2440_mmc_gpio_table);
s3c24xx_mci_set_platdata(&mini2440_mmc_cfg); s3c24xx_mci_set_platdata(&mini2440_mmc_cfg);

View File

@ -84,9 +84,15 @@ static struct s3c2410_uartcfg n30_uartcfgs[] = {
}; };
static struct s3c2410_udc_mach_info n30_udc_cfg __initdata = { 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[] = { 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")); WARN_ON(gpio_request(S3C2410_GPG(4), "mmc power"));
s3c24xx_fb_set_platdata(&n30_fb_info); s3c24xx_fb_set_platdata(&n30_fb_info);
gpiod_add_lookup_table(&n30_udc_gpio_table);
s3c24xx_udc_set_platdata(&n30_udc_cfg); s3c24xx_udc_set_platdata(&n30_udc_cfg);
gpiod_add_lookup_table(&n30_mci_gpio_table); gpiod_add_lookup_table(&n30_mci_gpio_table);
s3c24xx_mci_set_platdata(&n30_mci_cfg); s3c24xx_mci_set_platdata(&n30_mci_cfg);

View File

@ -643,9 +643,15 @@ static struct s3c2410_platform_nand rx1950_nand_info = {
}; };
static struct s3c2410_udc_mach_info rx1950_udc_cfg __initdata = { 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 = { 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); gpio_direction_output(S3C2410_GPJ(6), 0);
pwm_add_table(rx1950_pwm_lookup, ARRAY_SIZE(rx1950_pwm_lookup)); 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_audio_gpio_table);
gpiod_add_lookup_table(&rx1950_bat_gpio_table); gpiod_add_lookup_table(&rx1950_bat_gpio_table);
/* Configure the I2S pins (GPE0...GPE4) in correct mode */ /* Configure the I2S pins (GPE0...GPE4) in correct mode */

View File

@ -12,7 +12,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/gpio.h> #include <linux/gpio/machine.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <linux/serial_s3c.h> #include <linux/serial_s3c.h>
#include <linux/platform_device.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 = { 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 = { static struct platform_device *smdk2413_devices[] __initdata = {
&s3c_device_ohci, &s3c_device_ohci,
@ -115,7 +121,7 @@ static void __init smdk2413_machine_init(void)
S3C2410_MISCCR_USBSUSPND0 | S3C2410_MISCCR_USBSUSPND0 |
S3C2410_MISCCR_USBSUSPND1, 0x0); S3C2410_MISCCR_USBSUSPND1, 0x0);
gpiod_add_lookup_table(&smdk2413_udc_gpio_table);
s3c24xx_udc_set_platdata(&smdk2413_udc_cfg); s3c24xx_udc_set_platdata(&smdk2413_udc_cfg);
s3c_i2c0_set_platdata(NULL); s3c_i2c0_set_platdata(NULL);
/* Configure the I2S pins (GPE0...GPE4) in correct mode */ /* Configure the I2S pins (GPE0...GPE4) in correct mode */

View File

@ -1299,7 +1299,7 @@
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy0>, <&usb3_phy0>; phys = <&usb3_phy0>, <&usb3_phy0>;
phy-names = "usb2-phy", "usb3-phy"; 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>; interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy1>, <&usb3_phy1>; phys = <&usb3_phy1>, <&usb3_phy1>;
phy-names = "usb2-phy", "usb3-phy"; phy-names = "usb2-phy", "usb3-phy";
snps,dis-u2-freeclk-exists-quirk; snps,gfladj-refclk-lpm-sel-quirk;
}; };
}; };

View File

@ -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) static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
{ {
u16 frame_size = le16_to_cpu( struct usb_host_interface *alt = fc_usb->uintf->cur_altsetting;
fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); u16 frame_size;
int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * int bufsize, i, j, ret;
frame_size, i, j, ret;
int buffer_offset = 0; 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", deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
B2C2_USB_NUM_ISO_URB, B2C2_USB_NUM_ISO_URB,
B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
@ -501,17 +503,21 @@ urb_error:
static int flexcop_usb_init(struct flexcop_usb *fc_usb) static int flexcop_usb_init(struct flexcop_usb *fc_usb)
{ {
/* use the alternate setting with the larges buffer */ struct usb_host_interface *alt;
int ret = usb_set_interface(fc_usb->udev, 0, 1); int ret;
/* use the alternate setting with the largest buffer */
ret = usb_set_interface(fc_usb->udev, 0, 1);
if (ret) { if (ret) {
err("set interface failed."); err("set interface failed.");
return ret; return ret;
} }
if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1) alt = fc_usb->uintf->cur_altsetting;
if (alt->desc.bNumEndpoints < 1)
return -ENODEV; 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; return -ENODEV;
switch (fc_usb->udev->speed) { switch (fc_usb->udev->speed) {

View File

@ -18,6 +18,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <media/v4l2-ctrls.h> #include <media/v4l2-ctrls.h>
#include <media/v4l2-uvc.h>
#include "uvcvideo.h" #include "uvcvideo.h"

View File

@ -20,6 +20,7 @@
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-uvc.h>
#include "uvcvideo.h" #include "uvcvideo.h"
@ -34,198 +35,6 @@ static unsigned int uvc_quirks_param = -1;
unsigned int uvc_dbg_param; unsigned int uvc_dbg_param;
unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 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 * Utility functions
*/ */
@ -245,19 +54,6 @@ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
return NULL; 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 enum v4l2_colorspace uvc_colorspace(const u8 primaries)
{ {
static const enum v4l2_colorspace colorprimaries[] = { 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 */ 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 * Terminal and unit management
*/ */

View File

@ -386,7 +386,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
mutex_unlock(&stream->mutex); mutex_unlock(&stream->mutex);
denominator = 10000000; denominator = 10000000;
uvc_simplify_fraction(&numerator, &denominator, 8, 333); v4l2_simplify_fraction(&numerator, &denominator, 8, 333);
memset(parm, 0, sizeof(*parm)); memset(parm, 0, sizeof(*parm));
parm->type = stream->type; parm->type = stream->type;
@ -427,7 +427,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
else else
timeperframe = parm->parm.output.timeperframe; timeperframe = parm->parm.output.timeperframe;
interval = uvc_fraction_to_interval(timeperframe.numerator, interval = v4l2_fraction_to_interval(timeperframe.numerator,
timeperframe.denominator); timeperframe.denominator);
uvc_dbg(stream->dev, FORMAT, "Setting frame interval to %u/%u (%u)\n", uvc_dbg(stream->dev, FORMAT, "Setting frame interval to %u/%u (%u)\n",
timeperframe.numerator, timeperframe.denominator, interval); timeperframe.numerator, timeperframe.denominator, interval);
@ -481,7 +481,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
/* Return the actual frame period. */ /* Return the actual frame period. */
timeperframe.numerator = probe.dwFrameInterval; timeperframe.numerator = probe.dwFrameInterval;
timeperframe.denominator = 10000000; timeperframe.denominator = 10000000;
uvc_simplify_fraction(&timeperframe.numerator, v4l2_simplify_fraction(&timeperframe.numerator,
&timeperframe.denominator, 8, 333); &timeperframe.denominator, 8, 333);
if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 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 = fival->discrete.numerator =
frame->dwFrameInterval[index]; frame->dwFrameInterval[index];
fival->discrete.denominator = 10000000; fival->discrete.denominator = 10000000;
uvc_simplify_fraction(&fival->discrete.numerator, v4l2_simplify_fraction(&fival->discrete.numerator,
&fival->discrete.denominator, 8, 333); &fival->discrete.denominator, 8, 333);
} else { } else {
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; 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.max.denominator = 10000000;
fival->stepwise.step.numerator = frame->dwFrameInterval[2]; fival->stepwise.step.numerator = frame->dwFrameInterval[2];
fival->stepwise.step.denominator = 10000000; 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); &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); &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); &fival->stepwise.step.denominator, 8, 333);
} }

View File

@ -41,144 +41,6 @@
#define UVC_EXT_GPIO_UNIT 0x7ffe #define UVC_EXT_GPIO_UNIT 0x7ffe
#define UVC_EXT_GPIO_UNIT_ID 0x100 #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. * Driver specific constants.
*/ */
@ -283,12 +145,6 @@ struct uvc_control {
struct uvc_fh *handle; /* File handle that last changed the 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. * 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); struct uvc_xu_control_query *xqry);
/* Utility functions */ /* 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, struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
u8 epaddr); u8 epaddr);
u16 uvc_endpoint_max_bpi(struct usb_device *dev, struct usb_host_endpoint *ep); u16 uvc_endpoint_max_bpi(struct usb_device *dev, struct usb_host_endpoint *ep);

View File

@ -484,3 +484,89 @@ s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul,
return freq > 0 ? freq : -EINVAL; return freq > 0 ? freq : -EINVAL;
} }
EXPORT_SYMBOL_GPL(v4l2_get_link_freq); 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);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 // 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> #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); 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_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl; 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; return;
} }
dev_dbg(dev, "power on UTMI pad %u\n", index);
tegra186_utmi_bias_pad_power_on(padctl); tegra186_utmi_bias_pad_power_on(padctl);
udelay(2); 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)); 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_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl; 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) if (!phy)
return; return;
dev_dbg(padctl->dev, "power down UTMI pad %u\n", index);
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
value |= USB2_OTG_PD; value |= USB2_OTG_PD;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); 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); value |= RPD_CTRL(priv->calib.rpd_ctrl);
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
/* TODO: pad power saving */ tegra186_utmi_pad_power_on(phy);
tegra_phy_xusb_utmi_pad_power_on(phy);
return 0; return 0;
} }
static int tegra186_utmi_phy_power_off(struct phy *phy) static int tegra186_utmi_phy_power_off(struct phy *phy)
{ {
/* TODO: pad power saving */ tegra186_utmi_pad_power_down(phy);
tegra_phy_xusb_utmi_pad_power_down(phy);
return 0; return 0;
} }
@ -1483,6 +1486,8 @@ static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
.suspend_noirq = tegra186_xusb_padctl_suspend_noirq, .suspend_noirq = tegra186_xusb_padctl_suspend_noirq,
.resume_noirq = tegra186_xusb_padctl_resume_noirq, .resume_noirq = tegra186_xusb_padctl_resume_noirq,
.vbus_override = tegra186_xusb_padctl_vbus_override, .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) #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // 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> #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); 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, int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
unsigned int port) unsigned int port)
{ {

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* 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. * Copyright (c) 2015, Google Inc.
*/ */
@ -412,6 +412,8 @@ struct tegra_xusb_padctl_ops {
unsigned int index, bool enable); unsigned int index, bool enable);
int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set); int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set);
int (*utmi_port_reset)(struct phy *phy); 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 { struct tegra_xusb_padctl_soc {

View File

@ -27,6 +27,16 @@ config USB4_DEBUGFS_WRITE
Only enable this if you know what you are doing! Never enable Only enable this if you know what you are doing! Never enable
this for production systems or distro kernels. 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 config USB4_KUNIT_TEST
bool "KUnit tests" if !KUNIT_ALL_TESTS bool "KUnit tests" if !KUNIT_ALL_TESTS
depends on USB4 && KUNIT=y depends on USB4 && KUNIT=y

View File

@ -12,6 +12,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "tb.h" #include "tb.h"
#include "sb_regs.h"
#define PORT_CAP_PCIE_LEN 1 #define PORT_CAP_PCIE_LEN 1
#define PORT_CAP_POWER_LEN 2 #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 #define DEBUGFS_MODE 0400
#endif #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) static int port_clear_all_counters(struct tb_port *port)
{ {
u32 *buf; u32 *buf;
@ -689,6 +1512,8 @@ void tb_switch_debugfs_init(struct tb_switch *sw)
debugfs_create_file("counters", 0600, debugfs_dir, port, debugfs_create_file("counters", 0600, debugfs_dir, port,
&counters_fops); &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) void tb_switch_debugfs_remove(struct tb_switch *sw)
{ {
margining_switch_remove(sw);
debugfs_remove_recursive(sw->debugfs_dir); 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 * tb_service_debugfs_init() - Add debugfs directory for service
* @svc: Thunderbolt service pointer * @svc: Thunderbolt service pointer

View File

@ -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++) { for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
if (!uuid_is_null(&uuids[i])) if (!uuid_is_null(&uuids[i]))
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%pUb", ret += sysfs_emit_at(buf, ret, "%pUb", &uuids[i]);
&uuids[i]);
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s", ret += sysfs_emit_at(buf, ret, "%s", i < tb->nboot_acl - 1 ? "," : "\n");
i < tb->nboot_acl - 1 ? "," : "\n");
} }
out: out:
@ -247,7 +245,7 @@ static ssize_t deauthorization_show(struct device *dev,
tb->security_level == TB_SECURITY_SECURE) tb->security_level == TB_SECURITY_SECURE)
deauthorization = !!tb->cm_ops->disapprove_switch; deauthorization = !!tb->cm_ops->disapprove_switch;
return sprintf(buf, "%d\n", deauthorization); return sysfs_emit(buf, "%d\n", deauthorization);
} }
static DEVICE_ATTR_RO(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)) if (tb->security_level < ARRAY_SIZE(tb_security_names))
name = tb_security_names[tb->security_level]; 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); static DEVICE_ATTR_RO(security);

View File

@ -2518,6 +2518,9 @@ struct tb *icm_probe(struct tb_nhi *nhi)
case PCI_DEVICE_ID_INTEL_ADL_NHI1: case PCI_DEVICE_ID_INTEL_ADL_NHI1:
case PCI_DEVICE_ID_INTEL_RPL_NHI0: case PCI_DEVICE_ID_INTEL_RPL_NHI0:
case PCI_DEVICE_ID_INTEL_RPL_NHI1: 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->is_supported = icm_tgl_is_supported;
icm->driver_ready = icm_icl_driver_ready; icm->driver_ready = icm_icl_driver_ready;
icm->set_uuid = icm_icl_set_uuid; icm->set_uuid = icm_icl_set_uuid;

View File

@ -1184,6 +1184,7 @@ static void nhi_check_iommu(struct tb_nhi *nhi)
static int nhi_init_msi(struct tb_nhi *nhi) static int nhi_init_msi(struct tb_nhi *nhi)
{ {
struct pci_dev *pdev = nhi->pdev; struct pci_dev *pdev = nhi->pdev;
struct device *dev = &pdev->dev;
int res, irq, nvec; int res, irq, nvec;
/* In case someone left them on. */ /* 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, res = devm_request_irq(&pdev->dev, irq, nhi_msi,
IRQF_NO_SUSPEND, "thunderbolt", nhi); IRQF_NO_SUSPEND, "thunderbolt", nhi);
if (res) { if (res)
dev_err(&pdev->dev, "request_irq failed, aborting\n"); return dev_err_probe(dev, res, "request_irq failed, aborting\n");
return res;
}
} }
return 0; 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) 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_nhi *nhi;
struct tb *tb; struct tb *tb;
int res; int res;
if (!nhi_imr_valid(pdev)) { if (!nhi_imr_valid(pdev))
dev_warn(&pdev->dev, "firmware image not valid, aborting\n"); return dev_err_probe(dev, -ENODEV, "firmware image not valid, aborting\n");
return -ENODEV;
}
res = pcim_enable_device(pdev); res = pcim_enable_device(pdev);
if (res) { if (res)
dev_err(&pdev->dev, "cannot enable PCI device, aborting\n"); return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n");
return res;
}
res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt"); res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt");
if (res) { if (res)
dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n"); return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n");
return res;
}
nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL); nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL);
if (!nhi) 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 */ /* cannot fail - table is allocated in pcim_iomap_regions */
nhi->iobase = pcim_iomap_table(pdev)[0]; nhi->iobase = pcim_iomap_table(pdev)[0];
nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff; 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, nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
sizeof(*nhi->tx_rings), GFP_KERNEL); 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); nhi_check_iommu(nhi);
res = nhi_init_msi(nhi); res = nhi_init_msi(nhi);
if (res) { if (res)
dev_err(&pdev->dev, "cannot enable MSI, aborting\n"); return dev_err_probe(dev, res, "cannot enable MSI, aborting\n");
return res;
}
spin_lock_init(&nhi->lock); spin_lock_init(&nhi->lock);
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (res) { if (res)
dev_err(&pdev->dev, "failed to set DMA mask\n"); return dev_err_probe(dev, res, "failed to set DMA mask\n");
return res;
}
pci_set_master(pdev); 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); tb = nhi_select_cm(nhi);
if (!tb) { if (!tb)
dev_err(&nhi->pdev->dev, return dev_err_probe(dev, -ENODEV,
"failed to determine connection manager, aborting\n"); "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); res = tb_domain_add(tb);
if (res) { if (res) {
@ -1433,6 +1421,7 @@ static struct pci_device_id nhi_ids[] = {
.driver_data = (kernel_ulong_t)&icl_nhi_ops }, .driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1), { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops }, .driver_data = (kernel_ulong_t)&icl_nhi_ops },
/* Thunderbolt 4 */
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0), { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0),
.driver_data = (kernel_ulong_t)&icl_nhi_ops }, .driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1), { 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 }, .driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1), { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops }, .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 */ /* Any USB4 compliant host */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },

View File

@ -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_TITAN_RIDGE_DD_BRIDGE 0x15ef
#define PCI_DEVICE_ID_INTEL_ADL_NHI0 0x463e #define PCI_DEVICE_ID_INTEL_ADL_NHI0 0x463e
#define PCI_DEVICE_ID_INTEL_ADL_NHI1 0x466d #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_NHI1 0x8a0d
#define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17 #define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17
#define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b #define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b

View File

@ -12,19 +12,315 @@
#include "tb.h" #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); 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 * tb_nvm_alloc() - Allocate new NVM structure
* @dev: Device owning the NVM * @dev: Device owning the NVM
* *
* Allocates new NVM structure with unique @id and returns it. In case * 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) struct tb_nvm *tb_nvm_alloc(struct device *dev)
{ {
const struct tb_nvm_vendor_ops *vops = NULL;
struct tb_nvm *nvm; 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); nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
if (!nvm) if (!nvm)
@ -38,14 +334,85 @@ struct tb_nvm *tb_nvm_alloc(struct device *dev)
nvm->id = ret; nvm->id = ret;
nvm->dev = dev; nvm->dev = dev;
nvm->vops = vops;
return nvm; 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 * tb_nvm_add_active() - Adds active NVMem device to NVM
* @nvm: NVM structure * @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 * @reg_read: Pointer to the function to read the NVM (passed directly to the
* NVMem device) * 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. * needed. The first parameter passed to @reg_read is @nvm structure.
* Returns %0 in success and negative errno otherwise. * 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_config config;
struct nvmem_device *nvmem; 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.id = nvm->id;
config.stride = 4; config.stride = 4;
config.word_size = 4; config.word_size = 4;
config.size = size; config.size = nvm->active_size;
config.dev = nvm->dev; config.dev = nvm->dev;
config.owner = THIS_MODULE; config.owner = THIS_MODULE;
config.priv = nvm; 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 * tb_nvm_add_non_active() - Adds non-active NVMem device to NVM
* @nvm: NVM structure * @nvm: NVM structure
* @size: Size of the non-active NVM in bytes
* @reg_write: Pointer to the function to write the NVM (passed directly * @reg_write: Pointer to the function to write the NVM (passed directly
* to the NVMem device) * to the NVMem device)
* *
* Registers new non-active NVmem device for @nvm. The @reg_write is called * Registers new non-active NVmem device for @nvm. The @reg_write is called
* directly from NVMem so it must handle possible concurrent access if * directly from NVMem so it must handle possible concurrent access if
* needed. The first parameter passed to @reg_write is @nvm structure. * 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. * Returns %0 in success and negative errno otherwise.
*/ */
int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size, int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write)
nvmem_reg_write_t reg_write)
{ {
struct nvmem_config config; struct nvmem_config config;
struct nvmem_device *nvmem; 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.id = nvm->id;
config.stride = 4; config.stride = 4;
config.word_size = 4; config.word_size = 4;
config.size = size; config.size = NVM_MAX_SIZE;
config.dev = nvm->dev; config.dev = nvm->dev;
config.owner = THIS_MODULE; config.owner = THIS_MODULE;
config.priv = nvm; config.priv = nvm;

View File

@ -16,8 +16,23 @@
#define TB_MAX_RETIMER_INDEX 6 #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_nvm *nvm = priv;
struct tb_retimer *rt = tb_to_retimer(nvm->dev); 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; 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); mutex_unlock(&rt->tb->lock);
out: out:
@ -40,8 +55,7 @@ out:
return ret; return ret;
} }
static int tb_retimer_nvm_write(void *priv, unsigned int offset, void *val, static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
size_t bytes)
{ {
struct tb_nvm *nvm = priv; struct tb_nvm *nvm = priv;
struct tb_retimer *rt = tb_to_retimer(nvm->dev); 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) static int tb_retimer_nvm_add(struct tb_retimer *rt)
{ {
struct tb_nvm *nvm; struct tb_nvm *nvm;
u32 val, nvm_size;
int ret; int ret;
nvm = tb_nvm_alloc(&rt->dev); nvm = tb_nvm_alloc(&rt->dev);
if (IS_ERR(nvm)) if (IS_ERR(nvm)) {
return PTR_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, ret = tb_nvm_read_version(nvm);
sizeof(val));
if (ret) if (ret)
goto err_nvm; goto err_nvm;
nvm->major = val >> 16; ret = tb_nvm_add_active(nvm, nvm_read);
nvm->minor = val >> 8;
ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_FLASH_SIZE,
&val, sizeof(val));
if (ret) if (ret)
goto err_nvm; goto err_nvm;
nvm_size = (SZ_1M << (val & 7)) / 8; ret = tb_nvm_add_non_active(nvm, nvm_write);
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);
if (ret) if (ret)
goto err_nvm; goto err_nvm;
@ -94,59 +97,33 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)
return 0; return 0;
err_nvm: err_nvm:
dev_dbg(&rt->dev, "NVM upgrade disabled\n");
if (!IS_ERR(nvm))
tb_nvm_free(nvm); tb_nvm_free(nvm);
return ret; return ret;
} }
static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt) static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
{ {
unsigned int image_size, hdr_size; unsigned int image_size;
const u8 *buf = rt->nvm->buf; const u8 *buf;
u16 ds_size, device;
int ret; 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; 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, ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
image_size); image_size);
if (!ret) if (ret)
rt->nvm->flushed = true;
return ret; return ret;
rt->nvm->flushed = true;
return 0;
} }
static int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only) 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); 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); static DEVICE_ATTR_RO(device);
@ -200,8 +177,10 @@ static ssize_t nvm_authenticate_show(struct device *dev,
if (!rt->nvm) if (!rt->nvm)
ret = -EAGAIN; ret = -EAGAIN;
else if (rt->no_nvm_upgrade)
ret = -EOPNOTSUPP;
else else
ret = sprintf(buf, "%#x\n", rt->auth_status); ret = sysfs_emit(buf, "%#x\n", rt->auth_status);
mutex_unlock(&rt->tb->lock); mutex_unlock(&rt->tb->lock);
@ -276,7 +255,7 @@ static ssize_t nvm_version_show(struct device *dev,
if (!rt->nvm) if (!rt->nvm)
ret = -EAGAIN; ret = -EAGAIN;
else 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); mutex_unlock(&rt->tb->lock);
return ret; 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); 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); static DEVICE_ATTR_RO(vendor);

View File

@ -26,10 +26,68 @@ enum usb4_sb_opcode {
USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */ USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */
USB4_SB_OPCODE_NVM_AUTH_WRITE = 0x48545541, /* "AUTH" */ USB4_SB_OPCODE_NVM_AUTH_WRITE = 0x48545541, /* "AUTH" */
USB4_SB_OPCODE_NVM_READ = 0x52524641, /* "AFRR" */ 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 0x09
#define USB4_SB_METADATA_NVM_AUTH_WRITE_MASK GENMASK(5, 0) #define USB4_SB_METADATA_NVM_AUTH_WRITE_MASK GENMASK(5, 0)
#define USB4_SB_DATA 0x12 #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 #endif

View File

@ -19,8 +19,6 @@
/* Switch NVM support */ /* Switch NVM support */
#define NVM_CSS 0x10
struct nvm_auth_status { struct nvm_auth_status {
struct list_head list; struct list_head list;
uuid_t uuid; 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) static int nvm_validate_and_write(struct tb_switch *sw)
{ {
unsigned int image_size, hdr_size; unsigned int image_size;
const u8 *buf = sw->nvm->buf; const u8 *buf;
u16 ds_size;
int ret; int ret;
if (!buf) ret = tb_nvm_validate(sw->nvm);
return -EINVAL;
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) if (ret)
return ret; return ret;
}
/* Skip headers in the image */ ret = tb_nvm_write_headers(sw->nvm);
buf += hdr_size; if (ret)
image_size -= hdr_size; return ret;
}
buf = sw->nvm->buf_data_start;
image_size = sw->nvm->buf_data_size;
if (tb_switch_is_usb4(sw)) if (tb_switch_is_usb4(sw))
ret = usb4_switch_nvm_write(sw, 0, buf, image_size); ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
else else
ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size); ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size);
if (!ret) if (ret)
sw->nvm->flushed = true;
return ret; return ret;
sw->nvm->flushed = true;
return 0;
} }
static int nvm_authenticate_host_dma_port(struct tb_switch *sw) 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); 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) static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
{ {
int ret; int ret;
@ -335,8 +285,26 @@ static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
return ret; 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_nvm *nvm = priv;
struct tb_switch *sw = tb_to_switch(nvm->dev); 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; goto out;
} }
ret = nvm_read(sw, offset, val, bytes); ret = tb_switch_nvm_read(sw, offset, val, bytes);
mutex_unlock(&sw->tb->lock); mutex_unlock(&sw->tb->lock);
out: out:
@ -359,8 +327,7 @@ out:
return ret; return ret;
} }
static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
size_t bytes)
{ {
struct tb_nvm *nvm = priv; struct tb_nvm *nvm = priv;
struct tb_switch *sw = tb_to_switch(nvm->dev); 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) static int tb_switch_nvm_add(struct tb_switch *sw)
{ {
struct tb_nvm *nvm; struct tb_nvm *nvm;
u32 val;
int ret; int ret;
if (!nvm_readable(sw)) if (!nvm_readable(sw))
return 0; return 0;
/* nvm = tb_nvm_alloc(&sw->dev);
* The NVM format of non-Intel hardware is not known so if (IS_ERR(nvm)) {
* currently restrict NVM upgrade for Intel hardware. We may ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm);
* relax this in the future when we learn other NVM formats. goto err_nvm;
*/
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); ret = tb_nvm_read_version(nvm);
if (IS_ERR(nvm)) if (ret)
return PTR_ERR(nvm); goto err_nvm;
/* /*
* If the switch is in safe-mode the only accessible portion of * 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. * write new functional NVM.
*/ */
if (!sw->safe_mode) { if (!sw->safe_mode) {
u32 nvm_size, hdr_size; ret = tb_nvm_add_active(nvm, nvm_read);
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);
if (ret) if (ret)
goto err_nvm; goto err_nvm;
} }
if (!sw->no_nvm_upgrade) { if (!sw->no_nvm_upgrade) {
ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, ret = tb_nvm_add_non_active(nvm, nvm_write);
tb_switch_nvm_write);
if (ret) if (ret)
goto err_nvm; goto err_nvm;
} }
@ -446,7 +387,11 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
return 0; return 0;
err_nvm: err_nvm:
tb_sw_dbg(sw, "NVM upgrade disabled\n");
sw->no_nvm_upgrade = true;
if (!IS_ERR(nvm))
tb_nvm_free(nvm); tb_nvm_free(nvm);
return ret; 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); 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) static int tb_port_start_lane_initialization(struct tb_port *port)
{ {
int ret; int ret;
@ -1620,7 +1694,7 @@ static ssize_t authorized_show(struct device *dev,
{ {
struct tb_switch *sw = tb_to_switch(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) 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); 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); 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); 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); 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); 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); 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); 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); static DEVICE_ATTR_RO(generation);
@ -1771,9 +1845,9 @@ static ssize_t key_show(struct device *dev, struct device_attribute *attr,
return restart_syscall(); return restart_syscall();
if (sw->key) 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 else
ret = sprintf(buf, "\n"); ret = sysfs_emit(buf, "\n");
mutex_unlock(&sw->tb->lock); mutex_unlock(&sw->tb->lock);
return ret; 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); 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); 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; u32 status;
nvm_get_auth_status(sw, &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, 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; goto exit_rpm;
} }
if (sw->no_nvm_upgrade) {
ret = -EOPNOTSUPP;
goto exit_unlock;
}
/* If NVMem devices are not yet added */ /* If NVMem devices are not yet added */
if (!sw->nvm) { if (!sw->nvm) {
ret = -EAGAIN; ret = -EAGAIN;
@ -1954,7 +2033,7 @@ static ssize_t nvm_version_show(struct device *dev,
else if (!sw->nvm) else if (!sw->nvm)
ret = -EAGAIN; ret = -EAGAIN;
else 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); 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); 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); 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); 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); 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); 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); 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"); 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 * tb_switch_add() - Add a switch to the domain
* @sw: Switch to add * @sw: Switch to add
@ -2891,6 +2990,10 @@ int tb_switch_add(struct tb_switch *sw)
return ret; return ret;
} }
ret = tb_switch_port_hotplug_enable(sw);
if (ret)
return ret;
ret = device_add(&sw->dev); ret = device_add(&sw->dev);
if (ret) { if (ret) {
dev_err(&sw->dev, "failed to add device: %d\n", 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; 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) static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
{ {
struct tb_switch *parent = tb_switch_parent(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); 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) static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx)
{ {
struct tb_switch *parent = tb_switch_parent(sw); struct tb_switch *parent = tb_switch_parent(sw);

View File

@ -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, static void tb_switch_discover_tunnels(struct tb_switch *sw,
struct list_head *list, struct list_head *list,
bool alloc_hopids) bool alloc_hopids)
@ -1416,8 +1442,11 @@ static int tb_start(struct tb *tb)
* ICM firmware upgrade needs running firmware and in native * ICM firmware upgrade needs running firmware and in native
* mode that is not available so disable firmware upgrade of the * mode that is not available so disable firmware upgrade of the
* root switch. * 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 */ /* All USB4 routers support runtime PM */
tb->root_switch->rpm = tb_switch_is_usb4(tb->root_switch); 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); tb_scan_switch(tb->root_switch);
/* Find out tunnels created by the boot firmware */ /* Find out tunnels created by the boot firmware */
tb_discover_tunnels(tb); 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 * If the boot firmware did not create USB 3.x tunnels create them
* now for the whole topology. * now for the whole topology.

View File

@ -23,11 +23,6 @@
#define NVM_MAX_SIZE SZ_512K #define NVM_MAX_SIZE SZ_512K
#define NVM_DATA_DWORDS 16 #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 * struct tb_nvm - Structure holding NVM information
* @dev: Owner of the NVM * @dev: Owner of the NVM
@ -35,28 +30,35 @@
* @minor: Minor version number of the active NVM portion * @minor: Minor version number of the active NVM portion
* @id: Identifier used with both NVM portions * @id: Identifier used with both NVM portions
* @active: Active portion NVMem device * @active: Active portion NVMem device
* @active_size: Size in bytes of the active NVM
* @non_active: Non-active portion NVMem device * @non_active: Non-active portion NVMem device
* @buf: Buffer where the NVM image is stored before it is written to * @buf: Buffer where the NVM image is stored before it is written to
* the actual NVM flash device * 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 * @buf_data_size: Number of bytes actually consumed by the new NVM
* image * image
* @authenticating: The device is authenticating the new NVM * @authenticating: The device is authenticating the new NVM
* @flushed: The image has been flushed to the storage area * @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 * The user of this structure needs to handle serialization of possible
* concurrent access. * concurrent access.
*/ */
struct tb_nvm { struct tb_nvm {
struct device *dev; struct device *dev;
u8 major; u32 major;
u8 minor; u32 minor;
int id; int id;
struct nvmem_device *active; struct nvmem_device *active;
size_t active_size;
struct nvmem_device *non_active; struct nvmem_device *non_active;
void *buf; void *buf;
void *buf_data_start;
size_t buf_data_size; size_t buf_data_size;
bool authenticating; bool authenticating;
bool flushed; bool flushed;
const struct tb_nvm_vendor_ops *vops;
}; };
enum tb_nvm_write_ops { enum tb_nvm_write_ops {
@ -113,8 +115,8 @@ struct tb_switch_tmu {
enum tb_clx { enum tb_clx {
TB_CLX_DISABLE, TB_CLX_DISABLE,
/* CL0s and CL1 are enabled and supported together */ /* CL0s and CL1 are enabled and supported together */
TB_CL1, TB_CL1 = BIT(0),
TB_CL2, TB_CL2 = BIT(1),
}; };
/** /**
@ -279,12 +281,16 @@ struct tb_port {
* @can_offline: Does the port have necessary platform support to moved * @can_offline: Does the port have necessary platform support to moved
* it into offline mode and back * it into offline mode and back
* @offline: The port is currently in offline mode * @offline: The port is currently in offline mode
* @margining: Pointer to margining structure if enabled
*/ */
struct usb4_port { struct usb4_port {
struct device dev; struct device dev;
struct tb_port *port; struct tb_port *port;
bool can_offline; bool can_offline;
bool 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 * @device: Device ID of the retimer
* @port: Pointer to the lane 0 adapter * @port: Pointer to the lane 0 adapter
* @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise) * @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 * @auth_status: Status of last NVM authentication
*/ */
struct tb_retimer { struct tb_retimer {
@ -306,6 +313,7 @@ struct tb_retimer {
u32 device; u32 device;
struct tb_port *port; struct tb_port *port;
struct tb_nvm *nvm; struct tb_nvm *nvm;
bool no_nvm_upgrade;
u32 auth_status; 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); 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, int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
size_t bytes); size_t bytes);
int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size, int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write);
nvmem_reg_write_t reg_write);
void tb_nvm_free(struct tb_nvm *nvm); void tb_nvm_free(struct tb_nvm *nvm);
void tb_nvm_exit(void); 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, unsigned int retries, write_block_fn write_next_block,
void *write_block_data); 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, struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
u64 route); u64 route);
struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb, 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 tb_port_wait_for_link_width(struct tb_port *port, int width,
int timeout_msec); int timeout_msec);
int tb_port_update_credits(struct tb_port *port); 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_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); 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, struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
u8 depth); 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); int tb_retimer_scan(struct tb_port *port, bool add);
void tb_retimer_remove_all(struct tb_port *port); 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); void usb4_switch_remove_ports(struct tb_switch *sw);
int usb4_port_unlock(struct tb_port *port); 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); int usb4_port_configure(struct tb_port *port);
void usb4_port_unconfigure(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); 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_router_online(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port); int usb4_port_enumerate_retimers(struct tb_port *port);
bool usb4_port_clx_supported(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_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf, 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_debugfs_exit(void);
void tb_switch_debugfs_init(struct tb_switch *sw); void tb_switch_debugfs_init(struct tb_switch *sw);
void tb_switch_debugfs_remove(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_init(struct tb_service *svc);
void tb_service_debugfs_remove(struct tb_service *svc); void tb_service_debugfs_remove(struct tb_service *svc);
#else #else
@ -1271,6 +1301,8 @@ static inline void tb_debugfs_init(void) { }
static inline void tb_debugfs_exit(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_init(struct tb_switch *sw) { }
static inline void tb_switch_debugfs_remove(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_init(struct tb_service *svc) { }
static inline void tb_service_debugfs_remove(struct tb_service *svc) { } static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
#endif #endif

View File

@ -308,6 +308,7 @@ struct tb_regs_port_header {
#define ADP_CS_5 0x05 #define ADP_CS_5 0x05
#define ADP_CS_5_LCA_MASK GENMASK(28, 22) #define ADP_CS_5_LCA_MASK GENMASK(28, 22)
#define ADP_CS_5_LCA_SHIFT 22 #define ADP_CS_5_LCA_SHIFT 22
#define ADP_CS_5_DHP BIT(31)
/* TMU adapter registers */ /* TMU adapter registers */
#define TMU_ADP_CS_3 0x03 #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_SUPPORTED_WIDTH_DUAL 0x2
#define LANE_ADP_CS_0_CL0S_SUPPORT BIT(26) #define LANE_ADP_CS_0_CL0S_SUPPORT BIT(26)
#define LANE_ADP_CS_0_CL1_SUPPORT BIT(27) #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 0x01
#define LANE_ADP_CS_1_TARGET_SPEED_MASK GENMASK(3, 0) #define LANE_ADP_CS_1_TARGET_SPEED_MASK GENMASK(3, 0)
#define LANE_ADP_CS_1_TARGET_SPEED_GEN3 0xc #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_TARGET_WIDTH_DUAL 0x3
#define LANE_ADP_CS_1_CL0S_ENABLE BIT(10) #define LANE_ADP_CS_1_CL0S_ENABLE BIT(10)
#define LANE_ADP_CS_1_CL1_ENABLE BIT(11) #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_LD BIT(14)
#define LANE_ADP_CS_1_LB BIT(15) #define LANE_ADP_CS_1_LB BIT(15)
#define LANE_ADP_CS_1_CURRENT_SPEED_MASK GENMASK(19, 16) #define LANE_ADP_CS_1_CURRENT_SPEED_MASK GENMASK(19, 16)

View File

@ -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); 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) static int usb4_port_set_configured(struct tb_port *port, bool configured)
{ {
int ret; int ret;
@ -1386,6 +1406,126 @@ bool usb4_port_clx_supported(struct tb_port *port)
return !!(val & PORT_CS_18_CPS); 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, static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
enum usb4_sb_opcode opcode, enum usb4_sb_opcode opcode,
int timeout_msec) int timeout_msec)

View File

@ -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 * It should be null terminated but anything else is pretty much
* allowed. * 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); 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); 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); 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); 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); 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); 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); 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); 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); static DEVICE_ATTR_RO(prtcstns);
@ -1131,11 +1131,6 @@ static int populate_properties(struct tb_xdomain *xd,
return 0; 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) static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd)
{ {
bool change = false; bool change = false;
@ -1440,6 +1435,8 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd)
if (xd->vendor_name && xd->device_name) if (xd->vendor_name && xd->device_name)
dev_info(&xd->dev, "%s %s\n", xd->vendor_name, dev_info(&xd->dev, "%s %s\n", xd->vendor_name,
xd->device_name); xd->device_name);
tb_xdomain_debugfs_init(xd);
} else { } else {
kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); 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); 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); 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)) if (mutex_lock_interruptible(&xd->lock))
return -ERESTARTSYS; 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); mutex_unlock(&xd->lock);
return ret; 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); 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); 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); 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); 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)) if (mutex_lock_interruptible(&xd->lock))
return -ERESTARTSYS; 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); mutex_unlock(&xd->lock);
return ret; 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); 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); 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); 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); 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); 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); 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) void tb_xdomain_remove(struct tb_xdomain *xd)
{ {
tb_xdomain_debugfs_remove(xd);
stop_handshake(xd); stop_handshake(xd);
device_for_each_child_reverse(&xd->dev, xd, unregister_service); device_for_each_child_reverse(&xd->dev, xd, unregister_service);

View File

@ -1026,7 +1026,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
/* public fields */ /* public fields */
instance->driver = driver; instance->driver = driver;
strlcpy(instance->driver_name, driver->driver_name, strscpy(instance->driver_name, driver->driver_name,
sizeof(instance->driver_name)); sizeof(instance->driver_name));
instance->usb_dev = usb_dev; instance->usb_dev = usb_dev;

View File

@ -110,8 +110,6 @@ static int cdns3_plat_probe(struct platform_device *pdev)
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
if (cdns->wakeup_irq == -EPROBE_DEFER) if (cdns->wakeup_irq == -EPROBE_DEFER)
return cdns->wakeup_irq; return cdns->wakeup_irq;
else if (cdns->wakeup_irq == 0)
return -EINVAL;
if (cdns->wakeup_irq < 0) { if (cdns->wakeup_irq < 0) {
dev_dbg(dev, "couldn't get wakeup irq\n"); dev_dbg(dev, "couldn't get wakeup irq\n");

View File

@ -34,26 +34,26 @@ config USB_CHIPIDEA_HOST
ChipIdea driver. ChipIdea driver.
config USB_CHIPIDEA_PCI config USB_CHIPIDEA_PCI
tristate "Enable PCI glue driver" if EMBEDDED tristate "Enable PCI glue driver" if EXPERT
depends on USB_PCI depends on USB_PCI
depends on NOP_USB_XCEIV depends on NOP_USB_XCEIV
default USB_CHIPIDEA default USB_CHIPIDEA
config USB_CHIPIDEA_MSM config USB_CHIPIDEA_MSM
tristate "Enable MSM hsusb glue driver" if EMBEDDED tristate "Enable MSM hsusb glue driver" if EXPERT
default USB_CHIPIDEA default USB_CHIPIDEA
config USB_CHIPIDEA_IMX 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 depends on OF
default USB_CHIPIDEA default USB_CHIPIDEA
config USB_CHIPIDEA_GENERIC config USB_CHIPIDEA_GENERIC
tristate "Enable generic USB2 glue driver" if EMBEDDED tristate "Enable generic USB2 glue driver" if EXPERT
default USB_CHIPIDEA default USB_CHIPIDEA
config USB_CHIPIDEA_TEGRA config USB_CHIPIDEA_TEGRA
tristate "Enable Tegra USB glue driver" if EMBEDDED tristate "Enable Tegra USB glue driver" if EXPERT
depends on OF depends on OF
default USB_CHIPIDEA default USB_CHIPIDEA

View File

@ -30,6 +30,7 @@ static const struct ci_hdrc_platform_data ci_default_pdata = {
static const struct ci_hdrc_platform_data ci_zynq_pdata = { static const struct ci_hdrc_platform_data ci_zynq_pdata = {
.capoffset = DEF_CAPOFFSET, .capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_PHY_VBUS_CONTROL,
}; };
static const struct ci_hdrc_platform_data ci_zevio_pdata = { static const struct ci_hdrc_platform_data ci_zevio_pdata = {

View File

@ -63,6 +63,13 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
priv->enabled = 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)) { if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) {
/* /*
* Marvell 28nm HSIC PHY requires forcing the port to HS mode. * Marvell 28nm HSIC PHY requires forcing the port to HS mode.

View File

@ -471,6 +471,10 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
return; return;
} }
} }
if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL)
usb_phy_vbus_on(ci->usb_phy);
/* Disable data pulse irq */ /* Disable data pulse irq */
hw_write_otgsc(ci, OTGSC_DPIE, 0); 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) if (ci->platdata->reg_vbus)
regulator_disable(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_drop = 1;
fsm->a_bus_req = 0; fsm->a_bus_req = 0;
} }

View File

@ -958,7 +958,7 @@ static void wdm_wwan_rx(struct wdm_device *desc, int length)
if (!skb) if (!skb)
return; return;
memcpy(skb_put(skb, length), desc->inbuf, length); skb_put_data(skb, desc->inbuf, length);
wwan_port_rx(port, skb); wwan_port_rx(port, skb);
/* inbuf has been copied, it is safe to check for outstanding data */ /* inbuf has been copied, it is safe to check for outstanding data */

View File

@ -208,28 +208,26 @@ static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue); snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
} }
/** static void usb_decode_ctrl_generic(char *str, size_t size, __u8 bRequestType,
* usb_decode_ctrl - Returns human readable representation of control request. __u8 bRequest, __u16 wValue, __u16 wIndex,
* @str: buffer to return a human-readable representation of control request. __u16 wLength)
* This buffer should have about 200 bytes. {
* @size: size of str buffer. u8 recip = bRequestType & USB_RECIP_MASK;
* @bRequestType: matches the USB bmRequestType field u8 type = bRequestType & USB_TYPE_MASK;
* @bRequest: matches the USB bRequest field
* @wValue: matches the USB wValue field (CPU byte order) snprintf(str, size,
* @wIndex: matches the USB wIndex field (CPU byte order) "Type=%s Recipient=%s Dir=%s bRequest=%u wValue=%u wIndex=%u wLength=%u",
* @wLength: matches the USB wLength field (CPU byte order) (type == USB_TYPE_STANDARD) ? "Standard" :
* (type == USB_TYPE_VENDOR) ? "Vendor" :
* Function returns decoded, formatted and human-readable description of (type == USB_TYPE_CLASS) ? "Class" : "Unknown",
* control request packet. (recip == USB_RECIP_DEVICE) ? "Device" :
* (recip == USB_RECIP_INTERFACE) ? "Interface" :
* The usage scenario for this is for tracepoints, so function as a return (recip == USB_RECIP_ENDPOINT) ? "Endpoint" : "Unknown",
* use the same value as in parameters. This approach allows to use this (bRequestType & USB_DIR_IN) ? "IN" : "OUT",
* function in TP_printk bRequest, wValue, wIndex, wLength);
* }
* Important: wValue, wIndex, wLength parameters before invoking this function
* should be processed by le16_to_cpu macro. static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType,
*/
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
__u8 bRequest, __u16 wValue, __u16 wIndex, __u8 bRequest, __u16 wValue, __u16 wIndex,
__u16 wLength) __u16 wLength)
{ {
@ -272,14 +270,48 @@ const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
usb_decode_set_isoch_delay(wValue, str, size); usb_decode_set_isoch_delay(wValue, str, size);
break; break;
default: default:
snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x", usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
bRequestType, bRequest, wValue, wIndex, wLength);
(u8)(cpu_to_le16(wValue) & 0xff), break;
(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 - 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; return str;

View File

@ -233,7 +233,7 @@ err:
return 0; 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; struct ulpi *ulpi = seq->private;
@ -269,21 +269,7 @@ static int ulpi_regs_read(struct seq_file *seq, void *data)
return 0; return 0;
} }
DEFINE_SHOW_ATTRIBUTE(ulpi_regs);
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 ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL) #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); 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", dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
ulpi->id.vendor, ulpi->id.product); ulpi->id.vendor, ulpi->id.product);

View File

@ -208,10 +208,8 @@ static int usb_conn_probe(struct platform_device *pdev)
if (PTR_ERR(info->vbus) == -ENODEV) if (PTR_ERR(info->vbus) == -ENODEV)
info->vbus = NULL; info->vbus = NULL;
if (IS_ERR(info->vbus)) { if (IS_ERR(info->vbus))
ret = PTR_ERR(info->vbus); return dev_err_probe(dev, PTR_ERR(info->vbus), "failed to get vbus\n");
return dev_err_probe(dev, ret, "failed to get vbus :%d\n", ret);
}
info->role_sw = usb_role_switch_get(dev); info->role_sw = usb_role_switch_get(dev);
if (IS_ERR(info->role_sw)) if (IS_ERR(info->role_sw))

View File

@ -1434,7 +1434,7 @@ static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
if (!intf || !intf->dev.driver) if (!intf || !intf->dev.driver)
ret = -ENODATA; ret = -ENODATA;
else { else {
strlcpy(gd.driver, intf->dev.driver->name, strscpy(gd.driver, intf->dev.driver->name,
sizeof(gd.driver)); sizeof(gd.driver));
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0); ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
} }

View File

@ -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 * usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed * @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
* @driver: USB HC driver handle * @driver: USB HC driver handle
* *
* Context: task context, might sleep * 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. * Return: 0 if successful.
*/ */
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id, int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
const struct hc_driver *driver)
{ {
struct usb_hcd *hcd; struct usb_hcd *hcd;
int retval; int retval;
@ -180,9 +178,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
if (usb_disabled()) if (usb_disabled())
return -ENODEV; return -ENODEV;
if (!id)
return -EINVAL;
if (!driver) if (!driver)
return -EINVAL; return -EINVAL;

View File

@ -1474,7 +1474,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
urb->sg, urb->sg,
urb->num_sgs, urb->num_sgs,
dir); dir);
if (n <= 0) if (!n)
ret = -EAGAIN; ret = -EAGAIN;
else else
urb->transfer_flags |= URB_DMA_MAP_SG; urb->transfer_flags |= URB_DMA_MAP_SG;
@ -2158,21 +2158,14 @@ static struct urb *request_single_step_set_feature_urb(
{ {
struct urb *urb; struct urb *urb;
struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_host_endpoint *ep;
urb = usb_alloc_urb(0, GFP_KERNEL); urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) if (!urb)
return NULL; return NULL;
urb->pipe = usb_rcvctrlpipe(udev, 0); 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->dev = udev;
urb->setup_packet = (void *)dr; urb->setup_packet = (void *)dr;
urb->transfer_buffer = buf; urb->transfer_buffer = buf;

View File

@ -388,6 +388,15 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Kingston DataTraveler 3.0 */ /* Kingston DataTraveler 3.0 */
{ USB_DEVICE(0x0951, 0x1666), .driver_info = USB_QUIRK_NO_LPM }, { 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 */ /* X-Rite/Gretag-Macbeth Eye-One Pro display colorimeter */
{ USB_DEVICE(0x0971, 0x2000), .driver_info = USB_QUIRK_NO_SET_INTF }, { 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_DEVICE(0x1532, 0x0116), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, 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 */ /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
{ USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM }, { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },

View File

@ -3,36 +3,6 @@
* core.c - DesignWare HS OTG Controller common routines * core.c - DesignWare HS OTG Controller common routines
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,36 +3,6 @@
* core.h - DesignWare HS OTG Controller common declarations * core.h - DesignWare HS OTG Controller common declarations
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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__ #ifndef __DWC2_CORE_H__

View File

@ -3,36 +3,6 @@
* core_intr.c - DesignWare HS OTG Controller common interrupt handling * core_intr.c - DesignWare HS OTG Controller common interrupt handling
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,36 +3,6 @@
* hcd.c - DesignWare HS OTG Controller host-mode routines * hcd.c - DesignWare HS OTG Controller host-mode routines
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,37 +3,8 @@
* hcd.h - DesignWare HS OTG Controller host-mode declarations * hcd.h - DesignWare HS OTG Controller host-mode declarations
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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__ #ifndef __DWC2_HCD_H__
#define __DWC2_HCD_H__ #define __DWC2_HCD_H__

View File

@ -3,36 +3,6 @@
* hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines * hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,36 +3,6 @@
* hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling * hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,36 +3,6 @@
* hcd_queue.c - DesignWare HS OTG Controller host queuing routines * hcd_queue.c - DesignWare HS OTG Controller host queuing routines
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,36 +3,6 @@
* hw.h - DesignWare HS OTG Controller hardware definitions * hw.h - DesignWare HS OTG Controller hardware definitions
* *
* Copyright 2004-2013 Synopsys, Inc. * 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__ #ifndef __DWC2_HW_H__

View File

@ -1,36 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* /*
* Copyright (C) 2004-2016 Synopsys, Inc. * 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> #include <linux/kernel.h>

View File

@ -3,36 +3,6 @@
* pci.c - DesignWare HS OTG Controller PCI driver * pci.c - DesignWare HS OTG Controller PCI driver
* *
* Copyright (C) 2004-2013 Synopsys, Inc. * 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.
*/ */
/* /*

View File

@ -3,36 +3,6 @@
* platform.c - DesignWare HS OTG Controller platform driver * platform.c - DesignWare HS OTG Controller platform driver
* *
* Copyright (C) Matthijs Kooijman <matthijs@stdin.nl> * 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> #include <linux/kernel.h>

View File

@ -23,7 +23,6 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/reset.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 * mode. If the controller supports DRD but the dr_mode is not
* specified or set to OTG, then set the mode to peripheral. * 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) || (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
!device_property_read_bool(dwc->dev, "usb-role-switch")) && !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
!DWC3_VER_IS_PRIOR(DWC3, 330A)) !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) reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, 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); dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
} }
@ -789,7 +792,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
else else
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM; 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; reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 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); 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)) { if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); 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"); "snps,dis-del-phy-power-chg-quirk");
dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev, dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
"snps,dis-tx-ipgap-linecheck-quirk"); "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, dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
"snps,parkmode-disable-ss-quirk"); "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, dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk"); "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) static int dwc3_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -1753,8 +1735,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc); dwc3_get_properties(dwc);
dwc->reset = devm_reset_control_array_get_optional_shared(dev); dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) if (IS_ERR(dwc->reset)) {
return PTR_ERR(dwc->reset); ret = PTR_ERR(dwc->reset);
goto put_usb_psy;
}
if (dev->of_node) { if (dev->of_node) {
/* /*
@ -1764,45 +1748,57 @@ static int dwc3_probe(struct platform_device *pdev)
* check for them to retain backwards compatibility. * check for them to retain backwards compatibility.
*/ */
dwc->bus_clk = devm_clk_get_optional(dev, "bus_early"); dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
if (IS_ERR(dwc->bus_clk)) if (IS_ERR(dwc->bus_clk)) {
return dev_err_probe(dev, PTR_ERR(dwc->bus_clk), ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
"could not get bus clock\n"); "could not get bus clock\n");
goto put_usb_psy;
}
if (dwc->bus_clk == NULL) { if (dwc->bus_clk == NULL) {
dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk"); dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
if (IS_ERR(dwc->bus_clk)) if (IS_ERR(dwc->bus_clk)) {
return dev_err_probe(dev, PTR_ERR(dwc->bus_clk), ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
"could not get bus clock\n"); "could not get bus clock\n");
goto put_usb_psy;
}
} }
dwc->ref_clk = devm_clk_get_optional(dev, "ref"); dwc->ref_clk = devm_clk_get_optional(dev, "ref");
if (IS_ERR(dwc->ref_clk)) if (IS_ERR(dwc->ref_clk)) {
return dev_err_probe(dev, PTR_ERR(dwc->ref_clk), ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
"could not get ref clock\n"); "could not get ref clock\n");
goto put_usb_psy;
}
if (dwc->ref_clk == NULL) { if (dwc->ref_clk == NULL) {
dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk"); dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
if (IS_ERR(dwc->ref_clk)) if (IS_ERR(dwc->ref_clk)) {
return dev_err_probe(dev, PTR_ERR(dwc->ref_clk), ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
"could not get ref clock\n"); "could not get ref clock\n");
goto put_usb_psy;
}
} }
dwc->susp_clk = devm_clk_get_optional(dev, "suspend"); dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
if (IS_ERR(dwc->susp_clk)) if (IS_ERR(dwc->susp_clk)) {
return dev_err_probe(dev, PTR_ERR(dwc->susp_clk), ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
"could not get suspend clock\n"); "could not get suspend clock\n");
goto put_usb_psy;
}
if (dwc->susp_clk == NULL) { if (dwc->susp_clk == NULL) {
dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk"); dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
if (IS_ERR(dwc->susp_clk)) if (IS_ERR(dwc->susp_clk)) {
return dev_err_probe(dev, PTR_ERR(dwc->susp_clk), ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
"could not get suspend clock\n"); "could not get suspend clock\n");
goto put_usb_psy;
}
} }
} }
ret = reset_control_deassert(dwc->reset); ret = reset_control_deassert(dwc->reset);
if (ret) if (ret)
return ret; goto put_usb_psy;
ret = dwc3_clk_enable(dwc); ret = dwc3_clk_enable(dwc);
if (ret) if (ret)
@ -1844,13 +1840,6 @@ static int dwc3_probe(struct platform_device *pdev)
goto err2; 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); ret = dwc3_get_dr_mode(dwc);
if (ret) if (ret)
goto err3; goto err3;
@ -1909,7 +1898,7 @@ disable_clks:
dwc3_clk_disable(dwc); dwc3_clk_disable(dwc);
assert_reset: assert_reset:
reset_control_assert(dwc->reset); reset_control_assert(dwc->reset);
put_usb_psy:
if (dwc->usb_psy) if (dwc->usb_psy)
power_supply_put(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: case DWC3_GCTL_PRTCAP_DEVICE:
if (pm_runtime_suspended(dwc->dev)) if (pm_runtime_suspended(dwc->dev))
break; break;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc); dwc3_gadget_suspend(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
synchronize_irq(dwc->irq_gadget); synchronize_irq(dwc->irq_gadget);
dwc3_core_exit(dwc); dwc3_core_exit(dwc);
break; break;
@ -2040,9 +2027,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
return ret; return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc); dwc3_gadget_resume(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
break; break;
case DWC3_GCTL_PRTCAP_HOST: case DWC3_GCTL_PRTCAP_HOST:
if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {

View File

@ -263,6 +263,7 @@
#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) #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_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10)
/* Global Status Register */ /* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10) #define DWC3_GSTS_OTG_IP BIT(10)
@ -391,6 +392,7 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f #define DWC3_GFLADJ_30MHZ_MASK 0x3f
#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) #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 GENMASK(30, 24)
#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31) #define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
@ -1096,6 +1098,8 @@ struct dwc3_scratchpad_array {
* change quirk. * change quirk.
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
* check during HS transmit. * 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 * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
* instances in park mode. * instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @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_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1; unsigned dis_del_phy_power_chg_quirk:1;
unsigned dis_tx_ipgap_linecheck_quirk:1; unsigned dis_tx_ipgap_linecheck_quirk:1;
unsigned resume_hs_terminations:1;
unsigned parkmode_disable_ss_quirk:1; unsigned parkmode_disable_ss_quirk:1;
unsigned gfladj_refclk_lpm_sel:1;
unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2; 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, int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
u32 param); u32 param);
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc); void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status);
#else #else
static inline int dwc3_gadget_init(struct dwc3 *dwc) static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; } { return 0; }

View File

@ -278,7 +278,7 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
break; break;
case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_XFERINPROGRESS:
scnprintf(str + len, size - len, scnprintf(str + len, size - len,
"Transfer In Progress [%d] (%c%c%c)", "Transfer In Progress [%08x] (%c%c%c)",
event->parameters, event->parameters,
status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_IOC ? 'I' : 'i',
@ -286,7 +286,7 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
break; break;
case DWC3_DEPEVT_XFERNOTREADY: case DWC3_DEPEVT_XFERNOTREADY:
len += scnprintf(str + len, size - len, len += scnprintf(str + len, size - len,
"Transfer Not Ready [%d]%s", "Transfer Not Ready [%08x]%s",
event->parameters, event->parameters,
status & DEPEVT_STATUS_TRANSFER_ACTIVE ? status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
" (Active)" : " (Not Active)"); " (Active)" : " (Not Active)");

View File

@ -8,6 +8,7 @@
*/ */
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
@ -438,6 +439,51 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
return NOTIFY_DONE; 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) #if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
#define ROLE_SWITCH 1 #define ROLE_SWITCH 1
static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, 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")) device_property_read_bool(dwc->dev, "usb-role-switch"))
return dwc3_setup_role_switch(dwc); 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) { if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier; dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,

View File

@ -40,9 +40,10 @@
#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee #define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
#define PCI_DEVICE_ID_INTEL_TGPH 0x43ee #define PCI_DEVICE_ID_INTEL_TGPH 0x43ee
#define PCI_DEVICE_ID_INTEL_JSP 0x4dee #define PCI_DEVICE_ID_INTEL_JSP 0x4dee
#define PCI_DEVICE_ID_INTEL_ADL 0x465e #define PCI_DEVICE_ID_INTEL_ADL 0x460e
#define PCI_DEVICE_ID_INTEL_ADLP 0x51ee #define PCI_DEVICE_ID_INTEL_ADL_PCH 0x51ee
#define PCI_DEVICE_ID_INTEL_ADLM 0x54ee #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_ADLS 0x7ae1
#define PCI_DEVICE_ID_INTEL_RPL 0x460e #define PCI_DEVICE_ID_INTEL_RPL 0x460e
#define PCI_DEVICE_ID_INTEL_RPLS 0x7a61 #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), { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL),
(kernel_ulong_t) &dwc3_pci_intel_swnode, }, (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, }, (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, }, (kernel_ulong_t) &dwc3_pci_intel_swnode, },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLS), { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLS),

View File

@ -243,6 +243,7 @@ static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
*/ */
static int dwc3_qcom_interconnect_init(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; struct device *dev = qcom->dev;
int ret; int ret;
@ -263,21 +264,20 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
return PTR_ERR(qcom->icc_path_apps); return PTR_ERR(qcom->icc_path_apps);
} }
if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER || max_speed = usb_get_maximum_speed(&qcom->dwc3->dev);
usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN) if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
ret = icc_set_bw(qcom->icc_path_ddr, ret = icc_set_bw(qcom->icc_path_ddr,
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW); USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
else } else {
ret = icc_set_bw(qcom->icc_path_ddr, 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) { if (ret) {
dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret); dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret);
return ret; return ret;
} }
ret = icc_set_bw(qcom->icc_path_apps, ret = icc_set_bw(qcom->icc_path_apps, APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
if (ret) { if (ret) {
dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret); dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret);
return 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[] = { static const struct of_device_id dwc3_qcom_of_match[] = {
{ .compatible = "qcom,dwc3" }, { .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); MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);

View File

@ -47,6 +47,7 @@ struct dwc3_xlnx {
struct device *dev; struct device *dev;
void __iomem *regs; void __iomem *regs;
int (*pltfm_init)(struct dwc3_xlnx *data); 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) 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 device *dev = priv_data->dev;
struct reset_control *crst, *hibrst, *apbrst; struct reset_control *crst, *hibrst, *apbrst;
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
struct phy *usb3_phy;
int ret = 0; int ret = 0;
u32 reg; u32 reg;
usb3_phy = devm_phy_optional_get(dev, "usb3-phy"); priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
if (IS_ERR(usb3_phy)) { if (IS_ERR(priv_data->usb3_phy)) {
ret = PTR_ERR(usb3_phy); ret = PTR_ERR(priv_data->usb3_phy);
dev_err_probe(dev, ret, dev_err_probe(dev, ret,
"failed to get USB3 PHY\n"); "failed to get USB3 PHY\n");
goto err; 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. * in use but the usb3-phy entry is missing from the device tree.
* Therefore, skip these operations in this case. * Therefore, skip these operations in this case.
*/ */
if (!usb3_phy) if (!priv_data->usb3_phy)
goto skip_usb3_phy; goto skip_usb3_phy;
crst = devm_reset_control_get_exclusive(dev, "usb_crst"); 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; goto err;
} }
ret = phy_init(usb3_phy); ret = phy_init(priv_data->usb3_phy);
if (ret < 0) { if (ret < 0) {
phy_exit(usb3_phy); phy_exit(priv_data->usb3_phy);
goto err; goto err;
} }
@ -196,9 +196,9 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err; goto err;
} }
ret = phy_power_on(usb3_phy); ret = phy_power_on(priv_data->usb3_phy);
if (ret < 0) { if (ret < 0) {
phy_exit(usb3_phy); phy_exit(priv_data->usb3_phy);
goto err; goto err;
} }
@ -322,7 +322,7 @@ static int dwc3_xlnx_remove(struct platform_device *pdev)
return 0; 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); 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; 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); 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; return 0;
} }
static UNIVERSAL_DEV_PM_OPS(dwc3_xlnx_dev_pm_ops, dwc3_xlnx_suspend_common, static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
dwc3_xlnx_resume_common, dwc3_xlnx_runtime_idle); {
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 = { static struct platform_driver dwc3_xlnx_driver = {
.probe = dwc3_xlnx_probe, .probe = dwc3_xlnx_probe,

View File

@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret; int ret;
spin_lock_irqsave(&dwc->lock, flags); 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", dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name); dep->name);
ret = -ESHUTDOWN; ret = -ESHUTDOWN;
@ -293,7 +293,10 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
continue; continue;
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP; dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
if (dwc->connected)
dwc3_stop_active_transfer(dwc3_ep, true, true); 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; int ret = -EINVAL;
u32 len; u32 len;
if (!dwc->gadget_driver || !dwc->connected) if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out; goto out;
trace_dwc3_ctrl_req(ctrl); trace_dwc3_ctrl_req(ctrl);
@ -1118,6 +1121,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
{ {
switch (event->status) { switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA: case DEPEVT_STATUS_CONTROL_DATA:
if (!dwc->softconnect || !dwc->connected)
return;
/* /*
* We already have a DATA transfer in the controller's cache, * We already have a DATA transfer in the controller's cache,
* if we receive a XferNotReady(DATA) we will ignore it, unless * if we receive a XferNotReady(DATA) we will ignore it, unless

View File

@ -366,7 +366,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
dwc3_writel(dep->regs, DWC3_DEPCMD, 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; ret = 0;
goto skip_status; goto skip_status;
} }
@ -965,29 +967,33 @@ out:
return 0; 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; struct dwc3_request *req;
dwc3_stop_active_transfer(dep, true, false); 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 */ /* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) { while (!list_empty(&dep->started_list)) {
req = next_request(&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)) { while (!list_empty(&dep->pending_list)) {
req = next_request(&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)) { while (!list_empty(&dep->cancelled_list)) {
req = next_request(&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; struct dwc3 *dwc = dep->dwc;
u32 reg; u32 reg;
u32 mask;
trace_dwc3_gadget_ep_disable(dep); trace_dwc3_gadget_ep_disable(dep);
@ -1022,11 +1029,19 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->endpoint.desc = NULL; dep->endpoint.desc = NULL;
} }
dwc3_remove_requests(dwc, dep); dwc3_remove_requests(dwc, dep, -ECONNRESET);
dep->stream_capable = false; dep->stream_capable = false;
dep->type = 0; 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; return 0;
} }
@ -2340,7 +2355,7 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
if (!dep) if (!dep)
continue; 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) static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{ {
u32 reg; u32 reg;
u32 timeout = 500; u32 timeout = 2000;
if (pm_runtime_suspended(dwc->dev)) if (pm_runtime_suspended(dwc->dev))
return 0; 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); dwc3_gadget_dctl_write_safe(dwc, reg);
do { do {
usleep_range(1000, 2000);
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc->regs, DWC3_DSTS);
reg &= DWC3_DSTS_DEVCTRLHLT; reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg)); } while (--timeout && !(!is_on ^ !reg));
@ -2501,6 +2517,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
if (dwc->ep0state != EP0_SETUP_PHASE) { if (dwc->ep0state != EP0_SETUP_PHASE) {
int ret; int ret;
if (dwc->delayed_status)
dwc3_ep0_send_delayed_status(dwc);
reinit_completion(&dwc->ep0_in_setup); reinit_completion(&dwc->ep0_in_setup);
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
@ -2568,6 +2587,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
return 0; return 0;
} }
synchronize_irq(dwc->irq_gadget);
if (!is_on) { if (!is_on) {
ret = dwc3_gadget_soft_disconnect(dwc); ret = dwc3_gadget_soft_disconnect(dwc);
} else { } else {
@ -2718,6 +2739,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0]; dep = dwc->eps[0];
dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name); 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 = dwc->eps[1];
dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name); 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 * streams are updated, and the device controller will not be
* triggered to generate ERDY to move the next stream data. To * triggered to generate ERDY to move the next stream data. To
* workaround this and maintain compatibility with various * 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. * instead of waiting for the host to prime the endpoint.
*/ */
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) { if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
@ -3596,11 +3619,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep = dwc->eps[epnum]; dep = dwc->eps[epnum];
if (!(dep->flags & DWC3_EP_ENABLED)) { if (!(dep->flags & DWC3_EP_ENABLED)) {
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
return; return;
/* Handle only EPCMDCMPLT when EP disabled */ /* 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; 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 * timeout. Delay issuing the End Transfer command until the Setup TRB is
* prepared. * prepared.
*/ */
if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) { if (dwc->ep0state != EP0_SETUP_PHASE) {
dep->flags |= DWC3_EP_DELAY_STOP; dep->flags |= DWC3_EP_DELAY_STOP;
return; return;
} }
@ -3763,13 +3787,24 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_INITU2ENA; reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_gadget_dctl_write_safe(dwc, reg); dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->connected = false;
dwc3_disconnect_gadget(dwc); dwc3_disconnect_gadget(dwc);
dwc->gadget->speed = USB_SPEED_UNKNOWN; dwc->gadget->speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false; dwc->setup_packet_pending = false;
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); 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) 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 lanes = 1;
u8 speed; u8 speed;
if (!dwc->softconnect)
return;
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD; speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed; dwc->speed = speed;
@ -4129,7 +4167,7 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
unsigned int is_ss = evtinfo & BIT(4); 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 * have a known issue which can cause USB CV TD.9.23 to fail
* randomly. * randomly.
* *
@ -4507,12 +4545,17 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc) int dwc3_gadget_suspend(struct dwc3 *dwc)
{ {
unsigned long flags;
if (!dwc->gadget_driver) if (!dwc->gadget_driver)
return 0; return 0;
dwc3_gadget_run_stop(dwc, false, false); dwc3_gadget_run_stop(dwc, false, false);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_disconnect_gadget(dwc); dwc3_disconnect_gadget(dwc);
__dwc3_gadget_stop(dwc); __dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0; return 0;
} }

View File

@ -241,7 +241,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->enqueue = dep->trb_enqueue; __entry->enqueue = dep->trb_enqueue;
__entry->dequeue = dep->trb_dequeue; __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, __get_str(name), __entry->trb, __entry->enqueue,
__entry->dequeue, __entry->bph, __entry->bpl, __entry->dequeue, __entry->bph, __entry->bpl,
({char *s; ({char *s;
@ -267,6 +267,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
s = ""; s = "";
} s; }), } s; }),
DWC3_TRB_SIZE_LENGTH(__entry->size), __entry->ctrl, 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_HWO ? 'H' : 'h',
__entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l', __entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l',
__entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c', __entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c',

View File

@ -2645,10 +2645,10 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
unsigned i = 0; unsigned i = 0;
vla_group(d); vla_group(d);
vla_item(d, struct usb_gadget_strings *, stringtabs, 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_gadget_strings, stringtab, lang_count);
vla_item(d, struct usb_string, strings, 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); 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); existing = _ffs_do_find_dev(name);
if (!existing) if (!existing)
strlcpy(dev->name, name, ARRAY_SIZE(dev->name)); strscpy(dev->name, name, ARRAY_SIZE(dev->name));
else if (existing != dev) else if (existing != dev)
ret = -EBUSY; ret = -EBUSY;

View File

@ -2662,11 +2662,16 @@ static ssize_t forced_eject_store(struct device *dev,
} }
static DEVICE_ATTR_RW(nofua); 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); 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 ******************************/ /****************************** FSG COMMON ******************************/
static void fsg_lun_release(struct device *dev) static void fsg_lun_release(struct device *dev)

View File

@ -450,39 +450,35 @@ struct ndp_parser_opts {
unsigned next_ndp_index; unsigned next_ndp_index;
}; };
#define INIT_NDP16_OPTS { \ static const struct ndp_parser_opts ndp16_opts = {
.nth_sign = USB_CDC_NCM_NTH16_SIGN, \ .nth_sign = USB_CDC_NCM_NTH16_SIGN,
.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,
.nth_size = sizeof(struct usb_cdc_ncm_nth16), \ .nth_size = sizeof(struct usb_cdc_ncm_nth16),
.ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp16),
.dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \ .dpe_size = sizeof(struct usb_cdc_ncm_dpe16),
.ndplen_align = 4, \ .ndplen_align = 4,
.dgram_item_len = 1, \ .dgram_item_len = 1,
.block_length = 1, \ .block_length = 1,
.ndp_index = 1, \ .ndp_index = 1,
.reserved1 = 0, \ .reserved1 = 0,
.reserved2 = 0, \ .reserved2 = 0,
.next_ndp_index = 1, \ .next_ndp_index = 1,
} };
static const struct ndp_parser_opts ndp32_opts = {
#define INIT_NDP32_OPTS { \ .nth_sign = USB_CDC_NCM_NTH32_SIGN,
.nth_sign = USB_CDC_NCM_NTH32_SIGN, \ .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,
.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth32),
.nth_size = sizeof(struct usb_cdc_ncm_nth32), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp32),
.ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ .dpe_size = sizeof(struct usb_cdc_ncm_dpe32),
.dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \ .ndplen_align = 8,
.ndplen_align = 8, \ .dgram_item_len = 2,
.dgram_item_len = 2, \ .block_length = 2,
.block_length = 2, \ .ndp_index = 2,
.ndp_index = 2, \ .reserved1 = 1,
.reserved1 = 1, \ .reserved2 = 2,
.reserved2 = 2, \ .next_ndp_index = 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 inline void put_ncm(__le16 **p, unsigned size, unsigned val) static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
{ {

View File

@ -89,7 +89,7 @@ struct printer_dev {
u8 printer_cdev_open; u8 printer_cdev_open;
wait_queue_head_t wait; wait_queue_head_t wait;
unsigned q_len; unsigned q_len;
char *pnp_string; /* We don't own memory! */ char **pnp_string; /* We don't own memory! */
struct usb_function function; struct usb_function function;
}; };
@ -1000,16 +1000,16 @@ static int printer_func_setup(struct usb_function *f,
if ((wIndex>>8) != dev->interface) if ((wIndex>>8) != dev->interface)
break; break;
if (!dev->pnp_string) { if (!*dev->pnp_string) {
value = 0; value = 0;
break; break;
} }
value = strlen(dev->pnp_string); value = strlen(*dev->pnp_string);
buf[0] = (value >> 8) & 0xFF; buf[0] = (value >> 8) & 0xFF;
buf[1] = value & 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, DBG(dev, "1284 PNP String: %x %s\n", value,
dev->pnp_string); *dev->pnp_string);
break; break;
case GET_PORT_STATUS: /* Get Port Status */ 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); kref_init(&dev->kref);
++opts->refcnt; ++opts->refcnt;
dev->minor = opts->minor; dev->minor = opts->minor;
dev->pnp_string = opts->pnp_string; dev->pnp_string = &opts->pnp_string;
dev->q_len = opts->q_len; dev->q_len = opts->q_len;
mutex_unlock(&opts->lock); mutex_unlock(&opts->lock);

View File

@ -2306,7 +2306,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc); DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc);
static int tcm_init(void) static int __init tcm_init(void)
{ {
int ret; int ret;
@ -2322,7 +2322,7 @@ static int tcm_init(void)
} }
module_init(tcm_init); module_init(tcm_init);
static void tcm_exit(void) static void __exit tcm_exit(void)
{ {
target_unregister_template(&usbg_ops); target_unregister_template(&usbg_ops);
usb_function_unregister(&tcmusb_func); usb_function_unregister(&tcmusb_func);

View File

@ -421,7 +421,7 @@ uvc_register_video(struct uvc_device *uvc)
int ret; int ret;
/* TODO reference counting. */ /* 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 = &uvc->v4l2_dev;
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops; 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.vfl_dir = VFL_DIR_TX;
uvc->vdev.lock = &uvc->video.mutex; uvc->vdev.lock = &uvc->video.mutex;
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 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); 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 uvc_device *uvc = to_uvc(f);
struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
func_inst); func_inst);
config_item_put(&uvc->header->item);
--opts->refcnt; --opts->refcnt;
kfree(uvc); kfree(uvc);
} }
@ -897,10 +898,14 @@ static void uvc_function_unbind(struct usb_configuration *c,
{ {
struct usb_composite_dev *cdev = c->cdev; struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f); struct uvc_device *uvc = to_uvc(f);
struct uvc_video *video = &uvc->video;
long wait_ret = 1; long wait_ret = 1;
uvcg_info(f, "%s()\n", __func__); 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 * 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 * 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 uvc_device *uvc;
struct f_uvc_opts *opts; struct f_uvc_opts *opts;
struct uvc_descriptor_header **strm_cls; struct uvc_descriptor_header **strm_cls;
struct config_item *streaming, *header, *h;
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
if (uvc == NULL) 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.fs_streaming = opts->fs_streaming;
uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_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; ++opts->refcnt;
mutex_unlock(&opts->lock); mutex_unlock(&opts->lock);
@ -988,6 +1016,11 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
uvc->func.bind_deactivated = true; uvc->func.bind_deactivated = true;
return &uvc->func; 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); DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);

View File

@ -869,7 +869,7 @@ EXPORT_SYMBOL_GPL(rndis_msg_parser);
static inline int rndis_get_nr(void) 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) 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" "used : %s\n"
"state : %s\n" "state : %s\n"
"medium : 0x%08X\n" "medium : 0x%08X\n"
"speed : %d\n" "speed : %u\n"
"cable : %s\n" "cable : %s\n"
"vendor ID : 0x%08X\n" "vendor ID : 0x%08X\n"
"vendor : %s\n", "vendor : %s\n",

View File

@ -144,10 +144,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
{ {
struct eth_dev *dev = netdev_priv(net); struct eth_dev *dev = netdev_priv(net);
strlcpy(p->driver, "g_ether", sizeof(p->driver)); strscpy(p->driver, "g_ether", sizeof(p->driver));
strlcpy(p->version, UETH__VERSION, sizeof(p->version)); strscpy(p->version, UETH__VERSION, sizeof(p->version));
strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); strscpy(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->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
} }
/* REVISIT can also support: /* REVISIT can also support:

View File

@ -1443,7 +1443,7 @@ void gserial_resume(struct gserial *gser)
} }
EXPORT_SYMBOL_GPL(gserial_resume); EXPORT_SYMBOL_GPL(gserial_resume);
static int userial_init(void) static int __init userial_init(void)
{ {
struct tty_driver *driver; struct tty_driver *driver;
unsigned i; unsigned i;
@ -1496,7 +1496,7 @@ fail:
} }
module_init(userial_init); module_init(userial_init);
static void userial_cleanup(void) static void __exit userial_cleanup(void)
{ {
tty_unregister_driver(gs_tty_driver); tty_unregister_driver(gs_tty_driver);
tty_driver_kref_put(gs_tty_driver); tty_driver_kref_put(gs_tty_driver);

View File

@ -88,6 +88,7 @@ struct uvc_video {
struct usb_ep *ep; struct usb_ep *ep;
struct work_struct pump; struct work_struct pump;
struct workqueue_struct *async_wq;
/* Frame parameters */ /* Frame parameters */
u8 bpp; u8 bpp;
@ -133,6 +134,8 @@ struct uvc_device {
bool func_connected; bool func_connected;
wait_queue_head_t func_connected_queue; wait_queue_head_t func_connected_queue;
struct uvcg_streaming_header *header;
/* Descriptors */ /* Descriptors */
struct { struct {
const struct uvc_descriptor_header * const *fs_control; const struct uvc_descriptor_header * const *fs_control;

View File

@ -18,12 +18,161 @@
#include <media/v4l2-dev.h> #include <media/v4l2-dev.h>
#include <media/v4l2-event.h> #include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-uvc.h>
#include "f_uvc.h" #include "f_uvc.h"
#include "uvc.h" #include "uvc.h"
#include "uvc_queue.h" #include "uvc_queue.h"
#include "uvc_video.h" #include "uvc_video.h"
#include "uvc_v4l2.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 * 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 uvc_device *uvc = video_get_drvdata(vdev);
struct usb_composite_dev *cdev = uvc->func.config->cdev; struct usb_composite_dev *cdev = uvc->func.config->cdev;
strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); strscpy(cap->driver, "g_uvc", sizeof(cap->driver));
strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); strscpy(cap->card, cdev->gadget->name, sizeof(cap->card));
strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), strscpy(cap->bus_info, dev_name(&cdev->gadget->dev),
sizeof(cap->bus_info)); sizeof(cap->bus_info));
return 0; return 0;
} }
@ -134,6 +283,139 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
return 0; 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 static int
uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) 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; return ret;
if (uvc->state == UVC_STATE_STREAMING) if (uvc->state == UVC_STATE_STREAMING)
schedule_work(&video->pump); queue_work(video->async_wq, &video->pump);
return ret; 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 = { const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
.vidioc_querycap = uvc_v4l2_querycap, .vidioc_querycap = uvc_v4l2_querycap,
.vidioc_try_fmt_vid_out = uvc_v4l2_try_format,
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format, .vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
.vidioc_s_fmt_vid_out = uvc_v4l2_set_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_reqbufs = uvc_v4l2_reqbufs,
.vidioc_querybuf = uvc_v4l2_querybuf, .vidioc_querybuf = uvc_v4l2_querybuf,
.vidioc_qbuf = uvc_v4l2_qbuf, .vidioc_qbuf = uvc_v4l2_qbuf,

View File

@ -277,7 +277,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock_irqrestore(&video->req_lock, flags); spin_unlock_irqrestore(&video->req_lock, flags);
if (uvc->state == UVC_STATE_STREAMING) if (uvc->state == UVC_STATE_STREAMING)
schedule_work(&video->pump); queue_work(video->async_wq, &video->pump);
} }
static int static int
@ -485,7 +485,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
video->req_int_count = 0; video->req_int_count = 0;
schedule_work(&video->pump); queue_work(video->async_wq, &video->pump);
return ret; return ret;
} }
@ -499,6 +499,11 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
spin_lock_init(&video->req_lock); spin_lock_init(&video->req_lock);
INIT_WORK(&video->pump, uvcg_video_pump); 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->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV; video->fcc = V4L2_PIX_FMT_YUYV;
video->bpp = 16; video->bpp = 16;

View File

@ -994,7 +994,7 @@ static const struct usb_gadget_ops at91_udc_ops = {
.udc_stop = at91_stop, .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. * power budgets after an appropriate SET_CONFIGURATION.
*/ */
/* .vbus_power = at91_vbus_power, */ /* .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) if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0)
board->vbus_polled = 1; board->vbus_polled = 1;
board->vbus_pin = gpiod_get_from_of_node(np, "atmel,vbus-gpio", 0, board->vbus_pin = fwnode_gpiod_get_index(of_fwnode_handle(np),
GPIOD_IN, "udc_vbus"); "atmel,vbus", 0, GPIOD_IN,
"udc_vbus");
if (IS_ERR(board->vbus_pin)) if (IS_ERR(board->vbus_pin))
board->vbus_pin = NULL; 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"); GPIOD_ASIS, "udc_pullup");
if (IS_ERR(board->pullup_pin)) if (IS_ERR(board->pullup_pin))
board->pullup_pin = NULL; board->pullup_pin = NULL;

View File

@ -91,7 +91,7 @@ module_param(dma_mode, ushort, 0644);
* mode 2 == ep-a 1k, ep-b 1k, ep-c 512db * mode 2 == ep-a 1k, ep-b 1k, ep-c 512db
* mode 3 == ep-a 1k, ep-b disabled, 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); 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 * USB suspend requests will be ignored. This is acceptable for
* self-powered devices. For bus powered devices set this to 1. * 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); module_param(enable_suspend, ushort, 0644);
static void assert_out_naking(struct net2272_ep *ep, const char *where) static void assert_out_naking(struct net2272_ep *ep, const char *where)

View File

@ -2234,7 +2234,7 @@ static int proc_otg_show(struct seq_file *s)
char *ctrl_name = "(UNKNOWN)"; char *ctrl_name = "(UNKNOWN)";
tmp = omap_readl(OTG_REV); tmp = omap_readl(OTG_REV);
ctrl_name = "tranceiver_ctrl"; ctrl_name = "transceiver_ctrl";
trans = omap_readw(USB_TRANSCEIVER_CTRL); trans = omap_readw(USB_TRANSCEIVER_CTRL);
seq_printf(s, "\nOTG rev %d.%d, %s %05x\n", seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
tmp >> 4, tmp & 0xf, ctrl_name, trans); 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 */ /* set up driver data structures */
BUG_ON(strlen(name) >= sizeof ep->name); 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->queue);
INIT_LIST_HEAD(&ep->iso); INIT_LIST_HEAD(&ep->iso);
ep->bEndpointAddress = addr; ep->bEndpointAddress = addr;

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