USB / Thunderbolt changes for 7.1-rc1

Here is the big set of USB and Thunderbolt changes for 7.1-rc1.
 
 Lots of little things in here, nothing major, just constant
 improvements, updates, and new features.  Highlights are:
   - new USB power supply driver support (will cause merge conflicts in
     drivers/power/supply/Makefile, but it's a simple fix)  These changes
     did touch outside of drivers/usb/ but got acks from the relevant
     mantainers for them.
   - dts file updates and conversions
   - string function conversions into "safer" ones
   - new device quirks
   - xhci driver updates
   - usb gadget driver minor fixes
   - typec driver additions and updates
   - small number of thunderbolt driver changes
   - dwc3 driver updates and additions of new hardware support
   - other minor driver updates
 
 All of these have been in the linux-next tree for a while with no
 reported issues
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCaeSx6g8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynqjQCgqhbj0Pg2DiL+hZ4xEVlsKD8MJsMAn0vbdsR5
 UiYztWABA245P1hO9i+K
 =/fxh
 -----END PGP SIGNATURE-----

Merge tag 'usb-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB / Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt changes for 7.1-rc1.

  Lots of little things in here, nothing major, just constant
  improvements, updates, and new features. Highlights are:

   - new USB power supply driver support.

     These changes did touch outside of drivers/usb/ but got acks from
     the relevant mantainers for them.

   - dts file updates and conversions

   - string function conversions into "safer" ones

   - new device quirks

   - xhci driver updates

   - usb gadget driver minor fixes

   - typec driver additions and updates

   - small number of thunderbolt driver changes

   - dwc3 driver updates and additions of new hardware support

   - other minor driver updates

  All of these have been in the linux-next tree for a while with no
  reported issues"

* tag 'usb-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (176 commits)
  usb: dwc3: starfive: Add JHB100 USB 2.0 DRD controller
  dt-bindings: usb: dwc3: add support for StarFive JHB100
  dt-bindings: usb: atmel,at91sam9rl-udc: convert to DT schema
  dt-bindings: usb: atmel,at91rm9200-udc: convert to DT schema
  dt-bindings: usb: generic-ehci: fix schema structure and add at91sam9g45 constraints
  dt-bindings: usb: generic-ohci: add AT91RM9200 OHCI binding support
  arm: dts: at91: remove unused #address-cells/#size-cells from sam9x60 udc node
  drivers/usb/host: Fix spelling error 'seperate' -> 'separate'
  usbip: tools: add hint when no exported devices are found
  USB: serial: iuu_phoenix: fix iuutool author name
  usb: gadget: f_ncm: validate minimum block_len in ncm_unwrap_ntb()
  usb: gadget: f_phonet: fix skb frags[] overflow in pn_rx_complete()
  usb: gadget: f_hid: Add missing error code
  usb: typec: cros_ec_ucsi: Load driver from OF and ACPI definitions
  dt-bindings: chrome: Add cros-ec-ucsi compatibility to typec binding
  USB: of: Simplify with scoped for each OF child loop
  usbip: validate number_of_packets in usbip_pack_ret_submit()
  usb: gadget: renesas_usb3: validate endpoint index in standard request handlers
  usb: core: config: reverse the size check of the SSP isoc endpoint descriptor
  usb: typec: ucsi: Set usb mode on partner change
  ...
This commit is contained in:
Linus Torvalds 2026-04-19 08:47:40 -07:00
commit 99ef60d119
134 changed files with 4767 additions and 1385 deletions

View File

@ -675,7 +675,8 @@ Description:
Valid values:
"Unknown", "SDP", "DCP", "CDP", "ACA", "C", "PD",
"PD_DRP", "PD_PPS", "BrickID"
"PD_DRP", "PD_PPS", "BrickID", "PD_SPR_AVS",
"PD_PPS_SPR_AVS"
**Device Specific Properties**

View File

@ -8,17 +8,28 @@ title: Google Chrome OS EC(Embedded Controller) Type C port driver.
maintainers:
- Benson Leung <bleung@chromium.org>
- Prashant Malani <pmalani@chromium.org>
- Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
- Andrei Kuchynski <akuchynski@chromium.org>
- Łukasz Bartosik <ukaszb@chromium.org>
- Jameson Thies <jthies@google.com>
description:
Chrome OS devices have an Embedded Controller(EC) which has access to
Type C port state. This node is intended to allow the host to read and
control the Type C ports. The node for this device should be under a
cros-ec node like google,cros-ec-spi.
control the Type C ports. This binding is compatible with both the
cros-ec-typec and cros-ec-ucsi drivers. The cros-ec-typec driver
supports the host command interface used by the Chrome OS EC with a
built-in Type-C port manager and external Type-C Port Controller
(TCPC). The cros-ec-ucsi driver supports the USB Type-C Connector
System Software (UCSI) interface used by the Chrome OS EC when the
platform has a separate power delivery controller (PDC). The node for
this device should be under a cros-ec node like google,cros-ec-spi.
properties:
compatible:
const: google,cros-ec-typec
enum:
- google,cros-ec-typec
- google,cros-ec-ucsi
'#address-cells':
const: 1

View File

@ -300,6 +300,40 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint8-array
maxItems: 4
sink-load-step:
description: Indicates the preferred load step slew rate in mA/usec for
the port (in sink mode). This property is defined in "6.5.13.7" of
"USB Power Delivery Specification Revision 3.1 Version 1.8".
$ref: /schemas/types.yaml#/definitions/uint32
enum: [150, 500]
default: 150
sink-load-characteristics:
description: Indicates the port's (in sink mode) preferred load
characteristics. Users can leverage SINK_LOAD_CHAR() defined in
dt-bindings/usb/pd.h to populate this field. This property is defined in
"6.5.13.8" of "USB Power Delivery Specification Revision 3.1 Version 1.8".
$ref: /schemas/types.yaml#/definitions/uint16
sink-compliance:
description: Represents the types of sources the sink device has been tested
and certified with. This property is defined in "6.5.13.9" of
"USB Power Delivery Specification Revision 3.1 Version 1.8"
Bit 0 when set indicates it has been tested on LPS compliant source
Bit 1 when set indicates it has been tested on PS1 compliant source
Bit 2 when set indicates it has been tested on PS2 compliant source
$ref: /schemas/types.yaml#/definitions/uint8
maximum: 7
charging-adapter-pdp-milliwatt:
description: This corresponds to the Power Delivery Profile rating of the
charging adapter shipped or recommended for use with the connector port.
This property is a requirement to infer the USB PD property
"SPR Sink Operational PDP" given in "6.5.13.14" of
"USB Power Delivery Specification Revision 3.1 Version 1.8".
minimum: 0
maximum: 100000
dependencies:
pd-disable: [typec-power-opmode]
sink-vdos-v1: [ sink-vdos ]
@ -331,8 +365,9 @@ $defs:
"Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3
Sink Capabilities Message, the order of each entry(PDO) should follow the
PD spec chapter 6.4.1. Required for power sink and power dual role. User
can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined
in dt-bindings/usb/pd.h.
can specify the sink PDO array via
PDO_FIXED/BATT/VAR/PPS_APDO/SPR_AVS_SNK_APDO() defined in
dt-bindings/usb/pd.h.
minItems: 1
maxItems: 7
$ref: /schemas/types.yaml#/definitions/uint32-array

View File

@ -16,6 +16,9 @@ description: |
The MAX77759 includes Battery Charger, Fuel Gauge, temperature sensors, USB
Type-C Port Controller (TCPC), NVMEM, and a GPIO expander.
allOf:
- $ref: /schemas/power/supply/power-supply.yaml#
properties:
compatible:
const: maxim,max77759
@ -37,12 +40,18 @@ properties:
nvmem-0:
$ref: /schemas/nvmem/maxim,max77759-nvmem.yaml
chgin-otg-regulator:
type: object
description: Provides Boost for sourcing VBUS.
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
required:
- compatible
- interrupts
- reg
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@ -59,6 +68,11 @@ examples:
interrupt-controller;
#interrupt-cells = <2>;
power-supplies = <&maxtcpci>;
chgin-otg-regulator {
regulator-name = "chgin-otg";
};
gpio {
compatible = "maxim,max77759-gpio";

View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/atmel,at91rm9200-udc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel AT91 USB Device Controller (UDC)
maintainers:
- Nicolas Ferre <nicolas.ferre@microchip.com>
- Alexandre Belloni <alexandre.belloni@bootlin.com>
description:
The Atmel AT91 USB Device Controller provides USB gadget (device-mode)
functionality on AT91 SoCs. It requires a peripheral clock and an AHB
clock for operation and may optionally control VBUS power through a GPIO.
properties:
compatible:
enum:
- atmel,at91rm9200-udc
- atmel,at91sam9260-udc
- atmel,at91sam9261-udc
- atmel,at91sam9263-udc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 2
clock-names:
items:
- const: pclk
- const: hclk
atmel,vbus-gpio:
description: GPIO used to enable or control VBUS power for the USB bus.
maxItems: 1
atmel,matrix:
$ref: /schemas/types.yaml#/definitions/phandle
description: Phandle to the Atmel bus matrix controller.
atmel,pullup-gpio:
description:
GPIO controlling the USB D+ pull-up resistor used to signal device
connection to the host.
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/at91.h>
#include <dt-bindings/gpio/gpio.h>
gadget@fffa4000 {
compatible = "atmel,at91rm9200-udc";
reg = <0xfffa4000 0x4000>;
interrupts = <11 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&udc_clk>, <&udpck>;
clock-names = "pclk", "hclk";
atmel,vbus-gpio = <&pioC 5 GPIO_ACTIVE_HIGH>;
};
...

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/atmel,at91sam9rl-udc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel High-Speed USB Device Controller (USBA)
maintainers:
- Nicolas Ferre <nicolas.ferre@microchip.com>
- Alexandre Belloni <alexandre.belloni@bootlin.com>
description:
The Atmel High-Speed USB Device Controller (USBA) provides USB 2.0
high-speed gadget functionality on several Atmel and Microchip SoCs.
The controller requires a peripheral clock and a host clock for operation
and may optionally use a GPIO to detect VBUS presence.
properties:
compatible:
oneOf:
- enum:
- atmel,at91sam9rl-udc
- atmel,at91sam9g45-udc
- atmel,sama5d3-udc
- items:
- const: microchip,lan9662-udc
- const: atmel,sama5d3-udc
- const: microchip,sam9x60-udc
reg:
maxItems: 2
interrupts:
maxItems: 1
clocks:
maxItems: 2
clock-names:
minItems: 2
maxItems: 2
items:
enum: [pclk, hclk]
atmel,vbus-gpio:
description: GPIO used to detect the presence of VBUS, indicating that
the USB cable is connected.
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/at91.h>
#include <dt-bindings/gpio/gpio.h>
gadget@fff78000 {
compatible = "atmel,at91sam9g45-udc";
reg = <0x00600000 0x80000
0xfff78000 0x400>;
interrupts = <27 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 27>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
clock-names = "pclk", "hclk";
atmel,vbus-gpio = <&pioC 15 GPIO_ACTIVE_HIGH>;
};
...

View File

@ -1,125 +0,0 @@
Atmel SOC USB controllers
OHCI
Required properties:
- compatible: Should be "atmel,at91rm9200-ohci" for USB controllers
used in host mode.
- reg: Address and length of the register set for the device
- interrupts: Should contain ohci interrupt
- clocks: Should reference the peripheral, host and system clocks
- clock-names: Should contain three strings
"ohci_clk" for the peripheral clock
"hclk" for the host clock
"uhpck" for the system clock
- num-ports: Number of ports.
- atmel,vbus-gpio: If present, specifies a gpio that needs to be
activated for the bus to be powered.
- atmel,oc-gpio: If present, specifies a gpio that needs to be
activated for the overcurrent detection.
usb0: ohci@500000 {
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00500000 0x100000>;
clocks = <&uhphs_clk>, <&uhphs_clk>, <&uhpck>;
clock-names = "ohci_clk", "hclk", "uhpck";
interrupts = <20 4>;
num-ports = <2>;
};
EHCI
Required properties:
- compatible: Should be "atmel,at91sam9g45-ehci" for USB controllers
used in host mode.
- reg: Address and length of the register set for the device
- interrupts: Should contain ehci interrupt
- clocks: Should reference the peripheral and the UTMI clocks
- clock-names: Should contain two strings
"ehci_clk" for the peripheral clock
"usb_clk" for the UTMI clock
Optional properties:
- phy_type : For multi port host USB controllers, should be one of
"utmi", or "hsic".
usb1: ehci@800000 {
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00800000 0x100000>;
interrupts = <22 4>;
clocks = <&utmi>, <&uhphs_clk>;
clock-names = "usb_clk", "ehci_clk";
};
AT91 USB device controller
Required properties:
- compatible: Should be one of the following
"atmel,at91rm9200-udc"
"atmel,at91sam9260-udc"
"atmel,at91sam9261-udc"
"atmel,at91sam9263-udc"
- reg: Address and length of the register set for the device
- interrupts: Should contain macb interrupt
- clocks: Should reference the peripheral and the AHB clocks
- clock-names: Should contain two strings
"pclk" for the peripheral clock
"hclk" for the AHB clock
Optional properties:
- atmel,vbus-gpio: If present, specifies a gpio that needs to be
activated for the bus to be powered.
usb1: gadget@fffa4000 {
compatible = "atmel,at91rm9200-udc";
reg = <0xfffa4000 0x4000>;
interrupts = <10 4>;
clocks = <&udc_clk>, <&udpck>;
clock-names = "pclk", "hclk";
atmel,vbus-gpio = <&pioC 5 0>;
};
Atmel High-Speed USB device controller
Required properties:
- compatible: Should be one of the following
"atmel,at91sam9rl-udc"
"atmel,at91sam9g45-udc"
"atmel,sama5d3-udc"
"microchip,sam9x60-udc"
"microchip,lan9662-udc"
For "microchip,lan9662-udc" the fallback "atmel,sama5d3-udc"
is required.
- reg: Address and length of the register set for the device
- interrupts: Should contain usba interrupt
- clocks: Should reference the peripheral and host clocks
- clock-names: Should contain two strings
"pclk" for the peripheral clock
"hclk" for the host clock
Deprecated property:
- ep childnode: To specify the number of endpoints and their properties.
Optional properties:
- atmel,vbus-gpio: If present, specifies a gpio that allows to detect whether
vbus is present (USB is connected).
Deprecated child node properties:
- name: Name of the endpoint.
- reg: Num of the endpoint.
- atmel,fifo-size: Size of the fifo.
- atmel,nb-banks: Number of banks.
- atmel,can-dma: Boolean to specify if the endpoint support DMA.
- atmel,can-isoc: Boolean to specify if the endpoint support ISOC.
usb2: gadget@fff78000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "atmel,at91sam9rl-udc";
reg = <0x00600000 0x80000
0xfff78000 0x400>;
interrupts = <27 4 0>;
clocks = <&utmi>, <&udphs_clk>;
clock-names = "hclk", "pclk";
atmel,vbus-gpio = <&pioB 19 0>;
};

View File

@ -85,6 +85,7 @@ required:
allOf:
- $ref: usb-drd.yaml#
- $ref: usb-xhci.yaml#
unevaluatedProperties: false

View File

@ -0,0 +1,79 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/corechips,sl6341.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Corechips SL6341 USB 2.0/3.0 Hub Controller
maintainers:
- Alexey Charkov <alchark@flipper.net>
allOf:
- $ref: usb-hub.yaml#
properties:
compatible:
enum:
- usb3431,6241
- usb3431,6341
reg: true
peer-hub: true
reset-gpios:
description: GPIO controlling the RSTN pin.
vdd1v1-supply:
description:
The regulator that provides 1.1V core power to the hub.
vdd3v3-supply:
description:
The regulator that provides 3.3V IO 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
- vdd1v1-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
usb {
#address-cells = <1>;
#size-cells = <0>;
/* 2.0 hub */
hub_2_0: hub@1 {
compatible = "usb3431,6241";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
vdd1v1-supply = <&vdd1v1_hub>;
};
/* 3.0 hub */
hub_3_0: hub@2 {
compatible = "usb3431,6341";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
vdd1v1-supply = <&vdd1v1_hub>;
};
};

View File

@ -10,6 +10,8 @@ title: NXP iMX8MP Soc USB Controller
maintainers:
- Li Jun <jun.li@nxp.com>
deprecated: true
properties:
compatible:
oneOf:

View File

@ -9,19 +9,6 @@ title: USB EHCI Controller
maintainers:
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
allOf:
- $ref: usb-hcd.yaml
- if:
properties:
compatible:
not:
contains:
const: ibm,usb-ehci-440epx
then:
properties:
reg:
maxItems: 1
properties:
compatible:
oneOf:
@ -167,6 +154,39 @@ required:
- reg
- interrupts
allOf:
- $ref: usb-hcd.yaml
- if:
properties:
compatible:
not:
contains:
const: ibm,usb-ehci-440epx
then:
properties:
reg:
maxItems: 1
- if:
properties:
compatible:
contains:
const: atmel,at91sam9g45-ehci
then:
properties:
clock-names:
items:
- const: usb_clk
- const: ehci_clk
phy_type:
enum:
- utmi
- hsic
required:
- clocks
- clock-names
unevaluatedProperties: false
examples:

View File

@ -55,6 +55,7 @@ properties:
- ti,ohci-omap3
- items:
- enum:
- atmel,at91rm9200-ohci
- cavium,octeon-6335-ohci
- nintendo,hollywood-usb-ohci
- nxp,ohci-nxp
@ -137,6 +138,24 @@ properties:
The associated ISP1301 device. Necessary for the UDC controller for
connecting to the USB physical layer.
atmel,vbus-gpio:
description:
GPIO used to control or sense the USB VBUS power. Each entry
represents a VBUS-related GPIO; count and order may vary by hardware.
Entries follow standard GPIO specifier format. A value of 0 indicates
an unused or unavailable VBUS signal.
minItems: 1
maxItems: 3
atmel,oc-gpio:
description:
GPIO used to signal USB overcurrent condition. Each entry represents
an OC detection GPIO; count and order may vary by hardware. Entries
follow standard GPIO specifier format. A value of 0 indicates an
unused or unavailable OC signal.
minItems: 1
maxItems: 3
required:
- compatible
- reg
@ -144,6 +163,28 @@ required:
allOf:
- $ref: usb-hcd.yaml
- if:
properties:
compatible:
contains:
const: atmel,at91rm9200-ohci
then:
properties:
clock-names:
items:
- const: ohci_clk
- const: hclk
- const: uhpck
required:
- clocks
- clock-names
else:
properties:
atmel,vbus-gpio: false
atmel,oc-gpio: false
- if:
not:
properties:

View File

@ -32,6 +32,9 @@ properties:
description:
Properties for usb c connector.
vbus-supply:
description: Regulator to control sourcing Vbus.
required:
- compatible
- reg
@ -53,6 +56,7 @@ examples:
reg = <0x25>;
interrupt-parent = <&gpa8>;
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
vbus-supply = <&chgin_otg_reg>;
connector {
compatible = "usb-c-connector";
@ -75,6 +79,10 @@ examples:
PDO_FIXED(9000, 2000, 0)>;
sink-bc12-completion-time-ms = <500>;
pd-revision = /bits/ 8 <0x03 0x01 0x01 0x08>;
sink-load-step = <150>;
sink-load-characteristics = /bits/ 16 <SINK_LOAD_CHAR(0, 1, 1, 2)>;
sink-compliance = /bits/ 8 <(COMPLIANCE_LPS | COMPLIANCE_PS1)>;
charging-adapter-pdp-milliwatt = <18000>;
};
};
};

View File

@ -1,23 +0,0 @@
Maxim Integrated SPI-based USB 2.0 host controller MAX3421E
Required properties:
- compatible: Should be "maxim,max3421"
- spi-max-frequency: maximum frequency for this device must not exceed 26 MHz.
- reg: chip select number to which this device is connected.
- maxim,vbus-en-pin: <GPOUTx ACTIVE_LEVEL>
GPOUTx is the number (1-8) of the GPOUT pin of MAX3421E to drive Vbus.
ACTIVE_LEVEL is 0 or 1.
- interrupts: the interrupt line description for the interrupt controller.
The driver configures MAX3421E for active low level triggered interrupts,
configure your interrupt line accordingly.
Example:
usb@0 {
compatible = "maxim,max3421";
reg = <0>;
maxim,vbus-en-pin = <3 1>;
spi-max-frequency = <26000000>;
interrupt-parent = <&PIC>;
interrupts = <42>;
};

View File

@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/maxim,max3421.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MAXIM MAX3421e USB Peripheral/Host Controller
maintainers:
- David Mosberger <davidm@egauge.net>
description: |
The controller provides USB2.0 compliant with Full Speed or Low Speed when in
the host mode. At peripheral, it operates at Full Speed. At both cases, it
uses a SPI interface.
Datasheet at:
https://www.analog.com/media/en/technical-documentation/data-sheets/max3421e.pdf
properties:
compatible:
const: maxim,max3421
reg:
maxItems: 1
interrupts:
maxItems: 1
spi-max-frequency:
maximum: 26000000
maxim,vbus-en-pin:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
One of eight GPOUT pins to control external VBUS power and the polarity
of the active level. It's an array of GPIO number and the active level of it.
minItems: 2
maxItems: 2
required:
- compatible
- reg
- interrupts
- maxim,vbus-en-pin
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
usb@0 {
compatible = "maxim,max3421";
reg = <0>;
maxim,vbus-en-pin = <3 1>;
spi-max-frequency = <26000000>;
interrupt-parent = <&gpio>;
interrupts = <42>;
};
};

View File

@ -37,6 +37,9 @@ properties:
clocks:
maxItems: 1
resets:
maxItems: 1
microchip,ext-vbus-drv:
description:
Some ULPI USB PHYs do not support an internal VBUS supply and driving

View File

@ -0,0 +1,123 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2026 NXP
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/nxp,imx-dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP i.MX Soc USB Controller
maintainers:
- Xu Yang <xu.yang_2@nxp.com>
properties:
compatible:
oneOf:
- items:
- enum:
- nxp,imx94-dwc3
- nxp,imx95-dwc3
- const: nxp,imx8mp-dwc3
- const: nxp,imx8mp-dwc3
reg:
items:
- description: DWC3 core registers
- description: HSIO Block Control registers
- description: Wrapper registers of dwc3 core
reg-names:
items:
- const: core
- const: blkctl
- const: glue
interrupts:
items:
- description: DWC3 controller interrupt
- description: Wakeup interrupt from glue logic
interrupt-names:
items:
- const: dwc_usb3
- const: wakeup
iommus:
maxItems: 1
clocks:
items:
- description: System hsio root clock
- description: SoC Bus Clock for AHB/AXI/Native
- description: Reference clock for generating ITP when UTMI/ULPI PHY is suspended
- description: Suspend clock used for usb wakeup logic
clock-names:
items:
- const: hsio
- const: bus_early
- const: ref
- const: suspend
fsl,permanently-attached:
type: boolean
description:
Indicates if the device attached to a downstream port is
permanently attached
fsl,disable-port-power-control:
type: boolean
description:
Indicates whether the host controller implementation includes port
power control. Defines Bit 3 in capability register (HCCPARAMS)
fsl,over-current-active-low:
type: boolean
description:
Over current signal polarity is active low
fsl,power-active-low:
type: boolean
description:
Power pad (PWR) polarity is active low
power-domains:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- power-domains
allOf:
- $ref: snps,dwc3-common.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb@4c100000 {
compatible = "nxp,imx94-dwc3", "nxp,imx8mp-dwc3";
reg = <0x4c100000 0x10000>,
<0x4c010010 0x04>,
<0x4c1f0000 0x20>;
reg-names = "core", "blkctl", "glue";
clocks = <&scmi_clk 74>, //IMX94_CLK_HSIO
<&scmi_clk 74>, //IMX94_CLK_HSIO
<&scmi_clk 2>, //IMX94_CLK_24M
<&scmi_clk 1>; //IMX94_CLK_32K
clock-names = "hsio", "bus_early", "ref", "suspend";
interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 386 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "dwc_usb3", "wakeup";
power-domains = <&scmi_devpd 13>; //IMX94_PD_HSIO_TOP
phys = <&usb3_phy>, <&usb3_phy>;
phy-names = "usb2-phy", "usb3-phy";
snps,gfladj-refclk-lpm-sel-quirk;
snps,parkmode-disable-ss-quirk;
};

View File

@ -26,6 +26,10 @@ properties:
$ref: /schemas/connector/usb-connector.yaml#
unevaluatedProperties: false
orientation-gpios:
maxItems: 1
description: Optional orientation select control
required:
- compatible
- reg

View File

@ -1,36 +0,0 @@
ST USB OHCI controller
Required properties:
- compatible : must be "st,st-ohci-300x"
- reg : physical base addresses of the controller and length of memory mapped
region
- interrupts : one OHCI controller interrupt should be described here
- clocks : phandle list of usb clocks
- clock-names : should be "ic" for interconnect clock and "clk48"
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
- phys : phandle for the PHY device
- phy-names : should be "usb"
- resets : phandle to the powerdown and reset controller for the USB IP
- reset-names : should be "power" and "softreset".
See: Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml
See: Documentation/devicetree/bindings/reset/reset.txt
Example:
ohci0: usb@fe1ffc00 {
compatible = "st,st-ohci-300x";
reg = <0xfe1ffc00 0x100>;
interrupts = <GIC_SPI 149 IRQ_TYPE_NONE>;
clocks = <&clk_s_a1_ls 0>,
<&clockgen_b0 0>;
clock-names = "ic", "clk48";
phys = <&usb2_phy>;
phy-names = "usb";
resets = <&powerdown STIH416_USB0_POWERDOWN>,
<&softreset STIH416_USB0_SOFTRESET>;
reset-names = "power", "softreset";
};

View File

@ -1,80 +0,0 @@
OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
OMAP MUSB GLUE
- compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
- ti,hwmods : must be "usb_otg_hs"
- multipoint : Should be "1" indicating the musb controller supports
multipoint. This is a MUSB configuration-specific setting.
- num-eps : Specifies the number of endpoints. This is also a
MUSB configuration-specific setting. Should be set to "16"
- ram-bits : Specifies the ram address size. Should be set to "12"
- interface-type : This is a board specific setting to describe the type of
interface between the controller and the phy. It should be "0" or "1"
specifying ULPI and UTMI respectively.
- mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
represents PERIPHERAL.
- power : Should be "50". This signifies the controller can supply up to
100mA when operating in host mode.
- usb-phy : the phandle for the PHY device
- phys : the phandle for the PHY device (used by generic PHY framework)
- phy-names : the names of the PHY corresponding to the PHYs present in the
*phy* phandle.
Optional properties:
- ctrl-module : phandle of the control module this glue uses to write to
mailbox
SOC specific device node entry
usb_otg_hs: usb_otg_hs@4a0ab000 {
compatible = "ti,omap4-musb";
ti,hwmods = "usb_otg_hs";
multipoint = <1>;
num-eps = <16>;
ram-bits = <12>;
ctrl-module = <&omap_control_usb>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
};
Board specific device node entry
&usb_otg_hs {
interface-type = <1>;
mode = <3>;
power = <50>;
};
OMAP DWC3 GLUE
- compatible : Should be
* "ti,dwc3" for OMAP5 and DRA7
* "ti,am437x-dwc3" for AM437x
- ti,hwmods : Should be "usb_otg_ss"
- reg : Address and length of the register set for the device.
- interrupts : The irq number of this device that is used to interrupt the
MPU
- #address-cells, #size-cells : Must be present if the device has sub-nodes
- utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID.
It should be set to "1" for HW mode and "2" for SW mode.
- ranges: the child address space are mapped 1:1 onto the parent address space
Optional Properties:
- extcon : phandle for the extcon device omap dwc3 uses to detect
connect/disconnect events.
- vbus-supply : phandle to the regulator device tree node if needed.
Sub-nodes:
The dwc3 core should be added as subnode to omap dwc3 glue.
- dwc3 :
The binding details of dwc3 can be found in:
Documentation/devicetree/bindings/usb/snps,dwc3.yaml
omap_dwc3 {
compatible = "ti,dwc3";
ti,hwmods = "usb_otg_ss";
reg = <0x4a020000 0x1ff>;
interrupts = <0 93 4>;
#address-cells = <1>;
#size-cells = <1>;
utmi-mode = <2>;
ranges;
};

View File

@ -24,6 +24,7 @@ properties:
compatible:
items:
- enum:
- qcom,eliza-dwc3
- qcom,glymur-dwc3
- qcom,glymur-dwc3-mp
- qcom,ipq4019-dwc3
@ -153,8 +154,6 @@ properties:
wakeup-source: true
# Required child node:
required:
- compatible
- reg
@ -175,6 +174,7 @@ allOf:
then:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
items:
@ -203,6 +203,7 @@ allOf:
compatible:
contains:
enum:
- qcom,ipq5424-dwc3
- qcom,ipq9574-dwc3
- qcom,kaanapali-dwc3
- qcom,msm8953-dwc3
@ -222,6 +223,7 @@ allOf:
then:
properties:
clocks:
minItems: 5
maxItems: 5
clock-names:
items:
@ -264,6 +266,7 @@ allOf:
then:
properties:
clocks:
minItems: 4
maxItems: 4
clock-names:
items:
@ -283,6 +286,7 @@ allOf:
then:
properties:
clocks:
minItems: 4
maxItems: 4
clock-names:
items:
@ -303,6 +307,7 @@ allOf:
then:
properties:
clocks:
minItems: 9
maxItems: 9
clock-names:
items:
@ -346,14 +351,17 @@ allOf:
compatible:
contains:
enum:
- qcom,eliza-dwc3
- qcom,milos-dwc3
- qcom,qcm2290-dwc3
- qcom,qcs615-dwc3
- qcom,sar2130p-dwc3
- qcom,sc8180x-dwc3
- qcom,sc8180x-dwc3-mp
- qcom,sm4250-dwc3
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3
- qcom,sm6375-dwc3
- qcom,sm8150-dwc3
- qcom,sm8250-dwc3
- qcom,sm8450-dwc3
@ -363,6 +371,7 @@ allOf:
properties:
clocks:
minItems: 6
maxItems: 6
clock-names:
items:
- const: cfg_noc
@ -404,6 +413,7 @@ allOf:
then:
properties:
clocks:
minItems: 7
maxItems: 7
clock-names:
items:
@ -446,6 +456,7 @@ allOf:
- qcom,msm8996-dwc3
- qcom,qcs404-dwc3
- qcom,sdm660-dwc3
- qcom,sm4250-dwc3
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3
then:
@ -472,6 +483,7 @@ allOf:
then:
properties:
interrupts:
minItems: 4
maxItems: 4
interrupt-names:
items:
@ -480,6 +492,26 @@ allOf:
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,ipq5424-dwc3
- qcom,ipq9574-dwc3
then:
properties:
interrupts:
minItems: 5
maxItems: 5
interrupt-names:
items:
- const: dwc_usb3
- const: pwr_event
- const: qusb2_phy
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- if:
properties:
compatible:
@ -500,13 +532,14 @@ allOf:
- const: pwr_event
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- const: ss_phy_irq
- enum: [hs_phy_irq, ss_phy_irq]
- if:
properties:
compatible:
contains:
enum:
- qcom,eliza-dwc3
- qcom,ipq4019-dwc3
- qcom,ipq8064-dwc3
- qcom,kaanapali-dwc3
@ -523,8 +556,8 @@ allOf:
- qcom,sdx55-dwc3
- qcom,sdx65-dwc3
- qcom,sdx75-dwc3
- qcom,sm4250-dwc3
- qcom,sm6350-dwc3
- qcom,sm6375-dwc3
- qcom,sm8150-dwc3
- qcom,sm8250-dwc3
- qcom,sm8350-dwc3

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/renesas,upd720201-pci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: UPD720201/UPD720202 USB 3.0 xHCI Host Controller (PCIe)
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
UPD720201 USB 3.0 xHCI Host Controller via PCIe x1 Gen2 interface.
The UPD720202 supports up to two downstream ports, while UPD720201
supports up to four downstream USB 3.0 rev1.0 ports.
properties:
compatible:
enum:
- pci1912,0014 # UPD720201
- pci1912,0015 # UPD720202
reg:
maxItems: 1
avdd33-supply:
description: +3.3 V power supply for analog circuit
vdd10-supply:
description: +1.05 V power supply
vdd33-supply:
description: +3.3 V power supply
required:
- compatible
- reg
- avdd33-supply
- vdd10-supply
- vdd33-supply
allOf:
- $ref: usb-xhci.yaml
additionalProperties: true
examples:
- |
pcie@0 {
reg = <0x0 0x1000>;
ranges = <0x02000000 0x0 0x100000 0x10000000 0x0 0x0>;
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
usb-controller@0 {
compatible = "pci1912,0014";
reg = <0x0 0x0 0x0 0x0 0x0>;
avdd33-supply = <&avdd33_reg>;
vdd10-supply = <&vdd10_reg>;
vdd33-supply = <&vdd33_reg>;
};
};

View File

@ -18,11 +18,21 @@ description: |
properties:
compatible:
enum:
- richtek,rt1711h
- richtek,rt1715
oneOf:
- enum:
- richtek,rt1711h
- richtek,rt1715
- items:
- enum:
- hynetek,husb311
- const: richtek,rt1711h
- items:
- enum:
- etekmicro,et7304
- const: richtek,rt1715
description:
RT1711H support PD20, RT1715 support PD30 except Fast Role Swap.
RT1711H support PD20, ET7304 and RT1715 support PD30 except Fast Role Swap.
HUSB311 is a rebrand of RT1711H which is pin and register compatible.
reg:
maxItems: 1

View File

@ -27,7 +27,9 @@ allOf:
properties:
compatible:
const: spacemit,k1-dwc3
enum:
- spacemit,k1-dwc3
- spacemit,k3-dwc3
reg:
maxItems: 1
@ -42,11 +44,13 @@ properties:
maxItems: 1
phys:
minItems: 1
items:
- description: phandle to USB2/HS PHY
- description: phandle to USB3/SS PHY
phy-names:
minItems: 1
items:
- const: usb2-phy
- const: usb3-phy

View File

@ -0,0 +1,85 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/st,st-ohci-300x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics USB OHCI Controller
maintainers:
- Peter Griffin <peter.griffin@linaro.org>
description:
The STMicroelectronics USB Open Host Controller Interface (OHCI)
compliant USB host controller found in ST platforms. The controller
provides full- and low-speed USB host functionality and interfaces
with an external USB PHY. It requires dedicated clock, reset, and
interrupt resources for proper operation.
allOf:
- $ref: /schemas/usb/usb-hcd.yaml#
properties:
compatible:
const: st,st-ohci-300x
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 2
clock-names:
items:
- const: ic
- const: clk48
phys:
maxItems: 1
phy-names:
items:
- const: usb
resets:
maxItems: 2
reset-names:
items:
- const: power
- const: softreset
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- phys
- phy-names
- resets
- reset-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/reset/stih407-resets.h>
usb@fe1ffc00 {
compatible = "st,st-ohci-300x";
reg = <0xfe1ffc00 0x100>;
interrupts = <GIC_SPI 149 IRQ_TYPE_NONE>;
clocks = <&clk_s_a1_ls 0>,
<&clockgen_b0 0>;
clock-names = "ic", "clk48";
phys = <&usb2_phy>;
phy-names = "usb";
resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>,
<&softreset STIH407_USB2_PORT0_SOFTRESET>;
reset-names = "power", "softreset";
};
...

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/starfive,jhb100-dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: StarFive JHB100 DWC3 USB SoC Controller
maintainers:
- Minda Chen <minda.chen@starfivetech.com>
description:
The USB DRD controller on JHB100 BMC SoC.
allOf:
- $ref: snps,dwc3-common.yaml#
properties:
compatible:
const: starfive,jhb100-dwc3
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: USB main enable clk
- description: DWC3 bus early clock
- description: DWC3 ref clock
clock-names:
items:
- const: main
- const: bus_early
- const: ref
resets:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
unevaluatedProperties: false
examples:
- |
usb@11800000 {
compatible = "starfive,jhb100-dwc3";
reg = <0x11800000 0x10000>;
clocks = <&usbcrg 9>,
<&usbcrg 5>,
<&usbcrg 6>;
clock-names = "main", "bus_early", "ref";
resets = <&usbcrg 4>;
interrupts = <105>;
dr_mode = "host";
};

View File

@ -0,0 +1,62 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/terminus,fe11.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Terminus FE1.1/1.1S USB 2.0 Hub Controller
maintainers:
- Yixun Lan <dlan@kernel.org>
allOf:
- $ref: usb-hub.yaml#
properties:
compatible:
enum:
- usb1a40,0101
reg: true
reset-gpios:
description:
GPIO controlling the RESET#.
vdd-supply:
description:
Regulator supply to the hub, one of 3.3V or 5V can be chosen.
ports:
$ref: /schemas/graph.yaml#/properties/ports
patternProperties:
'^port@':
$ref: /schemas/graph.yaml#/properties/port
properties:
reg:
minimum: 1
maximum: 4
required:
- compatible
- reg
- vdd-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
usb {
#address-cells = <1>;
#size-cells = <0>;
hub@1 {
compatible = "usb1a40,0101";
reg = <1>;
reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
vdd-supply = <&vcc_5v>;
};
};

View File

@ -0,0 +1,100 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments OMAP DWC3 USB Glue Layer
maintainers:
- Felipe Balbi <balbi@ti.com>
description:
Texas Instruments glue layer for Synopsys DesignWare USB3 (DWC3)
controller on OMAP and AM43xx SoCs. Manages SoC-specific integration
including register mapping, interrupt routing, UTMI/PIPE interface mode
selection (HW/SW), and child DWC3 core instantiation via address space
translation. Supports both legacy single-instance and multi-instance
(numbered) configurations.
properties:
compatible:
enum:
- ti,dwc3
- ti,am437x-dwc3
reg:
maxItems: 1
interrupts:
maxItems: 1
utmi-mode:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Controls the source of UTMI/PIPE status for VBUS and OTG ID.
1 for HW mode, 2 for SW mode.
enum: [1, 2]
"#address-cells":
const: 1
"#size-cells":
const: 1
ranges: true
extcon:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle for the extcon device used to detect connect/
disconnect events.
vbus-supply:
description: Phandle to the regulator device tree node if needed.
patternProperties:
"^usb@[0-9a-f]+$":
type: object
$ref: snps,dwc3.yaml#
unevaluatedProperties: false
required:
- reg
- compatible
- interrupts
- "#address-cells"
- "#size-cells"
- utmi-mode
- ranges
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
omap_dwc3_1@0 {
compatible = "ti,dwc3";
reg = <0x0 0x10000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <1>;
utmi-mode = <2>;
ranges = <0 0 0x20000>;
usb@10000 {
compatible = "snps,dwc3";
reg = <0x10000 0x17000>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "peripheral", "host", "otg";
phys = <&usb2_phy1>, <&usb3_phy1>;
phy-names = "usb2-phy", "usb3-phy";
maximum-speed = "super-speed";
dr_mode = "otg";
snps,dis_u3_susphy_quirk;
snps,dis_u2_susphy_quirk;
};
};
...

View File

@ -0,0 +1,120 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,omap4-musb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments OMAP MUSB USB OTG Controller
maintainers:
- Felipe Balbi <balbi@ti.com>
description:
Texas Instruments glue layer for the Mentor Graphics MUSB OTG controller.
Handles SoC-specific integration including PHY interface bridging(ULPI/
UTMI), interrupt aggregation, DMA engine coordination (internal/
external), VBUS/session control via control module mailbox, and
clock/reset management. Provides fixed hardware configuration parameters
to the generic MUSB core driver.
properties:
compatible:
enum:
- ti,omap3-musb
- ti,omap4-musb
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 2
interrupt-names:
minItems: 1
items:
- const: mc
- const: dma
multipoint:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Indicates the MUSB controller supports multipoint. This is a MUSB
configuration-specific setting.
const: 1
num-eps:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Specifies the number of endpoints. This is a MUSB configuration
specific setting.
const: 16
ram-bits:
description: Specifies the RAM address size.
const: 12
interface-type:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Describes the type of interface between the controller and the PHY.
0 for ULPI, 1 for UTMI.
enum: [0, 1]
mode:
$ref: /schemas/types.yaml#/definitions/uint32
description: 1 for HOST, 2 for PERIPHERAL, 3 for OTG.
enum: [1, 2, 3]
power:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Indicates the maximum current the controller can supply when
operating in host mode. A value of 50 corresponds to 100 mA, and a
value of 150 corresponds to 300 mA.
enum: [50, 150]
phys:
maxItems: 1
phy-names:
const: usb2-phy
usb-phy:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: Phandle for the PHY device.
deprecated: true
ctrl-module:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle of the control module this glue uses to write to mailbox.
required:
- reg
- compatible
- interrupts
- interrupt-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb@4a0ab000 {
compatible = "ti,omap4-musb";
reg = <0x4a0ab000 0x1000>;
interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "mc", "dma";
multipoint = <1>;
num-eps = <16>;
ram-bits = <12>;
ctrl-module = <&omap_control_usb>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
interface-type = <1>;
mode = <3>;
power = <50>;
};
...

View File

@ -11,6 +11,7 @@ maintainers:
allOf:
- $ref: usb-device.yaml#
- $ref: usb-hub.yaml#
properties:
compatible:
@ -30,17 +31,20 @@ properties:
description:
VDD power supply to the hub
peer-hub:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to the peer hub on the controller.
peer-hub: true
patternProperties:
'^.*@[1-9a-f][0-9a-f]*$':
description: The hard wired USB devices
type: object
$ref: /schemas/usb/usb-device.yaml
additionalProperties: true
required:
- compatible
- reg
- peer-hub
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@ -56,7 +60,14 @@ examples:
compatible = "usb451,8142";
reg = <1>;
peer-hub = <&hub_3_0>;
#address-cells = <1>;
#size-cells = <0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
hub@1 {
compatible = "usb123,4567";
reg = <1>;
};
};
/* 3.0 hub on port 2 */

View File

@ -365,6 +365,8 @@ patternProperties:
description: CORERIVER Semiconductor Co.,Ltd.
"^corpro,.*":
description: Chengdu Corpro Technology Co., Ltd.
"^corechips,.*":
description: Shenzhen Corechips Microelectronics Co., Ltd.
"^cortina,.*":
description: Cortina Systems, Inc.
"^cosmic,.*":
@ -547,6 +549,8 @@ patternProperties:
description: ESTeem Wireless Modems
"^eswin,.*":
description: Beijing ESWIN Technology Group Co. Ltd.
"^etekmicro,.*":
description: Wuxi ETEK Micro-Electronics Co.,Ltd.
"^ettus,.*":
description: NI Ettus Research
"^eukrea,.*":
@ -751,6 +755,8 @@ patternProperties:
description: Hycon Technology Corp.
"^hydis,.*":
description: Hydis Technologies
"^hynetek,.*":
description: Hynetek Semiconductor Co., Ltd.
"^hynitron,.*":
description: Shanghai Hynitron Microelectronics Co. Ltd.
"^hynix,.*":

View File

@ -15816,6 +15816,12 @@ F: drivers/mfd/max77759.c
F: drivers/nvmem/max77759-nvmem.c
F: include/linux/mfd/max77759.h
MAXIM MAX77759 BATTERY CHARGER DRIVER
M: Amit Sunil Dhamne <amitsd@google.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/power/supply/max77759_charger.c
MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER
M: Javier Martinez Canillas <javier@dowhile0.org>
L: linux-kernel@vger.kernel.org
@ -25486,10 +25492,11 @@ F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml
F: drivers/reset/starfive/reset-starfive-jh71*
F: include/dt-bindings/reset/starfive?jh71*.h
STARFIVE JH71X0 USB DRIVERS
STARFIVE USB DRIVERS
M: Minda Chen <minda.chen@starfivetech.com>
S: Maintained
F: Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml
F: Documentation/devicetree/bindings/usb/starfive,jhb100-dwc3.yaml
F: drivers/usb/cdns3/cdns3-starfive.c
STARFIVE JH71XX PMU CONTROLLER DRIVER
@ -26536,7 +26543,7 @@ F: drivers/media/i2c/thp7312.c
F: include/uapi/linux/thp7312.h
THUNDERBOLT DMA TRAFFIC TEST DRIVER
M: Isaac Hazan <isaac.hazan@intel.com>
M: Mika Westerberg <westeri@kernel.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/thunderbolt/dma_test.c

View File

@ -201,8 +201,24 @@ static const struct regmap_config max77759_regmap_config_charger = {
* - SYSUVLO_INT
* - FSHIP_NOT_RD
* - CHGR_INT: charger
* - CHG_INT
* - CHG_INT2
* - INT1
* - AICL
* - CHGIN
* - WCIN
* - CHG
* - BAT
* - INLIM
* - THM2
* - BYP
* - INT2
* - INSEL
* - SYS_UVLO1
* - SYS_UVLO2
* - BAT_OILO
* - CHG_STA_CC
* - CHG_STA_CV
* - CHG_STA_TO
* - CHG_STA_DONE
*/
enum {
MAX77759_INT_MAXQ,
@ -228,8 +244,22 @@ enum {
};
enum {
MAX77759_CHARGER_INT_1,
MAX77759_CHARGER_INT_2,
MAX77759_CHGR_INT1_AICL,
MAX77759_CHGR_INT1_CHGIN,
MAX77759_CHGR_INT1_WCIN,
MAX77759_CHGR_INT1_CHG,
MAX77759_CHGR_INT1_BAT,
MAX77759_CHGR_INT1_INLIM,
MAX77759_CHGR_INT1_THM2,
MAX77759_CHGR_INT1_BYP,
MAX77759_CHGR_INT2_INSEL,
MAX77759_CHGR_INT2_SYS_UVLO1,
MAX77759_CHGR_INT2_SYS_UVLO2,
MAX77759_CHGR_INT2_BAT_OILO,
MAX77759_CHGR_INT2_CHG_STA_CC,
MAX77759_CHGR_INT2_CHG_STA_CV,
MAX77759_CHGR_INT2_CHG_STA_TO,
MAX77759_CHGR_INT2_CHG_STA_DONE,
};
static const struct regmap_irq max77759_pmic_irqs[] = {
@ -256,8 +286,38 @@ static const struct regmap_irq max77759_topsys_irqs[] = {
};
static const struct regmap_irq max77759_chgr_irqs[] = {
REGMAP_IRQ_REG(MAX77759_CHARGER_INT_1, 0, GENMASK(7, 0)),
REGMAP_IRQ_REG(MAX77759_CHARGER_INT_2, 1, GENMASK(7, 0)),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_AICL, 0,
MAX77759_CHGR_REG_CHG_INT_AICL),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHGIN, 0,
MAX77759_CHGR_REG_CHG_INT_CHGIN),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_WCIN, 0,
MAX77759_CHGR_REG_CHG_INT_WCIN),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHG, 0,
MAX77759_CHGR_REG_CHG_INT_CHG),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BAT, 0,
MAX77759_CHGR_REG_CHG_INT_BAT),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_INLIM, 0,
MAX77759_CHGR_REG_CHG_INT_INLIM),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_THM2, 0,
MAX77759_CHGR_REG_CHG_INT_THM2),
REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BYP, 0,
MAX77759_CHGR_REG_CHG_INT_BYP),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_INSEL, 1,
MAX77759_CHGR_REG_CHG_INT2_INSEL),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO1, 1,
MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO2, 1,
MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_BAT_OILO, 1,
MAX77759_CHGR_REG_CHG_INT2_BAT_OILO),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CC, 1,
MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CV, 1,
MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_TO, 1,
MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO),
REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_DONE, 1,
MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE),
};
static const struct regmap_irq_chip max77759_pmic_irq_chip = {
@ -297,11 +357,12 @@ static const struct regmap_irq_chip max77759_topsys_irq_chip = {
.num_irqs = ARRAY_SIZE(max77759_topsys_irqs),
};
static const struct regmap_irq_chip max77759_chrg_irq_chip = {
static const struct regmap_irq_chip max77759_chgr_irq_chip = {
.name = "max77759-chgr",
.domain_suffix = "CHGR",
.status_base = MAX77759_CHGR_REG_CHG_INT,
.mask_base = MAX77759_CHGR_REG_CHG_INT_MASK,
.ack_base = MAX77759_CHGR_REG_CHG_INT,
.num_regs = 2,
.irqs = max77759_chgr_irqs,
.num_irqs = ARRAY_SIZE(max77759_chgr_irqs),
@ -325,8 +386,22 @@ static const struct resource max77759_gpio_resources[] = {
};
static const struct resource max77759_charger_resources[] = {
DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_1, "INT1"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_2, "INT2"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_AICL, "AICL"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHGIN, "CHGIN"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_WCIN, "WCIN"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHG, "CHG"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BAT, "BAT"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_INLIM, "INLIM"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_THM2, "THM2"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BYP, "BYP"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_INSEL, "INSEL"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO1, "SYS_UVLO1"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO2, "SYS_UVLO2"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_BAT_OILO, "BAT_OILO"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CC, "CHG_STA_CC"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CV, "CHG_STA_CV"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_TO, "CHG_STA_TO"),
DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_DONE, "CHG_STA_DONE"),
};
static const struct mfd_cell max77759_cells[] = {
@ -567,7 +642,7 @@ static int max77759_add_chained_charger(struct i2c_client *client,
max77759->regmap_charger,
MAX77759_INT_CHGR,
parent,
&max77759_chrg_irq_chip,
&max77759_chgr_irq_chip,
&irq_chip_data);
if (ret)
return ret;

View File

@ -642,6 +642,17 @@ config CHARGER_MAX77705
help
Say Y to enable support for the Maxim MAX77705 battery charger.
config CHARGER_MAX77759
tristate "Maxim MAX77759 battery charger driver"
depends on MFD_MAX77759 && REGULATOR
default MFD_MAX77759
help
Say M or Y here to enable the MAX77759 battery charger. MAX77759
charger is a function of the MAX77759 PMIC. This is a dual input
switch-mode charger. This driver supports buck and OTG boost modes.
If built as a module, it will be called max77759_charger.
config CHARGER_MAX77976
tristate "Maxim MAX77976 battery charger driver"
depends on I2C

View File

@ -130,3 +130,4 @@ obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o
obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o
obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o
obj-$(CONFIG_MACSMC_POWER) += macsmc-power.o
obj-$(CONFIG_CHARGER_MAX77759) += max77759_charger.o

View File

@ -0,0 +1,774 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* max77759_charger.c - Battery charger driver for MAX77759 charger device.
*
* Copyright 2025 Google LLC.
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/devm-helpers.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/linear_range.h>
#include <linux/mfd/max77759.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/string_choices.h>
#include <linux/workqueue.h>
/* Default values for Fast Charge Current & Float Voltage */
#define CHG_CC_DEFAULT_UA 2266770
#define CHG_FV_DEFAULT_MV 4300
#define MAX_NUM_RETRIES 3
#define PSY_WORK_RETRY_DELAY_MS 10
#define FOREACH_IRQ(S) \
S(AICL), \
S(CHGIN), \
S(CHG), \
S(INLIM), \
S(BAT_OILO), \
S(CHG_STA_CC), \
S(CHG_STA_CV), \
S(CHG_STA_TO), \
S(CHG_STA_DONE)
#define GENERATE_ENUM(e) e
#define GENERATE_STRING(s) #s
enum {
FOREACH_IRQ(GENERATE_ENUM)
};
static const char *const chgr_irqs_str[] = {
FOREACH_IRQ(GENERATE_STRING)
};
#define NUM_IRQS ARRAY_SIZE(chgr_irqs_str)
/* Fast charge current limits (in uA) */
static const struct linear_range chgcc_limit_ranges[] = {
LINEAR_RANGE(133330, 0x0, 0x2, 0),
LINEAR_RANGE(200000, 0x3, 0x3C, 66670),
};
/* Charge Termination Voltage Limits (in mV) */
static const struct linear_range chg_cv_prm_ranges[] = {
LINEAR_RANGE(3800, 0x38, 0x39, 100),
LINEAR_RANGE(4000, 0x0, 0x32, 10),
};
/* USB input current limits (in uA) */
static const struct linear_range chgin_ilim_ranges[] = {
LINEAR_RANGE(100000, 0x3, 0x7F, 25000),
};
struct max77759_charger {
struct device *dev;
struct regmap *regmap;
struct power_supply *psy;
struct regulator_dev *chgin_otg_rdev;
struct notifier_block nb;
struct power_supply *tcpm_psy;
struct delayed_work psy_work;
struct mutex retry_lock; /* Protects psy_work_retry_cnt */
u32 psy_work_retry_cnt;
int irqs[NUM_IRQS];
struct mutex lock; /* protects the state below */
enum max77759_chgr_mode mode;
};
static inline int unlock_prot_regs(struct max77759_charger *chg, bool unlock)
{
return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_06,
MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT, unlock
? MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT : 0);
}
static int charger_input_valid(struct max77759_charger *chg)
{
u32 val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_INT_OK, &val);
if (ret)
return ret;
return (val & MAX77759_CHGR_REG_CHG_INT_CHG) &&
(val & MAX77759_CHGR_REG_CHG_INT_CHGIN);
}
static int get_online(struct max77759_charger *chg)
{
u32 val;
int ret;
ret = charger_input_valid(chg);
if (ret <= 0)
return ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_02, &val);
if (ret)
return ret;
guard(mutex)(&chg->lock);
return (val & MAX77759_CHGR_REG_CHG_DETAILS_02_CHGIN_STS) &&
(chg->mode == MAX77759_CHGR_MODE_CHG_BUCK_ON);
}
static int get_status(struct max77759_charger *chg)
{
u32 val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val);
if (ret)
return ret;
switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS, val)) {
case MAX77759_CHGR_CHG_DTLS_PREQUAL:
case MAX77759_CHGR_CHG_DTLS_CC:
case MAX77759_CHGR_CHG_DTLS_CV:
case MAX77759_CHGR_CHG_DTLS_TO:
return POWER_SUPPLY_STATUS_CHARGING;
case MAX77759_CHGR_CHG_DTLS_DONE:
return POWER_SUPPLY_STATUS_FULL;
case MAX77759_CHGR_CHG_DTLS_TIMER_FAULT:
case MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM:
case MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER:
case MAX77759_CHGR_CHG_DTLS_SUSP_JEITA:
return POWER_SUPPLY_STATUS_NOT_CHARGING;
case MAX77759_CHGR_CHG_DTLS_OFF:
return POWER_SUPPLY_STATUS_DISCHARGING;
default:
break;
}
return POWER_SUPPLY_STATUS_UNKNOWN;
}
static int get_charge_type(struct max77759_charger *chg)
{
u32 val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val);
if (ret)
return ret;
switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS, val)) {
case MAX77759_CHGR_CHG_DTLS_PREQUAL:
return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
case MAX77759_CHGR_CHG_DTLS_CC:
case MAX77759_CHGR_CHG_DTLS_CV:
return POWER_SUPPLY_CHARGE_TYPE_FAST;
case MAX77759_CHGR_CHG_DTLS_TO:
return POWER_SUPPLY_CHARGE_TYPE_STANDARD;
case MAX77759_CHGR_CHG_DTLS_DONE:
case MAX77759_CHGR_CHG_DTLS_TIMER_FAULT:
case MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM:
case MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER:
case MAX77759_CHGR_CHG_DTLS_SUSP_JEITA:
case MAX77759_CHGR_CHG_DTLS_OFF:
return POWER_SUPPLY_CHARGE_TYPE_NONE;
default:
break;
}
return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
}
static int get_chg_health(struct max77759_charger *chg)
{
u32 val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_00, &val);
if (ret)
return ret;
switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_00_CHGIN_DTLS, val)) {
case MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE:
case MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE:
return POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
case MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE:
return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
case MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID:
return POWER_SUPPLY_HEALTH_GOOD;
default:
break;
}
return POWER_SUPPLY_HEALTH_UNKNOWN;
}
static int get_batt_health(struct max77759_charger *chg)
{
u32 val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val);
if (ret)
return ret;
switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_BAT_DTLS, val)) {
case MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP:
return POWER_SUPPLY_HEALTH_NO_BATTERY;
case MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY:
return POWER_SUPPLY_HEALTH_DEAD;
case MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT:
return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
case MAX77759_CHGR_BAT_DTLS_BAT_OKAY:
case MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE:
return POWER_SUPPLY_HEALTH_GOOD;
case MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE:
return POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
case MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE:
return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
case MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT:
return POWER_SUPPLY_HEALTH_OVERCURRENT;
default:
break;
}
return POWER_SUPPLY_HEALTH_UNKNOWN;
}
static int get_health(struct max77759_charger *chg)
{
int ret;
ret = get_online(chg);
if (ret < 0)
return ret;
if (ret) {
ret = get_chg_health(chg);
if (ret < 0 || ret != POWER_SUPPLY_HEALTH_GOOD)
return ret;
}
return get_batt_health(chg);
}
static int get_fast_charge_current(struct max77759_charger *chg)
{
u32 regval, val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_02, &regval);
if (ret)
return ret;
regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC, regval);
ret = linear_range_get_value_array(chgcc_limit_ranges,
ARRAY_SIZE(chgcc_limit_ranges),
regval, &val);
return ret ? ret : val;
}
static int set_fast_charge_current_limit(struct max77759_charger *chg,
u32 cc_max_ua)
{
bool found;
u32 regval;
linear_range_get_selector_high_array(chgcc_limit_ranges,
ARRAY_SIZE(chgcc_limit_ranges),
cc_max_ua, &regval, &found);
if (!found)
return -EINVAL;
return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_02,
MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC, regval);
}
static int get_float_voltage(struct max77759_charger *chg)
{
u32 regval, val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_04, &regval);
if (ret)
return ret;
regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM, regval);
ret = linear_range_get_value_array(chg_cv_prm_ranges,
ARRAY_SIZE(chg_cv_prm_ranges),
regval, &val);
return ret ? ret : val;
}
static int set_float_voltage_limit(struct max77759_charger *chg, u32 fv_mv)
{
u32 regval;
bool found;
linear_range_get_selector_high_array(chg_cv_prm_ranges,
ARRAY_SIZE(chg_cv_prm_ranges),
fv_mv, &regval, &found);
if (!found)
return -EINVAL;
return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_04,
MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM,
regval);
}
static int get_input_current_limit(struct max77759_charger *chg)
{
u32 regval, val;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_09, &regval);
if (ret)
return ret;
regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM, regval);
regval = umax(regval, chgin_ilim_ranges[0].min_sel);
ret = linear_range_get_value_array(chgin_ilim_ranges,
ARRAY_SIZE(chgin_ilim_ranges),
regval, &val);
return ret ? ret : val;
}
static int set_input_current_limit(struct max77759_charger *chg, int ilim_ua)
{
u32 regval;
if (ilim_ua < 0)
return -EINVAL;
linear_range_get_selector_within(chgin_ilim_ranges, ilim_ua, &regval);
return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_09,
MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM,
regval);
}
static const enum power_supply_property max77759_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
};
static int max77759_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *pval)
{
struct max77759_charger *chg = power_supply_get_drvdata(psy);
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = get_online(chg);
break;
case POWER_SUPPLY_PROP_PRESENT:
ret = charger_input_valid(chg);
break;
case POWER_SUPPLY_PROP_STATUS:
ret = get_status(chg);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = get_charge_type(chg);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = get_health(chg);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
ret = get_fast_charge_current(chg);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
ret = get_float_voltage(chg);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = get_input_current_limit(chg);
break;
default:
ret = -EINVAL;
}
pval->intval = ret;
return ret < 0 ? ret : 0;
}
static const struct power_supply_desc max77759_charger_desc = {
.name = "max77759-charger",
.type = POWER_SUPPLY_TYPE_USB,
.properties = max77759_charger_props,
.num_properties = ARRAY_SIZE(max77759_charger_props),
.get_property = max77759_charger_get_property,
};
static int charger_set_mode(struct max77759_charger *chg,
enum max77759_chgr_mode mode)
{
int ret;
guard(mutex)(&chg->lock);
if (chg->mode == mode)
return 0;
if ((mode == MAX77759_CHGR_MODE_CHG_BUCK_ON ||
mode == MAX77759_CHGR_MODE_OTG_BOOST_ON) &&
chg->mode != MAX77759_CHGR_MODE_OFF) {
dev_err(chg->dev, "Invalid mode transition from %d to %d\n",
chg->mode, mode);
return -EINVAL;
}
ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_00,
MAX77759_CHGR_REG_CHG_CNFG_00_MODE, mode);
if (ret)
return ret;
chg->mode = mode;
return 0;
}
static int enable_chgin_otg(struct regulator_dev *rdev)
{
struct max77759_charger *chg = rdev_get_drvdata(rdev);
return charger_set_mode(chg, MAX77759_CHGR_MODE_OTG_BOOST_ON);
}
static int disable_chgin_otg(struct regulator_dev *rdev)
{
struct max77759_charger *chg = rdev_get_drvdata(rdev);
return charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
}
static int chgin_otg_status(struct regulator_dev *rdev)
{
struct max77759_charger *chg = rdev_get_drvdata(rdev);
guard(mutex)(&chg->lock);
return chg->mode == MAX77759_CHGR_MODE_OTG_BOOST_ON;
}
static const struct regulator_ops chgin_otg_reg_ops = {
.enable = enable_chgin_otg,
.disable = disable_chgin_otg,
.is_enabled = chgin_otg_status,
};
static const struct regulator_desc chgin_otg_reg_desc = {
.name = "chgin-otg",
.of_match = of_match_ptr("chgin-otg-regulator"),
.owner = THIS_MODULE,
.ops = &chgin_otg_reg_ops,
.fixed_uV = 5000000,
.n_voltages = 1,
};
static irqreturn_t irq_handler(int irq, void *data)
{
struct max77759_charger *chg = data;
power_supply_changed(chg->psy);
return IRQ_HANDLED;
}
static irqreturn_t bat_oilo_irq_handler(int irq, void *data)
{
struct max77759_charger *chg = data;
dev_warn_ratelimited(chg->dev,
"Battery over-current threshold crossed\n");
return irq_handler(irq, data);
}
static int max77759_init_irqhandler(struct max77759_charger *chg)
{
struct device *dev = chg->dev;
irq_handler_t thread_fn;
char *name;
int i, ret;
for (i = 0; i < ARRAY_SIZE(chgr_irqs_str); i++) {
ret = platform_get_irq_byname(to_platform_device(dev),
chgr_irqs_str[i]);
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to get irq resource for %s\n",
chgr_irqs_str[i]);
chg->irqs[i] = ret;
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", dev_name(dev),
chgr_irqs_str[i]);
if (!name)
return dev_err_probe(dev, -ENOMEM,
"Failed to allocate space for irqname: %s\n",
chgr_irqs_str[i]);
if (i == BAT_OILO)
thread_fn = bat_oilo_irq_handler;
else
thread_fn = irq_handler;
ret = devm_request_threaded_irq(dev, chg->irqs[i], NULL,
thread_fn, 0, name, chg);
if (ret)
return dev_err_probe(dev, ret,
"Unable to register irq handler for %s\n",
chgr_irqs_str[i]);
}
return 0;
}
static int max77759_charger_init(struct max77759_charger *chg)
{
struct power_supply_battery_info *info;
u32 regval, fast_chg_curr, fv;
int ret;
ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_00, &regval);
if (ret)
return ret;
chg->mode = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_00_MODE, regval);
ret = charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
if (ret)
return ret;
if (power_supply_get_battery_info(chg->psy, &info)) {
fv = CHG_FV_DEFAULT_MV;
fast_chg_curr = CHG_CC_DEFAULT_UA;
} else {
fv = info->constant_charge_voltage_max_uv / 1000;
fast_chg_curr = info->constant_charge_current_max_ua;
}
ret = set_fast_charge_current_limit(chg, fast_chg_curr);
if (ret)
return ret;
ret = set_float_voltage_limit(chg, fv);
if (ret)
return ret;
ret = unlock_prot_regs(chg, true);
if (ret)
return ret;
/* Disable wireless charging input */
ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_12,
MAX77759_CHGR_REG_CHG_CNFG_12_WCINSEL, 0);
if (ret)
goto relock;
ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_18,
MAX77759_CHGR_REG_CHG_CNFG_18_WDTEN, 0);
if (ret)
goto relock;
return unlock_prot_regs(chg, false);
relock:
(void)unlock_prot_regs(chg, false);
return ret;
}
static void psy_work_item(struct work_struct *work)
{
struct max77759_charger *chg =
container_of(work, struct max77759_charger, psy_work.work);
union power_supply_propval current_limit, online;
int ret;
ret = power_supply_get_property(chg->tcpm_psy,
POWER_SUPPLY_PROP_CURRENT_MAX,
&current_limit);
if (ret) {
dev_err(chg->dev,
"Failed to get CURRENT_MAX psy property, ret=%d\n",
ret);
goto err;
}
ret = power_supply_get_property(chg->tcpm_psy, POWER_SUPPLY_PROP_ONLINE,
&online);
if (ret) {
dev_err(chg->dev,
"Failed to get ONLINE psy property, ret=%d\n",
ret);
goto err;
}
if (online.intval && current_limit.intval) {
ret = set_input_current_limit(chg, current_limit.intval);
if (ret) {
dev_err(chg->dev,
"Unable to set current limit, ret=%d\n", ret);
goto err;
}
charger_set_mode(chg, MAX77759_CHGR_MODE_CHG_BUCK_ON);
} else {
charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
}
scoped_guard(mutex, &chg->retry_lock) {
if (chg->psy_work_retry_cnt)
dev_dbg(chg->dev,
"chg psy_work succeeded after %u tries\n",
chg->psy_work_retry_cnt);
chg->psy_work_retry_cnt = 0;
}
return;
err:
charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
scoped_guard(mutex, &chg->retry_lock) {
if (chg->psy_work_retry_cnt >= MAX_NUM_RETRIES) {
dev_err(chg->dev, "chg psy work failed, giving up\n");
return;
}
++chg->psy_work_retry_cnt;
dev_dbg(chg->dev, "Retrying %u/%u chg psy_work\n",
chg->psy_work_retry_cnt, MAX_NUM_RETRIES);
schedule_delayed_work(&chg->psy_work,
msecs_to_jiffies(PSY_WORK_RETRY_DELAY_MS));
}
}
static int psy_changed(struct notifier_block *nb, unsigned long evt, void *data)
{
struct max77759_charger *chg = container_of(nb, struct max77759_charger,
nb);
static const char *psy_name = "tcpm-source";
struct power_supply *psy = data;
if (!strnstr(psy->desc->name, psy_name, strlen(psy_name)) ||
evt != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK;
chg->tcpm_psy = psy;
scoped_guard(mutex, &chg->retry_lock)
chg->psy_work_retry_cnt = 0;
schedule_delayed_work(&chg->psy_work, 0);
return NOTIFY_OK;
}
static void max_tcpci_unregister_psy_notifier(void *nb)
{
power_supply_unreg_notifier(nb);
}
static int max77759_charger_probe(struct platform_device *pdev)
{
struct regulator_config chgin_otg_reg_cfg;
struct power_supply_config psy_cfg;
struct device *dev = &pdev->dev;
struct max77759_charger *chg;
int ret;
device_set_of_node_from_dev(dev, dev->parent);
chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
if (!chg)
return -ENOMEM;
platform_set_drvdata(pdev, chg);
chg->dev = dev;
chg->regmap = dev_get_regmap(dev->parent, "charger");
if (!chg->regmap)
return dev_err_probe(dev, -ENODEV, "Missing regmap\n");
ret = devm_mutex_init(dev, &chg->lock);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize lock\n");
ret = devm_mutex_init(dev, &chg->retry_lock);
if (ret)
return dev_err_probe(dev, ret,
"Failed to initialize retry_lock\n");
psy_cfg.fwnode = dev_fwnode(dev);
psy_cfg.drv_data = chg;
chg->psy = devm_power_supply_register(dev, &max77759_charger_desc,
&psy_cfg);
if (IS_ERR(chg->psy))
return dev_err_probe(dev, PTR_ERR(chg->psy),
"Failed to register psy\n");
ret = max77759_charger_init(chg);
if (ret)
return dev_err_probe(dev, ret,
"Failed to initialize max77759 charger\n");
chgin_otg_reg_cfg.dev = dev;
chgin_otg_reg_cfg.driver_data = chg;
chgin_otg_reg_cfg.of_node = dev_of_node(dev);
chg->chgin_otg_rdev = devm_regulator_register(dev, &chgin_otg_reg_desc,
&chgin_otg_reg_cfg);
if (IS_ERR(chg->chgin_otg_rdev))
return dev_err_probe(dev, PTR_ERR(chg->chgin_otg_rdev),
"Failed to register chgin otg regulator\n");
ret = devm_delayed_work_autocancel(dev, &chg->psy_work, psy_work_item);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize psy work\n");
chg->nb.notifier_call = psy_changed;
ret = power_supply_reg_notifier(&chg->nb);
if (ret)
return dev_err_probe(dev, ret,
"Unable to register psy notifier\n");
ret = devm_add_action_or_reset(dev, max_tcpci_unregister_psy_notifier,
&chg->nb);
if (ret)
return dev_err_probe(dev, ret,
"Failed to add devm action to unregister psy notifier\n");
return max77759_init_irqhandler(chg);
}
static const struct platform_device_id max77759_charger_id[] = {
{ .name = "max77759-charger", },
{ }
};
MODULE_DEVICE_TABLE(platform, max77759_charger_id);
static struct platform_driver max77759_charger_driver = {
.driver = {
.name = "max77759-charger",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = max77759_charger_probe,
.id_table = max77759_charger_id,
};
module_platform_driver(max77759_charger_driver);
MODULE_AUTHOR("Amit Sunil Dhamne <amitsd@google.com>");
MODULE_DESCRIPTION("Maxim MAX77759 charger driver");
MODULE_LICENSE("GPL");

View File

@ -70,6 +70,8 @@ static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = {
[POWER_SUPPLY_USB_TYPE_PD] = "PD",
[POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP",
[POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS",
[POWER_SUPPLY_USB_TYPE_PD_SPR_AVS] = "PD_SPR_AVS",
[POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS] = "PD_PPS_SPR_AVS",
[POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID",
};

View File

@ -55,7 +55,7 @@ struct tb_dma_port {
struct tb_switch *sw;
u8 port;
u32 base;
u8 *buf;
u8 buf[];
};
/*
@ -209,16 +209,10 @@ struct tb_dma_port *dma_port_alloc(struct tb_switch *sw)
if (port < 0)
return NULL;
dma = kzalloc_obj(*dma);
dma = kzalloc_flex(*dma, buf, MAIL_DATA_DWORDS);
if (!dma)
return NULL;
dma->buf = kmalloc_array(MAIL_DATA_DWORDS, sizeof(u32), GFP_KERNEL);
if (!dma->buf) {
kfree(dma);
return NULL;
}
dma->sw = sw;
dma->port = port;
dma->base = DMA_PORT_CAP;
@ -232,10 +226,7 @@ struct tb_dma_port *dma_port_alloc(struct tb_switch *sw)
*/
void dma_port_free(struct tb_dma_port *dma)
{
if (dma) {
kfree(dma->buf);
kfree(dma);
}
kfree(dma);
}
static int dma_port_wait_for_completion(struct tb_dma_port *dma,

View File

@ -150,22 +150,17 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
num_hops++;
}
path = kzalloc_obj(*path);
path = kzalloc_flex(*path, hops, num_hops);
if (!path)
return NULL;
path->path_length = num_hops;
path->name = name;
path->tb = src->sw->tb;
path->path_length = num_hops;
path->activated = true;
path->alloc_hopid = alloc_hopid;
path->hops = kzalloc_objs(*path->hops, num_hops);
if (!path->hops) {
kfree(path);
return NULL;
}
tb_dbg(path->tb, "discovering %s path starting from %llx:%u\n",
path->name, tb_route(src->sw), src->port);
@ -245,10 +240,6 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
size_t num_hops;
int i, ret;
path = kzalloc_obj(*path);
if (!path)
return NULL;
first_port = last_port = NULL;
i = 0;
tb_for_each_port_on_path(src, dst, in_port) {
@ -259,20 +250,17 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
}
/* Check that src and dst are reachable */
if (first_port != src || last_port != dst) {
kfree(path);
if (first_port != src || last_port != dst)
return NULL;
}
/* Each hop takes two ports */
num_hops = i / 2;
path->hops = kzalloc_objs(*path->hops, num_hops);
if (!path->hops) {
kfree(path);
path = kzalloc_flex(*path, hops, num_hops);
if (!path)
return NULL;
}
path->path_length = num_hops;
path->alloc_hopid = true;
in_hopid = src_hopid;
@ -339,7 +327,6 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
}
path->tb = tb;
path->path_length = num_hops;
path->name = name;
return path;
@ -372,7 +359,6 @@ void tb_path_free(struct tb_path *path)
}
}
kfree(path->hops);
kfree(path);
}

View File

@ -23,6 +23,9 @@ static void quirk_dp_credit_allocation(struct tb_switch *sw)
static void quirk_clx_disable(struct tb_switch *sw)
{
if (tb_switch_is_titan_ridge(sw) && sw->nvm && sw->nvm->major >= 0x65)
return;
sw->quirks |= QUIRK_NO_CLX;
tb_sw_dbg(sw, "disabling CL states\n");
}
@ -61,6 +64,10 @@ static const struct tb_quirk tb_quirks[] = {
/* Dell WD19TB supports self-authentication on unplug */
{ 0x0000, 0x0000, 0x00d4, 0xb070, quirk_force_power_link },
{ 0x0000, 0x0000, 0x00d4, 0xb071, quirk_force_power_link },
/* Intel Titan Ridge CLx is unstable on early firmware versions */
{ 0x8086, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE, 0x0000, 0x0000,
quirk_clx_disable },
/*
* Intel Goshen Ridge NVM 27 and before report wrong number of
* DP buffers.

View File

@ -347,7 +347,7 @@ static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
return ret;
}
static int tb_switch_nvm_add(struct tb_switch *sw)
static int tb_switch_nvm_init(struct tb_switch *sw)
{
struct tb_nvm *nvm;
int ret;
@ -365,6 +365,26 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
if (ret)
goto err_nvm;
sw->nvm = nvm;
return 0;
err_nvm:
tb_sw_dbg(sw, "NVM upgrade disabled\n");
sw->no_nvm_upgrade = true;
if (!IS_ERR(nvm))
tb_nvm_free(nvm);
return ret;
}
static int tb_switch_nvm_add(struct tb_switch *sw)
{
struct tb_nvm *nvm = sw->nvm;
int ret;
if (!nvm)
return 0;
/*
* If the switch is in safe-mode the only accessible portion of
* the NVM is the non-active one where userspace is expected to
@ -383,14 +403,12 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
goto err_nvm;
}
sw->nvm = nvm;
return 0;
err_nvm:
tb_sw_dbg(sw, "NVM upgrade disabled\n");
sw->no_nvm_upgrade = true;
if (!IS_ERR(nvm))
tb_nvm_free(nvm);
tb_nvm_free(nvm);
return ret;
}
@ -3311,6 +3329,10 @@ int tb_switch_add(struct tb_switch *sw)
return ret;
}
ret = tb_switch_nvm_init(sw);
if (ret)
return ret;
if (!sw->safe_mode) {
tb_switch_credits_init(sw);

View File

@ -419,9 +419,9 @@ enum tb_path_port {
* @activated: Is the path active
* @clear_fc: Clear all flow control from the path config space entries
* when deactivating this path
* @hops: Path hops
* @path_length: How many hops the path uses
* @alloc_hopid: Does this path consume port HopID
* @hops: Path hops
*
* A path consists of a number of hops (see &struct tb_path_hop). To
* establish a PCIe tunnel two paths have to be created between the two
@ -440,9 +440,10 @@ struct tb_path {
bool drop_packages;
bool activated;
bool clear_fc;
struct tb_path_hop *hops;
int path_length;
bool alloc_hopid;
struct tb_path_hop hops[] __counted_by(path_length);
};
/* HopIDs 0-7 are reserved by the Thunderbolt protocol */

View File

@ -180,19 +180,14 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
{
struct tb_tunnel *tunnel;
tunnel = kzalloc_obj(*tunnel);
tunnel = kzalloc_flex(*tunnel, paths, npaths);
if (!tunnel)
return NULL;
tunnel->paths = kzalloc_objs(tunnel->paths[0], npaths);
if (!tunnel->paths) {
kfree(tunnel);
return NULL;
}
tunnel->npaths = npaths;
INIT_LIST_HEAD(&tunnel->list);
tunnel->tb = tb;
tunnel->npaths = npaths;
tunnel->type = type;
kref_init(&tunnel->kref);
@ -219,7 +214,6 @@ static void tb_tunnel_destroy(struct kref *kref)
tb_path_free(tunnel->paths[i]);
}
kfree(tunnel->paths);
kfree(tunnel);
}

View File

@ -37,7 +37,6 @@ enum tb_tunnel_state {
* @src_port: Source port of the tunnel
* @dst_port: Destination port of the tunnel. For discovered incomplete
* tunnels may be %NULL or null adapter port instead.
* @paths: All paths required by the tunnel
* @npaths: Number of paths in @paths
* @pre_activate: Optional tunnel specific initialization called before
* activation. Can touch hardware.
@ -69,13 +68,13 @@ enum tb_tunnel_state {
* @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read
* @callback: Optional callback called when DP tunnel is fully activated
* @callback_data: Optional data for @callback
* @paths: All paths required by the tunnel
*/
struct tb_tunnel {
struct kref kref;
struct tb *tb;
struct tb_port *src_port;
struct tb_port *dst_port;
struct tb_path **paths;
size_t npaths;
int (*pre_activate)(struct tb_tunnel *tunnel);
int (*activate)(struct tb_tunnel *tunnel, bool activate);
@ -107,6 +106,8 @@ struct tb_tunnel {
struct delayed_work dprx_work;
void (*callback)(struct tb_tunnel *tunnel, void *data);
void *callback_data;
struct tb_path *paths[] __counted_by(npaths);
};
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,

View File

@ -544,30 +544,31 @@ static irqreturn_t ci_irq_handler(int irq, void *data)
if (ret == IRQ_HANDLED)
return ret;
}
}
/*
* Handle id change interrupt, it indicates device/host function
* switch.
*/
if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
ci->id_event = true;
/* Clear ID change irq status */
hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
ci_otg_queue_work(ci);
return IRQ_HANDLED;
}
/*
* Handle id change interrupt, it indicates device/host function
* switch.
*/
if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
ci->id_event = true;
/* Clear ID change irq status */
hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
}
/*
* Handle vbus change interrupt, it indicates device connection
* and disconnection events.
*/
if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
ci->b_sess_valid_event = true;
/* Clear BSV irq */
hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
ci_otg_queue_work(ci);
return IRQ_HANDLED;
/*
* Handle vbus change interrupt, it indicates device connection
* and disconnection events.
*/
if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
ci->b_sess_valid_event = true;
/* Clear BSV irq */
hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
}
if (ci->id_event || ci->b_sess_valid_event) {
ci_otg_queue_work(ci);
return IRQ_HANDLED;
}
}
/* Handle device/host interrupt */
@ -618,28 +619,13 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
struct ci_hdrc_cable *cable;
if (role == USB_ROLE_HOST) {
cable = &ci->platdata->id_extcon;
cable->changed = true;
cable->connected = true;
cable = &ci->platdata->vbus_extcon;
cable->changed = true;
cable->connected = false;
} else if (role == USB_ROLE_DEVICE) {
cable = &ci->platdata->id_extcon;
cable->changed = true;
cable->connected = false;
cable = &ci->platdata->vbus_extcon;
cable->changed = true;
cable->connected = true;
} else {
cable = &ci->platdata->id_extcon;
cable->changed = true;
cable->connected = false;
cable = &ci->platdata->vbus_extcon;
cable->changed = true;
cable->connected = false;
}
cable = &ci->platdata->id_extcon;
cable->changed = true;
cable->connected = (role == USB_ROLE_HOST);
cable = &ci->platdata->vbus_extcon;
cable->changed = true;
cable->connected = (role == USB_ROLE_DEVICE);
ci_irq(ci);
return 0;

View File

@ -130,6 +130,9 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
if (ci->role != CI_ROLE_GADGET)
return;
if (!ci->is_otg) {
if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS)
usb_gadget_vbus_connect(&ci->gadget);
@ -187,8 +190,8 @@ void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci);
if (role == CI_ROLE_GADGET &&
IS_ERR(ci->platdata->vbus_extcon.edev))
if (role == CI_ROLE_GADGET && !ci->role_switch &&
IS_ERR(ci->platdata->vbus_extcon.edev))
/*
* Wait vbus lower than OTGSC_BSV before connecting
* to host. If connecting status is from an external

View File

@ -114,6 +114,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
int retval;
retval = usb_autopm_get_interface(acm->control);
#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */
#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */
if (retval)
return retval;
@ -710,12 +712,14 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
acm->control->needs_remote_wakeup = 1;
acm->ctrlurb->dev = acm->dev;
retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
if (retval) {
dev_err(&acm->control->dev,
"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
goto error_submit_urb;
if (!(acm->quirks & ALWAYS_POLL_CTRL)) {
acm->ctrlurb->dev = acm->dev;
retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
if (retval) {
dev_err(&acm->control->dev,
"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
goto error_submit_urb;
}
}
acm_tty_set_termios(tty, NULL);
@ -788,6 +792,14 @@ static void acm_port_shutdown(struct tty_port *port)
acm_unpoison_urbs(acm);
if (acm->quirks & ALWAYS_POLL_CTRL) {
acm->ctrlurb->dev = acm->dev;
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
dev_dbg(&acm->control->dev,
"ctrl polling restart failed after port close\n");
/* port_shutdown() cleared DTR/RTS; restore them */
acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
}
}
static void acm_tty_cleanup(struct tty_struct *tty)
@ -1328,6 +1340,9 @@ static int acm_probe(struct usb_interface *intf,
dev_dbg(&intf->dev,
"Your device has switched interfaces.\n");
swap(control_interface, data_interface);
} else if (quirks & VENDOR_CLASS_DATA_IFACE) {
dev_dbg(&intf->dev,
"Vendor-specific data interface class, continuing.\n");
} else {
return -EINVAL;
}
@ -1522,6 +1537,9 @@ static int acm_probe(struct usb_interface *intf,
acm->line.bDataBits = 8;
acm_set_line(acm, &acm->line);
if (quirks & ALWAYS_POLL_CTRL)
acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
if (!acm->combined_interfaces) {
rv = usb_driver_claim_interface(&acm_driver, data_interface, acm);
if (rv)
@ -1543,6 +1561,13 @@ static int acm_probe(struct usb_interface *intf,
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
if (acm->quirks & ALWAYS_POLL_CTRL) {
acm->ctrlurb->dev = acm->dev;
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
dev_warn(&intf->dev,
"failed to start persistent ctrl polling\n");
}
return 0;
err_release_data_interface:
@ -1669,7 +1694,7 @@ static int acm_resume(struct usb_interface *intf)
acm_unpoison_urbs(acm);
if (tty_port_initialized(&acm->port)) {
if (tty_port_initialized(&acm->port) || (acm->quirks & ALWAYS_POLL_CTRL)) {
rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
for (;;) {
@ -2016,6 +2041,20 @@ static const struct usb_device_id acm_ids[] = {
/* CH343 supports CAP_BRK, but doesn't advertise it */
{ USB_DEVICE(0x1a86, 0x55d3), .driver_info = MISSING_CAP_BRK, },
/*
* Lenovo Yoga Book 9 14IAH10 (83KJ) INGENIC 17EF:6161 touchscreen
* composite device. The CDC ACM control interface (0) uses a standard
* Union descriptor, but the data interface (1) is declared as vendor-
* specific class (0xff) with no CDC data descriptors, so cdc-acm would
* normally reject it. The firmware also requires continuous polling of
* the notification endpoint (EP 0x82) to suppress a 20-second watchdog
* reset; ALWAYS_POLL_CTRL keeps the ctrlurb active even when no TTY is
* open. Match only the control interface by class to avoid probing the
* vendor-specific data interface.
*/
{ USB_DEVICE_INTERFACE_CLASS(0x17ef, 0x6161, USB_CLASS_COMM),
.driver_info = VENDOR_CLASS_DATA_IFACE | ALWAYS_POLL_CTRL },
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },

View File

@ -54,8 +54,8 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
* follows the SuperSpeed Endpoint Companion descriptor
*/
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE ||
desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) {
dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
"for config %d interface %d altsetting %d ep %d.\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);

View File

@ -26,14 +26,6 @@ struct ep_device {
#define to_ep_device(_dev) \
container_of(_dev, struct ep_device, dev)
struct ep_attribute {
struct attribute attr;
ssize_t (*show)(struct usb_device *,
struct usb_endpoint_descriptor *, char *);
};
#define to_ep_attribute(_attr) \
container_of(_attr, struct ep_attribute, attr)
#define usb_ep_attr(field, format_string) \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, \

View File

@ -1063,7 +1063,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
}
EXPORT_SYMBOL_GPL(usb_string);
/* one UTF-8-encoded 16-bit character has at most three bytes */
/* one 16-bit character, when UTF-8-encoded, has at most three bytes */
#define MAX_USB_STRING_SIZE (127 * 3 + 1)
/**
@ -1084,16 +1084,18 @@ char *usb_cache_string(struct usb_device *udev, int index)
return NULL;
buf = kmalloc(MAX_USB_STRING_SIZE, GFP_NOIO);
if (buf) {
len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
if (len > 0) {
smallbuf = kmalloc(++len, GFP_NOIO);
if (!smallbuf)
return buf;
memcpy(smallbuf, buf, len);
}
if (!buf)
return NULL;
len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
if (len <= 0) {
kfree(buf);
return NULL;
}
smallbuf = krealloc(buf, len + 1, GFP_NOIO);
if (unlikely(!smallbuf))
return buf;
return smallbuf;
}
EXPORT_SYMBOL_GPL(usb_cache_string);

View File

@ -79,17 +79,13 @@ EXPORT_SYMBOL_GPL(usb_of_has_combined_node);
static bool usb_of_has_devices_or_graph(const struct usb_device *hub)
{
const struct device_node *np = hub->dev.of_node;
struct device_node *child;
if (of_graph_is_present(np))
return true;
for_each_child_of_node(np, child) {
if (of_property_present(child, "reg")) {
of_node_put(child);
for_each_child_of_node_scoped(np, child)
if (of_property_present(child, "reg"))
return true;
}
}
return false;
}

View File

@ -21,6 +21,20 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
static bool usb_port_allow_power_off(struct usb_device *hdev,
struct usb_hub *hub,
struct usb_port *port_dev)
{
if (hub_is_port_power_switchable(hub))
return true;
if (!IS_ENABLED(CONFIG_ACPI))
return false;
return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED &&
usb_acpi_power_manageable(hdev, port_dev->portnum - 1);
}
static ssize_t early_stop_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -141,6 +155,7 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
usb_disconnect(&port_dev->child);
rc = usb_hub_set_port_power(hdev, hub, port1, !disabled);
msleep(2 * hub_power_on_good_delay(hub));
if (disabled) {
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
@ -805,10 +820,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
device_enable_async_suspend(&port_dev->dev);
/*
* Keep hidden the ability to enable port-poweroff if the hub
* does not support power switching.
* Keep hidden the ability to enable port-poweroff if neither the
* USB hub nor platform firmware can manage downstream port power.
*/
if (!hub_is_port_power_switchable(hub))
if (!usb_port_allow_power_off(hdev, hub, port_dev))
return 0;
/* Attempt to let userspace take over the policy. */

View File

@ -150,6 +150,18 @@ config USB_DWC3_IMX8MP
functionality.
Say 'Y' or 'M' if you have one such device.
config USB_DWC3_IMX
tristate "NXP iMX Platform"
depends on OF && COMMON_CLK
depends on (ARCH_MXC && ARM64) || COMPILE_TEST
default USB_DWC3
help
NXP iMX SoC use DesignWare Core IP for USB2/3
functionality.
This driver also handles the wakeup feature outside
of DesignWare Core.
Say 'Y' or 'M' if you have one such device.
config USB_DWC3_XILINX
tristate "Xilinx Platforms"
depends on (ARCH_ZYNQMP || COMPILE_TEST) && OF

View File

@ -55,6 +55,7 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
obj-$(CONFIG_USB_DWC3_IMX) += dwc3-imx.o
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o

View File

@ -782,6 +782,24 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
return 0;
}
static void dwc3_ulpi_setup(struct dwc3 *dwc)
{
int index;
u32 reg;
/* Don't do anything if there is no ULPI PHY */
if (!dwc->ulpi)
return;
if (dwc->enable_usb2_transceiver_delay) {
for (index = 0; index < dwc->num_usb2_ports; index++) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
}
}
}
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
@ -1363,6 +1381,8 @@ int dwc3_core_init(struct dwc3 *dwc)
dwc->ulpi_ready = true;
}
dwc3_ulpi_setup(dwc);
if (!dwc->phys_ready) {
ret = dwc3_core_get_phy(dwc);
if (ret)
@ -1675,6 +1695,9 @@ static void dwc3_get_software_properties(struct dwc3 *dwc,
u16 gsbuscfg0_reqinfo;
int ret;
if (properties->needs_full_reinit)
dwc->needs_full_reinit = true;
dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
if (properties->gsbuscfg0_reqinfo !=
@ -2479,7 +2502,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
if (!PMSG_IS_AUTO(msg) &&
(!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
dwc3_core_exit(dwc);
break;
}
@ -2542,7 +2566,8 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
dwc3_gadget_resume(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
if (!PMSG_IS_AUTO(msg) &&
(!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;

View File

@ -302,6 +302,7 @@
#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
#define DWC3_GUSB2PHYCFG_XCVRDLY BIT(9)
#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
@ -1119,6 +1120,8 @@ struct dwc3_glue_ops {
* @usb3_lpm_capable: set if hadrware supports Link Power Management
* @usb2_lpm_disable: set to disable usb2 lpm for host
* @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget
* @needs_full_reinit: set to indicate the core may lose power and need full
* initialization during system pm
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@ -1149,7 +1152,7 @@ struct dwc3_glue_ops {
* VBUS with an external supply.
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
* instances in park mode.
* @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed
* @parkmode_disable_hs_quirk: set if we need to disable all HighSpeed
* instances in park mode.
* @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter
* running based on ref_clk
@ -1161,6 +1164,8 @@ struct dwc3_glue_ops {
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @dis_split_quirk: set to disable split boundary.
* @enable_usb2_transceiver_delay: Set to insert a delay before the
* assertion of the TxValid signal during a HS Chirp.
* @sys_wakeup: set if the device may do system wakeup.
* @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2.
@ -1373,6 +1378,7 @@ struct dwc3 {
unsigned usb3_lpm_capable:1;
unsigned usb2_lpm_disable:1;
unsigned usb2_gadget_lpm_disable:1;
unsigned needs_full_reinit:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
@ -1403,6 +1409,7 @@ struct dwc3 {
unsigned dis_metastability_quirk:1;
unsigned dis_split_quirk:1;
unsigned enable_usb2_transceiver_delay:1;
unsigned async_callbacks:1;
unsigned sys_wakeup:1;
unsigned wakeup_configured:1;

View File

@ -12,6 +12,8 @@
#include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/otg.h>
#include "glue.h"
#define EIC7700_HSP_BUS_FILTER_EN BIT(0)
@ -69,6 +71,20 @@ static int dwc3_eic7700_init(struct dwc3_generic *dwc3g)
return 0;
}
static int dwc3_spacemit_k1_init(struct dwc3_generic *dwc3g)
{
struct device *dev = dwc3g->dev;
if (usb_get_dr_mode(dev) == USB_DR_MODE_HOST) {
int ret = devm_regulator_get_enable_optional(dev, "vbus");
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable VBUS\n");
}
return 0;
}
static int dwc3_generic_probe(struct platform_device *pdev)
{
const struct dwc3_generic_config *plat_config;
@ -201,6 +217,11 @@ static const struct dev_pm_ops dwc3_generic_dev_pm_ops = {
dwc3_generic_runtime_idle)
};
static const struct dwc3_generic_config spacemit_k1_dwc3 = {
.init = dwc3_spacemit_k1_init,
.properties = DWC3_DEFAULT_PROPERTIES,
};
static const struct dwc3_generic_config fsl_ls1028_dwc3 = {
.properties.gsbuscfg0_reqinfo = 0x2222,
};
@ -211,9 +232,11 @@ static const struct dwc3_generic_config eic7700_dwc3 = {
};
static const struct of_device_id dwc3_generic_of_match[] = {
{ .compatible = "spacemit,k1-dwc3", },
{ .compatible = "spacemit,k1-dwc3", &spacemit_k1_dwc3},
{ .compatible = "spacemit,k3-dwc3", },
{ .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3},
{ .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3},
{ .compatible = "starfive,jhb100-dwc3", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dwc3_generic_of_match);

448
drivers/usb/dwc3/dwc3-imx.c Normal file
View File

@ -0,0 +1,448 @@
// SPDX-License-Identifier: GPL-2.0
/*
* dwc3-imx.c - NXP i.MX Soc USB3 Specific Glue layer
*
* Copyright 2026 NXP
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "core.h"
#include "glue.h"
/* USB wakeup registers */
#define USB_WAKEUP_CTRL 0x00
/* Global wakeup interrupt enable, also used to clear interrupt */
#define USB_WAKEUP_EN BIT(31)
/* Wakeup from connect or disconnect, only for superspeed */
#define USB_WAKEUP_SS_CONN BIT(5)
/* 0 select vbus_valid, 1 select sessvld */
#define USB_WAKEUP_VBUS_SRC_SESS_VAL BIT(4)
/* Enable signal for wake up from u3 state */
#define USB_WAKEUP_U3_EN BIT(3)
/* Enable signal for wake up from id change */
#define USB_WAKEUP_ID_EN BIT(2)
/* Enable signal for wake up from vbus change */
#define USB_WAKEUP_VBUS_EN BIT(1)
/* Enable signal for wake up from dp/dm change */
#define USB_WAKEUP_DPDM_EN BIT(0)
#define USB_WAKEUP_EN_MASK GENMASK(5, 0)
/* USB glue registers */
#define USB_CTRL0 0x00
#define USB_CTRL1 0x04
#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */
#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */
#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */
#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */
#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */
struct dwc3_imx {
struct dwc3 dwc;
struct device *dev;
void __iomem *blkctl_base;
void __iomem *glue_base;
struct clk *hsio_clk;
struct clk *suspend_clk;
int irq;
bool pm_suspended;
bool wakeup_pending;
unsigned permanent_attached:1;
unsigned disable_pwr_ctrl:1;
unsigned overcur_active_low:1;
unsigned power_active_low:1;
};
#define to_dwc3_imx(d) container_of((d), struct dwc3_imx, dwc)
static void dwc3_imx_get_property(struct dwc3_imx *dwc_imx)
{
struct device *dev = dwc_imx->dev;
dwc_imx->permanent_attached =
device_property_read_bool(dev, "fsl,permanently-attached");
dwc_imx->disable_pwr_ctrl =
device_property_read_bool(dev, "fsl,disable-port-power-control");
dwc_imx->overcur_active_low =
device_property_read_bool(dev, "fsl,over-current-active-low");
dwc_imx->power_active_low =
device_property_read_bool(dev, "fsl,power-active-low");
}
static void dwc3_imx_configure_glue(struct dwc3_imx *dwc_imx)
{
u32 value;
if (!dwc_imx->glue_base)
return;
value = readl(dwc_imx->glue_base + USB_CTRL0);
if (dwc_imx->permanent_attached)
value |= USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED;
else
value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
if (dwc_imx->disable_pwr_ctrl)
value &= ~USB_CTRL0_PORTPWR_EN;
else
value |= USB_CTRL0_PORTPWR_EN;
writel(value, dwc_imx->glue_base + USB_CTRL0);
value = readl(dwc_imx->glue_base + USB_CTRL1);
if (dwc_imx->overcur_active_low)
value |= USB_CTRL1_OC_POLARITY;
else
value &= ~USB_CTRL1_OC_POLARITY;
if (dwc_imx->power_active_low)
value |= USB_CTRL1_PWR_POLARITY;
else
value &= ~USB_CTRL1_PWR_POLARITY;
writel(value, dwc_imx->glue_base + USB_CTRL1);
}
static void dwc3_imx_wakeup_enable(struct dwc3_imx *dwc_imx, pm_message_t msg)
{
struct dwc3 *dwc = &dwc_imx->dwc;
u32 val;
val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) {
val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN;
if (PMSG_IS_AUTO(msg))
val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN;
} else {
val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
USB_WAKEUP_VBUS_SRC_SESS_VAL;
}
writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
}
static void dwc3_imx_wakeup_disable(struct dwc3_imx *dwc_imx)
{
u32 val;
val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK);
writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
}
static irqreturn_t dwc3_imx_interrupt(int irq, void *data)
{
struct dwc3_imx *dwc_imx = data;
struct dwc3 *dwc = &dwc_imx->dwc;
if (!dwc_imx->pm_suspended)
return IRQ_HANDLED;
disable_irq_nosync(dwc_imx->irq);
dwc_imx->wakeup_pending = true;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci)
pm_runtime_resume(&dwc->xhci->dev);
else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
pm_runtime_get(dwc->dev);
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);
}
static struct dwc3_glue_ops dwc3_imx_glue_ops = {
.pre_set_role = dwc3_imx_pre_set_role,
};
static const struct property_entry dwc3_imx_properties[] = {
PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"),
PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"),
{},
};
static const struct software_node dwc3_imx_swnode = {
.properties = dwc3_imx_properties,
};
static int dwc3_imx_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3_imx *dwc_imx;
struct dwc3 *dwc;
struct resource *res;
const char *irq_name;
struct dwc3_probe_data probe_data = {};
int ret, irq;
dwc_imx = devm_kzalloc(dev, sizeof(*dwc_imx), GFP_KERNEL);
if (!dwc_imx)
return -ENOMEM;
platform_set_drvdata(pdev, dwc_imx);
dwc_imx->dev = dev;
dwc3_imx_get_property(dwc_imx);
dwc_imx->blkctl_base = devm_platform_ioremap_resource_byname(pdev, "blkctl");
if (IS_ERR(dwc_imx->blkctl_base))
return PTR_ERR(dwc_imx->blkctl_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "glue");
if (!res) {
dev_warn(dev, "Base address for glue layer missing\n");
} else {
dwc_imx->glue_base = devm_ioremap_resource(dev, res);
if (IS_ERR(dwc_imx->glue_base))
return PTR_ERR(dwc_imx->glue_base);
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
if (!res)
return dev_err_probe(dev, -ENODEV, "missing core memory resource\n");
dwc_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio");
if (IS_ERR(dwc_imx->hsio_clk))
return dev_err_probe(dev, PTR_ERR(dwc_imx->hsio_clk),
"Failed to get hsio clk\n");
dwc_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend");
if (IS_ERR(dwc_imx->suspend_clk))
return dev_err_probe(dev, PTR_ERR(dwc_imx->suspend_clk),
"Failed to get suspend clk\n");
irq = platform_get_irq_byname(pdev, "wakeup");
if (irq < 0)
return irq;
dwc_imx->irq = irq;
irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", dev_name(dev));
if (!irq_name)
return dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n");
ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx_interrupt,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
irq_name, dwc_imx);
if (ret)
return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", irq);
ret = device_add_software_node(dev, &dwc3_imx_swnode);
if (ret)
return dev_err_probe(dev, ret, "failed to add software node\n");
dwc3_imx_configure_glue(dwc_imx);
dwc = &dwc_imx->dwc;
dwc->dev = dev;
dwc->glue_ops = &dwc3_imx_glue_ops;
probe_data.res = res;
probe_data.dwc = dwc;
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
probe_data.properties.needs_full_reinit = true;
ret = dwc3_core_probe(&probe_data);
if (ret) {
device_remove_software_node(dev);
return ret;
}
device_set_wakeup_capable(dev, true);
return 0;
}
static void dwc3_imx_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3 *dwc = dev_get_drvdata(dev);
dwc3_core_remove(dwc);
device_remove_software_node(dev);
}
static void dwc3_imx_suspend(struct dwc3_imx *dwc_imx, pm_message_t msg)
{
if (dwc_imx->pm_suspended)
return;
if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc_imx->dev))
dwc3_imx_wakeup_enable(dwc_imx, msg);
enable_irq(dwc_imx->irq);
dwc_imx->pm_suspended = true;
}
static void dwc3_imx_resume(struct dwc3_imx *dwc_imx, pm_message_t msg)
{
struct dwc3 *dwc = &dwc_imx->dwc;
if (!dwc_imx->pm_suspended)
return;
dwc_imx->pm_suspended = false;
if (!dwc_imx->wakeup_pending)
disable_irq_nosync(dwc_imx->irq);
dwc3_imx_wakeup_disable(dwc_imx);
/* Upon power loss any previous configuration is lost, restore it */
dwc3_imx_configure_glue(dwc_imx);
if (dwc_imx->wakeup_pending) {
dwc_imx->wakeup_pending = false;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
pm_runtime_put_autosuspend(dwc->dev);
else
/*
* Add wait for xhci switch from suspend
* clock to normal clock to detect connection.
*/
usleep_range(9000, 10000);
}
}
static int dwc3_imx_runtime_suspend(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
int ret;
ret = dwc3_runtime_suspend(dwc);
if (ret)
return ret;
dwc3_imx_suspend(dwc_imx, PMSG_AUTO_SUSPEND);
return 0;
}
static int dwc3_imx_runtime_resume(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
dwc3_imx_resume(dwc_imx, PMSG_AUTO_RESUME);
return dwc3_runtime_resume(dwc);
}
static int dwc3_imx_runtime_idle(struct device *dev)
{
return dwc3_runtime_idle(dev_get_drvdata(dev));
}
static int dwc3_imx_pm_suspend(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
int ret;
ret = dwc3_pm_suspend(dwc);
if (ret)
return ret;
dwc3_imx_suspend(dwc_imx, PMSG_SUSPEND);
if (device_may_wakeup(dev)) {
enable_irq_wake(dwc_imx->irq);
device_set_out_band_wakeup(dev);
} else {
clk_disable_unprepare(dwc_imx->suspend_clk);
}
clk_disable_unprepare(dwc_imx->hsio_clk);
return 0;
}
static int dwc3_imx_pm_resume(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
int ret;
if (device_may_wakeup(dwc_imx->dev)) {
disable_irq_wake(dwc_imx->irq);
} else {
ret = clk_prepare_enable(dwc_imx->suspend_clk);
if (ret)
return ret;
}
ret = clk_prepare_enable(dwc_imx->hsio_clk);
if (ret) {
clk_disable_unprepare(dwc_imx->suspend_clk);
return ret;
}
dwc3_imx_resume(dwc_imx, PMSG_RESUME);
ret = dwc3_pm_resume(dwc);
if (ret)
return ret;
return 0;
}
static void dwc3_imx_complete(struct device *dev)
{
dwc3_pm_complete(dev_get_drvdata(dev));
}
static int dwc3_imx_prepare(struct device *dev)
{
return dwc3_pm_prepare(dev_get_drvdata(dev));
}
static const struct dev_pm_ops dwc3_imx_dev_pm_ops = {
SYSTEM_SLEEP_PM_OPS(dwc3_imx_pm_suspend, dwc3_imx_pm_resume)
RUNTIME_PM_OPS(dwc3_imx_runtime_suspend, dwc3_imx_runtime_resume,
dwc3_imx_runtime_idle)
.complete = pm_sleep_ptr(dwc3_imx_complete),
.prepare = pm_sleep_ptr(dwc3_imx_prepare),
};
static const struct of_device_id dwc3_imx_of_match[] = {
{ .compatible = "nxp,imx8mp-dwc3", },
{},
};
MODULE_DEVICE_TABLE(of, dwc3_imx_of_match);
static struct platform_driver dwc3_imx_driver = {
.probe = dwc3_imx_probe,
.remove = dwc3_imx_remove,
.driver = {
.name = "imx-dwc3",
.pm = pm_ptr(&dwc3_imx_dev_pm_ops),
.of_match_table = dwc3_imx_of_match,
},
};
module_platform_driver(dwc3_imx_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DesignWare USB3 i.MX Glue Layer");

View File

@ -526,14 +526,14 @@ static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
int irq;
irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
if (irq <= 0)
if (irq < 0)
return 1;
for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
sprintf(irq_name, "dp_hs_phy_%d", port_num);
irq = platform_get_irq_byname_optional(pdev, irq_name);
if (irq <= 0)
if (irq < 0)
return port_num - 1;
}

View File

@ -1688,7 +1688,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
* transfer, there's no need to update the transfer.
*/
if (!ret && !starting)
return ret;
return 0;
req = next_request(&dep->started_list);
if (!req) {

View File

@ -12,9 +12,12 @@
/**
* dwc3_properties: DWC3 core properties
* @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field
* @needs_full_reinit: indicate the controller may not remain power during system
* pm and need full initialization
*/
struct dwc3_properties {
u32 gsbuscfg0_reqinfo;
unsigned needs_full_reinit:1;
};
#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \

View File

@ -10,10 +10,13 @@
#include <linux/delay.h>
#include <linux/time64.h>
#include <linux/ulpi/regs.h>
#include <linux/ulpi/driver.h>
#include "core.h"
#include "io.h"
#define USB_VENDOR_MICROCHIP 0x0424
#define DWC3_ULPI_ADDR(a) \
((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = {
.write = dwc3_ulpi_write,
};
static void dwc3_ulpi_detect_config(struct dwc3 *dwc)
{
struct ulpi *ulpi = dwc->ulpi;
switch (ulpi->id.vendor) {
case USB_VENDOR_MICROCHIP:
switch (ulpi->id.product) {
case 0x0009:
/* Microchip USB3340 ULPI PHY */
dwc->enable_usb2_transceiver_delay = true;
break;
default:
break;
}
break;
default:
break;
}
}
int dwc3_ulpi_init(struct dwc3 *dwc)
{
/* Register the interface */
@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc)
return PTR_ERR(dwc->ulpi);
}
dwc3_ulpi_detect_config(dwc);
return 0;
}

View File

@ -106,7 +106,7 @@ struct f_hidg {
struct list_head report_list;
struct device dev;
struct cdev cdev;
struct cdev *cdev;
struct usb_function func;
struct usb_ep *in_ep;
@ -749,8 +749,9 @@ static int f_hidg_release(struct inode *inode, struct file *fd)
static int f_hidg_open(struct inode *inode, struct file *fd)
{
struct kobject *parent = inode->i_cdev->kobj.parent;
struct f_hidg *hidg =
container_of(inode->i_cdev, struct f_hidg, cdev);
container_of(parent, struct f_hidg, dev.kobj);
fd->private_data = hidg;
@ -1276,8 +1277,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
}
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
status = cdev_device_add(&hidg->cdev, &hidg->dev);
hidg->cdev = cdev_alloc();
if (!hidg->cdev) {
status = -ENOMEM;
goto fail_free_all;
}
hidg->cdev->ops = &f_hidg_fops;
status = cdev_device_add(hidg->cdev, &hidg->dev);
if (status)
goto fail_free_all;
@ -1579,7 +1586,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
cdev_device_del(&hidg->cdev, &hidg->dev);
cdev_device_del(hidg->cdev, &hidg->dev);
destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
}

View File

@ -1541,9 +1541,9 @@ static int f_midi2_create_card(struct f_midi2 *midi2)
return err;
midi2->card = card;
strcpy(card->driver, "f_midi2");
strcpy(card->shortname, "MIDI 2.0 Gadget");
strcpy(card->longname, "MIDI 2.0 Gadget");
strscpy(card->driver, "f_midi2");
strscpy(card->shortname, "MIDI 2.0 Gadget");
strscpy(card->longname, "MIDI 2.0 Gadget");
id = 0;
for (i = 0; i < midi2->num_eps; i++) {

View File

@ -1210,8 +1210,8 @@ static int ncm_unwrap_ntb(struct gether *port,
block_len = get_ncm(&tmp, opts->block_length);
/* (d)wBlockLength */
if (block_len > ntb_max) {
INFO(port->func.config->cdev, "OUT size exceeded\n");
if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) {
INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len);
goto err;
}

View File

@ -333,6 +333,15 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
if (unlikely(!skb))
break;
if (unlikely(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) {
/* Frame count from host exceeds frags[] capacity */
dev_kfree_skb_any(skb);
if (fp->rx.skb == skb)
fp->rx.skb = NULL;
dev->stats.rx_length_errors++;
break;
}
if (skb->len == 0) { /* First fragment */
skb->protocol = htons(ETH_P_PHONET);
skb_reset_mac_header(skb);

View File

@ -1086,7 +1086,7 @@ static int gs_console_init(struct gs_port *port)
if (!cons)
return -ENOMEM;
strcpy(cons->console.name, "ttyGS");
strscpy(cons->console.name, "ttyGS");
cons->console.write = gs_console_write;
cons->console.device = gs_console_device;
cons->console.flags = CON_PRINTBUFFER;

View File

@ -1647,6 +1647,10 @@ void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport)
u8 ep_num;
ep_num = (le32_to_cpu(sreport->offset[3])>>4) & 0x1f;
if (ep_num >= bdc->num_eps) {
dev_err(bdc->dev, "xsf for invalid ep %u\n", ep_num);
return;
}
ep = bdc->bdc_ep_array[ep_num];
if (!ep || !(ep->flags & BDC_EP_ENABLED)) {
dev_err(bdc->dev, "xsf for ep not enabled\n");

View File

@ -1266,8 +1266,9 @@ static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc)
* @speed: The maximum speed to allowed to run
*
* This call is issued by the UDC Class driver before calling
* usb_gadget_udc_start() in order to make sure that we don't try to
* connect on speeds the gadget driver doesn't support.
* usb_gadget_udc_start_locked() in order to make sure that
* we don't try to connect on speeds the gadget driver
* doesn't support.
*/
static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
enum usb_device_speed speed)

View File

@ -1462,7 +1462,7 @@ static int pxa_udc_wakeup(struct usb_gadget *_gadget)
return 0;
}
static void udc_enable(struct pxa_udc *udc);
static int udc_enable(struct pxa_udc *udc);
static void udc_disable(struct pxa_udc *udc);
/**
@ -1519,14 +1519,20 @@ static int should_disable_udc(struct pxa_udc *udc)
static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
{
struct pxa_udc *udc = to_gadget_udc(_gadget);
int ret;
if (!udc->gpiod && !udc->udc_command)
return -EOPNOTSUPP;
dplus_pullup(udc, is_active);
if (should_enable_udc(udc))
udc_enable(udc);
if (should_enable_udc(udc)) {
ret = udc_enable(udc);
if (ret) {
dplus_pullup(udc, !is_active);
return ret;
}
}
if (should_disable_udc(udc))
udc_disable(udc);
return 0;
@ -1545,10 +1551,16 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct pxa_udc *udc = to_gadget_udc(_gadget);
int ret;
udc->vbus_sensed = is_active;
if (should_enable_udc(udc))
udc_enable(udc);
if (should_enable_udc(udc)) {
ret = udc_enable(udc);
if (ret) {
udc->vbus_sensed = !is_active;
return ret;
}
}
if (should_disable_udc(udc))
udc_disable(udc);
@ -1691,12 +1703,18 @@ static void udc_init_data(struct pxa_udc *dev)
* Enables the udc device : enables clocks, udc interrupts, control endpoint
* interrupts, sets usb as UDC client and setups endpoints.
*/
static void udc_enable(struct pxa_udc *udc)
static int udc_enable(struct pxa_udc *udc)
{
if (udc->enabled)
return;
int ret;
clk_enable(udc->clk);
if (udc->enabled)
return 0;
ret = clk_enable(udc->clk);
if (ret) {
dev_err(udc->dev, "clk_enable failed: %d\n", ret);
return ret;
}
udc_writel(udc, UDCICR0, 0);
udc_writel(udc, UDCICR1, 0);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
@ -1726,6 +1744,8 @@ static void udc_enable(struct pxa_udc *udc)
pio_irq_enable(&udc->pxa_ep[0]);
udc->enabled = 1;
return 0;
}
/**
@ -1761,10 +1781,16 @@ static int pxa27x_udc_start(struct usb_gadget *g,
}
}
if (should_enable_udc(udc))
udc_enable(udc);
if (should_enable_udc(udc)) {
retval = udc_enable(udc);
if (retval)
goto fail_enable;
}
return 0;
fail_enable:
if (!IS_ERR_OR_NULL(udc->transceiver))
otg_set_peripheral(udc->transceiver->otg, NULL);
fail:
udc->driver = NULL;
return retval;
@ -2430,10 +2456,16 @@ static int pxa_udc_probe(struct platform_device *pdev)
goto err_add_gadget;
pxa_init_debugfs(udc);
if (should_enable_udc(udc))
udc_enable(udc);
if (should_enable_udc(udc)) {
retval = udc_enable(udc);
if (retval)
goto err_enable;
}
return 0;
err_enable:
usb_del_gadget_udc(&udc->gadget);
pxa_cleanup_debugfs(udc);
err_add_gadget:
if (!IS_ERR_OR_NULL(udc->transceiver))
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
@ -2509,13 +2541,19 @@ static int pxa_udc_resume(struct platform_device *_dev)
{
struct pxa_udc *udc = platform_get_drvdata(_dev);
struct pxa_ep *ep;
int ret;
ep = &udc->pxa_ep[0];
udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME));
dplus_pullup(udc, udc->pullup_resume);
if (should_enable_udc(udc))
udc_enable(udc);
if (should_enable_udc(udc)) {
ret = udc_enable(udc);
if (ret) {
dplus_pullup(udc, !udc->pullup_resume);
return ret;
}
}
/*
* We do not handle OTG yet.
*

View File

@ -1669,6 +1669,10 @@ static bool usb3_std_req_get_status(struct renesas_usb3 *usb3,
break;
case USB_RECIP_ENDPOINT:
num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
if (num >= usb3->num_usb3_eps) {
stall = true;
break;
}
usb3_ep = usb3_get_ep(usb3, num);
if (usb3_ep->halt)
status |= 1 << USB_ENDPOINT_HALT;
@ -1781,7 +1785,8 @@ static bool usb3_std_req_feature_endpoint(struct renesas_usb3 *usb3,
struct renesas_usb3_ep *usb3_ep;
struct renesas_usb3_request *usb3_req;
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
if ((le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) ||
(num >= usb3->num_usb3_eps))
return true; /* stall */
usb3_ep = usb3_get_ep(usb3, num);

View File

@ -3151,7 +3151,7 @@ int udc_probe(struct udc *dev)
tmp, dev->phys_addr, dev->chiprev,
(dev->chiprev == UDC_HSA0_REV) ?
"A0" : "B1");
strcpy(tmp, UDC_DRIVER_VERSION_STRING);
strscpy(tmp, UDC_DRIVER_VERSION_STRING);
if (dev->chiprev == UDC_HSA0_REV) {
dev_err(dev->dev, "chip revision is A0; too old\n");
retval = -ENODEV;

View File

@ -12,7 +12,6 @@
#include <linux/clk.h>
#include <linux/platform_data/usb-ehci-orion.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/io.h>
@ -60,7 +59,6 @@
struct orion_ehci_hcd {
struct clk *clk;
struct phy *phy;
};
static struct hc_driver __read_mostly ehci_orion_hc_driver;
@ -276,13 +274,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
goto err_put_hcd;
}
priv->phy = devm_phy_optional_get(&pdev->dev, "usb");
if (IS_ERR(priv->phy)) {
err = PTR_ERR(priv->phy);
if (err != -ENOSYS)
goto err_dis_clk;
}
/*
* (Re-)program MBUS remapping windows if we are asked to.
*/

View File

@ -426,16 +426,11 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
}
/* allocate the private part of the URB */
urb_priv = kzalloc_obj(*urb_priv, mem_flags);
urb_priv = kzalloc_flex(*urb_priv, tds, size, mem_flags);
if (!urb_priv)
return -ENOMEM;
/* allocate the private part of the URB */
urb_priv->tds = kzalloc_objs(*urb_priv->tds, size, mem_flags);
if (!urb_priv->tds) {
kfree(urb_priv);
return -ENOMEM;
}
urb_priv->num_of_tds = size;
spin_lock_irqsave(&fhci->lock, flags);
@ -444,8 +439,6 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
goto err;
/* fill the private part of the URB */
urb_priv->num_of_tds = size;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->error_count = 0;
@ -453,10 +446,8 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
fhci_queue_urb(fhci, urb);
err:
if (ret) {
kfree(urb_priv->tds);
if (ret)
kfree(urb_priv);
}
spin_unlock_irqrestore(&fhci->lock, flags);
return ret;
}

View File

@ -387,9 +387,10 @@ struct urb_priv {
int tds_cnt;
int state;
struct td **tds;
struct ed *ed;
struct timer_list time_out;
struct td *tds[] __counted_by(num_of_tds);
};
struct endpoint {

View File

@ -700,6 +700,10 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
seq_printf(s, "port[%d] available bw: %d%%.\n", i,
ctx->bytes[i]);
err_out:
if (ret == -EIO) {
seq_puts(s, "Get Port Bandwidth failed\n");
ret = 0;
}
pm_runtime_put_sync(dev);
xhci_free_port_bw_ctx(xhci, ctx);
return ret;
@ -710,7 +714,7 @@ static int xhci_ss_bw_show(struct seq_file *s, void *unused)
int ret;
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
ret = xhci_port_bw_show(xhci, USB_SPEED_SUPER, s);
ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_SS), s);
return ret;
}
@ -719,7 +723,7 @@ static int xhci_hs_bw_show(struct seq_file *s, void *unused)
int ret;
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
ret = xhci_port_bw_show(xhci, USB_SPEED_HIGH, s);
ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_HS), s);
return ret;
}
@ -728,7 +732,7 @@ static int xhci_fs_bw_show(struct seq_file *s, void *unused)
int ret;
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
ret = xhci_port_bw_show(xhci, USB_SPEED_FULL, s);
ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_FS), s);
return ret;
}

View File

@ -375,11 +375,11 @@ static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
}
static unsigned int xhci_port_speed(unsigned int port_status)
static unsigned int xhci_port_speed(int portsc)
{
if (DEV_LOWSPEED(port_status))
if (DEV_LOWSPEED(portsc))
return USB_PORT_STAT_LOW_SPEED;
if (DEV_HIGHSPEED(port_status))
if (DEV_HIGHSPEED(portsc))
return USB_PORT_STAT_HIGH_SPEED;
/*
* FIXME: Yes, we should check for full speed, but the core uses that as
@ -429,9 +429,9 @@ static unsigned int xhci_port_speed(unsigned int port_status)
/**
* xhci_port_state_to_neutral() - Clean up read portsc value back into writeable
* @state: u32 port value read from portsc register to be cleanup up
* @portsc: u32 port value read from portsc register to be cleanup up
*
* Given a port state, this function returns a value that would result in the
* Given a portsc, this function returns a value that would result in the
* port being in the same state, if the value was written to the port status
* control register.
* Save Read Only (RO) bits and save read/write bits where
@ -442,10 +442,10 @@ static unsigned int xhci_port_speed(unsigned int port_status)
* changing port state.
*/
u32 xhci_port_state_to_neutral(u32 state)
u32 xhci_port_state_to_neutral(u32 portsc)
{
/* Save read-only status and port state */
return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
return (portsc & XHCI_PORT_RO) | (portsc & XHCI_PORT_RWS);
}
EXPORT_SYMBOL_GPL(xhci_port_state_to_neutral);
@ -577,8 +577,8 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
hcd->self.busnum, port->hcd_portnum + 1, portsc);
}
static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
u16 wIndex, struct xhci_port *port, u32 port_status)
static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, struct xhci_port *port,
u32 portsc)
{
char *port_change_bit;
u32 status;
@ -621,11 +621,11 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
return;
}
/* Change bits are all write 1 to clear */
xhci_portsc_writel(port, port_status | status);
port_status = xhci_portsc_readl(port);
xhci_portsc_writel(port, portsc | status);
portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
wIndex + 1, port_change_bit, port_status);
port->hcd_portnum + 1, port_change_bit, portsc);
}
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
@ -675,14 +675,13 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
spin_lock_irqsave(&xhci->lock, *flags);
}
static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
u16 test_mode, u16 wIndex)
static void xhci_port_set_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum)
{
u32 temp;
struct xhci_port *port;
/* xhci only supports test mode for usb2 ports */
port = xhci->usb2_rhub.ports[wIndex];
port = xhci->usb2_rhub.ports[portnum];
temp = readl(&port->port_reg->portpmsc);
temp |= test_mode << PORT_TEST_MODE_SHIFT;
writel(temp, &port->port_reg->portpmsc);
@ -691,8 +690,8 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
xhci_start(xhci);
}
static int xhci_enter_test_mode(struct xhci_hcd *xhci,
u16 test_mode, u16 wIndex, unsigned long *flags)
static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum,
unsigned long *flags)
__must_hold(&xhci->lock)
{
int i, retval;
@ -726,10 +725,8 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
/* Disable runtime PM for test mode */
pm_runtime_forbid(xhci_to_hcd(xhci)->self.controller);
/* Set PORTPMSC.PTC field to enter selected test mode */
/* Port is selected by wIndex. port_id = wIndex + 1 */
xhci_dbg(xhci, "Enter Test Mode: %d, Port_id=%d\n",
test_mode, wIndex + 1);
xhci_port_set_test_mode(xhci, test_mode, wIndex);
xhci_dbg(xhci, "Enter Test Mode: %u, Port_id=%d\n", test_mode, portnum + 1);
xhci_port_set_test_mode(xhci, test_mode, portnum);
return retval;
}
@ -853,53 +850,37 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
}
/* Updates Link Status for super Speed port */
static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
u32 *status, u32 status_reg)
static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, u32 *status, u32 portsc)
{
u32 pls = status_reg & PORT_PLS_MASK;
u32 pls = portsc & PORT_PLS_MASK;
/* When the CAS bit is set then warm reset
* should be performed on port
/*
* CAS indicates that a warm reset is required, it may be set in any
* link state and is only present on roothubs.
*/
if (status_reg & PORT_CAS) {
/* The CAS bit can be set while the port is
* in any link state.
* Only roothubs have CAS bit, so we
* pretend to be in compliance mode
* unless we're already in compliance
* or the inactive state.
if (portsc & PORT_CAS) {
/*
* If not already in Compliance or Inactive state,
* report Compliance Mode so the hub logic triggers a warm reset.
*/
if (pls != USB_SS_PORT_LS_COMP_MOD &&
pls != USB_SS_PORT_LS_SS_INACTIVE) {
if (pls != XDEV_COMP_MODE && pls != XDEV_INACTIVE)
pls = USB_SS_PORT_LS_COMP_MOD;
}
/* Return also connection bit -
* hub state machine resets port
* when this bit is set.
*/
pls |= USB_PORT_STAT_CONNECTION;
} else {
/*
* Resume state is an xHCI internal state. Do not report it to
* usb core, instead, pretend to be U3, thus usb core knows
* it's not ready for transfer.
*/
if (pls == XDEV_RESUME) {
*status |= USB_SS_PORT_LS_U3;
return;
}
/* Signal a connection change to force a reset */
*status |= USB_PORT_STAT_CONNECTION;
} else if (pls == XDEV_RESUME) {
/*
* If CAS bit isn't set but the Port is already at
* Compliance Mode, fake a connection so the USB core
* notices the Compliance state and resets the port.
* This resolves an issue generated by the SN65LVPE502CP
* in which sometimes the port enters compliance mode
* caused by a delay on the host-device negotiation.
* Resume is an internal xHCI-only state and must not be exposed
* to usbcore. Report it as U3 so transfers are blocked.
*/
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
(pls == USB_SS_PORT_LS_COMP_MOD))
pls |= USB_PORT_STAT_CONNECTION;
pls = USB_SS_PORT_LS_U3;
} else if (pls == XDEV_COMP_MODE) {
/*
* Some hardware may enter Compliance Mode without CAS.
* Fake a connection event so usbcore notices and resets the port.
*/
if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
*status |= USB_PORT_STAT_CONNECTION;
}
/* update status field */
@ -913,17 +894,16 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
* the compliance mode timer is deleted. A port won't enter
* compliance mode if it has previously entered U0.
*/
static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
u16 wIndex)
static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 portsc, int portnum)
{
u32 all_ports_seen_u0 = ((1 << xhci->usb3_rhub.num_ports) - 1);
bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
bool port_in_u0 = ((portsc & PORT_PLS_MASK) == XDEV_U0);
if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
return;
if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
xhci->port_status_u0 |= 1 << wIndex;
xhci->port_status_u0 |= 1 << portnum;
if (xhci->port_status_u0 == all_ports_seen_u0) {
timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
@ -941,12 +921,12 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
struct xhci_bus_state *bus_state;
struct xhci_hcd *xhci;
struct usb_hcd *hcd;
u32 wIndex;
int portnum;
hcd = port->rhub->hcd;
bus_state = &port->rhub->bus_state;
xhci = hcd_to_xhci(hcd);
wIndex = port->hcd_portnum;
portnum = port->hcd_portnum;
if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) {
return -EINVAL;
@ -954,7 +934,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
/* did port event handler already start resume timing? */
if (!port->resume_timestamp) {
/* If not, maybe we are in a host initiated resume? */
if (test_bit(wIndex, &bus_state->resuming_ports)) {
if (test_bit(portnum, &bus_state->resuming_ports)) {
/* Host initiated resume doesn't time the resume
* signalling using resume_done[].
* It manually sets RESUME state, sleeps 20ms
@ -968,20 +948,20 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
unsigned long timeout = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
set_bit(wIndex, &bus_state->resuming_ports);
set_bit(portnum, &bus_state->resuming_ports);
port->resume_timestamp = timeout;
mod_timer(&hcd->rh_timer, timeout);
usb_hcd_start_port_resume(&hcd->self, wIndex);
usb_hcd_start_port_resume(&hcd->self, portnum);
}
/* Has resume been signalled for USB_RESUME_TIME yet? */
} else if (time_after_eq(jiffies, port->resume_timestamp)) {
int time_left;
xhci_dbg(xhci, "resume USB2 port %d-%d\n",
hcd->self.busnum, wIndex + 1);
hcd->self.busnum, portnum + 1);
port->resume_timestamp = 0;
clear_bit(wIndex, &bus_state->resuming_ports);
clear_bit(portnum, &bus_state->resuming_ports);
reinit_completion(&port->rexit_done);
port->rexit_active = true;
@ -1005,7 +985,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
int port_status = xhci_portsc_readl(port);
xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n",
hcd->self.busnum, wIndex + 1, port_status);
hcd->self.busnum, portnum + 1, port_status);
/*
* keep rexit_active set if U0 transition failed so we
* know to report PORT_STAT_SUSPEND status back to
@ -1014,21 +994,21 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
*/
}
usb_hcd_end_port_resume(&hcd->self, wIndex);
bus_state->port_c_suspend |= 1 << wIndex;
bus_state->suspended_ports &= ~(1 << wIndex);
usb_hcd_end_port_resume(&hcd->self, portnum);
bus_state->port_c_suspend |= 1 << portnum;
bus_state->suspended_ports &= ~(1 << portnum);
}
return 0;
}
static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
static u32 xhci_get_ext_port_status(u32 portsc, u32 port_li)
{
u32 ext_stat = 0;
int speed_id;
/* only support rx and tx lane counts of 1 in usb3.1 spec */
speed_id = DEV_PORT_SPEED(raw_port_status);
speed_id = DEV_PORT_SPEED(portsc);
ext_stat |= speed_id; /* bits 3:0, RX speed id */
ext_stat |= speed_id << 4; /* bits 7:4, TX speed id */
@ -1153,10 +1133,8 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,
* - Stop the Synopsys redriver Compliance Mode polling.
* - Drop and reacquire the xHCI lock, in order to wait for port resume.
*/
static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_bus_state *bus_state,
u16 wIndex, u32 raw_port_status,
unsigned long *flags)
static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_bus_state *bus_state,
int portnum, u32 portsc, unsigned long *flags)
__releases(&xhci->lock)
__acquires(&xhci->lock)
{
@ -1165,38 +1143,37 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_port *port;
rhub = xhci_get_rhub(hcd);
port = rhub->ports[wIndex];
port = rhub->ports[portnum];
/* common wPortChange bits */
if (raw_port_status & PORT_CSC)
if (portsc & PORT_CSC)
status |= USB_PORT_STAT_C_CONNECTION << 16;
if (raw_port_status & PORT_PEC)
if (portsc & PORT_PEC)
status |= USB_PORT_STAT_C_ENABLE << 16;
if ((raw_port_status & PORT_OCC))
if (portsc & PORT_OCC)
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
if ((raw_port_status & PORT_RC))
if (portsc & PORT_RC)
status |= USB_PORT_STAT_C_RESET << 16;
/* common wPortStatus bits */
if (raw_port_status & PORT_CONNECT) {
if (portsc & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION;
status |= xhci_port_speed(raw_port_status);
status |= xhci_port_speed(portsc);
}
if (raw_port_status & PORT_PE)
if (portsc & PORT_PE)
status |= USB_PORT_STAT_ENABLE;
if (raw_port_status & PORT_OC)
if (portsc & PORT_OC)
status |= USB_PORT_STAT_OVERCURRENT;
if (raw_port_status & PORT_RESET)
if (portsc & PORT_RESET)
status |= USB_PORT_STAT_RESET;
/* USB2 and USB3 specific bits, including Port Link State */
if (hcd->speed >= HCD_USB3)
xhci_get_usb3_port_status(port, &status, raw_port_status);
xhci_get_usb3_port_status(port, &status, portsc);
else
xhci_get_usb2_port_status(port, &status, raw_port_status,
flags);
xhci_get_usb2_port_status(port, &status, portsc, flags);
if (bus_state->port_c_suspend & (1 << wIndex))
if (bus_state->port_c_suspend & (1 << portnum))
status |= USB_PORT_STAT_C_SUSPEND << 16;
return status;
@ -1208,23 +1185,23 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports;
unsigned long flags;
u32 temp, status;
u32 portsc, portpmsc, status;
int retval = 0;
struct xhci_bus_state *bus_state;
u16 link_state = 0;
u16 wake_mask = 0;
u16 timeout = 0;
u16 test_mode = 0;
u16 link_state;
u16 wake_mask;
u8 timeout;
u8 test_mode;
u8 desc_type;
struct xhci_hub *rhub;
struct xhci_port **ports;
struct xhci_port *port;
int portnum1;
int portnum;
rhub = xhci_get_rhub(hcd);
ports = rhub->ports;
max_ports = rhub->num_ports;
bus_state = &rhub->bus_state;
portnum1 = wIndex & 0xff;
spin_lock_irqsave(&xhci->lock, flags);
switch (typeReq) {
@ -1233,13 +1210,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
memset(buf, 0, 4);
break;
case GetHubDescriptor:
desc_type = (wValue & 0xff00) >> 8;
/* Check to make sure userspace is asking for the USB 3.0 hub
* descriptor for the USB 3.0 roothub. If not, we stall the
* endpoint, like external hubs do.
*/
if (hcd->speed >= HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
(wLength < USB_DT_SS_HUB_SIZE || desc_type != USB_DT_SS_HUB)) {
xhci_dbg(xhci, "Wrong hub descriptor type for "
"USB 3.0 roothub.\n");
goto error;
@ -1248,7 +1225,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
(struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
if ((wValue & 0xff00) != (USB_DT_BOS << 8))
desc_type = (wValue & 0xff00) >> 8;
if (desc_type != USB_DT_BOS)
goto error;
if (hcd->speed < HCD_USB3)
@ -1258,29 +1236,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
spin_unlock_irqrestore(&xhci->lock, flags);
return retval;
case GetPortStatus:
if (!portnum1 || portnum1 > max_ports)
portnum = (wIndex & 0xff) - 1;
if (!in_range(portnum, 0, max_ports))
goto error;
wIndex--;
port = ports[portnum1 - 1];
temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) {
port = ports[portnum];
portsc = xhci_portsc_readl(port);
if (portsc == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
trace_xhci_get_port_status(port, temp);
status = xhci_get_port_status(hcd, bus_state, wIndex, temp,
&flags);
trace_xhci_get_port_status(port, portsc);
status = xhci_get_port_status(hcd, bus_state, portnum, portsc, &flags);
if (status == 0xffffffff)
goto error;
xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x",
hcd->self.busnum, portnum1, temp, status);
hcd->self.busnum, portnum + 1, portsc, status);
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
/* if USB 3.1 extended port status return additional 4 bytes */
if (wValue == 0x02) {
if (wValue == HUB_EXT_PORT_STATUS) {
u32 port_li;
if (hcd->speed < HCD_USB31 || wLength != 8) {
@ -1289,38 +1266,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
}
port_li = readl(&port->port_reg->portli);
status = xhci_get_ext_port_status(temp, port_li);
status = xhci_get_ext_port_status(portsc, port_li);
put_unaligned_le32(status, &buf[4]);
}
break;
case SetPortFeature:
if (wValue == USB_PORT_FEAT_LINK_STATE)
link_state = (wIndex & 0xff00) >> 3;
if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
wake_mask = wIndex & 0xff00;
if (wValue == USB_PORT_FEAT_TEST)
test_mode = (wIndex & 0xff00) >> 8;
/* The MSB of wIndex is the U1/U2 timeout */
timeout = (wIndex & 0xff00) >> 8;
wIndex &= 0xff;
if (!portnum1 || portnum1 > max_ports)
portnum = (wIndex & 0xff) - 1;
if (!in_range(portnum, 0, max_ports))
goto error;
port = ports[portnum1 - 1];
wIndex--;
temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) {
port = ports[portnum];
portsc = xhci_portsc_readl(port);
if (portsc == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
temp = xhci_port_state_to_neutral(temp);
portsc = xhci_port_state_to_neutral(portsc);
/* FIXME: What new port features do we need to support? */
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
temp = xhci_portsc_readl(port);
if ((temp & PORT_PLS_MASK) != XDEV_U0) {
portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_U0) {
/* Resume the port to U0 first */
xhci_set_link_state(xhci, port, XDEV_U0);
spin_unlock_irqrestore(&xhci->lock, flags);
@ -1331,11 +1298,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* a port unless the port reports that it is in the
* enabled (PED = 1,PLS < 3) state.
*/
temp = xhci_portsc_readl(port);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) ||
(portsc & PORT_PLS_MASK) >= XDEV_U3) {
xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n",
hcd->self.busnum, portnum1);
hcd->self.busnum, portnum + 1);
goto error;
}
@ -1354,34 +1321,35 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
msleep(10); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags);
temp = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << wIndex;
portsc = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << portnum;
break;
case USB_PORT_FEAT_LINK_STATE:
temp = xhci_portsc_readl(port);
link_state = (wIndex & 0xff00) >> 3;
portsc = xhci_portsc_readl(port);
/* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
xhci_dbg(xhci, "Disable port %d-%d\n",
hcd->self.busnum, portnum1);
temp = xhci_port_state_to_neutral(temp);
hcd->self.busnum, portnum + 1);
portsc = xhci_port_state_to_neutral(portsc);
/*
* Clear all change bits, so that we get a new
* connection event.
*/
temp |= PORT_CSC | PORT_PEC | PORT_WRC |
PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC;
xhci_portsc_writel(port, temp | PORT_PE);
temp = xhci_portsc_readl(port);
portsc |= PORT_CSC | PORT_PEC | PORT_WRC |
PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC;
xhci_portsc_writel(port, portsc | PORT_PE);
portsc = xhci_portsc_readl(port);
break;
}
/* Put link in RxDetect (enable port) */
if (link_state == USB_SS_PORT_LS_RX_DETECT) {
xhci_dbg(xhci, "Enable port %d-%d\n",
hcd->self.busnum, portnum1);
xhci_set_link_state(xhci, port, link_state);
temp = xhci_portsc_readl(port);
hcd->self.busnum, portnum + 1);
xhci_set_link_state(xhci, port, XDEV_RXDETECT);
portsc = xhci_portsc_readl(port);
break;
}
@ -1405,27 +1373,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
}
if ((temp & PORT_CONNECT)) {
if ((portsc & PORT_CONNECT)) {
xhci_warn(xhci, "Can't set compliance mode when port is connected\n");
goto error;
}
xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n",
hcd->self.busnum, portnum1);
xhci_set_link_state(xhci, port, link_state);
hcd->self.busnum, portnum + 1);
xhci_set_link_state(xhci, port, XDEV_COMP_MODE);
temp = xhci_portsc_readl(port);
portsc = xhci_portsc_readl(port);
break;
}
/* Port must be enabled */
if (!(temp & PORT_PE)) {
if (!(portsc & PORT_PE)) {
retval = -ENODEV;
break;
}
/* Can't set port link state above '3' (U3) */
if (link_state > USB_SS_PORT_LS_U3) {
xhci_warn(xhci, "Cannot set port %d-%d link state %d\n",
hcd->self.busnum, portnum1, link_state);
hcd->self.busnum, portnum + 1, link_state);
goto error;
}
@ -1437,7 +1405,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* completion
*/
if (link_state == USB_SS_PORT_LS_U0) {
u32 pls = temp & PORT_PLS_MASK;
u32 pls = portsc & PORT_PLS_MASK;
bool wait_u0 = false;
/* already in U0 */
@ -1450,7 +1418,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
reinit_completion(&port->u3exit_done);
}
if (pls <= XDEV_U3) /* U1, U2, U3 */
xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0);
xhci_set_link_state(xhci, port, XDEV_U0);
if (!wait_u0) {
if (pls > XDEV_U3)
goto error;
@ -1460,9 +1428,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (!wait_for_completion_timeout(&port->u3exit_done,
msecs_to_jiffies(500)))
xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n",
hcd->self.busnum, portnum1);
hcd->self.busnum, portnum + 1);
spin_lock_irqsave(&xhci->lock, flags);
temp = xhci_portsc_readl(port);
portsc = xhci_portsc_readl(port);
break;
}
@ -1476,17 +1444,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_stop_device(xhci, port->slot_id, 1);
spin_lock_irqsave(&xhci->lock, flags);
}
xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3);
xhci_set_link_state(xhci, port, XDEV_U3);
spin_unlock_irqrestore(&xhci->lock, flags);
while (retries--) {
usleep_range(4000, 8000);
temp = xhci_portsc_readl(port);
if ((temp & PORT_PLS_MASK) == XDEV_U3)
portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) == XDEV_U3)
break;
}
spin_lock_irqsave(&xhci->lock, flags);
temp = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << wIndex;
portsc = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << portnum;
}
break;
case USB_PORT_FEAT_POWER:
@ -1499,93 +1467,98 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_set_port_power(xhci, port, true, &flags);
break;
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
xhci_portsc_writel(port, temp);
portsc |= PORT_RESET;
xhci_portsc_writel(port, portsc);
temp = xhci_portsc_readl(port);
portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n",
hcd->self.busnum, portnum1, temp);
hcd->self.busnum, portnum + 1, portsc);
break;
case USB_PORT_FEAT_REMOTE_WAKE_MASK:
wake_mask = wIndex & 0xff00;
xhci_set_remote_wake_mask(xhci, port, wake_mask);
temp = xhci_portsc_readl(port);
portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n",
hcd->self.busnum, portnum1, temp);
hcd->self.busnum, portnum + 1, portsc);
break;
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
xhci_portsc_writel(port, temp);
temp = xhci_portsc_readl(port);
portsc |= PORT_WR;
xhci_portsc_writel(port, portsc);
portsc = xhci_portsc_readl(port);
break;
case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
temp = readl(&port->port_reg->portpmsc);
temp &= ~PORT_U1_TIMEOUT_MASK;
temp |= PORT_U1_TIMEOUT(timeout);
writel(temp, &port->port_reg->portpmsc);
timeout = (wIndex & 0xff00) >> 8;
portpmsc = readl(&port->port_reg->portpmsc);
portpmsc &= ~PORT_U1_TIMEOUT_MASK;
portpmsc |= PORT_U1_TIMEOUT(timeout);
writel(portpmsc, &port->port_reg->portpmsc);
break;
case USB_PORT_FEAT_U2_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
temp = readl(&port->port_reg->portpmsc);
temp &= ~PORT_U2_TIMEOUT_MASK;
temp |= PORT_U2_TIMEOUT(timeout);
writel(temp, &port->port_reg->portpmsc);
timeout = (wIndex & 0xff00) >> 8;
portpmsc = readl(&port->port_reg->portpmsc);
portpmsc &= ~PORT_U2_TIMEOUT_MASK;
portpmsc |= PORT_U2_TIMEOUT(timeout);
writel(portpmsc, &port->port_reg->portpmsc);
break;
case USB_PORT_FEAT_TEST:
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
if (hcd->speed != HCD_USB2)
goto error;
test_mode = (wIndex & 0xff00) >> 8;
if (test_mode > USB_TEST_FORCE_ENABLE ||
test_mode < USB_TEST_J)
goto error;
retval = xhci_enter_test_mode(xhci, test_mode, wIndex,
&flags);
retval = xhci_enter_test_mode(xhci, test_mode, portnum, &flags);
break;
default:
goto error;
}
/* unblock any posted writes */
temp = xhci_portsc_readl(port);
portsc = xhci_portsc_readl(port);
break;
case ClearPortFeature:
if (!portnum1 || portnum1 > max_ports)
portnum = (wIndex & 0xff) - 1;
if (!in_range(portnum, 0, max_ports))
goto error;
port = ports[portnum1 - 1];
wIndex--;
temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) {
port = ports[portnum];
portsc = xhci_portsc_readl(port);
if (portsc == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
/* FIXME: What new port features do we need to support? */
temp = xhci_port_state_to_neutral(temp);
portsc = xhci_port_state_to_neutral(portsc);
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
temp = xhci_portsc_readl(port);
portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
xhci_dbg(xhci, "PORTSC %04x\n", temp);
if (temp & PORT_RESET)
xhci_dbg(xhci, "PORTSC %04x\n", portsc);
if (portsc & PORT_RESET)
goto error;
if ((temp & PORT_PLS_MASK) == XDEV_U3) {
if ((temp & PORT_PE) == 0)
if ((portsc & PORT_PLS_MASK) == XDEV_U3) {
if ((portsc & PORT_PE) == 0)
goto error;
set_bit(wIndex, &bus_state->resuming_ports);
usb_hcd_start_port_resume(&hcd->self, wIndex);
set_bit(portnum, &bus_state->resuming_ports);
usb_hcd_start_port_resume(&hcd->self, portnum);
xhci_set_link_state(xhci, port, XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(USB_RESUME_TIMEOUT);
spin_lock_irqsave(&xhci->lock, flags);
xhci_set_link_state(xhci, port, XDEV_U0);
clear_bit(wIndex, &bus_state->resuming_ports);
usb_hcd_end_port_resume(&hcd->self, wIndex);
clear_bit(portnum, &bus_state->resuming_ports);
usb_hcd_end_port_resume(&hcd->self, portnum);
}
bus_state->port_c_suspend |= 1 << wIndex;
bus_state->port_c_suspend |= 1 << portnum;
if (!port->slot_id) {
xhci_dbg(xhci, "slot_id is zero\n");
@ -1594,7 +1567,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_ring_device(xhci, port->slot_id);
break;
case USB_PORT_FEAT_C_SUSPEND:
bus_state->port_c_suspend &= ~(1 << wIndex);
bus_state->port_c_suspend &= ~(1 << portnum);
fallthrough;
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_BH_PORT_RESET:
@ -1603,7 +1576,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_PORT_LINK_STATE:
case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp);
xhci_clear_port_change_bit(xhci, wValue, port, portsc);
break;
case USB_PORT_FEAT_ENABLE:
xhci_disable_port(xhci, port);

View File

@ -129,6 +129,13 @@ static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_rin
ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE);
}
void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
xhci_initialize_ring_segments(xhci, ring);
xhci_initialize_ring_info(ring);
trace_xhci_ring_alloc(ring);
}
/*
* Link the src ring segments to the dst ring.
* Set Toggle Cycle for the new ring if needed.
@ -389,9 +396,6 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs,
if (ret)
goto fail;
xhci_initialize_ring_segments(xhci, ring);
xhci_initialize_ring_info(ring);
trace_xhci_ring_alloc(ring);
return ring;
fail:
@ -668,6 +672,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
cur_ring = stream_info->stream_rings[cur_stream];
if (!cur_ring)
goto cleanup_rings;
xhci_ring_init(xhci, cur_ring);
cur_ring->stream_id = cur_stream;
cur_ring->trb_address_map = &stream_info->trb_address_map;
/* Set deq ptr, cycle bit, and stream context type */
@ -930,7 +936,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev,
* that tt_info, then free the child first. Recursive.
* We can't rely on udev at this point to find child-parent relationships.
*/
static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *vdev;
struct list_head *tt_list_head;
@ -988,14 +994,14 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
if (!dev->out_ctx)
goto fail;
xhci_dbg(xhci, "Slot %d output ctx = 0x%pad (dma)\n", slot_id, &dev->out_ctx->dma);
xhci_dbg(xhci, "Slot %d output ctx = %pad (dma)\n", slot_id, &dev->out_ctx->dma);
/* Allocate the (input) device context for address device command */
dev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, flags);
if (!dev->in_ctx)
goto fail;
xhci_dbg(xhci, "Slot %d input ctx = 0x%pad (dma)\n", slot_id, &dev->in_ctx->dma);
xhci_dbg(xhci, "Slot %d input ctx = %pad (dma)\n", slot_id, &dev->in_ctx->dma);
/* Initialize the cancellation and bandwidth list for each ep */
for (i = 0; i < 31; i++) {
@ -1011,6 +1017,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
if (!dev->eps[0].ring)
goto fail;
xhci_ring_init(xhci, dev->eps[0].ring);
dev->udev = udev;
/* Point to output device context in dcbaa. */
@ -1492,6 +1500,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
xhci_ring_init(xhci, ep_ring);
/* Fill the endpoint context */
ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
@ -1895,10 +1904,36 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
}
EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
/* Cleanup roothub bandwidth data */
void xhci_rh_bw_cleanup(struct xhci_hcd *xhci)
{
struct xhci_root_port_bw_info *rh_bw;
struct xhci_tt_bw_info *tt_info, *tt_next;
struct list_head *eps, *ep, *ep_next;
for (int i = 0; i < xhci->max_ports; i++) {
rh_bw = &xhci->rh_bw[i];
/* Clear and free all TT bandwidth entries */
list_for_each_entry_safe(tt_info, tt_next, &rh_bw->tts, tt_list) {
list_del(&tt_info->tt_list);
kfree(tt_info);
}
/* Clear per-interval endpoint lists */
for (int j = 0; j < XHCI_MAX_INTERVAL; j++) {
eps = &rh_bw->bw_table.interval_bw[j].endpoints;
list_for_each_safe(ep, ep_next, eps)
list_del_init(ep);
}
}
}
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
int i, j;
int i;
cancel_delayed_work_sync(&xhci->cmd_timer);
@ -1917,15 +1952,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
xhci_cleanup_command_queue(xhci);
for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) {
struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;
for (j = 0; j < XHCI_MAX_INTERVAL; j++) {
struct list_head *ep = &bwt->interval_bw[j].endpoints;
while (!list_empty(ep))
list_del_init(ep->next);
}
}
for (i = xhci->max_slots; i > 0; i--)
xhci_free_virt_devices_depth_first(xhci, i);
@ -1959,18 +1985,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
scratchpad_free(xhci);
if (!xhci->rh_bw)
goto no_bw;
if (xhci->rh_bw)
xhci_rh_bw_cleanup(xhci);
for (i = 0; i < xhci->max_ports; i++) {
struct xhci_tt_bw_info *tt, *n;
list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) {
list_del(&tt->tt_list);
kfree(tt);
}
}
no_bw:
xhci->cmd_ring_reserved_trbs = 0;
xhci->usb2_rhub.num_ports = 0;
xhci->usb3_rhub.num_ports = 0;
@ -1992,7 +2009,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->port_caps = NULL;
xhci->interrupters = NULL;
xhci->page_size = 0;
xhci->usb2_rhub.bus_state.bus_suspended = 0;
xhci->usb3_rhub.bus_state.bus_suspended = 0;
}
@ -2149,15 +2165,28 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
/* FIXME: Should we disable ports not in the Extended Capabilities? */
}
static void xhci_create_rhub_port_array(struct xhci_hcd *xhci,
struct xhci_hub *rhub, gfp_t flags)
static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, struct xhci_hub *rhub,
unsigned int max_ports, gfp_t flags)
{
int port_index = 0;
int i;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
if (!rhub->num_ports)
if (!rhub->num_ports) {
xhci_info(xhci, "USB%u root hub has no ports\n", rhub->maj_rev);
return;
}
/*
* Place limits on the number of roothub ports so that the hub
* descriptors aren't longer than the USB core will allocate.
*/
if (rhub->num_ports > max_ports) {
xhci->usb3_rhub.num_ports = max_ports;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Limiting USB%u root hub ports to %u",
rhub->maj_rev, max_ports);
}
rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports),
flags, dev_to_node(dev));
if (!rhub->ports)
@ -2253,30 +2282,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
"Found %u USB 2.0 ports and %u USB 3.0 ports.",
xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports);
/* Place limits on the number of roothub ports so that the hub
* descriptors aren't longer than the USB core will allocate.
*/
if (xhci->usb3_rhub.num_ports > USB_SS_MAXPORTS) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Limiting USB 3.0 roothub ports to %u.",
USB_SS_MAXPORTS);
xhci->usb3_rhub.num_ports = USB_SS_MAXPORTS;
}
if (xhci->usb2_rhub.num_ports > USB_MAXCHILDREN) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Limiting USB 2.0 roothub ports to %u.",
USB_MAXCHILDREN);
xhci->usb2_rhub.num_ports = USB_MAXCHILDREN;
}
if (!xhci->usb2_rhub.num_ports)
xhci_info(xhci, "USB2 root hub has no ports\n");
if (!xhci->usb3_rhub.num_ports)
xhci_info(xhci, "USB3 root hub has no ports\n");
xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags);
xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags);
xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, USB_MAXCHILDREN, flags);
xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, USB_SS_MAXPORTS, flags);
return 0;
}
@ -2362,6 +2369,8 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
if (!ir)
return NULL;
xhci_ring_init(xhci, ir->event_ring);
spin_lock_irq(&xhci->lock);
if (!intr_num) {
/* Find available secondary interrupter, interrupter 0 is reserved for primary */
@ -2403,6 +2412,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
dma_addr_t dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
/*
* xHCI section 5.4.6 - Device Context array must be
* "physically contiguous and 64-byte (cache line) aligned".
@ -2413,7 +2424,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci->dcbaa->dma = dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Device context base array address = 0x%pad (DMA), %p (virt)",
"Device context base array address = %pad (DMA), %p (virt)",
&xhci->dcbaa->dma, xhci->dcbaa);
/*
@ -2474,15 +2485,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
goto fail;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
&xhci->cmd_ring->first_seg->dma);
/*
* Reserve one command ring TRB for disabling LPM.
* Since the USB core grabs the shared usb_bus bandwidth mutex before
* disabling LPM, we only need to reserve one TRB for all devices.
*/
xhci->cmd_ring_reserved_trbs++;
/* Allocate and set up primary interrupter 0 with an event ring. */
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring");
@ -2501,6 +2503,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
if (xhci_setup_port_arrays(xhci, flags))
goto fail;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__);
return 0;
fail:

View File

@ -30,7 +30,7 @@ static void xhci_mvebu_mbus_config(void __iomem *base,
writel(0, base + USB3_WIN_BASE(win));
}
/* Program each DRAM CS in a seperate window */
/* Program each DRAM CS in a separate window */
for (win = 0; win < dram->num_cs; win++) {
const struct mbus_dram_window *cs = &dram->cs[win];

View File

@ -755,7 +755,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
}
if ((ep->ep_state & SET_DEQ_PENDING)) {
xhci_warn(xhci, "Set TR Deq already pending, don't submit for 0x%pad\n",
xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n",
&addr);
return -EBUSY;
}
@ -763,7 +763,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
/* This function gets called from contexts where it cannot sleep */
cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
if (!cmd) {
xhci_warn(xhci, "Can't alloc Set TR Deq cmd 0x%pad\n", &addr);
xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr);
return -ENOMEM;
}
@ -3208,10 +3208,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
/*
* Clear the op reg interrupt status first,
* so we can receive interrupts from other MSI-X interrupters.
* Write 1 to clear the interrupt status.
* USBSTS bits are write 1 to clear.
*/
status |= STS_EINT;
writel(status, &xhci->op_regs->status);
writel(STS_EINT, &xhci->op_regs->status);
/* This is the handler of the primary interrupter */
xhci_handle_events(xhci, xhci->interrupters[0], false);

View File

@ -1357,15 +1357,11 @@ static void tegra_xhci_id_work(struct work_struct *work)
dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode));
mutex_lock(&tegra->lock);
if (tegra->host_mode)
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
else
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
mutex_unlock(&tegra->lock);
tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl,
tegra->otg_usb2_port);
@ -2566,7 +2562,9 @@ static const struct tegra_xusb_soc tegra124_soc = {
.smi_intr = XUSB_CFG_ARU_SMI_INTR,
},
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) || IS_ENABLED(CONFIG_ARCH_TEGRA_132_SOC)
MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
#endif
static const char * const tegra210_supply_names[] = {
"dvddio-pex",
@ -2604,11 +2602,15 @@ static const struct tegra_xusb_soc tegra210_soc = {
.smi_intr = XUSB_CFG_ARU_SMI_INTR,
},
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
MODULE_FIRMWARE("nvidia/tegra210/xusb.bin");
#endif
static const char * const tegra186_supply_names[] = {
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
MODULE_FIRMWARE("nvidia/tegra186/xusb.bin");
#endif
static const struct tegra_xusb_phy_type tegra186_phy_types[] = {
{ .name = "usb3", .num = 3, },
@ -2681,7 +2683,9 @@ static const struct tegra_xusb_soc tegra194_soc = {
},
.lpm_support = true,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
MODULE_FIRMWARE("nvidia/tegra194/xusb.bin");
#endif
static const struct tegra_xusb_soc_ops tegra234_ops = {
.mbox_reg_readl = &bar2_readl,

View File

@ -536,34 +536,24 @@ static void xhci_set_dev_notifications(struct xhci_hcd *xhci)
writel(dev_notf, &xhci->op_regs->dev_notification);
}
/*
* Initialize memory for HCD and xHC (one-time init).
*
* Program the PAGESIZE register, initialize the device context array, create
* device contexts (?), set up a command ring segment (or two?), create event
* ring (one for now).
*/
static int xhci_init(struct usb_hcd *hcd)
/* Setup basic xHCI registers */
static void xhci_init(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int retval;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
spin_lock_init(&xhci->lock);
INIT_LIST_HEAD(&xhci->cmd_list);
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
init_completion(&xhci->cmd_ring_stop_completion);
xhci_hcd_page_size(xhci);
memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs));
retval = xhci_mem_init(xhci, GFP_KERNEL);
if (retval)
return retval;
/* Set the Number of Device Slots Enabled to the maximum supported value */
xhci_enable_max_dev_slots(xhci);
/* Initialize the Command ring */
xhci_ring_init(xhci, xhci->cmd_ring);
/*
* Reserve one command ring TRB for disabling LPM.
* Since the USB core grabs the shared usb_bus bandwidth mutex before
* disabling LPM, we only need to reserve one TRB for all devices.
*/
xhci->cmd_ring_reserved_trbs = 1;
/* Set the address in the Command Ring Control register */
xhci_set_cmd_ring_deq(xhci);
@ -577,6 +567,7 @@ static int xhci_init(struct usb_hcd *hcd)
xhci_set_dev_notifications(xhci);
/* Initialize the Primary interrupter */
xhci_ring_init(xhci, xhci->interrupters[0]->event_ring);
xhci_add_interrupter(xhci, 0);
xhci->interrupters[0]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
@ -587,7 +578,6 @@ static int xhci_init(struct usb_hcd *hcd)
}
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__);
return 0;
}
/*-------------------------------------------------------------------------*/
@ -967,11 +957,11 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
*/
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
{
int rc = 0;
int err;
unsigned int delay = XHCI_MAX_HALT_USEC * 2;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
u32 res;
u32 usbsts;
if (!hcd->state)
return 0;
@ -1017,11 +1007,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
/* Some chips from Fresco Logic need an extraordinary delay */
delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1;
if (xhci_handshake(&xhci->op_regs->status,
STS_HALT, STS_HALT, delay)) {
xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
err = xhci_handshake(&xhci->op_regs->status, STS_HALT, STS_HALT, delay);
if (err) {
xhci_warn(xhci, "Clearing Run/Stop bit failed %d\n", err);
goto handshake_error;
}
xhci_clear_command_ring(xhci);
@ -1032,28 +1021,34 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
command = readl(&xhci->op_regs->command);
command |= CMD_CSS;
writel(command, &xhci->op_regs->command);
err = xhci_handshake(&xhci->op_regs->status, STS_SAVE, 0, 20 * USEC_PER_MSEC);
usbsts = readl(&xhci->op_regs->status);
xhci->broken_suspend = 0;
if (xhci_handshake(&xhci->op_regs->status,
STS_SAVE, 0, 20 * 1000)) {
/*
* AMD SNPS xHC 3.0 occasionally does not clear the
* SSS bit of USBSTS and when driver tries to poll
* to see if the xHC clears BIT(8) which never happens
* and driver assumes that controller is not responding
* and times out. To workaround this, its good to check
* if SRE and HCE bits are not set (as per xhci
* Section 5.4.2) and bypass the timeout.
*/
res = readl(&xhci->op_regs->status);
if ((xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND) &&
(((res & STS_SRE) == 0) &&
((res & STS_HCE) == 0))) {
xhci->broken_suspend = 1;
} else {
xhci_warn(xhci, "WARN: xHC save state timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
if (err) {
/*
* AMD SNPS xHC 3.0 occasionally does not clear the
* SSS bit of USBSTS and when driver tries to poll
* to see if the xHC clears BIT(8) which never happens
* and driver assumes that controller is not responding
* and times out. To workaround this, its good to check
* if SRE and HCE bits are not set (as per xhci
* Section 5.4.2) and bypass the timeout.
*/
if (!(xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND)) {
xhci_warn(xhci, "Controller Save State failed %d\n", err);
goto handshake_error;
}
if (usbsts & (STS_SRE | STS_HCE)) {
xhci_warn(xhci, "Controller Save State failed, USBSTS 0x%08x\n", usbsts);
goto handshake_error;
}
xhci_dbg(xhci, "SNPS broken suspend, save state unreliable\n");
xhci->broken_suspend = 1;
} else if (usbsts & STS_SRE) {
xhci_warn(xhci, "Suspend Save Error (SRE), USBSTS 0x%08x\n", usbsts);
}
spin_unlock_irq(&xhci->lock);
@ -1069,7 +1064,11 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
__func__);
}
return rc;
return 0;
handshake_error:
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
EXPORT_SYMBOL_GPL(xhci_suspend);
@ -1083,10 +1082,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
{
u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct xhci_segment *seg;
int retval = 0;
bool comp_timer_running = false;
bool pending_portevent = false;
bool suspended_usb3_devs = false;
bool reset_registers = false;
if (!hcd->state)
return 0;
@ -1105,10 +1105,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
spin_lock_irq(&xhci->lock);
if (xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
power_lost = true;
if (!power_lost) {
if (power_lost || xhci->broken_suspend || xhci->quirks & XHCI_RESET_ON_RESUME) {
xhci_dbg(xhci, "HC state lost, performing host controller reset\n");
reset_registers = true;
} else {
xhci_dbg(xhci, "HC state intact, continuing without reset\n");
/*
* Some controllers might lose power during suspend, so wait
* for controller not ready bit to clear, just as in xHC init.
@ -1141,19 +1142,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
}
temp = readl(&xhci->op_regs->status);
/* re-initialize the HC on Restore Error, or Host Controller Error */
if ((temp & (STS_SRE | STS_HCE)) &&
!(xhci->xhc_state & XHCI_STATE_REMOVING)) {
if (!power_lost)
/* re-initialize the HC on Restore Error, or Host Controller Error */
temp = readl(&xhci->op_regs->status);
if ((temp & (STS_SRE | STS_HCE)) && !(xhci->xhc_state & XHCI_STATE_REMOVING)) {
xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
power_lost = true;
reset_registers = true;
}
}
if (power_lost) {
if (reset_registers) {
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
!(xhci_all_ports_seen_u0(xhci))) {
timer_delete_sync(&xhci->comp_mode_recovery_timer);
@ -1177,27 +1175,33 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
if (retval)
return retval;
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
xhci_disable_interrupter(xhci, xhci->interrupters[0]);
cancel_delayed_work_sync(&xhci->cmd_timer);
/* Delete all remaining commands */
xhci_cleanup_command_queue(xhci);
/* Clear data which is re-initilized during runtime */
xhci_for_each_ring_seg(xhci->interrupters[0]->event_ring->first_seg, seg)
memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT);
for (int i = xhci->max_slots; i > 0; i--)
xhci_free_virt_devices_depth_first(xhci, i);
xhci_rh_bw_cleanup(xhci);
xhci->cmd_ring_reserved_trbs = 0;
xhci_for_each_ring_seg(xhci->cmd_ring->first_seg, seg)
memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT);
xhci_dbg(xhci, "cleaning up memory\n");
xhci_mem_cleanup(xhci);
xhci_debugfs_exit(xhci);
xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
readl(&xhci->op_regs->status));
/* USB core calls the PCI reinit and start functions twice:
xhci_init(hcd);
/*
* USB core calls the PCI reinit and start functions twice:
* first with the primary HCD, and then with the secondary HCD.
* If we don't do the same, the host will never be started.
*/
xhci_dbg(xhci, "Initialize the xhci_hcd\n");
retval = xhci_init(hcd);
if (retval)
return retval;
comp_timer_running = true;
xhci_dbg(xhci, "Start the primary HCD\n");
retval = xhci_run(hcd);
if (!retval && xhci->shared_hcd) {
@ -1241,40 +1245,36 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
xhci_dbc_resume(xhci);
if (retval == 0) {
/*
* Resume roothubs only if there are pending events.
* USB 3 devices resend U3 LFPS wake after a 100ms delay if
* the first wake signalling failed, give it that chance if
* there are suspended USB 3 devices.
*/
if (xhci->usb3_rhub.bus_state.suspended_ports ||
xhci->usb3_rhub.bus_state.bus_suspended)
suspended_usb3_devs = true;
/*
* Resume roothubs only if there are pending events.
* USB 3 devices resend U3 LFPS wake after a 100ms delay if
* the first wake signalling failed, give it that chance if
* there are suspended USB 3 devices.
*/
if (xhci->usb3_rhub.bus_state.suspended_ports || xhci->usb3_rhub.bus_state.bus_suspended)
suspended_usb3_devs = true;
pending_portevent = xhci_pending_portevent(xhci);
if (suspended_usb3_devs && !pending_portevent && is_auto_resume) {
msleep(120);
pending_portevent = xhci_pending_portevent(xhci);
if (suspended_usb3_devs && !pending_portevent && is_auto_resume) {
msleep(120);
pending_portevent = xhci_pending_portevent(xhci);
}
if (pending_portevent) {
if (xhci->shared_hcd)
usb_hcd_resume_root_hub(xhci->shared_hcd);
usb_hcd_resume_root_hub(hcd);
}
}
done:
if (pending_portevent) {
if (xhci->shared_hcd)
usb_hcd_resume_root_hub(xhci->shared_hcd);
usb_hcd_resume_root_hub(hcd);
}
/*
* If system is subject to the Quirk, Compliance Mode Timer needs to
* be re-initialized Always after a system resume. Ports are subject
* to suffer the Compliance Mode issue again. It doesn't matter if
* ports have entered previously to U0 before system's suspension.
*/
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running)
if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
compliance_mode_recovery_timer_init(xhci);
done:
if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller));
@ -3201,7 +3201,12 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(xhci_reset_bandwidth);
/* Get the available bandwidth of the ports under the xhci roothub */
/*
* Get the available bandwidth of the ports under the xhci roothub.
* EIO means the command failed: command not implemented or unsupported
* speed (TRB Error), some ASMedia complete with Parameter Error when
* querying the root hub (slot_id = 0), or other error or timeout.
*/
int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
u8 dev_speed)
{
@ -3230,6 +3235,8 @@ int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ct
spin_unlock_irqrestore(&xhci->lock, flags);
wait_for_completion(cmd->completion);
if (cmd->status != COMP_SUCCESS)
ret = -EIO;
err_out:
kfree(cmd->completion);
kfree(cmd);
@ -3285,7 +3292,6 @@ static void xhci_endpoint_disable(struct usb_hcd *hcd,
xhci_dbg(xhci, "endpoint disable with ep_state 0x%x\n",
ep->ep_state);
done:
host_ep->hcpriv = NULL;
spin_unlock_irqrestore(&xhci->lock, flags);
}
@ -5521,12 +5527,21 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
}
xhci_dbg(xhci, "Calling HCD init\n");
/* Initialize HCD and host controller data structures. */
retval = xhci_init(hcd);
spin_lock_init(&xhci->lock);
INIT_LIST_HEAD(&xhci->cmd_list);
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
init_completion(&xhci->cmd_ring_stop_completion);
xhci_hcd_page_size(xhci);
memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs));
/* Allocate xHCI data structures */
retval = xhci_mem_init(xhci, GFP_KERNEL);
if (retval)
return retval;
xhci_dbg(xhci, "Called HCD init\n");
/* Initialize HCD and host controller data structures */
xhci_init(hcd);
if (xhci_hcd_is_usb3(hcd))
xhci_hcd_init_usb3_data(xhci, hcd);

View File

@ -12,6 +12,7 @@
#ifndef __LINUX_XHCI_HCD_H
#define __LINUX_XHCI_HCD_H
#include <linux/bits.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include <linux/kernel.h>
@ -125,17 +126,17 @@ struct xhci_op_regs {
* PCI config regs). HC does NOT drive a USB reset on the downstream ports.
* The xHCI driver must reinitialize the xHC after setting this bit.
*/
#define CMD_RESET (1 << 1)
#define CMD_RESET BIT(1)
/* Event Interrupt Enable - a '1' allows interrupts from the host controller */
#define CMD_EIE XHCI_CMD_EIE
/* Host System Error Interrupt Enable - get out-of-band signal for HC errors */
#define CMD_HSEIE XHCI_CMD_HSEIE
/* bits 4:6 are reserved (and should be preserved on writes). */
/* light reset (port status stays unchanged) - reset completed when this is 0 */
#define CMD_LRESET (1 << 7)
#define CMD_LRESET BIT(7)
/* host controller save/restore state. */
#define CMD_CSS (1 << 8)
#define CMD_CRS (1 << 9)
#define CMD_CSS BIT(8)
#define CMD_CRS BIT(9)
/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
#define CMD_EWE XHCI_CMD_EWE
/* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root
@ -143,9 +144,9 @@ struct xhci_op_regs {
* '0' means the xHC can power it off if all ports are in the disconnect,
* disabled, or powered-off state.
*/
#define CMD_PM_INDEX (1 << 11)
#define CMD_PM_INDEX BIT(11)
/* bit 14 Extended TBC Enable, changes Isoc TRB fields to support larger TBC */
#define CMD_ETE (1 << 14)
#define CMD_ETE BIT(14)
/* bits 15:31 are reserved (and should be preserved on writes). */
#define XHCI_RESET_LONG_USEC (10 * 1000 * 1000)
@ -155,22 +156,22 @@ struct xhci_op_regs {
/* HC not running - set to 1 when run/stop bit is cleared. */
#define STS_HALT XHCI_STS_HALT
/* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */
#define STS_FATAL (1 << 2)
#define STS_FATAL BIT(2)
/* event interrupt - clear this prior to clearing any IP flags in IR set*/
#define STS_EINT (1 << 3)
#define STS_EINT BIT(3)
/* port change detect */
#define STS_PORT (1 << 4)
#define STS_PORT BIT(4)
/* bits 5:7 reserved and zeroed */
/* save state status - '1' means xHC is saving state */
#define STS_SAVE (1 << 8)
#define STS_SAVE BIT(8)
/* restore state status - '1' means xHC is restoring state */
#define STS_RESTORE (1 << 9)
#define STS_RESTORE BIT(9)
/* true: save or restore error */
#define STS_SRE (1 << 10)
#define STS_SRE BIT(10)
/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
#define STS_CNR XHCI_STS_CNR
/* true: internal Host Controller Error - SW needs to reset and reinitialize */
#define STS_HCE (1 << 12)
#define STS_HCE BIT(12)
/* bits 13:31 reserved and should be preserved */
/*
@ -182,17 +183,17 @@ struct xhci_op_regs {
/* Most of the device notification types should only be used for debug.
* SW does need to pay attention to function wake notifications.
*/
#define DEV_NOTE_FWAKE (1 << 1)
#define DEV_NOTE_FWAKE BIT(1)
/* CRCR - Command Ring Control Register - cmd_ring bitmasks */
/* bit 0 - Cycle bit indicates the ownership of the command ring */
#define CMD_RING_CYCLE (1 << 0)
#define CMD_RING_CYCLE BIT(0)
/* stop ring operation after completion of the currently executing command */
#define CMD_RING_PAUSE (1 << 1)
#define CMD_RING_PAUSE BIT(1)
/* stop ring immediately - abort the currently executing command */
#define CMD_RING_ABORT (1 << 2)
#define CMD_RING_ABORT BIT(2)
/* true: command ring is running */
#define CMD_RING_RUNNING (1 << 3)
#define CMD_RING_RUNNING BIT(3)
/* bits 63:6 - Command Ring pointer */
#define CMD_RING_PTR_MASK GENMASK_ULL(63, 6)
@ -200,9 +201,9 @@ struct xhci_op_regs {
/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */
#define MAX_DEVS(p) ((p) & 0xff)
/* bit 8: U3 Entry Enabled, assert PLC when root port enters U3, xhci 1.1 */
#define CONFIG_U3E (1 << 8)
#define CONFIG_U3E BIT(8)
/* bit 9: Configuration Information Enable, xhci 1.1 */
#define CONFIG_CIE (1 << 9)
#define CONFIG_CIE BIT(9)
/* bits 10:31 - reserved and should be preserved */
/* bits 15:0 - HCD page shift bit */
@ -235,9 +236,9 @@ struct xhci_intr_reg {
/* iman bitmasks */
/* bit 0 - Interrupt Pending (IP), whether there is an interrupt pending. Write-1-to-clear. */
#define IMAN_IP (1 << 0)
#define IMAN_IP BIT(0)
/* bit 1 - Interrupt Enable (IE), whether the interrupter is capable of generating an interrupt */
#define IMAN_IE (1 << 1)
#define IMAN_IE BIT(1)
/* imod bitmasks */
/*
@ -267,7 +268,7 @@ struct xhci_intr_reg {
* bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by
* a work queue (or delayed service routine)?
*/
#define ERST_EHB (1 << 3)
#define ERST_EHB BIT(3)
/* bits 63:4 - Event Ring Dequeue Pointer */
#define ERST_PTR_MASK GENMASK_ULL(63, 4)
@ -356,15 +357,15 @@ struct xhci_slot_ctx {
#define GET_DEV_SPEED(n) (((n) & DEV_SPEED) >> 20)
/* bit 24 reserved */
/* Is this LS/FS device connected through a HS hub? - bit 25 */
#define DEV_MTT (0x1 << 25)
#define DEV_MTT BIT(25)
/* Set if the device is a hub - bit 26 */
#define DEV_HUB (0x1 << 26)
#define DEV_HUB BIT(26)
/* Index of the last valid endpoint context in this device context - 27:31 */
#define LAST_CTX_MASK (0x1f << 27)
#define LAST_CTX(p) ((p) << 27)
#define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1)
#define SLOT_FLAG (1 << 0)
#define EP0_FLAG (1 << 1)
#define SLOT_FLAG BIT(0)
#define EP0_FLAG BIT(1)
/* dev_info2 bitmasks */
/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */
@ -463,7 +464,7 @@ struct xhci_ep_ctx {
#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
#define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10)
/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
#define EP_HAS_LSA (1 << 15)
#define EP_HAS_LSA BIT(15)
/* hosts with LEC=1 use bits 31:24 as ESIT high bits. */
#define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff)
@ -498,7 +499,7 @@ struct xhci_ep_ctx {
#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
/* deq bitmasks */
#define EP_CTX_CYCLE_MASK (1 << 0)
#define EP_CTX_CYCLE_MASK BIT(0)
/* bits 63:4 - TR Dequeue Pointer */
#define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4)
@ -661,18 +662,18 @@ struct xhci_virt_ep {
struct xhci_ring *new_ring;
unsigned int err_count;
unsigned int ep_state;
#define SET_DEQ_PENDING (1 << 0)
#define EP_HALTED (1 << 1) /* For stall handling */
#define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */
#define SET_DEQ_PENDING BIT(0)
#define EP_HALTED BIT(1) /* For stall handling */
#define EP_STOP_CMD_PENDING BIT(2) /* For URB cancellation */
/* Transitioning the endpoint to using streams, don't enqueue URBs */
#define EP_GETTING_STREAMS (1 << 3)
#define EP_HAS_STREAMS (1 << 4)
#define EP_GETTING_STREAMS BIT(3)
#define EP_HAS_STREAMS BIT(4)
/* Transitioning the endpoint to not using streams, don't enqueue URBs */
#define EP_GETTING_NO_STREAMS (1 << 5)
#define EP_HARD_CLEAR_TOGGLE (1 << 6)
#define EP_SOFT_CLEAR_TOGGLE (1 << 7)
#define EP_GETTING_NO_STREAMS BIT(5)
#define EP_HARD_CLEAR_TOGGLE BIT(6)
#define EP_SOFT_CLEAR_TOGGLE BIT(7)
/* usb_hub_clear_tt_buffer is in progress */
#define EP_CLEARING_TT (1 << 8)
#define EP_CLEARING_TT BIT(8)
/* ---- Related to URB cancellation ---- */
struct list_head cancelled_td_list;
struct xhci_hcd *xhci;
@ -954,7 +955,7 @@ struct xhci_link_trb {
};
/* control bitfields */
#define LINK_TOGGLE (0x1<<1)
#define LINK_TOGGLE BIT(1)
/* Command completion event TRB */
struct xhci_event_cmd {
@ -968,13 +969,13 @@ struct xhci_event_cmd {
#define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)
#define TRB_BSR BIT(9)
/* Configure Endpoint - Deconfigure */
#define TRB_DC (1<<9)
#define TRB_DC BIT(9)
/* Stop Ring - Transfer State Preserve */
#define TRB_TSP (1<<9)
#define TRB_TSP BIT(9)
enum xhci_ep_reset_type {
EP_HARD_RESET,
@ -1017,13 +1018,13 @@ enum xhci_setup_dev {
#define SCT_FOR_TRB(p) (((p) & 0x7) << 1)
/* Link TRB specific fields */
#define TRB_TC (1<<1)
#define TRB_TC BIT(1)
/* Port Status Change Event TRB fields */
/* Port ID - bits 31:24 */
#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24)
#define EVENT_DATA (1 << 2)
#define EVENT_DATA BIT(2)
/* Normal TRB fields */
/* transfer_len bitmasks - bits 0:16 */
@ -1038,36 +1039,36 @@ enum xhci_setup_dev {
#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff)
/* Cycle bit - indicates TRB ownership by HC or HCD */
#define TRB_CYCLE (1<<0)
#define TRB_CYCLE BIT(0)
/*
* Force next event data TRB to be evaluated before task switch.
* Used to pass OS data back after a TD completes.
*/
#define TRB_ENT (1<<1)
#define TRB_ENT BIT(1)
/* Interrupt on short packet */
#define TRB_ISP (1<<2)
#define TRB_ISP BIT(2)
/* Set PCIe no snoop attribute */
#define TRB_NO_SNOOP (1<<3)
#define TRB_NO_SNOOP BIT(3)
/* Chain multiple TRBs into a TD */
#define TRB_CHAIN (1<<4)
#define TRB_CHAIN BIT(4)
/* Interrupt on completion */
#define TRB_IOC (1<<5)
#define TRB_IOC BIT(5)
/* The buffer pointer contains immediate data */
#define TRB_IDT (1<<6)
#define TRB_IDT BIT(6)
/* TDs smaller than this might use IDT */
#define TRB_IDT_MAX_SIZE 8
/* Block Event Interrupt */
#define TRB_BEI (1<<9)
#define TRB_BEI BIT(9)
/* Control transfer TRB specific fields */
#define TRB_DIR_IN (1<<16)
#define TRB_DIR_IN BIT(16)
#define TRB_TX_TYPE(p) ((p) << 16)
#define TRB_DATA_OUT 2
#define TRB_DATA_IN 3
/* Isochronous TRB specific fields */
#define TRB_SIA (1<<31)
#define TRB_SIA BIT(31)
#define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20)
#define GET_FRAME_ID(p) (((p) >> 20) & 0x7ff)
/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */
@ -1535,9 +1536,9 @@ struct xhci_hcd {
struct xhci_interrupter **interrupters;
struct xhci_ring *cmd_ring;
unsigned int cmd_ring_state;
#define CMD_RING_STATE_RUNNING (1 << 0)
#define CMD_RING_STATE_ABORTED (1 << 1)
#define CMD_RING_STATE_STOPPED (1 << 2)
#define CMD_RING_STATE_RUNNING BIT(0)
#define CMD_RING_STATE_ABORTED BIT(1)
#define CMD_RING_STATE_STOPPED BIT(2)
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
struct delayed_work cmd_timer;
@ -1578,9 +1579,9 @@ struct xhci_hcd {
*
* There are no reports of xHCI host controllers that display this issue.
*/
#define XHCI_STATE_DYING (1 << 0)
#define XHCI_STATE_HALTED (1 << 1)
#define XHCI_STATE_REMOVING (1 << 2)
#define XHCI_STATE_DYING BIT(0)
#define XHCI_STATE_HALTED BIT(1)
#define XHCI_STATE_REMOVING BIT(2)
unsigned long long quirks;
#define XHCI_LINK_TRB_QUIRK BIT_ULL(0)
#define XHCI_RESET_EP_QUIRK BIT_ULL(1) /* Deprecated */
@ -1792,6 +1793,7 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
void xhci_mem_cleanup(struct xhci_hcd *xhci);
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags);
void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id);
void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id);
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags);
int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev);
void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
@ -1803,6 +1805,7 @@ void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int old_active_eps);
void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info);
void xhci_rh_bw_cleanup(struct xhci_hcd *xhci);
void xhci_update_bw_info(struct xhci_hcd *xhci,
struct xhci_container_ctx *in_ctx,
struct xhci_input_control_ctx *ctrl_ctx,
@ -1823,6 +1826,7 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int num_trbs, gfp_t flags);
void xhci_initialize_ring_info(struct xhci_ring *ring);
void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring);
void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
unsigned int ep_index);

View File

@ -55,7 +55,6 @@
* Status:
*
* Untested with multiple scanners.
* Untested on SMP.
* Untested on a bigendian machine.
*
* History:
@ -170,25 +169,13 @@ static struct usb_driver mts_usb_driver = {
#define MTS_VERSION "0.4.3"
#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
#define MTS_WARNING(x...) \
printk( KERN_WARNING MTS_NAME x )
#define MTS_ERROR(x...) \
printk( KERN_ERR MTS_NAME x )
#define MTS_INT_ERROR(x...) \
MTS_ERROR(x)
#define MTS_MESSAGE(x...) \
printk( KERN_INFO MTS_NAME x )
#if defined MTS_DO_DEBUG
#define MTS_DEBUG(x...) \
printk( KERN_DEBUG MTS_NAME x )
#define MTS_DEBUG_GOT_HERE() \
MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ )
#define MTS_DEBUG_INT() \
do { MTS_DEBUG_GOT_HERE(); \
MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
do { MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
mts_debug_dump(context->instance);\
} while(0)
@ -197,7 +184,6 @@ static struct usb_driver mts_usb_driver = {
#define MTS_NUL_STATEMENT do { } while(0)
#define MTS_DEBUG(x...) MTS_NUL_STATEMENT
#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
#endif
@ -316,7 +302,6 @@ static inline void mts_debug_dump(struct mts_desc* dummy)
#endif
static inline void mts_urb_abort(struct mts_desc* desc) {
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
usb_kill_urb( desc->urb );
@ -332,8 +317,6 @@ static int mts_scsi_abort(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
MTS_DEBUG_GOT_HERE();
mts_urb_abort(desc);
return FAILED;
@ -344,7 +327,6 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb)
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int result;
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
@ -386,7 +368,8 @@ void mts_int_submit_urb (struct urb* transfer,
res = usb_submit_urb( transfer, GFP_ATOMIC );
if ( unlikely(res) ) {
MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
dev_err(&context->instance->usb_dev->dev,
"could not submit URB! Error was %d\n",(int)res );
set_host_byte(context->srb, DID_ERROR);
mts_transfer_cleanup(transfer);
}
@ -452,12 +435,9 @@ static void mts_command_done( struct urb *transfer )
if ( unlikely(status) ) {
if (status == -ENOENT) {
/* We are being killed */
MTS_DEBUG_GOT_HERE();
set_host_byte(context->srb, DID_ABORT);
} else {
/* A genuine error has occurred */
MTS_DEBUG_GOT_HERE();
set_host_byte(context->srb, DID_ERROR);
}
mts_transfer_cleanup(transfer);
@ -523,8 +503,6 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
{
int pipe;
MTS_DEBUG_GOT_HERE();
desc->context.instance = desc;
desc->context.srb = srb;
@ -565,7 +543,6 @@ static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb)
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int res;
MTS_DEBUG_GOT_HERE();
mts_show_command(srb);
mts_debug_dump(desc);
@ -601,7 +578,7 @@ static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb)
res=usb_submit_urb(desc->urb, GFP_ATOMIC);
if(unlikely(res)){
MTS_ERROR("error %d submitting URB\n",(int)res);
dev_err(&desc->usb_dev->dev, "error %d submitting URB\n",(int)res);
set_host_byte(srb, DID_ERROR);
if(likely(callback != NULL))
@ -666,59 +643,47 @@ static int mts_usb_probe(struct usb_interface *intf,
/* the current altsetting on the interface we're probing */
struct usb_host_interface *altsetting;
MTS_DEBUG_GOT_HERE();
MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
le16_to_cpu(dev->descriptor.idProduct),
le16_to_cpu(dev->descriptor.idVendor) );
MTS_DEBUG_GOT_HERE();
/* the current altsetting on the interface we're probing */
altsetting = intf->cur_altsetting;
/* Check if the config is sane */
if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) {
MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
dev_warn(&dev->dev, "expecting %d got %d endpoints! Bailing out.\n",
(int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints );
return -ENODEV;
}
for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) {
if ((altsetting->endpoint[i].desc.bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
(int)altsetting->endpoint[i].desc.bEndpointAddress );
} else {
if (altsetting->endpoint[i].desc.bEndpointAddress &
USB_DIR_IN)
*ep_in_current++
= altsetting->endpoint[i].desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else {
if ( ep_out != -1 ) {
MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
return -ENODEV;
}
ep_out = altsetting->endpoint[i].desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) {
*ep_in_current++ = usb_endpoint_num(&altsetting->endpoint[i].desc);
} else if (usb_endpoint_is_bulk_out(&altsetting->endpoint[i].desc)) {
if (ep_out == -1) {
ep_out = usb_endpoint_num(&altsetting->endpoint[i].desc);
} else {
dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
usb_endpoint_num(&altsetting->endpoint[i].desc));
return -ENODEV;
}
} else {
dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
usb_endpoint_num(&altsetting->endpoint[i].desc));
}
}
if (ep_in_current != &ep_in_set[2]) {
MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n");
dev_warn(&dev->dev, "couldn't find two input bulk endpoints. Bailing out.\n");
return -ENODEV;
}
if ( ep_out == -1 ) {
MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
dev_warn(&dev->dev, "couldn't find an output bulk endpoint. Bailing out.\n" );
return -ENODEV;
}
@ -744,15 +709,15 @@ static int mts_usb_probe(struct usb_interface *intf,
new_desc->ep_image = ep_in_set[1];
if ( new_desc->ep_out != MTS_EP_OUT )
MTS_WARNING( "will this work? Command EP is not usually %d\n",
dev_warn(&dev->dev, "will this work? Command EP is not usually %d\n",
(int)new_desc->ep_out );
if ( new_desc->ep_response != MTS_EP_RESPONSE )
MTS_WARNING( "will this work? Response EP is not usually %d\n",
dev_warn(&dev->dev, "will this work? Response EP is not usually %d\n",
(int)new_desc->ep_response );
if ( new_desc->ep_image != MTS_EP_IMAGE )
MTS_WARNING( "will this work? Image data EP is not usually %d\n",
dev_warn(&dev->dev, "will this work? Image data EP is not usually %d\n",
(int)new_desc->ep_image );
new_desc->host = scsi_host_alloc(&mts_scsi_host_template,

View File

@ -210,7 +210,7 @@ static int mfi_fc_probe(struct usb_device *udev)
goto err_free_name;
}
mfi->udev = usb_get_dev(udev);
mfi->udev = udev;
dev_set_drvdata(&udev->dev, mfi);
return 0;
@ -231,7 +231,6 @@ static void mfi_fc_disconnect(struct usb_device *udev)
power_supply_unregister(mfi->battery);
kfree(mfi->battery_desc.name);
dev_set_drvdata(&udev->dev, NULL);
usb_put_dev(mfi->udev);
kfree(mfi);
}

View File

@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid.h>
#include <linux/usb.h>
#include <linux/backlight.h>
#include <linux/timer.h>
@ -20,9 +21,6 @@
#define APPLE_VENDOR_ID 0x05AC
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_SET_REPORT 0x09
#define ACD_USB_TIMEOUT 250
#define ACD_USB_EDID 0x0302
@ -140,7 +138,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd)
retval = usb_control_msg(
pdata->udev,
usb_sndctrlpipe(pdata->udev, 0),
USB_REQ_SET_REPORT,
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ACD_USB_BRIGHTNESS,
0,
@ -163,7 +161,7 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd)
retval = usb_control_msg(
pdata->udev,
usb_rcvctrlpipe(pdata->udev, 0),
USB_REQ_GET_REPORT,
HID_REQ_GET_REPORT,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ACD_USB_BRIGHTNESS,
0,

View File

@ -215,7 +215,7 @@ static int cypress_probe(struct usb_interface *interface,
if (!dev)
goto error_mem;
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->udev = interface_to_usbdev(interface);
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
@ -239,8 +239,6 @@ static void cypress_disconnect(struct usb_interface *interface)
* device files have been removed */
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
dev_info(&interface->dev,
"Cypress CY7C63xxx device now disconnected\n");

View File

@ -311,7 +311,7 @@ static int cytherm_probe(struct usb_interface *interface,
if (!dev)
goto error_mem;
dev->udev = usb_get_dev(udev);
dev->udev = udev;
usb_set_intfdata(interface, dev);
@ -334,8 +334,6 @@ static void cytherm_disconnect(struct usb_interface *interface)
/* first remove the files, then NULL the pointer */
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
dev_info(&interface->dev, "Cypress thermometer now disconnected\n");

View File

@ -21,6 +21,7 @@
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/hid.h>
#include <linux/usb/iowarrior.h>
#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
@ -74,7 +75,6 @@ struct iowarrior {
struct mutex mutex; /* locks this structure */
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
unsigned char minor; /* the starting minor number for this device */
struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */
struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */
struct urb *int_in_urb; /* the urb for reading data */
@ -99,14 +99,13 @@ struct iowarrior {
/* globals */
/*--------------*/
#define USB_REQ_GET_REPORT 0x01
//#if 0
static int usb_get_report(struct usb_device *dev,
struct usb_host_interface *inter, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_REPORT,
HID_REQ_GET_REPORT,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size,
@ -114,14 +113,12 @@ static int usb_get_report(struct usb_device *dev,
}
//#endif
#define USB_REQ_SET_REPORT 0x09
static int usb_set_report(struct usb_interface *intf, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(interface_to_usbdev(intf),
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
USB_REQ_SET_REPORT,
HID_REQ_SET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
@ -234,8 +231,7 @@ static void iowarrior_write_callback(struct urb *urb)
"nonzero write bulk status received: %d\n", status);
}
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
kfree(urb->transfer_buffer);
/* tell a waiting writer the interrupt-out-pipe is available again */
atomic_dec(&dev->write_busy);
wake_up_interruptible(&dev->write_wait);
@ -246,7 +242,6 @@ static void iowarrior_write_callback(struct urb *urb)
*/
static inline void iowarrior_delete(struct iowarrior *dev)
{
dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
kfree(dev->int_in_buffer);
usb_free_urb(dev->int_in_urb);
kfree(dev->read_queue);
@ -297,9 +292,6 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer,
goto exit;
}
dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
dev->minor, count);
/* read count must be packet size (+ time stamp) */
if ((count != dev->report_size)
&& (count != (dev->report_size + 1))) {
@ -367,20 +359,21 @@ static ssize_t iowarrior_write(struct file *file,
size_t count, loff_t *ppos)
{
struct iowarrior *dev;
int retval = 0;
int retval;
char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
struct urb *int_out_urb = NULL;
dev = file->private_data;
mutex_lock(&dev->mutex);
retval = mutex_lock_interruptible(&dev->mutex);
if (retval < 0)
return -EINTR;
/* verify that the device wasn't unplugged */
if (!dev->present) {
retval = -ENODEV;
goto exit;
}
dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
dev->minor, count);
/* if count is 0 we're already done */
if (count == 0) {
retval = 0;
@ -443,8 +436,7 @@ static ssize_t iowarrior_write(struct file *file,
retval = -ENOMEM;
goto error_no_urb;
}
buf = usb_alloc_coherent(dev->udev, dev->report_size,
GFP_KERNEL, &int_out_urb->transfer_dma);
buf = kmalloc(dev->report_size, GFP_KERNEL);
if (!buf) {
retval = -ENOMEM;
dev_dbg(&dev->interface->dev,
@ -457,7 +449,6 @@ static ssize_t iowarrior_write(struct file *file,
buf, dev->report_size,
iowarrior_write_callback, dev,
dev->int_out_endpoint->bInterval);
int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
@ -483,8 +474,7 @@ static ssize_t iowarrior_write(struct file *file,
goto exit;
}
error:
usb_free_coherent(dev->udev, dev->report_size, buf,
int_out_urb->transfer_dma);
kfree(buf);
error_no_buffer:
usb_free_urb(int_out_urb);
error_no_urb:
@ -523,9 +513,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
goto error_out;
}
dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n",
dev->minor, cmd, arg);
retval = 0;
switch (cmd) {
case IOW_WRITE:
@ -671,8 +658,6 @@ static int iowarrior_release(struct inode *inode, struct file *file)
if (!dev)
return -ENODEV;
dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
/* lock our device */
mutex_lock(&dev->mutex);
@ -775,6 +760,7 @@ static int iowarrior_probe(struct usb_interface *interface,
struct usb_host_interface *iface_desc;
int retval = -ENOMEM;
int res;
int minor;
/* allocate memory for our device state and initialize it */
dev = kzalloc_obj(struct iowarrior);
@ -890,12 +876,12 @@ static int iowarrior_probe(struct usb_interface *interface,
goto error;
}
dev->minor = interface->minor;
minor = interface->minor;
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
"now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE);
return retval;
error:

View File

@ -565,11 +565,13 @@ static struct platform_driver onboard_dev_driver = {
/************************** USB driver **************************/
#define VENDOR_ID_BISON 0x5986
#define VENDOR_ID_CORECHIPS 0x3431
#define VENDOR_ID_CYPRESS 0x04b4
#define VENDOR_ID_GENESYS 0x05e3
#define VENDOR_ID_MICROCHIP 0x0424
#define VENDOR_ID_PARADE 0x1da0
#define VENDOR_ID_REALTEK 0x0bda
#define VENDOR_ID_TERMINUS 0x1a40
#define VENDOR_ID_TI 0x0451
#define VENDOR_ID_VIA 0x2109
#define VENDOR_ID_XMOS 0x20B1
@ -649,6 +651,8 @@ static void onboard_dev_usbdev_disconnect(struct usb_device *udev)
static const struct usb_device_id onboard_dev_id_table[] = {
{ USB_DEVICE(VENDOR_ID_BISON, 0x1198) }, /* Bison Electronics Inc. Integrated Camera */
{ USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6241) }, /* SL6341 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6341) }, /* SL6341 3.0 HUB */
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6500) }, /* CYUSB330x 3.0 HUB */
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6502) }, /* CYUSB330x 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6503) }, /* CYUSB33{0,1}x 2.0 HUB, Vendor Mode */
@ -673,6 +677,7 @@ static const struct usb_device_id onboard_dev_id_table[] = {
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0179) }, /* RTL8188ETV 2.4GHz WiFi */
{ USB_DEVICE(VENDOR_ID_TERMINUS, 0x0101) }, /* Terminus FE1.1s 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */

View File

@ -66,13 +66,6 @@ static const struct onboard_dev_pdata ti_tusb8020b_data = {
.is_hub = true,
};
static const struct onboard_dev_pdata ti_tusb8041_data = {
.reset_us = 3000,
.num_supplies = 1,
.supply_names = { "vdd" },
.is_hub = true,
};
static const struct onboard_dev_pdata bison_intcamera_data = {
.reset_us = 1000,
.num_supplies = 1,
@ -80,6 +73,13 @@ static const struct onboard_dev_pdata bison_intcamera_data = {
.is_hub = false,
};
static const struct onboard_dev_pdata corechips_sl6341_data = {
.reset_us = 10000,
.num_supplies = 2,
.supply_names = { "vdd1v1", "vdd3v3" },
.is_hub = true,
};
static const struct onboard_dev_pdata cypress_hx3_data = {
.reset_us = 10000,
.num_supplies = 2,
@ -87,13 +87,6 @@ static const struct onboard_dev_pdata cypress_hx3_data = {
.is_hub = true,
};
static const struct onboard_dev_pdata cypress_hx2vl_data = {
.reset_us = 1,
.num_supplies = 1,
.supply_names = { "vdd" },
.is_hub = true,
};
static const struct onboard_dev_pdata genesys_gl850g_data = {
.reset_us = 3,
.num_supplies = 1,
@ -108,6 +101,11 @@ static const struct onboard_dev_pdata genesys_gl852g_data = {
.is_hub = true,
};
static const struct onboard_dev_pdata usb_a_conn_data = {
.num_supplies = 1,
.supply_names = { "vbus" },
};
static const struct onboard_dev_pdata vialab_vl817_data = {
.reset_us = 10,
.num_supplies = 1,
@ -130,6 +128,7 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = {
};
static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usb-a-connector", .data = &usb_a_conn_data, },
{ .compatible = "usb424,2412", .data = &microchip_usb424_data, },
{ .compatible = "usb424,2514", .data = &microchip_usb2514_data, },
{ .compatible = "usb424,2517", .data = &microchip_usb424_data, },
@ -137,13 +136,13 @@ static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usb424,5744", .data = &microchip_usb5744_data, },
{ .compatible = "usb451,8025", .data = &ti_tusb8020b_data, },
{ .compatible = "usb451,8027", .data = &ti_tusb8020b_data, },
{ .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
{ .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
{ .compatible = "usb451,8440", .data = &ti_tusb8041_data, },
{ .compatible = "usb451,8442", .data = &ti_tusb8041_data, },
{ .compatible = "usb451,8140", .data = &ti_tusb8020b_data, },
{ .compatible = "usb451,8142", .data = &ti_tusb8020b_data, },
{ .compatible = "usb451,8440", .data = &ti_tusb8020b_data, },
{ .compatible = "usb451,8442", .data = &ti_tusb8020b_data, },
{ .compatible = "usb4b4,6504", .data = &cypress_hx3_data, },
{ .compatible = "usb4b4,6506", .data = &cypress_hx3_data, },
{ .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, },
{ .compatible = "usb4b4,6570", .data = &microchip_usb424_data, },
{ .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
{ .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
{ .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
@ -153,12 +152,15 @@ static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
{ .compatible = "usb1a40,0101", .data = &vialab_vl817_data, },
{ .compatible = "usb1a86,8091", .data = &wch_ch334_data, },
{ .compatible = "usb1da0,5511", .data = &parade_ps5511_data, },
{ .compatible = "usb1da0,55a1", .data = &parade_ps5511_data, },
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
{ .compatible = "usb2109,2817", .data = &vialab_vl817_data, },
{ .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, },
{ .compatible = "usb3431,6241", .data = &corechips_sl6341_data, },
{ .compatible = "usb3431,6341", .data = &corechips_sl6341_data, },
{ .compatible = "usb5986,1198", .data = &bison_intcamera_data, },
{}
};

View File

@ -92,7 +92,7 @@ static int tv_probe(struct usb_interface *interface,
goto error;
}
dev->udev = usb_get_dev(udev);
dev->udev = udev;
usb_set_intfdata(interface, dev);
return 0;
@ -108,7 +108,6 @@ static void tv_disconnect(struct usb_interface *interface)
dev = usb_get_intfdata (interface);
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
}

View File

@ -776,7 +776,7 @@ static int ljca_probe(struct usb_interface *interface,
init_completion(&adap->cmd_completion);
INIT_LIST_HEAD(&adap->client_list);
adap->intf = usb_get_intf(interface);
adap->intf = interface;
adap->usb_dev = usb_dev;
adap->dev = dev;
@ -787,7 +787,7 @@ static int ljca_probe(struct usb_interface *interface,
ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
if (ret) {
dev_err(dev, "bulk endpoints not found\n");
goto err_put;
goto err_destroy_mutex;
}
adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in));
adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out));
@ -797,14 +797,14 @@ static int ljca_probe(struct usb_interface *interface,
adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL);
if (!adap->rx_buf) {
ret = -ENOMEM;
goto err_put;
goto err_destroy_mutex;
}
/* alloc rx urb */
adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!adap->rx_urb) {
ret = -ENOMEM;
goto err_put;
goto err_destroy_mutex;
}
usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe,
adap->rx_buf, adap->rx_len, ljca_recv, adap);
@ -836,10 +836,7 @@ static int ljca_probe(struct usb_interface *interface,
err_free:
usb_free_urb(adap->rx_urb);
err_put:
usb_put_intf(adap->intf);
err_destroy_mutex:
mutex_destroy(&adap->mutex);
return ret;
@ -864,8 +861,6 @@ static void ljca_disconnect(struct usb_interface *interface)
usb_free_urb(adap->rx_urb);
usb_put_intf(adap->intf);
mutex_destroy(&adap->mutex);
}

View File

@ -312,7 +312,7 @@ static int sevseg_probe(struct usb_interface *interface,
if (!mydev)
goto error_mem;
mydev->udev = usb_get_dev(udev);
mydev->udev = udev;
mydev->intf = interface;
usb_set_intfdata(interface, mydev);
@ -338,7 +338,6 @@ static void sevseg_disconnect(struct usb_interface *interface)
mydev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
usb_put_dev(mydev->udev);
kfree(mydev);
dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
}

View File

@ -677,35 +677,32 @@ static int uss720_probe(struct usb_interface *intf,
struct parport_uss720_private *priv;
struct parport *pp;
unsigned char reg;
int ret;
int ret = -ENODEV;
dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n",
le16_to_cpu(usbdev->descriptor.idVendor),
le16_to_cpu(usbdev->descriptor.idProduct));
/* our known interfaces have 3 alternate settings */
if (intf->num_altsetting != 3) {
usb_put_dev(usbdev);
return -ENODEV;
}
if (intf->num_altsetting != 3)
goto bail_out_early;
ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
dev_dbg(&intf->dev, "set interface result %d\n", ret);
interface = intf->cur_altsetting;
if (interface->desc.bNumEndpoints < 2) {
usb_put_dev(usbdev);
return -ENODEV;
}
if (interface->desc.bNumEndpoints < 2)
goto bail_out_early;
/*
* Allocate parport interface
*/
ret = -ENOMEM;
priv = kzalloc_obj(struct parport_uss720_private);
if (!priv) {
usb_put_dev(usbdev);
return -ENOMEM;
}
if (!priv)
goto bail_out_early;
priv->pp = NULL;
priv->usbdev = usbdev;
kref_init(&priv->ref_count);
@ -752,6 +749,10 @@ static int uss720_probe(struct usb_interface *intf,
kill_all_async_requests_priv(priv);
kref_put(&priv->ref_count, destroy_priv);
return -ENODEV;
bail_out_early:
usb_put_dev(usbdev);
return ret;
}
static void uss720_disconnect(struct usb_interface *intf)

View File

@ -1600,7 +1600,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
/* log core options (read using indexed model) */
reg = musb_read_configdata(mbase);
strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
strscpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
if (reg & MUSB_CONFIGDATA_DYNFIFO) {
strcat(aInfo, ", dyn FIFOs");
musb->dyn_fifo = true;

View File

@ -40,7 +40,6 @@ struct usbhsg_gpriv {
struct usb_gadget gadget;
struct usbhs_mod mod;
struct usbhsg_uep *uep;
int uep_size;
struct usb_gadget_driver *driver;
@ -53,6 +52,7 @@ struct usbhsg_gpriv {
#define USBHSG_STATUS_WEDGE (1 << 2)
#define USBHSG_STATUS_SELF_POWERED (1 << 3)
#define USBHSG_STATUS_SOFT_CONNECT (1 << 4)
struct usbhsg_uep uep[] __counted_by(uep_size);
};
struct usbhsg_recip_handle {
@ -1084,15 +1084,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
int i;
int ret;
gpriv = kzalloc_obj(struct usbhsg_gpriv);
gpriv = kzalloc_flex(*gpriv, uep, pipe_size);
if (!gpriv)
return -ENOMEM;
uep = kzalloc_objs(struct usbhsg_uep, pipe_size);
if (!uep) {
ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv;
}
gpriv->uep_size = pipe_size;
gpriv->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_UNDEFINED);
dev_info(dev, "%stransceiver found\n",
@ -1115,8 +1111,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
gpriv->mod.name = "gadget";
gpriv->mod.start = usbhsg_start;
gpriv->mod.stop = usbhsg_stop;
gpriv->uep = uep;
gpriv->uep_size = pipe_size;
usbhsg_status_init(gpriv);
/*
@ -1175,9 +1169,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
return 0;
err_add_udc:
kfree(gpriv->uep);
usbhs_mod_gadget_probe_err_gpriv:
kfree(gpriv);
return ret;
@ -1189,6 +1180,5 @@ void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
usb_del_gadget_udc(&gpriv->gadget);
kfree(gpriv->uep);
kfree(gpriv);
}

View File

@ -6,7 +6,7 @@
* Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
*
* Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
* Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
*
* And tested with help of WB Electronics
*/

View File

@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(2) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff), /* Telit FN990A (ECM) */
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff), /* Telit FN990A (MBIM) */
.driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990A (PCIe) */
.driver_info = RSVD(0) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff), /* Telit FN990A (rmnet + audio) */

View File

@ -1600,29 +1600,29 @@ static int ti_download_firmware(struct ti_device *tdev)
if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) {
switch (le16_to_cpu(dev->descriptor.idProduct)) {
case MTS_CDMA_PRODUCT_ID:
strcpy(buf, "mts_cdma.fw");
strscpy(buf, "mts_cdma.fw");
break;
case MTS_GSM_PRODUCT_ID:
strcpy(buf, "mts_gsm.fw");
strscpy(buf, "mts_gsm.fw");
break;
case MTS_EDGE_PRODUCT_ID:
strcpy(buf, "mts_edge.fw");
strscpy(buf, "mts_edge.fw");
break;
case MTS_MT9234MU_PRODUCT_ID:
strcpy(buf, "mts_mt9234mu.fw");
strscpy(buf, "mts_mt9234mu.fw");
break;
case MTS_MT9234ZBA_PRODUCT_ID:
strcpy(buf, "mts_mt9234zba.fw");
strscpy(buf, "mts_mt9234zba.fw");
break;
case MTS_MT9234ZBAOLD_PRODUCT_ID:
strcpy(buf, "mts_mt9234zba.fw");
strscpy(buf, "mts_mt9234zba.fw");
break; }
}
if (buf[0] == '\0') {
if (tdev->td_is_3410)
strcpy(buf, "ti_3410.fw");
strscpy(buf, "ti_3410.fw");
else
strcpy(buf, "ti_5052.fw");
strscpy(buf, "ti_5052.fw");
}
status = request_firmware(&fw_p, buf, &dev->dev);
}

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