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:
Linus Torvalds 2026-02-17 09:36:43 -08:00
commit 17f8d20093
110 changed files with 3494 additions and 4594 deletions

View file

@ -4,11 +4,12 @@ KernelVersion: 3.19
Description: Description:
The attributes: The attributes:
========== ==================================== ================ ====================================
index index value for the USB MIDI adapter index index value for the USB MIDI adapter
id ID string for the USB MIDI adapter id ID string for the USB MIDI adapter
buflen MIDI buffer length buflen MIDI buffer length
qlen USB read request queue length qlen USB read request queue length
in_ports number of MIDI input ports in_ports number of MIDI input ports
out_ports number of MIDI output ports out_ports number of MIDI output ports
========== ==================================== interface_string USB AudioControl interface string
================ ====================================

View file

@ -85,3 +85,45 @@ Description:
up to 5000. The default value is 64 ms. up to 5000. The default value is 64 ms.
This polling interval is used while DbC is enabled but has no This polling interval is used while DbC is enabled but has no
active data transfers. 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.

View file

@ -162,6 +162,17 @@ Description: Lists the supported USB Modes. The default USB mode that is used
- usb3 (USB 3.2) - usb3 (USB 3.2)
- usb4 (USB4) - 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/) USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
What: /sys/class/typec/<port>-partner/accessory_mode What: /sys/class/typec/<port>-partner/accessory_mode

View file

@ -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 The driver will create one virtual ethernet interface per Thunderbolt
port which are named like ``thunderbolt0`` and so on. From this point 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. configure the interface or let your GUI handle it automatically.
Forcing power Forcing power

View file

@ -17,8 +17,8 @@ description: |+
Supported number of devices and endpoints vary depending on hardware Supported number of devices and endpoints vary depending on hardware
revisions. AST2400 and AST2500 Virtual Hub supports 5 downstream devices revisions. AST2400 and AST2500 Virtual Hub supports 5 downstream devices
and 15 generic endpoints, while AST2600 Virtual Hub supports 7 downstream and 15 generic endpoints, while AST2600 and AST2700 Virtual Hub supports
devices and 21 generic endpoints. 7 downstream devices and 21 generic endpoints.
properties: properties:
compatible: compatible:
@ -26,6 +26,7 @@ properties:
- aspeed,ast2400-usb-vhub - aspeed,ast2400-usb-vhub
- aspeed,ast2500-usb-vhub - aspeed,ast2500-usb-vhub
- aspeed,ast2600-usb-vhub - aspeed,ast2600-usb-vhub
- aspeed,ast2700-usb-vhub
reg: reg:
maxItems: 1 maxItems: 1
@ -33,6 +34,9 @@ properties:
clocks: clocks:
maxItems: 1 maxItems: 1
resets:
maxItems: 1
interrupts: interrupts:
maxItems: 1 maxItems: 1
@ -107,6 +111,20 @@ required:
- aspeed,vhub-downstream-ports - aspeed,vhub-downstream-ports
- aspeed,vhub-generic-endpoints - aspeed,vhub-generic-endpoints
if:
properties:
compatible:
contains:
const: aspeed,ast2700-usb-vhub
then:
required:
- resets
else:
properties:
resets: false
additionalProperties: false additionalProperties: false
examples: examples:

View file

@ -93,6 +93,8 @@ properties:
minItems: 1 minItems: 1
maxItems: 2 maxItems: 2
dma-coherent: true
interrupts: interrupts:
maxItems: 1 maxItems: 1

View file

@ -64,6 +64,8 @@ properties:
reg: reg:
maxItems: 1 maxItems: 1
dma-coherent: true
interrupts: interrupts:
maxItems: 1 maxItems: 1

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

View file

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

View file

@ -27,6 +27,7 @@ properties:
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC} - renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
- renesas,usbhs-r9a07g054 # RZ/V2L - renesas,usbhs-r9a07g054 # RZ/V2L
- renesas,usbhs-r9a08g045 # RZ/G3S - renesas,usbhs-r9a08g045 # RZ/G3S
- renesas,usbhs-r9a09g047 # RZ/G3E
- renesas,usbhs-r9a09g056 # RZ/V2N - renesas,usbhs-r9a09g056 # RZ/V2N
- renesas,usbhs-r9a09g057 # RZ/V2H(P) - renesas,usbhs-r9a09g057 # RZ/V2H(P)
- const: renesas,rzg2l-usbhs - const: renesas,rzg2l-usbhs

View file

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

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

View file

@ -368,14 +368,15 @@ Function-specific configfs interface
The function name to use when creating the function directory is "midi". The function name to use when creating the function directory is "midi".
The MIDI function provides these attributes in its function directory: The MIDI function provides these attributes in its function directory:
=============== ==================================== ================ ====================================
buflen MIDI buffer length buflen MIDI buffer length
id ID string for the USB MIDI adapter id ID string for the USB MIDI adapter
in_ports number of MIDI input ports in_ports number of MIDI input ports
index index value for the USB MIDI adapter index index value for the USB MIDI adapter
out_ports number of MIDI output ports out_ports number of MIDI output ports
qlen USB read request queue length qlen USB read request queue length
=============== ==================================== interface_string USB AudioControl interface string
================ ====================================
Testing the MIDI function 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_mult 0..2 (hs/ss only)
isoc_maxburst 0..15 (ss only) isoc_maxburst 0..15 (ss only)
bulk_buflen buffer length bulk_buflen buffer length
bulk_maxburst 0..15 (ss only)
bulk_qlen depth of queue for bulk bulk_qlen depth of queue for bulk
iso_qlen depth of queue for iso iso_qlen depth of queue for iso
=============== ================================== =============== ==================================

View file

@ -10859,10 +10859,12 @@ P: Documentation/process/maintainer-soc-clean-dts.rst
C: irc://irc.oftc.net/pixel6-kernel-dev C: irc://irc.oftc.net/pixel6-kernel-dev
F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml 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/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: arch/arm64/boot/dts/exynos/google/
F: drivers/clk/samsung/clk-gs101.c F: drivers/clk/samsung/clk-gs101.c
F: drivers/soc/samsung/gs101-pmu.c F: drivers/soc/samsung/gs101-pmu.c
F: drivers/phy/samsung/phy-gs101-ufs.c F: drivers/phy/samsung/phy-gs101-ufs.c
F: drivers/usb/dwc3/dwc3-google.c
F: include/dt-bindings/clock/google,gs101* F: include/dt-bindings/clock/google,gs101*
K: [gG]oogle.?[tT]ensor K: [gG]oogle.?[tT]ensor

View file

@ -491,6 +491,7 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
cap->driver_data = cros_port; cap->driver_data = cros_port;
cap->ops = &cros_typec_usb_mode_ops; cap->ops = &cros_typec_usb_mode_ops;
cap->no_mode_control = !typec->ap_driven_altmode;
cros_port->port = typec_register_port(dev, cap); cros_port->port = typec_register_port(dev, cap);
if (IS_ERR(cros_port->port)) { if (IS_ERR(cros_port->port)) {

View file

@ -586,7 +586,7 @@ int tb_path_activate(struct tb_path *path)
tb_dbg(path->tb, "%s path activation complete\n", path->name); tb_dbg(path->tb, "%s path activation complete\n", path->name);
return 0; return 0;
err: 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; return res;
} }

View file

@ -30,7 +30,6 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/
obj-$(CONFIG_USB_XHCI_HCD) += host/ obj-$(CONFIG_USB_XHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_USB_ISP1362_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_FSL_USB2) += host/ obj-$(CONFIG_USB_FSL_USB2) += host/
obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/

View file

@ -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); cdns->roles[cdns->role]->resume(cdns, power_lost);
return 0; return 0;

View file

@ -385,6 +385,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
const struct ci_hdrc_imx_platform_flag *imx_platform_flag; const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
const char *irq_name;
imx_platform_flag = of_device_get_match_data(&pdev->dev); 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); data->wakeup_irq = platform_get_irq_optional(pdev, 1);
if (data->wakeup_irq > 0) { 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, ret = devm_request_threaded_irq(dev, data->wakeup_irq,
NULL, ci_wakeup_irq_handler, NULL, ci_wakeup_irq_handler,
IRQF_ONESHOT | IRQF_NO_AUTOEN, IRQF_ONESHOT | IRQF_NO_AUTOEN,
pdata.name, data); irq_name, data);
if (ret) if (ret)
goto err_clk; goto err_clk;
} }

View file

@ -931,6 +931,13 @@ __acquires(hwep->lock)
list_del_init(&hwreq->queue); list_del_init(&hwreq->queue);
hwreq->req.status = -ESHUTDOWN; 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) { if (hwreq->req.complete != NULL) {
spin_unlock(hwep->lock); spin_unlock(hwep->lock);
usb_gadget_giveback_request(&hwep->ep, &hwreq->req); usb_gadget_giveback_request(&hwep->ep, &hwreq->req);

View file

@ -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 */ /* host controllers we manage */
DEFINE_IDR (usb_bus_idr); DEFINE_IDR (usb_bus_idr);
EXPORT_SYMBOL_GPL (usb_bus_idr); EXPORT_SYMBOL_GPL (usb_bus_idr);

View file

@ -578,6 +578,7 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{ {
switch (hsotg->dr_mode) { switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST: case USB_DR_MODE_HOST:
dwc2_force_mode(hsotg, true);
/* /*
* NOTE: This is required for some rockchip soc based * NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2. * platforms on their host-only dwc2.

View file

@ -211,4 +211,15 @@ config USB_DWC3_APPLE
mode on these machines. mode on these machines.
Say 'Y' or 'M' if you have such device. 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 endif

View file

@ -59,3 +59,4 @@ obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o
obj-$(CONFIG_USB_DWC3_GOOGLE) += dwc3-google.o

View file

@ -114,23 +114,23 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
int i; int i;
for (i = 0; i < dwc->num_usb3_ports; 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) if (enable && !dwc->dis_u3_susphy_quirk)
reg |= DWC3_GUSB3PIPECTL_SUSPHY; reg |= DWC3_GUSB3PIPECTL_SUSPHY;
else else
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; 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++) { 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) if (enable && !dwc->dis_u2_susphy_quirk)
reg |= DWC3_GUSB2PHYCFG_SUSPHY; reg |= DWC3_GUSB2PHYCFG_SUSPHY;
else else
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 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); 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; unsigned int hw_mode;
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc, DWC3_GCTL);
/* /*
* For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and * 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(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode); reg |= DWC3_GCTL_PRTCAPDIR(mode);
dwc3_writel(dwc->regs, DWC3_GCTL, reg); dwc3_writel(dwc, DWC3_GCTL, reg);
dwc->current_dr_role = mode; dwc->current_dr_role = mode;
trace_dwc3_set_prtcap(mode); trace_dwc3_set_prtcap(dwc, mode);
} }
EXPORT_SYMBOL_GPL(dwc3_set_prtcap); 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) || if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
DWC3_VER_IS_PRIOR(DWC31, 190A)) && DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) { desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET; 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 * Wait for internal clocks to synchronized. DWC_usb31 and
@ -228,9 +228,9 @@ static void __dwc3_set_mode(struct work_struct *work)
*/ */
msleep(100); msleep(100);
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET; reg &= ~DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg); dwc3_writel(dwc, DWC3_GCTL, reg);
} }
spin_lock_irqsave(&dwc->lock, flags); 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); phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
if (dwc->dis_split_quirk) { if (dwc->dis_split_quirk) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE; reg |= DWC3_GUCTL3_SPLITDISABLE;
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); dwc3_writel(dwc, DWC3_GUCTL3, reg);
} }
} }
break; break;
@ -306,11 +306,11 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
u32 reg; u32 reg;
dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE, dwc3_writel(dwc, DWC3_GDBGFIFOSPACE,
DWC3_GDBGFIFOSPACE_NUM(dep->number) | DWC3_GDBGFIFOSPACE_NUM(dep->number) |
DWC3_GDBGFIFOSPACE_TYPE(type)); DWC3_GDBGFIFOSPACE_TYPE(type));
reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE); reg = dwc3_readl(dwc, DWC3_GDBGFIFOSPACE);
return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg); 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) if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0; return 0;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST; reg |= DWC3_DCTL_CSFTRST;
reg &= ~DWC3_DCTL_RUN_STOP; reg &= ~DWC3_DCTL_RUN_STOP;
dwc3_gadget_dctl_write_safe(dwc, reg); dwc3_gadget_dctl_write_safe(dwc, reg);
@ -347,7 +347,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
retries = 10; retries = 10;
do { do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST)) if (!(reg & DWC3_DCTL_CSFTRST))
goto done; goto done;
@ -387,12 +387,12 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
if (dwc->fladj == 0) if (dwc->fladj == 0)
return; return;
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); reg = dwc3_readl(dwc, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK; dft = reg & DWC3_GFLADJ_30MHZ_MASK;
if (dft != dwc->fladj) { if (dft != dwc->fladj) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK; reg &= ~DWC3_GFLADJ_30MHZ_MASK;
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; 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; return;
} }
reg = dwc3_readl(dwc->regs, DWC3_GUCTL); reg = dwc3_readl(dwc, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK; reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); 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)) if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return; return;
@ -455,7 +455,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
*/ */
decr = 480000000 / rate; decr = 480000000 / rate;
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); reg = dwc3_readl(dwc, DWC3_GFLADJ);
reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
& ~DWC3_GFLADJ_240MHZDECR & ~DWC3_GFLADJ_240MHZDECR
& ~DWC3_GFLADJ_240MHZDECR_PLS1; & ~DWC3_GFLADJ_240MHZDECR_PLS1;
@ -466,7 +466,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
if (dwc->gfladj_refclk_lpm_sel) if (dwc->gfladj_refclk_lpm_sel)
reg |= DWC3_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 = dwc->ev_buf;
evt->lpos = 0; evt->lpos = 0;
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), dwc3_writel(dwc, DWC3_GEVNTADRLO(0),
lower_32_bits(evt->dma)); lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), dwc3_writel(dwc, DWC3_GEVNTADRHI(0),
upper_32_bits(evt->dma)); upper_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length)); DWC3_GEVNTSIZ_SIZE(evt->length));
/* Clear any stale event */ /* Clear any stale event */
reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg); dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
return 0; 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 * Exynos platforms may not be able to access event buffer if the
* controller failed to halt on dwc3_core_exit(). * 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)) if (!(reg & DWC3_DSTS_DEVCTRLHLT))
return; return;
@ -601,14 +601,14 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
evt->lpos = 0; evt->lpos = 0;
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0); dwc3_writel(dwc, DWC3_GEVNTADRLO(0), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0); dwc3_writel(dwc, DWC3_GEVNTADRHI(0), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
| DWC3_GEVNTSIZ_SIZE(0)); | DWC3_GEVNTSIZ_SIZE(0));
/* Clear any stale event */ /* Clear any stale event */
reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg); dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
} }
static void dwc3_core_num_eps(struct dwc3 *dwc) 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; struct dwc3_hwparams *parms = &dwc->hwparams;
parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); parms->hwparams0 = dwc3_readl(dwc, DWC3_GHWPARAMS0);
parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); parms->hwparams1 = dwc3_readl(dwc, DWC3_GHWPARAMS1);
parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); parms->hwparams2 = dwc3_readl(dwc, DWC3_GHWPARAMS2);
parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); parms->hwparams3 = dwc3_readl(dwc, DWC3_GHWPARAMS3);
parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); parms->hwparams4 = dwc3_readl(dwc, DWC3_GHWPARAMS4);
parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); parms->hwparams5 = dwc3_readl(dwc, DWC3_GHWPARAMS5);
parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); parms->hwparams6 = dwc3_readl(dwc, DWC3_GHWPARAMS6);
parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); parms->hwparams7 = dwc3_readl(dwc, DWC3_GHWPARAMS7);
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); parms->hwparams8 = dwc3_readl(dwc, DWC3_GHWPARAMS8);
if (DWC3_IP_IS(DWC32)) 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) 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) { if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); reg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
reg &= ~DWC3_GSBUSCFG0_REQINFO(~0); reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo); 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; 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 * 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) if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg); dwc3_writel(dwc, DWC3_GUSB3PIPECTL(index), reg);
return 0; return 0;
} }
@ -715,7 +715,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
{ {
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
/* Select the HS PHY interface */ /* Select the HS PHY interface */
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { 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 && } else if (dwc->hsphy_interface &&
!strncmp(dwc->hsphy_interface, "ulpi", 4)) { !strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
} else { } else {
/* Relying on default value. */ /* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) 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) if (dwc->ulpi_ext_vbus_drv)
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV; reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
return 0; return 0;
} }
@ -991,7 +991,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
{ {
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); reg = dwc3_readl(dwc, DWC3_GSNPSID);
dwc->ip = DWC3_GSNPS_ID(reg); dwc->ip = DWC3_GSNPS_ID(reg);
if (dwc->ip == DWC4_IP) if (dwc->ip == DWC4_IP)
dwc->ip = DWC32_IP; dwc->ip = DWC32_IP;
@ -1000,8 +1000,8 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
if (DWC3_IP_IS(DWC3)) { if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg; dwc->revision = reg;
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) { } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); dwc->revision = dwc3_readl(dwc, DWC3_VER_NUMBER);
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); dwc->version_type = dwc3_readl(dwc, DWC3_VER_TYPE);
} else { } else {
return false; return false;
} }
@ -1015,7 +1015,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
unsigned int hw_mode; unsigned int hw_mode;
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK; reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); 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)) if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN; 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); 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 ret;
int i; int i;
cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); cfg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
/* /*
* Handle property "snps,incr-burst-type-adjustment". * Handle property "snps,incr-burst-type-adjustment".
@ -1188,7 +1188,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
break; 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) 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. * (3x or more) to be within the requirement.
*/ */
scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000); 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) || 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) > DWC3_GCTL_PWRDNSCALE(scale*3)) {
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK); reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
reg |= DWC3_GCTL_PWRDNSCALE(scale); 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; tx_maxburst = dwc->tx_max_burst_prd;
if (rx_thr_num && rx_maxburst) { 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_RXTHRNUMPKTSEL_PRD;
reg &= ~DWC31_RXTHRNUMPKT_PRD(~0); 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(~0);
reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst); 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) { 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_TXTHRNUMPKTSEL_PRD;
reg &= ~DWC31_TXTHRNUMPKT_PRD(~0); 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(~0);
reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst); 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 (DWC3_IP_IS(DWC3)) {
if (rx_thr_num && rx_maxburst) { 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_PKTCNTSEL;
reg &= ~DWC3_GRXTHRCFG_RXPKTCNT(~0); 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(~0);
reg |= DWC3_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst); 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) { 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_PKTCNTSEL;
reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0); 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(~0);
reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst); reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
} }
} else { } else {
if (rx_thr_num && rx_maxburst) { 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_PKTCNTSEL;
reg &= ~DWC31_GRXTHRCFG_RXPKTCNT(~0); 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(~0);
reg |= DWC31_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst); 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) { 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_PKTCNTSEL;
reg &= ~DWC31_GTXTHRCFG_TXPKTCNT(~0); 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(~0);
reg |= DWC31_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst); 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 * Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found. * 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); ret = dwc3_phy_setup(dwc);
if (ret) if (ret)
@ -1410,9 +1410,9 @@ int dwc3_core_init(struct dwc3 *dwc)
* DWC_usb31 controller. * DWC_usb31 controller.
*/ */
if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) { 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; 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. * setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
*/ */
if (DWC3_VER_IS(DWC3, 320A)) { if (DWC3_VER_IS(DWC3, 320A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); reg = dwc3_readl(dwc, DWC3_GUCTL2);
reg &= ~DWC3_GUCTL2_LC_TIMER; 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. * legacy ULPI PHYs.
*/ */
if (dwc->resume_hs_terminations) { 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; 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)) { 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 * 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; 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); dwc3_config_threshold(dwc);
@ -1492,9 +1492,9 @@ int dwc3_core_init(struct dwc3 *dwc)
int i; int i;
for (i = 0; i < dwc->num_usb3_ports; 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; 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. * function is available only from version 1.70a.
*/ */
if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) { 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; reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); dwc3_writel(dwc, DWC3_GUCTL3, reg);
} }
return 0; return 0;
@ -2155,6 +2155,20 @@ static int dwc3_get_num_ports(struct dwc3 *dwc)
return 0; 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) static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
{ {
struct power_supply *usb_psy; struct power_supply *usb_psy;
@ -2169,6 +2183,7 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
if (!usb_psy) if (!usb_psy)
return ERR_PTR(-EPROBE_DEFER); return ERR_PTR(-EPROBE_DEFER);
INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
return usb_psy; return usb_psy;
} }
@ -2395,8 +2410,10 @@ void dwc3_core_remove(struct dwc3 *dwc)
dwc3_free_event_buffers(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); power_supply_put(dwc->usb_psy);
}
} }
EXPORT_SYMBOL_GPL(dwc3_core_remove); EXPORT_SYMBOL_GPL(dwc3_core_remove);
@ -2439,9 +2456,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
int ret; int ret;
if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) { 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_GUSB2PHYCFG_SUSPHY) ||
(dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) & (dwc3_readl(dwc, DWC3_GUSB3PIPECTL(0)) &
DWC3_GUSB3PIPECTL_SUSPHY); DWC3_GUSB3PIPECTL_SUSPHY);
/* /*
* TI AM62 platform requires SUSPHY to be * 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 || if (dwc->dis_u2_susphy_quirk ||
dwc->dis_enblslpm_quirk) { dwc->dis_enblslpm_quirk) {
for (i = 0; i < dwc->num_usb2_ports; i++) { 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 | reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
DWC3_GUSB2PHYCFG_SUSPHY; 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 */ /* 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 */ /* Restore GUSB2PHYCFG bits that were modified in suspend */
for (i = 0; i < dwc->num_usb2_ports; i++) { 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) if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_enblslpm_quirk) if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; 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++) 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 && if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
dwc->dis_split_quirk) { dwc->dis_split_quirk) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE; reg |= DWC3_GUCTL3_SPLITDISABLE;
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); dwc3_writel(dwc, DWC3_GUCTL3, reg);
} }
} }
EXPORT_SYMBOL_GPL(dwc3_pm_complete); EXPORT_SYMBOL_GPL(dwc3_pm_complete);

View file

@ -165,10 +165,10 @@
#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */ #define DWC3_DCFG1 0xc740 /* DWC_usb32 only */
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10)) #define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
#define DWC3_DEPCMDPAR2 0x00 #define DWC3_DEPCMDPAR2(n) (DWC3_DEP_BASE(n) + 0x00)
#define DWC3_DEPCMDPAR1 0x04 #define DWC3_DEPCMDPAR1(n) (DWC3_DEP_BASE(n) + 0x04)
#define DWC3_DEPCMDPAR0 0x08 #define DWC3_DEPCMDPAR0(n) (DWC3_DEP_BASE(n) + 0x08)
#define DWC3_DEPCMD 0x0c #define DWC3_DEPCMD(n) (DWC3_DEP_BASE(n) + 0x0c)
#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4)) #define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
@ -749,8 +749,6 @@ struct dwc3_ep {
struct list_head pending_list; struct list_head pending_list;
struct list_head started_list; struct list_head started_list;
void __iomem *regs;
struct dwc3_trb *trb_pool; struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma; dma_addr_t trb_pool_dma;
struct dwc3 *dwc; struct dwc3 *dwc;
@ -1060,6 +1058,8 @@ struct dwc3_glue_ops {
* @role_switch_default_mode: default operation mode of controller while * @role_switch_default_mode: default operation mode of controller while
* usb role is USB_ROLE_NONE. * usb role is USB_ROLE_NONE.
* @usb_psy: pointer to power supply interface. * @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 * @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY * @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to array of USB2 PHYs * @usb2_generic_phy: pointer to array of USB2 PHYs
@ -1246,6 +1246,8 @@ struct dwc3 {
enum usb_dr_mode role_switch_default_mode; enum usb_dr_mode role_switch_default_mode;
struct power_supply *usb_psy; struct power_supply *usb_psy;
struct work_struct vbus_draw_work;
unsigned int current_limit;
u32 fladj; u32 fladj;
u32 ref_clk_per; u32 ref_clk_per;

View file

@ -36,23 +36,19 @@
#define dump_ep_register_set(n) \ #define dump_ep_register_set(n) \
{ \ { \
.name = "DEPCMDPAR2("__stringify(n)")", \ .name = "DEPCMDPAR2("__stringify(n)")", \
.offset = DWC3_DEP_BASE(n) + \ .offset = DWC3_DEPCMDPAR2(n), \
DWC3_DEPCMDPAR2, \
}, \ }, \
{ \ { \
.name = "DEPCMDPAR1("__stringify(n)")", \ .name = "DEPCMDPAR1("__stringify(n)")", \
.offset = DWC3_DEP_BASE(n) + \ .offset = DWC3_DEPCMDPAR1(n), \
DWC3_DEPCMDPAR1, \
}, \ }, \
{ \ { \
.name = "DEPCMDPAR0("__stringify(n)")", \ .name = "DEPCMDPAR0("__stringify(n)")", \
.offset = DWC3_DEP_BASE(n) + \ .offset = DWC3_DEPCMDPAR0(n), \
DWC3_DEPCMDPAR0, \
}, \ }, \
{ \ { \
.name = "DEPCMD("__stringify(n)")", \ .name = "DEPCMD("__stringify(n)")", \
.offset = DWC3_DEP_BASE(n) + \ .offset = DWC3_DEPCMD(n), \
DWC3_DEPCMD, \
} }
@ -300,14 +296,14 @@ static void dwc3_host_lsp(struct seq_file *s)
reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel); reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel);
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); val = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val); seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val);
if (dbc_enabled && sel < 256) { if (dbc_enabled && sel < 256) {
reg |= DWC3_GDBGLSPMUX_ENDBC; reg |= DWC3_GDBGLSPMUX_ENDBC;
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); val = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val); 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++) { for (i = 0; i < 16; i++) {
reg = DWC3_GDBGLSPMUX_DEVSELECT(i); reg = DWC3_GDBGLSPMUX_DEVSELECT(i);
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); reg = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg); 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; return ret;
spin_lock_irqsave(&dwc->lock, flags); 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); current_mode = DWC3_GSTS_CURMOD(reg);
switch (current_mode) { switch (current_mode) {
@ -410,7 +406,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
return ret; return ret;
spin_lock_irqsave(&dwc->lock, flags); 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); spin_unlock_irqrestore(&dwc->lock, flags);
mode = DWC3_GCTL_PRTCAP(reg); mode = DWC3_GCTL_PRTCAP(reg);
@ -482,7 +478,7 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
return ret; return ret;
spin_lock_irqsave(&dwc->lock, flags); 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 &= DWC3_DCTL_TSTCTRL_MASK;
reg >>= 1; reg >>= 1;
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
@ -581,7 +577,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
return ret; return ret;
spin_lock_irqsave(&dwc->lock, flags); 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) { if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
seq_puts(s, "Not available\n"); seq_puts(s, "Not available\n");
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
@ -589,7 +585,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
return 0; return 0;
} }
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg); state = DWC3_DSTS_USBLNKST(reg);
speed = reg & DWC3_DSTS_CONNECTSPD; speed = reg & DWC3_DSTS_CONNECTSPD;
@ -643,14 +639,14 @@ static ssize_t dwc3_link_state_write(struct file *file,
return ret; return ret;
spin_lock_irqsave(&dwc->lock, flags); 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) { if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
pm_runtime_put_sync(dwc->dev); pm_runtime_put_sync(dwc->dev);
return -EINVAL; return -EINVAL;
} }
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD; speed = reg & DWC3_DSTS_CONNECTSPD;
if (speed < DWC3_DSTS_SUPERSPEED && 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); spin_lock_irqsave(&dwc->lock, flags);
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); 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); lower_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO0);
upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1); upper_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO1);
ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
seq_printf(s, "0x%016llx\n", ep_info); seq_printf(s, "0x%016llx\n", ep_info);

View file

@ -18,25 +18,25 @@
static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask) 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); 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) 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); 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) 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 | \ #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; struct dwc3 *dwc = _dwc;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
reg = dwc3_readl(dwc->regs, DWC3_OEVT); reg = dwc3_readl(dwc, DWC3_OEVT);
if (reg) { if (reg) {
/* ignore non OTG events, we can't disable them in OEVTEN */ /* ignore non OTG events, we can't disable them in OEVTEN */
if (!(reg & DWC3_OTG_ALL_EVENTS)) { if (!(reg & DWC3_OTG_ALL_EVENTS)) {
dwc3_writel(dwc->regs, DWC3_OEVT, reg); dwc3_writel(dwc, DWC3_OEVT, reg);
return IRQ_NONE; return IRQ_NONE;
} }
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST && if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
!(reg & DWC3_OEVT_DEVICEMODE)) !(reg & DWC3_OEVT_DEVICEMODE))
dwc->otg_restart_host = true; dwc->otg_restart_host = true;
dwc3_writel(dwc->regs, DWC3_OEVT, reg); dwc3_writel(dwc, DWC3_OEVT, reg);
ret = IRQ_WAKE_THREAD; 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 * 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. * 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; reg |= DWC3_OCFG_SFTRSTMASK;
dwc3_writel(dwc->regs, DWC3_OCFG, reg); dwc3_writel(dwc, DWC3_OCFG, reg);
/* Disable hibernation for simplicity */ /* Disable hibernation for simplicity */
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_GBLHIBERNATIONEN; reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg); dwc3_writel(dwc, DWC3_GCTL, reg);
/* /*
* Initialize OTG registers as per * Initialize OTG registers as per
* Figure 11-4 OTG Driver Overall Programming Flow * Figure 11-4 OTG Driver Overall Programming Flow
*/ */
/* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */ /* 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); reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
dwc3_writel(dwc->regs, DWC3_OCFG, reg); dwc3_writel(dwc, DWC3_OCFG, reg);
/* OEVT = FFFF */ /* OEVT = FFFF */
dwc3_otg_clear_events(dwc); dwc3_otg_clear_events(dwc);
/* OEVTEN = 0 */ /* OEVTEN = 0 */
@ -127,11 +127,11 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
* OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0, * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
* OCTL.HNPReq = 0 * OCTL.HNPReq = 0
*/ */
reg = dwc3_readl(dwc->regs, DWC3_OCTL); reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PERIMODE; reg |= DWC3_OCTL_PERIMODE;
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN | reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
DWC3_OCTL_HNPREQ); 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) static int dwc3_otg_get_irq(struct dwc3 *dwc)
@ -175,9 +175,9 @@ void dwc3_otg_init(struct dwc3 *dwc)
/* GCTL.PrtCapDir=2'b11 */ /* GCTL.PrtCapDir=2'b11 */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
/* GUSB2PHYCFG0.SusPHY=0 */ /* GUSB2PHYCFG0.SusPHY=0 */
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
/* Initialize OTG registers */ /* Initialize OTG registers */
dwc3_otgregs_init(dwc); dwc3_otgregs_init(dwc);
@ -203,17 +203,17 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
* OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0, * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 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 | reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
dwc3_writel(dwc->regs, DWC3_OCTL, reg); dwc3_writel(dwc, DWC3_OCTL, reg);
/* /*
* OCFG.DisPrtPwrCutoff = 0/1 * OCFG.DisPrtPwrCutoff = 0/1
*/ */
reg = dwc3_readl(dwc->regs, DWC3_OCFG); reg = dwc3_readl(dwc, DWC3_OCFG);
reg &= ~DWC3_OCFG_DISPWRCUTTOFF; 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 * 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 */ /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
if (!dwc->dis_u2_susphy_quirk) { 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; 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 */ /* 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; 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 */ /* 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 */ /* 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); 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 */ /* 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 * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
* but we keep them 0 for simple dual-role operation. * 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 */ /* OCFG.OTGSftRstMsk = 0/1 */
reg |= DWC3_OCFG_SFTRSTMASK; reg |= DWC3_OCFG_SFTRSTMASK;
dwc3_writel(dwc->regs, DWC3_OCFG, reg); dwc3_writel(dwc, DWC3_OCFG, reg);
/* /*
* OCTL.PeriMode = 1 * OCTL.PeriMode = 1
* OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0 * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 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_PERIMODE;
reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ | reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
dwc3_writel(dwc->regs, DWC3_OCTL, reg); dwc3_writel(dwc, DWC3_OCTL, reg);
/* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */ /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN); dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN);
/* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */ /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
if (!dwc->dis_u2_susphy_quirk) { 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; reg |= DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
} }
/* GCTL.GblHibernationEn = 0. Already 0. */ /* GCTL.GblHibernationEn = 0. Already 0. */
} }
@ -319,10 +319,10 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc)
DWC3_OEVTEN_BDEVBHOSTENDEN); DWC3_OEVTEN_BDEVBHOSTENDEN);
/* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */ /* 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_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
reg |= DWC3_OCTL_PERIMODE; 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) 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; return;
if (!ignore_idstatus) { if (!ignore_idstatus) {
reg = dwc3_readl(dwc->regs, DWC3_OSTS); reg = dwc3_readl(dwc, DWC3_OSTS);
id = !!(reg & DWC3_OSTS_CONIDSTS); id = !!(reg & DWC3_OSTS_CONIDSTS);
dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE : 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_otgregs_init(dwc);
dwc3_otg_host_init(dwc); dwc3_otg_host_init(dwc);
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_pre_set_role(dwc, USB_ROLE_HOST);
ret = dwc3_host_init(dwc); ret = dwc3_host_init(dwc);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to initialize host\n"); 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); otg_set_vbus(dwc->usb2_phy->otg, false);
if (dwc->usb2_generic_phy[0]) if (dwc->usb2_generic_phy[0])
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); 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); ret = dwc3_gadget_init(dwc);
if (ret) if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n"); 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) unsigned long event, void *ptr)
{ {
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); 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_pre_set_role(dwc, role);
DWC3_GCTL_PRTCAP_HOST : dwc3_set_mode(dwc, mode);
DWC3_GCTL_PRTCAP_DEVICE);
return NOTIFY_DONE; return NOTIFY_DONE;
} }

View 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);
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, &reg_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);
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);
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);
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");

View file

@ -51,7 +51,7 @@
struct dwc3_imx8mp { struct dwc3_imx8mp {
struct device *dev; struct device *dev;
struct platform_device *dwc3; struct platform_device *dwc3_pdev;
void __iomem *hsio_blk_base; void __iomem *hsio_blk_base;
void __iomem *glue_base; void __iomem *glue_base;
struct clk *hsio_clk; 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, static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
pm_message_t msg) 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; u32 val;
if (!dwc3) 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) static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
{ {
struct dwc3_imx8mp *dwc3_imx = _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) if (!dwc3_imx->pm_suspended)
return IRQ_HANDLED; return IRQ_HANDLED;
@ -158,11 +158,31 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
return IRQ_HANDLED; 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) static int dwc3_imx8mp_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node; struct device_node *node = dev->of_node;
struct dwc3_imx8mp *dwc3_imx; struct dwc3_imx8mp *dwc3_imx;
struct dwc3 *dwc3;
struct resource *res; struct resource *res;
int err, irq; int err, irq;
@ -233,13 +253,24 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
goto remove_swnode; goto remove_swnode;
} }
dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np); dwc3_imx->dwc3_pdev = of_find_device_by_node(dwc3_np);
if (!dwc3_imx->dwc3) { if (!dwc3_imx->dwc3_pdev) {
dev_err(dev, "failed to get dwc3 platform device\n"); dev_err(dev, "failed to get dwc3 platform device\n");
err = -ENODEV; err = -ENODEV;
goto depopulate; 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, err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
IRQF_ONESHOT, dev_name(dev), dwc3_imx); IRQF_ONESHOT, dev_name(dev), dwc3_imx);
if (err) { if (err) {
@ -253,7 +284,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
return 0; return 0;
put_dwc3: put_dwc3:
put_device(&dwc3_imx->dwc3->dev); put_device(&dwc3_imx->dwc3_pdev->dev);
depopulate: depopulate:
of_platform_depopulate(dev); of_platform_depopulate(dev);
remove_swnode: 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 dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
put_device(&dwc3_imx->dwc3->dev); put_device(&dwc3_imx->dwc3_pdev->dev);
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
of_platform_depopulate(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) 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; int ret = 0;
if (!dwc3_imx->pm_suspended) if (!dwc3_imx->pm_suspended)

View file

@ -132,21 +132,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err; 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"); crst = devm_reset_control_get_exclusive(dev, "usb_crst");
if (IS_ERR(crst)) { if (IS_ERR(crst)) {
ret = PTR_ERR(crst); ret = PTR_ERR(crst);
@ -171,22 +156,31 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err; goto err;
} }
ret = reset_control_assert(crst); /*
if (ret < 0) { * Asserting the core resets is not required unless a USB3 PHY is used.
dev_err(dev, "Failed to assert core reset\n"); * They may also break the configuration if USB3 is actually in use but
goto err; * 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");
goto err;
}
ret = reset_control_assert(hibrst); ret = reset_control_assert(hibrst);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to assert hibernation reset\n"); dev_err(dev, "Failed to assert hibernation reset\n");
goto err; goto err;
} }
ret = reset_control_assert(apbrst); ret = reset_control_assert(apbrst);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to assert APB reset\n"); dev_err(dev, "Failed to assert APB reset\n");
goto err; goto err;
}
} }
ret = phy_init(priv_data->usb3_phy); ret = phy_init(priv_data->usb3_phy);
@ -201,11 +195,15 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err; goto err;
} }
/* Set PIPE Power Present signal in FPD Power Present Register*/ if (priv_data->usb3_phy) {
writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); /* 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 */ /* Set the PIPE Clock Select bit in FPD PIPE Clock register */
writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); 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); ret = reset_control_deassert(crst);
if (ret < 0) { if (ret < 0) {
@ -225,7 +223,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err; goto err;
} }
skip_usb3_phy:
/* ulpi reset via gpio-modepin or gpio-framework driver */ /* ulpi reset via gpio-modepin or gpio-framework driver */
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio)) { if (IS_ERR(reset_gpio)) {

View file

@ -361,7 +361,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU1ENA) if (reg & DWC3_DCTL_INITU1ENA)
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
if (reg & DWC3_DCTL_INITU2ENA) 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) if (set && dwc->dis_u1_entry_quirk)
return -EINVAL; return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
if (set) if (set)
reg |= DWC3_DCTL_INITU1ENA; reg |= DWC3_DCTL_INITU1ENA;
else else
reg &= ~DWC3_DCTL_INITU1ENA; reg &= ~DWC3_DCTL_INITU1ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc3_writel(dwc, DWC3_DCTL, reg);
return 0; 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) if (set && dwc->dis_u2_entry_quirk)
return -EINVAL; return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
if (set) if (set)
reg |= DWC3_DCTL_INITU2ENA; reg |= DWC3_DCTL_INITU2ENA;
else else
reg &= ~DWC3_DCTL_INITU2ENA; reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc3_writel(dwc, DWC3_DCTL, reg);
return 0; return 0;
} }
@ -612,10 +612,10 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL; return -EINVAL;
} }
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK); reg &= ~(DWC3_DCFG_DEVADDR_MASK);
reg |= DWC3_DCFG_DEVADDR(addr); reg |= DWC3_DCFG_DEVADDR(addr);
dwc3_writel(dwc->regs, DWC3_DCFG, reg); dwc3_writel(dwc, DWC3_DCFG, reg);
if (addr) if (addr)
usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); 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 * Enable transition to U1/U2 state when
* nothing is pending from application. * nothing is pending from application.
*/ */
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
if (!dwc->dis_u1_entry_quirk) if (!dwc->dis_u1_entry_quirk)
reg |= DWC3_DCTL_ACCEPTU1ENA; reg |= DWC3_DCTL_ACCEPTU1ENA;
if (!dwc->dis_u2_entry_quirk) if (!dwc->dis_u2_entry_quirk)
reg |= DWC3_DCTL_ACCEPTU2ENA; reg |= DWC3_DCTL_ACCEPTU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc3_writel(dwc, DWC3_DCTL, reg);
} }
break; 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->u2sel = le16_to_cpu(timing.u2sel);
dwc->u2pel = le16_to_cpu(timing.u2pel); 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) if (reg & DWC3_DCTL_INITU2ENA)
param = dwc->u2pel; param = dwc->u2pel;
if (reg & DWC3_DCTL_INITU1ENA) 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) if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out; goto out;
trace_dwc3_ctrl_req(ctrl); trace_dwc3_ctrl_req(dwc, ctrl);
len = le16_to_cpu(ctrl->wLength); len = le16_to_cpu(ctrl->wLength);
if (!len) { if (!len) {

View file

@ -42,7 +42,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{ {
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK; reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) { switch (mode) {
@ -73,7 +73,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
{ {
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
return DWC3_DSTS_USBLNKST(reg); 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)) { if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) { while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD) if (reg & DWC3_DSTS_DCNRD)
udelay(5); udelay(5);
else else
@ -108,15 +108,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
/* set no action before sending new link state change */ /* 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 */ /* set requested state */
reg |= DWC3_DCTL_ULSTCHNGREQ(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, * 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 */ /* wait for a change in DSTS */
retries = 10000; retries = 10000;
while (--retries) { while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
if (DWC3_DSTS_USBLNKST(reg) == state) if (DWC3_DSTS_USBLNKST(reg) == state)
return 0; return 0;
@ -260,11 +260,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
int ret = 0; int ret = 0;
u32 reg; u32 reg;
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc, DWC3_DGCMDPAR, param);
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); dwc3_writel(dwc, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
do { do {
reg = dwc3_readl(dwc->regs, DWC3_DGCMD); reg = dwc3_readl(dwc, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) { if (!(reg & DWC3_DGCMD_CMDACT)) {
status = DWC3_DGCMD_STATUS(reg); status = DWC3_DGCMD_STATUS(reg);
if (status) if (status)
@ -278,7 +278,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
status = -ETIMEDOUT; status = -ETIMEDOUT;
} }
trace_dwc3_gadget_generic_cmd(cmd, param, status); trace_dwc3_gadget_generic_cmd(dwc, cmd, param, status);
return ret; return ret;
} }
@ -320,6 +320,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
int cmd_status = 0; int cmd_status = 0;
int ret = -EINVAL; int ret = -EINVAL;
u8 epnum = dep->number;
/* /*
* When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or * 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 || if (dwc->gadget->speed <= USB_SPEED_HIGH ||
DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) { 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)) { if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~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) 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. * improve performance.
*/ */
if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) { if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); dwc3_writel(dwc, DWC3_DEPCMDPAR0(epnum), params->param0);
dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); dwc3_writel(dwc, DWC3_DEPCMDPAR1(epnum), params->param1);
dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); 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 else
cmd |= DWC3_DEPCMD_CMDACT; cmd |= DWC3_DEPCMD_CMDACT;
dwc3_writel(dep->regs, DWC3_DEPCMD, cmd); dwc3_writel(dwc, DWC3_DEPCMD(epnum), cmd);
if (!(cmd & DWC3_DEPCMD_CMDACT) || if (!(cmd & DWC3_DEPCMD_CMDACT) ||
(DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER && (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 { do {
reg = dwc3_readl(dep->regs, DWC3_DEPCMD); reg = dwc3_readl(dwc, DWC3_DEPCMD(epnum));
if (!(reg & DWC3_DEPCMD_CMDACT)) { if (!(reg & DWC3_DEPCMD_CMDACT)) {
cmd_status = DWC3_DEPCMD_STATUS(reg); cmd_status = DWC3_DEPCMD_STATUS(reg);
@ -447,9 +448,9 @@ skip_status:
mdelay(1); mdelay(1);
if (saved_config) { if (saved_config) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= saved_config; reg |= saved_config;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
} }
return ret; return ret;
@ -726,7 +727,7 @@ static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc)
u32 reg; u32 reg;
/* Check if TXFIFOs start at non-zero addr */ /* 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); fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg);
ram_depth -= (fifo_0_start >> 16); ram_depth -= (fifo_0_start >> 16);
@ -754,7 +755,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
/* Read ep0IN related TXFIFO size */ /* Read ep0IN related TXFIFO size */
dep = dwc->eps[1]; dep = dwc->eps[1];
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
if (DWC3_IP_IS(DWC3)) if (DWC3_IP_IS(DWC3))
fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size); fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
else else
@ -769,10 +770,10 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
/* Don't change TXFRAMNUM on usb31 version */ /* Don't change TXFRAMNUM on usb31 version */
size = DWC3_IP_IS(DWC3) ? 0 : size = DWC3_IP_IS(DWC3) ? 0 :
dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) & dwc3_readl(dwc, DWC3_GTXFIFOSIZ(num >> 1)) &
DWC31_GTXFIFOSIZ_TXFRAMNUM; 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; dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
} }
dwc->num_ep_resized = 0; dwc->num_ep_resized = 0;
@ -875,7 +876,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
fifo_size++; fifo_size++;
/* Check if TXFIFOs start at non-zero addr */ /* 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_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16)); 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; 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; dep->flags |= DWC3_EP_TXFIFO_RESIZED;
dwc->num_ep_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->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED; dep->flags |= DWC3_EP_ENABLED;
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); reg = dwc3_readl(dwc, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number); 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_dequeue = 0;
dep->trb_enqueue = 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) if (dep->flags & DWC3_EP_STALL)
__dwc3_gadget_ep_set_halt(dep, 0, false); __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); 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); dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
@ -1742,7 +1743,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
{ {
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
return DWC3_DSTS_SOFFN(reg); 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)) if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return; return;
reg = dwc3_readl(dwc->regs, DWC3_DEVTEN); reg = dwc3_readl(dwc, DWC3_DEVTEN);
if (set) if (set)
reg |= DWC3_DEVTEN_ULSTCNGEN; reg |= DWC3_DEVTEN_ULSTCNGEN;
else else
reg &= ~DWC3_DEVTEN_ULSTCNGEN; 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) 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. * 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); link_state = DWC3_DSTS_USBLNKST(reg);
@ -2407,9 +2408,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
/* Recent versions do this automatically */ /* Recent versions do this automatically */
if (DWC3_VER_IS_PRIOR(DWC3, 194A)) { if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */ /* write zeroes to Link Change Request */
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; 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) if (ssp_rate == USB_SSP_GEN_UNKNOWN)
ssp_rate = dwc->max_ssp_rate; 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_SPEED_MASK;
reg &= ~DWC3_DCFG_NUMLANES(~0); 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) dwc->max_ssp_rate != USB_SSP_GEN_2x1)
reg |= DWC3_DCFG_NUMLANES(1); 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) static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
@ -2560,7 +2561,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
return; return;
} }
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK); reg &= ~(DWC3_DCFG_SPEED_MASK);
/* /*
@ -2611,7 +2612,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
speed < USB_SPEED_SUPER_PLUS) speed < USB_SPEED_SUPER_PLUS)
reg &= ~DWC3_DCFG_NUMLANES(~0); 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) 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 * mentioned in the dwc3 programming guide. It has been tested on an
* Exynos platforms. * Exynos platforms.
*/ */
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~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) 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 (is_on) {
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) { if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK; reg &= ~DWC3_DCTL_TRGTULST_MASK;
@ -2674,14 +2675,14 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
do { do {
usleep_range(1000, 2000); usleep_range(1000, 2000);
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
reg &= DWC3_DSTS_DEVCTRLHLT; reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg)); } while (--timeout && !(!is_on ^ !reg));
if (saved_config) { if (saved_config) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= saved_config; reg |= saved_config;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
} }
if (!timeout) if (!timeout)
@ -2857,13 +2858,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
reg |= DWC3_DEVTEN_U3L2L1SUSPEN; 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) static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
{ {
/* mask all interrupts */ /* 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); 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); nump = min_t(u32, nump, 16);
/* update NumP */ /* update NumP */
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_NUMP_MASK; reg &= ~DWC3_DCFG_NUMP_MASK;
reg |= nump << DWC3_DCFG_NUMP_SHIFT; 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) 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. * the core supports IMOD, disable it.
*/ */
if (dwc->imod_interval) { if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
} else if (dwc3_has_imod(dwc)) { } 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 * 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. * 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)) if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
else else
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc); 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 * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
* the stream performance. * the stream performance.
*/ */
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg |= DWC3_DCFG_IGNSTRMPP; 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 */ /* Enable MST by default if the device is capable of MST */
if (DWC3_MST_CAPABLE(&dwc->hwparams)) { 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; reg &= ~DWC3_DCFG1_DIS_MST_ENH;
dwc3_writel(dwc->regs, DWC3_DCFG1, reg); dwc3_writel(dwc, DWC3_DCFG1, reg);
} }
/* Start with SuperSpeed Default */ /* 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) static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
{ {
struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3 *dwc = gadget_to_dwc(g);
union power_supply_propval val = {0};
int ret;
if (dwc->usb2_phy) if (dwc->usb2_phy)
return usb_phy_set_power(dwc->usb2_phy, mA); 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) if (!dwc->usb_psy)
return -EOPNOTSUPP; return -EOPNOTSUPP;
val.intval = 1000 * mA; dwc->current_limit = mA;
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); 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 is represented in bits, we need it in bytes */
mdwidth /= 8; 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)) if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size); size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else else
@ -3288,7 +3287,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
mdwidth /= 8; mdwidth /= 8;
/* All OUT endpoints share a single RxFIFO space */ /* 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)) if (DWC3_IP_IS(DWC3))
size = DWC3_GRXFIFOSIZ_RXFDEP(size); size = DWC3_GRXFIFOSIZ_RXFDEP(size);
else else
@ -3381,7 +3380,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
dep->dwc = dwc; dep->dwc = dwc;
dep->number = epnum; dep->number = epnum;
dep->direction = direction; dep->direction = direction;
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep; dwc->eps[epnum] = dep;
dep->combo_num = 0; dep->combo_num = 0;
dep->start_cmd_status = 0; dep->start_cmd_status = 0;
@ -3742,9 +3740,9 @@ out:
return no_started_trb; return no_started_trb;
} }
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
reg |= dwc->u1u2; reg |= dwc->u1u2;
dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc3_writel(dwc, DWC3_DCTL, reg);
dwc->u1u2 = 0; 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); 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_INITU1ENA;
reg &= ~DWC3_DCTL_INITU2ENA; reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_gadget_dctl_write_safe(dwc, reg); 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); dwc3_stop_active_transfers(dwc);
dwc->connected = true; dwc->connected = true;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK; reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg); dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false; dwc->test_mode = false;
@ -4172,9 +4170,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_clear_stall_all_ep(dwc); dwc3_clear_stall_all_ep(dwc);
/* Reset device address to zero */ /* Reset device address to zero */
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK); 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) 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) if (!dwc->softconnect)
return; return;
reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg = dwc3_readl(dwc, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD; speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed; dwc->speed = speed;
@ -4263,11 +4261,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
!dwc->usb2_gadget_lpm_disable && !dwc->usb2_gadget_lpm_disable &&
(speed != DWC3_DSTS_SUPERSPEED) && (speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) { (speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg |= DWC3_DCFG_LPM_CAP; 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_MASK | DWC3_DCTL_L1_HIBER_EN);
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold | 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); dwc3_gadget_dctl_write_safe(dwc, reg);
} else { } else {
if (dwc->usb2_gadget_lpm_disable) { if (dwc->usb2_gadget_lpm_disable) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_LPM_CAP; 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; reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg); 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) { switch (dwc->link_state) {
case DWC3_LINK_STATE_U1: case DWC3_LINK_STATE_U1:
case DWC3_LINK_STATE_U2: case DWC3_LINK_STATE_U2:
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc, DWC3_DCTL);
u1u2 = reg & (DWC3_DCTL_INITU2ENA u1u2 = reg & (DWC3_DCTL_INITU2ENA
| DWC3_DCTL_ACCEPTU2ENA | DWC3_DCTL_ACCEPTU2ENA
| DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU1ENA
@ -4558,7 +4556,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
/* Unmask interrupt */ /* Unmask interrupt */
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length)); DWC3_GEVNTSIZ_SIZE(evt->length));
evt->flags &= ~DWC3_EVENT_PENDING; evt->flags &= ~DWC3_EVENT_PENDING;
@ -4569,8 +4567,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
wmb(); wmb();
if (dwc->imod_interval) { if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
} }
return ret; return ret;
@ -4619,7 +4617,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (evt->flags & DWC3_EVENT_PENDING) if (evt->flags & DWC3_EVENT_PENDING)
return IRQ_HANDLED; return IRQ_HANDLED;
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); count = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK; count &= DWC3_GEVNTCOUNT_MASK;
if (!count) if (!count)
return IRQ_NONE; return IRQ_NONE;
@ -4634,7 +4632,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
evt->flags |= DWC3_EVENT_PENDING; evt->flags |= DWC3_EVENT_PENDING;
/* Mask interrupt */ /* Mask interrupt */
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length)); DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
amount = min(count, evt->length - evt->lpos); 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) if (amount < count)
memcpy(evt->cache, evt->buf, count - amount); 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; return IRQ_WAKE_THREAD;
} }

View file

@ -132,7 +132,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{ {
u32 res_id; 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); 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) static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
{ {
value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
dwc3_writel(dwc->regs, DWC3_DCTL, value); dwc3_writel(dwc, DWC3_DCTL, value);
} }
#endif /* __DRIVERS_USB_DWC3_GADGET_H */ #endif /* __DRIVERS_USB_DWC3_GADGET_H */

View file

@ -16,9 +16,10 @@
#include "debug.h" #include "debug.h"
#include "core.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; u32 value;
void __iomem *base = dwc->regs;
/* /*
* We requested the mem region starting from the Globals address * 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 * documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation * 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; 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 * We requested the mem region starting from the Globals address
* space, see dwc3_probe in core.c. * 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 * documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation * 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 */ #endif /* __DRIVERS_USB_DWC3_IO_H */

View file

@ -20,63 +20,70 @@
#include "debug.h" #include "debug.h"
DECLARE_EVENT_CLASS(dwc3_log_set_prtcap, DECLARE_EVENT_CLASS(dwc3_log_set_prtcap,
TP_PROTO(u32 mode), TP_PROTO(struct dwc3 *dwc, u32 mode),
TP_ARGS(mode), TP_ARGS(dwc, mode),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__field(u32, mode) __field(u32, mode)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dwc->xhci_resources[0].start;
__entry->mode = mode; __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, DEFINE_EVENT(dwc3_log_set_prtcap, dwc3_set_prtcap,
TP_PROTO(u32 mode), TP_PROTO(struct dwc3 *dwc, u32 mode),
TP_ARGS(mode) TP_ARGS(dwc, mode)
); );
DECLARE_EVENT_CLASS(dwc3_log_io, DECLARE_EVENT_CLASS(dwc3_log_io,
TP_PROTO(void *base, u32 offset, u32 value), TP_PROTO(struct dwc3 *dwc, void *base, u32 offset, u32 value),
TP_ARGS(base, offset, value), TP_ARGS(dwc, base, offset, value),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__field(void *, base) __field(void *, base)
__field(u32, offset) __field(u32, offset)
__field(u32, value) __field(u32, value)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dwc->xhci_resources[0].start;
__entry->base = base; __entry->base = base;
__entry->offset = offset; __entry->offset = offset;
__entry->value = value; __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->base + __entry->offset,
__entry->offset, __entry->offset,
__entry->value) __entry->value)
); );
DEFINE_EVENT(dwc3_log_io, dwc3_readl, DEFINE_EVENT(dwc3_log_io, dwc3_readl,
TP_PROTO(void __iomem *base, u32 offset, u32 value), TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
TP_ARGS(base, offset, value) TP_ARGS(dwc, base, offset, value)
); );
DEFINE_EVENT(dwc3_log_io, dwc3_writel, DEFINE_EVENT(dwc3_log_io, dwc3_writel,
TP_PROTO(void __iomem *base, u32 offset, u32 value), TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
TP_ARGS(base, offset, value) TP_ARGS(dwc, base, offset, value)
); );
DECLARE_EVENT_CLASS(dwc3_log_event, DECLARE_EVENT_CLASS(dwc3_log_event,
TP_PROTO(u32 event, struct dwc3 *dwc), TP_PROTO(u32 event, struct dwc3 *dwc),
TP_ARGS(event, dwc), TP_ARGS(event, dwc),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__field(u32, event) __field(u32, event)
__field(u32, ep0state) __field(u32, ep0state)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dwc->xhci_resources[0].start;
__entry->event = event; __entry->event = event;
__entry->ep0state = dwc->ep0state; __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, dwc3_decode_event(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
__entry->event, __entry->ep0state)) __entry->event, __entry->ep0state))
); );
@ -87,9 +94,10 @@ DEFINE_EVENT(dwc3_log_event, dwc3_event,
); );
DECLARE_EVENT_CLASS(dwc3_log_ctrl, DECLARE_EVENT_CLASS(dwc3_log_ctrl,
TP_PROTO(struct usb_ctrlrequest *ctrl), TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl), TP_ARGS(dwc, ctrl),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__field(__u8, bRequestType) __field(__u8, bRequestType)
__field(__u8, bRequest) __field(__u8, bRequest)
__field(__u16, wValue) __field(__u16, wValue)
@ -97,13 +105,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
__field(__u16, wLength) __field(__u16, wLength)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dwc->xhci_resources[0].start;
__entry->bRequestType = ctrl->bRequestType; __entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest; __entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue); __entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex); __entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength); __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->bRequestType,
__entry->bRequest, __entry->wValue, __entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength) __entry->wIndex, __entry->wLength)
@ -111,14 +121,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
); );
DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req, DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req,
TP_PROTO(struct usb_ctrlrequest *ctrl), TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl) TP_ARGS(dwc, ctrl)
); );
DECLARE_EVENT_CLASS(dwc3_log_request, DECLARE_EVENT_CLASS(dwc3_log_request,
TP_PROTO(struct dwc3_request *req), TP_PROTO(struct dwc3_request *req),
TP_ARGS(req), TP_ARGS(req),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__string(name, req->dep->name) __string(name, req->dep->name)
__field(struct dwc3_request *, req) __field(struct dwc3_request *, req)
__field(unsigned int, actual) __field(unsigned int, actual)
@ -129,7 +140,7 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__field(int, no_interrupt) __field(int, no_interrupt)
), ),
TP_fast_assign( TP_fast_assign(
__assign_str(name); __entry->base_address = req->dep->dwc->xhci_resources[0].start;
__entry->req = req; __entry->req = req;
__entry->actual = req->request.actual; __entry->actual = req->request.actual;
__entry->length = req->request.length; __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->short_not_ok = req->request.short_not_ok;
__entry->no_interrupt = req->request.no_interrupt; __entry->no_interrupt = req->request.no_interrupt;
), ),
TP_printk("%s: req %p length %u/%u %s%s%s ==> %d", TP_printk("%pa: %s: req %p length %u/%u %s%s%s ==> %d",
__get_str(name), __entry->req, __entry->actual, __entry->length, &__entry->base_address,
__get_str(name), __entry->req,
__entry->actual, __entry->length,
__entry->zero ? "Z" : "z", __entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s", __entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "i" : "I", __entry->no_interrupt ? "i" : "I",
@ -173,28 +186,30 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
); );
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd, DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
TP_PROTO(unsigned int cmd, u32 param, int status), TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
TP_ARGS(cmd, param, status), TP_ARGS(dwc, cmd, param, status),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__field(unsigned int, cmd) __field(unsigned int, cmd)
__field(u32, param) __field(u32, param)
__field(int, status) __field(int, status)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dwc->xhci_resources[0].start;
__entry->cmd = cmd; __entry->cmd = cmd;
__entry->param = param; __entry->param = param;
__entry->status = status; __entry->status = status;
), ),
TP_printk("cmd '%s' [%x] param %08x --> status: %s", TP_printk("%pa: cmd '%s' [%x] param %08x --> status: %s",
dwc3_gadget_generic_cmd_string(__entry->cmd), &__entry->base_address, dwc3_gadget_generic_cmd_string(__entry->cmd),
__entry->cmd, __entry->param, __entry->cmd, __entry->param,
dwc3_gadget_generic_cmd_status_string(__entry->status) dwc3_gadget_generic_cmd_status_string(__entry->status)
) )
); );
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd, DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
TP_PROTO(unsigned int cmd, u32 param, int status), TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
TP_ARGS(cmd, param, status) TP_ARGS(dwc, cmd, param, status)
); );
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, 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), struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
TP_ARGS(dep, cmd, params, cmd_status), TP_ARGS(dep, cmd, params, cmd_status),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__string(name, dep->name) __string(name, dep->name)
__field(unsigned int, cmd) __field(unsigned int, cmd)
__field(u32, param0) __field(u32, param0)
@ -210,6 +226,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__field(int, cmd_status) __field(int, cmd_status)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name); __assign_str(name);
__entry->cmd = cmd; __entry->cmd = cmd;
__entry->param0 = params->param0; __entry->param0 = params->param0;
@ -217,8 +234,9 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__entry->param2 = params->param2; __entry->param2 = params->param2;
__entry->cmd_status = cmd_status; __entry->cmd_status = cmd_status;
), ),
TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s", TP_printk("%pa: %s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd), &__entry->base_address, __get_str(name),
dwc3_gadget_ep_cmd_string(__entry->cmd),
__entry->cmd, __entry->param0, __entry->cmd, __entry->param0,
__entry->param1, __entry->param2, __entry->param1, __entry->param2,
dwc3_ep_cmd_status_string(__entry->cmd_status) 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_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
TP_ARGS(dep, trb), TP_ARGS(dep, trb),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__string(name, dep->name) __string(name, dep->name)
__field(struct dwc3_trb *, trb) __field(struct dwc3_trb *, trb)
__field(u32, bpl) __field(u32, bpl)
@ -246,6 +265,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__field(u32, dequeue) __field(u32, dequeue)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name); __assign_str(name);
__entry->trb = trb; __entry->trb = trb;
__entry->bpl = trb->bpl; __entry->bpl = trb->bpl;
@ -256,8 +276,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->enqueue = dep->trb_enqueue; __entry->enqueue = dep->trb_enqueue;
__entry->dequeue = dep->trb_dequeue; __entry->dequeue = dep->trb_dequeue;
), ),
TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)", 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)",
__get_str(name), __entry->trb, __entry->enqueue, &__entry->base_address, __get_str(name), __entry->trb, __entry->enqueue,
__entry->dequeue, __entry->bph, __entry->bpl, __entry->dequeue, __entry->bph, __entry->bpl,
({char *s; ({char *s;
int pcm = ((__entry->size >> 24) & 3) + 1; int pcm = ((__entry->size >> 24) & 3) + 1;
@ -307,6 +327,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
TP_PROTO(struct dwc3_ep *dep), TP_PROTO(struct dwc3_ep *dep),
TP_ARGS(dep), TP_ARGS(dep),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(phys_addr_t, base_address)
__string(name, dep->name) __string(name, dep->name)
__field(unsigned int, maxpacket) __field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit) __field(unsigned int, maxpacket_limit)
@ -318,6 +339,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__field(u8, trb_dequeue) __field(u8, trb_dequeue)
), ),
TP_fast_assign( TP_fast_assign(
__entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name); __assign_str(name);
__entry->maxpacket = dep->endpoint.maxpacket; __entry->maxpacket = dep->endpoint.maxpacket;
__entry->maxpacket_limit = dep->endpoint.maxpacket_limit; __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_enqueue = dep->trb_enqueue;
__entry->trb_dequeue = dep->trb_dequeue; __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", TP_printk("%pa: %s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
__get_str(name), __entry->maxpacket, &__entry->base_address, __get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams, __entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->trb_enqueue, __entry->maxburst, __entry->trb_enqueue,
__entry->trb_dequeue, __entry->trb_dequeue,

View file

@ -33,13 +33,13 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read)
if (read) if (read)
ns += DWC3_ULPI_BASE_DELAY; 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) if (reg & DWC3_GUSB2PHYCFG_SUSPHY)
usleep_range(1000, 1200); usleep_range(1000, 1200);
while (count--) { while (count--) {
ndelay(ns); ndelay(ns);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
if (reg & DWC3_GUSB2PHYACC_DONE) if (reg & DWC3_GUSB2PHYACC_DONE)
return 0; return 0;
cpu_relax(); cpu_relax();
@ -55,13 +55,13 @@ static int dwc3_ulpi_read(struct device *dev, u8 addr)
int ret; int ret;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 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); ret = dwc3_ulpi_busyloop(dwc, addr, true);
if (ret) if (ret)
return ret; return ret;
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
return DWC3_GUSB2PHYACC_DATA(reg); 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_NEWREGREQ | DWC3_ULPI_ADDR(addr);
reg |= DWC3_GUSB2PHYACC_WRITE | val; 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); return dwc3_ulpi_busyloop(dwc, addr, false);
} }

View file

@ -5625,11 +5625,6 @@ int __init fotg210_hcd_init(void)
if (usb_disabled()) if (usb_disabled())
return -ENODEV; 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", pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n",
hcd_name, sizeof(struct fotg210_qh), hcd_name, sizeof(struct fotg210_qh),
sizeof(struct fotg210_qtd), sizeof(struct fotg210_qtd),
@ -5643,5 +5638,4 @@ int __init fotg210_hcd_init(void)
void __exit fotg210_hcd_cleanup(void) void __exit fotg210_hcd_cleanup(void)
{ {
debugfs_remove(fotg210_debug_root); debugfs_remove(fotg210_debug_root);
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
} }

View file

