mirror of
https://github.com/torvalds/linux.git
synced 2026-03-07 23:24:35 +01:00
USB / Thunderbolt changes for 7.0-rc1
Here is the "big" set of USB and Thunderbolt driver updates for 7.0-rc1. Overall more lines were removed than added, thanks to dropping the obsolete isp1362 USB host controller driver, always a nice change. Other than that, nothing major happening here, highlights are: - lots of dwc3 driver updates and new hardware support added - usb gadget function driver updates - usb phy driver updates - typec driver updates and additions - USB rust binding updates for syntax and formatting changes - more usb serial device ids added - other smaller USB core and driver updates and additions All of these have been in linux-next for a long time, with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCaZR0Sw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylcCgCfUyUFi0UOMPRyrU/fo5nyeWomgvsAnRst3nva y7BvYwC2L4FIP23snrTM =8S4Q -----END PGP SIGNATURE----- Merge tag 'usb-7.0-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 updates for 7.0-rc1. Overall more lines were removed than added, thanks to dropping the obsolete isp1362 USB host controller driver, always a nice change. Other than that, nothing major happening here, highlights are: - lots of dwc3 driver updates and new hardware support added - usb gadget function driver updates - usb phy driver updates - typec driver updates and additions - USB rust binding updates for syntax and formatting changes - more usb serial device ids added - other smaller USB core and driver updates and additions All of these have been in linux-next for a long time, with no reported problems" * tag 'usb-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (77 commits) usb: typec: ucsi: Add Thunderbolt alternate mode support usb: typec: hd3ss3220: Check if regulator needs to be switched usb: phy: tegra: parametrize PORTSC1 register offset usb: phy: tegra: parametrize HSIC PTS value usb: phy: tegra: return error value from utmi_wait_register usb: phy: tegra: cosmetic fixes dt-bindings: usb: renesas,usbhs: Add RZ/G3E SoC support usb: dwc2: fix resume failure if dr_mode is host usb: cdns3: fix role switching during resume usb: dwc3: gadget: Move vbus draw to workqueue context USB: serial: option: add Telit FN920C04 RNDIS compositions usb: dwc3: Log dwc3 address in traces usb: gadget: tegra-xudc: Add handling for BLCG_COREPLL_PWRDN usb: phy: tegra: add HSIC support usb: phy: tegra: use phy type directly usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi usb: typec: ucsi: Support mode selection to activate altmodes usb: typec: Introduce mode_selection bit usb: typec: Implement mode selection usb: typec: Expose alternate mode priority via sysfs ...
This commit is contained in:
commit
17f8d20093
110 changed files with 3494 additions and 4594 deletions
|
|
@ -4,11 +4,12 @@ KernelVersion: 3.19
|
|||
Description:
|
||||
The attributes:
|
||||
|
||||
========== ====================================
|
||||
================ ====================================
|
||||
index index value for the USB MIDI adapter
|
||||
id ID string for the USB MIDI adapter
|
||||
buflen MIDI buffer length
|
||||
qlen USB read request queue length
|
||||
in_ports number of MIDI input ports
|
||||
out_ports number of MIDI output ports
|
||||
========== ====================================
|
||||
interface_string USB AudioControl interface string
|
||||
================ ====================================
|
||||
|
|
|
|||
|
|
@ -85,3 +85,45 @@ Description:
|
|||
up to 5000. The default value is 64 ms.
|
||||
This polling interval is used while DbC is enabled but has no
|
||||
active data transfers.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_serial
|
||||
Date: January 2026
|
||||
Contact: Łukasz Bartosik <ukaszb@chromium.org>
|
||||
Description:
|
||||
The dbc_serial attribute allows to change the serial number
|
||||
string descriptor presented by the debug device when a host
|
||||
requests a string descriptor with iSerialNumber index.
|
||||
Index is found in the iSerialNumber field in the device
|
||||
descriptor.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB device descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is "0001".
|
||||
The field length can be from 1 to 126 characters.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_product
|
||||
Date: January 2026
|
||||
Contact: Łukasz Bartosik <ukaszb@chromium.org>
|
||||
Description:
|
||||
The dbc_product attribute allows to change the product string
|
||||
descriptor presented by the debug device when a host requests
|
||||
a string descriptor with iProduct index.
|
||||
Index is found in the iProduct field in the device descriptor.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB device descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is "Linux USB Debug Target".
|
||||
The field length can be from 1 to 126 characters.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_manufacturer
|
||||
Date: January 2026
|
||||
Contact: Łukasz Bartosik <ukaszb@chromium.org>
|
||||
Description:
|
||||
The dbc_manufacturer attribute allows to change the manufacturer
|
||||
string descriptor presented by the debug device when a host
|
||||
requests a string descriptor with iManufacturer index.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB device descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is "Linux Foundation".
|
||||
The field length can be from 1 to 126 characters.
|
||||
|
|
|
|||
|
|
@ -162,6 +162,17 @@ Description: Lists the supported USB Modes. The default USB mode that is used
|
|||
- usb3 (USB 3.2)
|
||||
- usb4 (USB4)
|
||||
|
||||
What: /sys/class/typec/<port>/<alt-mode>/priority
|
||||
Date: July 2025
|
||||
Contact: Andrei Kuchynski <akuchynski@chromium.org>
|
||||
Description:
|
||||
Displays and allows setting the priority for a specific alternate mode.
|
||||
The priority is an integer in the range 0-255. A lower numerical value
|
||||
indicates a higher priority (0 is the highest).
|
||||
If the new value is already in use by another mode, the priority of the
|
||||
conflicting mode and any subsequent modes will be incremented until they
|
||||
are all unique.
|
||||
|
||||
USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
|
||||
|
||||
What: /sys/class/typec/<port>-partner/accessory_mode
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ is built-in to the kernel image, there is no need to do anything.
|
|||
|
||||
The driver will create one virtual ethernet interface per Thunderbolt
|
||||
port which are named like ``thunderbolt0`` and so on. From this point
|
||||
you can either use standard userspace tools like ``ifconfig`` to
|
||||
you can either use standard userspace tools like ``ip`` to
|
||||
configure the interface or let your GUI handle it automatically.
|
||||
|
||||
Forcing power
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ description: |+
|
|||
|
||||
Supported number of devices and endpoints vary depending on hardware
|
||||
revisions. AST2400 and AST2500 Virtual Hub supports 5 downstream devices
|
||||
and 15 generic endpoints, while AST2600 Virtual Hub supports 7 downstream
|
||||
devices and 21 generic endpoints.
|
||||
and 15 generic endpoints, while AST2600 and AST2700 Virtual Hub supports
|
||||
7 downstream devices and 21 generic endpoints.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -26,6 +26,7 @@ properties:
|
|||
- aspeed,ast2400-usb-vhub
|
||||
- aspeed,ast2500-usb-vhub
|
||||
- aspeed,ast2600-usb-vhub
|
||||
- aspeed,ast2700-usb-vhub
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
@ -33,6 +34,9 @@ properties:
|
|||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
@ -107,6 +111,20 @@ required:
|
|||
- aspeed,vhub-downstream-ports
|
||||
- aspeed,vhub-generic-endpoints
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: aspeed,ast2700-usb-vhub
|
||||
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
else:
|
||||
properties:
|
||||
resets: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ properties:
|
|||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
|||
140
Documentation/devicetree/bindings/usb/google,lga-dwc3.yaml
Normal file
140
Documentation/devicetree/bindings/usb/google,lga-dwc3.yaml
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright (c) 2025, Google LLC
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/google,lga-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Google Tensor Series G5 (Laguna) DWC3 USB SoC Controller
|
||||
|
||||
maintainers:
|
||||
- Roy Luo <royluo@google.com>
|
||||
|
||||
description:
|
||||
Describes the DWC3 USB controller block implemented on Google Tensor SoCs,
|
||||
starting with the G5 generation (laguna). Based on Synopsys DWC3 IP, the
|
||||
controller features Dual-Role Device single port with hibernation add-on.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: google,lga-dwc3
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Core DWC3 IP registers.
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Core DWC3 interrupt.
|
||||
- description: High speed power management event for remote wakeup.
|
||||
- description: Super speed power management event for remote wakeup.
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: hs_pme
|
||||
- const: ss_pme
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Non-sticky module clock.
|
||||
- description: Sticky module clock.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: non_sticky
|
||||
- const: sticky
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: Non-sticky module reset.
|
||||
- description: Sticky module reset.
|
||||
- description: DRD bus reset.
|
||||
- description: Top-level reset.
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: non_sticky
|
||||
- const: sticky
|
||||
- const: drd_bus
|
||||
- const: top
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: Power switchable domain, the child of top domain.
|
||||
Turning it on puts the controller into full power state,
|
||||
turning it off puts the controller into power gated state.
|
||||
- description: Top domain, the parent of power switchable domain.
|
||||
Turning it on puts the controller into power gated state,
|
||||
turning it off completely shuts off the controller.
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: psw
|
||||
- const: top
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
google,usb-cfg-csr:
|
||||
description:
|
||||
A phandle to a syscon node used to access the USB configuration
|
||||
registers. These registers are the top-level wrapper of the USB
|
||||
subsystem and provide control and status for the integrated USB
|
||||
controller and USB PHY.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to the syscon node.
|
||||
- description: USB host controller configuration register offset.
|
||||
- description: USB custom interrrupts control register offset.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
- google,usb-cfg-csr
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwc3-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
usb@c400000 {
|
||||
compatible = "google,lga-dwc3";
|
||||
reg = <0 0x0c400000 0 0xd060>;
|
||||
interrupts = <GIC_SPI 580 IRQ_TYPE_LEVEL_HIGH 0>,
|
||||
<GIC_SPI 597 IRQ_TYPE_LEVEL_HIGH 0>,
|
||||
<GIC_SPI 598 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
interrupt-names = "core", "hs_pme", "ss_pme";
|
||||
clocks = <&hsion_usbc_non_sticky_clk>, <&hsion_usbc_sticky_clk>;
|
||||
clock-names = "non_sticky", "sticky";
|
||||
resets = <&hsion_resets_usbc_non_sticky>, <&hsion_resets_usbc_sticky>,
|
||||
<&hsion_resets_usb_drd_bus>, <&hsion_resets_usb_top>;
|
||||
reset-names = "non_sticky", "sticky", "drd_bus", "top";
|
||||
power-domains = <&hsio_n_usb_psw>, <&hsio_n_usb>;
|
||||
power-domain-names = "psw", "top";
|
||||
phys = <&usb_phy 0>;
|
||||
phy-names = "usb2-phy";
|
||||
snps,quirk-frame-length-adjustment = <0x20>;
|
||||
snps,gfladj-refclk-lpm-sel-quirk;
|
||||
snps,incr-burst-type-adjustment = <4>;
|
||||
google,usb-cfg-csr = <&usb_cfg_csr 0x0 0x20>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/microchip,lan9691-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip LAN969x SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Robert Marko <robert.marko@sartura.hr>
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- microchip,lan9691-dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- microchip,lan9691-dwc3
|
||||
- const: snps,dwc3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Gated USB DRD clock
|
||||
- description: Controller reference clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus_early
|
||||
- const: ref
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwc3.yaml#
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb@300000 {
|
||||
compatible = "microchip,lan9691-dwc3", "snps,dwc3";
|
||||
reg = <0x300000 0x80000>;
|
||||
interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks 12>, <&clks 11>;
|
||||
clock-names = "bus_early", "ref";
|
||||
};
|
||||
|
|
@ -27,6 +27,7 @@ properties:
|
|||
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,usbhs-r9a07g054 # RZ/V2L
|
||||
- renesas,usbhs-r9a08g045 # RZ/G3S
|
||||
- renesas,usbhs-r9a09g047 # RZ/G3E
|
||||
- renesas,usbhs-r9a09g056 # RZ/V2N
|
||||
- renesas,usbhs-r9a09g057 # RZ/V2H(P)
|
||||
- const: renesas,rzg2l-usbhs
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/socionext,uniphier-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Socionext Uniphier SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
|
||||
- Masami Hiramatsu <mhiramat@kernel.org>
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: socionext,uniphier-dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: socionext,uniphier-dwc3
|
||||
- const: snps,dwc3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Host or single combined interrupt
|
||||
- description: Peripheral interrupt
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- enum:
|
||||
- dwc_usb3
|
||||
- host
|
||||
- const: peripheral
|
||||
|
||||
clocks:
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref
|
||||
- const: bus_early
|
||||
- const: suspend
|
||||
|
||||
phys:
|
||||
description: 1 to 4 HighSpeed PHYs followed by 1 or 2 SuperSpeed PHYs
|
||||
minItems: 1
|
||||
maxItems: 6
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- phys
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwc3.yaml#
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb@65a00000 {
|
||||
compatible = "socionext,uniphier-dwc3", "snps,dwc3";
|
||||
reg = <0x65a00000 0xcd00>;
|
||||
interrupt-names = "dwc_usb3";
|
||||
interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "ref", "bus_early", "suspend";
|
||||
clocks = <&sys_clk 12>, <&sys_clk 12>, <&sys_clk 12>;
|
||||
resets = <&usb0_rst 15>;
|
||||
phys = <&usb0_hsphy0>, <&usb0_hsphy1>,
|
||||
<&usb0_ssphy0>, <&usb0_ssphy1>;
|
||||
dr_mode = "host";
|
||||
};
|
||||
65
Documentation/devicetree/bindings/usb/wch,ch334.yaml
Normal file
65
Documentation/devicetree/bindings/usb/wch,ch334.yaml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/wch,ch334.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: WCH CH334/CH335 USB 2.0 Hub Controller
|
||||
|
||||
maintainers:
|
||||
- Chaoyi Chen <kernel@airkyi.com>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-hub.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb1a86,8091
|
||||
|
||||
reg: true
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO controlling the RESET# pin.
|
||||
|
||||
vdd33-supply:
|
||||
description:
|
||||
The regulator that provides 3.3V core power to the hub.
|
||||
|
||||
v5-supply:
|
||||
description:
|
||||
The regulator that provides 3.3V or 5V power to the hub.
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
patternProperties:
|
||||
'^port@':
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hub: hub@1 {
|
||||
compatible = "usb1a86,8091";
|
||||
reg = <1>;
|
||||
reset-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
|
||||
v5-supply = <&vcc_3v3>;
|
||||
vdd33-supply = <&vcc_3v3>;
|
||||
};
|
||||
};
|
||||
|
|
@ -368,14 +368,15 @@ Function-specific configfs interface
|
|||
The function name to use when creating the function directory is "midi".
|
||||
The MIDI function provides these attributes in its function directory:
|
||||
|
||||
=============== ====================================
|
||||
================ ====================================
|
||||
buflen MIDI buffer length
|
||||
id ID string for the USB MIDI adapter
|
||||
in_ports number of MIDI input ports
|
||||
index index value for the USB MIDI adapter
|
||||
out_ports number of MIDI output ports
|
||||
qlen USB read request queue length
|
||||
=============== ====================================
|
||||
interface_string USB AudioControl interface string
|
||||
================ ====================================
|
||||
|
||||
Testing the MIDI function
|
||||
-------------------------
|
||||
|
|
@ -686,6 +687,7 @@ The SOURCESINK function provides these attributes in its function directory:
|
|||
isoc_mult 0..2 (hs/ss only)
|
||||
isoc_maxburst 0..15 (ss only)
|
||||
bulk_buflen buffer length
|
||||
bulk_maxburst 0..15 (ss only)
|
||||
bulk_qlen depth of queue for bulk
|
||||
iso_qlen depth of queue for iso
|
||||
=============== ==================================
|
||||
|
|
|
|||
|
|
@ -10859,10 +10859,12 @@ P: Documentation/process/maintainer-soc-clean-dts.rst
|
|||
C: irc://irc.oftc.net/pixel6-kernel-dev
|
||||
F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
|
||||
F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml
|
||||
F: Documentation/devicetree/bindings/usb/google,lga-dwc3.yaml
|
||||
F: arch/arm64/boot/dts/exynos/google/
|
||||
F: drivers/clk/samsung/clk-gs101.c
|
||||
F: drivers/soc/samsung/gs101-pmu.c
|
||||
F: drivers/phy/samsung/phy-gs101-ufs.c
|
||||
F: drivers/usb/dwc3/dwc3-google.c
|
||||
F: include/dt-bindings/clock/google,gs101*
|
||||
K: [gG]oogle.?[tT]ensor
|
||||
|
||||
|
|
|
|||
|
|
@ -491,6 +491,7 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
|
|||
|
||||
cap->driver_data = cros_port;
|
||||
cap->ops = &cros_typec_usb_mode_ops;
|
||||
cap->no_mode_control = !typec->ap_driven_altmode;
|
||||
|
||||
cros_port->port = typec_register_port(dev, cap);
|
||||
if (IS_ERR(cros_port->port)) {
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ int tb_path_activate(struct tb_path *path)
|
|||
tb_dbg(path->tb, "%s path activation complete\n", path->name);
|
||||
return 0;
|
||||
err:
|
||||
tb_WARN(path->tb, "%s path activation failed\n", path->name);
|
||||
tb_warn(path->tb, "%s path activation failed: %d\n", path->name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/
|
|||
obj-$(CONFIG_USB_FHCI_HCD) += host/
|
||||
obj-$(CONFIG_USB_XHCI_HCD) += host/
|
||||
obj-$(CONFIG_USB_SL811_HCD) += host/
|
||||
obj-$(CONFIG_USB_ISP1362_HCD) += host/
|
||||
obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
obj-$(CONFIG_USB_FSL_USB2) += host/
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += host/
|
||||
|
|
|
|||
|
|
@ -551,7 +551,7 @@ int cdns_resume(struct cdns *cdns)
|
|||
}
|
||||
}
|
||||
|
||||
if (cdns->roles[cdns->role]->resume)
|
||||
if (!role_changed && cdns->roles[cdns->role]->resume)
|
||||
cdns->roles[cdns->role]->resume(cdns, power_lost);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -385,6 +385,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
const char *irq_name;
|
||||
|
||||
imx_platform_flag = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
|
|
@ -525,10 +526,16 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
|
||||
data->wakeup_irq = platform_get_irq_optional(pdev, 1);
|
||||
if (data->wakeup_irq > 0) {
|
||||
irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", pdata.name);
|
||||
if (!irq_name) {
|
||||
dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, data->wakeup_irq,
|
||||
NULL, ci_wakeup_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
pdata.name, data);
|
||||
irq_name, data);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -931,6 +931,13 @@ __acquires(hwep->lock)
|
|||
list_del_init(&hwreq->queue);
|
||||
hwreq->req.status = -ESHUTDOWN;
|
||||
|
||||
/* Unmap DMA and clean up bounce buffers before giving back */
|
||||
usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent,
|
||||
&hwreq->req, hwep->dir);
|
||||
|
||||
if (hwreq->sgt.sgl)
|
||||
sglist_do_debounce(hwreq, false);
|
||||
|
||||
if (hwreq->req.complete != NULL) {
|
||||
spin_unlock(hwep->lock);
|
||||
usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
|
||||
|
|
|
|||
|
|
@ -77,10 +77,6 @@
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Keep track of which host controller drivers are loaded */
|
||||
unsigned long usb_hcds_loaded;
|
||||
EXPORT_SYMBOL_GPL(usb_hcds_loaded);
|
||||
|
||||
/* host controllers we manage */
|
||||
DEFINE_IDR (usb_bus_idr);
|
||||
EXPORT_SYMBOL_GPL (usb_bus_idr);
|
||||
|
|
|
|||
|
|
@ -578,6 +578,7 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
|
|||
{
|
||||
switch (hsotg->dr_mode) {
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc2_force_mode(hsotg, true);
|
||||
/*
|
||||
* NOTE: This is required for some rockchip soc based
|
||||
* platforms on their host-only dwc2.
|
||||
|
|
|
|||
|
|
@ -211,4 +211,15 @@ config USB_DWC3_APPLE
|
|||
mode on these machines.
|
||||
Say 'Y' or 'M' if you have such device.
|
||||
|
||||
config USB_DWC3_GOOGLE
|
||||
tristate "Google Platform"
|
||||
help
|
||||
Support the DesignWare Core USB3 IP found on Google Tensor SoCs,
|
||||
starting with the G5 generation (Laguna). This driver includes
|
||||
support for hibernation in host mode.
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dwc3-google.ko.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -59,3 +59,4 @@ obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
|
|||
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
|
||||
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
|
||||
obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o
|
||||
obj-$(CONFIG_USB_DWC3_GOOGLE) += dwc3-google.o
|
||||
|
|
|
|||
|
|
@ -114,23 +114,23 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
|
||||
if (enable && !dwc->dis_u3_susphy_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
else
|
||||
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
|
||||
}
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
|
||||
if (enable && !dwc->dis_u2_susphy_quirk)
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
else
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_enable_susphy);
|
||||
|
|
@ -140,7 +140,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
|
|||
unsigned int hw_mode;
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
|
||||
/*
|
||||
* For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
|
||||
|
|
@ -155,10 +155,10 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
|
|||
|
||||
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
|
||||
reg |= DWC3_GCTL_PRTCAPDIR(mode);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GCTL, reg);
|
||||
|
||||
dwc->current_dr_role = mode;
|
||||
trace_dwc3_set_prtcap(mode);
|
||||
trace_dwc3_set_prtcap(dwc, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
|
||||
|
||||
|
|
@ -216,9 +216,9 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
|
||||
DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
|
||||
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
reg |= DWC3_GCTL_CORESOFTRESET;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GCTL, reg);
|
||||
|
||||
/*
|
||||
* Wait for internal clocks to synchronized. DWC_usb31 and
|
||||
|
|
@ -228,9 +228,9 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
*/
|
||||
msleep(100);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_CORESOFTRESET;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GCTL, reg);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
|
@ -254,9 +254,9 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
|
||||
|
||||
if (dwc->dis_split_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL3);
|
||||
reg |= DWC3_GUCTL3_SPLITDISABLE;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL3, reg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -306,11 +306,11 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
|
||||
dwc3_writel(dwc, DWC3_GDBGFIFOSPACE,
|
||||
DWC3_GDBGFIFOSPACE_NUM(dep->number) |
|
||||
DWC3_GDBGFIFOSPACE_TYPE(type));
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
|
||||
reg = dwc3_readl(dwc, DWC3_GDBGFIFOSPACE);
|
||||
|
||||
return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
|
||||
return 0;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg |= DWC3_DCTL_CSFTRST;
|
||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
|
|
@ -347,7 +347,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
retries = 10;
|
||||
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (!(reg & DWC3_DCTL_CSFTRST))
|
||||
goto done;
|
||||
|
||||
|
|
@ -387,12 +387,12 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
|
|||
if (dwc->fladj == 0)
|
||||
return;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
|
||||
reg = dwc3_readl(dwc, DWC3_GFLADJ);
|
||||
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
|
||||
if (dft != dwc->fladj) {
|
||||
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
|
||||
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
|
||||
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
|
||||
dwc3_writel(dwc, DWC3_GFLADJ, reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -424,10 +424,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
|
|||
return;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL);
|
||||
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
|
||||
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL, reg);
|
||||
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
|
||||
return;
|
||||
|
|
@ -455,7 +455,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
|
|||
*/
|
||||
decr = 480000000 / rate;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
|
||||
reg = dwc3_readl(dwc, DWC3_GFLADJ);
|
||||
reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
|
||||
& ~DWC3_GFLADJ_240MHZDECR
|
||||
& ~DWC3_GFLADJ_240MHZDECR_PLS1;
|
||||
|
|
@ -466,7 +466,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
|
|||
if (dwc->gfladj_refclk_lpm_sel)
|
||||
reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
|
||||
dwc3_writel(dwc, DWC3_GFLADJ, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -569,16 +569,16 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
|||
|
||||
evt = dwc->ev_buf;
|
||||
evt->lpos = 0;
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
|
||||
dwc3_writel(dwc, DWC3_GEVNTADRLO(0),
|
||||
lower_32_bits(evt->dma));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
|
||||
dwc3_writel(dwc, DWC3_GEVNTADRHI(0),
|
||||
upper_32_bits(evt->dma));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
|
||||
dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
|
||||
DWC3_GEVNTSIZ_SIZE(evt->length));
|
||||
|
||||
/* Clear any stale event */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
|
||||
reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
|
||||
dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -593,7 +593,7 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
|||
* Exynos platforms may not be able to access event buffer if the
|
||||
* controller failed to halt on dwc3_core_exit().
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
if (!(reg & DWC3_DSTS_DEVCTRLHLT))
|
||||
return;
|
||||
|
||||
|
|
@ -601,14 +601,14 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
|||
|
||||
evt->lpos = 0;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
|
||||
dwc3_writel(dwc, DWC3_GEVNTADRLO(0), 0);
|
||||
dwc3_writel(dwc, DWC3_GEVNTADRHI(0), 0);
|
||||
dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
|
||||
| DWC3_GEVNTSIZ_SIZE(0));
|
||||
|
||||
/* Clear any stale event */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
|
||||
reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
|
||||
dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
|
||||
}
|
||||
|
||||
static void dwc3_core_num_eps(struct dwc3 *dwc)
|
||||
|
|
@ -622,18 +622,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
|||
{
|
||||
struct dwc3_hwparams *parms = &dwc->hwparams;
|
||||
|
||||
parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0);
|
||||
parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1);
|
||||
parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2);
|
||||
parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3);
|
||||
parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4);
|
||||
parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5);
|
||||
parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
|
||||
parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
|
||||
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
|
||||
parms->hwparams0 = dwc3_readl(dwc, DWC3_GHWPARAMS0);
|
||||
parms->hwparams1 = dwc3_readl(dwc, DWC3_GHWPARAMS1);
|
||||
parms->hwparams2 = dwc3_readl(dwc, DWC3_GHWPARAMS2);
|
||||
parms->hwparams3 = dwc3_readl(dwc, DWC3_GHWPARAMS3);
|
||||
parms->hwparams4 = dwc3_readl(dwc, DWC3_GHWPARAMS4);
|
||||
parms->hwparams5 = dwc3_readl(dwc, DWC3_GHWPARAMS5);
|
||||
parms->hwparams6 = dwc3_readl(dwc, DWC3_GHWPARAMS6);
|
||||
parms->hwparams7 = dwc3_readl(dwc, DWC3_GHWPARAMS7);
|
||||
parms->hwparams8 = dwc3_readl(dwc, DWC3_GHWPARAMS8);
|
||||
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
|
||||
parms->hwparams9 = dwc3_readl(dwc, DWC3_GHWPARAMS9);
|
||||
}
|
||||
|
||||
static void dwc3_config_soc_bus(struct dwc3 *dwc)
|
||||
|
|
@ -641,10 +641,10 @@ static void dwc3_config_soc_bus(struct dwc3 *dwc)
|
|||
if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
|
||||
reg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
|
||||
reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
|
||||
reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
|
||||
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
|
||||
dwc3_writel(dwc, DWC3_GSBUSCFG0, reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -668,7 +668,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(index));
|
||||
|
||||
/*
|
||||
* Make sure UX_EXIT_PX is cleared as that causes issues with some
|
||||
|
|
@ -706,7 +706,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
|
|||
if (dwc->dis_del_phy_power_chg_quirk)
|
||||
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB3PIPECTL(index), reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -715,7 +715,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
|
||||
|
||||
/* Select the HS PHY interface */
|
||||
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
|
||||
|
|
@ -727,7 +727,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
|
|||
} else if (dwc->hsphy_interface &&
|
||||
!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
|
||||
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
|
||||
} else {
|
||||
/* Relying on default value. */
|
||||
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
|
||||
|
|
@ -777,7 +777,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
|
|||
if (dwc->ulpi_ext_vbus_drv)
|
||||
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -991,7 +991,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
|
||||
reg = dwc3_readl(dwc, DWC3_GSNPSID);
|
||||
dwc->ip = DWC3_GSNPS_ID(reg);
|
||||
if (dwc->ip == DWC4_IP)
|
||||
dwc->ip = DWC32_IP;
|
||||
|
|
@ -1000,8 +1000,8 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
|
|||
if (DWC3_IP_IS(DWC3)) {
|
||||
dwc->revision = reg;
|
||||
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
|
||||
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
|
||||
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
|
||||
dwc->revision = dwc3_readl(dwc, DWC3_VER_NUMBER);
|
||||
dwc->version_type = dwc3_readl(dwc, DWC3_VER_TYPE);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1015,7 +1015,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
|
|||
unsigned int hw_mode;
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
|
||||
power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
|
||||
|
|
@ -1093,7 +1093,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
|
|||
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GCTL, reg);
|
||||
}
|
||||
|
||||
static int dwc3_core_get_phy(struct dwc3 *dwc);
|
||||
|
|
@ -1113,7 +1113,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
|
||||
cfg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
|
||||
|
||||
/*
|
||||
* Handle property "snps,incr-burst-type-adjustment".
|
||||
|
|
@ -1188,7 +1188,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
|
|||
break;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
|
||||
dwc3_writel(dwc, DWC3_GSBUSCFG0, cfg);
|
||||
}
|
||||
|
||||
static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
|
||||
|
|
@ -1213,12 +1213,12 @@ static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
|
|||
* (3x or more) to be within the requirement.
|
||||
*/
|
||||
scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
if ((reg & DWC3_GCTL_PWRDNSCALE_MASK) < DWC3_GCTL_PWRDNSCALE(scale) ||
|
||||
(reg & DWC3_GCTL_PWRDNSCALE_MASK) > DWC3_GCTL_PWRDNSCALE(scale*3)) {
|
||||
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
|
||||
reg |= DWC3_GCTL_PWRDNSCALE(scale);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GCTL, reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1241,7 +1241,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
tx_maxburst = dwc->tx_max_burst_prd;
|
||||
|
||||
if (rx_thr_num && rx_maxburst) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
|
||||
reg |= DWC31_RXTHRNUMPKTSEL_PRD;
|
||||
|
||||
reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
|
||||
|
|
@ -1250,11 +1250,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
|
||||
reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
|
||||
}
|
||||
|
||||
if (tx_thr_num && tx_maxburst) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
|
||||
reg |= DWC31_TXTHRNUMPKTSEL_PRD;
|
||||
|
||||
reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
|
||||
|
|
@ -1263,7 +1263,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
|
||||
reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1274,7 +1274,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
|
||||
if (DWC3_IP_IS(DWC3)) {
|
||||
if (rx_thr_num && rx_maxburst) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
|
||||
reg |= DWC3_GRXTHRCFG_PKTCNTSEL;
|
||||
|
||||
reg &= ~DWC3_GRXTHRCFG_RXPKTCNT(~0);
|
||||
|
|
@ -1283,11 +1283,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
reg &= ~DWC3_GRXTHRCFG_MAXRXBURSTSIZE(~0);
|
||||
reg |= DWC3_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
|
||||
}
|
||||
|
||||
if (tx_thr_num && tx_maxburst) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
|
||||
reg |= DWC3_GTXTHRCFG_PKTCNTSEL;
|
||||
|
||||
reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0);
|
||||
|
|
@ -1296,11 +1296,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
reg &= ~DWC3_GTXTHRCFG_MAXTXBURSTSIZE(~0);
|
||||
reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
|
||||
}
|
||||
} else {
|
||||
if (rx_thr_num && rx_maxburst) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
|
||||
reg |= DWC31_GRXTHRCFG_PKTCNTSEL;
|
||||
|
||||
reg &= ~DWC31_GRXTHRCFG_RXPKTCNT(~0);
|
||||
|
|
@ -1309,11 +1309,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
reg &= ~DWC31_GRXTHRCFG_MAXRXBURSTSIZE(~0);
|
||||
reg |= DWC31_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
|
||||
}
|
||||
|
||||
if (tx_thr_num && tx_maxburst) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
|
||||
reg |= DWC31_GTXTHRCFG_PKTCNTSEL;
|
||||
|
||||
reg &= ~DWC31_GTXTHRCFG_TXPKTCNT(~0);
|
||||
|
|
@ -1322,7 +1322,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
|
|||
reg &= ~DWC31_GTXTHRCFG_MAXTXBURSTSIZE(~0);
|
||||
reg |= DWC31_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1345,7 +1345,7 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
* Write Linux Version Code to our GUID register so it's easy to figure
|
||||
* out which kernel version a bug was found.
|
||||
*/
|
||||
dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
|
||||
dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE);
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
|
|
@ -1410,9 +1410,9 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
* DWC_usb31 controller.
|
||||
*/
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL2);
|
||||
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL2, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1425,9 +1425,9 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
* setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
|
||||
*/
|
||||
if (DWC3_VER_IS(DWC3, 320A)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL2);
|
||||
reg &= ~DWC3_GUCTL2_LC_TIMER;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL2, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1440,13 +1440,13 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
* legacy ULPI PHYs.
|
||||
*/
|
||||
if (dwc->resume_hs_terminations) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL1);
|
||||
reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL1);
|
||||
|
||||
/*
|
||||
* Enable hardware control of sending remote wakeup
|
||||
|
|
@ -1481,7 +1481,7 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
dwc3_config_threshold(dwc);
|
||||
|
|
@ -1492,9 +1492,9 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i));
|
||||
reg = dwc3_readl(dwc, DWC3_LLUCTL(i));
|
||||
reg |= DWC3_LLUCTL_FORCE_GEN1;
|
||||
dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg);
|
||||
dwc3_writel(dwc, DWC3_LLUCTL(i), reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1513,9 +1513,9 @@ int dwc3_core_init(struct dwc3 *dwc)
|
|||
* function is available only from version 1.70a.
|
||||
*/
|
||||
if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL3);
|
||||
reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL3, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -2155,6 +2155,20 @@ static int dwc3_get_num_ports(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_vbus_draw_work(struct work_struct *work)
|
||||
{
|
||||
struct dwc3 *dwc = container_of(work, struct dwc3, vbus_draw_work);
|
||||
union power_supply_propval val = {0};
|
||||
int ret;
|
||||
|
||||
val.intval = 1000 * (dwc->current_limit);
|
||||
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
||||
|
||||
if (ret < 0)
|
||||
dev_dbg(dwc->dev, "Error (%d) setting vbus draw (%d mA)\n",
|
||||
ret, dwc->current_limit);
|
||||
}
|
||||
|
||||
static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
||||
{
|
||||
struct power_supply *usb_psy;
|
||||
|
|
@ -2169,6 +2183,7 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
|||
if (!usb_psy)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
|
||||
return usb_psy;
|
||||
}
|
||||
|
||||
|
|
@ -2395,8 +2410,10 @@ void dwc3_core_remove(struct dwc3 *dwc)
|
|||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
if (dwc->usb_psy)
|
||||
if (dwc->usb_psy) {
|
||||
cancel_work_sync(&dwc->vbus_draw_work);
|
||||
power_supply_put(dwc->usb_psy);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_core_remove);
|
||||
|
||||
|
|
@ -2439,9 +2456,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
|||
int ret;
|
||||
|
||||
if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
|
||||
dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
|
||||
dwc->susphy_state = (dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)) &
|
||||
DWC3_GUSB2PHYCFG_SUSPHY) ||
|
||||
(dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) &
|
||||
(dwc3_readl(dwc, DWC3_GUSB3PIPECTL(0)) &
|
||||
DWC3_GUSB3PIPECTL_SUSPHY);
|
||||
/*
|
||||
* TI AM62 platform requires SUSPHY to be
|
||||
|
|
@ -2471,10 +2488,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
|||
if (dwc->dis_u2_susphy_quirk ||
|
||||
dwc->dis_enblslpm_quirk) {
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
|
||||
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
|
||||
DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
|
||||
}
|
||||
|
||||
/* Give some time for USB2 PHY to suspend */
|
||||
|
|
@ -2534,14 +2551,14 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
|
|||
}
|
||||
/* Restore GUSB2PHYCFG bits that were modified in suspend */
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
|
||||
if (dwc->dis_u2_susphy_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
if (dwc->dis_enblslpm_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
|
||||
}
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
|
|
@ -2723,9 +2740,9 @@ void dwc3_pm_complete(struct dwc3 *dwc)
|
|||
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
|
||||
dwc->dis_split_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
|
||||
reg = dwc3_readl(dwc, DWC3_GUCTL3);
|
||||
reg |= DWC3_GUCTL3_SPLITDISABLE;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
|
||||
dwc3_writel(dwc, DWC3_GUCTL3, reg);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_pm_complete);
|
||||
|
|
|
|||
|
|
@ -165,10 +165,10 @@
|
|||
#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */
|
||||
|
||||
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
|
||||
#define DWC3_DEPCMDPAR2 0x00
|
||||
#define DWC3_DEPCMDPAR1 0x04
|
||||
#define DWC3_DEPCMDPAR0 0x08
|
||||
#define DWC3_DEPCMD 0x0c
|
||||
#define DWC3_DEPCMDPAR2(n) (DWC3_DEP_BASE(n) + 0x00)
|
||||
#define DWC3_DEPCMDPAR1(n) (DWC3_DEP_BASE(n) + 0x04)
|
||||
#define DWC3_DEPCMDPAR0(n) (DWC3_DEP_BASE(n) + 0x08)
|
||||
#define DWC3_DEPCMD(n) (DWC3_DEP_BASE(n) + 0x0c)
|
||||
|
||||
#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
|
||||
|
||||
|
|
@ -749,8 +749,6 @@ struct dwc3_ep {
|
|||
struct list_head pending_list;
|
||||
struct list_head started_list;
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
struct dwc3_trb *trb_pool;
|
||||
dma_addr_t trb_pool_dma;
|
||||
struct dwc3 *dwc;
|
||||
|
|
@ -1060,6 +1058,8 @@ struct dwc3_glue_ops {
|
|||
* @role_switch_default_mode: default operation mode of controller while
|
||||
* usb role is USB_ROLE_NONE.
|
||||
* @usb_psy: pointer to power supply interface.
|
||||
* @vbus_draw_work: Work to set the vbus drawing limit
|
||||
* @current_limit: How much current to draw from vbus, in milliAmperes.
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to array of USB2 PHYs
|
||||
|
|
@ -1246,6 +1246,8 @@ struct dwc3 {
|
|||
enum usb_dr_mode role_switch_default_mode;
|
||||
|
||||
struct power_supply *usb_psy;
|
||||
struct work_struct vbus_draw_work;
|
||||
unsigned int current_limit;
|
||||
|
||||
u32 fladj;
|
||||
u32 ref_clk_per;
|
||||
|
|
|
|||
|
|
@ -36,23 +36,19 @@
|
|||
#define dump_ep_register_set(n) \
|
||||
{ \
|
||||
.name = "DEPCMDPAR2("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR2, \
|
||||
.offset = DWC3_DEPCMDPAR2(n), \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMDPAR1("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR1, \
|
||||
.offset = DWC3_DEPCMDPAR1(n), \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMDPAR0("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR0, \
|
||||
.offset = DWC3_DEPCMDPAR0(n), \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMD("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMD, \
|
||||
.offset = DWC3_DEPCMD(n), \
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -300,14 +296,14 @@ static void dwc3_host_lsp(struct seq_file *s)
|
|||
|
||||
reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val);
|
||||
|
||||
if (dbc_enabled && sel < 256) {
|
||||
reg |= DWC3_GDBGLSPMUX_ENDBC;
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val);
|
||||
}
|
||||
}
|
||||
|
|
@ -320,8 +316,8 @@ static void dwc3_gadget_lsp(struct seq_file *s)
|
|||
|
||||
for (i = 0; i < 16; i++) {
|
||||
reg = DWC3_GDBGLSPMUX_DEVSELECT(i);
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
|
||||
reg = dwc3_readl(dwc, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg);
|
||||
}
|
||||
}
|
||||
|
|
@ -339,7 +335,7 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
|
|||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_GSTS);
|
||||
current_mode = DWC3_GSTS_CURMOD(reg);
|
||||
|
||||
switch (current_mode) {
|
||||
|
|
@ -410,7 +406,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
|
|||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
mode = DWC3_GCTL_PRTCAP(reg);
|
||||
|
|
@ -482,7 +478,7 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
|
|||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= DWC3_DCTL_TSTCTRL_MASK;
|
||||
reg >>= 1;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
@ -581,7 +577,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
seq_puts(s, "Not available\n");
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
@ -589,7 +585,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
state = DWC3_DSTS_USBLNKST(reg);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
|
||||
|
|
@ -643,14 +639,14 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
|||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
pm_runtime_put_sync(dwc->dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
|
||||
if (speed < DWC3_DSTS_SUPERSPEED &&
|
||||
|
|
@ -946,10 +942,10 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
|
||||
|
||||
lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0);
|
||||
upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1);
|
||||
lower_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO0);
|
||||
upper_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO1);
|
||||
|
||||
ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
|
||||
seq_printf(s, "0x%016llx\n", ep_info);
|
||||
|
|
|
|||
|
|
@ -18,25 +18,25 @@
|
|||
|
||||
static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
|
||||
{
|
||||
u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
|
||||
u32 reg = dwc3_readl(dwc, DWC3_OEVTEN);
|
||||
|
||||
reg &= ~(disable_mask);
|
||||
dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
|
||||
dwc3_writel(dwc, DWC3_OEVTEN, reg);
|
||||
}
|
||||
|
||||
static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
|
||||
{
|
||||
u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
|
||||
u32 reg = dwc3_readl(dwc, DWC3_OEVTEN);
|
||||
|
||||
reg |= (enable_mask);
|
||||
dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
|
||||
dwc3_writel(dwc, DWC3_OEVTEN, reg);
|
||||
}
|
||||
|
||||
static void dwc3_otg_clear_events(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
|
||||
u32 reg = dwc3_readl(dwc, DWC3_OEVT);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
|
||||
dwc3_writel(dwc, DWC3_OEVTEN, reg);
|
||||
}
|
||||
|
||||
#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
|
||||
|
|
@ -72,18 +72,18 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
|
|||
struct dwc3 *dwc = _dwc;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OEVT);
|
||||
reg = dwc3_readl(dwc, DWC3_OEVT);
|
||||
if (reg) {
|
||||
/* ignore non OTG events, we can't disable them in OEVTEN */
|
||||
if (!(reg & DWC3_OTG_ALL_EVENTS)) {
|
||||
dwc3_writel(dwc->regs, DWC3_OEVT, reg);
|
||||
dwc3_writel(dwc, DWC3_OEVT, reg);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
|
||||
!(reg & DWC3_OEVT_DEVICEMODE))
|
||||
dwc->otg_restart_host = true;
|
||||
dwc3_writel(dwc->regs, DWC3_OEVT, reg);
|
||||
dwc3_writel(dwc, DWC3_OEVT, reg);
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
|
|
@ -100,23 +100,23 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
|
|||
* the signal outputs sent to the PHY, the OTG FSM logic of the
|
||||
* core and also the resets to the VBUS filters inside the core.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_OCFG);
|
||||
reg |= DWC3_OCFG_SFTRSTMASK;
|
||||
dwc3_writel(dwc->regs, DWC3_OCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_OCFG, reg);
|
||||
|
||||
/* Disable hibernation for simplicity */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_GCTL, reg);
|
||||
|
||||
/*
|
||||
* Initialize OTG registers as per
|
||||
* Figure 11-4 OTG Driver Overall Programming Flow
|
||||
*/
|
||||
/* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_OCFG);
|
||||
reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
|
||||
dwc3_writel(dwc->regs, DWC3_OCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_OCFG, reg);
|
||||
/* OEVT = FFFF */
|
||||
dwc3_otg_clear_events(dwc);
|
||||
/* OEVTEN = 0 */
|
||||
|
|
@ -127,11 +127,11 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
|
|||
* OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
|
||||
* OCTL.HNPReq = 0
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_OCTL);
|
||||
reg |= DWC3_OCTL_PERIMODE;
|
||||
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
|
||||
DWC3_OCTL_HNPREQ);
|
||||
dwc3_writel(dwc->regs, DWC3_OCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_OCTL, reg);
|
||||
}
|
||||
|
||||
static int dwc3_otg_get_irq(struct dwc3 *dwc)
|
||||
|
|
@ -175,9 +175,9 @@ void dwc3_otg_init(struct dwc3 *dwc)
|
|||
/* GCTL.PrtCapDir=2'b11 */
|
||||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
|
||||
/* GUSB2PHYCFG0.SusPHY=0 */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
/* Initialize OTG registers */
|
||||
dwc3_otgregs_init(dwc);
|
||||
|
|
@ -203,17 +203,17 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
|
|||
* OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
|
||||
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_OCTL);
|
||||
reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
|
||||
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
|
||||
dwc3_writel(dwc->regs, DWC3_OCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_OCTL, reg);
|
||||
|
||||
/*
|
||||
* OCFG.DisPrtPwrCutoff = 0/1
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_OCFG);
|
||||
reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
|
||||
dwc3_writel(dwc->regs, DWC3_OCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_OCFG, reg);
|
||||
|
||||
/*
|
||||
* OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
|
||||
|
|
@ -229,15 +229,15 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
|
|||
|
||||
/* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
|
||||
if (!dwc->dis_u2_susphy_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
/* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_OCTL);
|
||||
reg |= DWC3_OCTL_PRTPWRCTL;
|
||||
dwc3_writel(dwc->regs, DWC3_OCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_OCTL, reg);
|
||||
}
|
||||
|
||||
/* should be called after Host controller driver is stopped */
|
||||
|
|
@ -258,9 +258,9 @@ static void dwc3_otg_host_exit(struct dwc3 *dwc)
|
|||
*/
|
||||
|
||||
/* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_OCTL);
|
||||
reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
|
||||
dwc3_writel(dwc->regs, DWC3_OCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_OCTL, reg);
|
||||
}
|
||||
|
||||
/* should be called before the gadget controller driver is started */
|
||||
|
|
@ -274,27 +274,27 @@ static void dwc3_otg_device_init(struct dwc3 *dwc)
|
|||
* OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
|
||||
* but we keep them 0 for simple dual-role operation.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_OCFG);
|
||||
/* OCFG.OTGSftRstMsk = 0/1 */
|
||||
reg |= DWC3_OCFG_SFTRSTMASK;
|
||||
dwc3_writel(dwc->regs, DWC3_OCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_OCFG, reg);
|
||||
/*
|
||||
* OCTL.PeriMode = 1
|
||||
* OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
|
||||
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_OCTL);
|
||||
reg |= DWC3_OCTL_PERIMODE;
|
||||
reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
|
||||
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
|
||||
dwc3_writel(dwc->regs, DWC3_OCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_OCTL, reg);
|
||||
/* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
|
||||
dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN);
|
||||
/* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
|
||||
if (!dwc->dis_u2_susphy_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
/* GCTL.GblHibernationEn = 0. Already 0. */
|
||||
}
|
||||
|
|
@ -319,10 +319,10 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc)
|
|||
DWC3_OEVTEN_BDEVBHOSTENDEN);
|
||||
|
||||
/* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_OCTL);
|
||||
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
|
||||
reg |= DWC3_OCTL_PERIMODE;
|
||||
dwc3_writel(dwc->regs, DWC3_OCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_OCTL, reg);
|
||||
}
|
||||
|
||||
void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
||||
|
|
@ -341,7 +341,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
|||
return;
|
||||
|
||||
if (!ignore_idstatus) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_OSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_OSTS);
|
||||
id = !!(reg & DWC3_OSTS_CONIDSTS);
|
||||
|
||||
dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
|
||||
|
|
@ -381,6 +381,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
|||
dwc3_otgregs_init(dwc);
|
||||
dwc3_otg_host_init(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
dwc3_pre_set_role(dwc, USB_ROLE_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to initialize host\n");
|
||||
|
|
@ -406,6 +407,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
|||
otg_set_vbus(dwc->usb2_phy->otg, false);
|
||||
if (dwc->usb2_generic_phy[0])
|
||||
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
|
||||
dwc3_pre_set_role(dwc, USB_ROLE_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to initialize peripheral\n");
|
||||
|
|
@ -433,10 +435,12 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
|
|||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
|
||||
u32 mode = event ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE;
|
||||
enum usb_role role = mode == DWC3_GCTL_PRTCAP_HOST ?
|
||||
USB_ROLE_HOST : USB_ROLE_DEVICE;
|
||||
|
||||
dwc3_set_mode(dwc, event ?
|
||||
DWC3_GCTL_PRTCAP_HOST :
|
||||
DWC3_GCTL_PRTCAP_DEVICE);
|
||||
dwc3_pre_set_role(dwc, role);
|
||||
dwc3_set_mode(dwc, mode);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
|
|
|||
626
drivers/usb/dwc3/dwc3-google.c
Normal file
626
drivers/usb/dwc3/dwc3-google.c
Normal file
|
|
@ -0,0 +1,626 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dwc3-google.c - Google DWC3 Specific Glue Layer
|
||||
*
|
||||
* Copyright (c) 2025, Google LLC
|
||||
* Author: Roy Luo <royluo@google.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include "core.h"
|
||||
#include "glue.h"
|
||||
|
||||
/* HOST CFG registers */
|
||||
#define HC_STATUS_OFFSET 0x0
|
||||
#define HC_STATUS_CURRENT_POWER_STATE_U2PMU GENMASK(1, 0)
|
||||
#define HC_STATUS_CURRENT_POWER_STATE_U3PMU GENMASK(4, 3)
|
||||
|
||||
#define HOST_CFG1_OFFSET 0x4
|
||||
#define HOST_CFG1_PME_EN BIT(3)
|
||||
#define HOST_CFG1_PM_POWER_STATE_REQUEST GENMASK(5, 4)
|
||||
#define HOST_CFG1_PM_POWER_STATE_D0 0x0
|
||||
#define HOST_CFG1_PM_POWER_STATE_D3 0x3
|
||||
|
||||
/* USBINT registers */
|
||||
#define USBINT_CFG1_OFFSET 0x0
|
||||
#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK BIT(2)
|
||||
#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK BIT(3)
|
||||
#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN BIT(8)
|
||||
#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN BIT(9)
|
||||
#define USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR BIT(14)
|
||||
#define USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR BIT(15)
|
||||
|
||||
#define USBINT_STATUS_OFFSET 0x4
|
||||
#define USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW BIT(2)
|
||||
#define USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW BIT(3)
|
||||
|
||||
#define USBCS_TOP_CTRL_CFG1_OFFSET 0xc
|
||||
#define USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE BIT(5)
|
||||
|
||||
#define DWC3_GOOGLE_MAX_RESETS 4
|
||||
|
||||
struct dwc3_google {
|
||||
struct device *dev;
|
||||
struct dwc3 dwc;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
struct reset_control_bulk_data rsts[DWC3_GOOGLE_MAX_RESETS];
|
||||
int num_rsts;
|
||||
struct reset_control *non_sticky_rst;
|
||||
struct device *usb_psw_pd;
|
||||
struct device_link *usb_psw_pd_dl;
|
||||
struct notifier_block usb_psw_pd_nb;
|
||||
struct device *usb_top_pd;
|
||||
struct device_link *usb_top_pd_dl;
|
||||
struct regmap *usb_cfg_regmap;
|
||||
unsigned int host_cfg_offset;
|
||||
unsigned int usbint_cfg_offset;
|
||||
int hs_pme_irq;
|
||||
int ss_pme_irq;
|
||||
bool is_usb2only;
|
||||
bool is_hibernation;
|
||||
};
|
||||
|
||||
#define to_dwc3_google(d) container_of_const((d), struct dwc3_google, dwc)
|
||||
|
||||
static int dwc3_google_rst_init(struct dwc3_google *google)
|
||||
{
|
||||
int ret;
|
||||
|
||||
google->num_rsts = 4;
|
||||
google->rsts[0].id = "non_sticky";
|
||||
google->rsts[1].id = "sticky";
|
||||
google->rsts[2].id = "drd_bus";
|
||||
google->rsts[3].id = "top";
|
||||
|
||||
ret = devm_reset_control_bulk_get_exclusive(google->dev,
|
||||
google->num_rsts,
|
||||
google->rsts);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
google->non_sticky_rst = google->rsts[0].rstc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
regmap_read(google->usb_cfg_regmap,
|
||||
google->host_cfg_offset + HOST_CFG1_OFFSET, ®);
|
||||
|
||||
reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST;
|
||||
reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) |
|
||||
HOST_CFG1_PME_EN);
|
||||
regmap_write(google->usb_cfg_regmap,
|
||||
google->host_cfg_offset + HOST_CFG1_OFFSET, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(google->usb_cfg_regmap,
|
||||
google->host_cfg_offset + HC_STATUS_OFFSET, reg,
|
||||
(FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U2PMU,
|
||||
reg) == state &&
|
||||
FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U3PMU,
|
||||
reg) == state),
|
||||
10, 10000);
|
||||
|
||||
if (ret)
|
||||
dev_err(google->dev, "failed to set PMU state %d\n", state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear pme interrupts and report their status.
|
||||
* The hardware requires write-1 then write-0 sequence to clear the interrupt bits.
|
||||
*/
|
||||
static u32 dwc3_google_clear_pme_irqs(struct dwc3_google *google)
|
||||
{
|
||||
u32 irq_status, reg_set, reg_clear;
|
||||
|
||||
regmap_read(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_STATUS_OFFSET, &irq_status);
|
||||
|
||||
irq_status &= (USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW |
|
||||
USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW);
|
||||
if (!irq_status)
|
||||
return irq_status;
|
||||
|
||||
regmap_read(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, ®_set);
|
||||
|
||||
reg_clear = reg_set;
|
||||
if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW) {
|
||||
reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR;
|
||||
reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR;
|
||||
}
|
||||
if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW) {
|
||||
reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR;
|
||||
reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR;
|
||||
}
|
||||
|
||||
regmap_write(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_set);
|
||||
regmap_write(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_clear);
|
||||
|
||||
return irq_status;
|
||||
}
|
||||
|
||||
static void dwc3_google_enable_pme_irq(struct dwc3_google *google)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
regmap_read(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, ®);
|
||||
reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK |
|
||||
USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK);
|
||||
reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN |
|
||||
USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN);
|
||||
regmap_write(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg);
|
||||
|
||||
enable_irq(google->hs_pme_irq);
|
||||
enable_irq(google->ss_pme_irq);
|
||||
enable_irq_wake(google->hs_pme_irq);
|
||||
enable_irq_wake(google->ss_pme_irq);
|
||||
}
|
||||
|
||||
static void dwc3_google_disable_pme_irq(struct dwc3_google *google)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
regmap_read(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, ®);
|
||||
reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN |
|
||||
USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN);
|
||||
reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK |
|
||||
USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK);
|
||||
regmap_write(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg);
|
||||
|
||||
disable_irq_wake(google->hs_pme_irq);
|
||||
disable_irq_wake(google->ss_pme_irq);
|
||||
disable_irq_nosync(google->hs_pme_irq);
|
||||
disable_irq_nosync(google->ss_pme_irq);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_google_resume_irq(int irq, void *data)
|
||||
{
|
||||
struct dwc3_google *google = data;
|
||||
struct dwc3 *dwc = &google->dwc;
|
||||
u32 irq_status, dr_role;
|
||||
|
||||
irq_status = dwc3_google_clear_pme_irqs(google);
|
||||
dr_role = dwc->current_dr_role;
|
||||
|
||||
if (!irq_status || !google->is_hibernation ||
|
||||
dr_role != DWC3_GCTL_PRTCAP_HOST) {
|
||||
dev_dbg(google->dev, "spurious pme irq %d, hibernation %d, dr_role %u\n",
|
||||
irq, google->is_hibernation, dr_role);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (dwc->xhci)
|
||||
pm_runtime_resume(&dwc->xhci->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dwc3_google_request_irq(struct dwc3_google *google, struct platform_device *pdev,
|
||||
const char *irq_name, const char *req_name)
|
||||
{
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, irq_name);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
irq_set_status_flags(irq, IRQ_NOAUTOEN);
|
||||
ret = devm_request_threaded_irq(google->dev, irq, NULL,
|
||||
dwc3_google_resume_irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
req_name, google);
|
||||
if (ret < 0) {
|
||||
dev_err(google->dev, "failed to request irq %s\n", req_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static int dwc3_google_usb_psw_pd_notifier(struct notifier_block *nb, unsigned long action, void *d)
|
||||
{
|
||||
struct dwc3_google *google = container_of(nb, struct dwc3_google, usb_psw_pd_nb);
|
||||
int ret;
|
||||
|
||||
if (!google->is_hibernation)
|
||||
return NOTIFY_OK;
|
||||
|
||||
if (action == GENPD_NOTIFY_OFF) {
|
||||
dev_dbg(google->dev, "enter D3 power state\n");
|
||||
dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D3);
|
||||
ret = reset_control_assert(google->non_sticky_rst);
|
||||
if (ret)
|
||||
dev_err(google->dev, "non sticky reset assert failed: %d\n", ret);
|
||||
} else if (action == GENPD_NOTIFY_ON) {
|
||||
dev_dbg(google->dev, "enter D0 power state\n");
|
||||
dwc3_google_clear_pme_irqs(google);
|
||||
ret = reset_control_deassert(google->non_sticky_rst);
|
||||
if (ret)
|
||||
dev_err(google->dev, "non sticky reset deassert failed: %d\n", ret);
|
||||
dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D0);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void dwc3_google_pm_domain_deinit(struct dwc3_google *google)
|
||||
{
|
||||
if (google->usb_top_pd_dl)
|
||||
device_link_del(google->usb_top_pd_dl);
|
||||
|
||||
if (!IS_ERR_OR_NULL(google->usb_top_pd)) {
|
||||
device_set_wakeup_capable(google->usb_top_pd, false);
|
||||
dev_pm_domain_detach(google->usb_top_pd, true);
|
||||
}
|
||||
|
||||
if (google->usb_psw_pd_dl)
|
||||
device_link_del(google->usb_psw_pd_dl);
|
||||
|
||||
if (!IS_ERR_OR_NULL(google->usb_psw_pd)) {
|
||||
dev_pm_genpd_remove_notifier(google->usb_psw_pd);
|
||||
dev_pm_domain_detach(google->usb_psw_pd, true);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_google_pm_domain_init(struct dwc3_google *google)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Establish PM RUNTIME link between dwc dev and its power domain usb_psw_pd,
|
||||
* register notifier block to handle hibernation.
|
||||
*/
|
||||
google->usb_psw_pd = dev_pm_domain_attach_by_name(google->dev, "psw");
|
||||
if (IS_ERR_OR_NULL(google->usb_psw_pd)) {
|
||||
dev_err(google->dev, "failed to get psw pd");
|
||||
ret = google->usb_psw_pd ? PTR_ERR(google->usb_psw_pd) : -ENODATA;
|
||||
return ret;
|
||||
}
|
||||
|
||||
google->usb_psw_pd_nb.notifier_call = dwc3_google_usb_psw_pd_notifier;
|
||||
ret = dev_pm_genpd_add_notifier(google->usb_psw_pd, &google->usb_psw_pd_nb);
|
||||
if (ret) {
|
||||
dev_err(google->dev, "failed to add psw pd notifier");
|
||||
goto err;
|
||||
}
|
||||
|
||||
google->usb_psw_pd_dl = device_link_add(google->dev, google->usb_psw_pd,
|
||||
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (!google->usb_psw_pd_dl) {
|
||||
dev_err(google->usb_psw_pd, "failed to add device link");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_top_pd is the parent power domain of usb_psw_pd. Keeping usb_top_pd on
|
||||
* while usb_psw_pd is off places the controller in a power-gated state,
|
||||
* essential for hibernation. Acquire a handle to usb_top_pd and sets it as
|
||||
* wakeup-capable to allow the domain to be left on during system suspend.
|
||||
*/
|
||||
google->usb_top_pd = dev_pm_domain_attach_by_name(google->dev, "top");
|
||||
if (IS_ERR_OR_NULL(google->usb_top_pd)) {
|
||||
dev_err(google->dev, "failed to get top pd");
|
||||
ret = google->usb_top_pd ? PTR_ERR(google->usb_top_pd) : -ENODATA;
|
||||
goto err;
|
||||
}
|
||||
device_set_wakeup_capable(google->usb_top_pd, true);
|
||||
|
||||
google->usb_top_pd_dl = device_link_add(google->dev, google->usb_top_pd,
|
||||
DL_FLAG_STATELESS);
|
||||
if (!google->usb_top_pd_dl) {
|
||||
dev_err(google->usb_top_pd, "failed to add device link");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dwc3_google_pm_domain_deinit(google);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_google_program_usb2only(struct dwc3_google *google)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
regmap_read(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, ®);
|
||||
reg |= USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE;
|
||||
regmap_write(google->usb_cfg_regmap,
|
||||
google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, reg);
|
||||
}
|
||||
|
||||
static int dwc3_google_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_probe_data probe_data = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_google *google;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
u32 args[2];
|
||||
|
||||
google = devm_kzalloc(&pdev->dev, sizeof(*google), GFP_KERNEL);
|
||||
if (!google)
|
||||
return -ENOMEM;
|
||||
|
||||
google->dev = &pdev->dev;
|
||||
|
||||
ret = dwc3_google_pm_domain_init(google);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to init pdom\n");
|
||||
|
||||
google->usb_cfg_regmap =
|
||||
syscon_regmap_lookup_by_phandle_args(dev->of_node,
|
||||
"google,usb-cfg-csr",
|
||||
ARRAY_SIZE(args), args);
|
||||
if (IS_ERR(google->usb_cfg_regmap)) {
|
||||
return dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap),
|
||||
"invalid usb cfg csr\n");
|
||||
}
|
||||
|
||||
google->host_cfg_offset = args[0];
|
||||
google->usbint_cfg_offset = args[1];
|
||||
|
||||
if (device_property_match_string(dev, "phy-names", "usb3-phy") < 0) {
|
||||
google->is_usb2only = true;
|
||||
dwc3_google_program_usb2only(google);
|
||||
}
|
||||
|
||||
ret = devm_clk_bulk_get_all_enabled(dev, &google->clks);
|
||||
if (ret < 0) {
|
||||
ret = dev_err_probe(dev, ret, "failed to get and enable clks\n");
|
||||
goto err_deinit_pdom;
|
||||
}
|
||||
google->num_clks = ret;
|
||||
|
||||
ret = dwc3_google_rst_init(google);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "failed to get resets\n");
|
||||
goto err_deinit_pdom;
|
||||
}
|
||||
|
||||
ret = reset_control_bulk_deassert(google->num_rsts, google->rsts);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "failed to deassert rsts\n");
|
||||
goto err_deinit_pdom;
|
||||
}
|
||||
|
||||
ret = dwc3_google_request_irq(google, pdev, "hs_pme", "USB HS wakeup");
|
||||
if (ret < 0) {
|
||||
ret = dev_err_probe(dev, ret, "failed to request hs pme irq");
|
||||
goto err_reset_assert;
|
||||
}
|
||||
google->hs_pme_irq = ret;
|
||||
|
||||
ret = dwc3_google_request_irq(google, pdev, "ss_pme", "USB SS wakeup");
|
||||
if (ret < 0) {
|
||||
ret = dev_err_probe(dev, ret, "failed to request ss pme irq");
|
||||
goto err_reset_assert;
|
||||
}
|
||||
google->ss_pme_irq = ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = dev_err_probe(dev, -ENODEV, "invalid memory\n");
|
||||
goto err_reset_assert;
|
||||
}
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
|
||||
google->dwc.dev = dev;
|
||||
probe_data.dwc = &google->dwc;
|
||||
probe_data.res = res;
|
||||
probe_data.ignore_clocks_and_resets = true;
|
||||
ret = dwc3_core_probe(&probe_data);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
|
||||
goto err_reset_assert;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset_assert:
|
||||
reset_control_bulk_assert(google->num_rsts, google->rsts);
|
||||
|
||||
err_deinit_pdom:
|
||||
dwc3_google_pm_domain_deinit(google);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_google_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct dwc3_google *google = to_dwc3_google(dwc);
|
||||
|
||||
dwc3_core_remove(&google->dwc);
|
||||
|
||||
reset_control_bulk_assert(google->num_rsts, google->rsts);
|
||||
|
||||
dwc3_google_pm_domain_deinit(google);
|
||||
}
|
||||
|
||||
static int dwc3_google_suspend(struct dwc3_google *google, pm_message_t msg)
|
||||
{
|
||||
if (pm_runtime_suspended(google->dev))
|
||||
return 0;
|
||||
|
||||
if (google->dwc.current_dr_role == DWC3_GCTL_PRTCAP_HOST) {
|
||||
/*
|
||||
* Follow dwc3_suspend_common() guidelines for deciding between
|
||||
* a full teardown and hibernation.
|
||||
*/
|
||||
if (PMSG_IS_AUTO(msg) || device_may_wakeup(google->dev)) {
|
||||
dev_dbg(google->dev, "enter hibernation");
|
||||
pm_runtime_get_sync(google->usb_top_pd);
|
||||
device_wakeup_enable(google->usb_top_pd);
|
||||
dwc3_google_enable_pme_irq(google);
|
||||
google->is_hibernation = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset_control_bulk_assert(google->num_rsts, google->rsts);
|
||||
clk_bulk_disable_unprepare(google->num_clks, google->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_google_resume(struct dwc3_google *google, pm_message_t msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (google->is_hibernation) {
|
||||
dev_dbg(google->dev, "exit hibernation");
|
||||
dwc3_google_disable_pme_irq(google);
|
||||
device_wakeup_disable(google->usb_top_pd);
|
||||
pm_runtime_put_sync(google->usb_top_pd);
|
||||
google->is_hibernation = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (google->is_usb2only)
|
||||
dwc3_google_program_usb2only(google);
|
||||
|
||||
ret = clk_bulk_prepare_enable(google->num_clks, google->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_bulk_deassert(google->num_rsts, google->rsts);
|
||||
if (ret) {
|
||||
clk_bulk_disable_unprepare(google->num_clks, google->clks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_google_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_google *google = to_dwc3_google(dwc);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_pm_suspend(&google->dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc3_google_suspend(google, PMSG_SUSPEND);
|
||||
}
|
||||
|
||||
static int dwc3_google_pm_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_google *google = to_dwc3_google(dwc);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_google_resume(google, PMSG_RESUME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc3_pm_resume(&google->dwc);
|
||||
}
|
||||
|
||||
static void dwc3_google_complete(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_pm_complete(dwc);
|
||||
}
|
||||
|
||||
static int dwc3_google_prepare(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
|
||||
return dwc3_pm_prepare(dwc);
|
||||
}
|
||||
|
||||
static int dwc3_google_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_google *google = to_dwc3_google(dwc);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_runtime_suspend(&google->dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc3_google_suspend(google, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
|
||||
static int dwc3_google_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_google *google = to_dwc3_google(dwc);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_google_resume(google, PMSG_AUTO_RESUME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc3_runtime_resume(&google->dwc);
|
||||
}
|
||||
|
||||
static int dwc3_google_runtime_idle(struct device *dev)
|
||||
{
|
||||
return dwc3_runtime_idle(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_google_dev_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(dwc3_google_pm_suspend, dwc3_google_pm_resume)
|
||||
RUNTIME_PM_OPS(dwc3_google_runtime_suspend, dwc3_google_runtime_resume,
|
||||
dwc3_google_runtime_idle)
|
||||
.complete = pm_sleep_ptr(dwc3_google_complete),
|
||||
.prepare = pm_sleep_ptr(dwc3_google_prepare),
|
||||
};
|
||||
|
||||
static const struct of_device_id dwc3_google_of_match[] = {
|
||||
{ .compatible = "google,lga-dwc3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_google_of_match);
|
||||
|
||||
static struct platform_driver dwc3_google_driver = {
|
||||
.probe = dwc3_google_probe,
|
||||
.remove = dwc3_google_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-google",
|
||||
.pm = pm_ptr(&dwc3_google_dev_pm_ops),
|
||||
.of_match_table = dwc3_google_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_google_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("DesignWare DWC3 Google Glue Driver");
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
struct dwc3_imx8mp {
|
||||
struct device *dev;
|
||||
struct platform_device *dwc3;
|
||||
struct platform_device *dwc3_pdev;
|
||||
void __iomem *hsio_blk_base;
|
||||
void __iomem *glue_base;
|
||||
struct clk *hsio_clk;
|
||||
|
|
@ -100,7 +100,7 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
|
|||
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
|
||||
pm_message_t msg)
|
||||
{
|
||||
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
|
||||
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev);
|
||||
u32 val;
|
||||
|
||||
if (!dwc3)
|
||||
|
|
@ -142,7 +142,7 @@ static const struct software_node dwc3_imx8mp_swnode = {
|
|||
static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
|
||||
{
|
||||
struct dwc3_imx8mp *dwc3_imx = _dwc3_imx;
|
||||
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
|
||||
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev);
|
||||
|
||||
if (!dwc3_imx->pm_suspended)
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -158,11 +158,31 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
|
||||
{
|
||||
if (role == USB_ROLE_HOST)
|
||||
/*
|
||||
* For xhci host, we need disable dwc core auto
|
||||
* suspend, because during this auto suspend delay(5s),
|
||||
* xhci host RUN_STOP is cleared and wakeup is not
|
||||
* enabled, if device is inserted, xhci host can't
|
||||
* response the connection.
|
||||
*/
|
||||
pm_runtime_dont_use_autosuspend(dwc->dev);
|
||||
else
|
||||
pm_runtime_use_autosuspend(dwc->dev);
|
||||
}
|
||||
|
||||
struct dwc3_glue_ops dwc3_imx_glue_ops = {
|
||||
.pre_set_role = dwc3_imx_pre_set_role,
|
||||
};
|
||||
|
||||
static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct dwc3_imx8mp *dwc3_imx;
|
||||
struct dwc3 *dwc3;
|
||||
struct resource *res;
|
||||
int err, irq;
|
||||
|
||||
|
|
@ -233,13 +253,24 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
goto remove_swnode;
|
||||
}
|
||||
|
||||
dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
|
||||
if (!dwc3_imx->dwc3) {
|
||||
dwc3_imx->dwc3_pdev = of_find_device_by_node(dwc3_np);
|
||||
if (!dwc3_imx->dwc3_pdev) {
|
||||
dev_err(dev, "failed to get dwc3 platform device\n");
|
||||
err = -ENODEV;
|
||||
goto depopulate;
|
||||
}
|
||||
|
||||
dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev);
|
||||
if (!dwc3) {
|
||||
err = dev_err_probe(dev, -EPROBE_DEFER, "failed to get dwc3 platform data\n");
|
||||
goto depopulate;
|
||||
}
|
||||
|
||||
dwc3->glue_ops = &dwc3_imx_glue_ops;
|
||||
|
||||
if (dwc3->dr_mode == USB_DR_MODE_HOST)
|
||||
pm_runtime_dont_use_autosuspend(dwc3->dev);
|
||||
|
||||
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
|
||||
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
|
||||
if (err) {
|
||||
|
|
@ -253,7 +284,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
put_dwc3:
|
||||
put_device(&dwc3_imx->dwc3->dev);
|
||||
put_device(&dwc3_imx->dwc3_pdev->dev);
|
||||
depopulate:
|
||||
of_platform_depopulate(dev);
|
||||
remove_swnode:
|
||||
|
|
@ -270,7 +301,7 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev)
|
|||
struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
put_device(&dwc3_imx->dwc3->dev);
|
||||
put_device(&dwc3_imx->dwc3_pdev->dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
of_platform_depopulate(dev);
|
||||
|
|
@ -296,7 +327,7 @@ static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
|
|||
|
||||
static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
|
||||
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev);
|
||||
int ret = 0;
|
||||
|
||||
if (!dwc3_imx->pm_suspended)
|
||||
|
|
|
|||
|
|
@ -132,21 +132,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
|||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following core resets are not required unless a USB3 PHY
|
||||
* is used, and the subsequent register settings are not required
|
||||
* unless a core reset is performed (they should be set properly
|
||||
* by the first-stage boot loader, but may be reverted by a core
|
||||
* reset). They may also break the configuration if USB3 is actually
|
||||
* in use but the usb3-phy entry is missing from the device tree.
|
||||
* Therefore, skip these operations in this case.
|
||||
*/
|
||||
if (!priv_data->usb3_phy) {
|
||||
/* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
|
||||
writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
|
||||
goto skip_usb3_phy;
|
||||
}
|
||||
|
||||
crst = devm_reset_control_get_exclusive(dev, "usb_crst");
|
||||
if (IS_ERR(crst)) {
|
||||
ret = PTR_ERR(crst);
|
||||
|
|
@ -171,6 +156,14 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
|||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Asserting the core resets is not required unless a USB3 PHY is used.
|
||||
* They may also break the configuration if USB3 is actually in use but
|
||||
* the usb3-phy entry is missing from the device tree. Therefore, skip
|
||||
* a full reset cycle and just deassert the resets if the phy is
|
||||
* absent.
|
||||
*/
|
||||
if (priv_data->usb3_phy) {
|
||||
ret = reset_control_assert(crst);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to assert core reset\n");
|
||||
|
|
@ -188,6 +181,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
|||
dev_err(dev, "Failed to assert APB reset\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = phy_init(priv_data->usb3_phy);
|
||||
if (ret < 0) {
|
||||
|
|
@ -201,11 +195,15 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (priv_data->usb3_phy) {
|
||||
/* Set PIPE Power Present signal in FPD Power Present Register*/
|
||||
writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
|
||||
|
||||
/* Set the PIPE Clock Select bit in FPD PIPE Clock register */
|
||||
writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
|
||||
} else {
|
||||
/* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
|
||||
writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(crst);
|
||||
if (ret < 0) {
|
||||
|
|
@ -225,7 +223,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
|||
goto err;
|
||||
}
|
||||
|
||||
skip_usb3_phy:
|
||||
/* ulpi reset via gpio-modepin or gpio-framework driver */
|
||||
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(reset_gpio)) {
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
|||
|
||||
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
|
|
@ -417,12 +417,12 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
|
|||
if (set && dwc->dis_u1_entry_quirk)
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (set)
|
||||
reg |= DWC3_DCTL_INITU1ENA;
|
||||
else
|
||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -441,12 +441,12 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
|
|||
if (set && dwc->dis_u2_entry_quirk)
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (set)
|
||||
reg |= DWC3_DCTL_INITU2ENA;
|
||||
else
|
||||
reg &= ~DWC3_DCTL_INITU2ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -612,10 +612,10 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
|
||||
reg |= DWC3_DCFG_DEVADDR(addr);
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
|
||||
if (addr)
|
||||
usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
|
||||
|
|
@ -672,12 +672,12 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
* Enable transition to U1/U2 state when
|
||||
* nothing is pending from application.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (!dwc->dis_u1_entry_quirk)
|
||||
reg |= DWC3_DCTL_ACCEPTU1ENA;
|
||||
if (!dwc->dis_u2_entry_quirk)
|
||||
reg |= DWC3_DCTL_ACCEPTU2ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -717,7 +717,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
|||
dwc->u2sel = le16_to_cpu(timing.u2sel);
|
||||
dwc->u2pel = le16_to_cpu(timing.u2pel);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
param = dwc->u2pel;
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
|
|
@ -833,7 +833,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
|||
if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
|
||||
goto out;
|
||||
|
||||
trace_dwc3_ctrl_req(ctrl);
|
||||
trace_dwc3_ctrl_req(dwc, ctrl);
|
||||
|
||||
len = le16_to_cpu(ctrl->wLength);
|
||||
if (!len) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
|
||||
switch (mode) {
|
||||
|
|
@ -73,7 +73,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
|
||||
return DWC3_DSTS_USBLNKST(reg);
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|||
*/
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
|
||||
while (--retries) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
if (reg & DWC3_DSTS_DCNRD)
|
||||
udelay(5);
|
||||
else
|
||||
|
|
@ -108,15 +108,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||
|
||||
/* set no action before sending new link state change */
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
|
||||
/* set requested state */
|
||||
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
|
||||
/*
|
||||
* The following code is racy when called from dwc3_gadget_wakeup,
|
||||
|
|
@ -128,7 +128,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|||
/* wait for a change in DSTS */
|
||||
retries = 10000;
|
||||
while (--retries) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
|
||||
if (DWC3_DSTS_USBLNKST(reg) == state)
|
||||
return 0;
|
||||
|
|
@ -260,11 +260,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
|
|||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
||||
dwc3_writel(dwc, DWC3_DGCMDPAR, param);
|
||||
dwc3_writel(dwc, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
||||
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
|
||||
reg = dwc3_readl(dwc, DWC3_DGCMD);
|
||||
if (!(reg & DWC3_DGCMD_CMDACT)) {
|
||||
status = DWC3_DGCMD_STATUS(reg);
|
||||
if (status)
|
||||
|
|
@ -278,7 +278,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
|
|||
status = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
trace_dwc3_gadget_generic_cmd(cmd, param, status);
|
||||
trace_dwc3_gadget_generic_cmd(dwc, cmd, param, status);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -320,6 +320,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
|
||||
int cmd_status = 0;
|
||||
int ret = -EINVAL;
|
||||
u8 epnum = dep->number;
|
||||
|
||||
/*
|
||||
* When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
|
||||
|
|
@ -333,7 +334,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
*/
|
||||
if (dwc->gadget->speed <= USB_SPEED_HIGH ||
|
||||
DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
|
||||
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
|
@ -345,7 +346,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
}
|
||||
|
||||
if (saved_config)
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -355,9 +356,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
* improve performance.
|
||||
*/
|
||||
if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
|
||||
dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
|
||||
dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
|
||||
dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
|
||||
dwc3_writel(dwc, DWC3_DEPCMDPAR0(epnum), params->param0);
|
||||
dwc3_writel(dwc, DWC3_DEPCMDPAR1(epnum), params->param1);
|
||||
dwc3_writel(dwc, DWC3_DEPCMDPAR2(epnum), params->param2);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -381,7 +382,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
else
|
||||
cmd |= DWC3_DEPCMD_CMDACT;
|
||||
|
||||
dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
|
||||
dwc3_writel(dwc, DWC3_DEPCMD(epnum), cmd);
|
||||
|
||||
if (!(cmd & DWC3_DEPCMD_CMDACT) ||
|
||||
(DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
|
||||
|
|
@ -391,7 +392,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
}
|
||||
|
||||
do {
|
||||
reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
|
||||
reg = dwc3_readl(dwc, DWC3_DEPCMD(epnum));
|
||||
if (!(reg & DWC3_DEPCMD_CMDACT)) {
|
||||
cmd_status = DWC3_DEPCMD_STATUS(reg);
|
||||
|
||||
|
|
@ -447,9 +448,9 @@ skip_status:
|
|||
mdelay(1);
|
||||
|
||||
if (saved_config) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= saved_config;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -726,7 +727,7 @@ static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc)
|
|||
u32 reg;
|
||||
|
||||
/* Check if TXFIFOs start at non-zero addr */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
|
||||
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg);
|
||||
|
||||
ram_depth -= (fifo_0_start >> 16);
|
||||
|
|
@ -754,7 +755,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
|
|||
|
||||
/* Read ep0IN related TXFIFO size */
|
||||
dep = dwc->eps[1];
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
||||
size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
|
||||
else
|
||||
|
|
@ -769,10 +770,10 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
|
|||
|
||||
/* Don't change TXFRAMNUM on usb31 version */
|
||||
size = DWC3_IP_IS(DWC3) ? 0 :
|
||||
dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
|
||||
dwc3_readl(dwc, DWC3_GTXFIFOSIZ(num >> 1)) &
|
||||
DWC31_GTXFIFOSIZ_TXFRAMNUM;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
|
||||
dwc3_writel(dwc, DWC3_GTXFIFOSIZ(num >> 1), size);
|
||||
dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
|
||||
}
|
||||
dwc->num_ep_resized = 0;
|
||||
|
|
@ -875,7 +876,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
|
|||
fifo_size++;
|
||||
|
||||
/* Check if TXFIFOs start at non-zero addr */
|
||||
tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
||||
tmp = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
|
||||
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
|
||||
|
||||
fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
|
||||
|
|
@ -898,7 +899,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
|
||||
dwc3_writel(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
|
||||
dep->flags |= DWC3_EP_TXFIFO_RESIZED;
|
||||
dwc->num_ep_resized++;
|
||||
|
||||
|
|
@ -942,9 +943,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
dep->type = usb_endpoint_type(desc);
|
||||
dep->flags |= DWC3_EP_ENABLED;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
|
||||
reg = dwc3_readl(dwc, DWC3_DALEPENA);
|
||||
reg |= DWC3_DALEPENA_EP(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
dwc3_writel(dwc, DWC3_DALEPENA, reg);
|
||||
|
||||
dep->trb_dequeue = 0;
|
||||
dep->trb_enqueue = 0;
|
||||
|
|
@ -1079,9 +1080,9 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
|||
if (dep->flags & DWC3_EP_STALL)
|
||||
__dwc3_gadget_ep_set_halt(dep, 0, false);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
|
||||
reg = dwc3_readl(dwc, DWC3_DALEPENA);
|
||||
reg &= ~DWC3_DALEPENA_EP(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
dwc3_writel(dwc, DWC3_DALEPENA, reg);
|
||||
|
||||
dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
|
||||
|
||||
|
|
@ -1742,7 +1743,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
return DWC3_DSTS_SOFFN(reg);
|
||||
}
|
||||
|
||||
|
|
@ -2350,13 +2351,13 @@ static void dwc3_gadget_enable_linksts_evts(struct dwc3 *dwc, bool set)
|
|||
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
|
||||
return;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
|
||||
reg = dwc3_readl(dwc, DWC3_DEVTEN);
|
||||
if (set)
|
||||
reg |= DWC3_DEVTEN_ULSTCNGEN;
|
||||
else
|
||||
reg &= ~DWC3_DEVTEN_ULSTCNGEN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
dwc3_writel(dwc, DWC3_DEVTEN, reg);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_get_frame(struct usb_gadget *g)
|
||||
|
|
@ -2379,7 +2380,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
|||
*
|
||||
* We can check that via USB Link State bits in DSTS register.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
|
||||
link_state = DWC3_DSTS_USBLNKST(reg);
|
||||
|
||||
|
|
@ -2407,9 +2408,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
|||
/* Recent versions do this automatically */
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
|
||||
/* write zeroes to Link Change Request */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2529,7 +2530,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
|
|||
if (ssp_rate == USB_SSP_GEN_UNKNOWN)
|
||||
ssp_rate = dwc->max_ssp_rate;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg &= ~DWC3_DCFG_SPEED_MASK;
|
||||
reg &= ~DWC3_DCFG_NUMLANES(~0);
|
||||
|
||||
|
|
@ -2542,7 +2543,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
|
|||
dwc->max_ssp_rate != USB_SSP_GEN_2x1)
|
||||
reg |= DWC3_DCFG_NUMLANES(1);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
|
||||
|
|
@ -2560,7 +2561,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
|
|||
return;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
||||
|
||||
/*
|
||||
|
|
@ -2611,7 +2612,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
|
|||
speed < USB_SPEED_SUPER_PLUS)
|
||||
reg &= ~DWC3_DCFG_NUMLANES(~0);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||
|
|
@ -2636,7 +2637,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
|||
* mentioned in the dwc3 programming guide. It has been tested on an
|
||||
* Exynos platforms.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
|
||||
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
|
@ -2648,9 +2649,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
|||
}
|
||||
|
||||
if (saved_config)
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
if (is_on) {
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
|
||||
reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
||||
|
|
@ -2674,14 +2675,14 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
|||
|
||||
do {
|
||||
usleep_range(1000, 2000);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
reg &= DWC3_DSTS_DEVCTRLHLT;
|
||||
} while (--timeout && !(!is_on ^ !reg));
|
||||
|
||||
if (saved_config) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= saved_config;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
|
|
@ -2857,13 +2858,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
|
|||
if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
|
||||
reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
dwc3_writel(dwc, DWC3_DEVTEN, reg);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
||||
{
|
||||
/* mask all interrupts */
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
|
||||
dwc3_writel(dwc, DWC3_DEVTEN, 0x00);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
|
||||
|
|
@ -2904,10 +2905,10 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
|
|||
nump = min_t(u32, nump, 16);
|
||||
|
||||
/* update NumP */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg &= ~DWC3_DCFG_NUMP_MASK;
|
||||
reg |= nump << DWC3_DCFG_NUMP_SHIFT;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_start(struct dwc3 *dwc)
|
||||
|
|
@ -2921,10 +2922,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
|||
* the core supports IMOD, disable it.
|
||||
*/
|
||||
if (dwc->imod_interval) {
|
||||
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
|
||||
dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
|
||||
dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
|
||||
} else if (dwc3_has_imod(dwc)) {
|
||||
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
|
||||
dwc3_writel(dwc, DWC3_DEV_IMOD(0), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2934,13 +2935,13 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
|||
* This way, we maximize the chances that we'll be able to get several
|
||||
* bursts of data without going through any sort of endpoint throttling.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
|
||||
else
|
||||
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
|
||||
|
||||
dwc3_gadget_setup_nump(dwc);
|
||||
|
||||
|
|
@ -2951,15 +2952,15 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
|||
* ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
|
||||
* the stream performance.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_IGNSTRMPP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
|
||||
/* Enable MST by default if the device is capable of MST */
|
||||
if (DWC3_MST_CAPABLE(&dwc->hwparams)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG1);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG1);
|
||||
reg &= ~DWC3_DCFG1_DIS_MST_ENH;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG1, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG1, reg);
|
||||
}
|
||||
|
||||
/* Start with SuperSpeed Default */
|
||||
|
|
@ -3123,8 +3124,6 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
|
|||
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
union power_supply_propval val = {0};
|
||||
int ret;
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
return usb_phy_set_power(dwc->usb2_phy, mA);
|
||||
|
|
@ -3132,10 +3131,10 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
|||
if (!dwc->usb_psy)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
val.intval = 1000 * mA;
|
||||
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
||||
dwc->current_limit = mA;
|
||||
schedule_work(&dwc->vbus_draw_work);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3239,7 +3238,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
|
|||
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||
mdwidth /= 8;
|
||||
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
|
||||
size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1));
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
|
||||
else
|
||||
|
|
@ -3288,7 +3287,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
|
|||
mdwidth /= 8;
|
||||
|
||||
/* All OUT endpoints share a single RxFIFO space */
|
||||
size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
|
||||
size = dwc3_readl(dwc, DWC3_GRXFIFOSIZ(0));
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
|
||||
else
|
||||
|
|
@ -3381,7 +3380,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
|||
dep->dwc = dwc;
|
||||
dep->number = epnum;
|
||||
dep->direction = direction;
|
||||
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
||||
dwc->eps[epnum] = dep;
|
||||
dep->combo_num = 0;
|
||||
dep->start_cmd_status = 0;
|
||||
|
|
@ -3742,9 +3740,9 @@ out:
|
|||
return no_started_trb;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg |= dwc->u1u2;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc3_writel(dwc, DWC3_DCTL, reg);
|
||||
|
||||
dwc->u1u2 = 0;
|
||||
}
|
||||
|
|
@ -4074,7 +4072,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
|||
|
||||
dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||
reg &= ~DWC3_DCTL_INITU2ENA;
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
|
|
@ -4163,7 +4161,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
dwc3_stop_active_transfers(dwc);
|
||||
dwc->connected = true;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
dwc->test_mode = false;
|
||||
|
|
@ -4172,9 +4170,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
dwc3_clear_stall_all_ep(dwc);
|
||||
|
||||
/* Reset device address to zero */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
|
|
@ -4188,7 +4186,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
if (!dwc->softconnect)
|
||||
return;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
reg = dwc3_readl(dwc, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
dwc->speed = speed;
|
||||
|
||||
|
|
@ -4263,11 +4261,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
!dwc->usb2_gadget_lpm_disable &&
|
||||
(speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
||||
|
||||
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
|
||||
|
|
@ -4290,12 +4288,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
} else {
|
||||
if (dwc->usb2_gadget_lpm_disable) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg = dwc3_readl(dwc, DWC3_DCFG);
|
||||
reg &= ~DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
dwc3_writel(dwc, DWC3_DCFG, reg);
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
}
|
||||
|
|
@ -4401,7 +4399,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
switch (dwc->link_state) {
|
||||
case DWC3_LINK_STATE_U1:
|
||||
case DWC3_LINK_STATE_U2:
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg = dwc3_readl(dwc, DWC3_DCTL);
|
||||
u1u2 = reg & (DWC3_DCTL_INITU2ENA
|
||||
| DWC3_DCTL_ACCEPTU2ENA
|
||||
| DWC3_DCTL_INITU1ENA
|
||||
|
|
@ -4558,7 +4556,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
|
|||
ret = IRQ_HANDLED;
|
||||
|
||||
/* Unmask interrupt */
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
|
||||
dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
|
||||
DWC3_GEVNTSIZ_SIZE(evt->length));
|
||||
|
||||
evt->flags &= ~DWC3_EVENT_PENDING;
|
||||
|
|
@ -4569,8 +4567,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
|
|||
wmb();
|
||||
|
||||
if (dwc->imod_interval) {
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
|
||||
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
|
||||
dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
|
||||
dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -4619,7 +4617,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|||
if (evt->flags & DWC3_EVENT_PENDING)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
|
||||
count = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
|
||||
count &= DWC3_GEVNTCOUNT_MASK;
|
||||
if (!count)
|
||||
return IRQ_NONE;
|
||||
|
|
@ -4634,7 +4632,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|||
evt->flags |= DWC3_EVENT_PENDING;
|
||||
|
||||
/* Mask interrupt */
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
|
||||
dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
|
||||
DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
|
||||
|
||||
amount = min(count, evt->length - evt->lpos);
|
||||
|
|
@ -4643,7 +4641,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|||
if (amount < count)
|
||||
memcpy(evt->cache, evt->buf, count - amount);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
|
||||
dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
|
|||
{
|
||||
u32 res_id;
|
||||
|
||||
res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
|
||||
res_id = dwc3_readl(dep->dwc, DWC3_DEPCMD(dep->number));
|
||||
dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
|
|||
static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
|
||||
{
|
||||
value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, value);
|
||||
dwc3_writel(dwc, DWC3_DCTL, value);
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
#include "debug.h"
|
||||
#include "core.h"
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
static inline u32 dwc3_readl(struct dwc3 *dwc, u32 offset)
|
||||
{
|
||||
u32 value;
|
||||
void __iomem *base = dwc->regs;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
|
|
@ -32,13 +33,15 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
|||
* documentation, so we revert it back to the proper addresses, the
|
||||
* same way they are described on SNPS documentation
|
||||
*/
|
||||
trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value);
|
||||
trace_dwc3_readl(dwc, base - DWC3_GLOBALS_REGS_START, offset, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
static inline void dwc3_writel(struct dwc3 *dwc, u32 offset, u32 value)
|
||||
{
|
||||
void __iomem *base = dwc->regs;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
|
|
@ -51,7 +54,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
|||
* documentation, so we revert it back to the proper addresses, the
|
||||
* same way they are described on SNPS documentation
|
||||
*/
|
||||
trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value);
|
||||
trace_dwc3_writel(dwc, base - DWC3_GLOBALS_REGS_START, offset, value);
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_IO_H */
|
||||
|
|
|
|||
|
|
@ -20,63 +20,70 @@
|
|||
#include "debug.h"
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_set_prtcap,
|
||||
TP_PROTO(u32 mode),
|
||||
TP_ARGS(mode),
|
||||
TP_PROTO(struct dwc3 *dwc, u32 mode),
|
||||
TP_ARGS(dwc, mode),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__field(u32, mode)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dwc->xhci_resources[0].start;
|
||||
__entry->mode = mode;
|
||||
),
|
||||
TP_printk("mode %s", dwc3_mode_string(__entry->mode))
|
||||
TP_printk("%pa: mode %s", &__entry->base_address, dwc3_mode_string(__entry->mode))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_set_prtcap, dwc3_set_prtcap,
|
||||
TP_PROTO(u32 mode),
|
||||
TP_ARGS(mode)
|
||||
TP_PROTO(struct dwc3 *dwc, u32 mode),
|
||||
TP_ARGS(dwc, mode)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_io,
|
||||
TP_PROTO(void *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value),
|
||||
TP_PROTO(struct dwc3 *dwc, void *base, u32 offset, u32 value),
|
||||
TP_ARGS(dwc, base, offset, value),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__field(void *, base)
|
||||
__field(u32, offset)
|
||||
__field(u32, value)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dwc->xhci_resources[0].start;
|
||||
__entry->base = base;
|
||||
__entry->offset = offset;
|
||||
__entry->value = value;
|
||||
),
|
||||
TP_printk("addr %p offset %04x value %08x",
|
||||
TP_printk("%pa: addr %p offset %04x value %08x",
|
||||
&__entry->base_address,
|
||||
__entry->base + __entry->offset,
|
||||
__entry->offset,
|
||||
__entry->value)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_io, dwc3_readl,
|
||||
TP_PROTO(void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value)
|
||||
TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(dwc, base, offset, value)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_io, dwc3_writel,
|
||||
TP_PROTO(void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(base, offset, value)
|
||||
TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
|
||||
TP_ARGS(dwc, base, offset, value)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_event,
|
||||
TP_PROTO(u32 event, struct dwc3 *dwc),
|
||||
TP_ARGS(event, dwc),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__field(u32, event)
|
||||
__field(u32, ep0state)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dwc->xhci_resources[0].start;
|
||||
__entry->event = event;
|
||||
__entry->ep0state = dwc->ep0state;
|
||||
),
|
||||
TP_printk("event (%08x): %s", __entry->event,
|
||||
TP_printk("%pa: event (%08x): %s", &__entry->base_address, __entry->event,
|
||||
dwc3_decode_event(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
|
||||
__entry->event, __entry->ep0state))
|
||||
);
|
||||
|
|
@ -87,9 +94,10 @@ DEFINE_EVENT(dwc3_log_event, dwc3_event,
|
|||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl),
|
||||
TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(dwc, ctrl),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__field(__u8, bRequestType)
|
||||
__field(__u8, bRequest)
|
||||
__field(__u16, wValue)
|
||||
|
|
@ -97,13 +105,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
|||
__field(__u16, wLength)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dwc->xhci_resources[0].start;
|
||||
__entry->bRequestType = ctrl->bRequestType;
|
||||
__entry->bRequest = ctrl->bRequest;
|
||||
__entry->wValue = le16_to_cpu(ctrl->wValue);
|
||||
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
__entry->wLength = le16_to_cpu(ctrl->wLength);
|
||||
),
|
||||
TP_printk("%s", usb_decode_ctrl(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
|
||||
TP_printk("%pa: %s", &__entry->base_address, usb_decode_ctrl(__get_buf(DWC3_MSG_MAX),
|
||||
DWC3_MSG_MAX,
|
||||
__entry->bRequestType,
|
||||
__entry->bRequest, __entry->wValue,
|
||||
__entry->wIndex, __entry->wLength)
|
||||
|
|
@ -111,14 +121,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
|||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl)
|
||||
TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(dwc, ctrl)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_request,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__string(name, req->dep->name)
|
||||
__field(struct dwc3_request *, req)
|
||||
__field(unsigned int, actual)
|
||||
|
|
@ -129,7 +140,7 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
|||
__field(int, no_interrupt)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name);
|
||||
__entry->base_address = req->dep->dwc->xhci_resources[0].start;
|
||||
__entry->req = req;
|
||||
__entry->actual = req->request.actual;
|
||||
__entry->length = req->request.length;
|
||||
|
|
@ -138,8 +149,10 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
|||
__entry->short_not_ok = req->request.short_not_ok;
|
||||
__entry->no_interrupt = req->request.no_interrupt;
|
||||
),
|
||||
TP_printk("%s: req %p length %u/%u %s%s%s ==> %d",
|
||||
__get_str(name), __entry->req, __entry->actual, __entry->length,
|
||||
TP_printk("%pa: %s: req %p length %u/%u %s%s%s ==> %d",
|
||||
&__entry->base_address,
|
||||
__get_str(name), __entry->req,
|
||||
__entry->actual, __entry->length,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "i" : "I",
|
||||
|
|
@ -173,28 +186,30 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
|
|||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(cmd, param, status),
|
||||
TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(dwc, cmd, param, status),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param)
|
||||
__field(int, status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dwc->xhci_resources[0].start;
|
||||
__entry->cmd = cmd;
|
||||
__entry->param = param;
|
||||
__entry->status = status;
|
||||
),
|
||||
TP_printk("cmd '%s' [%x] param %08x --> status: %s",
|
||||
dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
TP_printk("%pa: cmd '%s' [%x] param %08x --> status: %s",
|
||||
&__entry->base_address, dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param,
|
||||
dwc3_gadget_generic_cmd_status_string(__entry->status)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(cmd, param, status)
|
||||
TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(dwc, cmd, param, status)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
|
|
@ -202,6 +217,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
|||
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
|
||||
TP_ARGS(dep, cmd, params, cmd_status),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__string(name, dep->name)
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param0)
|
||||
|
|
@ -210,6 +226,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
|||
__field(int, cmd_status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dep->dwc->xhci_resources[0].start;
|
||||
__assign_str(name);
|
||||
__entry->cmd = cmd;
|
||||
__entry->param0 = params->param0;
|
||||
|
|
@ -217,8 +234,9 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
|||
__entry->param2 = params->param2;
|
||||
__entry->cmd_status = cmd_status;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
|
||||
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
TP_printk("%pa: %s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
|
||||
&__entry->base_address, __get_str(name),
|
||||
dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param0,
|
||||
__entry->param1, __entry->param2,
|
||||
dwc3_ep_cmd_status_string(__entry->cmd_status)
|
||||
|
|
@ -235,6 +253,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
|
||||
TP_ARGS(dep, trb),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__string(name, dep->name)
|
||||
__field(struct dwc3_trb *, trb)
|
||||
__field(u32, bpl)
|
||||
|
|
@ -246,6 +265,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
__field(u32, dequeue)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dep->dwc->xhci_resources[0].start;
|
||||
__assign_str(name);
|
||||
__entry->trb = trb;
|
||||
__entry->bpl = trb->bpl;
|
||||
|
|
@ -256,8 +276,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
__entry->enqueue = dep->trb_enqueue;
|
||||
__entry->dequeue = dep->trb_dequeue;
|
||||
),
|
||||
TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
|
||||
__get_str(name), __entry->trb, __entry->enqueue,
|
||||
TP_printk("%pa: %s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
|
||||
&__entry->base_address, __get_str(name), __entry->trb, __entry->enqueue,
|
||||
__entry->dequeue, __entry->bph, __entry->bpl,
|
||||
({char *s;
|
||||
int pcm = ((__entry->size >> 24) & 3) + 1;
|
||||
|
|
@ -307,6 +327,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
|
|||
TP_PROTO(struct dwc3_ep *dep),
|
||||
TP_ARGS(dep),
|
||||
TP_STRUCT__entry(
|
||||
__field(phys_addr_t, base_address)
|
||||
__string(name, dep->name)
|
||||
__field(unsigned int, maxpacket)
|
||||
__field(unsigned int, maxpacket_limit)
|
||||
|
|
@ -318,6 +339,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
|
|||
__field(u8, trb_dequeue)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->base_address = dep->dwc->xhci_resources[0].start;
|
||||
__assign_str(name);
|
||||
__entry->maxpacket = dep->endpoint.maxpacket;
|
||||
__entry->maxpacket_limit = dep->endpoint.maxpacket_limit;
|
||||
|
|
@ -328,8 +350,8 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
|
|||
__entry->trb_enqueue = dep->trb_enqueue;
|
||||
__entry->trb_dequeue = dep->trb_dequeue;
|
||||
),
|
||||
TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
|
||||
__get_str(name), __entry->maxpacket,
|
||||
TP_printk("%pa: %s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
|
||||
&__entry->base_address, __get_str(name), __entry->maxpacket,
|
||||
__entry->maxpacket_limit, __entry->max_streams,
|
||||
__entry->maxburst, __entry->trb_enqueue,
|
||||
__entry->trb_dequeue,
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read)
|
|||
if (read)
|
||||
ns += DWC3_ULPI_BASE_DELAY;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
|
||||
if (reg & DWC3_GUSB2PHYCFG_SUSPHY)
|
||||
usleep_range(1000, 1200);
|
||||
|
||||
while (count--) {
|
||||
ndelay(ns);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
|
||||
if (reg & DWC3_GUSB2PHYACC_DONE)
|
||||
return 0;
|
||||
cpu_relax();
|
||||
|
|
@ -55,13 +55,13 @@ static int dwc3_ulpi_read(struct device *dev, u8 addr)
|
|||
int ret;
|
||||
|
||||
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg);
|
||||
|
||||
ret = dwc3_ulpi_busyloop(dwc, addr, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
|
||||
reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
|
||||
|
||||
return DWC3_GUSB2PHYACC_DATA(reg);
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val)
|
|||
|
||||
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
|
||||
reg |= DWC3_GUSB2PHYACC_WRITE | val;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
|
||||
dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg);
|
||||
|
||||
return dwc3_ulpi_busyloop(dwc, addr, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5625,11 +5625,6 @@ int __init fotg210_hcd_init(void)
|
|||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
|
||||
if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
|
||||
test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
|
||||
pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n");
|
||||
|
||||
pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n",
|
||||
hcd_name, sizeof(struct fotg210_qh),
|
||||
sizeof(struct fotg210_qtd),
|
||||
|
|
@ -5643,5 +5638,4 @@ int __init fotg210_hcd_init(void)
|
|||
void __exit fotg210_hcd_cleanup(void)
|
||||
{
|
||||
debugfs_remove(fotg210_debug_root);
|
||||
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ static void gadget_info_attr_release(struct config_item *item)
|
|||
kfree(gi);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations gadget_root_item_ops = {
|
||||
static const struct configfs_item_operations gadget_root_item_ops = {
|
||||
.release = gadget_info_attr_release,
|
||||
};
|
||||
|
||||
|
|
@ -514,7 +514,7 @@ static void config_usb_cfg_unlink(
|
|||
WARN(1, "Unable to locate function to unbind\n");
|
||||
}
|
||||
|
||||
static struct configfs_item_operations gadget_config_item_ops = {
|
||||
static const struct configfs_item_operations gadget_config_item_ops = {
|
||||
.release = gadget_config_attr_release,
|
||||
.allow_link = config_usb_cfg_link,
|
||||
.drop_link = config_usb_cfg_unlink,
|
||||
|
|
@ -663,7 +663,7 @@ static void function_drop(
|
|||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations functions_ops = {
|
||||
static const struct configfs_group_operations functions_ops = {
|
||||
.make_group = &function_make,
|
||||
.drop_item = &function_drop,
|
||||
};
|
||||
|
|
@ -766,7 +766,7 @@ static void config_desc_drop(
|
|||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations config_desc_ops = {
|
||||
static const struct configfs_group_operations config_desc_ops = {
|
||||
.make_group = &config_desc_make,
|
||||
.drop_item = &config_desc_drop,
|
||||
};
|
||||
|
|
@ -799,7 +799,7 @@ static void gadget_language_attr_release(struct config_item *item)
|
|||
kfree(gs);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations gadget_language_langid_item_ops = {
|
||||
static const struct configfs_item_operations gadget_language_langid_item_ops = {
|
||||
.release = gadget_language_attr_release,
|
||||
};
|
||||
|
||||
|
|
@ -852,7 +852,7 @@ static void gadget_string_release(struct config_item *item)
|
|||
kfree(string);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations gadget_string_item_ops = {
|
||||
static const struct configfs_item_operations gadget_string_item_ops = {
|
||||
.release = gadget_string_release,
|
||||
};
|
||||
|
||||
|
|
@ -901,7 +901,7 @@ static void gadget_language_string_drop(struct config_group *group,
|
|||
string->usb_string.id = i++;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations gadget_language_langid_group_ops = {
|
||||
static const struct configfs_group_operations gadget_language_langid_group_ops = {
|
||||
.make_item = gadget_language_string_make,
|
||||
.drop_item = gadget_language_string_drop,
|
||||
};
|
||||
|
|
@ -960,7 +960,7 @@ static void gadget_language_drop(struct config_group *group,
|
|||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations gadget_language_group_ops = {
|
||||
static const struct configfs_group_operations gadget_language_group_ops = {
|
||||
.make_group = &gadget_language_make,
|
||||
.drop_item = &gadget_language_drop,
|
||||
};
|
||||
|
|
@ -1266,7 +1266,7 @@ static void os_desc_unlink(struct config_item *os_desc_ci,
|
|||
mutex_unlock(&gi->lock);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations os_desc_ops = {
|
||||
static const struct configfs_item_operations os_desc_ops = {
|
||||
.allow_link = os_desc_link,
|
||||
.drop_link = os_desc_unlink,
|
||||
};
|
||||
|
|
@ -1391,7 +1391,7 @@ static void usb_os_desc_ext_prop_release(struct config_item *item)
|
|||
kfree(ext_prop); /* frees a whole chunk */
|
||||
}
|
||||
|
||||
static struct configfs_item_operations ext_prop_ops = {
|
||||
static const struct configfs_item_operations ext_prop_ops = {
|
||||
.release = usb_os_desc_ext_prop_release,
|
||||
};
|
||||
|
||||
|
|
@ -1456,7 +1456,7 @@ static void ext_prop_drop(struct config_group *group, struct config_item *item)
|
|||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations interf_grp_ops = {
|
||||
static const struct configfs_group_operations interf_grp_ops = {
|
||||
.make_item = &ext_prop_make,
|
||||
.drop_item = &ext_prop_drop,
|
||||
};
|
||||
|
|
@ -2061,7 +2061,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item)
|
|||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations gadgets_ops = {
|
||||
static const struct configfs_group_operations gadgets_ops = {
|
||||
.make_group = &gadgets_make,
|
||||
.drop_item = &gadgets_drop,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -793,7 +793,7 @@ static void acm_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations acm_item_ops = {
|
||||
static const struct configfs_item_operations acm_item_ops = {
|
||||
.release = acm_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1509,7 +1509,7 @@ static int ffs_dmabuf_attach(struct file *file, int fd)
|
|||
goto err_dmabuf_detach;
|
||||
}
|
||||
|
||||
dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
dir = epfile->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
||||
|
||||
err = ffs_dma_resv_lock(dmabuf, nonblock);
|
||||
if (err)
|
||||
|
|
@ -1639,7 +1639,7 @@ static int ffs_dmabuf_transfer(struct file *file,
|
|||
/* Make sure we don't have writers */
|
||||
timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
|
||||
retl = dma_resv_wait_timeout(dmabuf->resv,
|
||||
dma_resv_usage_rw(epfile->in),
|
||||
dma_resv_usage_rw(!epfile->in),
|
||||
true, timeout);
|
||||
if (retl == 0)
|
||||
retl = -EBUSY;
|
||||
|
|
@ -1684,7 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file,
|
|||
dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops,
|
||||
&priv->lock, priv->context, seqno);
|
||||
|
||||
resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
|
||||
resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
|
||||
|
||||
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
|
||||
dma_resv_unlock(dmabuf->resv);
|
||||
|
|
@ -1744,10 +1744,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
{
|
||||
int fd;
|
||||
|
||||
if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
|
||||
return -EFAULT;
|
||||
|
||||
return ffs_dmabuf_attach(file, fd);
|
||||
}
|
||||
|
|
@ -1755,10 +1753,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
{
|
||||
int fd;
|
||||
|
||||
if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
|
||||
return -EFAULT;
|
||||
|
||||
return ffs_dmabuf_detach(file, fd);
|
||||
}
|
||||
|
|
@ -1766,10 +1762,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
{
|
||||
struct usb_ffs_dmabuf_transfer_req req;
|
||||
|
||||
if (copy_from_user(&req, (void __user *)value, sizeof(req))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (copy_from_user(&req, (void __user *)value, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
return ffs_dmabuf_transfer(file, &req);
|
||||
}
|
||||
|
|
@ -4000,7 +3994,7 @@ static void ffs_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations ffs_item_ops = {
|
||||
static const struct configfs_item_operations ffs_item_ops = {
|
||||
.release = ffs_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1328,7 +1328,7 @@ static void hid_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations hidg_item_ops = {
|
||||
static const struct configfs_item_operations hidg_item_ops = {
|
||||
.release = hid_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&lb_opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations lb_item_ops = {
|
||||
static const struct configfs_item_operations lb_item_ops = {
|
||||
.release = lb_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3153,7 +3153,7 @@ static void fsg_lun_attr_release(struct config_item *item)
|
|||
kfree(lun_opts);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations fsg_lun_item_ops = {
|
||||
static const struct configfs_item_operations fsg_lun_item_ops = {
|
||||
.release = fsg_lun_attr_release,
|
||||
};
|
||||
|
||||
|
|
@ -3369,7 +3369,7 @@ static void fsg_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations fsg_item_ops = {
|
||||
static const struct configfs_item_operations fsg_item_ops = {
|
||||
.release = fsg_attr_release,
|
||||
};
|
||||
|
||||
|
|
@ -3462,7 +3462,7 @@ static struct configfs_attribute *fsg_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct configfs_group_operations fsg_group_ops = {
|
||||
static const struct configfs_group_operations fsg_group_ops = {
|
||||
.make_group = fsg_lun_make,
|
||||
.drop_item = fsg_lun_drop,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -875,6 +875,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct usb_string *us;
|
||||
struct f_midi_opts *opts;
|
||||
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
|
||||
|
||||
midi->gadget = cdev->gadget;
|
||||
|
|
@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (status < 0)
|
||||
goto fail_register;
|
||||
|
||||
opts = container_of(f->fi, struct f_midi_opts, func_inst);
|
||||
if (opts->interface_string)
|
||||
midi_string_defs[STRING_FUNC_IDX].s = opts->interface_string;
|
||||
|
||||
/* maybe allocate device-global string ID */
|
||||
us = usb_gstrings_attach(c->cdev, midi_strings,
|
||||
ARRAY_SIZE(midi_string_defs));
|
||||
|
|
@ -1086,7 +1091,7 @@ static void midi_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations midi_item_ops = {
|
||||
static const struct configfs_item_operations midi_item_ops = {
|
||||
.release = midi_attr_release,
|
||||
};
|
||||
|
||||
|
|
@ -1178,59 +1183,60 @@ end: \
|
|||
\
|
||||
CONFIGFS_ATTR(f_midi_opts_, name);
|
||||
|
||||
#define F_MIDI_OPT_STRING(name) \
|
||||
static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
|
||||
{ \
|
||||
struct f_midi_opts *opts = to_f_midi_opts(item); \
|
||||
ssize_t result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->name) { \
|
||||
result = strscpy(page, opts->name, PAGE_SIZE); \
|
||||
} else { \
|
||||
page[0] = 0; \
|
||||
result = 0; \
|
||||
} \
|
||||
\
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_midi_opts_##name##_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_midi_opts *opts = to_f_midi_opts(item); \
|
||||
int ret; \
|
||||
char *c; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt > 1) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
c = kstrndup(page, len, GFP_KERNEL); \
|
||||
if (!c) { \
|
||||
ret = -ENOMEM; \
|
||||
goto end; \
|
||||
} \
|
||||
kfree(opts->name); \
|
||||
opts->name = c; \
|
||||
ret = len; \
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(f_midi_opts_, name)
|
||||
|
||||
F_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS);
|
||||
F_MIDI_OPT(buflen, false, 0);
|
||||
F_MIDI_OPT(qlen, false, 0);
|
||||
F_MIDI_OPT(in_ports, true, MAX_PORTS);
|
||||
F_MIDI_OPT(out_ports, true, MAX_PORTS);
|
||||
|
||||
static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_midi_opts *opts = to_f_midi_opts(item);
|
||||
ssize_t result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->id) {
|
||||
result = strscpy(page, opts->id, PAGE_SIZE);
|
||||
} else {
|
||||
page[0] = 0;
|
||||
result = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_midi_opts_id_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_midi_opts *opts = to_f_midi_opts(item);
|
||||
int ret;
|
||||
char *c;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt > 1) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
c = kstrndup(page, len, GFP_KERNEL);
|
||||
if (!c) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
if (opts->id_allocated)
|
||||
kfree(opts->id);
|
||||
opts->id = c;
|
||||
opts->id_allocated = true;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_midi_opts_, id);
|
||||
F_MIDI_OPT_STRING(id);
|
||||
F_MIDI_OPT_STRING(interface_string);
|
||||
|
||||
static struct configfs_attribute *midi_attrs[] = {
|
||||
&f_midi_opts_attr_index,
|
||||
|
|
@ -1239,6 +1245,7 @@ static struct configfs_attribute *midi_attrs[] = {
|
|||
&f_midi_opts_attr_in_ports,
|
||||
&f_midi_opts_attr_out_ports,
|
||||
&f_midi_opts_attr_id,
|
||||
&f_midi_opts_attr_interface_string,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -1262,8 +1269,8 @@ static void f_midi_free_inst(struct usb_function_instance *f)
|
|||
mutex_unlock(&opts->lock);
|
||||
|
||||
if (free) {
|
||||
if (opts->id_allocated)
|
||||
kfree(opts->id);
|
||||
kfree(opts->interface_string);
|
||||
kfree(opts);
|
||||
}
|
||||
}
|
||||
|
|
@ -1279,7 +1286,8 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
|
|||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = f_midi_free_inst;
|
||||
opts->index = SNDRV_DEFAULT_IDX1;
|
||||
opts->id = SNDRV_DEFAULT_STR1;
|
||||
opts->id = NULL;
|
||||
opts->interface_string = NULL;
|
||||
opts->buflen = 512;
|
||||
opts->qlen = 32;
|
||||
opts->in_ports = 1;
|
||||
|
|
|
|||
|
|
@ -2316,7 +2316,7 @@ static void f_midi2_block_opts_release(struct config_item *item)
|
|||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_midi2_block_item_ops = {
|
||||
static const struct configfs_item_operations f_midi2_block_item_ops = {
|
||||
.release = f_midi2_block_opts_release,
|
||||
};
|
||||
|
||||
|
|
@ -2479,11 +2479,11 @@ static void f_midi2_ep_opts_release(struct config_item *item)
|
|||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_midi2_ep_item_ops = {
|
||||
static const struct configfs_item_operations f_midi2_ep_item_ops = {
|
||||
.release = f_midi2_ep_opts_release,
|
||||
};
|
||||
|
||||
static struct configfs_group_operations f_midi2_ep_group_ops = {
|
||||
static const struct configfs_group_operations f_midi2_ep_group_ops = {
|
||||
.make_group = f_midi2_opts_block_make,
|
||||
.drop_item = f_midi2_opts_block_drop,
|
||||
};
|
||||
|
|
@ -2618,11 +2618,11 @@ static void f_midi2_opts_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_midi2_item_ops = {
|
||||
static const struct configfs_item_operations f_midi2_item_ops = {
|
||||
.release = f_midi2_opts_release,
|
||||
};
|
||||
|
||||
static struct configfs_group_operations f_midi2_group_ops = {
|
||||
static const struct configfs_group_operations f_midi2_group_ops = {
|
||||
.make_group = f_midi2_opts_ep_make,
|
||||
.drop_item = f_midi2_opts_ep_drop,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
|
|||
return container_of(f, struct f_ncm, port.func);
|
||||
}
|
||||
|
||||
static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f)
|
||||
{
|
||||
return container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
|
@ -859,6 +864,7 @@ invalid:
|
|||
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_ncm *ncm = func_to_ncm(f);
|
||||
struct f_ncm_opts *opts = func_to_ncm_opts(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
/* Control interface has only altsetting 0 */
|
||||
|
|
@ -881,9 +887,10 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
if (alt > 1)
|
||||
goto fail;
|
||||
|
||||
if (ncm->netdev) {
|
||||
scoped_guard(mutex, &opts->lock)
|
||||
if (opts->net) {
|
||||
DBG(cdev, "reset ncm\n");
|
||||
ncm->netdev = NULL;
|
||||
opts->net = NULL;
|
||||
gether_disconnect(&ncm->port);
|
||||
ncm_reset_values(ncm);
|
||||
}
|
||||
|
|
@ -919,7 +926,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
net = gether_connect(&ncm->port);
|
||||
if (IS_ERR(net))
|
||||
return PTR_ERR(net);
|
||||
ncm->netdev = net;
|
||||
scoped_guard(mutex, &opts->lock)
|
||||
opts->net = net;
|
||||
}
|
||||
|
||||
spin_lock(&ncm->lock);
|
||||
|
|
@ -1366,12 +1374,14 @@ err:
|
|||
static void ncm_disable(struct usb_function *f)
|
||||
{
|
||||
struct f_ncm *ncm = func_to_ncm(f);
|
||||
struct f_ncm_opts *opts = func_to_ncm_opts(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
DBG(cdev, "ncm deactivated\n");
|
||||
|
||||
if (ncm->netdev) {
|
||||
ncm->netdev = NULL;
|
||||
scoped_guard(mutex, &opts->lock)
|
||||
if (opts->net) {
|
||||
opts->net = NULL;
|
||||
gether_disconnect(&ncm->port);
|
||||
}
|
||||
|
||||
|
|
@ -1433,39 +1443,44 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_ncm *ncm = func_to_ncm(f);
|
||||
struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
|
||||
struct usb_string *us;
|
||||
int status = 0;
|
||||
struct usb_ep *ep;
|
||||
struct f_ncm_opts *ncm_opts;
|
||||
|
||||
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
|
||||
struct net_device *netdev __free(free_gether_netdev) = NULL;
|
||||
struct usb_request *request __free(free_usb_request) = NULL;
|
||||
|
||||
if (!can_support_ecm(cdev->gadget))
|
||||
return -EINVAL;
|
||||
|
||||
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||
|
||||
if (cdev->use_os_string) {
|
||||
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
|
||||
if (!os_desc_table)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_lock(&ncm_opts->lock);
|
||||
gether_set_gadget(ncm_opts->net, cdev->gadget);
|
||||
if (!ncm_opts->bound) {
|
||||
ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
|
||||
status = gether_register_netdev(ncm_opts->net);
|
||||
}
|
||||
mutex_unlock(&ncm_opts->lock);
|
||||
netdev = gether_setup_default();
|
||||
if (IS_ERR(netdev))
|
||||
return -ENOMEM;
|
||||
|
||||
scoped_guard(mutex, &ncm_opts->lock) {
|
||||
gether_apply_opts(netdev, &ncm_opts->net_opts);
|
||||
netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
|
||||
}
|
||||
|
||||
gether_set_gadget(netdev, cdev->gadget);
|
||||
status = gether_register_netdev(netdev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
ncm_opts->bound = true;
|
||||
|
||||
ncm_string_defs[1].s = ncm->ethaddr;
|
||||
/* export host's Ethernet address in CDC format */
|
||||
status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
|
||||
sizeof(ncm->ethaddr));
|
||||
if (status < 12)
|
||||
return -EINVAL;
|
||||
ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
|
||||
|
||||
us = usb_gstrings_attach(cdev, ncm_strings,
|
||||
ARRAY_SIZE(ncm_string_defs));
|
||||
|
|
@ -1563,6 +1578,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
f->os_desc_n = 1;
|
||||
}
|
||||
ncm->notify_req = no_free_ptr(request);
|
||||
ncm->netdev = no_free_ptr(netdev);
|
||||
ncm->port.ioport = netdev_priv(ncm->netdev);
|
||||
|
||||
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
|
||||
ncm->port.in_ep->name, ncm->port.out_ep->name,
|
||||
|
|
@ -1577,19 +1594,19 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
|
|||
}
|
||||
|
||||
/* f_ncm_item_ops */
|
||||
USB_ETHERNET_CONFIGFS_ITEM(ncm);
|
||||
USB_ETHER_OPTS_ITEM(ncm);
|
||||
|
||||
/* f_ncm_opts_dev_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
|
||||
USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm);
|
||||
|
||||
/* f_ncm_opts_host_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
|
||||
USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
|
||||
|
||||
/* f_ncm_opts_qmult */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
|
||||
USB_ETHER_OPTS_ATTR_QMULT(ncm);
|
||||
|
||||
/* f_ncm_opts_ifname */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
|
||||
USB_ETHER_OPTS_ATTR_IFNAME(ncm);
|
||||
|
||||
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
|
||||
char *page)
|
||||
|
|
@ -1655,34 +1672,27 @@ static void ncm_free_inst(struct usb_function_instance *f)
|
|||
struct f_ncm_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_ncm_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts->ncm_interf_group);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *ncm_alloc_inst(void)
|
||||
{
|
||||
struct f_ncm_opts *opts;
|
||||
struct usb_function_instance *ret;
|
||||
struct usb_os_desc *descs[1];
|
||||
char *names[1];
|
||||
struct config_group *ncm_interf_group;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
struct f_ncm_opts *opts __free(kfree) = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts->net = NULL;
|
||||
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
|
||||
gether_setup_opts_default(&opts->net_opts, "usb");
|
||||
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = ncm_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
if (IS_ERR(opts->net)) {
|
||||
struct net_device *net = opts->net;
|
||||
kfree(opts);
|
||||
return ERR_CAST(net);
|
||||
}
|
||||
opts->max_segment_size = ETH_FRAME_LEN;
|
||||
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
||||
|
||||
|
|
@ -1693,26 +1703,22 @@ static struct usb_function_instance *ncm_alloc_inst(void)
|
|||
ncm_interf_group =
|
||||
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
|
||||
names, THIS_MODULE);
|
||||
if (IS_ERR(ncm_interf_group)) {
|
||||
ncm_free_inst(&opts->func_inst);
|
||||
if (IS_ERR(ncm_interf_group))
|
||||
return ERR_CAST(ncm_interf_group);
|
||||
}
|
||||
opts->ncm_interf_group = ncm_interf_group;
|
||||
|
||||
return &opts->func_inst;
|
||||
ret = &opts->func_inst;
|
||||
retain_and_null_ptr(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ncm_free(struct usb_function *f)
|
||||
{
|
||||
struct f_ncm *ncm;
|
||||
struct f_ncm_opts *opts;
|
||||
struct f_ncm_opts *opts = func_to_ncm_opts(f);
|
||||
|
||||
ncm = func_to_ncm(f);
|
||||
opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||
kfree(ncm);
|
||||
mutex_lock(&opts->lock);
|
||||
scoped_guard(mutex, &opts->lock)
|
||||
opts->refcnt--;
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(func_to_ncm(f));
|
||||
}
|
||||
|
||||
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
|
@ -1736,13 +1742,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
kfree(ncm->notify_req->buf);
|
||||
usb_ep_free_request(ncm->notify, ncm->notify_req);
|
||||
|
||||
ncm->port.ioport = NULL;
|
||||
gether_cleanup(netdev_priv(ncm->netdev));
|
||||
}
|
||||
|
||||
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_ncm *ncm;
|
||||
struct f_ncm_opts *opts;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
|
||||
|
|
@ -1750,22 +1758,12 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_ncm_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt++;
|
||||
|
||||
/* export host's Ethernet address in CDC format */
|
||||
status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
|
||||
sizeof(ncm->ethaddr));
|
||||
if (status < 12) { /* strlen("01234567890a") */
|
||||
kfree(ncm);
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
scoped_guard(mutex, &opts->lock)
|
||||
opts->refcnt++;
|
||||
|
||||
spin_lock_init(&ncm->lock);
|
||||
ncm_reset_values(ncm);
|
||||
ncm->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
ncm->port.is_fixed = true;
|
||||
ncm->port.supports_multi_frame = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations obex_item_ops = {
|
||||
static const struct configfs_item_operations obex_item_ops = {
|
||||
.release = obex_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -585,7 +585,7 @@ static void phonet_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations phonet_item_ops = {
|
||||
static const struct configfs_item_operations phonet_item_ops = {
|
||||
.release = phonet_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations printer_item_ops = {
|
||||
static const struct configfs_item_operations printer_item_ops = {
|
||||
.release = printer_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations serial_item_ops = {
|
||||
static const struct configfs_item_operations serial_item_ops = {
|
||||
.release = serial_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ struct f_sourcesink {
|
|||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned buflen;
|
||||
unsigned bulk_maxburst;
|
||||
unsigned bulk_qlen;
|
||||
unsigned iso_qlen;
|
||||
};
|
||||
|
|
@ -328,6 +329,12 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
source_sink_intf_alt0.bInterfaceNumber = id;
|
||||
source_sink_intf_alt1.bInterfaceNumber = id;
|
||||
|
||||
if (ss->bulk_maxburst > 15)
|
||||
ss->bulk_maxburst = 15;
|
||||
|
||||
ss_source_comp_desc.bMaxBurst = ss->bulk_maxburst;
|
||||
ss_sink_comp_desc.bMaxBurst = ss->bulk_maxburst;
|
||||
|
||||
/* allocate bulk endpoints */
|
||||
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
|
||||
if (!ss->in_ep) {
|
||||
|
|
@ -853,6 +860,7 @@ static struct usb_function *source_sink_alloc_func(
|
|||
ss->isoc_mult = ss_opts->isoc_mult;
|
||||
ss->isoc_maxburst = ss_opts->isoc_maxburst;
|
||||
ss->buflen = ss_opts->bulk_buflen;
|
||||
ss->bulk_maxburst = ss_opts->bulk_maxburst;
|
||||
ss->bulk_qlen = ss_opts->bulk_qlen;
|
||||
ss->iso_qlen = ss_opts->iso_qlen;
|
||||
|
||||
|
|
@ -882,7 +890,7 @@ static void ss_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&ss_opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations ss_item_ops = {
|
||||
static const struct configfs_item_operations ss_item_ops = {
|
||||
.release = ss_attr_release,
|
||||
};
|
||||
|
||||
|
|
@ -1101,6 +1109,49 @@ end:
|
|||
|
||||
CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst);
|
||||
|
||||
static ssize_t f_ss_opts_bulk_maxburst_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sysfs_emit(page, "%u\n", opts->bulk_maxburst);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_bulk_maxburst_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int ret;
|
||||
u8 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou8(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
if (num > 15) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opts->bulk_maxburst = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_ss_opts_, bulk_maxburst);
|
||||
|
||||
static ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
|
|
@ -1222,6 +1273,7 @@ static struct configfs_attribute *ss_attrs[] = {
|
|||
&f_ss_opts_attr_isoc_mult,
|
||||
&f_ss_opts_attr_isoc_maxburst,
|
||||
&f_ss_opts_attr_bulk_buflen,
|
||||
&f_ss_opts_attr_bulk_maxburst,
|
||||
&f_ss_opts_attr_bulk_qlen,
|
||||
&f_ss_opts_attr_iso_qlen,
|
||||
NULL,
|
||||
|
|
|
|||
|
|
@ -1227,7 +1227,7 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd)
|
|||
goto out;
|
||||
|
||||
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
|
||||
cmd->sense_iu.sense, cmd->unpacked_lun, 0,
|
||||
cmd->sense_iu.sense, cmd->unpacked_lun, cmd->data_len,
|
||||
cmd->prio_attr, dir, flags);
|
||||
|
||||
return;
|
||||
|
|
@ -1389,6 +1389,7 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req)
|
|||
cmd->tmr_func = 0;
|
||||
cmd->tmr_rsp = RC_RESPONSE_UNKNOWN;
|
||||
cmd->flags = 0;
|
||||
cmd->data_len = 0;
|
||||
|
||||
cmd_iu = (struct command_iu *)iu;
|
||||
|
||||
|
|
@ -2446,7 +2447,7 @@ static void tcm_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations tcm_item_ops = {
|
||||
static const struct configfs_item_operations tcm_item_ops = {
|
||||
.release = tcm_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_uac1_item_ops = {
|
||||
static const struct configfs_item_operations f_uac1_item_ops = {
|
||||
.release = f_uac1_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -812,7 +812,7 @@ static void f_uac1_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_uac1_item_ops = {
|
||||
static const struct configfs_item_operations f_uac1_item_ops = {
|
||||
.release = f_uac1_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item)
|
|||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_uac2_item_ops = {
|
||||
static const struct configfs_item_operations f_uac2_item_ops = {
|
||||
.release = f_uac2_attr_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct f_ss_opts {
|
|||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned bulk_maxburst;
|
||||
unsigned bulk_qlen;
|
||||
unsigned iso_qlen;
|
||||
|
||||
|
|
|
|||
|
|
@ -1040,6 +1040,36 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gether_set_ifname);
|
||||
|
||||
void gether_setup_opts_default(struct gether_opts *opts, const char *name)
|
||||
{
|
||||
opts->qmult = QMULT_DEFAULT;
|
||||
snprintf(opts->name, sizeof(opts->name), "%s%%d", name);
|
||||
eth_random_addr(opts->dev_mac);
|
||||
opts->addr_assign_type = NET_ADDR_RANDOM;
|
||||
eth_random_addr(opts->host_mac);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gether_setup_opts_default);
|
||||
|
||||
void gether_apply_opts(struct net_device *net, struct gether_opts *opts)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
|
||||
dev->qmult = opts->qmult;
|
||||
|
||||
if (opts->ifname_set) {
|
||||
strscpy(net->name, opts->name, sizeof(net->name));
|
||||
dev->ifname_set = true;
|
||||
}
|
||||
|
||||
memcpy(dev->host_mac, opts->host_mac, sizeof(dev->host_mac));
|
||||
|
||||
if (opts->addr_assign_type == NET_ADDR_SET) {
|
||||
memcpy(dev->dev_mac, opts->dev_mac, sizeof(dev->dev_mac));
|
||||
net->addr_assign_type = opts->addr_assign_type;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gether_apply_opts);
|
||||
|
||||
void gether_suspend(struct gether *link)
|
||||
{
|
||||
struct eth_dev *dev = link->ioport;
|
||||
|
|
@ -1096,6 +1126,21 @@ void gether_cleanup(struct eth_dev *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gether_cleanup);
|
||||
|
||||
void gether_unregister_free_netdev(struct net_device *net)
|
||||
{
|
||||
if (!net)
|
||||
return;
|
||||
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
|
||||
if (net->reg_state == NETREG_REGISTERED) {
|
||||
unregister_netdev(net);
|
||||
flush_work(&dev->work);
|
||||
}
|
||||
free_netdev(net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gether_unregister_free_netdev);
|
||||
|
||||
/**
|
||||
* gether_connect - notify network layer that USB link is active
|
||||
* @link: the USB link, set up with endpoints, descriptors matching
|
||||
|
|
|
|||
|
|
@ -38,6 +38,31 @@
|
|||
|
||||
struct eth_dev;
|
||||
|
||||
/**
|
||||
* struct gether_opts - Options for Ethernet gadget function instances
|
||||
* @name: Pattern for the network interface name (e.g., "usb%d").
|
||||
* Used to generate the net device name.
|
||||
* @qmult: Queue length multiplier for high/super speed.
|
||||
* @host_mac: The MAC address to be used by the host side.
|
||||
* @dev_mac: The MAC address to be used by the device side.
|
||||
* @ifname_set: True if the interface name pattern has been set by userspace.
|
||||
* @addr_assign_type: The method used for assigning the device MAC address
|
||||
* (e.g., NET_ADDR_RANDOM, NET_ADDR_SET).
|
||||
*
|
||||
* This structure caches network-related settings provided through configfs
|
||||
* before the net_device is fully instantiated. This allows for early
|
||||
* configuration while deferring net_device allocation until the function
|
||||
* is bound.
|
||||
*/
|
||||
struct gether_opts {
|
||||
char name[IFNAMSIZ];
|
||||
unsigned int qmult;
|
||||
u8 host_mac[ETH_ALEN];
|
||||
u8 dev_mac[ETH_ALEN];
|
||||
bool ifname_set;
|
||||
unsigned char addr_assign_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* This represents the USB side of an "ethernet" link, managed by a USB
|
||||
* function which provides control and (maybe) framing. Two functions
|
||||
|
|
@ -258,6 +283,11 @@ int gether_get_ifname(struct net_device *net, char *name, int len);
|
|||
int gether_set_ifname(struct net_device *net, const char *name, int len);
|
||||
|
||||
void gether_cleanup(struct eth_dev *dev);
|
||||
void gether_unregister_free_netdev(struct net_device *net);
|
||||
DEFINE_FREE(free_gether_netdev, struct net_device *, gether_unregister_free_netdev(_T));
|
||||
|
||||
void gether_setup_opts_default(struct gether_opts *opts, const char *name);
|
||||
void gether_apply_opts(struct net_device *net, struct gether_opts *opts);
|
||||
|
||||
void gether_suspend(struct gether *link);
|
||||
void gether_resume(struct gether *link);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
#ifndef __U_ETHER_CONFIGFS_H
|
||||
#define __U_ETHER_CONFIGFS_H
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/hex.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
|
||||
static void _f_##_attr_release(struct config_item *item) \
|
||||
{ \
|
||||
|
|
@ -21,7 +28,7 @@
|
|||
usb_put_function_instance(&opts->func_inst); \
|
||||
} \
|
||||
\
|
||||
static struct configfs_item_operations _f_##_item_ops = { \
|
||||
static const struct configfs_item_operations _f_##_item_ops = { \
|
||||
.release = _f_##_attr_release, \
|
||||
}
|
||||
|
||||
|
|
@ -197,4 +204,174 @@ out: \
|
|||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, _n_)
|
||||
|
||||
#define USB_ETHER_OPTS_ITEM(_f_) \
|
||||
static void _f_##_attr_release(struct config_item *item) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
usb_put_function_instance(&opts->func_inst); \
|
||||
} \
|
||||
\
|
||||
static struct configfs_item_operations _f_##_item_ops = { \
|
||||
.release = _f_##_attr_release, \
|
||||
}
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_DEV_ADDR(_f_) \
|
||||
static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
return sysfs_emit(page, "%pM\n", opts->net_opts.dev_mac); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u8 new_addr[ETH_ALEN]; \
|
||||
const char *p = page; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
for (int i = 0; i < ETH_ALEN; i++) { \
|
||||
unsigned char num; \
|
||||
if ((*p == '.') || (*p == ':')) \
|
||||
p++; \
|
||||
num = hex_to_bin(*p++) << 4; \
|
||||
num |= hex_to_bin(*p++); \
|
||||
new_addr[i] = num; \
|
||||
} \
|
||||
if (!is_valid_ether_addr(new_addr)) \
|
||||
return -EINVAL; \
|
||||
memcpy(opts->net_opts.dev_mac, new_addr, ETH_ALEN); \
|
||||
opts->net_opts.addr_assign_type = NET_ADDR_SET; \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, dev_addr)
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_HOST_ADDR(_f_) \
|
||||
static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
return sysfs_emit(page, "%pM\n", opts->net_opts.host_mac); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u8 new_addr[ETH_ALEN]; \
|
||||
const char *p = page; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
for (int i = 0; i < ETH_ALEN; i++) { \
|
||||
unsigned char num; \
|
||||
if ((*p == '.') || (*p == ':')) \
|
||||
p++; \
|
||||
num = hex_to_bin(*p++) << 4; \
|
||||
num |= hex_to_bin(*p++); \
|
||||
new_addr[i] = num; \
|
||||
} \
|
||||
if (!is_valid_ether_addr(new_addr)) \
|
||||
return -EINVAL; \
|
||||
memcpy(opts->net_opts.host_mac, new_addr, ETH_ALEN); \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, host_addr)
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_QMULT(_f_) \
|
||||
static ssize_t _f_##_opts_qmult_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
return sysfs_emit(page, "%u\n", opts->net_opts.qmult); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_qmult_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u32 val; \
|
||||
int ret; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
ret = kstrtou32(page, 0, &val); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
\
|
||||
opts->net_opts.qmult = val; \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, qmult)
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_IFNAME(_f_) \
|
||||
static ssize_t _f_##_opts_ifname_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
const char *name; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
rtnl_lock(); \
|
||||
if (opts->net_opts.ifname_set) \
|
||||
name = opts->net_opts.name; \
|
||||
else if (opts->net) \
|
||||
name = netdev_name(opts->net); \
|
||||
else \
|
||||
name = "(inactive net_device)"; \
|
||||
rtnl_unlock(); \
|
||||
return sysfs_emit(page, "%s\n", name); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_ifname_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
char tmp[IFNAMSIZ]; \
|
||||
const char *p; \
|
||||
size_t c_len = len; \
|
||||
\
|
||||
if (c_len > 0 && page[c_len - 1] == '\n') \
|
||||
c_len--; \
|
||||
\
|
||||
if (c_len >= sizeof(tmp)) \
|
||||
return -E2BIG; \
|
||||
\
|
||||
strscpy(tmp, page, c_len + 1); \
|
||||
if (!dev_valid_name(tmp)) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
/* Require exactly one %d */ \
|
||||
p = strchr(tmp, '%'); \
|
||||
if (!p || p[1] != 'd' || strchr(p + 2, '%')) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
strscpy(opts->net_opts.name, tmp, sizeof(opts->net_opts.name)); \
|
||||
opts->net_opts.ifname_set = true; \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, ifname)
|
||||
|
||||
#endif /* __U_ETHER_CONFIGFS_H */
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct f_midi_opts {
|
|||
struct usb_function_instance func_inst;
|
||||
int index;
|
||||
char *id;
|
||||
bool id_allocated;
|
||||
char *interface_string;
|
||||
unsigned int in_ports;
|
||||
unsigned int out_ports;
|
||||
unsigned int buflen;
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
|
||||
struct f_ncm_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
|
||||
struct gether_opts net_opts;
|
||||
struct config_group *ncm_interf_group;
|
||||
struct usb_os_desc ncm_os_desc;
|
||||
char ncm_ext_compat_id[16];
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ static void uvcg_config_item_release(struct config_item *item)
|
|||
kfree(group);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_config_item_ops = {
|
||||
static const struct configfs_item_operations uvcg_config_item_ops = {
|
||||
.release = uvcg_config_item_release,
|
||||
};
|
||||
|
||||
|
|
@ -285,7 +285,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
|
|||
return &h->item;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_control_header_grp_ops = {
|
||||
static const struct configfs_group_operations uvcg_control_header_grp_ops = {
|
||||
.make_item = uvcg_control_header_make,
|
||||
};
|
||||
|
||||
|
|
@ -1233,7 +1233,7 @@ static void uvcg_extension_drop_link(struct config_item *src, struct config_item
|
|||
mutex_unlock(su_mutex);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_extension_item_ops = {
|
||||
static const struct configfs_item_operations uvcg_extension_item_ops = {
|
||||
.release = uvcg_extension_release,
|
||||
.allow_link = uvcg_extension_allow_link,
|
||||
.drop_link = uvcg_extension_drop_link,
|
||||
|
|
@ -1298,7 +1298,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const
|
|||
return &xu->item;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_extensions_grp_ops = {
|
||||
static const struct configfs_group_operations uvcg_extensions_grp_ops = {
|
||||
.make_item = uvcg_extension_make,
|
||||
.drop_item = uvcg_extension_drop,
|
||||
};
|
||||
|
|
@ -1414,7 +1414,7 @@ out:
|
|||
mutex_unlock(su_mutex);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_control_class_item_ops = {
|
||||
static const struct configfs_item_operations uvcg_control_class_item_ops = {
|
||||
.release = uvcg_config_item_release,
|
||||
.allow_link = uvcg_control_class_allow_link,
|
||||
.drop_link = uvcg_control_class_drop_link,
|
||||
|
|
@ -1664,7 +1664,7 @@ static void uvcg_format_drop_link(struct config_item *src, struct config_item *t
|
|||
mutex_unlock(su_mutex);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_format_item_operations = {
|
||||
static const struct configfs_item_operations uvcg_format_item_operations = {
|
||||
.release = uvcg_config_item_release,
|
||||
.allow_link = uvcg_format_allow_link,
|
||||
.drop_link = uvcg_format_drop_link,
|
||||
|
|
@ -1840,7 +1840,7 @@ out:
|
|||
mutex_unlock(su_mutex);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_streaming_header_item_ops = {
|
||||
static const struct configfs_item_operations uvcg_streaming_header_item_ops = {
|
||||
.release = uvcg_config_item_release,
|
||||
.allow_link = uvcg_streaming_header_allow_link,
|
||||
.drop_link = uvcg_streaming_header_drop_link,
|
||||
|
|
@ -1914,7 +1914,7 @@ static struct config_item
|
|||
return &h->item;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
|
||||
static const struct configfs_group_operations uvcg_streaming_header_grp_ops = {
|
||||
.make_item = uvcg_streaming_header_make,
|
||||
};
|
||||
|
||||
|
|
@ -2261,7 +2261,7 @@ static void uvcg_format_set_indices(struct config_group *fmt)
|
|||
* streaming/uncompressed/<NAME>
|
||||
*/
|
||||
|
||||
static struct configfs_group_operations uvcg_uncompressed_group_ops = {
|
||||
static const struct configfs_group_operations uvcg_uncompressed_group_ops = {
|
||||
.make_item = uvcg_frame_make,
|
||||
.drop_item = uvcg_frame_drop,
|
||||
};
|
||||
|
|
@ -2508,7 +2508,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
|
|||
return &h->fmt.group;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
|
||||
static const struct configfs_group_operations uvcg_uncompressed_grp_ops = {
|
||||
.make_group = uvcg_uncompressed_make,
|
||||
};
|
||||
|
||||
|
|
@ -2525,7 +2525,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
|
|||
* streaming/mjpeg/<NAME>
|
||||
*/
|
||||
|
||||
static struct configfs_group_operations uvcg_mjpeg_group_ops = {
|
||||
static const struct configfs_group_operations uvcg_mjpeg_group_ops = {
|
||||
.make_item = uvcg_frame_make,
|
||||
.drop_item = uvcg_frame_drop,
|
||||
};
|
||||
|
|
@ -2698,7 +2698,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
|
|||
return &h->fmt.group;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
|
||||
static const struct configfs_group_operations uvcg_mjpeg_grp_ops = {
|
||||
.make_group = uvcg_mjpeg_make,
|
||||
};
|
||||
|
||||
|
|
@ -2715,7 +2715,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
|
|||
* streaming/framebased/<NAME>
|
||||
*/
|
||||
|
||||
static struct configfs_group_operations uvcg_framebased_group_ops = {
|
||||
static const struct configfs_group_operations uvcg_framebased_group_ops = {
|
||||
.make_item = uvcg_frame_make,
|
||||
.drop_item = uvcg_frame_drop,
|
||||
};
|
||||
|
|
@ -2953,7 +2953,7 @@ static struct config_group *uvcg_framebased_make(struct config_group *group,
|
|||
return &h->fmt.group;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_framebased_grp_ops = {
|
||||
static const struct configfs_group_operations uvcg_framebased_grp_ops = {
|
||||
.make_group = uvcg_framebased_make,
|
||||
};
|
||||
|
||||
|
|
@ -3056,7 +3056,7 @@ static void uvcg_color_matching_release(struct config_item *item)
|
|||
kfree(color_match);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_color_matching_item_ops = {
|
||||
static const struct configfs_item_operations uvcg_color_matching_item_ops = {
|
||||
.release = uvcg_color_matching_release,
|
||||
};
|
||||
|
||||
|
|
@ -3089,7 +3089,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group,
|
|||
return &color_match->group;
|
||||
}
|
||||
|
||||
static struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
|
||||
static const struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
|
||||
.make_group = uvcg_color_matching_make,
|
||||
};
|
||||
|
||||
|
|
@ -3530,7 +3530,7 @@ out:
|
|||
mutex_unlock(su_mutex);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvcg_streaming_class_item_ops = {
|
||||
static const struct configfs_item_operations uvcg_streaming_class_item_ops = {
|
||||
.release = uvcg_config_item_release,
|
||||
.allow_link = uvcg_streaming_class_allow_link,
|
||||
.drop_link = uvcg_streaming_class_drop_link,
|
||||
|
|
@ -3698,7 +3698,7 @@ static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt)
|
|||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations uvc_func_item_ops = {
|
||||
static const struct configfs_item_operations uvc_func_item_ops = {
|
||||
.release = uvc_func_item_release,
|
||||
.allow_link = uvc_func_allow_link,
|
||||
.drop_link = uvc_func_drop_link,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "vhub.h"
|
||||
|
||||
|
|
@ -280,6 +281,8 @@ static void ast_vhub_remove(struct platform_device *pdev)
|
|||
if (vhub->clk)
|
||||
clk_disable_unprepare(vhub->clk);
|
||||
|
||||
reset_control_assert(vhub->rst);
|
||||
|
||||
spin_unlock_irqrestore(&vhub->lock, flags);
|
||||
|
||||
if (vhub->ep0_bufs)
|
||||
|
|
@ -294,6 +297,7 @@ static void ast_vhub_remove(struct platform_device *pdev)
|
|||
static int ast_vhub_probe(struct platform_device *pdev)
|
||||
{
|
||||
enum usb_device_speed max_speed;
|
||||
const u64 *dma_mask_ptr;
|
||||
struct ast_vhub *vhub;
|
||||
struct resource *res;
|
||||
int i, rc = 0;
|
||||
|
|
@ -348,6 +352,16 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
vhub->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
|
||||
if (IS_ERR(vhub->rst)) {
|
||||
rc = PTR_ERR(vhub->rst);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = reset_control_deassert(vhub->rst);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
/* Check if we need to limit the HW to USB1 */
|
||||
max_speed = usb_get_maximum_speed(&pdev->dev);
|
||||
if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH)
|
||||
|
|
@ -370,6 +384,12 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev);
|
||||
if (dma_mask_ptr) {
|
||||
rc = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr);
|
||||
if (rc)
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
* Allocate DMA buffers for all EP0s in one chunk,
|
||||
* one per port and one for the vHub itself
|
||||
|
|
@ -412,15 +432,25 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static const u64 dma_mask_32 = DMA_BIT_MASK(32);
|
||||
static const u64 dma_mask_64 = DMA_BIT_MASK(64);
|
||||
|
||||
static const struct of_device_id ast_vhub_dt_ids[] = {
|
||||
{
|
||||
.compatible = "aspeed,ast2400-usb-vhub",
|
||||
.data = &dma_mask_32,
|
||||
},
|
||||
{
|
||||
.compatible = "aspeed,ast2500-usb-vhub",
|
||||
.data = &dma_mask_32,
|
||||
},
|
||||
{
|
||||
.compatible = "aspeed,ast2600-usb-vhub",
|
||||
.data = &dma_mask_32,
|
||||
},
|
||||
{
|
||||
.compatible = "aspeed,ast2700-usb-vhub",
|
||||
.data = &dma_mask_64,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ struct ast_vhub {
|
|||
spinlock_t lock;
|
||||
struct work_struct wake_work;
|
||||
struct clk *clk;
|
||||
struct reset_control *rst;
|
||||
|
||||
/* EP0 DMA buffers allocated in one chunk */
|
||||
void *ep0_bufs;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ static int poll_oip(struct bdc *bdc, u32 usec)
|
|||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status,
|
||||
ret = readl_poll_timeout_atomic(bdc->regs + BDC_BDCSC, status,
|
||||
(BDC_CSTS(status) != BDC_OIP), 10, usec);
|
||||
if (ret)
|
||||
dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
|
||||
|
|
|
|||
|
|
@ -3392,17 +3392,18 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
|
|||
{
|
||||
u32 val, imod;
|
||||
|
||||
if (xudc->soc->has_ipfs) {
|
||||
val = xudc_readl(xudc, BLCG);
|
||||
if (xudc->soc->has_ipfs) {
|
||||
val |= BLCG_ALL;
|
||||
val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE |
|
||||
BLCG_COREPLL_PWRDN);
|
||||
val |= BLCG_IOPLL_0_PWRDN;
|
||||
val |= BLCG_IOPLL_1_PWRDN;
|
||||
val |= BLCG_IOPLL_2_PWRDN;
|
||||
|
||||
xudc_writel(xudc, val, BLCG);
|
||||
} else {
|
||||
val &= ~BLCG_COREPLL_PWRDN;
|
||||
}
|
||||
xudc_writel(xudc, val, BLCG);
|
||||
|
||||
if (xudc->soc->port_speed_quirk)
|
||||
tegra_xudc_limit_port_speed(xudc);
|
||||
|
|
@ -3953,6 +3954,7 @@ static void tegra_xudc_remove(struct platform_device *pdev)
|
|||
static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
dev_dbg(xudc->dev, "entering ELPG\n");
|
||||
|
||||
|
|
@ -3965,6 +3967,10 @@ static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
|
|||
|
||||
spin_unlock_irqrestore(&xudc->lock, flags);
|
||||
|
||||
val = xudc_readl(xudc, BLCG);
|
||||
val |= BLCG_COREPLL_PWRDN;
|
||||
xudc_writel(xudc, val, BLCG);
|
||||
|
||||
clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks);
|
||||
|
||||
regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
|
||||
|
|
|
|||
|
|
@ -383,18 +383,6 @@ config USB_ISP116X_HCD
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called isp116x-hcd.
|
||||
|
||||
config USB_ISP1362_HCD
|
||||
tristate "ISP1362 HCD support"
|
||||
depends on HAS_IOPORT
|
||||
depends on COMPILE_TEST # nothing uses this
|
||||
help
|
||||
Supports the Philips ISP1362 chip as a host controller
|
||||
|
||||
This driver does not support isochronous transfers.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called isp1362-hcd.
|
||||
|
||||
config USB_MAX3421_HCD
|
||||
tristate "MAX3421 HCD (USB-over-SPI) support"
|
||||
depends on USB && SPI
|
||||
|
|
@ -616,7 +604,7 @@ config USB_UHCI_ASPEED
|
|||
|
||||
config USB_FHCI_HCD
|
||||
tristate "Freescale QE USB Host Controller support"
|
||||
depends on OF_GPIO && QE_GPIO && QUICC_ENGINE
|
||||
depends on QE_GPIO && QUICC_ENGINE
|
||||
select FSL_GTM
|
||||
select QE_USB
|
||||
help
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o
|
|||
|
||||
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
|
||||
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
|
||||
obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o
|
||||
|
||||
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
|
||||
obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
|
||||
|
|
|
|||
|
|
@ -1354,12 +1354,6 @@ static int __init ehci_hcd_init(void)
|
|||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
|
||||
if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
|
||||
test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
|
||||
printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
|
||||
" before uhci_hcd and ohci_hcd, not after\n");
|
||||
|
||||
pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n",
|
||||
hcd_name,
|
||||
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
|
||||
|
|
@ -1390,7 +1384,6 @@ clean0:
|
|||
debugfs_remove(ehci_debug_root);
|
||||
ehci_debug_root = NULL;
|
||||
#endif
|
||||
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
|
||||
return retval;
|
||||
}
|
||||
module_init(ehci_hcd_init);
|
||||
|
|
@ -1404,6 +1397,5 @@ static void __exit ehci_hcd_cleanup(void)
|
|||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
debugfs_remove(ehci_debug_root);
|
||||
#endif
|
||||
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
|
||||
}
|
||||
module_exit(ehci_hcd_cleanup);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,914 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* ISP1362 HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de>
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#define MAX_ROOT_PORTS 2
|
||||
|
||||
#define USE_32BIT 0
|
||||
|
||||
/* These options are mutually exclusive */
|
||||
#define USE_PLATFORM_DELAY 0
|
||||
#define USE_NDELAY 0
|
||||
|
||||
#define DUMMY_DELAY_ACCESS do {} while (0)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#define USB_RESET_WIDTH 50
|
||||
#define MAX_XFER_SIZE 1023
|
||||
|
||||
/* Buffer sizes */
|
||||
#define ISP1362_BUF_SIZE 4096
|
||||
#define ISP1362_ISTL_BUFSIZE 512
|
||||
#define ISP1362_INTL_BLKSIZE 64
|
||||
#define ISP1362_INTL_BUFFERS 16
|
||||
#define ISP1362_ATL_BLKSIZE 64
|
||||
|
||||
#define ISP1362_REG_WRITE_OFFSET 0x80
|
||||
|
||||
#define REG_WIDTH_16 0x000
|
||||
#define REG_WIDTH_32 0x100
|
||||
#define REG_WIDTH_MASK 0x100
|
||||
#define REG_NO_MASK 0x0ff
|
||||
|
||||
#ifdef ISP1362_DEBUG
|
||||
typedef const unsigned int isp1362_reg_t;
|
||||
|
||||
#define REG_ACCESS_R 0x200
|
||||
#define REG_ACCESS_W 0x400
|
||||
#define REG_ACCESS_RW 0x600
|
||||
#define REG_ACCESS_MASK 0x600
|
||||
|
||||
#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK)
|
||||
|
||||
#define ISP1362_REG(name, addr, width, rw) \
|
||||
static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw))
|
||||
|
||||
#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W))
|
||||
#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w))
|
||||
#else
|
||||
typedef const unsigned char isp1362_reg_t;
|
||||
#define ISP1362_REG_NO(r) (r)
|
||||
|
||||
#define ISP1362_REG(name, addr, width, rw) \
|
||||
static isp1362_reg_t __maybe_unused ISP1362_REG_##name = addr
|
||||
|
||||
#define REG_ACCESS_TEST(r) do {} while (0)
|
||||
#define REG_WIDTH_TEST(r, w) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* OHCI compatible registers */
|
||||
/*
|
||||
* Note: Some of the ISP1362 'OHCI' registers implement only
|
||||
* a subset of the bits defined in the OHCI spec.
|
||||
*
|
||||
* Bitmasks for the individual bits of these registers are defined in "ohci.h"
|
||||
*/
|
||||
ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R);
|
||||
ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
|
||||
/* Philips ISP1362 specific registers */
|
||||
ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
#define HCHWCFG_DISABLE_SUSPEND (1 << 15)
|
||||
#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14)
|
||||
#define HCHWCFG_PULLDOWN_DS2 (1 << 13)
|
||||
#define HCHWCFG_PULLDOWN_DS1 (1 << 12)
|
||||
#define HCHWCFG_CLKNOTSTOP (1 << 11)
|
||||
#define HCHWCFG_ANALOG_OC (1 << 10)
|
||||
#define HCHWCFG_ONEINT (1 << 9)
|
||||
#define HCHWCFG_DACK_MODE (1 << 8)
|
||||
#define HCHWCFG_ONEDMA (1 << 7)
|
||||
#define HCHWCFG_DACK_POL (1 << 6)
|
||||
#define HCHWCFG_DREQ_POL (1 << 5)
|
||||
#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
|
||||
#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
|
||||
#define HCHWCFG_INT_POL (1 << 2)
|
||||
#define HCHWCFG_INT_TRIGGER (1 << 1)
|
||||
#define HCHWCFG_INT_ENABLE (1 << 0)
|
||||
|
||||
ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
#define HCDMACFG_CTR_ENABLE (1 << 7)
|
||||
#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
|
||||
#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
|
||||
#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
|
||||
#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
|
||||
#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
|
||||
#define HCDMACFG_DMA_ENABLE (1 << 4)
|
||||
#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
|
||||
#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK)
|
||||
#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0)
|
||||
#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1)
|
||||
#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2)
|
||||
#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3)
|
||||
#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4)
|
||||
#define HCDMACFG_DMA_RW_SELECT (1 << 0)
|
||||
|
||||
ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
|
||||
ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
#define HCuPINT_SOF (1 << 0)
|
||||
#define HCuPINT_ISTL0 (1 << 1)
|
||||
#define HCuPINT_ISTL1 (1 << 2)
|
||||
#define HCuPINT_EOT (1 << 3)
|
||||
#define HCuPINT_OPR (1 << 4)
|
||||
#define HCuPINT_SUSP (1 << 5)
|
||||
#define HCuPINT_CLKRDY (1 << 6)
|
||||
#define HCuPINT_INTL (1 << 7)
|
||||
#define HCuPINT_ATL (1 << 8)
|
||||
#define HCuPINT_OTG (1 << 9)
|
||||
|
||||
ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
/* same bit definitions apply as for HCuPINT */
|
||||
|
||||
ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R);
|
||||
#define HCCHIPID_MASK 0xff00
|
||||
#define HCCHIPID_MAGIC 0x3600
|
||||
|
||||
ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
|
||||
ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W);
|
||||
#define HCSWRES_MAGIC 0x00f6
|
||||
|
||||
ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
#define HCBUFSTAT_ISTL0_FULL (1 << 0)
|
||||
#define HCBUFSTAT_ISTL1_FULL (1 << 1)
|
||||
#define HCBUFSTAT_INTL_ACTIVE (1 << 2)
|
||||
#define HCBUFSTAT_ATL_ACTIVE (1 << 3)
|
||||
#define HCBUFSTAT_RESET_HWPP (1 << 4)
|
||||
#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5)
|
||||
#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6)
|
||||
#define HCBUFSTAT_ISTL0_DONE (1 << 8)
|
||||
#define HCBUFSTAT_ISTL1_DONE (1 << 9)
|
||||
#define HCBUFSTAT_PAIRED_PTDPP (1 << 10)
|
||||
|
||||
ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
#define HCDIRADDR_ADDR_MASK 0x0000ffff
|
||||
#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK)
|
||||
#define HCDIRADDR_COUNT_MASK 0xffff0000
|
||||
#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK)
|
||||
ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
|
||||
ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
|
||||
ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R);
|
||||
ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R);
|
||||
|
||||
ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R);
|
||||
ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R);
|
||||
|
||||
ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
|
||||
|
||||
ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R);
|
||||
ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW);
|
||||
|
||||
/* Philips transfer descriptor, cpu-endian */
|
||||
struct ptd {
|
||||
u16 count;
|
||||
#define PTD_COUNT_MSK (0x3ff << 0)
|
||||
#define PTD_TOGGLE_MSK (1 << 10)
|
||||
#define PTD_ACTIVE_MSK (1 << 11)
|
||||
#define PTD_CC_MSK (0xf << 12)
|
||||
u16 mps;
|
||||
#define PTD_MPS_MSK (0x3ff << 0)
|
||||
#define PTD_SPD_MSK (1 << 10)
|
||||
#define PTD_LAST_MSK (1 << 11)
|
||||
#define PTD_EP_MSK (0xf << 12)
|
||||
u16 len;
|
||||
#define PTD_LEN_MSK (0x3ff << 0)
|
||||
#define PTD_DIR_MSK (3 << 10)
|
||||
#define PTD_DIR_SETUP (0)
|
||||
#define PTD_DIR_OUT (1)
|
||||
#define PTD_DIR_IN (2)
|
||||
u16 faddr;
|
||||
#define PTD_FA_MSK (0x7f << 0)
|
||||
/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */
|
||||
#define PTD_SF_ISO_MSK (0xff << 8)
|
||||
#define PTD_SF_INT_MSK (0x1f << 8)
|
||||
#define PTD_PR_MSK (0x07 << 13)
|
||||
} __attribute__ ((packed, aligned(2)));
|
||||
#define PTD_HEADER_SIZE sizeof(struct ptd)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Copied from ohci.h: */
|
||||
/*
|
||||
* Hardware transfer status codes -- CC from PTD
|
||||
*/
|
||||
#define PTD_CC_NOERROR 0x00
|
||||
#define PTD_CC_CRC 0x01
|
||||
#define PTD_CC_BITSTUFFING 0x02
|
||||
#define PTD_CC_DATATOGGLEM 0x03
|
||||
#define PTD_CC_STALL 0x04
|
||||
#define PTD_DEVNOTRESP 0x05
|
||||
#define PTD_PIDCHECKFAIL 0x06
|
||||
#define PTD_UNEXPECTEDPID 0x07
|
||||
#define PTD_DATAOVERRUN 0x08
|
||||
#define PTD_DATAUNDERRUN 0x09
|
||||
/* 0x0A, 0x0B reserved for hardware */
|
||||
#define PTD_BUFFEROVERRUN 0x0C
|
||||
#define PTD_BUFFERUNDERRUN 0x0D
|
||||
/* 0x0E, 0x0F reserved for HCD */
|
||||
#define PTD_NOTACCESSED 0x0F
|
||||
|
||||
|
||||
/* map OHCI TD status codes (CC) to errno values */
|
||||
static const int cc_to_error[16] = {
|
||||
/* No Error */ 0,
|
||||
/* CRC Error */ -EILSEQ,
|
||||
/* Bit Stuff */ -EPROTO,
|
||||
/* Data Togg */ -EILSEQ,
|
||||
/* Stall */ -EPIPE,
|
||||
/* DevNotResp */ -ETIMEDOUT,
|
||||
/* PIDCheck */ -EPROTO,
|
||||
/* UnExpPID */ -EPROTO,
|
||||
/* DataOver */ -EOVERFLOW,
|
||||
/* DataUnder */ -EREMOTEIO,
|
||||
/* (for hw) */ -EIO,
|
||||
/* (for hw) */ -EIO,
|
||||
/* BufferOver */ -ECOMM,
|
||||
/* BuffUnder */ -ENOSR,
|
||||
/* (for HCD) */ -EALREADY,
|
||||
/* (for HCD) */ -EALREADY
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* HcControl (control) register masks
|
||||
*/
|
||||
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
|
||||
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
|
||||
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
|
||||
|
||||
/* pre-shifted values for HCFS */
|
||||
# define OHCI_USB_RESET (0 << 6)
|
||||
# define OHCI_USB_RESUME (1 << 6)
|
||||
# define OHCI_USB_OPER (2 << 6)
|
||||
# define OHCI_USB_SUSPEND (3 << 6)
|
||||
|
||||
/*
|
||||
* HcCommandStatus (cmdstatus) register masks
|
||||
*/
|
||||
#define OHCI_HCR (1 << 0) /* host controller reset */
|
||||
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
|
||||
|
||||
/*
|
||||
* masks used with interrupt registers:
|
||||
* HcInterruptStatus (intrstatus)
|
||||
* HcInterruptEnable (intrenable)
|
||||
* HcInterruptDisable (intrdisable)
|
||||
*/
|
||||
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
|
||||
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
|
||||
#define OHCI_INTR_SF (1 << 2) /* start frame */
|
||||
#define OHCI_INTR_RD (1 << 3) /* resume detect */
|
||||
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
|
||||
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
|
||||
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
|
||||
#define OHCI_INTR_OC (1 << 30) /* ownership change */
|
||||
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
|
||||
|
||||
/* roothub.portstatus [i] bits */
|
||||
#define RH_PS_CCS 0x00000001 /* current connect status */
|
||||
#define RH_PS_PES 0x00000002 /* port enable status*/
|
||||
#define RH_PS_PSS 0x00000004 /* port suspend status */
|
||||
#define RH_PS_POCI 0x00000008 /* port over current indicator */
|
||||
#define RH_PS_PRS 0x00000010 /* port reset status */
|
||||
#define RH_PS_PPS 0x00000100 /* port power status */
|
||||
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
|
||||
#define RH_PS_CSC 0x00010000 /* connect status change */
|
||||
#define RH_PS_PESC 0x00020000 /* port enable status change */
|
||||
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
|
||||
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
|
||||
#define RH_PS_PRSC 0x00100000 /* port reset status change */
|
||||
|
||||
/* roothub.status bits */
|
||||
#define RH_HS_LPS 0x00000001 /* local power status */
|
||||
#define RH_HS_OCI 0x00000002 /* over current indicator */
|
||||
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
|
||||
#define RH_HS_LPSC 0x00010000 /* local power status change */
|
||||
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
|
||||
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
|
||||
|
||||
/* roothub.b masks */
|
||||
#define RH_B_DR 0x0000ffff /* device removable flags */
|
||||
#define RH_B_PPCM 0xffff0000 /* port power control mask */
|
||||
|
||||
/* roothub.a masks */
|
||||
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
|
||||
#define RH_A_PSM (1 << 8) /* power switching mode */
|
||||
#define RH_A_NPS (1 << 9) /* no power switching */
|
||||
#define RH_A_DT (1 << 10) /* device type (mbz) */
|
||||
#define RH_A_OCPM (1 << 11) /* over current protection mode */
|
||||
#define RH_A_NOCP (1 << 12) /* no over current protection */
|
||||
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
|
||||
|
||||
#define FI 0x2edf /* 12000 bits per frame (-1) */
|
||||
#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
|
||||
#define LSTHRESH 0x628 /* lowspeed bit threshold */
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* PTD accessor macros. */
|
||||
#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
|
||||
#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
|
||||
#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
|
||||
#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
|
||||
#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
|
||||
#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
|
||||
#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
|
||||
#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
|
||||
#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
|
||||
#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
|
||||
#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
|
||||
#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
|
||||
#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
|
||||
#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
|
||||
#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
|
||||
#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
|
||||
#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
|
||||
#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
|
||||
#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
|
||||
#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
|
||||
#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
|
||||
#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
|
||||
#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8)
|
||||
#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK)
|
||||
#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8)
|
||||
#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK)
|
||||
#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13)
|
||||
#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK)
|
||||
|
||||
#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
|
||||
#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
|
||||
|
||||
struct isp1362_ep {
|
||||
struct usb_host_endpoint *hep;
|
||||
struct usb_device *udev;
|
||||
|
||||
/* philips transfer descriptor */
|
||||
struct ptd ptd;
|
||||
|
||||
u8 maxpacket;
|
||||
u8 epnum;
|
||||
u8 nextpid;
|
||||
u16 error_count;
|
||||
u16 length; /* of current packet */
|
||||
s16 ptd_offset; /* buffer offset in ISP1362 where
|
||||
PTD has been stored
|
||||
(for access thru HCDIRDATA) */
|
||||
int ptd_index;
|
||||
int num_ptds;
|
||||
void *data; /* to databuf */
|
||||
/* queue of active EPs (the ones transmitted to the chip) */
|
||||
struct list_head active;
|
||||
|
||||
/* periodic schedule */
|
||||
u8 branch;
|
||||
u16 interval;
|
||||
u16 load;
|
||||
u16 last_iso;
|
||||
|
||||
/* async schedule */
|
||||
struct list_head schedule; /* list of all EPs that need processing */
|
||||
struct list_head remove_list;
|
||||
int num_req;
|
||||
};
|
||||
|
||||
struct isp1362_ep_queue {
|
||||
struct list_head active; /* list of PTDs currently processed by HC */
|
||||
atomic_t finishing;
|
||||
unsigned long buf_map;
|
||||
unsigned long skip_map;
|
||||
int free_ptd;
|
||||
u16 buf_start;
|
||||
u16 buf_size;
|
||||
u16 blk_size; /* PTD buffer block size for ATL and INTL */
|
||||
u8 buf_count;
|
||||
u8 buf_avail;
|
||||
char name[16];
|
||||
|
||||
/* for statistical tracking */
|
||||
u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */
|
||||
u8 ptd_count; /* number of ptds submitted to this queue */
|
||||
};
|
||||
|
||||
struct isp1362_hcd {
|
||||
spinlock_t lock;
|
||||
void __iomem *addr_reg;
|
||||
void __iomem *data_reg;
|
||||
|
||||
struct isp1362_platform_data *board;
|
||||
|
||||
unsigned long stat1, stat2, stat4, stat8, stat16;
|
||||
|
||||
/* HC registers */
|
||||
u32 intenb; /* "OHCI" interrupts */
|
||||
u16 irqenb; /* uP interrupts */
|
||||
|
||||
/* Root hub registers */
|
||||
u32 rhdesca;
|
||||
u32 rhdescb;
|
||||
u32 rhstatus;
|
||||
u32 rhport[MAX_ROOT_PORTS];
|
||||
unsigned long next_statechange;
|
||||
|
||||
/* HC control reg shadow copy */
|
||||
u32 hc_control;
|
||||
|
||||
/* async schedule: control, bulk */
|
||||
struct list_head async;
|
||||
|
||||
/* periodic schedule: int */
|
||||
u16 load[PERIODIC_SIZE];
|
||||
struct list_head periodic;
|
||||
u16 fmindex;
|
||||
|
||||
/* periodic schedule: isochronous */
|
||||
struct list_head isoc;
|
||||
unsigned int istl_flip:1;
|
||||
unsigned int irq_active:1;
|
||||
|
||||
/* Schedules for the current frame */
|
||||
struct isp1362_ep_queue atl_queue;
|
||||
struct isp1362_ep_queue intl_queue;
|
||||
struct isp1362_ep_queue istl_queue[2];
|
||||
|
||||
/* list of PTDs retrieved from HC */
|
||||
struct list_head remove_list;
|
||||
enum {
|
||||
ISP1362_INT_SOF,
|
||||
ISP1362_INT_ISTL0,
|
||||
ISP1362_INT_ISTL1,
|
||||
ISP1362_INT_EOT,
|
||||
ISP1362_INT_OPR,
|
||||
ISP1362_INT_SUSP,
|
||||
ISP1362_INT_CLKRDY,
|
||||
ISP1362_INT_INTL,
|
||||
ISP1362_INT_ATL,
|
||||
ISP1362_INT_OTG,
|
||||
NUM_ISP1362_IRQS
|
||||
} IRQ_NAMES;
|
||||
unsigned int irq_stat[NUM_ISP1362_IRQS];
|
||||
int req_serial;
|
||||
};
|
||||
|
||||
static inline const char *ISP1362_INT_NAME(int n)
|
||||
{
|
||||
switch (n) {
|
||||
case ISP1362_INT_SOF: return "SOF";
|
||||
case ISP1362_INT_ISTL0: return "ISTL0";
|
||||
case ISP1362_INT_ISTL1: return "ISTL1";
|
||||
case ISP1362_INT_EOT: return "EOT";
|
||||
case ISP1362_INT_OPR: return "OPR";
|
||||
case ISP1362_INT_SUSP: return "SUSP";
|
||||
case ISP1362_INT_CLKRDY: return "CLKRDY";
|
||||
case ISP1362_INT_INTL: return "INTL";
|
||||
case ISP1362_INT_ATL: return "ATL";
|
||||
case ISP1362_INT_OTG: return "OTG";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr)
|
||||
{
|
||||
unsigned long p = (unsigned long)ptr;
|
||||
if (!(p & 0xf))
|
||||
isp1362_hcd->stat16++;
|
||||
else if (!(p & 0x7))
|
||||
isp1362_hcd->stat8++;
|
||||
else if (!(p & 0x3))
|
||||
isp1362_hcd->stat4++;
|
||||
else if (!(p & 0x1))
|
||||
isp1362_hcd->stat2++;
|
||||
else
|
||||
isp1362_hcd->stat1++;
|
||||
}
|
||||
|
||||
static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct isp1362_hcd *) (hcd->hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd)
|
||||
{
|
||||
return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv);
|
||||
}
|
||||
|
||||
#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0)
|
||||
|
||||
/*
|
||||
* ISP1362 HW Interface
|
||||
*/
|
||||
|
||||
#define DBG(level, fmt...) \
|
||||
do { \
|
||||
if (dbg_level > level) \
|
||||
pr_debug(fmt); \
|
||||
} while (0)
|
||||
|
||||
#ifdef VERBOSE
|
||||
# define VDBG(fmt...) DBG(3, fmt)
|
||||
#else
|
||||
# define VDBG(fmt...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef REGISTERS
|
||||
# define RDBG(fmt...) DBG(1, fmt)
|
||||
#else
|
||||
# define RDBG(fmt...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef URB_TRACE
|
||||
#define URB_DBG(fmt...) DBG(0, fmt)
|
||||
#else
|
||||
#define URB_DBG(fmt...) do {} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#if USE_PLATFORM_DELAY
|
||||
#if USE_NDELAY
|
||||
#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously.
|
||||
#endif
|
||||
#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d)
|
||||
#elif USE_NDELAY
|
||||
#define isp1362_delay(h, d) ndelay(d)
|
||||
#else
|
||||
#define isp1362_delay(h, d) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define get_urb(ep) ({ \
|
||||
BUG_ON(list_empty(&ep->hep->urb_list)); \
|
||||
container_of(ep->hep->urb_list.next, struct urb, urb_list); \
|
||||
})
|
||||
|
||||
/* basic access functions for ISP1362 chip registers */
|
||||
/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure,
|
||||
* that all register accesses are performed with interrupts disabled, since the interrupt
|
||||
* handler has no way of restoring the previous state.
|
||||
*/
|
||||
static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg)
|
||||
{
|
||||
REG_ACCESS_TEST(reg);
|
||||
DUMMY_DELAY_ACCESS;
|
||||
writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg);
|
||||
DUMMY_DELAY_ACCESS;
|
||||
isp1362_delay(isp1362_hcd, 1);
|
||||
}
|
||||
|
||||
static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val)
|
||||
{
|
||||
DUMMY_DELAY_ACCESS;
|
||||
writew(val, isp1362_hcd->data_reg);
|
||||
}
|
||||
|
||||
static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
DUMMY_DELAY_ACCESS;
|
||||
val = readw(isp1362_hcd->data_reg);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val)
|
||||
{
|
||||
#if USE_32BIT
|
||||
DUMMY_DELAY_ACCESS;
|
||||
writel(val, isp1362_hcd->data_reg);
|
||||
#else
|
||||
DUMMY_DELAY_ACCESS;
|
||||
writew((u16)val, isp1362_hcd->data_reg);
|
||||
DUMMY_DELAY_ACCESS;
|
||||
writew(val >> 16, isp1362_hcd->data_reg);
|
||||
#endif
|
||||
}
|
||||
|
||||
static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
#if USE_32BIT
|
||||
DUMMY_DELAY_ACCESS;
|
||||
val = readl(isp1362_hcd->data_reg);
|
||||
#else
|
||||
DUMMY_DELAY_ACCESS;
|
||||
val = (u32)readw(isp1362_hcd->data_reg);
|
||||
DUMMY_DELAY_ACCESS;
|
||||
val |= (u32)readw(isp1362_hcd->data_reg) << 16;
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
/* use readsw/writesw to access the fifo whenever possible */
|
||||
/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */
|
||||
static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
|
||||
{
|
||||
u8 *dp = buf;
|
||||
u16 data;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf);
|
||||
#if USE_32BIT
|
||||
if (len >= 4) {
|
||||
RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2);
|
||||
readsl(isp1362_hcd->data_reg, dp, len >> 2);
|
||||
dp += len & ~3;
|
||||
len &= 3;
|
||||
}
|
||||
#endif
|
||||
if (len >= 2) {
|
||||
RDBG("%s: Using readsw for %d words\n", __func__, len >> 1);
|
||||
insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
|
||||
dp += len & ~1;
|
||||
len &= 1;
|
||||
}
|
||||
|
||||
BUG_ON(len & ~1);
|
||||
if (len > 0) {
|
||||
data = isp1362_read_data16(isp1362_hcd);
|
||||
RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__,
|
||||
(u8)data, (u32)dp);
|
||||
*dp = (u8)data;
|
||||
}
|
||||
}
|
||||
|
||||
static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
|
||||
{
|
||||
u8 *dp = buf;
|
||||
u16 data;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
if ((unsigned long)dp & 0x1) {
|
||||
/* not aligned */
|
||||
for (; len > 1; len -= 2) {
|
||||
data = *dp++;
|
||||
data |= *dp++ << 8;
|
||||
isp1362_write_data16(isp1362_hcd, data);
|
||||
}
|
||||
if (len)
|
||||
isp1362_write_data16(isp1362_hcd, *dp);
|
||||
return;
|
||||
}
|
||||
|
||||
RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf);
|
||||
#if USE_32BIT
|
||||
if (len >= 4) {
|
||||
RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2);
|
||||
writesl(isp1362_hcd->data_reg, dp, len >> 2);
|
||||
dp += len & ~3;
|
||||
len &= 3;
|
||||
}
|
||||
#endif
|
||||
if (len >= 2) {
|
||||
RDBG("%s: Using writesw for %d words\n", __func__, len >> 1);
|
||||
outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
|
||||
dp += len & ~1;
|
||||
len &= 1;
|
||||
}
|
||||
|
||||
BUG_ON(len & ~1);
|
||||
if (len > 0) {
|
||||
/* finally write any trailing byte; we don't need to care
|
||||
* about the high byte of the last word written
|
||||
*/
|
||||
data = (u16)*dp;
|
||||
RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__,
|
||||
data, (u32)dp);
|
||||
isp1362_write_data16(isp1362_hcd, data);
|
||||
}
|
||||
}
|
||||
|
||||
#define isp1362_read_reg16(d, r) ({ \
|
||||
u16 __v; \
|
||||
REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
|
||||
isp1362_write_addr(d, ISP1362_REG_##r); \
|
||||
__v = isp1362_read_data16(d); \
|
||||
RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \
|
||||
ISP1362_REG_NO(ISP1362_REG_##r)); \
|
||||
__v; \
|
||||
})
|
||||
|
||||
#define isp1362_read_reg32(d, r) ({ \
|
||||
u32 __v; \
|
||||
REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
|
||||
isp1362_write_addr(d, ISP1362_REG_##r); \
|
||||
__v = isp1362_read_data32(d); \
|
||||
RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \
|
||||
ISP1362_REG_NO(ISP1362_REG_##r)); \
|
||||
__v; \
|
||||
})
|
||||
|
||||
#define isp1362_write_reg16(d, r, v) { \
|
||||
REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
|
||||
isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
|
||||
isp1362_write_data16(d, (u16)(v)); \
|
||||
RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \
|
||||
ISP1362_REG_NO(ISP1362_REG_##r)); \
|
||||
}
|
||||
|
||||
#define isp1362_write_reg32(d, r, v) { \
|
||||
REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
|
||||
isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
|
||||
isp1362_write_data32(d, (u32)(v)); \
|
||||
RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \
|
||||
ISP1362_REG_NO(ISP1362_REG_##r)); \
|
||||
}
|
||||
|
||||
#define isp1362_set_mask16(d, r, m) { \
|
||||
u16 __v; \
|
||||
__v = isp1362_read_reg16(d, r); \
|
||||
if ((__v | m) != __v) \
|
||||
isp1362_write_reg16(d, r, __v | m); \
|
||||
}
|
||||
|
||||
#define isp1362_clr_mask16(d, r, m) { \
|
||||
u16 __v; \
|
||||
__v = isp1362_read_reg16(d, r); \
|
||||
if ((__v & ~m) != __v) \
|
||||
isp1362_write_reg16(d, r, __v & ~m); \
|
||||
}
|
||||
|
||||
#define isp1362_set_mask32(d, r, m) { \
|
||||
u32 __v; \
|
||||
__v = isp1362_read_reg32(d, r); \
|
||||
if ((__v | m) != __v) \
|
||||
isp1362_write_reg32(d, r, __v | m); \
|
||||
}
|
||||
|
||||
#define isp1362_clr_mask32(d, r, m) { \
|
||||
u32 __v; \
|
||||
__v = isp1362_read_reg32(d, r); \
|
||||
if ((__v & ~m) != __v) \
|
||||
isp1362_write_reg32(d, r, __v & ~m); \
|
||||
}
|
||||
|
||||
#define isp1362_show_reg(d, r) { \
|
||||
if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \
|
||||
DBG(0, "%-12s[%02x]: %08x\n", #r, \
|
||||
ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \
|
||||
else \
|
||||
DBG(0, "%-12s[%02x]: %04x\n", #r, \
|
||||
ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \
|
||||
}
|
||||
|
||||
static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len)
|
||||
{
|
||||
len = (len + 1) & ~1;
|
||||
|
||||
isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE);
|
||||
isp1362_write_reg32(isp1362_hcd, HCDIRADDR,
|
||||
HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len));
|
||||
}
|
||||
|
||||
static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
|
||||
{
|
||||
isp1362_write_diraddr(isp1362_hcd, offset, len);
|
||||
|
||||
DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n",
|
||||
__func__, len, offset, buf);
|
||||
|
||||
isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
|
||||
|
||||
isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA);
|
||||
|
||||
isp1362_read_fifo(isp1362_hcd, buf, len);
|
||||
isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
|
||||
}
|
||||
|
||||
static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
|
||||
{
|
||||
isp1362_write_diraddr(isp1362_hcd, offset, len);
|
||||
|
||||
DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n",
|
||||
__func__, len, offset, buf);
|
||||
|
||||
isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
|
||||
|
||||
isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET);
|
||||
isp1362_write_fifo(isp1362_hcd, buf, len);
|
||||
|
||||
isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) dump_data(char *buf, int len)
|
||||
{
|
||||
if (dbg_level > 0) {
|
||||
int k;
|
||||
int lf = 0;
|
||||
|
||||
for (k = 0; k < len; ++k) {
|
||||
if (!lf)
|
||||
DBG(0, "%04x:", k);
|
||||
printk(" %02x", ((u8 *) buf)[k]);
|
||||
lf = 1;
|
||||
if (!k)
|
||||
continue;
|
||||
if (k % 16 == 15) {
|
||||
printk("\n");
|
||||
lf = 0;
|
||||
continue;
|
||||
}
|
||||
if (k % 8 == 7)
|
||||
printk(" ");
|
||||
if (k % 4 == 3)
|
||||
printk(" ");
|
||||
}
|
||||
if (lf)
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PTD_TRACE)
|
||||
|
||||
static void dump_ptd(struct ptd *ptd)
|
||||
{
|
||||
DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n",
|
||||
container_of(ptd, struct isp1362_ep, ptd),
|
||||
PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd),
|
||||
PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
|
||||
PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd),
|
||||
PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd));
|
||||
DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr);
|
||||
}
|
||||
|
||||
static void dump_ptd_out_data(struct ptd *ptd, u8 *buf)
|
||||
{
|
||||
if (dbg_level > 0) {
|
||||
if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
|
||||
DBG(0, "--out->\n");
|
||||
dump_data(buf, PTD_GET_LEN(ptd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_ptd_in_data(struct ptd *ptd, u8 *buf)
|
||||
{
|
||||
if (dbg_level > 0) {
|
||||
if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
|
||||
DBG(0, "<--in--\n");
|
||||
dump_data(buf, PTD_GET_COUNT(ptd));
|
||||
}
|
||||
DBG(0, "-----\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_ptd_queue(struct isp1362_ep_queue *epq)
|
||||
{
|
||||
struct isp1362_ep *ep;
|
||||
int dbg = dbg_level;
|
||||
|
||||
dbg_level = 1;
|
||||
list_for_each_entry(ep, &epq->active, active) {
|
||||
dump_ptd(&ep->ptd);
|
||||
dump_data(ep->data, ep->length);
|
||||
}
|
||||
dbg_level = dbg;
|
||||
}
|
||||
#else
|
||||
#define dump_ptd(ptd) do {} while (0)
|
||||
#define dump_ptd_in_data(ptd, buf) do {} while (0)
|
||||
#define dump_ptd_out_data(ptd, buf) do {} while (0)
|
||||
#define dump_ptd_data(ptd, buf) do {} while (0)
|
||||
#define dump_ptd_queue(epq) do {} while (0)
|
||||
#endif
|
||||
|
|
@ -1282,7 +1282,6 @@ static int __init ohci_hcd_mod_init(void)
|
|||
|
||||
pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name,
|
||||
sizeof (struct ed), sizeof (struct td));
|
||||
set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
|
||||
|
||||
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
|
||||
|
||||
|
|
@ -1332,7 +1331,6 @@ static int __init ohci_hcd_mod_init(void)
|
|||
debugfs_remove(ohci_debug_root);
|
||||
ohci_debug_root = NULL;
|
||||
|
||||
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
|
||||
return retval;
|
||||
}
|
||||
module_init(ohci_hcd_mod_init);
|
||||
|
|
@ -1352,7 +1350,6 @@ static void __exit ohci_hcd_mod_exit(void)
|
|||
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
#endif
|
||||
debugfs_remove(ohci_debug_root);
|
||||
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
|
||||
}
|
||||
module_exit(ohci_hcd_mod_exit);
|
||||
|
||||
|
|
|
|||
|
|
@ -867,8 +867,6 @@ static int __init uhci_hcd_init(void)
|
|||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
set_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
|
||||
if (!errbuf)
|
||||
|
|
@ -912,8 +910,6 @@ up_failed:
|
|||
|
||||
errbuf_failed:
|
||||
#endif
|
||||
|
||||
clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -930,7 +926,6 @@ static void __exit uhci_hcd_cleanup(void)
|
|||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
kfree(errbuf);
|
||||
#endif
|
||||
clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
|
||||
}
|
||||
|
||||
module_init(uhci_hcd_init);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
#include "xhci-trace.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
|
||||
static const struct dbc_str dbc_str_default = {
|
||||
.manufacturer = "Linux Foundation",
|
||||
.product = "Linux USB Debug Target",
|
||||
.serial = "0001",
|
||||
};
|
||||
|
||||
static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
|
|
@ -52,55 +58,6 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring)
|
|||
kfree(ring);
|
||||
}
|
||||
|
||||
static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
|
||||
{
|
||||
struct usb_string_descriptor *s_desc;
|
||||
u32 string_length;
|
||||
|
||||
/* Serial string: */
|
||||
s_desc = (struct usb_string_descriptor *)strings->serial;
|
||||
utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
|
||||
DBC_MAX_STRING_LENGTH);
|
||||
|
||||
s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
string_length = s_desc->bLength;
|
||||
string_length <<= 8;
|
||||
|
||||
/* Product string: */
|
||||
s_desc = (struct usb_string_descriptor *)strings->product;
|
||||
utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
|
||||
DBC_MAX_STRING_LENGTH);
|
||||
|
||||
s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
string_length += s_desc->bLength;
|
||||
string_length <<= 8;
|
||||
|
||||
/* Manufacture string: */
|
||||
s_desc = (struct usb_string_descriptor *)strings->manufacturer;
|
||||
utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
|
||||
strlen(DBC_STRING_MANUFACTURER),
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
|
||||
DBC_MAX_STRING_LENGTH);
|
||||
|
||||
s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
string_length += s_desc->bLength;
|
||||
string_length <<= 8;
|
||||
|
||||
/* String0: */
|
||||
strings->string0[0] = 4;
|
||||
strings->string0[1] = USB_DT_STRING;
|
||||
strings->string0[2] = 0x09;
|
||||
strings->string0[3] = 0x04;
|
||||
string_length += 4;
|
||||
|
||||
return string_length;
|
||||
}
|
||||
|
||||
static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc)
|
||||
{
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
|
@ -124,7 +81,65 @@ static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc)
|
|||
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
|
||||
}
|
||||
|
||||
static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
|
||||
static u8 get_str_desc_len(const char *desc)
|
||||
{
|
||||
return ((struct usb_string_descriptor *)desc)->bLength;
|
||||
}
|
||||
|
||||
static u32 dbc_prepare_info_context_str_len(struct dbc_str_descs *descs)
|
||||
{
|
||||
u32 len;
|
||||
|
||||
len = get_str_desc_len(descs->serial);
|
||||
len <<= 8;
|
||||
len += get_str_desc_len(descs->product);
|
||||
len <<= 8;
|
||||
len += get_str_desc_len(descs->manufacturer);
|
||||
len <<= 8;
|
||||
len += get_str_desc_len(descs->string0);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int xhci_dbc_populate_str_desc(char *desc, const char *src)
|
||||
{
|
||||
struct usb_string_descriptor *s_desc;
|
||||
int len;
|
||||
|
||||
s_desc = (struct usb_string_descriptor *)desc;
|
||||
|
||||
/* len holds number of 2 byte UTF-16 characters */
|
||||
len = utf8s_to_utf16s(src, strlen(src), UTF16_LITTLE_ENDIAN,
|
||||
(wchar_t *)s_desc->wData, USB_MAX_STRING_LEN * 2);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
s_desc->bLength = len * 2 + 2;
|
||||
s_desc->bDescriptorType = USB_DT_STRING;
|
||||
|
||||
return s_desc->bLength;
|
||||
}
|
||||
|
||||
static void xhci_dbc_populate_str_descs(struct dbc_str_descs *str_descs,
|
||||
struct dbc_str *str)
|
||||
{
|
||||
/* Serial string: */
|
||||
xhci_dbc_populate_str_desc(str_descs->serial, str->serial);
|
||||
|
||||
/* Product string: */
|
||||
xhci_dbc_populate_str_desc(str_descs->product, str->product);
|
||||
|
||||
/* Manufacturer string: */
|
||||
xhci_dbc_populate_str_desc(str_descs->manufacturer, str->manufacturer);
|
||||
|
||||
/* String0: */
|
||||
str_descs->string0[0] = 4;
|
||||
str_descs->string0[1] = USB_DT_STRING;
|
||||
str_descs->string0[2] = 0x09;
|
||||
str_descs->string0[3] = 0x04;
|
||||
}
|
||||
|
||||
static void xhci_dbc_init_contexts(struct xhci_dbc *dbc)
|
||||
{
|
||||
struct dbc_info_context *info;
|
||||
u32 dev_info;
|
||||
|
|
@ -135,12 +150,12 @@ static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
|
|||
|
||||
/* Populate info Context: */
|
||||
info = (struct dbc_info_context *)dbc->ctx->bytes;
|
||||
dma = dbc->string_dma;
|
||||
dma = dbc->str_descs_dma;
|
||||
info->string0 = cpu_to_le64(dma);
|
||||
info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH);
|
||||
info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2);
|
||||
info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3);
|
||||
info->length = cpu_to_le32(string_length);
|
||||
info->manufacturer = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN);
|
||||
info->product = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 2);
|
||||
info->serial = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 3);
|
||||
info->length = cpu_to_le32(dbc_prepare_info_context_str_len(dbc->str_descs));
|
||||
|
||||
/* Populate bulk in and out endpoint contexts: */
|
||||
xhci_dbc_init_ep_contexts(dbc);
|
||||
|
|
@ -525,7 +540,6 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
|
|||
{
|
||||
int ret;
|
||||
dma_addr_t deq;
|
||||
u32 string_length;
|
||||
struct device *dev = dbc->dev;
|
||||
|
||||
/* Allocate various rings for events and transfers: */
|
||||
|
|
@ -552,11 +566,11 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
|
|||
goto ctx_fail;
|
||||
|
||||
/* Allocate the string table: */
|
||||
dbc->string_size = sizeof(*dbc->string);
|
||||
dbc->string = dma_alloc_coherent(dev, dbc->string_size,
|
||||
&dbc->string_dma, flags);
|
||||
if (!dbc->string)
|
||||
goto string_fail;
|
||||
dbc->str_descs_size = sizeof(*dbc->str_descs);
|
||||
dbc->str_descs = dma_alloc_coherent(dev, dbc->str_descs_size,
|
||||
&dbc->str_descs_dma, flags);
|
||||
if (!dbc->str_descs)
|
||||
goto str_descs_fail;
|
||||
|
||||
/* Setup ERST register: */
|
||||
writel(dbc->erst.num_entries, &dbc->regs->ersts);
|
||||
|
|
@ -566,16 +580,16 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
|
|||
dbc->ring_evt->dequeue);
|
||||
lo_hi_writeq(deq, &dbc->regs->erdp);
|
||||
|
||||
/* Setup strings and contexts: */
|
||||
string_length = xhci_dbc_populate_strings(dbc->string);
|
||||
xhci_dbc_init_contexts(dbc, string_length);
|
||||
/* Setup string descriptors and contexts: */
|
||||
xhci_dbc_populate_str_descs(dbc->str_descs, &dbc->str);
|
||||
xhci_dbc_init_contexts(dbc);
|
||||
|
||||
xhci_dbc_eps_init(dbc);
|
||||
dbc->state = DS_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
|
||||
string_fail:
|
||||
str_descs_fail:
|
||||
dbc_free_ctx(dev, dbc->ctx);
|
||||
dbc->ctx = NULL;
|
||||
ctx_fail:
|
||||
|
|
@ -600,8 +614,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
|
|||
|
||||
xhci_dbc_eps_exit(dbc);
|
||||
|
||||
dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma);
|
||||
dbc->string = NULL;
|
||||
dma_free_coherent(dbc->dev, dbc->str_descs_size, dbc->str_descs, dbc->str_descs_dma);
|
||||
dbc->str_descs = NULL;
|
||||
|
||||
dbc_free_ctx(dbc->dev, dbc->ctx);
|
||||
dbc->ctx = NULL;
|
||||
|
|
@ -1196,6 +1210,108 @@ static ssize_t dbc_bcdDevice_store(struct device *dev,
|
|||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_manufacturer_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", dbc->str.manufacturer);
|
||||
}
|
||||
|
||||
static ssize_t dbc_manufacturer_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
size_t len;
|
||||
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
len = strcspn(buf, "\n");
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > USB_MAX_STRING_LEN)
|
||||
return -E2BIG;
|
||||
|
||||
memcpy(dbc->str.manufacturer, buf, len);
|
||||
dbc->str.manufacturer[len] = '\0';
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_product_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", dbc->str.product);
|
||||
}
|
||||
|
||||
static ssize_t dbc_product_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
size_t len;
|
||||
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
len = strcspn(buf, "\n");
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > USB_MAX_STRING_LEN)
|
||||
return -E2BIG;
|
||||
|
||||
memcpy(dbc->str.product, buf, len);
|
||||
dbc->str.product[len] = '\0';
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_serial_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", dbc->str.serial);
|
||||
}
|
||||
|
||||
static ssize_t dbc_serial_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
struct xhci_dbc *dbc = xhci->dbc;
|
||||
size_t len;
|
||||
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
len = strcspn(buf, "\n");
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > USB_MAX_STRING_LEN)
|
||||
return -E2BIG;
|
||||
|
||||
memcpy(dbc->str.serial, buf, len);
|
||||
dbc->str.serial[len] = '\0';
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_bInterfaceProtocol_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -1283,6 +1399,9 @@ static DEVICE_ATTR_RW(dbc);
|
|||
static DEVICE_ATTR_RW(dbc_idVendor);
|
||||
static DEVICE_ATTR_RW(dbc_idProduct);
|
||||
static DEVICE_ATTR_RW(dbc_bcdDevice);
|
||||
static DEVICE_ATTR_RW(dbc_serial);
|
||||
static DEVICE_ATTR_RW(dbc_product);
|
||||
static DEVICE_ATTR_RW(dbc_manufacturer);
|
||||
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
|
||||
static DEVICE_ATTR_RW(dbc_poll_interval_ms);
|
||||
|
||||
|
|
@ -1291,6 +1410,9 @@ static struct attribute *dbc_dev_attrs[] = {
|
|||
&dev_attr_dbc_idVendor.attr,
|
||||
&dev_attr_dbc_idProduct.attr,
|
||||
&dev_attr_dbc_bcdDevice.attr,
|
||||
&dev_attr_dbc_serial.attr,
|
||||
&dev_attr_dbc_product.attr,
|
||||
&dev_attr_dbc_manufacturer.attr,
|
||||
&dev_attr_dbc_bInterfaceProtocol.attr,
|
||||
&dev_attr_dbc_poll_interval_ms.attr,
|
||||
NULL
|
||||
|
|
@ -1316,6 +1438,9 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
|
|||
dbc->bInterfaceProtocol = DBC_PROTOCOL;
|
||||
dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT;
|
||||
|
||||
/* initialize serial, product and manufacturer with default values */
|
||||
dbc->str = dbc_str_default;
|
||||
|
||||
if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE)
|
||||
goto err;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,10 +47,6 @@ struct dbc_info_context {
|
|||
#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8)
|
||||
|
||||
#define DBC_MAX_PACKET 1024
|
||||
#define DBC_MAX_STRING_LENGTH 64
|
||||
#define DBC_STRING_MANUFACTURER "Linux Foundation"
|
||||
#define DBC_STRING_PRODUCT "Linux USB Debug Target"
|
||||
#define DBC_STRING_SERIAL "0001"
|
||||
#define DBC_CONTEXT_SIZE 64
|
||||
|
||||
/*
|
||||
|
|
@ -63,11 +59,31 @@ struct dbc_info_context {
|
|||
#define DBC_PORTSC_LINK_CHANGE BIT(22)
|
||||
#define DBC_PORTSC_CONFIG_CHANGE BIT(23)
|
||||
|
||||
/*
|
||||
* The maximum length of a string descriptor is 255, because the bLength
|
||||
* field in the usb_string_descriptor struct is __u8. In practice the
|
||||
* maximum length is 254, because a string descriptor consists of a 2 byte
|
||||
* header followed by UTF-16 characters (2 bytes each). This allows for
|
||||
* only 126 characters (code points) in the string, which is where
|
||||
* USB_MAX_STRING_LEN comes from.
|
||||
*/
|
||||
#define USB_MAX_STRING_DESC_LEN 254
|
||||
|
||||
struct dbc_str_descs {
|
||||
char string0[DBC_MAX_STRING_LENGTH];
|
||||
char manufacturer[DBC_MAX_STRING_LENGTH];
|
||||
char product[DBC_MAX_STRING_LENGTH];
|
||||
char serial[DBC_MAX_STRING_LENGTH];
|
||||
char string0[USB_MAX_STRING_DESC_LEN];
|
||||
char manufacturer[USB_MAX_STRING_DESC_LEN];
|
||||
char product[USB_MAX_STRING_DESC_LEN];
|
||||
char serial[USB_MAX_STRING_DESC_LEN];
|
||||
};
|
||||
|
||||
/*
|
||||
* NULL terminated UTF-8 strings used to create UTF-16 strings
|
||||
* (with maxiumum USB_MAX_STRING_LEN 2 byte characters).
|
||||
*/
|
||||
struct dbc_str {
|
||||
char manufacturer[USB_MAX_STRING_LEN+1];
|
||||
char product[USB_MAX_STRING_LEN+1];
|
||||
char serial[USB_MAX_STRING_LEN+1];
|
||||
};
|
||||
|
||||
#define DBC_PROTOCOL 1 /* GNU Remote Debug Command */
|
||||
|
|
@ -133,9 +149,10 @@ struct xhci_dbc {
|
|||
struct xhci_erst erst;
|
||||
struct xhci_container_ctx *ctx;
|
||||
|
||||
struct dbc_str_descs *string;
|
||||
dma_addr_t string_dma;
|
||||
size_t string_size;
|
||||
struct dbc_str_descs *str_descs;
|
||||
dma_addr_t str_descs_dma;
|
||||
size_t str_descs_size;
|
||||
struct dbc_str str;
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
u16 bcdDevice;
|
||||
|
|
|
|||
|
|
@ -1570,7 +1570,6 @@ static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xu
|
|||
data = irq_get_irq_data(tegra->wake_irqs[i]);
|
||||
if (!data) {
|
||||
dev_warn(tegra->dev, "get wake event %d irq data fail\n", i);
|
||||
irq_dispose_mapping(tegra->wake_irqs[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1583,16 +1582,6 @@ static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xu
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_xusb_dispose_wake(struct tegra_xusb *tegra)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tegra->num_wakes; i++)
|
||||
irq_dispose_mapping(tegra->wake_irqs[i]);
|
||||
|
||||
tegra->num_wakes = 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_xusb *tegra;
|
||||
|
|
@ -1648,10 +1637,8 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
|
||||
tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
|
||||
if (IS_ERR(tegra->padctl)) {
|
||||
err = PTR_ERR(tegra->padctl);
|
||||
goto dispose_wake;
|
||||
}
|
||||
if (IS_ERR(tegra->padctl))
|
||||
return PTR_ERR(tegra->padctl);
|
||||
|
||||
np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
|
||||
if (!np) {
|
||||
|
|
@ -1975,8 +1962,6 @@ put_powerdomains:
|
|||
put_padctl:
|
||||
of_node_put(np);
|
||||
tegra_xusb_padctl_put(tegra->padctl);
|
||||
dispose_wake:
|
||||
tegra_xusb_dispose_wake(tegra);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2009,8 +1994,6 @@ static void tegra_xusb_remove(struct platform_device *pdev)
|
|||
if (tegra->padctl_irq)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
tegra_xusb_dispose_wake(tegra);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
tegra_xusb_disable(tegra);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,13 @@ static const struct onboard_dev_pdata vialab_vl817_data = {
|
|||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata wch_ch334_data = {
|
||||
.reset_us = 14000,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vdd33", "v5" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata xmos_xvf3500_data = {
|
||||
.reset_us = 1,
|
||||
.num_supplies = 2,
|
||||
|
|
@ -146,6 +153,7 @@ static const struct of_device_id onboard_dev_match[] = {
|
|||
{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usb1a86,8091", .data = &wch_ch334_data, },
|
||||
{ .compatible = "usb1da0,5511", .data = ¶de_ps5511_data, },
|
||||
{ .compatible = "usb1da0,55a1", .data = ¶de_ps5511_data, },
|
||||
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
|
|
@ -49,14 +49,12 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
|
|||
int ret = 0;
|
||||
|
||||
if (suspend) {
|
||||
if (!IS_ERR(nop->clk))
|
||||
clk_disable_unprepare(nop->clk);
|
||||
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
|
||||
ret = regulator_disable(nop->vcc);
|
||||
} else {
|
||||
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
|
||||
ret = regulator_enable(nop->vcc);
|
||||
if (!IS_ERR(nop->clk))
|
||||
clk_prepare_enable(nop->clk);
|
||||
}
|
||||
|
||||
|
|
@ -137,11 +135,9 @@ int usb_gen_phy_init(struct usb_phy *phy)
|
|||
dev_err(phy->dev, "Failed to enable power\n");
|
||||
}
|
||||
|
||||
if (!IS_ERR(nop->clk)) {
|
||||
ret = clk_prepare_enable(nop->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
nop_reset(nop);
|
||||
|
||||
|
|
@ -155,7 +151,6 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
|
|||
|
||||
gpiod_set_value_cansleep(nop->gpiod_reset, 1);
|
||||
|
||||
if (!IS_ERR(nop->clk))
|
||||
clk_disable_unprepare(nop->clk);
|
||||
|
||||
if (!IS_ERR(nop->vcc)) {
|
||||
|
|
@ -202,18 +197,9 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
|
|||
{
|
||||
enum usb_phy_type type = USB_PHY_TYPE_USB2;
|
||||
int err = 0;
|
||||
|
||||
u32 clk_rate = 0;
|
||||
bool needs_clk = false;
|
||||
|
||||
if (dev->of_node) {
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &clk_rate))
|
||||
clk_rate = 0;
|
||||
|
||||
needs_clk = of_property_present(node, "clocks");
|
||||
}
|
||||
device_property_read_u32(dev, "clock-frequency", &clk_rate);
|
||||
nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_ASIS);
|
||||
err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
|
||||
|
|
@ -235,20 +221,16 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
|
|||
if (!nop->phy.otg)
|
||||
return -ENOMEM;
|
||||
|
||||
nop->clk = devm_clk_get(dev, "main_clk");
|
||||
if (IS_ERR(nop->clk)) {
|
||||
dev_dbg(dev, "Can't get phy clock: %ld\n",
|
||||
PTR_ERR(nop->clk));
|
||||
if (needs_clk)
|
||||
return PTR_ERR(nop->clk);
|
||||
}
|
||||
nop->clk = devm_clk_get_optional(dev, "main_clk");
|
||||
if (IS_ERR(nop->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(nop->clk),
|
||||
"Can't get phy clock\n");
|
||||
|
||||
if (!IS_ERR(nop->clk) && clk_rate) {
|
||||
if (clk_rate) {
|
||||
err = clk_set_rate(nop->clk, clk_rate);
|
||||
if (err) {
|
||||
dev_err(dev, "Error setting clock rate\n");
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"Error setting clock rate\n");
|
||||
}
|
||||
|
||||
nop->vcc = devm_regulator_get_optional(dev, "vcc");
|
||||
|
|
@ -282,7 +264,6 @@ EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
|
|||
static int usb_phy_generic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node;
|
||||
struct usb_phy_generic *nop;
|
||||
int err;
|
||||
|
||||
|
|
@ -293,17 +274,17 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
|
|||
err = usb_phy_gen_create_phy(dev, nop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (nop->gpiod_vbus) {
|
||||
err = devm_request_threaded_irq(&pdev->dev,
|
||||
err = devm_request_threaded_irq(dev,
|
||||
gpiod_to_irq(nop->gpiod_vbus),
|
||||
NULL, nop_gpio_vbus_thread,
|
||||
VBUS_IRQ_FLAGS, "vbus_detect",
|
||||
nop);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
|
||||
gpiod_to_irq(nop->gpiod_vbus), err);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "can't request irq %i\n",
|
||||
gpiod_to_irq(nop->gpiod_vbus));
|
||||
|
||||
nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
|
||||
OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
|
||||
}
|
||||
|
|
@ -312,16 +293,13 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
|
|||
nop->phy.shutdown = usb_gen_phy_shutdown;
|
||||
|
||||
err = usb_add_phy_dev(&nop->phy);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "can't register transceiver\n");
|
||||
|
||||
platform_set_drvdata(pdev, nop);
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev,
|
||||
of_property_read_bool(dn, "wakeup-source"));
|
||||
device_set_wakeup_capable(dev,
|
||||
device_property_read_bool(dev, "wakeup-source"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,17 +29,26 @@
|
|||
#include <linux/usb/tegra_usb_phy.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
|
||||
#define USB_TXFILLTUNING 0x154
|
||||
#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16)
|
||||
#define USB_FIFO_TXFILL_MASK 0x1f0000
|
||||
|
||||
#define ULPI_VIEWPORT 0x170
|
||||
|
||||
/* PORTSC PTS/PHCD bits, Tegra20 only */
|
||||
#define TEGRA_USB_PORTSC1 0x184
|
||||
#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
|
||||
#define TEGRA_USB_PORTSC1_PHCD BIT(23)
|
||||
#define TEGRA_USB_PORTSC1_WKOC BIT(22)
|
||||
#define TEGRA_USB_PORTSC1_WKDS BIT(21)
|
||||
#define TEGRA_USB_PORTSC1_WKCN BIT(20)
|
||||
|
||||
/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
|
||||
#define TEGRA30_USB_PORTSC1 0x174
|
||||
#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
|
||||
#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
|
||||
#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
|
||||
#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC 4
|
||||
|
||||
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
|
||||
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
|
||||
|
|
@ -51,11 +60,12 @@
|
|||
#define USB_SUSP_CLR BIT(5)
|
||||
#define USB_PHY_CLK_VALID BIT(7)
|
||||
#define UTMIP_RESET BIT(11)
|
||||
#define UHSIC_RESET BIT(11)
|
||||
#define UTMIP_PHY_ENABLE BIT(12)
|
||||
#define ULPI_PHY_ENABLE BIT(13)
|
||||
#define USB_SUSP_SET BIT(14)
|
||||
#define UHSIC_RESET BIT(14)
|
||||
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
|
||||
#define UHSIC_PHY_ENABLE BIT(19)
|
||||
|
||||
#define USB_PHY_VBUS_SENSORS 0x404
|
||||
#define B_SESS_VLD_WAKEUP_EN BIT(14)
|
||||
|
|
@ -156,6 +166,58 @@
|
|||
#define UTMIP_BIAS_CFG1 0x83c
|
||||
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
|
||||
|
||||
/*
|
||||
* Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800
|
||||
* just where UTMIP registers should have been. This is the case only with Tegra20
|
||||
* Tegra30+ have UTMIP registers at 0x800 and UHSIC registers are shifted by 0x400
|
||||
* to 0xc00, but register layout is preserved.
|
||||
*/
|
||||
#define UHSIC_PLL_CFG1 0x804
|
||||
#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
|
||||
#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14)
|
||||
|
||||
#define UHSIC_HSRX_CFG0 0x808
|
||||
#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2)
|
||||
#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8)
|
||||
#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13)
|
||||
|
||||
#define UHSIC_HSRX_CFG1 0x80c
|
||||
#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
|
||||
|
||||
#define UHSIC_TX_CFG0 0x810
|
||||
#define UHSIC_HS_READY_WAIT_FOR_VALID BIT(9)
|
||||
|
||||
#define UHSIC_MISC_CFG0 0x814
|
||||
#define UHSIC_SUSPEND_EXIT_ON_EDGE BIT(7)
|
||||
#define UHSIC_DETECT_SHORT_CONNECT BIT(8)
|
||||
#define UHSIC_FORCE_XCVR_MODE BIT(15)
|
||||
#define UHSIC_DISABLE_BUSRESET BIT(20)
|
||||
|
||||
#define UHSIC_MISC_CFG1 0x818
|
||||
#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2)
|
||||
|
||||
#define UHSIC_PADS_CFG0 0x81c
|
||||
#define UHSIC_TX_RTUNEN 0xf000
|
||||
#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12)
|
||||
|
||||
#define UHSIC_PADS_CFG1 0x820
|
||||
#define UHSIC_PD_BG BIT(2)
|
||||
#define UHSIC_PD_TX BIT(3)
|
||||
#define UHSIC_PD_TRK BIT(4)
|
||||
#define UHSIC_PD_RX BIT(5)
|
||||
#define UHSIC_PD_ZI BIT(6)
|
||||
#define UHSIC_RX_SEL BIT(7)
|
||||
#define UHSIC_RPD_DATA BIT(9)
|
||||
#define UHSIC_RPD_STROBE BIT(10)
|
||||
#define UHSIC_RPU_DATA BIT(11)
|
||||
#define UHSIC_RPU_STROBE BIT(12)
|
||||
|
||||
#define UHSIC_CMD_CFG0 0x824
|
||||
#define UHSIC_PRETEND_CONNECT_DETECT BIT(5)
|
||||
|
||||
#define UHSIC_STAT_CFG0 0x828
|
||||
#define UHSIC_CONNECT_DETECT BIT(0)
|
||||
|
||||
/* For Tegra30 and above only, the address is different in Tegra20 */
|
||||
#define USB_USBMODE 0x1f8
|
||||
#define USB_USBMODE_MASK (3 << 0)
|
||||
|
|
@ -174,7 +236,8 @@ struct tegra_xtal_freq {
|
|||
u8 enable_delay;
|
||||
u8 stable_count;
|
||||
u8 active_delay;
|
||||
u8 xtal_freq_count;
|
||||
u8 utmi_xtal_freq_count;
|
||||
u16 hsic_xtal_freq_count;
|
||||
u16 debounce;
|
||||
};
|
||||
|
||||
|
|
@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
|
|||
.enable_delay = 0x02,
|
||||
.stable_count = 0x2F,
|
||||
.active_delay = 0x04,
|
||||
.xtal_freq_count = 0x76,
|
||||
.utmi_xtal_freq_count = 0x76,
|
||||
.hsic_xtal_freq_count = 0x1CA,
|
||||
.debounce = 0x7530,
|
||||
},
|
||||
{
|
||||
|
|
@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
|
|||
.enable_delay = 0x02,
|
||||
.stable_count = 0x33,
|
||||
.active_delay = 0x05,
|
||||
.xtal_freq_count = 0x7F,
|
||||
.utmi_xtal_freq_count = 0x7F,
|
||||
.hsic_xtal_freq_count = 0x1F0,
|
||||
.debounce = 0x7EF4,
|
||||
},
|
||||
{
|
||||
|
|
@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
|
|||
.enable_delay = 0x03,
|
||||
.stable_count = 0x4B,
|
||||
.active_delay = 0x06,
|
||||
.xtal_freq_count = 0xBB,
|
||||
.utmi_xtal_freq_count = 0xBB,
|
||||
.hsic_xtal_freq_count = 0x2DD,
|
||||
.debounce = 0xBB80,
|
||||
},
|
||||
{
|
||||
|
|
@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
|
|||
.enable_delay = 0x04,
|
||||
.stable_count = 0x66,
|
||||
.active_delay = 0x09,
|
||||
.xtal_freq_count = 0xFE,
|
||||
.utmi_xtal_freq_count = 0xFE,
|
||||
.hsic_xtal_freq_count = 0x3E0,
|
||||
.debounce = 0xFDE8,
|
||||
},
|
||||
};
|
||||
|
|
@ -532,7 +599,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
val = readl_relaxed(base + UTMIP_PLL_CFG1);
|
||||
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
|
||||
UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
|
||||
val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
|
||||
val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->utmi_xtal_freq_count) |
|
||||
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
|
||||
writel_relaxed(val, base + UTMIP_PLL_CFG1);
|
||||
}
|
||||
|
|
@ -803,17 +870,170 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg)
|
||||
{
|
||||
void __iomem *base = phy->regs;
|
||||
u32 shift = phy->soc_config->uhsic_registers_offset;
|
||||
|
||||
return readl_relaxed(base + shift + reg);
|
||||
}
|
||||
|
||||
static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value)
|
||||
{
|
||||
void __iomem *base = phy->regs;
|
||||
u32 shift = phy->soc_config->uhsic_registers_offset;
|
||||
|
||||
writel_relaxed(value, base + shift + reg);
|
||||
}
|
||||
|
||||
static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
|
||||
{
|
||||
struct tegra_utmip_config *config = phy->config;
|
||||
void __iomem *base = phy->regs;
|
||||
u32 val;
|
||||
int err = 0;
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
|
||||
val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
|
||||
UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
|
||||
val |= UHSIC_RX_SEL;
|
||||
tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);
|
||||
|
||||
udelay(2);
|
||||
|
||||
val = readl_relaxed(base + USB_SUSP_CTRL);
|
||||
val |= UHSIC_RESET;
|
||||
writel_relaxed(val, base + USB_SUSP_CTRL);
|
||||
|
||||
udelay(30);
|
||||
|
||||
val = readl_relaxed(base + USB_SUSP_CTRL);
|
||||
val |= UHSIC_PHY_ENABLE;
|
||||
writel_relaxed(val, base + USB_SUSP_CTRL);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG0);
|
||||
val &= ~(UHSIC_IDLE_WAIT(~0) |
|
||||
UHSIC_ELASTIC_UNDERRUN_LIMIT(~0) |
|
||||
UHSIC_ELASTIC_OVERRUN_LIMIT(~0));
|
||||
val |= UHSIC_IDLE_WAIT(config->idle_wait_delay) |
|
||||
UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_limit) |
|
||||
UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_limit);
|
||||
tegra_hsic_writel(phy, UHSIC_HSRX_CFG0, val);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG1);
|
||||
val &= ~UHSIC_HS_SYNC_START_DLY(~0);
|
||||
val |= UHSIC_HS_SYNC_START_DLY(config->hssync_start_delay);
|
||||
tegra_hsic_writel(phy, UHSIC_HSRX_CFG1, val);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_MISC_CFG0);
|
||||
val |= UHSIC_SUSPEND_EXIT_ON_EDGE;
|
||||
tegra_hsic_writel(phy, UHSIC_MISC_CFG0, val);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_MISC_CFG1);
|
||||
val &= ~UHSIC_PLLU_STABLE_COUNT(~0);
|
||||
val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count);
|
||||
tegra_hsic_writel(phy, UHSIC_MISC_CFG1, val);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_PLL_CFG1);
|
||||
val &= ~(UHSIC_XTAL_FREQ_COUNT(~0) |
|
||||
UHSIC_PLLU_ENABLE_DLY_COUNT(~0));
|
||||
val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->hsic_xtal_freq_count) |
|
||||
UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
|
||||
tegra_hsic_writel(phy, UHSIC_PLL_CFG1, val);
|
||||
|
||||
val = readl_relaxed(base + USB_SUSP_CTRL);
|
||||
val &= ~UHSIC_RESET;
|
||||
writel_relaxed(val, base + USB_SUSP_CTRL);
|
||||
|
||||
udelay(2);
|
||||
|
||||
if (phy->soc_config->requires_usbmode_setup) {
|
||||
val = readl_relaxed(base + USB_USBMODE);
|
||||
val &= ~USB_USBMODE_MASK;
|
||||
if (phy->mode == USB_DR_MODE_HOST)
|
||||
val |= USB_USBMODE_HOST;
|
||||
else
|
||||
val |= USB_USBMODE_DEVICE;
|
||||
writel_relaxed(val, base + USB_USBMODE);
|
||||
}
|
||||
|
||||
set_pts(phy, phy->soc_config->uhsic_pts_value);
|
||||
|
||||
val = readl_relaxed(base + USB_TXFILLTUNING);
|
||||
if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
|
||||
val = USB_FIFO_TXFILL_THRES(0x10);
|
||||
writel_relaxed(val, base + USB_TXFILLTUNING);
|
||||
}
|
||||
|
||||
val = readl_relaxed(base + phy->soc_config->portsc1_offset);
|
||||
val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS |
|
||||
TEGRA_USB_PORTSC1_WKCN);
|
||||
writel_relaxed(val, base + phy->soc_config->portsc1_offset);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0);
|
||||
val &= ~UHSIC_TX_RTUNEN;
|
||||
val |= UHSIC_TX_RTUNE(phy->soc_config->uhsic_tx_rtune);
|
||||
tegra_hsic_writel(phy, UHSIC_PADS_CFG0, val);
|
||||
|
||||
err = utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
|
||||
USB_PHY_CLK_VALID);
|
||||
|
||||
if (err)
|
||||
dev_err(phy->u_phy.dev,
|
||||
"Timeout waiting for PHY to stabilize on enable (HSIC)\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uhsic_phy_power_off(struct tegra_usb_phy *phy)
|
||||
{
|
||||
void __iomem *base = phy->regs;
|
||||
u32 val;
|
||||
|
||||
set_phcd(phy, true);
|
||||
|
||||
val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
|
||||
val |= (UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
|
||||
UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
|
||||
tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);
|
||||
|
||||
val = readl_relaxed(base + USB_SUSP_CTRL);
|
||||
val |= UHSIC_RESET;
|
||||
writel_relaxed(val, base + USB_SUSP_CTRL);
|
||||
|
||||
udelay(30);
|
||||
|
||||
val = readl_relaxed(base + USB_SUSP_CTRL);
|
||||
val &= ~UHSIC_PHY_ENABLE;
|
||||
writel_relaxed(val, base + USB_SUSP_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (phy->powered_on)
|
||||
return 0;
|
||||
|
||||
if (phy->is_ulpi_phy)
|
||||
err = ulpi_phy_power_on(phy);
|
||||
else
|
||||
switch (phy->phy_type) {
|
||||
case USBPHY_INTERFACE_MODE_UTMI:
|
||||
err = utmi_phy_power_on(phy);
|
||||
break;
|
||||
|
||||
case USBPHY_INTERFACE_MODE_ULPI:
|
||||
err = ulpi_phy_power_on(phy);
|
||||
break;
|
||||
|
||||
case USBPHY_INTERFACE_MODE_HSIC:
|
||||
err = uhsic_phy_power_on(phy);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -827,15 +1047,28 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
|||
|
||||
static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (!phy->powered_on)
|
||||
return 0;
|
||||
|
||||
if (phy->is_ulpi_phy)
|
||||
err = ulpi_phy_power_off(phy);
|
||||
else
|
||||
switch (phy->phy_type) {
|
||||
case USBPHY_INTERFACE_MODE_UTMI:
|
||||
err = utmi_phy_power_off(phy);
|
||||
break;
|
||||
|
||||
case USBPHY_INTERFACE_MODE_ULPI:
|
||||
err = ulpi_phy_power_off(phy);
|
||||
break;
|
||||
|
||||
case USBPHY_INTERFACE_MODE_HSIC:
|
||||
err = uhsic_phy_power_off(phy);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -854,7 +1087,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
|
|||
usb_phy_set_wakeup(u_phy, false);
|
||||
tegra_usb_phy_power_off(phy);
|
||||
|
||||
if (!phy->is_ulpi_phy)
|
||||
if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
|
||||
utmip_pad_close(phy);
|
||||
|
||||
regulator_disable(phy->vbus);
|
||||
|
|
@ -1040,7 +1273,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
|
|||
goto disable_clk;
|
||||
}
|
||||
|
||||
if (!phy->is_ulpi_phy) {
|
||||
if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) {
|
||||
err = utmip_pad_open(phy);
|
||||
if (err)
|
||||
goto disable_vbus;
|
||||
|
|
@ -1057,7 +1290,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
|
|||
return 0;
|
||||
|
||||
close_phy:
|
||||
if (!phy->is_ulpi_phy)
|
||||
if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
|
||||
utmip_pad_close(phy);
|
||||
|
||||
disable_vbus:
|
||||
|
|
@ -1095,8 +1328,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
|
|||
struct resource *res;
|
||||
int err;
|
||||
|
||||
tegra_phy->is_ulpi_phy = false;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
|
||||
|
|
@ -1231,6 +1462,10 @@ static const struct tegra_phy_soc_config tegra20_soc_config = {
|
|||
.requires_usbmode_setup = false,
|
||||
.requires_extra_tuning_parameters = false,
|
||||
.requires_pmc_ao_power_up = false,
|
||||
.uhsic_registers_offset = 0,
|
||||
.uhsic_tx_rtune = 0, /* 40 ohm */
|
||||
.uhsic_pts_value = 0, /* UTMI */
|
||||
.portsc1_offset = TEGRA_USB_PORTSC1,
|
||||
};
|
||||
|
||||
static const struct tegra_phy_soc_config tegra30_soc_config = {
|
||||
|
|
@ -1239,6 +1474,10 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
|
|||
.requires_usbmode_setup = true,
|
||||
.requires_extra_tuning_parameters = true,
|
||||
.requires_pmc_ao_power_up = true,
|
||||
.uhsic_registers_offset = 0x400,
|
||||
.uhsic_tx_rtune = 8, /* 50 ohm */
|
||||
.uhsic_pts_value = TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC,
|
||||
.portsc1_offset = TEGRA30_USB_PORTSC1,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_usb_phy_id_table[] = {
|
||||
|
|
@ -1252,7 +1491,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct tegra_usb_phy *tegra_phy;
|
||||
enum usb_phy_interface phy_type;
|
||||
struct reset_control *reset;
|
||||
struct gpio_desc *gpiod;
|
||||
struct resource *res;
|
||||
|
|
@ -1314,9 +1552,10 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
phy_type = of_usb_get_phy_mode(np);
|
||||
switch (phy_type) {
|
||||
tegra_phy->phy_type = of_usb_get_phy_mode(np);
|
||||
switch (tegra_phy->phy_type) {
|
||||
case USBPHY_INTERFACE_MODE_UTMI:
|
||||
case USBPHY_INTERFACE_MODE_HSIC:
|
||||
err = utmi_phy_probe(tegra_phy, pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -1341,8 +1580,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
|
|||
break;
|
||||
|
||||
case USBPHY_INTERFACE_MODE_ULPI:
|
||||
tegra_phy->is_ulpi_phy = true;
|
||||
|
||||
tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link");
|
||||
err = PTR_ERR_OR_ZERO(tegra_phy->clk);
|
||||
if (err) {
|
||||
|
|
@ -1382,7 +1619,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
|
|||
|
||||
default:
|
||||
dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
|
||||
phy_type);
|
||||
tegra_phy->phy_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1401,12 +1401,16 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = NCTRL(0) | RSVD(1) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */
|
||||
.driver_info = RSVD(0) | NCTRL(3) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a1, 0xff), /* Telit FN20C04 (RNDIS) */
|
||||
.driver_info = NCTRL(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */
|
||||
.driver_info = NCTRL(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */
|
||||
.driver_info = NCTRL(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */
|
||||
.driver_info = RSVD(0) | NCTRL(3) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a6, 0xff), /* Telit FN920C04 (RNDIS) */
|
||||
.driver_info = NCTRL(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */
|
||||
.driver_info = NCTRL(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a8, 0xff), /* Telit FN920C04 (ECM) */
|
||||
|
|
@ -1415,6 +1419,8 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */
|
||||
.driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10ab, 0xff), /* Telit FN920C04 (RNDIS) */
|
||||
.driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */
|
||||
.driver_info = NCTRL(5) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) },
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_TYPEC) += typec.o
|
||||
typec-y := class.o mux.o bus.o pd.o retimer.o
|
||||
typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o
|
||||
typec-$(CONFIG_ACPI) += port-mapper.o
|
||||
obj-$(CONFIG_TYPEC) += altmodes/
|
||||
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
|
||||
|
|
|
|||
|
|
@ -804,8 +804,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
|||
if (plug)
|
||||
typec_altmode_set_drvdata(plug, dp);
|
||||
|
||||
if (!alt->mode_selection) {
|
||||
dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
|
||||
schedule_work(&dp->work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
|
|||
typec_altmode_set_drvdata(alt, tbt);
|
||||
typec_altmode_set_ops(alt, &tbt_altmode_ops);
|
||||
|
||||
if (tbt_ready(alt)) {
|
||||
if (!alt->mode_selection && tbt_ready(alt)) {
|
||||
if (tbt->plug[TYPEC_PLUG_SOP_P])
|
||||
tbt->state = TBT_STATE_SOP_P_ENTER;
|
||||
else if (tbt->plug[TYPEC_PLUG_SOP_PP])
|
||||
|
|
|
|||
|
|
@ -445,7 +445,23 @@ static struct attribute *typec_attrs[] = {
|
|||
&dev_attr_description.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(typec);
|
||||
|
||||
static umode_t typec_is_visible(struct kobject *kobj, struct attribute *attr, int n)
|
||||
{
|
||||
if (is_typec_partner_altmode(kobj_to_dev(kobj)))
|
||||
return attr->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct attribute_group typec_group = {
|
||||
.is_visible = typec_is_visible,
|
||||
.attrs = typec_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *typec_groups[] = {
|
||||
&typec_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int typec_match(struct device *dev, const struct device_driver *driver)
|
||||
{
|
||||
|
|
@ -453,6 +469,9 @@ static int typec_match(struct device *dev, const struct device_driver *driver)
|
|||
struct typec_altmode *altmode = to_typec_altmode(dev);
|
||||
const struct typec_device_id *id;
|
||||
|
||||
if (!is_typec_partner_altmode(dev))
|
||||
return 0;
|
||||
|
||||
for (id = drv->id_table; id->svid; id++)
|
||||
if (id->svid == altmode->svid)
|
||||
return 1;
|
||||
|
|
@ -463,6 +482,9 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
|||
{
|
||||
const struct typec_altmode *altmode = to_typec_altmode(dev);
|
||||
|
||||
if (!is_typec_partner_altmode(dev))
|
||||
return 0;
|
||||
|
||||
if (add_uevent_var(env, "SVID=%04X", altmode->svid))
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -547,3 +569,4 @@ const struct bus_type typec_bus = {
|
|||
.probe = typec_probe,
|
||||
.remove = typec_remove,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(typec_bus);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include <linux/usb/typec_altmode.h>
|
||||
|
||||
struct bus_type;
|
||||
struct typec_mux;
|
||||
struct typec_retimer;
|
||||
|
||||
|
|
@ -28,9 +27,4 @@ struct altmode {
|
|||
|
||||
#define to_altmode(d) container_of(d, struct altmode, adev)
|
||||
|
||||
extern const struct bus_type typec_bus;
|
||||
extern const struct device_type typec_altmode_dev_type;
|
||||
|
||||
#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type)
|
||||
|
||||
#endif /* __USB_TYPEC_ALTMODE_H__ */
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ static int altmode_match(struct device *dev, const void *data)
|
|||
struct typec_altmode *adev = to_typec_altmode(dev);
|
||||
const struct typec_device_id *id = data;
|
||||
|
||||
if (!is_typec_altmode(dev))
|
||||
if (!is_typec_port_altmode(dev))
|
||||
return 0;
|
||||
|
||||
return (adev->svid == id->svid);
|
||||
|
|
@ -445,11 +445,88 @@ svid_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
static DEVICE_ATTR_RO(svid);
|
||||
|
||||
static int increment_duplicated_priority(struct device *dev, void *data)
|
||||
{
|
||||
if (is_typec_port_altmode(dev)) {
|
||||
struct typec_altmode **alt_target = (struct typec_altmode **)data;
|
||||
struct typec_altmode *alt = to_typec_altmode(dev);
|
||||
|
||||
if (alt != *alt_target && alt->priority == (*alt_target)->priority) {
|
||||
alt->priority++;
|
||||
*alt_target = alt;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_duplicated_priority(struct device *dev, void *data)
|
||||
{
|
||||
if (is_typec_port_altmode(dev)) {
|
||||
struct typec_altmode **alt_target = (struct typec_altmode **)data;
|
||||
struct typec_altmode *alt = to_typec_altmode(dev);
|
||||
|
||||
if (alt != *alt_target && alt->priority == (*alt_target)->priority)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int typec_mode_set_priority(struct typec_altmode *alt, const u8 priority)
|
||||
{
|
||||
struct typec_port *port = to_typec_port(alt->dev.parent);
|
||||
const u8 old_priority = alt->priority;
|
||||
int res = 1;
|
||||
|
||||
alt->priority = priority;
|
||||
while (res) {
|
||||
res = device_for_each_child(&port->dev, &alt, find_duplicated_priority);
|
||||
if (res) {
|
||||
alt->priority++;
|
||||
if (alt->priority == 0) {
|
||||
alt->priority = old_priority;
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = 1;
|
||||
alt->priority = priority;
|
||||
while (res)
|
||||
res = device_for_each_child(&port->dev, &alt,
|
||||
increment_duplicated_priority);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t priority_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
u8 val;
|
||||
int err = kstrtou8(buf, 10, &val);
|
||||
|
||||
if (!err)
|
||||
err = typec_mode_set_priority(to_typec_altmode(dev), val);
|
||||
|
||||
if (!err)
|
||||
return size;
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t priority_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%u\n", to_typec_altmode(dev)->priority);
|
||||
}
|
||||
static DEVICE_ATTR_RW(priority);
|
||||
|
||||
static struct attribute *typec_altmode_attrs[] = {
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_mode.attr,
|
||||
&dev_attr_svid.attr,
|
||||
&dev_attr_vdo.attr,
|
||||
&dev_attr_priority.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -457,11 +534,17 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
|
|||
struct attribute *attr, int n)
|
||||
{
|
||||
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
|
||||
struct typec_port *port = typec_altmode2port(adev);
|
||||
|
||||
if (attr == &dev_attr_active.attr)
|
||||
if (!is_typec_port(adev->dev.parent) &&
|
||||
(!adev->ops || !adev->ops->activate))
|
||||
if (attr == &dev_attr_active.attr) {
|
||||
if (!is_typec_port(adev->dev.parent)) {
|
||||
if (!port->mode_control || !adev->ops || !adev->ops->activate)
|
||||
return 0444;
|
||||
}
|
||||
} else if (attr == &dev_attr_priority.attr) {
|
||||
if (!is_typec_port(adev->dev.parent) || !port->mode_control)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
|
@ -532,15 +615,31 @@ static void typec_altmode_release(struct device *dev)
|
|||
kfree(alt);
|
||||
}
|
||||
|
||||
const struct device_type typec_altmode_dev_type = {
|
||||
.name = "typec_alternate_mode",
|
||||
const struct device_type typec_port_altmode_dev_type = {
|
||||
.name = "typec_port_alternate_mode",
|
||||
.groups = typec_altmode_groups,
|
||||
.release = typec_altmode_release,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(typec_port_altmode_dev_type);
|
||||
|
||||
const struct device_type typec_plug_altmode_dev_type = {
|
||||
.name = "typec_plug_alternate_mode",
|
||||
.groups = typec_altmode_groups,
|
||||
.release = typec_altmode_release,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(typec_plug_altmode_dev_type);
|
||||
|
||||
const struct device_type typec_partner_altmode_dev_type = {
|
||||
.name = "typec_partner_alternate_mode",
|
||||
.groups = typec_altmode_groups,
|
||||
.release = typec_altmode_release,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(typec_partner_altmode_dev_type);
|
||||
|
||||
static struct typec_altmode *
|
||||
typec_register_altmode(struct device *parent,
|
||||
const struct typec_altmode_desc *desc)
|
||||
const struct typec_altmode_desc *desc,
|
||||
const struct device_type *type)
|
||||
{
|
||||
unsigned int id = altmode_id_get(parent);
|
||||
bool is_port = is_typec_port(parent);
|
||||
|
|
@ -556,6 +655,7 @@ typec_register_altmode(struct device *parent,
|
|||
alt->adev.svid = desc->svid;
|
||||
alt->adev.mode = desc->mode;
|
||||
alt->adev.vdo = desc->vdo;
|
||||
alt->adev.mode_selection = desc->mode_selection;
|
||||
alt->roles = desc->roles;
|
||||
alt->id = id;
|
||||
|
||||
|
|
@ -575,7 +675,7 @@ typec_register_altmode(struct device *parent,
|
|||
|
||||
alt->adev.dev.parent = parent;
|
||||
alt->adev.dev.groups = alt->groups;
|
||||
alt->adev.dev.type = &typec_altmode_dev_type;
|
||||
alt->adev.dev.type = type;
|
||||
dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
|
||||
|
||||
get_device(alt->adev.dev.parent);
|
||||
|
|
@ -584,8 +684,6 @@ typec_register_altmode(struct device *parent,
|
|||
if (!is_port)
|
||||
typec_altmode_set_partner(alt);
|
||||
|
||||
/* The partners are bind to drivers */
|
||||
if (is_typec_partner(parent))
|
||||
alt->adev.dev.bus = &typec_bus;
|
||||
|
||||
/* Plug alt modes need a class to generate udev events. */
|
||||
|
|
@ -963,7 +1061,7 @@ struct typec_altmode *
|
|||
typec_partner_register_altmode(struct typec_partner *partner,
|
||||
const struct typec_altmode_desc *desc)
|
||||
{
|
||||
return typec_register_altmode(&partner->dev, desc);
|
||||
return typec_register_altmode(&partner->dev, desc, &typec_partner_altmode_dev_type);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
|
||||
|
||||
|
|
@ -1193,7 +1291,7 @@ struct typec_altmode *
|
|||
typec_plug_register_altmode(struct typec_plug *plug,
|
||||
const struct typec_altmode_desc *desc)
|
||||
{
|
||||
return typec_register_altmode(&plug->dev, desc);
|
||||
return typec_register_altmode(&plug->dev, desc, &typec_plug_altmode_dev_type);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
|
||||
|
||||
|
|
@ -2482,6 +2580,7 @@ typec_port_register_altmode(struct typec_port *port,
|
|||
struct typec_altmode *adev;
|
||||
struct typec_mux *mux;
|
||||
struct typec_retimer *retimer;
|
||||
int ret;
|
||||
|
||||
mux = typec_mux_get(&port->dev);
|
||||
if (IS_ERR(mux))
|
||||
|
|
@ -2493,13 +2592,19 @@ typec_port_register_altmode(struct typec_port *port,
|
|||
return ERR_CAST(retimer);
|
||||
}
|
||||
|
||||
adev = typec_register_altmode(&port->dev, desc);
|
||||
adev = typec_register_altmode(&port->dev, desc, &typec_port_altmode_dev_type);
|
||||
if (IS_ERR(adev)) {
|
||||
typec_retimer_put(retimer);
|
||||
typec_mux_put(mux);
|
||||
} else {
|
||||
to_altmode(adev)->mux = mux;
|
||||
to_altmode(adev)->retimer = retimer;
|
||||
|
||||
ret = typec_mode_set_priority(adev, 0);
|
||||
if (ret) {
|
||||
typec_unregister_altmode(adev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return adev;
|
||||
|
|
@ -2694,6 +2799,7 @@ struct typec_port *typec_register_port(struct device *parent,
|
|||
}
|
||||
|
||||
port->pd = cap->pd;
|
||||
port->mode_control = !cap->no_mode_control;
|
||||
|
||||
ret = device_add(&port->dev);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
struct typec_mux;
|
||||
struct typec_switch;
|
||||
struct usb_device;
|
||||
struct mode_selection;
|
||||
|
||||
struct typec_plug {
|
||||
struct device dev;
|
||||
|
|
@ -39,6 +40,7 @@ struct typec_partner {
|
|||
u8 usb_capability;
|
||||
|
||||
struct usb_power_delivery *pd;
|
||||
struct mode_selection *sel;
|
||||
|
||||
void (*attach)(struct typec_partner *partner, struct device *dev);
|
||||
void (*deattach)(struct typec_partner *partner, struct device *dev);
|
||||
|
|
@ -62,6 +64,7 @@ struct typec_port {
|
|||
struct mutex partner_link_lock;
|
||||
|
||||
enum typec_orientation orientation;
|
||||
bool mode_control;
|
||||
struct typec_switch *sw;
|
||||
struct typec_mux *mux;
|
||||
struct typec_retimer *retimer;
|
||||
|
|
|
|||
|
|
@ -204,6 +204,23 @@ static const struct typec_operations hd3ss3220_ops = {
|
|||
.port_type_set = hd3ss3220_port_type_set,
|
||||
};
|
||||
|
||||
static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (regulator_is_enabled(hd3ss3220->vbus) == on)
|
||||
return;
|
||||
|
||||
if (on)
|
||||
ret = regulator_enable(hd3ss3220->vbus);
|
||||
else
|
||||
ret = regulator_disable(hd3ss3220->vbus);
|
||||
|
||||
if (ret)
|
||||
dev_err(hd3ss3220->dev,
|
||||
"vbus regulator %s failed: %d\n", on ? "disable" : "enable", ret);
|
||||
}
|
||||
|
||||
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
|
||||
{
|
||||
enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
|
||||
|
|
@ -221,6 +238,9 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
|
|||
break;
|
||||
}
|
||||
|
||||
if (hd3ss3220->vbus && !hd3ss3220->id_gpiod)
|
||||
hd3ss3220_regulator_control(hd3ss3220, role_state == USB_ROLE_HOST);
|
||||
|
||||
hd3ss3220->role_state = role_state;
|
||||
}
|
||||
|
||||
|
|
@ -330,18 +350,10 @@ static const struct regmap_config config = {
|
|||
static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct hd3ss3220 *hd3ss3220 = dev_id;
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod);
|
||||
if (!id)
|
||||
ret = regulator_enable(hd3ss3220->vbus);
|
||||
else
|
||||
ret = regulator_disable(hd3ss3220->vbus);
|
||||
|
||||
if (ret)
|
||||
dev_err(hd3ss3220->dev,
|
||||
"vbus regulator %s failed: %d\n", id ? "disable" : "enable", ret);
|
||||
hd3ss3220_regulator_control(hd3ss3220, !id);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
|||
283
drivers/usb/typec/mode_selection.c
Normal file
283
drivers/usb/typec/mode_selection.c
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2025 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/usb/typec_altmode.h>
|
||||
|
||||
#include "class.h"
|
||||
|
||||
/**
|
||||
* struct mode_state - State tracking for a specific Type-C alternate mode
|
||||
* @svid: Standard or Vendor ID of the Alternate Mode
|
||||
* @priority: Mode priority
|
||||
* @error: Outcome of the last attempt to enter the mode
|
||||
* @list: List head to link this mode state into a prioritized list
|
||||
*/
|
||||
struct mode_state {
|
||||
u16 svid;
|
||||
u8 priority;
|
||||
int error;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mode_selection - Manages the selection and state of Alternate Modes
|
||||
* @mode_list: Prioritized list of available Alternate Modes
|
||||
* @lock: Mutex to protect mode_list
|
||||
* @work: Work structure
|
||||
* @partner: Handle to the Type-C partner device
|
||||
* @active_svid: svid of currently active mode
|
||||
* @timeout: Timeout for a mode entry attempt, ms
|
||||
* @delay: Delay between mode entry/exit attempts, ms
|
||||
*/
|
||||
struct mode_selection {
|
||||
struct list_head mode_list;
|
||||
/* Protects the mode_list*/
|
||||
struct mutex lock;
|
||||
struct delayed_work work;
|
||||
struct typec_partner *partner;
|
||||
u16 active_svid;
|
||||
unsigned int timeout;
|
||||
unsigned int delay;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mode_order - Mode activation tracking
|
||||
* @svid: Standard or Vendor ID of the Alternate Mode
|
||||
* @enter: Flag indicating if the driver is currently attempting to enter or
|
||||
* exit the mode
|
||||
* @result: Outcome of the attempt to activate the mode
|
||||
*/
|
||||
struct mode_order {
|
||||
u16 svid;
|
||||
int enter;
|
||||
int result;
|
||||
};
|
||||
|
||||
static int activate_altmode(struct device *dev, void *data)
|
||||
{
|
||||
if (is_typec_partner_altmode(dev)) {
|
||||
struct typec_altmode *alt = to_typec_altmode(dev);
|
||||
struct mode_order *order = (struct mode_order *)data;
|
||||
|
||||
if (order->svid == alt->svid) {
|
||||
if (alt->ops && alt->ops->activate)
|
||||
order->result = alt->ops->activate(alt, order->enter);
|
||||
else
|
||||
order->result = -EOPNOTSUPP;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mode_selection_activate(struct mode_selection *sel,
|
||||
const u16 svid, const int enter)
|
||||
|
||||
__must_hold(&sel->lock)
|
||||
{
|
||||
struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV};
|
||||
|
||||
/*
|
||||
* The port driver may acquire its internal mutex during alternate mode
|
||||
* activation. Since this is the same mutex that may be held during the
|
||||
* execution of typec_altmode_state_update(), it is crucial to release
|
||||
* sel->mutex before activation to avoid potential deadlock.
|
||||
* Note that sel->mode_list must remain invariant throughout this unlocked
|
||||
* interval.
|
||||
*/
|
||||
mutex_unlock(&sel->lock);
|
||||
device_for_each_child(&sel->partner->dev, &order, activate_altmode);
|
||||
mutex_lock(&sel->lock);
|
||||
|
||||
return order.result;
|
||||
}
|
||||
|
||||
static void mode_list_clean(struct mode_selection *sel)
|
||||
{
|
||||
struct mode_state *ms, *tmp;
|
||||
|
||||
list_for_each_entry_safe(ms, tmp, &sel->mode_list, list) {
|
||||
list_del(&ms->list);
|
||||
kfree(ms);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mode_selection_work_fn() - Alternate mode activation task
|
||||
* @work: work structure
|
||||
*
|
||||
* - If the Alternate Mode currently prioritized at the top of the list is already
|
||||
* active, the entire selection process is considered finished.
|
||||
* - If a different Alternate Mode is currently active, the system must exit that
|
||||
* active mode first before attempting any new entry.
|
||||
*
|
||||
* The function then checks the result of the attempt to entre the current mode,
|
||||
* stored in the `ms->error` field:
|
||||
* - if the attempt FAILED, the mode is deactivated and removed from the list.
|
||||
* - `ms->error` value of 0 signifies that the mode has not yet been activated.
|
||||
*
|
||||
* Once successfully activated, the task is scheduled for subsequent entry after
|
||||
* a timeout period. The alternate mode driver is expected to call back with the
|
||||
* actual mode entry result via `typec_altmode_state_update()`.
|
||||
*/
|
||||
static void mode_selection_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct mode_selection *sel = container_of(work,
|
||||
struct mode_selection, work.work);
|
||||
struct mode_state *ms;
|
||||
unsigned int delay = sel->delay;
|
||||
int result;
|
||||
|
||||
guard(mutex)(&sel->lock);
|
||||
|
||||
ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
|
||||
if (!ms)
|
||||
return;
|
||||
|
||||
if (sel->active_svid == ms->svid) {
|
||||
dev_dbg(&sel->partner->dev, "%x altmode is active\n", ms->svid);
|
||||
mode_list_clean(sel);
|
||||
} else if (sel->active_svid != 0) {
|
||||
result = mode_selection_activate(sel, sel->active_svid, 0);
|
||||
if (result)
|
||||
mode_list_clean(sel);
|
||||
else
|
||||
sel->active_svid = 0;
|
||||
} else if (ms->error) {
|
||||
dev_err(&sel->partner->dev, "%x: entry error %pe\n",
|
||||
ms->svid, ERR_PTR(ms->error));
|
||||
mode_selection_activate(sel, ms->svid, 0);
|
||||
list_del(&ms->list);
|
||||
kfree(ms);
|
||||
} else {
|
||||
result = mode_selection_activate(sel, ms->svid, 1);
|
||||
if (result) {
|
||||
dev_err(&sel->partner->dev, "%x: activation error %pe\n",
|
||||
ms->svid, ERR_PTR(result));
|
||||
list_del(&ms->list);
|
||||
kfree(ms);
|
||||
} else {
|
||||
delay = sel->timeout;
|
||||
ms->error = -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!list_empty(&sel->mode_list))
|
||||
schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
|
||||
}
|
||||
|
||||
void typec_altmode_state_update(struct typec_partner *partner, const u16 svid,
|
||||
const int error)
|
||||
{
|
||||
struct mode_selection *sel = partner->sel;
|
||||
struct mode_state *ms;
|
||||
|
||||
if (sel) {
|
||||
mutex_lock(&sel->lock);
|
||||
ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
|
||||
if (ms && ms->svid == svid) {
|
||||
ms->error = error;
|
||||
if (cancel_delayed_work(&sel->work))
|
||||
schedule_delayed_work(&sel->work, 0);
|
||||
}
|
||||
if (!error)
|
||||
sel->active_svid = svid;
|
||||
else
|
||||
sel->active_svid = 0;
|
||||
mutex_unlock(&sel->lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_altmode_state_update);
|
||||
|
||||
static int compare_priorities(void *priv,
|
||||
const struct list_head *a, const struct list_head *b)
|
||||
{
|
||||
const struct mode_state *msa = container_of(a, struct mode_state, list);
|
||||
const struct mode_state *msb = container_of(b, struct mode_state, list);
|
||||
|
||||
if (msa->priority < msb->priority)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int altmode_add_to_list(struct device *dev, void *data)
|
||||
{
|
||||
if (is_typec_partner_altmode(dev)) {
|
||||
struct list_head *list = (struct list_head *)data;
|
||||
struct typec_altmode *altmode = to_typec_altmode(dev);
|
||||
const struct typec_altmode *pdev = typec_altmode_get_partner(altmode);
|
||||
struct mode_state *ms;
|
||||
|
||||
if (pdev && altmode->ops && altmode->ops->activate) {
|
||||
ms = kzalloc(sizeof(*ms), GFP_KERNEL);
|
||||
if (!ms)
|
||||
return -ENOMEM;
|
||||
ms->svid = pdev->svid;
|
||||
ms->priority = pdev->priority;
|
||||
INIT_LIST_HEAD(&ms->list);
|
||||
list_add_tail(&ms->list, list);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int typec_mode_selection_start(struct typec_partner *partner,
|
||||
const unsigned int delay, const unsigned int timeout)
|
||||
{
|
||||
struct mode_selection *sel;
|
||||
int ret;
|
||||
|
||||
if (partner->usb_mode == USB_MODE_USB4)
|
||||
return -EBUSY;
|
||||
|
||||
if (partner->sel)
|
||||
return -EALREADY;
|
||||
|
||||
sel = kzalloc(sizeof(*sel), GFP_KERNEL);
|
||||
if (!sel)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&sel->mode_list);
|
||||
|
||||
ret = device_for_each_child(&partner->dev, &sel->mode_list,
|
||||
altmode_add_to_list);
|
||||
|
||||
if (ret || list_empty(&sel->mode_list)) {
|
||||
mode_list_clean(sel);
|
||||
kfree(sel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_sort(NULL, &sel->mode_list, compare_priorities);
|
||||
sel->partner = partner;
|
||||
sel->delay = delay;
|
||||
sel->timeout = timeout;
|
||||
mutex_init(&sel->lock);
|
||||
INIT_DELAYED_WORK(&sel->work, mode_selection_work_fn);
|
||||
schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
|
||||
partner->sel = sel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mode_selection_start);
|
||||
|
||||
void typec_mode_selection_delete(struct typec_partner *partner)
|
||||
{
|
||||
struct mode_selection *sel = partner->sel;
|
||||
|
||||
if (sel) {
|
||||
partner->sel = NULL;
|
||||
cancel_delayed_work_sync(&sel->work);
|
||||
mode_list_clean(sel);
|
||||
mutex_destroy(&sel->lock);
|
||||
kfree(sel);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mode_selection_delete);
|
||||
|
|
@ -1808,7 +1808,7 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt,
|
|||
/*
|
||||
* PD3.0 Spec 6.4.4.3.2: The SVIDs are returned 2 per VDO (see Table
|
||||
* 6-43), and can be returned maximum 6 VDOs per response (see Figure
|
||||
* 6-19). If the Respondersupports 12 or more SVID then the Discover
|
||||
* 6-19). If the Responder supports 12 or more SVID then the Discover
|
||||
* SVIDs Command Shall be executed multiple times until a Discover
|
||||
* SVIDs VDO is returned ending either with a SVID value of 0x0000 in
|
||||
* the last part of the last VDO or with a VDO containing two SVIDs
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ config CROS_EC_UCSI
|
|||
tristate "UCSI Driver for ChromeOS EC"
|
||||
depends on MFD_CROS_EC_DEV
|
||||
depends on CROS_USBPD_NOTIFY
|
||||
depends on !EXTCON_TCSS_CROS_EC
|
||||
default MFD_CROS_EC_DEV
|
||||
help
|
||||
This driver enables UCSI support for a ChromeOS EC. The EC is
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
|
|||
typec_ucsi-y += displayport.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_TYPEC_TBT_ALTMODE),)
|
||||
typec_ucsi-y += thunderbolt.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
|
||||
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
|
||||
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/usb/typec_altmode.h>
|
||||
|
||||
#include "ucsi.h"
|
||||
|
||||
|
|
@ -33,6 +34,11 @@
|
|||
/* Number of times to attempt recovery from a write timeout before giving up. */
|
||||
#define WRITE_TMO_CTR_MAX 5
|
||||
|
||||
/* Delay between mode entry/exit attempts, ms */
|
||||
static const unsigned int mode_selection_delay = 1000;
|
||||
/* Timeout for a mode entry attempt, ms */
|
||||
static const unsigned int mode_selection_timeout = 4000;
|
||||
|
||||
struct cros_ucsi_data {
|
||||
struct device *dev;
|
||||
struct ucsi *ucsi;
|
||||
|
|
@ -134,6 +140,20 @@ static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void cros_ucsi_add_partner_altmodes(struct ucsi_connector *con)
|
||||
{
|
||||
if (!con->typec_cap.no_mode_control)
|
||||
typec_mode_selection_start(con->partner,
|
||||
mode_selection_delay,
|
||||
mode_selection_timeout);
|
||||
}
|
||||
|
||||
static void cros_ucsi_remove_partner_altmodes(struct ucsi_connector *con)
|
||||
{
|
||||
if (!con->typec_cap.no_mode_control)
|
||||
typec_mode_selection_delete(con->partner);
|
||||
}
|
||||
|
||||
static const struct ucsi_operations cros_ucsi_ops = {
|
||||
.read_version = cros_ucsi_read_version,
|
||||
.read_cci = cros_ucsi_read_cci,
|
||||
|
|
@ -141,6 +161,8 @@ static const struct ucsi_operations cros_ucsi_ops = {
|
|||
.read_message_in = cros_ucsi_read_message_in,
|
||||
.async_control = cros_ucsi_async_control,
|
||||
.sync_control = cros_ucsi_sync_control,
|
||||
.add_partner_altmodes = cros_ucsi_add_partner_altmodes,
|
||||
.remove_partner_altmodes = cros_ucsi_remove_partner_altmodes,
|
||||
};
|
||||
|
||||
static void cros_ucsi_work(struct work_struct *work)
|
||||
|
|
|
|||
|
|
@ -112,15 +112,20 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
|
|||
union power_supply_propval *val)
|
||||
{
|
||||
u32 pdo;
|
||||
int max_voltage = 0;
|
||||
|
||||
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
|
||||
case UCSI_CONSTAT_PWR_OPMODE_PD:
|
||||
if (con->num_pdos > 0) {
|
||||
pdo = con->src_pdos[con->num_pdos - 1];
|
||||
val->intval = pdo_fixed_voltage(pdo) * 1000;
|
||||
} else {
|
||||
val->intval = 0;
|
||||
for (int i = 0; i < con->num_pdos; i++) {
|
||||
int pdo_voltage = 0;
|
||||
|
||||
pdo = con->src_pdos[i];
|
||||
if (pdo_type(pdo) == PDO_TYPE_FIXED)
|
||||
pdo_voltage = pdo_fixed_voltage(pdo) * 1000;
|
||||
max_voltage = (pdo_voltage > max_voltage) ? pdo_voltage
|
||||
: max_voltage;
|
||||
}
|
||||
val->intval = max_voltage;
|
||||
break;
|
||||
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
|
||||
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
|
||||
|
|
@ -168,6 +173,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
|
|||
union power_supply_propval *val)
|
||||
{
|
||||
u32 pdo;
|
||||
int max_current = 0;
|
||||
|
||||
if (!UCSI_CONSTAT(con, CONNECTED)) {
|
||||
val->intval = 0;
|
||||
|
|
@ -176,12 +182,16 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
|
|||
|
||||
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
|
||||
case UCSI_CONSTAT_PWR_OPMODE_PD:
|
||||
if (con->num_pdos > 0) {
|
||||
pdo = con->src_pdos[con->num_pdos - 1];
|
||||
val->intval = pdo_max_current(pdo) * 1000;
|
||||
} else {
|
||||
val->intval = 0;
|
||||
for (int i = 0; i < con->num_pdos; i++) {
|
||||
int pdo_current = 0;
|
||||
|
||||
pdo = con->src_pdos[i];
|
||||
if (pdo_type(pdo) == PDO_TYPE_FIXED)
|
||||
pdo_current = pdo_max_current(pdo) * 1000;
|
||||
max_current = (pdo_current > max_current) ? pdo_current
|
||||
: max_current;
|
||||
}
|
||||
val->intval = max_current;
|
||||
break;
|
||||
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
|
||||
val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
|
||||
|
|
@ -202,11 +212,29 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
|
|||
static int ucsi_psy_get_current_now(struct ucsi_connector *con,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD)
|
||||
val->intval = rdo_op_current(con->rdo) * 1000;
|
||||
else
|
||||
if (!UCSI_CONSTAT(con, CONNECTED)) {
|
||||
val->intval = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
|
||||
case UCSI_CONSTAT_PWR_OPMODE_PD:
|
||||
val->intval = rdo_op_current(con->rdo) * 1000;
|
||||
break;
|
||||
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
|
||||
val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
|
||||
break;
|
||||
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
|
||||
val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
|
||||
break;
|
||||
case UCSI_CONSTAT_PWR_OPMODE_BC:
|
||||
case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
|
||||
/* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
|
||||
default:
|
||||
val->intval = UCSI_TYPEC_DEFAULT_CURRENT * 1000;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
|
||||
|
|
|
|||
212
drivers/usb/typec/ucsi/thunderbolt.c
Normal file
212
drivers/usb/typec/ucsi/thunderbolt.c
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* UCSI Thunderbolt Alternate Mode Support
|
||||
*
|
||||
* Copyright 2026 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/usb/typec_tbt.h>
|
||||
#include <linux/usb/pd_vdo.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/device/devres.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/typec_altmode.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "ucsi.h"
|
||||
|
||||
/**
|
||||
* struct ucsi_tbt - Thunderbolt Alternate Mode private data structure
|
||||
* @con: Pointer to UCSI connector structure
|
||||
* @alt: Pointer to typec altmode structure
|
||||
* @work: Work structure
|
||||
* @cam: An offset into the list of alternate modes supported by the PPM
|
||||
* @header: VDO header
|
||||
*/
|
||||
struct ucsi_tbt {
|
||||
struct ucsi_connector *con;
|
||||
struct typec_altmode *alt;
|
||||
struct work_struct work;
|
||||
int cam;
|
||||
u32 header;
|
||||
};
|
||||
|
||||
static void ucsi_thunderbolt_work(struct work_struct *work)
|
||||
{
|
||||
struct ucsi_tbt *tbt = container_of(work, struct ucsi_tbt, work);
|
||||
|
||||
if (typec_altmode_vdm(tbt->alt, tbt->header, NULL, 0))
|
||||
dev_err(&tbt->alt->dev, "VDM 0x%x failed\n", tbt->header);
|
||||
|
||||
tbt->header = 0;
|
||||
}
|
||||
|
||||
static int ucsi_thunderbolt_set_altmode(struct ucsi_tbt *tbt,
|
||||
bool enter, u32 vdo)
|
||||
{
|
||||
int svdm_version;
|
||||
int cmd;
|
||||
int ret;
|
||||
u64 command = UCSI_SET_NEW_CAM |
|
||||
UCSI_CONNECTOR_NUMBER(tbt->con->num) |
|
||||
UCSI_SET_NEW_CAM_SET_AM(tbt->cam) |
|
||||
((u64)vdo << 32);
|
||||
|
||||
if (enter)
|
||||
command |= (1 << 23);
|
||||
|
||||
ret = ucsi_send_command(tbt->con->ucsi, command, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
svdm_version = typec_altmode_get_svdm_version(tbt->alt);
|
||||
if (svdm_version < 0)
|
||||
return svdm_version;
|
||||
|
||||
if (enter)
|
||||
cmd = CMD_ENTER_MODE;
|
||||
else
|
||||
cmd = CMD_EXIT_MODE;
|
||||
tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
|
||||
tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
|
||||
tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
|
||||
|
||||
schedule_work(&tbt->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_thunderbolt_enter(struct typec_altmode *alt, u32 *vdo)
|
||||
{
|
||||
struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
|
||||
struct ucsi_connector *con = tbt->con;
|
||||
u64 command;
|
||||
u8 cur = 0;
|
||||
int ret;
|
||||
|
||||
if (!ucsi_con_mutex_lock(con))
|
||||
return -ENOTCONN;
|
||||
|
||||
command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
|
||||
if (ret < 0) {
|
||||
if (con->ucsi->version > 0x0100)
|
||||
goto err_unlock;
|
||||
cur = 0xff;
|
||||
}
|
||||
|
||||
if (cur != 0xff) {
|
||||
if (cur >= UCSI_MAX_ALTMODES || con->port_altmode[cur] != alt)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = 0;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
ret = ucsi_thunderbolt_set_altmode(tbt, true, *vdo);
|
||||
ucsi_altmode_update_active(tbt->con);
|
||||
|
||||
err_unlock:
|
||||
ucsi_con_mutex_unlock(con);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_thunderbolt_exit(struct typec_altmode *alt)
|
||||
{
|
||||
struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
|
||||
int ret;
|
||||
|
||||
if (!ucsi_con_mutex_lock(tbt->con))
|
||||
return -ENOTCONN;
|
||||
|
||||
ret = ucsi_thunderbolt_set_altmode(tbt, false, 0);
|
||||
|
||||
ucsi_con_mutex_unlock(tbt->con);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_thunderbolt_vdm(struct typec_altmode *alt,
|
||||
u32 header, const u32 *data, int count)
|
||||
{
|
||||
struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
|
||||
int cmd_type = PD_VDO_CMDT(header);
|
||||
int cmd = PD_VDO_CMD(header);
|
||||
int svdm_version;
|
||||
|
||||
if (!ucsi_con_mutex_lock(tbt->con))
|
||||
return -ENOTCONN;
|
||||
|
||||
svdm_version = typec_altmode_get_svdm_version(alt);
|
||||
if (svdm_version < 0) {
|
||||
ucsi_con_mutex_unlock(tbt->con);
|
||||
return svdm_version;
|
||||
}
|
||||
|
||||
switch (cmd_type) {
|
||||
case CMDT_INIT:
|
||||
if (PD_VDO_SVDM_VER(header) < svdm_version) {
|
||||
svdm_version = PD_VDO_SVDM_VER(header);
|
||||
typec_partner_set_svdm_version(tbt->con->partner, svdm_version);
|
||||
}
|
||||
tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
|
||||
tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
|
||||
tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
|
||||
|
||||
schedule_work(&tbt->work);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ucsi_con_mutex_unlock(tbt->con);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct typec_altmode_ops ucsi_thunderbolt_ops = {
|
||||
.enter = ucsi_thunderbolt_enter,
|
||||
.exit = ucsi_thunderbolt_exit,
|
||||
.vdm = ucsi_thunderbolt_vdm,
|
||||
};
|
||||
|
||||
struct typec_altmode *ucsi_register_thunderbolt(struct ucsi_connector *con,
|
||||
bool override, int offset,
|
||||
struct typec_altmode_desc *desc)
|
||||
{
|
||||
struct typec_altmode *alt;
|
||||
struct ucsi_tbt *tbt;
|
||||
|
||||
alt = typec_port_register_altmode(con->port, desc);
|
||||
if (IS_ERR(alt) || !override)
|
||||
return alt;
|
||||
|
||||
tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
|
||||
if (!tbt) {
|
||||
typec_unregister_altmode(alt);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
tbt->cam = offset;
|
||||
tbt->con = con;
|
||||
tbt->alt = alt;
|
||||
INIT_WORK(&tbt->work, ucsi_thunderbolt_work);
|
||||
typec_altmode_set_drvdata(alt, tbt);
|
||||
typec_altmode_set_ops(alt, &ucsi_thunderbolt_ops);
|
||||
|
||||
return alt;
|
||||
}
|
||||
|
||||
void ucsi_thunderbolt_remove_partner(struct typec_altmode *alt)
|
||||
{
|
||||
struct ucsi_tbt *tbt;
|
||||
|
||||
if (alt) {
|
||||
tbt = typec_altmode_get_drvdata(alt);
|
||||
if (tbt)
|
||||
cancel_work_sync(&tbt->work);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/typec_dp.h>
|
||||
#include <linux/usb/typec_tbt.h>
|
||||
|
||||
#include "ucsi.h"
|
||||
#include "trace.h"
|
||||
|
|
@ -314,6 +315,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
|
|||
{
|
||||
const struct typec_altmode *altmode = NULL;
|
||||
u64 command;
|
||||
u16 svid = 0;
|
||||
int ret;
|
||||
u8 cur;
|
||||
int i;
|
||||
|
|
@ -335,6 +337,10 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
|
|||
for (i = 0; con->partner_altmode[i]; i++)
|
||||
typec_altmode_update_active(con->partner_altmode[i],
|
||||
con->partner_altmode[i] == altmode);
|
||||
|
||||
if (altmode)
|
||||
svid = altmode->svid;
|
||||
typec_altmode_state_update(con->partner, svid, 0);
|
||||
}
|
||||
|
||||
static int ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid)
|
||||
|
|
@ -412,6 +418,9 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
|
|||
alt = ucsi_register_displayport(con, override,
|
||||
i, desc);
|
||||
break;
|
||||
case USB_TYPEC_TBT_SID:
|
||||
alt = ucsi_register_thunderbolt(con, override, i, desc);
|
||||
break;
|
||||
default:
|
||||
alt = typec_port_register_altmode(con->port, desc);
|
||||
break;
|
||||
|
|
@ -609,6 +618,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
desc.vdo = alt[j].mid;
|
||||
desc.svid = alt[j].svid;
|
||||
desc.roles = TYPEC_PORT_DRD;
|
||||
desc.mode_selection = con->ucsi->ops->add_partner_altmodes &&
|
||||
!con->typec_cap.no_mode_control;
|
||||
|
||||
ret = ucsi_register_altmode(con, &desc, recipient);
|
||||
if (ret)
|
||||
|
|
@ -640,12 +651,15 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
}
|
||||
|
||||
while (adev[i]) {
|
||||
if (recipient == UCSI_RECIPIENT_SOP &&
|
||||
(adev[i]->svid == USB_TYPEC_DP_SID ||
|
||||
(adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
|
||||
adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) {
|
||||
if (recipient == UCSI_RECIPIENT_SOP) {
|
||||
pdev = typec_altmode_get_partner(adev[i]);
|
||||
|
||||
if (adev[i]->svid == USB_TYPEC_DP_SID ||
|
||||
(adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
|
||||
adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))
|
||||
ucsi_displayport_remove_partner((void *)pdev);
|
||||
else if (adev[i]->svid == USB_TYPEC_TBT_SID)
|
||||
ucsi_thunderbolt_remove_partner((void *)pdev);
|
||||
}
|
||||
typec_unregister_altmode(adev[i]);
|
||||
adev[i++] = NULL;
|
||||
|
|
@ -831,6 +845,8 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
|
|||
if (con->partner_altmode[0]) {
|
||||
num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
|
||||
typec_partner_set_num_altmodes(con->partner, num_partner_am);
|
||||
if (con->ucsi->ops->add_partner_altmodes)
|
||||
con->ucsi->ops->add_partner_altmodes(con);
|
||||
ucsi_altmode_update_active(con);
|
||||
return 0;
|
||||
} else {
|
||||
|
|
@ -1119,6 +1135,8 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
|
|||
return;
|
||||
|
||||
typec_set_mode(con->port, TYPEC_STATE_SAFE);
|
||||
if (con->ucsi->ops->remove_partner_altmodes)
|
||||
con->ucsi->ops->remove_partner_altmodes(con);
|
||||
|
||||
typec_partner_set_usb_power_delivery(con->partner, NULL);
|
||||
ucsi_unregister_partner_pdos(con);
|
||||
|
|
@ -1307,6 +1325,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
|
|||
|
||||
if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) {
|
||||
ucsi_partner_change(con);
|
||||
ucsi_altmode_update_active(con);
|
||||
|
||||
/* Complete pending data role swap */
|
||||
if (!completion_done(&con->complete))
|
||||
|
|
@ -1659,6 +1678,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
|
|||
|
||||
cap->driver_data = con;
|
||||
cap->ops = &ucsi_ops;
|
||||
cap->no_mode_control = !(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE);
|
||||
|
||||
if (ucsi->version >= UCSI_VERSION_2_0)
|
||||
con->typec_cap.orientation_aware = true;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ struct dentry;
|
|||
* @update_altmodes: Squashes duplicate DP altmodes
|
||||
* @update_connector: Update connector capabilities before registering
|
||||
* @connector_status: Updates connector status, called holding connector lock
|
||||
* @add_partner_altmodes: Start mode selection
|
||||
* @remove_partner_altmodes: Clean mode selection
|
||||
*
|
||||
* Read and write routines for UCSI interface. @sync_write must wait for the
|
||||
* Command Completion Event from the PPM before returning, and @async_write must
|
||||
|
|
@ -88,6 +90,8 @@ struct ucsi_operations {
|
|||
struct ucsi_altmode *updated);
|
||||
void (*update_connector)(struct ucsi_connector *con);
|
||||
void (*connector_status)(struct ucsi_connector *con);
|
||||
void (*add_partner_altmodes)(struct ucsi_connector *con);
|
||||
void (*remove_partner_altmodes)(struct ucsi_connector *con);
|
||||
};
|
||||
|
||||
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
|
||||
|
|
@ -596,6 +600,26 @@ static inline void
|
|||
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
|
||||
#endif /* CONFIG_TYPEC_DP_ALTMODE */
|
||||
|
||||
#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
|
||||
struct typec_altmode *
|
||||
ucsi_register_thunderbolt(struct ucsi_connector *con,
|
||||
bool override, int offset,
|
||||
struct typec_altmode_desc *desc);
|
||||
|
||||
void ucsi_thunderbolt_remove_partner(struct typec_altmode *adev);
|
||||
#else
|
||||
static inline struct typec_altmode *
|
||||
ucsi_register_thunderbolt(struct ucsi_connector *con,
|
||||
bool override, int offset,
|
||||
struct typec_altmode_desc *desc)
|
||||
{
|
||||
return typec_port_register_altmode(con->port, desc);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ucsi_thunderbolt_remove_partner(struct typec_altmode *adev) { }
|
||||
#endif /* CONFIG_TYPEC_TBT_ALTMODE */
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void ucsi_debugfs_init(void);
|
||||
void ucsi_debugfs_exit(void);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ void stub_complete(struct urb *urb)
|
|||
"stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
dev_info(&urb->dev->dev,
|
||||
dev_dbg(&urb->dev->dev,
|
||||
"unlinked by a call to usb_unlink_urb()\n");
|
||||
break;
|
||||
case -EPIPE:
|
||||
|
|
|
|||
|
|
@ -1295,8 +1295,7 @@ struct usb_driver {
|
|||
* resume and suspend functions will be called in addition to the driver's
|
||||
* own, so this part of the setup does not need to be replicated.
|
||||
*
|
||||
* USB drivers must provide all the fields listed above except driver,
|
||||
* match, and id_table.
|
||||
* USB device drivers must provide a name, other driver fields are optional.
|
||||
*/
|
||||
struct usb_device_driver {
|
||||
const char *name;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue