mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
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:
commit
99ef60d119
|
|
@ -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**
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -85,6 +85,7 @@ required:
|
|||
|
||||
allOf:
|
||||
- $ref: usb-drd.yaml#
|
||||
- $ref: usb-xhci.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
|
|
|||
79
Documentation/devicetree/bindings/usb/corechips,sl6341.yaml
Normal file
79
Documentation/devicetree/bindings/usb/corechips,sl6341.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -10,6 +10,8 @@ title: NXP iMX8MP Soc USB Controller
|
|||
maintainers:
|
||||
- Li Jun <jun.li@nxp.com>
|
||||
|
||||
deprecated: true
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
67
Documentation/devicetree/bindings/usb/maxim,max3421.yaml
Normal file
67
Documentation/devicetree/bindings/usb/maxim,max3421.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
123
Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml
Normal file
123
Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml
Normal 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;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
85
Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml
Normal file
85
Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml
Normal 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";
|
||||
};
|
||||
...
|
||||
|
|
@ -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";
|
||||
};
|
||||
62
Documentation/devicetree/bindings/usb/terminus,fe11.yaml
Normal file
62
Documentation/devicetree/bindings/usb/terminus,fe11.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
100
Documentation/devicetree/bindings/usb/ti,dwc3.yaml
Normal file
100
Documentation/devicetree/bindings/usb/ti,dwc3.yaml
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/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;
|
||||
};
|
||||
};
|
||||
...
|
||||
120
Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml
Normal file
120
Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml
Normal 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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,.*":
|
||||
|
|
|
|||
11
MAINTAINERS
11
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
774
drivers/power/supply/max77759_charger.c
Normal file
774
drivers/power/supply/max77759_charger.c
Normal 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, ®val);
|
||||
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, ®val, &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, ®val);
|
||||
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, ®val, &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, ®val);
|
||||
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, ®val);
|
||||
|
||||
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, ®val);
|
||||
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,
|
||||
¤t_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");
|
||||
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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, \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
448
drivers/usb/dwc3/dwc3-imx.c
Normal 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");
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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){ \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2514", .data = µchip_usb2514_data, },
|
||||
{ .compatible = "usb424,2517", .data = µchip_usb424_data, },
|
||||
|
|
@ -137,13 +136,13 @@ static const struct of_device_id onboard_dev_match[] = {
|
|||
{ .compatible = "usb424,5744", .data = µchip_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 = µchip_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 = ¶de_ps5511_data, },
|
||||
{ .compatible = "usb1da0,55a1", .data = ¶de_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, },
|
||||
{}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue
Block a user