@ -409,7 +409,7 @@ static void gadget_info_attr_release(struct config_item *item)
kfree(gi); 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, .release = gadget_info_attr_release,
}; };
@ -514,7 +514,7 @@ static void config_usb_cfg_unlink(
WARN(1, "Unable to locate function to unbind\n"); 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, .release = gadget_config_attr_release,
.allow_link = config_usb_cfg_link, .allow_link = config_usb_cfg_link,
.drop_link = config_usb_cfg_unlink, .drop_link = config_usb_cfg_unlink,
@ -663,7 +663,7 @@ static void function_drop(
config_item_put(item); config_item_put(item);
} }
static struct configfs_group_operations functions_ops = { static const struct configfs_group_operations functions_ops = {
.make_group = &function_make, .make_group = &function_make,
.drop_item = &function_drop, .drop_item = &function_drop,
}; };
@ -766,7 +766,7 @@ static void config_desc_drop(
config_item_put(item); 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, .make_group = &config_desc_make,
.drop_item = &config_desc_drop, .drop_item = &config_desc_drop,
}; };
@ -799,7 +799,7 @@ static void gadget_language_attr_release(struct config_item *item)
kfree(gs); 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, .release = gadget_language_attr_release,
}; };
@ -852,7 +852,7 @@ static void gadget_string_release(struct config_item *item)
kfree(string); 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, .release = gadget_string_release,
}; };
@ -901,7 +901,7 @@ static void gadget_language_string_drop(struct config_group *group,
string->usb_string.id = i++; 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, .make_item = gadget_language_string_make,
.drop_item = gadget_language_string_drop, .drop_item = gadget_language_string_drop,
}; };
@ -960,7 +960,7 @@ static void gadget_language_drop(struct config_group *group,
config_item_put(item); 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, .make_group = &gadget_language_make,
.drop_item = &gadget_language_drop, .drop_item = &gadget_language_drop,
}; };
@ -1266,7 +1266,7 @@ static void os_desc_unlink(struct config_item *os_desc_ci,
mutex_unlock(&gi->lock); 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, .allow_link = os_desc_link,
.drop_link = os_desc_unlink, .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 */ 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, .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); 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, .make_item = &ext_prop_make,
.drop_item = &ext_prop_drop, .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); config_item_put(item);
} }
static struct configfs_group_operations gadgets_ops = { static const struct configfs_group_operations gadgets_ops = {
.make_group = &gadgets_make, .make_group = &gadgets_make,
.drop_item = &gadgets_drop, .drop_item = &gadgets_drop,
}; };

View file

@ -793,7 +793,7 @@ static void acm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = acm_attr_release,
}; };

View file

@ -1509,7 +1509,7 @@ static int ffs_dmabuf_attach(struct file *file, int fd)
goto err_dmabuf_detach; 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); err = ffs_dma_resv_lock(dmabuf, nonblock);
if (err) if (err)
@ -1639,7 +1639,7 @@ static int ffs_dmabuf_transfer(struct file *file,
/* Make sure we don't have writers */ /* Make sure we don't have writers */
timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS); timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
retl = dma_resv_wait_timeout(dmabuf->resv, retl = dma_resv_wait_timeout(dmabuf->resv,
dma_resv_usage_rw(epfile->in), dma_resv_usage_rw(!epfile->in),
true, timeout); true, timeout);
if (retl == 0) if (retl == 0)
retl = -EBUSY; retl = -EBUSY;
@ -1684,7 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file,
dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops, dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops,
&priv->lock, priv->context, seqno); &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_add_fence(dmabuf->resv, &fence->base, resv_dir);
dma_resv_unlock(dmabuf->resv); dma_resv_unlock(dmabuf->resv);
@ -1744,10 +1744,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{ {
int fd; int fd;
if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
ret = -EFAULT; return -EFAULT;
break;
}
return ffs_dmabuf_attach(file, fd); return ffs_dmabuf_attach(file, fd);
} }
@ -1755,10 +1753,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{ {
int fd; int fd;
if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
ret = -EFAULT; return -EFAULT;
break;
}
return ffs_dmabuf_detach(file, fd); 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; struct usb_ffs_dmabuf_transfer_req req;
if (copy_from_user(&req, (void __user *)value, sizeof(req))) { if (copy_from_user(&req, (void __user *)value, sizeof(req)))
ret = -EFAULT; return -EFAULT;
break;
}
return ffs_dmabuf_transfer(file, &req); 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); 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, .release = ffs_attr_release,
}; };

View file

@ -1328,7 +1328,7 @@ static void hid_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = hid_attr_release,
}; };

View file

@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item)
usb_put_function_instance(&lb_opts->func_inst); 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, .release = lb_attr_release,
}; };

View file

@ -3153,7 +3153,7 @@ static void fsg_lun_attr_release(struct config_item *item)
kfree(lun_opts); 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, .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); 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, .release = fsg_attr_release,
}; };
@ -3462,7 +3462,7 @@ static struct configfs_attribute *fsg_attrs[] = {
NULL, NULL,
}; };
static struct configfs_group_operations fsg_group_ops = { static const struct configfs_group_operations fsg_group_ops = {
.make_group = fsg_lun_make, .make_group = fsg_lun_make,
.drop_item = fsg_lun_drop, .drop_item = fsg_lun_drop,
}; };

View file

@ -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 usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f); struct f_midi *midi = func_to_midi(f);
struct usb_string *us; struct usb_string *us;
struct f_midi_opts *opts;
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0; int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
midi->gadget = cdev->gadget; midi->gadget = cdev->gadget;
@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0) if (status < 0)
goto fail_register; 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 */ /* maybe allocate device-global string ID */
us = usb_gstrings_attach(c->cdev, midi_strings, us = usb_gstrings_attach(c->cdev, midi_strings,
ARRAY_SIZE(midi_string_defs)); 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); 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, .release = midi_attr_release,
}; };
@ -1178,59 +1183,60 @@ end: \
\ \
CONFIGFS_ATTR(f_midi_opts_, name); 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_SIGNED(index, true, SNDRV_CARDS);
F_MIDI_OPT(buflen, false, 0); F_MIDI_OPT(buflen, false, 0);
F_MIDI_OPT(qlen, false, 0); F_MIDI_OPT(qlen, false, 0);
F_MIDI_OPT(in_ports, true, MAX_PORTS); F_MIDI_OPT(in_ports, true, MAX_PORTS);
F_MIDI_OPT(out_ports, true, MAX_PORTS); F_MIDI_OPT(out_ports, true, MAX_PORTS);
F_MIDI_OPT_STRING(id);
static ssize_t f_midi_opts_id_show(struct config_item *item, char *page) F_MIDI_OPT_STRING(interface_string);
{
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);
static struct configfs_attribute *midi_attrs[] = { static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_index, &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_in_ports,
&f_midi_opts_attr_out_ports, &f_midi_opts_attr_out_ports,
&f_midi_opts_attr_id, &f_midi_opts_attr_id,
&f_midi_opts_attr_interface_string,
NULL, NULL,
}; };
@ -1262,8 +1269,8 @@ static void f_midi_free_inst(struct usb_function_instance *f)
mutex_unlock(&opts->lock); mutex_unlock(&opts->lock);
if (free) { if (free) {
if (opts->id_allocated) kfree(opts->id);
kfree(opts->id); kfree(opts->interface_string);
kfree(opts); kfree(opts);
} }
} }
@ -1279,7 +1286,8 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
mutex_init(&opts->lock); mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst; opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1; opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1; opts->id = NULL;
opts->interface_string = NULL;
opts->buflen = 512; opts->buflen = 512;
opts->qlen = 32; opts->qlen = 32;
opts->in_ports = 1; opts->in_ports = 1;

View file

@ -2316,7 +2316,7 @@ static void f_midi2_block_opts_release(struct config_item *item)
kfree(opts); 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, .release = f_midi2_block_opts_release,
}; };
@ -2479,11 +2479,11 @@ static void f_midi2_ep_opts_release(struct config_item *item)
kfree(opts); 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, .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, .make_group = f_midi2_opts_block_make,
.drop_item = f_midi2_opts_block_drop, .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); 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, .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, .make_group = f_midi2_opts_ep_make,
.drop_item = f_midi2_opts_ep_drop, .drop_item = f_midi2_opts_ep_drop,
}; };

View file

@ -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); 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) static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{ {
struct f_ncm *ncm = func_to_ncm(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; struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */ /* Control interface has only altsetting 0 */
@ -881,12 +887,13 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt > 1) if (alt > 1)
goto fail; goto fail;
if (ncm->netdev) { scoped_guard(mutex, &opts->lock)
DBG(cdev, "reset ncm\n"); if (opts->net) {
ncm->netdev = NULL; DBG(cdev, "reset ncm\n");
gether_disconnect(&ncm->port); opts->net = NULL;
ncm_reset_values(ncm); gether_disconnect(&ncm->port);
} ncm_reset_values(ncm);
}
/* /*
* CDC Network only sends data in non-default altsettings. * CDC Network only sends data in non-default altsettings.
@ -919,7 +926,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port); net = gether_connect(&ncm->port);
if (IS_ERR(net)) if (IS_ERR(net))
return PTR_ERR(net); return PTR_ERR(net);
ncm->netdev = net; scoped_guard(mutex, &opts->lock)
opts->net = net;
} }
spin_lock(&ncm->lock); spin_lock(&ncm->lock);
@ -1366,14 +1374,16 @@ err:
static void ncm_disable(struct usb_function *f) static void ncm_disable(struct usb_function *f)
{ {
struct f_ncm *ncm = func_to_ncm(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; struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ncm deactivated\n"); DBG(cdev, "ncm deactivated\n");
if (ncm->netdev) { scoped_guard(mutex, &opts->lock)
ncm->netdev = NULL; if (opts->net) {
gether_disconnect(&ncm->port); opts->net = NULL;
} gether_disconnect(&ncm->port);
}
if (ncm->notify->enabled) { if (ncm->notify->enabled) {
usb_ep_disable(ncm->notify); usb_ep_disable(ncm->notify);
@ -1433,39 +1443,44 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{ {
struct usb_composite_dev *cdev = c->cdev; struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f); struct f_ncm *ncm = func_to_ncm(f);
struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
struct usb_string *us; struct usb_string *us;
int status = 0; int status = 0;
struct usb_ep *ep; struct usb_ep *ep;
struct f_ncm_opts *ncm_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; 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; struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget)) if (!can_support_ecm(cdev->gadget))
return -EINVAL; return -EINVAL;
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
if (cdev->use_os_string) { if (cdev->use_os_string) {
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
if (!os_desc_table) if (!os_desc_table)
return -ENOMEM; return -ENOMEM;
} }
mutex_lock(&ncm_opts->lock); netdev = gether_setup_default();
gether_set_gadget(ncm_opts->net, cdev->gadget); if (IS_ERR(netdev))
if (!ncm_opts->bound) { return -ENOMEM;
ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
status = gether_register_netdev(ncm_opts->net);
}
mutex_unlock(&ncm_opts->lock);
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) if (status)
return status; return status;
ncm_opts->bound = true; /* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
ncm_string_defs[1].s = 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, us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs)); 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; f->os_desc_n = 1;
} }
ncm->notify_req = no_free_ptr(request); 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", DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name, 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 */ /* f_ncm_item_ops */
USB_ETHERNET_CONFIGFS_ITEM(ncm); USB_ETHER_OPTS_ITEM(ncm);
/* f_ncm_opts_dev_addr */ /* 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 */ /* f_ncm_opts_host_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm); USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
/* f_ncm_opts_qmult */ /* f_ncm_opts_qmult */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); USB_ETHER_OPTS_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */ /* 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, static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page) char *page)
@ -1655,34 +1672,27 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts; struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst); 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->ncm_interf_group);
kfree(opts); kfree(opts);
} }
static struct usb_function_instance *ncm_alloc_inst(void) 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]; struct usb_os_desc *descs[1];
char *names[1]; char *names[1];
struct config_group *ncm_interf_group; 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) if (!opts)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
opts->net = NULL;
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
gether_setup_opts_default(&opts->net_opts, "usb");
mutex_init(&opts->lock); mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst; 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; opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); 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 = ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE); names, THIS_MODULE);
if (IS_ERR(ncm_interf_group)) { if (IS_ERR(ncm_interf_group))
ncm_free_inst(&opts->func_inst);
return ERR_CAST(ncm_interf_group); return ERR_CAST(ncm_interf_group);
}
opts->ncm_interf_group = 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) static void ncm_free(struct usb_function *f)
{ {
struct f_ncm *ncm; struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct f_ncm_opts *opts;
ncm = func_to_ncm(f); scoped_guard(mutex, &opts->lock)
opts = container_of(f->fi, struct f_ncm_opts, func_inst); opts->refcnt--;
kfree(ncm); kfree(func_to_ncm(f));
mutex_lock(&opts->lock);
opts->refcnt--;
mutex_unlock(&opts->lock);
} }
static void ncm_unbind(struct usb_configuration *c, struct usb_function *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); kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req); 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) static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{ {
struct f_ncm *ncm; struct f_ncm *ncm;
struct f_ncm_opts *opts; struct f_ncm_opts *opts;
int status;
/* allocate and initialize one new instance */ /* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); 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); return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ncm_opts, func_inst); opts = container_of(fi, struct f_ncm_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
/* export host's Ethernet address in CDC format */ scoped_guard(mutex, &opts->lock)
status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, opts->refcnt++;
sizeof(ncm->ethaddr));
if (status < 12) { /* strlen("01234567890a") */
kfree(ncm);
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
spin_lock_init(&ncm->lock); spin_lock_init(&ncm->lock);
ncm_reset_values(ncm); ncm_reset_values(ncm);
ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true; ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true; ncm->port.supports_multi_frame = true;

View file

@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = obex_attr_release,
}; };

View file

@ -585,7 +585,7 @@ static void phonet_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = phonet_attr_release,
}; };

View file

@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = printer_attr_release,
}; };

View file

@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = serial_attr_release,
}; };

View file

@ -46,6 +46,7 @@ struct f_sourcesink {
unsigned isoc_mult; unsigned isoc_mult;
unsigned isoc_maxburst; unsigned isoc_maxburst;
unsigned buflen; unsigned buflen;
unsigned bulk_maxburst;
unsigned bulk_qlen; unsigned bulk_qlen;
unsigned iso_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_alt0.bInterfaceNumber = id;
source_sink_intf_alt1.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 */ /* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!ss->in_ep) { 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_mult = ss_opts->isoc_mult;
ss->isoc_maxburst = ss_opts->isoc_maxburst; ss->isoc_maxburst = ss_opts->isoc_maxburst;
ss->buflen = ss_opts->bulk_buflen; ss->buflen = ss_opts->bulk_buflen;
ss->bulk_maxburst = ss_opts->bulk_maxburst;
ss->bulk_qlen = ss_opts->bulk_qlen; ss->bulk_qlen = ss_opts->bulk_qlen;
ss->iso_qlen = ss_opts->iso_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); 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, .release = ss_attr_release,
}; };
@ -1101,6 +1109,49 @@ end:
CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst); 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) 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); 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_mult,
&f_ss_opts_attr_isoc_maxburst, &f_ss_opts_attr_isoc_maxburst,
&f_ss_opts_attr_bulk_buflen, &f_ss_opts_attr_bulk_buflen,
&f_ss_opts_attr_bulk_maxburst,
&f_ss_opts_attr_bulk_qlen, &f_ss_opts_attr_bulk_qlen,
&f_ss_opts_attr_iso_qlen, &f_ss_opts_attr_iso_qlen,
NULL, NULL,

View file

@ -1227,7 +1227,7 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd)
goto out; goto out;
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf, 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); cmd->prio_attr, dir, flags);
return; return;
@ -1389,6 +1389,7 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req)
cmd->tmr_func = 0; cmd->tmr_func = 0;
cmd->tmr_rsp = RC_RESPONSE_UNKNOWN; cmd->tmr_rsp = RC_RESPONSE_UNKNOWN;
cmd->flags = 0; cmd->flags = 0;
cmd->data_len = 0;
cmd_iu = (struct command_iu *)iu; 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); 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, .release = tcm_attr_release,
}; };

View file

@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = f_uac1_attr_release,
}; };

View file

@ -812,7 +812,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = f_uac1_attr_release,
}; };

View file

@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst); 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, .release = f_uac2_attr_release,
}; };

View file

@ -34,6 +34,7 @@ struct f_ss_opts {
unsigned isoc_mult; unsigned isoc_mult;
unsigned isoc_maxburst; unsigned isoc_maxburst;
unsigned bulk_buflen; unsigned bulk_buflen;
unsigned bulk_maxburst;
unsigned bulk_qlen; unsigned bulk_qlen;
unsigned iso_qlen; unsigned iso_qlen;

View file

@ -1040,6 +1040,36 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
} }
EXPORT_SYMBOL_GPL(gether_set_ifname); 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) void gether_suspend(struct gether *link)
{ {
struct eth_dev *dev = link->ioport; struct eth_dev *dev = link->ioport;
@ -1096,6 +1126,21 @@ void gether_cleanup(struct eth_dev *dev)
} }
EXPORT_SYMBOL_GPL(gether_cleanup); 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 * gether_connect - notify network layer that USB link is active
* @link: the USB link, set up with endpoints, descriptors matching * @link: the USB link, set up with endpoints, descriptors matching

View file

@ -38,6 +38,31 @@
struct eth_dev; 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 * This represents the USB side of an "ethernet" link, managed by a USB
* function which provides control and (maybe) framing. Two functions * 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); int gether_set_ifname(struct net_device *net, const char *name, int len);
void gether_cleanup(struct eth_dev *dev); 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_suspend(struct gether *link);
void gether_resume(struct gether *link); void gether_resume(struct gether *link);

View file

@ -13,6 +13,13 @@
#ifndef __U_ETHER_CONFIGFS_H #ifndef __U_ETHER_CONFIGFS_H
#define __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_) \ #define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
static void _f_##_attr_release(struct config_item *item) \ static void _f_##_attr_release(struct config_item *item) \
{ \ { \
@ -21,7 +28,7 @@
usb_put_function_instance(&opts->func_inst); \ 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, \ .release = _f_##_attr_release, \
} }
@ -197,4 +204,174 @@ out: \
\ \
CONFIGFS_ATTR(_f_##_opts_, _n_) 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 */ #endif /* __U_ETHER_CONFIGFS_H */

View file

@ -19,7 +19,7 @@ struct f_midi_opts {
struct usb_function_instance func_inst; struct usb_function_instance func_inst;
int index; int index;
char *id; char *id;
bool id_allocated; char *interface_string;
unsigned int in_ports; unsigned int in_ports;
unsigned int out_ports; unsigned int out_ports;
unsigned int buflen; unsigned int buflen;

View file

@ -15,11 +15,13 @@
#include <linux/usb/composite.h> #include <linux/usb/composite.h>
#include "u_ether.h"
struct f_ncm_opts { struct f_ncm_opts {
struct usb_function_instance func_inst; struct usb_function_instance func_inst;
struct net_device *net; struct net_device *net;
bool bound;
struct gether_opts net_opts;
struct config_group *ncm_interf_group; struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc; struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16]; char ncm_ext_compat_id[16];

View file

@ -128,7 +128,7 @@ static void uvcg_config_item_release(struct config_item *item)
kfree(group); 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, .release = uvcg_config_item_release,
}; };
@ -285,7 +285,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
return &h->item; 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, .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); 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, .release = uvcg_extension_release,
.allow_link = uvcg_extension_allow_link, .allow_link = uvcg_extension_allow_link,
.drop_link = uvcg_extension_drop_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; 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, .make_item = uvcg_extension_make,
.drop_item = uvcg_extension_drop, .drop_item = uvcg_extension_drop,
}; };
@ -1414,7 +1414,7 @@ out:
mutex_unlock(su_mutex); 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, .release = uvcg_config_item_release,
.allow_link = uvcg_control_class_allow_link, .allow_link = uvcg_control_class_allow_link,
.drop_link = uvcg_control_class_drop_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); 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, .release = uvcg_config_item_release,
.allow_link = uvcg_format_allow_link, .allow_link = uvcg_format_allow_link,
.drop_link = uvcg_format_drop_link, .drop_link = uvcg_format_drop_link,
@ -1840,7 +1840,7 @@ out:
mutex_unlock(su_mutex); 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, .release = uvcg_config_item_release,
.allow_link = uvcg_streaming_header_allow_link, .allow_link = uvcg_streaming_header_allow_link,
.drop_link = uvcg_streaming_header_drop_link, .drop_link = uvcg_streaming_header_drop_link,
@ -1914,7 +1914,7 @@ static struct config_item
return &h->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, .make_item = uvcg_streaming_header_make,
}; };
@ -2261,7 +2261,7 @@ static void uvcg_format_set_indices(struct config_group *fmt)
* streaming/uncompressed/<NAME> * 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, .make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop, .drop_item = uvcg_frame_drop,
}; };
@ -2508,7 +2508,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
return &h->fmt.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, .make_group = uvcg_uncompressed_make,
}; };
@ -2525,7 +2525,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
* streaming/mjpeg/<NAME> * 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, .make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop, .drop_item = uvcg_frame_drop,
}; };
@ -2698,7 +2698,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
return &h->fmt.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, .make_group = uvcg_mjpeg_make,
}; };
@ -2715,7 +2715,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
* streaming/framebased/<NAME> * 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, .make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop, .drop_item = uvcg_frame_drop,
}; };
@ -2953,7 +2953,7 @@ static struct config_group *uvcg_framebased_make(struct config_group *group,
return &h->fmt.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, .make_group = uvcg_framebased_make,
}; };
@ -3056,7 +3056,7 @@ static void uvcg_color_matching_release(struct config_item *item)
kfree(color_match); 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, .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; 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, .make_group = uvcg_color_matching_make,
}; };
@ -3530,7 +3530,7 @@ out:
mutex_unlock(su_mutex); 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, .release = uvcg_config_item_release,
.allow_link = uvcg_streaming_class_allow_link, .allow_link = uvcg_streaming_class_allow_link,
.drop_link = uvcg_streaming_class_drop_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); 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, .release = uvc_func_item_release,
.allow_link = uvc_func_allow_link, .allow_link = uvc_func_allow_link,
.drop_link = uvc_func_drop_link, .drop_link = uvc_func_drop_link,

View file

@ -23,6 +23,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/reset.h>
#include "vhub.h" #include "vhub.h"
@ -280,6 +281,8 @@ static void ast_vhub_remove(struct platform_device *pdev)
if (vhub->clk) if (vhub->clk)
clk_disable_unprepare(vhub->clk); clk_disable_unprepare(vhub->clk);
reset_control_assert(vhub->rst);
spin_unlock_irqrestore(&vhub->lock, flags); spin_unlock_irqrestore(&vhub->lock, flags);
if (vhub->ep0_bufs) 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) static int ast_vhub_probe(struct platform_device *pdev)
{ {
enum usb_device_speed max_speed; enum usb_device_speed max_speed;
const u64 *dma_mask_ptr;
struct ast_vhub *vhub; struct ast_vhub *vhub;
struct resource *res; struct resource *res;
int i, rc = 0; int i, rc = 0;
@ -348,6 +352,16 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err; 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 */ /* Check if we need to limit the HW to USB1 */
max_speed = usb_get_maximum_speed(&pdev->dev); max_speed = usb_get_maximum_speed(&pdev->dev);
if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH) 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; 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, * Allocate DMA buffers for all EP0s in one chunk,
* one per port and one for the vHub itself * one per port and one for the vHub itself
@ -412,15 +432,25 @@ static int ast_vhub_probe(struct platform_device *pdev)
return rc; 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[] = { static const struct of_device_id ast_vhub_dt_ids[] = {
{ {
.compatible = "aspeed,ast2400-usb-vhub", .compatible = "aspeed,ast2400-usb-vhub",
.data = &dma_mask_32,
}, },
{ {
.compatible = "aspeed,ast2500-usb-vhub", .compatible = "aspeed,ast2500-usb-vhub",
.data = &dma_mask_32,
}, },
{ {
.compatible = "aspeed,ast2600-usb-vhub", .compatible = "aspeed,ast2600-usb-vhub",
.data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2700-usb-vhub",
.data = &dma_mask_64,
}, },
{ } { }
}; };

View file

@ -388,6 +388,7 @@ struct ast_vhub {
spinlock_t lock; spinlock_t lock;
struct work_struct wake_work; struct work_struct wake_work;
struct clk *clk; struct clk *clk;
struct reset_control *rst;
/* EP0 DMA buffers allocated in one chunk */ /* EP0 DMA buffers allocated in one chunk */
void *ep0_bufs; void *ep0_bufs;

View file

@ -35,8 +35,8 @@ static int poll_oip(struct bdc *bdc, u32 usec)
u32 status; u32 status;
int ret; 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); (BDC_CSTS(status) != BDC_OIP), 10, usec);
if (ret) if (ret)
dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status); dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
else else

View file

@ -3392,17 +3392,18 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
{ {
u32 val, imod; u32 val, imod;
val = xudc_readl(xudc, BLCG);
if (xudc->soc->has_ipfs) { if (xudc->soc->has_ipfs) {
val = xudc_readl(xudc, BLCG);
val |= BLCG_ALL; val |= BLCG_ALL;
val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE | val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE |
BLCG_COREPLL_PWRDN); BLCG_COREPLL_PWRDN);
val |= BLCG_IOPLL_0_PWRDN; val |= BLCG_IOPLL_0_PWRDN;
val |= BLCG_IOPLL_1_PWRDN; val |= BLCG_IOPLL_1_PWRDN;
val |= BLCG_IOPLL_2_PWRDN; val |= BLCG_IOPLL_2_PWRDN;
} else {
xudc_writel(xudc, val, BLCG); val &= ~BLCG_COREPLL_PWRDN;
} }
xudc_writel(xudc, val, BLCG);
if (xudc->soc->port_speed_quirk) if (xudc->soc->port_speed_quirk)
tegra_xudc_limit_port_speed(xudc); 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) static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
{ {
unsigned long flags; unsigned long flags;
u32 val;
dev_dbg(xudc->dev, "entering ELPG\n"); 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); 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); clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks);
regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);

View file

@ -383,18 +383,6 @@ config USB_ISP116X_HCD
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called isp116x-hcd. 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 config USB_MAX3421_HCD
tristate "MAX3421 HCD (USB-over-SPI) support" tristate "MAX3421 HCD (USB-over-SPI) support"
depends on USB && SPI depends on USB && SPI
@ -616,7 +604,7 @@ config USB_UHCI_ASPEED
config USB_FHCI_HCD config USB_FHCI_HCD
tristate "Freescale QE USB Host Controller support" 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 FSL_GTM
select QE_USB select QE_USB
help help

View file

@ -52,7 +52,6 @@ obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-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) += ohci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o

View file

@ -1354,12 +1354,6 @@ static int __init ehci_hcd_init(void)
if (usb_disabled()) if (usb_disabled())
return -ENODEV; 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", pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n",
hcd_name, hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd), sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
@ -1390,7 +1384,6 @@ clean0:
debugfs_remove(ehci_debug_root); debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL; ehci_debug_root = NULL;
#endif #endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval; return retval;
} }
module_init(ehci_hcd_init); module_init(ehci_hcd_init);
@ -1404,6 +1397,5 @@ static void __exit ehci_hcd_cleanup(void)
#ifdef CONFIG_DYNAMIC_DEBUG #ifdef CONFIG_DYNAMIC_DEBUG
debugfs_remove(ehci_debug_root); debugfs_remove(ehci_debug_root);
#endif #endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
} }
module_exit(ehci_hcd_cleanup); module_exit(ehci_hcd_cleanup);

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1282,7 +1282,6 @@ static int __init ohci_hcd_mod_init(void)
pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name, pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td)); sizeof (struct ed), sizeof (struct td));
set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root); 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); debugfs_remove(ohci_debug_root);
ohci_debug_root = NULL; ohci_debug_root = NULL;
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
return retval; return retval;
} }
module_init(ohci_hcd_mod_init); 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); ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif #endif
debugfs_remove(ohci_debug_root); debugfs_remove(ohci_debug_root);
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
} }
module_exit(ohci_hcd_mod_exit); module_exit(ohci_hcd_mod_exit);

View file

@ -867,8 +867,6 @@ static int __init uhci_hcd_init(void)
if (usb_disabled()) if (usb_disabled())
return -ENODEV; return -ENODEV;
set_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
#ifdef CONFIG_DYNAMIC_DEBUG #ifdef CONFIG_DYNAMIC_DEBUG
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf) if (!errbuf)
@ -912,8 +910,6 @@ up_failed:
errbuf_failed: errbuf_failed:
#endif #endif
clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
return retval; return retval;
} }
@ -930,7 +926,6 @@ static void __exit uhci_hcd_cleanup(void)
#ifdef CONFIG_DYNAMIC_DEBUG #ifdef CONFIG_DYNAMIC_DEBUG
kfree(errbuf); kfree(errbuf);
#endif #endif
clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
} }
module_init(uhci_hcd_init); module_init(uhci_hcd_init);

View file

@ -29,6 +29,12 @@
#include "xhci-trace.h" #include "xhci-trace.h"
#include "xhci-dbgcap.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) static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx)
{ {
if (!ctx) if (!ctx)
@ -52,55 +58,6 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring)
kfree(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) static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc)
{ {
struct xhci_ep_ctx *ep_ctx; 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); 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; struct dbc_info_context *info;
u32 dev_info; u32 dev_info;
@ -135,12 +150,12 @@ static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
/* Populate info Context: */ /* Populate info Context: */
info = (struct dbc_info_context *)dbc->ctx->bytes; info = (struct dbc_info_context *)dbc->ctx->bytes;
dma = dbc->string_dma; dma = dbc->str_descs_dma;
info->string0 = cpu_to_le64(dma); info->string0 = cpu_to_le64(dma);
info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH); info->manufacturer = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN);
info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2); info->product = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 2);
info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3); info->serial = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 3);
info->length = cpu_to_le32(string_length); info->length = cpu_to_le32(dbc_prepare_info_context_str_len(dbc->str_descs));
/* Populate bulk in and out endpoint contexts: */ /* Populate bulk in and out endpoint contexts: */
xhci_dbc_init_ep_contexts(dbc); 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; int ret;
dma_addr_t deq; dma_addr_t deq;
u32 string_length;
struct device *dev = dbc->dev; struct device *dev = dbc->dev;
/* Allocate various rings for events and transfers: */ /* 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; goto ctx_fail;
/* Allocate the string table: */ /* Allocate the string table: */
dbc->string_size = sizeof(*dbc->string); dbc->str_descs_size = sizeof(*dbc->str_descs);
dbc->string = dma_alloc_coherent(dev, dbc->string_size, dbc->str_descs = dma_alloc_coherent(dev, dbc->str_descs_size,
&dbc->string_dma, flags); &dbc->str_descs_dma, flags);
if (!dbc->string) if (!dbc->str_descs)
goto string_fail; goto str_descs_fail;
/* Setup ERST register: */ /* Setup ERST register: */
writel(dbc->erst.num_entries, &dbc->regs->ersts); 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); dbc->ring_evt->dequeue);
lo_hi_writeq(deq, &dbc->regs->erdp); lo_hi_writeq(deq, &dbc->regs->erdp);
/* Setup strings and contexts: */ /* Setup string descriptors and contexts: */
string_length = xhci_dbc_populate_strings(dbc->string); xhci_dbc_populate_str_descs(dbc->str_descs, &dbc->str);
xhci_dbc_init_contexts(dbc, string_length); xhci_dbc_init_contexts(dbc);
xhci_dbc_eps_init(dbc); xhci_dbc_eps_init(dbc);
dbc->state = DS_INITIALIZED; dbc->state = DS_INITIALIZED;
return 0; return 0;
string_fail: str_descs_fail:
dbc_free_ctx(dev, dbc->ctx); dbc_free_ctx(dev, dbc->ctx);
dbc->ctx = NULL; dbc->ctx = NULL;
ctx_fail: ctx_fail:
@ -600,8 +614,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
xhci_dbc_eps_exit(dbc); xhci_dbc_eps_exit(dbc);
dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma); dma_free_coherent(dbc->dev, dbc->str_descs_size, dbc->str_descs, dbc->str_descs_dma);
dbc->string = NULL; dbc->str_descs = NULL;
dbc_free_ctx(dbc->dev, dbc->ctx); dbc_free_ctx(dbc->dev, dbc->ctx);
dbc->ctx = NULL; dbc->ctx = NULL;
@ -1196,6 +1210,108 @@ static ssize_t dbc_bcdDevice_store(struct device *dev,
return size; 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, static ssize_t dbc_bInterfaceProtocol_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
@ -1283,6 +1399,9 @@ static DEVICE_ATTR_RW(dbc);
static DEVICE_ATTR_RW(dbc_idVendor); static DEVICE_ATTR_RW(dbc_idVendor);
static DEVICE_ATTR_RW(dbc_idProduct); static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice); 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_bInterfaceProtocol);
static DEVICE_ATTR_RW(dbc_poll_interval_ms); 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_idVendor.attr,
&dev_attr_dbc_idProduct.attr, &dev_attr_dbc_idProduct.attr,
&dev_attr_dbc_bcdDevice.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_bInterfaceProtocol.attr,
&dev_attr_dbc_poll_interval_ms.attr, &dev_attr_dbc_poll_interval_ms.attr,
NULL NULL
@ -1316,6 +1438,9 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
dbc->bInterfaceProtocol = DBC_PROTOCOL; dbc->bInterfaceProtocol = DBC_PROTOCOL;
dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT; 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) if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE)
goto err; goto err;

View file

@ -47,10 +47,6 @@ struct dbc_info_context {
#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8) #define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8)
#define DBC_MAX_PACKET 1024 #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 #define DBC_CONTEXT_SIZE 64
/* /*
@ -63,11 +59,31 @@ struct dbc_info_context {
#define DBC_PORTSC_LINK_CHANGE BIT(22) #define DBC_PORTSC_LINK_CHANGE BIT(22)
#define DBC_PORTSC_CONFIG_CHANGE BIT(23) #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 { struct dbc_str_descs {
char string0[DBC_MAX_STRING_LENGTH]; char string0[USB_MAX_STRING_DESC_LEN];
char manufacturer[DBC_MAX_STRING_LENGTH]; char manufacturer[USB_MAX_STRING_DESC_LEN];
char product[DBC_MAX_STRING_LENGTH]; char product[USB_MAX_STRING_DESC_LEN];
char serial[DBC_MAX_STRING_LENGTH]; 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 */ #define DBC_PROTOCOL 1 /* GNU Remote Debug Command */
@ -133,9 +149,10 @@ struct xhci_dbc {
struct xhci_erst erst; struct xhci_erst erst;
struct xhci_container_ctx *ctx; struct xhci_container_ctx *ctx;
struct dbc_str_descs *string; struct dbc_str_descs *str_descs;
dma_addr_t string_dma; dma_addr_t str_descs_dma;
size_t string_size; size_t str_descs_size;
struct dbc_str str;
u16 idVendor; u16 idVendor;
u16 idProduct; u16 idProduct;
u16 bcdDevice; u16 bcdDevice;

View file

@ -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]); data = irq_get_irq_data(tegra->wake_irqs[i]);
if (!data) { if (!data) {
dev_warn(tegra->dev, "get wake event %d irq data fail\n", i); dev_warn(tegra->dev, "get wake event %d irq data fail\n", i);
irq_dispose_mapping(tegra->wake_irqs[i]);
break; break;
} }
@ -1583,16 +1582,6 @@ static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xu
return 0; 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) static int tegra_xusb_probe(struct platform_device *pdev)
{ {
struct tegra_xusb *tegra; struct tegra_xusb *tegra;
@ -1648,10 +1637,8 @@ static int tegra_xusb_probe(struct platform_device *pdev)
return err; return err;
tegra->padctl = tegra_xusb_padctl_get(&pdev->dev); tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
if (IS_ERR(tegra->padctl)) { if (IS_ERR(tegra->padctl))
err = PTR_ERR(tegra->padctl); return PTR_ERR(tegra->padctl);
goto dispose_wake;
}
np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0); np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
if (!np) { if (!np) {
@ -1975,8 +1962,6 @@ put_powerdomains:
put_padctl: put_padctl:
of_node_put(np); of_node_put(np);
tegra_xusb_padctl_put(tegra->padctl); tegra_xusb_padctl_put(tegra->padctl);
dispose_wake:
tegra_xusb_dispose_wake(tegra);
return err; return err;
} }
@ -2009,8 +1994,6 @@ static void tegra_xusb_remove(struct platform_device *pdev)
if (tegra->padctl_irq) if (tegra->padctl_irq)
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
tegra_xusb_dispose_wake(tegra);
pm_runtime_put(&pdev->dev); pm_runtime_put(&pdev->dev);
tegra_xusb_disable(tegra); tegra_xusb_disable(tegra);

View file

@ -115,6 +115,13 @@ static const struct onboard_dev_pdata vialab_vl817_data = {
.is_hub = true, .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 = { static const struct onboard_dev_pdata xmos_xvf3500_data = {
.reset_us = 1, .reset_us = 1,
.num_supplies = 2, .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,5411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
{ .compatible = "usb1a86,8091", .data = &wch_ch334_data, },
{ .compatible = "usb1da0,5511", .data = &parade_ps5511_data, }, { .compatible = "usb1da0,5511", .data = &parade_ps5511_data, },
{ .compatible = "usb1da0,55a1", .data = &parade_ps5511_data, }, { .compatible = "usb1da0,55a1", .data = &parade_ps5511_data, },
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, }, { .compatible = "usb2109,817", .data = &vialab_vl817_data, },

View file

@ -20,7 +20,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/of.h> #include <linux/property.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -49,15 +49,13 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
int ret = 0; int ret = 0;
if (suspend) { if (suspend) {
if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk);
clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
ret = regulator_disable(nop->vcc); ret = regulator_disable(nop->vcc);
} else { } else {
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
ret = regulator_enable(nop->vcc); ret = regulator_enable(nop->vcc);
if (!IS_ERR(nop->clk)) clk_prepare_enable(nop->clk);
clk_prepare_enable(nop->clk);
} }
return ret; return ret;
@ -137,11 +135,9 @@ int usb_gen_phy_init(struct usb_phy *phy)
dev_err(phy->dev, "Failed to enable power\n"); dev_err(phy->dev, "Failed to enable power\n");
} }
if (!IS_ERR(nop->clk)) { ret = clk_prepare_enable(nop->clk);
ret = clk_prepare_enable(nop->clk); if (ret)
if (ret) return ret;
return ret;
}
nop_reset(nop); nop_reset(nop);
@ -155,8 +151,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
gpiod_set_value_cansleep(nop->gpiod_reset, 1); gpiod_set_value_cansleep(nop->gpiod_reset, 1);
if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk);
clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc)) { if (!IS_ERR(nop->vcc)) {
if (regulator_disable(nop->vcc)) if (regulator_disable(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; enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err = 0; int err = 0;
u32 clk_rate = 0; u32 clk_rate = 0;
bool needs_clk = false;
if (dev->of_node) { device_property_read_u32(dev, "clock-frequency", &clk_rate);
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");
}
nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_ASIS); GPIOD_ASIS);
err = PTR_ERR_OR_ZERO(nop->gpiod_reset); 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) if (!nop->phy.otg)
return -ENOMEM; return -ENOMEM;
nop->clk = devm_clk_get(dev, "main_clk"); nop->clk = devm_clk_get_optional(dev, "main_clk");
if (IS_ERR(nop->clk)) { if (IS_ERR(nop->clk))
dev_dbg(dev, "Can't get phy clock: %ld\n", return dev_err_probe(dev, PTR_ERR(nop->clk),
PTR_ERR(nop->clk)); "Can't get phy clock\n");
if (needs_clk)
return PTR_ERR(nop->clk);
}
if (!IS_ERR(nop->clk) && clk_rate) { if (clk_rate) {
err = clk_set_rate(nop->clk, clk_rate); err = clk_set_rate(nop->clk, clk_rate);
if (err) { if (err)
dev_err(dev, "Error setting clock rate\n"); return dev_err_probe(dev, err,
return err; "Error setting clock rate\n");
}
} }
nop->vcc = devm_regulator_get_optional(dev, "vcc"); 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) static int usb_phy_generic_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node;
struct usb_phy_generic *nop; struct usb_phy_generic *nop;
int err; int err;
@ -293,17 +274,17 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
err = usb_phy_gen_create_phy(dev, nop); err = usb_phy_gen_create_phy(dev, nop);
if (err) if (err)
return err; return err;
if (nop->gpiod_vbus) { if (nop->gpiod_vbus) {
err = devm_request_threaded_irq(&pdev->dev, err = devm_request_threaded_irq(dev,
gpiod_to_irq(nop->gpiod_vbus), gpiod_to_irq(nop->gpiod_vbus),
NULL, nop_gpio_vbus_thread, NULL, nop_gpio_vbus_thread,
VBUS_IRQ_FLAGS, "vbus_detect", VBUS_IRQ_FLAGS, "vbus_detect",
nop); nop);
if (err) { if (err)
dev_err(&pdev->dev, "can't request irq %i, err: %d\n", return dev_err_probe(dev, err, "can't request irq %i\n",
gpiod_to_irq(nop->gpiod_vbus), err); gpiod_to_irq(nop->gpiod_vbus));
return err;
}
nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ? nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE; 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; nop->phy.shutdown = usb_gen_phy_shutdown;
err = usb_add_phy_dev(&nop->phy); err = usb_add_phy_dev(&nop->phy);
if (err) { if (err)
dev_err(&pdev->dev, "can't register transceiver, err: %d\n", return dev_err_probe(dev, err, "can't register transceiver\n");
err);
return err;
}
platform_set_drvdata(pdev, nop); platform_set_drvdata(pdev, nop);
device_set_wakeup_capable(&pdev->dev, device_set_wakeup_capable(dev,
of_property_read_bool(dn, "wakeup-source")); device_property_read_bool(dev, "wakeup-source"));
return 0; return 0;
} }

View file

@ -29,17 +29,26 @@
#include <linux/usb/tegra_usb_phy.h> #include <linux/usb/tegra_usb_phy.h>
#include <linux/usb/ulpi.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 #define ULPI_VIEWPORT 0x170
/* PORTSC PTS/PHCD bits, Tegra20 only */ /* PORTSC PTS/PHCD bits, Tegra20 only */
#define TEGRA_USB_PORTSC1 0x184 #define TEGRA_USB_PORTSC1 0x184
#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) #define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
#define TEGRA_USB_PORTSC1_PHCD BIT(23) #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 */ /* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
#define TEGRA30_USB_PORTSC1 0x174
#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 #define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) #define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) #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 */ /* Bits of PORTSC1, which will get cleared by writing 1 into them */
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
@ -51,11 +60,12 @@
#define USB_SUSP_CLR BIT(5) #define USB_SUSP_CLR BIT(5)
#define USB_PHY_CLK_VALID BIT(7) #define USB_PHY_CLK_VALID BIT(7)
#define UTMIP_RESET BIT(11) #define UTMIP_RESET BIT(11)
#define UHSIC_RESET BIT(11)
#define UTMIP_PHY_ENABLE BIT(12) #define UTMIP_PHY_ENABLE BIT(12)
#define ULPI_PHY_ENABLE BIT(13) #define ULPI_PHY_ENABLE BIT(13)
#define USB_SUSP_SET BIT(14) #define USB_SUSP_SET BIT(14)
#define UHSIC_RESET BIT(14)
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) #define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
#define UHSIC_PHY_ENABLE BIT(19)
#define USB_PHY_VBUS_SENSORS 0x404 #define USB_PHY_VBUS_SENSORS 0x404
#define B_SESS_VLD_WAKEUP_EN BIT(14) #define B_SESS_VLD_WAKEUP_EN BIT(14)
@ -156,6 +166,58 @@
#define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_CFG1 0x83c
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) #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 */ /* For Tegra30 and above only, the address is different in Tegra20 */
#define USB_USBMODE 0x1f8 #define USB_USBMODE 0x1f8
#define USB_USBMODE_MASK (3 << 0) #define USB_USBMODE_MASK (3 << 0)
@ -174,7 +236,8 @@ struct tegra_xtal_freq {
u8 enable_delay; u8 enable_delay;
u8 stable_count; u8 stable_count;
u8 active_delay; u8 active_delay;
u8 xtal_freq_count; u8 utmi_xtal_freq_count;
u16 hsic_xtal_freq_count;
u16 debounce; u16 debounce;
}; };
@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x02, .enable_delay = 0x02,
.stable_count = 0x2F, .stable_count = 0x2F,
.active_delay = 0x04, .active_delay = 0x04,
.xtal_freq_count = 0x76, .utmi_xtal_freq_count = 0x76,
.hsic_xtal_freq_count = 0x1CA,
.debounce = 0x7530, .debounce = 0x7530,
}, },
{ {
@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x02, .enable_delay = 0x02,
.stable_count = 0x33, .stable_count = 0x33,
.active_delay = 0x05, .active_delay = 0x05,
.xtal_freq_count = 0x7F, .utmi_xtal_freq_count = 0x7F,
.hsic_xtal_freq_count = 0x1F0,
.debounce = 0x7EF4, .debounce = 0x7EF4,
}, },
{ {
@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x03, .enable_delay = 0x03,
.stable_count = 0x4B, .stable_count = 0x4B,
.active_delay = 0x06, .active_delay = 0x06,
.xtal_freq_count = 0xBB, .utmi_xtal_freq_count = 0xBB,
.hsic_xtal_freq_count = 0x2DD,
.debounce = 0xBB80, .debounce = 0xBB80,
}, },
{ {
@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x04, .enable_delay = 0x04,
.stable_count = 0x66, .stable_count = 0x66,
.active_delay = 0x09, .active_delay = 0x09,
.xtal_freq_count = 0xFE, .utmi_xtal_freq_count = 0xFE,
.hsic_xtal_freq_count = 0x3E0,
.debounce = 0xFDE8, .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 = readl_relaxed(base + UTMIP_PLL_CFG1);
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
UTMIP_PLLU_ENABLE_DLY_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); UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
writel_relaxed(val, base + UTMIP_PLL_CFG1); writel_relaxed(val, base + UTMIP_PLL_CFG1);
} }
@ -803,17 +870,170 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
return 0; 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) static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{ {
int err; int err = 0;
if (phy->powered_on) if (phy->powered_on)
return 0; return 0;
if (phy->is_ulpi_phy) switch (phy->phy_type) {
err = ulpi_phy_power_on(phy); case USBPHY_INTERFACE_MODE_UTMI:
else
err = utmi_phy_power_on(phy); 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) if (err)
return 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) static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
{ {
int err; int err = 0;
if (!phy->powered_on) if (!phy->powered_on)
return 0; return 0;
if (phy->is_ulpi_phy) switch (phy->phy_type) {
err = ulpi_phy_power_off(phy); case USBPHY_INTERFACE_MODE_UTMI:
else
err = utmi_phy_power_off(phy); 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) if (err)
return 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); usb_phy_set_wakeup(u_phy, false);
tegra_usb_phy_power_off(phy); tegra_usb_phy_power_off(phy);
if (!phy->is_ulpi_phy) if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
utmip_pad_close(phy); utmip_pad_close(phy);
regulator_disable(phy->vbus); regulator_disable(phy->vbus);
@ -1040,7 +1273,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
goto disable_clk; goto disable_clk;
} }
if (!phy->is_ulpi_phy) { if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) {
err = utmip_pad_open(phy); err = utmip_pad_open(phy);
if (err) if (err)
goto disable_vbus; goto disable_vbus;
@ -1057,7 +1290,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
return 0; return 0;
close_phy: close_phy:
if (!phy->is_ulpi_phy) if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
utmip_pad_close(phy); utmip_pad_close(phy);
disable_vbus: disable_vbus:
@ -1095,8 +1328,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
struct resource *res; struct resource *res;
int err; int err;
tegra_phy->is_ulpi_phy = false;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) { if (!res) {
dev_err(&pdev->dev, "Failed to get UTMI pad regs\n"); 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_usbmode_setup = false,
.requires_extra_tuning_parameters = false, .requires_extra_tuning_parameters = false,
.requires_pmc_ao_power_up = 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 = { 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_usbmode_setup = true,
.requires_extra_tuning_parameters = true, .requires_extra_tuning_parameters = true,
.requires_pmc_ao_power_up = 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[] = { 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 device_node *np = pdev->dev.of_node;
struct tegra_usb_phy *tegra_phy; struct tegra_usb_phy *tegra_phy;
enum usb_phy_interface phy_type;
struct reset_control *reset; struct reset_control *reset;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
struct resource *res; struct resource *res;
@ -1314,9 +1552,10 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
return err; return err;
} }
phy_type = of_usb_get_phy_mode(np); tegra_phy->phy_type = of_usb_get_phy_mode(np);
switch (phy_type) { switch (tegra_phy->phy_type) {
case USBPHY_INTERFACE_MODE_UTMI: case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_HSIC:
err = utmi_phy_probe(tegra_phy, pdev); err = utmi_phy_probe(tegra_phy, pdev);
if (err) if (err)
return err; return err;
@ -1341,8 +1580,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
break; break;
case USBPHY_INTERFACE_MODE_ULPI: case USBPHY_INTERFACE_MODE_ULPI:
tegra_phy->is_ulpi_phy = true;
tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link"); tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link");
err = PTR_ERR_OR_ZERO(tegra_phy->clk); err = PTR_ERR_OR_ZERO(tegra_phy->clk);
if (err) { if (err) {
@ -1382,7 +1619,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
default: default:
dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n", dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
phy_type); tegra_phy->phy_type);
return -EINVAL; return -EINVAL;
} }

View file

@ -1401,12 +1401,16 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(1) }, .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) }, .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) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(4) }, .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */
.driver_info = NCTRL(4) }, .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) }, .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) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(4) }, .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a8, 0xff), /* Telit FN920C04 (ECM) */ { 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) }, .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, .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) */ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */
.driver_info = NCTRL(5) }, .driver_info = NCTRL(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) },

View file

@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o 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 typec-$(CONFIG_ACPI) += port-mapper.o
obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC) += altmodes/
obj-$(CONFIG_TYPEC_TCPM) += tcpm/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/

View file

@ -804,8 +804,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (plug) if (plug)
typec_altmode_set_drvdata(plug, dp); typec_altmode_set_drvdata(plug, dp);
dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; if (!alt->mode_selection) {
schedule_work(&dp->work); dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
schedule_work(&dp->work);
}
return 0; return 0;
} }

View file

@ -307,7 +307,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
typec_altmode_set_drvdata(alt, tbt); typec_altmode_set_drvdata(alt, tbt);
typec_altmode_set_ops(alt, &tbt_altmode_ops); 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]) if (tbt->plug[TYPEC_PLUG_SOP_P])
tbt->state = TBT_STATE_SOP_P_ENTER; tbt->state = TBT_STATE_SOP_P_ENTER;
else if (tbt->plug[TYPEC_PLUG_SOP_PP]) else if (tbt->plug[TYPEC_PLUG_SOP_PP])

View file

@ -445,7 +445,23 @@ static struct attribute *typec_attrs[] = {
&dev_attr_description.attr, &dev_attr_description.attr,
NULL 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) 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); struct typec_altmode *altmode = to_typec_altmode(dev);
const struct typec_device_id *id; const struct typec_device_id *id;
if (!is_typec_partner_altmode(dev))
return 0;
for (id = drv->id_table; id->svid; id++) for (id = drv->id_table; id->svid; id++)
if (id->svid == altmode->svid) if (id->svid == altmode->svid)
return 1; 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); 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)) if (add_uevent_var(env, "SVID=%04X", altmode->svid))
return -ENOMEM; return -ENOMEM;
@ -547,3 +569,4 @@ const struct bus_type typec_bus = {
.probe = typec_probe, .probe = typec_probe,
.remove = typec_remove, .remove = typec_remove,
}; };
EXPORT_SYMBOL_GPL(typec_bus);

View file

@ -5,7 +5,6 @@
#include <linux/usb/typec_altmode.h> #include <linux/usb/typec_altmode.h>
struct bus_type;
struct typec_mux; struct typec_mux;
struct typec_retimer; struct typec_retimer;
@ -28,9 +27,4 @@ struct altmode {
#define to_altmode(d) container_of(d, struct altmode, adev) #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__ */ #endif /* __USB_TYPEC_ALTMODE_H__ */

View file

@ -235,7 +235,7 @@ static int altmode_match(struct device *dev, const void *data)
struct typec_altmode *adev = to_typec_altmode(dev); struct typec_altmode *adev = to_typec_altmode(dev);
const struct typec_device_id *id = data; const struct typec_device_id *id = data;
if (!is_typec_altmode(dev)) if (!is_typec_port_altmode(dev))
return 0; return 0;
return (adev->svid == id->svid); 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 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[] = { static struct attribute *typec_altmode_attrs[] = {
&dev_attr_active.attr, &dev_attr_active.attr,
&dev_attr_mode.attr, &dev_attr_mode.attr,
&dev_attr_svid.attr, &dev_attr_svid.attr,
&dev_attr_vdo.attr, &dev_attr_vdo.attr,
&dev_attr_priority.attr,
NULL NULL
}; };
@ -457,11 +534,17 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n) struct attribute *attr, int n)
{ {
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); 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 (attr == &dev_attr_active.attr) {
if (!is_typec_port(adev->dev.parent) && if (!is_typec_port(adev->dev.parent)) {
(!adev->ops || !adev->ops->activate)) if (!port->mode_control || !adev->ops || !adev->ops->activate)
return 0444; return 0444;
}
} else if (attr == &dev_attr_priority.attr) {
if (!is_typec_port(adev->dev.parent) || !port->mode_control)
return 0;
}
return attr->mode; return attr->mode;
} }
@ -532,15 +615,31 @@ static void typec_altmode_release(struct device *dev)
kfree(alt); kfree(alt);
} }
const struct device_type typec_altmode_dev_type = { const struct device_type typec_port_altmode_dev_type = {
.name = "typec_alternate_mode", .name = "typec_port_alternate_mode",
.groups = typec_altmode_groups, .groups = typec_altmode_groups,
.release = typec_altmode_release, .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 * static struct typec_altmode *
typec_register_altmode(struct device *parent, 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); unsigned int id = altmode_id_get(parent);
bool is_port = is_typec_port(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.svid = desc->svid;
alt->adev.mode = desc->mode; alt->adev.mode = desc->mode;
alt->adev.vdo = desc->vdo; alt->adev.vdo = desc->vdo;
alt->adev.mode_selection = desc->mode_selection;
alt->roles = desc->roles; alt->roles = desc->roles;
alt->id = id; alt->id = id;
@ -575,7 +675,7 @@ typec_register_altmode(struct device *parent,
alt->adev.dev.parent = parent; alt->adev.dev.parent = parent;
alt->adev.dev.groups = alt->groups; 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); dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
get_device(alt->adev.dev.parent); get_device(alt->adev.dev.parent);
@ -584,9 +684,7 @@ typec_register_altmode(struct device *parent,
if (!is_port) if (!is_port)
typec_altmode_set_partner(alt); typec_altmode_set_partner(alt);
/* The partners are bind to drivers */ alt->adev.dev.bus = &typec_bus;
if (is_typec_partner(parent))
alt->adev.dev.bus = &typec_bus;
/* Plug alt modes need a class to generate udev events. */ /* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent)) if (is_typec_plug(parent))
@ -963,7 +1061,7 @@ struct typec_altmode *
typec_partner_register_altmode(struct typec_partner *partner, typec_partner_register_altmode(struct typec_partner *partner,
const struct typec_altmode_desc *desc) 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); EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
@ -1193,7 +1291,7 @@ struct typec_altmode *
typec_plug_register_altmode(struct typec_plug *plug, typec_plug_register_altmode(struct typec_plug *plug,
const struct typec_altmode_desc *desc) 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); 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_altmode *adev;
struct typec_mux *mux; struct typec_mux *mux;
struct typec_retimer *retimer; struct typec_retimer *retimer;
int ret;
mux = typec_mux_get(&port->dev); mux = typec_mux_get(&port->dev);
if (IS_ERR(mux)) if (IS_ERR(mux))
@ -2493,13 +2592,19 @@ typec_port_register_altmode(struct typec_port *port,
return ERR_CAST(retimer); 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)) { if (IS_ERR(adev)) {
typec_retimer_put(retimer); typec_retimer_put(retimer);
typec_mux_put(mux); typec_mux_put(mux);
} else { } else {
to_altmode(adev)->mux = mux; to_altmode(adev)->mux = mux;
to_altmode(adev)->retimer = retimer; to_altmode(adev)->retimer = retimer;
ret = typec_mode_set_priority(adev, 0);
if (ret) {
typec_unregister_altmode(adev);
return ERR_PTR(ret);
}
} }
return adev; return adev;
@ -2694,6 +2799,7 @@ struct typec_port *typec_register_port(struct device *parent,
} }
port->pd = cap->pd; port->pd = cap->pd;
port->mode_control = !cap->no_mode_control;
ret = device_add(&port->dev); ret = device_add(&port->dev);
if (ret) { if (ret) {

View file

@ -9,6 +9,7 @@
struct typec_mux; struct typec_mux;
struct typec_switch; struct typec_switch;
struct usb_device; struct usb_device;
struct mode_selection;
struct typec_plug { struct typec_plug {
struct device dev; struct device dev;
@ -39,6 +40,7 @@ struct typec_partner {
u8 usb_capability; u8 usb_capability;
struct usb_power_delivery *pd; struct usb_power_delivery *pd;
struct mode_selection *sel;
void (*attach)(struct typec_partner *partner, struct device *dev); void (*attach)(struct typec_partner *partner, struct device *dev);
void (*deattach)(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; struct mutex partner_link_lock;
enum typec_orientation orientation; enum typec_orientation orientation;
bool mode_control;
struct typec_switch *sw; struct typec_switch *sw;
struct typec_mux *mux; struct typec_mux *mux;
struct typec_retimer *retimer; struct typec_retimer *retimer;

View file

@ -204,6 +204,23 @@ static const struct typec_operations hd3ss3220_ops = {
.port_type_set = hd3ss3220_port_type_set, .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) static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
{ {
enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220); enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
@ -221,6 +238,9 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
break; break;
} }
if (hd3ss3220->vbus && !hd3ss3220->id_gpiod)
hd3ss3220_regulator_control(hd3ss3220, role_state == USB_ROLE_HOST);
hd3ss3220->role_state = role_state; 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) static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
{ {
struct hd3ss3220 *hd3ss3220 = dev_id; struct hd3ss3220 *hd3ss3220 = dev_id;
int ret;
int id; int id;
id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod); id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod);
if (!id) hd3ss3220_regulator_control(hd3ss3220, !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);
return IRQ_HANDLED; return IRQ_HANDLED;
} }

View 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);

View file

@ -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 * 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-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 Command Shall be executed multiple times until a Discover
* SVIDs VDO is returned ending either with a SVID value of 0x0000 in * 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 * the last part of the last VDO or with a VDO containing two SVIDs

View file

@ -73,7 +73,6 @@ config CROS_EC_UCSI
tristate "UCSI Driver for ChromeOS EC" tristate "UCSI Driver for ChromeOS EC"
depends on MFD_CROS_EC_DEV depends on MFD_CROS_EC_DEV
depends on CROS_USBPD_NOTIFY depends on CROS_USBPD_NOTIFY
depends on !EXTCON_TCSS_CROS_EC
default MFD_CROS_EC_DEV default MFD_CROS_EC_DEV
help help
This driver enables UCSI support for a ChromeOS EC. The EC is This driver enables UCSI support for a ChromeOS EC. The EC is

View file

@ -17,6 +17,10 @@ ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
typec_ucsi-y += displayport.o typec_ucsi-y += displayport.o
endif endif
ifneq ($(CONFIG_TYPEC_TBT_ALTMODE),)
typec_ucsi-y += thunderbolt.o
endif
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o

View file

@ -16,6 +16,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/usb/typec_altmode.h>
#include "ucsi.h" #include "ucsi.h"
@ -33,6 +34,11 @@
/* Number of times to attempt recovery from a write timeout before giving up. */ /* Number of times to attempt recovery from a write timeout before giving up. */
#define WRITE_TMO_CTR_MAX 5 #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 cros_ucsi_data {
struct device *dev; struct device *dev;
struct ucsi *ucsi; struct ucsi *ucsi;
@ -134,6 +140,20 @@ static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
return ret; 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 = { static const struct ucsi_operations cros_ucsi_ops = {
.read_version = cros_ucsi_read_version, .read_version = cros_ucsi_read_version,
.read_cci = cros_ucsi_read_cci, .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, .read_message_in = cros_ucsi_read_message_in,
.async_control = cros_ucsi_async_control, .async_control = cros_ucsi_async_control,
.sync_control = cros_ucsi_sync_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) static void cros_ucsi_work(struct work_struct *work)

View file

@ -112,15 +112,20 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
union power_supply_propval *val) union power_supply_propval *val)
{ {
u32 pdo; u32 pdo;
int max_voltage = 0;
switch (UCSI_CONSTAT(con, PWR_OPMODE)) { switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
case UCSI_CONSTAT_PWR_OPMODE_PD: case UCSI_CONSTAT_PWR_OPMODE_PD:
if (con->num_pdos > 0) { for (int i = 0; i < con->num_pdos; i++) {
pdo = con->src_pdos[con->num_pdos - 1]; int pdo_voltage = 0;
val->intval = pdo_fixed_voltage(pdo) * 1000;
} else { pdo = con->src_pdos[i];
val->intval = 0; 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; break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: 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) union power_supply_propval *val)
{ {
u32 pdo; u32 pdo;
int max_current = 0;
if (!UCSI_CONSTAT(con, CONNECTED)) { if (!UCSI_CONSTAT(con, CONNECTED)) {
val->intval = 0; val->intval = 0;
@ -176,12 +182,16 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
switch (UCSI_CONSTAT(con, PWR_OPMODE)) { switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
case UCSI_CONSTAT_PWR_OPMODE_PD: case UCSI_CONSTAT_PWR_OPMODE_PD:
if (con->num_pdos > 0) { for (int i = 0; i < con->num_pdos; i++) {
pdo = con->src_pdos[con->num_pdos - 1]; int pdo_current = 0;
val->intval = pdo_max_current(pdo) * 1000;
} else { pdo = con->src_pdos[i];
val->intval = 0; 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; break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
val->intval = UCSI_TYPEC_1_5_CURRENT * 1000; val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
@ -202,10 +212,28 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
static int ucsi_psy_get_current_now(struct ucsi_connector *con, static int ucsi_psy_get_current_now(struct ucsi_connector *con,
union power_supply_propval *val) union power_supply_propval *val)
{ {
if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) if (!UCSI_CONSTAT(con, CONNECTED)) {
val->intval = rdo_op_current(con->rdo) * 1000;
else
val->intval = 0; 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; return 0;
} }

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

View file

@ -13,6 +13,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb/typec_dp.h> #include <linux/usb/typec_dp.h>
#include <linux/usb/typec_tbt.h>
#include "ucsi.h" #include "ucsi.h"
#include "trace.h" #include "trace.h"
@ -314,6 +315,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
{ {
const struct typec_altmode *altmode = NULL; const struct typec_altmode *altmode = NULL;
u64 command; u64 command;
u16 svid = 0;
int ret; int ret;
u8 cur; u8 cur;
int i; int i;
@ -335,6 +337,10 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
for (i = 0; con->partner_altmode[i]; i++) for (i = 0; con->partner_altmode[i]; i++)
typec_altmode_update_active(con->partner_altmode[i], typec_altmode_update_active(con->partner_altmode[i],
con->partner_altmode[i] == altmode); 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) 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, alt = ucsi_register_displayport(con, override,
i, desc); i, desc);
break; break;
case USB_TYPEC_TBT_SID:
alt = ucsi_register_thunderbolt(con, override, i, desc);
break;
default: default:
alt = typec_port_register_altmode(con->port, desc); alt = typec_port_register_altmode(con->port, desc);
break; break;
@ -609,6 +618,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
desc.vdo = alt[j].mid; desc.vdo = alt[j].mid;
desc.svid = alt[j].svid; desc.svid = alt[j].svid;
desc.roles = TYPEC_PORT_DRD; 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); ret = ucsi_register_altmode(con, &desc, recipient);
if (ret) if (ret)
@ -640,12 +651,15 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
} }
while (adev[i]) { while (adev[i]) {
if (recipient == UCSI_RECIPIENT_SOP && 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))) {
pdev = typec_altmode_get_partner(adev[i]); pdev = typec_altmode_get_partner(adev[i]);
ucsi_displayport_remove_partner((void *)pdev);
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]); typec_unregister_altmode(adev[i]);
adev[i++] = NULL; adev[i++] = NULL;
@ -831,6 +845,8 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
if (con->partner_altmode[0]) { if (con->partner_altmode[0]) {
num_partner_am = ucsi_get_num_altmode(con->partner_altmode); num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
typec_partner_set_num_altmodes(con->partner, num_partner_am); 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); ucsi_altmode_update_active(con);
return 0; return 0;
} else { } else {
@ -1119,6 +1135,8 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
return; return;
typec_set_mode(con->port, TYPEC_STATE_SAFE); 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); typec_partner_set_usb_power_delivery(con->partner, NULL);
ucsi_unregister_partner_pdos(con); 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)) { if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) {
ucsi_partner_change(con); ucsi_partner_change(con);
ucsi_altmode_update_active(con);
/* Complete pending data role swap */ /* Complete pending data role swap */
if (!completion_done(&con->complete)) 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->driver_data = con;
cap->ops = &ucsi_ops; cap->ops = &ucsi_ops;
cap->no_mode_control = !(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE);
if (ucsi->version >= UCSI_VERSION_2_0) if (ucsi->version >= UCSI_VERSION_2_0)
con->typec_cap.orientation_aware = true; con->typec_cap.orientation_aware = true;

View file

@ -70,6 +70,8 @@ struct dentry;
* @update_altmodes: Squashes duplicate DP altmodes * @update_altmodes: Squashes duplicate DP altmodes
* @update_connector: Update connector capabilities before registering * @update_connector: Update connector capabilities before registering
* @connector_status: Updates connector status, called holding connector lock * @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 * 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 * Command Completion Event from the PPM before returning, and @async_write must
@ -88,6 +90,8 @@ struct ucsi_operations {
struct ucsi_altmode *updated); struct ucsi_altmode *updated);
void (*update_connector)(struct ucsi_connector *con); void (*update_connector)(struct ucsi_connector *con);
void (*connector_status)(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); 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) { } ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */ #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 #ifdef CONFIG_DEBUG_FS
void ucsi_debugfs_init(void); void ucsi_debugfs_init(void);
void ucsi_debugfs_exit(void); void ucsi_debugfs_exit(void);

View file

@ -55,8 +55,8 @@ void stub_complete(struct urb *urb)
"stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
return; return;
case -ECONNRESET: case -ECONNRESET:
dev_info(&urb->dev->dev, dev_dbg(&urb->dev->dev,
"unlinked by a call to usb_unlink_urb()\n"); "unlinked by a call to usb_unlink_urb()\n");
break; break;
case -EPIPE: case -EPIPE:
dev_info(&urb->dev->dev, "endpoint %d is stalled\n", dev_info(&urb->dev->dev, "endpoint %d is stalled\n",

View file

@ -1295,8 +1295,7 @@ struct usb_driver {
* resume and suspend functions will be called in addition to the driver's * 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. * own, so this part of the setup does not need to be replicated.
* *
* USB drivers must provide all the fields listed above except driver, * USB device drivers must provide a name, other driver fields are optional.
* match, and id_table.
*/ */
struct usb_device_driver { struct usb_device_driver {
const char *name; const char *name;

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