mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 06:04:23 +08:00
USB/Thunderbolt update for 6.12-rc1
Here is the large set of USB and Thunderbolt changes for 6.12-rc1. Nothing "major" in here, except for a new 9p network gadget that has been worked on for a long time (all of the needed acks are here.) Other than that, it's the usual set of: - Thunderbolt / USB4 driver updates and additions for new hardware - dwc3 driver updates and new features added - xhci driver updates - typec driver updates - USB gadget updates and api additions to make some gadgets more configurable by userspace - dwc2 driver updates - usb phy driver updates - usbip feature additions - other minor USB driver updates All of these have been in linux-next for a long time with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZvU0/g8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ykGcACfSqouxRg8FRtq+nIKHWXI9lOTnVcAoKd9PAgq 1i7yCNopPEPEW8sjz1GX =mY+S -----END PGP SIGNATURE----- Merge tag 'usb-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB/Thunderbolt updates from Greg KH: "Here is the large set of USB and Thunderbolt changes for 6.12-rc1. Nothing "major" in here, except for a new 9p network gadget that has been worked on for a long time (all of the needed acks are here) Other than that, it's the usual set of: - Thunderbolt / USB4 driver updates and additions for new hardware - dwc3 driver updates and new features added - xhci driver updates - typec driver updates - USB gadget updates and api additions to make some gadgets more configurable by userspace - dwc2 driver updates - usb phy driver updates - usbip feature additions - other minor USB driver updates All of these have been in linux-next for a long time with no reported issues" * tag 'usb-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (145 commits) sub: cdns3: Use predefined PCI vendor ID constant sub: cdns2: Use predefined PCI vendor ID constant USB: misc: yurex: fix race between read and write USB: misc: cypress_cy7c63: check for short transfer USB: appledisplay: close race between probe and completion handler USB: class: CDC-ACM: fix race between get_serial and set_serial usb: r8a66597-hcd: make read-only const arrays static usb: typec: ucsi: Fix busy loop on ASUS VivoBooks usb: dwc3: rtk: Clean up error code in __get_dwc3_maximum_speed() usb: storage: ene_ub6250: Fix right shift warnings usb: roles: Improve the fix for a false positive recursive locking complaint locking/mutex: Introduce mutex_init_with_key() locking/mutex: Define mutex_init() once net/9p/usbg: fix CONFIG_USB_GADGET dependency usb: xhci: fix loss of data on Cadence xHC usb: xHCI: add XHCI_RESET_ON_RESUME quirk for Phytium xHCI host usb: dwc3: imx8mp: disable SS_CON and U3 wakeup for system sleep usb: dwc3: imx8mp: add 2 software managed quirk properties for host mode usb: host: xhci-plat: Parse xhci-missing_cas_quirk and apply quirk usb: misc: onboard_usb_dev: add Microchip usb5744 SMBus programming support ...
This commit is contained in:
commit
4965ddb166
@ -6,3 +6,10 @@ Description:
|
||||
This item contains just one readonly attribute: port_num.
|
||||
It contains the port number of the /dev/ttyGS<n> device
|
||||
associated with acm function's instance "name".
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/acm.name/protocol
|
||||
Date: Aug 2024
|
||||
KernelVersion: 6.13
|
||||
Description:
|
||||
Reported bInterfaceProtocol for the ACM device. For legacy
|
||||
reasons, this defaults to 1 (USB_CDC_ACM_PROTO_AT_V25TER).
|
||||
|
@ -30,4 +30,12 @@ Description:
|
||||
req_number the number of pre-allocated requests
|
||||
for both capture and playback
|
||||
function_name name of the interface
|
||||
p_it_name playback input terminal name
|
||||
p_it_ch_name playback channels name
|
||||
p_ot_name playback output terminal name
|
||||
p_fu_vol_name playback mute/volume functional unit name
|
||||
c_it_name capture input terminal name
|
||||
c_it_ch_name capture channels name
|
||||
c_ot_name capture output terminal name
|
||||
c_fu_vol_name capture mute/volume functional unit name
|
||||
===================== =======================================
|
||||
|
@ -35,6 +35,17 @@ Description:
|
||||
req_number the number of pre-allocated requests
|
||||
for both capture and playback
|
||||
function_name name of the interface
|
||||
if_ctrl_name topology control name
|
||||
clksrc_in_name input clock name
|
||||
clksrc_out_name output clock name
|
||||
p_it_name playback input terminal name
|
||||
p_it_ch_name playback input first channel name
|
||||
p_ot_name playback output terminal name
|
||||
p_fu_vol_name playback mute/volume function unit name
|
||||
c_it_name capture input terminal name
|
||||
c_it_ch_name capture input first channel name
|
||||
c_ot_name capture output terminal name
|
||||
c_fu_vol_name capture mute/volume functional unit name
|
||||
c_terminal_type code of the capture terminal type
|
||||
p_terminal_type code of the playback terminal type
|
||||
===================== =======================================
|
||||
|
@ -87,6 +87,12 @@ properties:
|
||||
maximum: 119
|
||||
default: 100
|
||||
|
||||
nxp,sim:
|
||||
description:
|
||||
The system integration module (SIM) provides system control and chip
|
||||
configuration registers.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -110,6 +116,17 @@ allOf:
|
||||
required:
|
||||
- fsl,anatop
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: fsl,imx7ulp-usbphy
|
||||
then:
|
||||
required:
|
||||
- nxp,sim
|
||||
else:
|
||||
properties:
|
||||
nxp,sim: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
52
Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml
Normal file
52
Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml
Normal file
@ -0,0 +1,52 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/fsl,ls1028a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale layerscape SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,ls1028a-dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- fsl,ls1028a-dwc3
|
||||
- const: snps,dwc3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwc3.yaml#
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb@fe800000 {
|
||||
compatible = "fsl,ls1028a-dwc3", "snps,dwc3";
|
||||
reg = <0xfe800000 0x100000>;
|
||||
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -1,110 +0,0 @@
|
||||
MSM SoC HSUSB controllers
|
||||
|
||||
EHCI
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "qcom,ehci-host"
|
||||
- regs: offset and length of the register set in the memory map
|
||||
- usb-phy: phandle for the PHY device
|
||||
|
||||
Example EHCI controller device node:
|
||||
|
||||
ehci: ehci@f9a55000 {
|
||||
compatible = "qcom,ehci-host";
|
||||
reg = <0xf9a55000 0x400>;
|
||||
usb-phy = <&usb_otg>;
|
||||
};
|
||||
|
||||
USB PHY with optional OTG:
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain:
|
||||
"qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY
|
||||
"qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY
|
||||
|
||||
- regs: Offset and length of the register set in the memory map
|
||||
- interrupts: interrupt-specifier for the OTG interrupt.
|
||||
|
||||
- clocks: A list of phandle + clock-specifier pairs for the
|
||||
clocks listed in clock-names
|
||||
- clock-names: Should contain the following:
|
||||
"phy" USB PHY reference clock
|
||||
"core" Protocol engine clock
|
||||
"iface" Interface bus clock
|
||||
"alt_core" Protocol engine clock for targets with asynchronous
|
||||
reset methodology. (optional)
|
||||
|
||||
- vdccx-supply: phandle to the regulator for the vdd supply for
|
||||
digital circuit operation.
|
||||
- v1p8-supply: phandle to the regulator for the 1.8V supply
|
||||
- v3p3-supply: phandle to the regulator for the 3.3V supply
|
||||
|
||||
- resets: A list of phandle + reset-specifier pairs for the
|
||||
resets listed in reset-names
|
||||
- reset-names: Should contain the following:
|
||||
"phy" USB PHY controller reset
|
||||
"link" USB LINK controller reset
|
||||
|
||||
- qcom,otg-control: OTG control (VBUS and ID notifications) can be one of
|
||||
1 - PHY control
|
||||
2 - PMIC control
|
||||
|
||||
Optional properties:
|
||||
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
|
||||
|
||||
- switch-gpio: A phandle + gpio-specifier pair. Some boards are using Dual
|
||||
SPDT USB Switch, witch is controlled by GPIO to de/multiplex
|
||||
D+/D- USB lines between connectors.
|
||||
|
||||
- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device
|
||||
Mode Eye Diagram test. Start address at which these values will be
|
||||
written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as
|
||||
"do not overwrite default value at this address".
|
||||
For example: qcom,phy-init-sequence = < -1 0x63 >;
|
||||
Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1.
|
||||
|
||||
- qcom,phy-num: Select number of pyco-phy to use, can be one of
|
||||
0 - PHY one, default
|
||||
1 - Second PHY
|
||||
Some platforms may have configuration to allow USB
|
||||
controller work with any of the two HSPHYs present.
|
||||
|
||||
- qcom,vdd-levels: This property must be a list of three integer values
|
||||
(no, min, max) where each value represents either a voltage
|
||||
in microvolts or a value corresponding to voltage corner.
|
||||
|
||||
- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy
|
||||
and controller driver therefore enables pull-up explicitly
|
||||
before starting controller using usbcmd run/stop bit.
|
||||
|
||||
- extcon: phandles to external connector devices. First phandle
|
||||
should point to external connector, which provide "USB"
|
||||
cable events, the second should point to external connector
|
||||
device, which provide "USB-HOST" cable events. If one of
|
||||
the external connector devices is not required empty <0>
|
||||
phandle should be specified.
|
||||
|
||||
Example HSUSB OTG controller device node:
|
||||
|
||||
usb@f9a55000 {
|
||||
compatible = "qcom,usb-otg-snps";
|
||||
reg = <0xf9a55000 0x400>;
|
||||
interrupts = <0 134 0>;
|
||||
dr_mode = "peripheral";
|
||||
|
||||
clocks = <&gcc GCC_XO_CLK>, <&gcc GCC_USB_HS_SYSTEM_CLK>,
|
||||
<&gcc GCC_USB_HS_AHB_CLK>;
|
||||
|
||||
clock-names = "phy", "core", "iface";
|
||||
|
||||
vddcx-supply = <&pm8841_s2_corner>;
|
||||
v1p8-supply = <&pm8941_l6>;
|
||||
v3p3-supply = <&pm8941_l24>;
|
||||
|
||||
resets = <&gcc GCC_USB2A_PHY_BCR>, <&gcc GCC_USB_HS_BCR>;
|
||||
reset-names = "phy", "link";
|
||||
|
||||
qcom,otg-control = <1>;
|
||||
qcom,phy-init-sequence = < -1 0x63 >;
|
||||
qcom,vdd-levels = <1 5 7>;
|
||||
};
|
@ -52,6 +52,7 @@ properties:
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,x1e80100-dwc3
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
- const: qcom,dwc3
|
||||
|
||||
reg:
|
||||
@ -289,6 +290,7 @@ allOf:
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
- qcom,x1e80100-dwc3
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
@ -428,6 +430,21 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq5332-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 3
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: pwr_event
|
||||
- const: dp_hs_phy_irq
|
||||
- const: dm_hs_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-dwc3
|
||||
then:
|
||||
properties:
|
||||
@ -486,6 +503,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8180x-dwc3-mp
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
|
@ -13,10 +13,9 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: ti,j721e-usb
|
||||
- const: ti,am64-usb
|
||||
- items:
|
||||
- const: ti,j721e-usb
|
||||
- const: ti,am64-usb
|
||||
- const: ti,j721e-usb
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -48,11 +48,66 @@ For server running on QEMU host with virtio transport::
|
||||
|
||||
mount -t 9p -o trans=virtio <mount_tag> /mnt/9
|
||||
|
||||
where mount_tag is the tag associated by the server to each of the exported
|
||||
where mount_tag is the tag generated by the server to each of the exported
|
||||
mount points. Each 9P export is seen by the client as a virtio device with an
|
||||
associated "mount_tag" property. Available mount tags can be
|
||||
seen by reading /sys/bus/virtio/drivers/9pnet_virtio/virtio<n>/mount_tag files.
|
||||
|
||||
USBG Usage
|
||||
==========
|
||||
|
||||
To mount a 9p FS on a USB Host accessible via the gadget at runtime::
|
||||
|
||||
mount -t 9p -o trans=usbg,aname=/path/to/fs <device> /mnt/9
|
||||
|
||||
To mount a 9p FS on a USB Host accessible via the gadget as root filesystem::
|
||||
|
||||
root=<device> rootfstype=9p rootflags=trans=usbg,cache=loose,uname=root,access=0,dfltuid=0,dfltgid=0,aname=/path/to/rootfs
|
||||
|
||||
where <device> is the tag associated by the usb gadget transport.
|
||||
It is defined by the configfs instance name.
|
||||
|
||||
USBG Example
|
||||
============
|
||||
|
||||
The USB host exports a filesystem, while the gadget on the USB device
|
||||
side makes it mountable.
|
||||
|
||||
Diod (9pfs server) and the forwarder are on the development host, where
|
||||
the root filesystem is actually stored. The gadget is initialized during
|
||||
boot (or later) on the embedded board. Then the forwarder will find it
|
||||
on the USB bus and start forwarding requests.
|
||||
|
||||
In this case the 9p requests come from the device and are handled by the
|
||||
host. The reason is that USB device ports are normally not available on
|
||||
PCs, so a connection in the other direction would not work.
|
||||
|
||||
When using the usbg transport, for now there is no native usb host
|
||||
service capable to handle the requests from the gadget driver. For
|
||||
this we have to use the extra python tool p9_fwd.py from tools/usb.
|
||||
|
||||
Just start the 9pfs capable network server like diod/nfs-ganesha e.g.::
|
||||
|
||||
$ diod -f -n -d 0 -S -l 0.0.0.0:9999 -e $PWD
|
||||
|
||||
Optionaly scan your bus if there are more then one usbg gadgets to find their path::
|
||||
|
||||
$ python $kernel_dir/tools/usb/p9_fwd.py list
|
||||
|
||||
Bus | Addr | Manufacturer | Product | ID | Path
|
||||
--- | ---- | ---------------- | ---------------- | --------- | ----
|
||||
2 | 67 | unknown | unknown | 1d6b:0109 | 2-1.1.2
|
||||
2 | 68 | unknown | unknown | 1d6b:0109 | 2-1.1.3
|
||||
|
||||
Then start the python transport::
|
||||
|
||||
$ python $kernel_dir/tools/usb/p9_fwd.py --path 2-1.1.2 connect -p 9999
|
||||
|
||||
After that the gadget driver can be used as described above.
|
||||
|
||||
One use-case is to use it as an alternative to NFS root booting during
|
||||
the development of embedded Linux devices.
|
||||
|
||||
Options
|
||||
=======
|
||||
|
||||
@ -68,6 +123,7 @@ Options
|
||||
virtio connect to the next virtio channel available
|
||||
(from QEMU with trans_virtio module)
|
||||
rdma connect to a specified RDMA channel
|
||||
usbg connect to a specified usb gadget channel
|
||||
======== ============================================
|
||||
|
||||
uname=name user name to attempt mount as on the remote server. The
|
||||
|
39
Documentation/usb/functionfs-desc.rst
Normal file
39
Documentation/usb/functionfs-desc.rst
Normal file
@ -0,0 +1,39 @@
|
||||
======================
|
||||
FunctionFS Descriptors
|
||||
======================
|
||||
|
||||
Some of the descriptors that can be written to the FFS gadget are
|
||||
described below. Device and configuration descriptors are handled
|
||||
by the composite gadget and are not written by the user to the
|
||||
FFS gadget.
|
||||
|
||||
Descriptors are written to the "ep0" file in the FFS gadget
|
||||
following the descriptor header.
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/usb/functionfs.h
|
||||
:doc: descriptors
|
||||
|
||||
Interface Descriptors
|
||||
---------------------
|
||||
|
||||
Standard USB interface descriptors may be written. The class/subclass of the
|
||||
most recent interface descriptor determines what type of class-specific
|
||||
descriptors are accepted.
|
||||
|
||||
Class-Specific Descriptors
|
||||
--------------------------
|
||||
|
||||
Class-specific descriptors are accepted only for the class/subclass of the
|
||||
most recent interface descriptor. The following are some of the
|
||||
class-specific descriptors that are supported.
|
||||
|
||||
DFU Functional Descriptor
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the interface class is USB_CLASS_APP_SPEC and the interface subclass
|
||||
is USB_SUBCLASS_DFU, a DFU functional descriptor can be provided.
|
||||
The DFU functional descriptor is a described in the USB specification for
|
||||
Device Firmware Upgrade (DFU), version 1.1 as of this writing.
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/usb/functionfs.h
|
||||
:doc: usb_dfu_functional_descriptor
|
@ -25,6 +25,8 @@ interface numbers starting from zero). The FunctionFS changes
|
||||
them as needed also handling situation when numbers differ in
|
||||
different configurations.
|
||||
|
||||
For more information about FunctionFS descriptors see :doc:`functionfs-desc`
|
||||
|
||||
When descriptors and strings are written "ep#" files appear
|
||||
(one for each declared endpoint) which handle communication on
|
||||
a single endpoint. Again, FunctionFS takes care of the real
|
||||
|
@ -765,6 +765,17 @@ The uac2 function provides these attributes in its function directory:
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
function_name name of the interface
|
||||
if_ctrl_name topology control name
|
||||
clksrc_in_name input clock name
|
||||
clksrc_out_name output clock name
|
||||
p_it_name playback input terminal name
|
||||
p_it_ch_name playback input first channel name
|
||||
p_ot_name playback output terminal name
|
||||
p_fu_vol_name playback function unit name
|
||||
c_it_name capture input terminal name
|
||||
c_it_ch_name capture input first channel name
|
||||
c_ot_name capture output terminal name
|
||||
c_fu_vol_name capture functional unit name
|
||||
c_terminal_type code of the capture terminal type
|
||||
p_terminal_type code of the playback terminal type
|
||||
================ ====================================================
|
||||
@ -957,6 +968,14 @@ The uac1 function provides these attributes in its function directory:
|
||||
req_number the number of pre-allocated requests for both capture
|
||||
and playback
|
||||
function_name name of the interface
|
||||
p_it_name playback input terminal name
|
||||
p_it_ch_name playback channels name
|
||||
p_ot_name playback output terminal name
|
||||
p_fu_vol_name playback mute/volume functional unit name
|
||||
c_it_name capture input terminal name
|
||||
c_it_ch_name capture channels name
|
||||
c_ot_name capture output terminal name
|
||||
c_fu_vol_name capture mute/volume functional unit name
|
||||
================ ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
@ -11,6 +11,7 @@ USB support
|
||||
dwc3
|
||||
ehci
|
||||
functionfs
|
||||
functionfs-desc
|
||||
gadget_configfs
|
||||
gadget_hid
|
||||
gadget_multi
|
||||
|
@ -32,40 +32,20 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
|
||||
goto out_put;
|
||||
|
||||
/*
|
||||
* Try to find physical device walking upwards to the hierarcy.
|
||||
* We need to do this because the xHCI driver might not yet be
|
||||
* bound so the USB3 SuperSpeed ports are not yet created.
|
||||
* Ignore USB3 ports here as USB core will set up device links between
|
||||
* tunneled USB3 devices and NHI host during USB device creation.
|
||||
* USB3 ports might not even have a physical device yet if xHCI driver
|
||||
* isn't bound yet.
|
||||
*/
|
||||
do {
|
||||
dev = acpi_get_first_physical_node(adev);
|
||||
if (dev)
|
||||
break;
|
||||
|
||||
adev = acpi_dev_parent(adev);
|
||||
} while (adev);
|
||||
|
||||
/*
|
||||
* Check that the device is PCIe. This is because USB3
|
||||
* SuperSpeed ports have this property and they are not power
|
||||
* managed with the xHCI and the SuperSpeed hub so we create the
|
||||
* link from xHCI instead.
|
||||
*/
|
||||
while (dev && !dev_is_pci(dev))
|
||||
dev = dev->parent;
|
||||
|
||||
if (!dev)
|
||||
dev = acpi_get_first_physical_node(adev);
|
||||
if (!dev || !dev_is_pci(dev))
|
||||
goto out_put;
|
||||
|
||||
/*
|
||||
* Check that this actually matches the type of device we
|
||||
* expect. It should either be xHCI or PCIe root/downstream
|
||||
* port.
|
||||
*/
|
||||
/* Check that this matches a PCIe root/downstream port. */
|
||||
pdev = to_pci_dev(dev);
|
||||
if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI ||
|
||||
(pci_is_pcie(pdev) &&
|
||||
(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) {
|
||||
if (pci_is_pcie(pdev) &&
|
||||
(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)) {
|
||||
const struct device_link *link;
|
||||
|
||||
/*
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@ -34,6 +35,14 @@
|
||||
|
||||
#define COUNTER_SET_LEN 3
|
||||
|
||||
/*
|
||||
* USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms
|
||||
* probed to give good results.
|
||||
*/
|
||||
#define MIN_DWELL_TIME 100 /* ms */
|
||||
#define MAX_DWELL_TIME 500 /* ms */
|
||||
#define DWELL_SAMPLE_INTERVAL 10
|
||||
|
||||
/* Sideband registers and their sizes as defined in the USB4 spec */
|
||||
struct sb_reg {
|
||||
unsigned int reg;
|
||||
@ -394,8 +403,15 @@ out:
|
||||
* @ber_level: Current BER level contour value
|
||||
* @voltage_steps: Number of mandatory voltage steps
|
||||
* @max_voltage_offset: Maximum mandatory voltage offset (in mV)
|
||||
* @voltage_steps_optional_range: Number of voltage steps for optional range
|
||||
* @max_voltage_offset_optional_range: Maximum voltage offset for the optional
|
||||
* range (in mV).
|
||||
* @time_steps: Number of time margin steps
|
||||
* @max_time_offset: Maximum time margin offset (in mUI)
|
||||
* @voltage_time_offset: Offset for voltage / time for software margining
|
||||
* @dwell_time: Dwell time for software margining (in ms)
|
||||
* @error_counter: Error counter operation for software margining
|
||||
* @optional_voltage_offset_range: Enable optional extended voltage range
|
||||
* @software: %true if software margining is used instead of hardware
|
||||
* @time: %true if time margining is used instead of voltage
|
||||
* @right_high: %false if left/low margin test is performed, %true if
|
||||
@ -414,13 +430,37 @@ struct tb_margining {
|
||||
unsigned int ber_level;
|
||||
unsigned int voltage_steps;
|
||||
unsigned int max_voltage_offset;
|
||||
unsigned int voltage_steps_optional_range;
|
||||
unsigned int max_voltage_offset_optional_range;
|
||||
unsigned int time_steps;
|
||||
unsigned int max_time_offset;
|
||||
unsigned int voltage_time_offset;
|
||||
unsigned int dwell_time;
|
||||
enum usb4_margin_sw_error_counter error_counter;
|
||||
bool optional_voltage_offset_range;
|
||||
bool software;
|
||||
bool time;
|
||||
bool right_high;
|
||||
};
|
||||
|
||||
static int margining_modify_error_counter(struct tb_margining *margining,
|
||||
u32 lanes, enum usb4_margin_sw_error_counter error_counter)
|
||||
{
|
||||
struct usb4_port_margining_params params = { 0 };
|
||||
struct tb_port *port = margining->port;
|
||||
u32 result;
|
||||
|
||||
if (error_counter != USB4_MARGIN_SW_ERROR_COUNTER_CLEAR &&
|
||||
error_counter != USB4_MARGIN_SW_ERROR_COUNTER_STOP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
params.error_counter = error_counter;
|
||||
params.lanes = lanes;
|
||||
|
||||
return usb4_port_sw_margin(port, margining->target, margining->index,
|
||||
¶ms, &result);
|
||||
}
|
||||
|
||||
static bool supports_software(const struct tb_margining *margining)
|
||||
{
|
||||
return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
|
||||
@ -454,6 +494,12 @@ independent_time_margins(const struct tb_margining *margining)
|
||||
return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]);
|
||||
}
|
||||
|
||||
static bool
|
||||
supports_optional_voltage_offset_range(const struct tb_margining *margining)
|
||||
{
|
||||
return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
margining_ber_level_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
@ -553,6 +599,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
|
||||
margining->voltage_steps);
|
||||
seq_printf(s, "# maximum voltage offset: %u mV\n",
|
||||
margining->max_voltage_offset);
|
||||
seq_printf(s, "# optional voltage offset range support: %s\n",
|
||||
str_yes_no(supports_optional_voltage_offset_range(margining)));
|
||||
if (supports_optional_voltage_offset_range(margining)) {
|
||||
seq_printf(s, "# voltage margin steps, optional range: %u\n",
|
||||
margining->voltage_steps_optional_range);
|
||||
seq_printf(s, "# maximum voltage offset, optional range: %u mV\n",
|
||||
margining->max_voltage_offset_optional_range);
|
||||
}
|
||||
|
||||
switch (independent_voltage_margins(margining)) {
|
||||
case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
|
||||
@ -667,6 +721,198 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_lanes);
|
||||
|
||||
static ssize_t
|
||||
margining_voltage_time_offset_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
unsigned int max_margin;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint_from_user(user_buf, count, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
if (!margining->software)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (margining->time)
|
||||
max_margin = margining->time_steps;
|
||||
else
|
||||
if (margining->optional_voltage_offset_range)
|
||||
max_margin = margining->voltage_steps_optional_range;
|
||||
else
|
||||
max_margin = margining->voltage_steps;
|
||||
|
||||
margining->voltage_time_offset = clamp(val, 0, max_margin);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int margining_voltage_time_offset_show(struct seq_file *s,
|
||||
void *not_used)
|
||||
{
|
||||
const struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
if (!margining->software)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
seq_printf(s, "%d\n", margining->voltage_time_offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_voltage_time_offset);
|
||||
|
||||
static ssize_t
|
||||
margining_error_counter_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
enum usb4_margin_sw_error_counter error_counter;
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
char *buf;
|
||||
|
||||
buf = validate_and_copy_from_user(user_buf, &count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
buf[count - 1] = '\0';
|
||||
|
||||
if (!strcmp(buf, "nop"))
|
||||
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_NOP;
|
||||
else if (!strcmp(buf, "clear"))
|
||||
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR;
|
||||
else if (!strcmp(buf, "start"))
|
||||
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_START;
|
||||
else if (!strcmp(buf, "stop"))
|
||||
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
if (!margining->software)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
margining->error_counter = error_counter;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int margining_error_counter_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
const struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
if (!margining->software)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (margining->error_counter) {
|
||||
case USB4_MARGIN_SW_ERROR_COUNTER_NOP:
|
||||
seq_puts(s, "[nop] clear start stop\n");
|
||||
break;
|
||||
case USB4_MARGIN_SW_ERROR_COUNTER_CLEAR:
|
||||
seq_puts(s, "nop [clear] start stop\n");
|
||||
break;
|
||||
case USB4_MARGIN_SW_ERROR_COUNTER_START:
|
||||
seq_puts(s, "nop clear [start] stop\n");
|
||||
break;
|
||||
case USB4_MARGIN_SW_ERROR_COUNTER_STOP:
|
||||
seq_puts(s, "nop clear start [stop]\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_error_counter);
|
||||
|
||||
static ssize_t
|
||||
margining_dwell_time_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint_from_user(user_buf, count, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
if (!margining->software)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
margining->dwell_time = clamp(val, MIN_DWELL_TIME, MAX_DWELL_TIME);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int margining_dwell_time_show(struct seq_file *s, void *not_used)
|
||||
{
|
||||
struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
if (!margining->software)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
seq_printf(s, "%d\n", margining->dwell_time);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_dwell_time);
|
||||
|
||||
static ssize_t
|
||||
margining_optional_voltage_offset_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool_from_user(user_buf, count, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
margining->optional_voltage_offset_range = val;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int margining_optional_voltage_offset_show(struct seq_file *s,
|
||||
void *not_used)
|
||||
{
|
||||
struct tb_margining *margining = s->private;
|
||||
struct tb *tb = margining->port->sw->tb;
|
||||
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
|
||||
seq_printf(s, "%u\n", margining->optional_voltage_offset_range);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_optional_voltage_offset);
|
||||
|
||||
static ssize_t margining_mode_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
@ -739,6 +985,51 @@ static int margining_mode_show(struct seq_file *s, void *not_used)
|
||||
}
|
||||
DEBUGFS_ATTR_RW(margining_mode);
|
||||
|
||||
static int margining_run_sw(struct tb_margining *margining,
|
||||
struct usb4_port_margining_params *params)
|
||||
{
|
||||
u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL;
|
||||
int ret, i;
|
||||
|
||||
ret = usb4_port_sw_margin(margining->port, margining->target, margining->index,
|
||||
params, margining->results);
|
||||
if (ret)
|
||||
goto out_stop;
|
||||
|
||||
for (i = 0; i <= nsamples; i++) {
|
||||
u32 errors = 0;
|
||||
|
||||
ret = usb4_port_sw_margin_errors(margining->port, margining->target,
|
||||
margining->index, &margining->results[1]);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (margining->lanes == USB4_MARGIN_SW_LANE_0)
|
||||
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
|
||||
margining->results[1]);
|
||||
else if (margining->lanes == USB4_MARGIN_SW_LANE_1)
|
||||
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
|
||||
margining->results[1]);
|
||||
else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES)
|
||||
errors = margining->results[1];
|
||||
|
||||
/* Any errors stop the test */
|
||||
if (errors)
|
||||
break;
|
||||
|
||||
fsleep(DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
out_stop:
|
||||
/*
|
||||
* Stop the counters but don't clear them to allow the
|
||||
* different error counter configurations.
|
||||
*/
|
||||
margining_modify_error_counter(margining, margining->lanes,
|
||||
USB4_MARGIN_SW_ERROR_COUNTER_STOP);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int margining_run_write(void *data, u64 val)
|
||||
{
|
||||
struct tb_margining *margining = data;
|
||||
@ -779,36 +1070,43 @@ static int margining_run_write(void *data, u64 val)
|
||||
clx = ret;
|
||||
}
|
||||
|
||||
/* Clear the results */
|
||||
memset(margining->results, 0, sizeof(margining->results));
|
||||
|
||||
if (margining->software) {
|
||||
struct usb4_port_margining_params params = {
|
||||
.error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR,
|
||||
.lanes = margining->lanes,
|
||||
.time = margining->time,
|
||||
.voltage_time_offset = margining->voltage_time_offset,
|
||||
.right_high = margining->right_high,
|
||||
.optional_voltage_offset_range = margining->optional_voltage_offset_range,
|
||||
};
|
||||
|
||||
tb_port_dbg(port,
|
||||
"running software %s lane margining for %s lanes %u\n",
|
||||
margining->time ? "time" : "voltage", dev_name(dev),
|
||||
margining->lanes);
|
||||
ret = usb4_port_sw_margin(port, margining->target, margining->index,
|
||||
margining->lanes, margining->time,
|
||||
margining->right_high,
|
||||
USB4_MARGIN_SW_COUNTER_CLEAR);
|
||||
if (ret)
|
||||
goto out_clx;
|
||||
|
||||
ret = usb4_port_sw_margin_errors(port, margining->target,
|
||||
margining->index,
|
||||
&margining->results[0]);
|
||||
ret = margining_run_sw(margining, ¶ms);
|
||||
} else {
|
||||
struct usb4_port_margining_params params = {
|
||||
.ber_level = margining->ber_level,
|
||||
.lanes = margining->lanes,
|
||||
.time = margining->time,
|
||||
.right_high = margining->right_high,
|
||||
.optional_voltage_offset_range = margining->optional_voltage_offset_range,
|
||||
};
|
||||
|
||||
tb_port_dbg(port,
|
||||
"running hardware %s lane margining for %s lanes %u\n",
|
||||
margining->time ? "time" : "voltage", dev_name(dev),
|
||||
margining->lanes);
|
||||
/* Clear the results */
|
||||
margining->results[0] = 0;
|
||||
margining->results[1] = 0;
|
||||
ret = usb4_port_hw_margin(port, margining->target, margining->index,
|
||||
margining->lanes, margining->ber_level,
|
||||
margining->time, margining->right_high,
|
||||
|
||||
ret = usb4_port_hw_margin(port, margining->target, margining->index, ¶ms,
|
||||
margining->results);
|
||||
}
|
||||
|
||||
out_clx:
|
||||
if (down_sw)
|
||||
tb_switch_clx_enable(down_sw, clx);
|
||||
out_unlock:
|
||||
@ -837,6 +1135,13 @@ static ssize_t margining_results_write(struct file *file,
|
||||
margining->results[0] = 0;
|
||||
margining->results[1] = 0;
|
||||
|
||||
if (margining->software) {
|
||||
/* Clear the error counters */
|
||||
margining_modify_error_counter(margining,
|
||||
USB4_MARGIN_SW_ALL_LANES,
|
||||
USB4_MARGIN_SW_ERROR_COUNTER_CLEAR);
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
return count;
|
||||
}
|
||||
@ -852,6 +1157,8 @@ static void voltage_margin_show(struct seq_file *s,
|
||||
if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
|
||||
seq_puts(s, " exceeds maximum");
|
||||
seq_puts(s, "\n");
|
||||
if (margining->optional_voltage_offset_range)
|
||||
seq_puts(s, " optional voltage offset range enabled\n");
|
||||
}
|
||||
|
||||
static void time_margin_show(struct seq_file *s,
|
||||
@ -924,6 +1231,24 @@ static int margining_results_show(struct seq_file *s, void *not_used)
|
||||
voltage_margin_show(s, margining, val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
u32 lane_errors, result;
|
||||
|
||||
seq_printf(s, "0x%08x\n", margining->results[1]);
|
||||
result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]);
|
||||
|
||||
if (result == USB4_MARGIN_SW_LANE_0 ||
|
||||
result == USB4_MARGIN_SW_ALL_LANES) {
|
||||
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
|
||||
margining->results[1]);
|
||||
seq_printf(s, "# lane 0 errors: %u\n", lane_errors);
|
||||
}
|
||||
if (result == USB4_MARGIN_SW_LANE_1 ||
|
||||
result == USB4_MARGIN_SW_ALL_LANES) {
|
||||
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
|
||||
margining->results[1]);
|
||||
seq_printf(s, "# lane 1 errors: %u\n", lane_errors);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&tb->lock);
|
||||
@ -1091,6 +1416,15 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
|
||||
val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]);
|
||||
margining->max_voltage_offset = 74 + val * 2;
|
||||
|
||||
if (supports_optional_voltage_offset_range(margining)) {
|
||||
val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK,
|
||||
margining->caps[0]);
|
||||
margining->voltage_steps_optional_range = val;
|
||||
val = FIELD_GET(USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK,
|
||||
margining->caps[1]);
|
||||
margining->max_voltage_offset_optional_range = 74 + val * 2;
|
||||
}
|
||||
|
||||
if (supports_time(margining)) {
|
||||
val = FIELD_GET(USB4_MARGIN_CAP_1_TIME_STEPS_MASK, margining->caps[1]);
|
||||
margining->time_steps = val;
|
||||
@ -1127,6 +1461,22 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
|
||||
independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
|
||||
debugfs_create_file("margin", 0600, dir, margining,
|
||||
&margining_margin_fops);
|
||||
|
||||
margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR;
|
||||
margining->dwell_time = MIN_DWELL_TIME;
|
||||
|
||||
if (supports_optional_voltage_offset_range(margining))
|
||||
debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining,
|
||||
&margining_optional_voltage_offset_fops);
|
||||
|
||||
if (supports_software(margining)) {
|
||||
debugfs_create_file("voltage_time_offset", DEBUGFS_MODE, dir, margining,
|
||||
&margining_voltage_time_offset_fops);
|
||||
debugfs_create_file("error_counter", DEBUGFS_MODE, dir, margining,
|
||||
&margining_error_counter_fops);
|
||||
debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining,
|
||||
&margining_dwell_time_fops);
|
||||
}
|
||||
return margining;
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,9 @@ enum usb4_sb_opcode {
|
||||
#define USB4_MARGIN_CAP_0_TIME BIT(5)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6)
|
||||
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
|
||||
#define USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT BIT(19)
|
||||
#define USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK GENMASK(26, 20)
|
||||
#define USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK GENMASK(7, 0)
|
||||
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
|
||||
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
|
||||
#define USB4_MARGIN_CAP_1_TIME_MIN 0x0
|
||||
@ -72,6 +75,7 @@ enum usb4_sb_opcode {
|
||||
#define USB4_MARGIN_HW_RH BIT(4)
|
||||
#define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5)
|
||||
#define USB4_MARGIN_HW_BER_SHIFT 5
|
||||
#define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10)
|
||||
|
||||
/* Applicable to all margin values */
|
||||
#define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0)
|
||||
@ -82,13 +86,17 @@ enum usb4_sb_opcode {
|
||||
#define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24
|
||||
|
||||
/* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */
|
||||
#define USB4_MARGIN_SW_LANES_MASK GENMASK(2, 0)
|
||||
#define USB4_MARGIN_SW_LANE_0 0x0
|
||||
#define USB4_MARGIN_SW_LANE_1 0x1
|
||||
#define USB4_MARGIN_SW_ALL_LANES 0x7
|
||||
#define USB4_MARGIN_SW_TIME BIT(3)
|
||||
#define USB4_MARGIN_SW_RH BIT(4)
|
||||
#define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5)
|
||||
#define USB4_MARGIN_SW_VT_MASK GENMASK(12, 6)
|
||||
#define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13)
|
||||
#define USB4_MARGIN_SW_COUNTER_SHIFT 13
|
||||
#define USB4_MARGIN_SW_COUNTER_NOP 0x0
|
||||
#define USB4_MARGIN_SW_COUNTER_CLEAR 0x1
|
||||
#define USB4_MARGIN_SW_COUNTER_START 0x2
|
||||
#define USB4_MARGIN_SW_COUNTER_STOP 0x3
|
||||
|
||||
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0)
|
||||
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4)
|
||||
|
||||
#endif
|
||||
|
@ -1353,14 +1353,48 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index
|
||||
int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, u8 reg, const void *buf, u8 size);
|
||||
|
||||
/**
|
||||
* enum usb4_margin_sw_error_counter - Software margining error counter operation
|
||||
* @USB4_MARGIN_SW_ERROR_COUNTER_NOP: No change in counter setup
|
||||
* @USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: Set the error counter to 0, enable counter
|
||||
* @USB4_MARGIN_SW_ERROR_COUNTER_START: Start counter, count from last value
|
||||
* @USB4_MARGIN_SW_ERROR_COUNTER_STOP: Stop counter, do not clear value
|
||||
*/
|
||||
enum usb4_margin_sw_error_counter {
|
||||
USB4_MARGIN_SW_ERROR_COUNTER_NOP,
|
||||
USB4_MARGIN_SW_ERROR_COUNTER_CLEAR,
|
||||
USB4_MARGIN_SW_ERROR_COUNTER_START,
|
||||
USB4_MARGIN_SW_ERROR_COUNTER_STOP,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct usb4_port_margining_params - USB4 margining parameters
|
||||
* @error_counter: Error counter operation for software margining
|
||||
* @ber_level: Current BER level contour value
|
||||
* @lanes: %0, %1 or %7 (all)
|
||||
* @voltage_time_offset: Offset for voltage / time for software margining
|
||||
* @optional_voltage_offset_range: Enable optional extended voltage range
|
||||
* @right_high: %false if left/low margin test is performed, %true if right/high
|
||||
* @time: %true if time margining is used instead of voltage
|
||||
*/
|
||||
struct usb4_port_margining_params {
|
||||
enum usb4_margin_sw_error_counter error_counter;
|
||||
u32 ber_level;
|
||||
u32 lanes;
|
||||
u32 voltage_time_offset;
|
||||
bool optional_voltage_offset_range;
|
||||
bool right_high;
|
||||
bool time;
|
||||
};
|
||||
|
||||
int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, u32 *caps);
|
||||
int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, unsigned int lanes, unsigned int ber_level,
|
||||
bool timing, bool right_high, u32 *results);
|
||||
u8 index, const struct usb4_port_margining_params *params,
|
||||
u32 *results);
|
||||
int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, unsigned int lanes, bool timing,
|
||||
bool right_high, u32 counter);
|
||||
u8 index, const struct usb4_port_margining_params *params,
|
||||
u32 *results);
|
||||
int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, u32 *errors);
|
||||
|
||||
|
@ -1653,31 +1653,31 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
|
||||
* @port: USB4 port
|
||||
* @target: Sideband target
|
||||
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
|
||||
* @lanes: Which lanes to run (must match the port capabilities). Can be
|
||||
* %0, %1 or %7.
|
||||
* @ber_level: BER level contour value
|
||||
* @timing: Perform timing margining instead of voltage
|
||||
* @right_high: Use Right/high margin instead of left/low
|
||||
* @params: Parameters for USB4 hardware margining
|
||||
* @results: Array with at least two elements to hold the results
|
||||
*
|
||||
* Runs hardware lane margining on USB4 port and returns the result in
|
||||
* @results.
|
||||
*/
|
||||
int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, unsigned int lanes, unsigned int ber_level,
|
||||
bool timing, bool right_high, u32 *results)
|
||||
u8 index, const struct usb4_port_margining_params *params,
|
||||
u32 *results)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = lanes;
|
||||
if (timing)
|
||||
if (WARN_ON_ONCE(!params))
|
||||
return -EINVAL;
|
||||
|
||||
val = params->lanes;
|
||||
if (params->time)
|
||||
val |= USB4_MARGIN_HW_TIME;
|
||||
if (right_high)
|
||||
if (params->right_high)
|
||||
val |= USB4_MARGIN_HW_RH;
|
||||
if (ber_level)
|
||||
val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) &
|
||||
USB4_MARGIN_HW_BER_MASK;
|
||||
if (params->ber_level)
|
||||
val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level);
|
||||
if (params->optional_voltage_offset_range)
|
||||
val |= USB4_MARGIN_HW_OPT_VOLTAGE;
|
||||
|
||||
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
|
||||
sizeof(val));
|
||||
@ -1698,38 +1698,46 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
|
||||
* @port: USB4 port
|
||||
* @target: Sideband target
|
||||
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
|
||||
* @lanes: Which lanes to run (must match the port capabilities). Can be
|
||||
* %0, %1 or %7.
|
||||
* @timing: Perform timing margining instead of voltage
|
||||
* @right_high: Use Right/high margin instead of left/low
|
||||
* @counter: What to do with the error counter
|
||||
* @params: Parameters for USB4 software margining
|
||||
* @results: Data word for the operation completion data
|
||||
*
|
||||
* Runs software lane margining on USB4 port. Read back the error
|
||||
* counters by calling usb4_port_sw_margin_errors(). Returns %0 in
|
||||
* success and negative errno otherwise.
|
||||
*/
|
||||
int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
|
||||
u8 index, unsigned int lanes, bool timing,
|
||||
bool right_high, u32 counter)
|
||||
u8 index, const struct usb4_port_margining_params *params,
|
||||
u32 *results)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = lanes;
|
||||
if (timing)
|
||||
if (WARN_ON_ONCE(!params))
|
||||
return -EINVAL;
|
||||
|
||||
val = params->lanes;
|
||||
if (params->time)
|
||||
val |= USB4_MARGIN_SW_TIME;
|
||||
if (right_high)
|
||||
if (params->optional_voltage_offset_range)
|
||||
val |= USB4_MARGIN_SW_OPT_VOLTAGE;
|
||||
if (params->right_high)
|
||||
val |= USB4_MARGIN_SW_RH;
|
||||
val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) &
|
||||
USB4_MARGIN_SW_COUNTER_MASK;
|
||||
val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter);
|
||||
val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset);
|
||||
|
||||
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
|
||||
sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_port_sb_op(port, target, index,
|
||||
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
|
||||
ret = usb4_port_sb_op(port, target, index,
|
||||
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results,
|
||||
sizeof(*results));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,8 +37,7 @@ struct cdns3_wrap {
|
||||
#define PCI_DRIVER_NAME "cdns3-pci-usbss"
|
||||
#define PLAT_DRIVER_NAME "cdns-usb3"
|
||||
|
||||
#define CDNS_VENDOR_ID 0x17cd
|
||||
#define CDNS_DEVICE_ID 0x0100
|
||||
#define PCI_DEVICE_ID_CDNS_USB3 0x0100
|
||||
|
||||
static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
|
||||
{
|
||||
@ -190,7 +189,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev)
|
||||
}
|
||||
|
||||
static const struct pci_device_id cdns3_pci_ids[] = {
|
||||
{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
|
||||
{ PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USB3) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -28,10 +28,11 @@
|
||||
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
|
||||
#define PLAT_DRIVER_NAME "cdns-usbssp"
|
||||
|
||||
#define CDNS_VENDOR_ID 0x17cd
|
||||
#define CDNS_DEVICE_ID 0x0200
|
||||
#define CDNS_DRD_ID 0x0100
|
||||
#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
|
||||
#define PCI_DEVICE_ID_CDNS_USB3 0x0100
|
||||
#define PCI_DEVICE_ID_CDNS_UDC 0x0200
|
||||
|
||||
#define PCI_CLASS_SERIAL_USB_CDNS_USB3 (PCI_CLASS_SERIAL_USB << 8 | 0x80)
|
||||
#define PCI_CLASS_SERIAL_USB_CDNS_UDC PCI_CLASS_SERIAL_USB_DEVICE
|
||||
|
||||
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
|
||||
{
|
||||
@ -40,10 +41,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
|
||||
* Platform has two function. The fist keeps resources for
|
||||
* Host/Device while the secon keeps resources for DRD/OTG.
|
||||
*/
|
||||
if (pdev->device == CDNS_DEVICE_ID)
|
||||
return pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL);
|
||||
else if (pdev->device == CDNS_DRD_ID)
|
||||
return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL);
|
||||
if (pdev->device == PCI_DEVICE_ID_CDNS_UDC)
|
||||
return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USB3, NULL);
|
||||
if (pdev->device == PCI_DEVICE_ID_CDNS_USB3)
|
||||
return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_UDC, NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -220,12 +221,12 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct pci_device_id cdnsp_pci_ids[] = {
|
||||
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
|
||||
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
CDNS_DRD_IF, PCI_ANY_ID },
|
||||
{ PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
CDNS_DRD_IF, PCI_ANY_ID },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC),
|
||||
.class = PCI_CLASS_SERIAL_USB_CDNS_UDC },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC),
|
||||
.class = PCI_CLASS_SERIAL_USB_CDNS_USB3 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB3),
|
||||
.class = PCI_CLASS_SERIAL_USB_CDNS_USB3 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -718,7 +718,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
|
||||
seg = cdnsp_trb_in_td(pdev, cur_td->start_seg, cur_td->first_trb,
|
||||
cur_td->last_trb, hw_deq);
|
||||
|
||||
if (seg && (pep->ep_state & EP_ENABLED))
|
||||
if (seg && (pep->ep_state & EP_ENABLED) &&
|
||||
!(pep->ep_state & EP_DIS_IN_RROGRESS))
|
||||
cdnsp_find_new_dequeue_state(pdev, pep, preq->request.stream_id,
|
||||
cur_td, &deq_state);
|
||||
else
|
||||
@ -736,7 +737,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
|
||||
* During disconnecting all endpoint will be disabled so we don't
|
||||
* have to worry about updating dequeue pointer.
|
||||
*/
|
||||
if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING) {
|
||||
if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING ||
|
||||
pep->ep_state & EP_DIS_IN_RROGRESS) {
|
||||
status = -ESHUTDOWN;
|
||||
ret = cdnsp_cmd_set_deq(pdev, pep, &deq_state);
|
||||
}
|
||||
|
@ -62,7 +62,9 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
|
||||
.resume_quirk = xhci_cdns3_resume_quirk,
|
||||
};
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_cdnsp_xhci;
|
||||
static const struct xhci_plat_priv xhci_plat_cdnsp_xhci = {
|
||||
.quirks = XHCI_CDNS_SCTX_QUIRK,
|
||||
};
|
||||
|
||||
static int __cdns_host_init(struct cdns *cdns)
|
||||
{
|
||||
|
@ -128,7 +128,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
|
||||
* In case the fsl,usbmisc property is not present this device doesn't
|
||||
* need usbmisc. Return NULL (which is no error here)
|
||||
*/
|
||||
if (!of_get_property(np, "fsl,usbmisc", NULL))
|
||||
if (!of_property_present(np, "fsl,usbmisc"))
|
||||
return NULL;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
|
@ -18,7 +18,7 @@ struct npcm_udc_data {
|
||||
struct ci_hdrc_platform_data pdata;
|
||||
};
|
||||
|
||||
static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
|
||||
static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned int event)
|
||||
{
|
||||
struct device *dev = ci->dev->parent;
|
||||
|
||||
@ -28,7 +28,7 @@ static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
|
||||
hw_write(ci, OP_USBMODE, 0xffffffff, 0x0);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "unknown ci_hdrc event (%d)\n",event);
|
||||
dev_dbg(dev, "unknown ci_hdrc event (%d)\n", event);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
|
||||
hw_write(ci, OP_ENDPTLISTADDR, ~0, dma);
|
||||
/* interrupt, error, port change, reset, sleep/suspend */
|
||||
hw_write(ci, OP_USBINTR, ~0,
|
||||
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
|
||||
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI);
|
||||
} else {
|
||||
hw_write(ci, OP_USBINTR, ~0, 0);
|
||||
}
|
||||
@ -877,6 +877,7 @@ __releases(ci->lock)
|
||||
__acquires(ci->lock)
|
||||
{
|
||||
int retval;
|
||||
u32 intr;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
if (ci->gadget.speed != USB_SPEED_UNKNOWN)
|
||||
@ -890,6 +891,11 @@ __acquires(ci->lock)
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
/* clear SLI */
|
||||
hw_write(ci, OP_USBSTS, USBi_SLI, USBi_SLI);
|
||||
intr = hw_read(ci, OP_USBINTR, ~0);
|
||||
hw_write(ci, OP_USBINTR, ~0, intr | USBi_SLI);
|
||||
|
||||
ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC);
|
||||
if (ci->status == NULL)
|
||||
retval = -ENOMEM;
|
||||
|
@ -962,10 +962,12 @@ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss)
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
ss->line = acm->minor;
|
||||
mutex_lock(&acm->port.mutex);
|
||||
ss->close_delay = jiffies_to_msecs(acm->port.close_delay) / 10;
|
||||
ss->closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
|
||||
ASYNC_CLOSING_WAIT_NONE :
|
||||
jiffies_to_msecs(acm->port.closing_wait) / 10;
|
||||
mutex_unlock(&acm->port.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -754,7 +754,7 @@ static struct urb *usbtmc_create_urb(void)
|
||||
if (!urb)
|
||||
return NULL;
|
||||
|
||||
dmabuf = kmalloc(bufsize, GFP_KERNEL);
|
||||
dmabuf = kzalloc(bufsize, GFP_KERNEL);
|
||||
if (!dmabuf) {
|
||||
usb_free_urb(urb);
|
||||
return NULL;
|
||||
|
@ -107,19 +107,18 @@ EXPORT_SYMBOL_GPL(usb_speed_string);
|
||||
*/
|
||||
enum usb_device_speed usb_get_maximum_speed(struct device *dev)
|
||||
{
|
||||
const char *maximum_speed;
|
||||
const char *p = "maximum-speed";
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_string(dev, "maximum-speed", &maximum_speed);
|
||||
if (ret < 0)
|
||||
return USB_SPEED_UNKNOWN;
|
||||
|
||||
ret = match_string(ssp_rate, ARRAY_SIZE(ssp_rate), maximum_speed);
|
||||
ret = device_property_match_property_string(dev, p, ssp_rate, ARRAY_SIZE(ssp_rate));
|
||||
if (ret > 0)
|
||||
return USB_SPEED_SUPER_PLUS;
|
||||
|
||||
ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
|
||||
return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
|
||||
ret = device_property_match_property_string(dev, p, speed_names, ARRAY_SIZE(speed_names));
|
||||
if (ret > 0)
|
||||
return ret;
|
||||
|
||||
return USB_SPEED_UNKNOWN;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_maximum_speed);
|
||||
|
||||
@ -276,14 +275,13 @@ EXPORT_SYMBOL_GPL(usb_decode_interval);
|
||||
*/
|
||||
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
|
||||
{
|
||||
struct device_node *controller = NULL;
|
||||
struct device_node *controller;
|
||||
struct of_phandle_args args;
|
||||
const char *dr_mode;
|
||||
int index;
|
||||
int err;
|
||||
|
||||
do {
|
||||
controller = of_find_node_with_property(controller, "phys");
|
||||
for_each_node_with_property(controller, "phys") {
|
||||
if (!of_device_is_available(controller))
|
||||
continue;
|
||||
index = 0;
|
||||
@ -306,7 +304,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
|
||||
goto finish;
|
||||
index++;
|
||||
} while (args.np);
|
||||
} while (controller);
|
||||
}
|
||||
|
||||
finish:
|
||||
err = of_property_read_string(controller, "dr_mode", &dr_mode);
|
||||
|
@ -142,6 +142,53 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
|
||||
|
||||
/**
|
||||
* usb_acpi_add_usb4_devlink - add device link to USB4 Host Interface for tunneled USB3 devices
|
||||
*
|
||||
* @udev: Tunneled USB3 device connected to a roothub.
|
||||
*
|
||||
* Adds a device link between a tunneled USB3 device and the USB4 Host Interface
|
||||
* device to ensure correct runtime PM suspend and resume order. This function
|
||||
* should only be called for tunneled USB3 devices.
|
||||
* The USB4 Host Interface this tunneled device depends on is found from the roothub
|
||||
* port ACPI device specific data _DSD entry.
|
||||
*
|
||||
* Return: negative error code on failure, 0 otherwise
|
||||
*/
|
||||
static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
|
||||
{
|
||||
const struct device_link *link;
|
||||
struct usb_port *port_dev;
|
||||
struct usb_hub *hub;
|
||||
|
||||
if (!udev->parent || udev->parent->parent)
|
||||
return 0;
|
||||
|
||||
hub = usb_hub_to_struct_hub(udev->parent);
|
||||
port_dev = hub->ports[udev->portnum - 1];
|
||||
|
||||
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
|
||||
fwnode_find_reference(dev_fwnode(&port_dev->dev), "usb4-host-interface", 0);
|
||||
|
||||
if (IS_ERR(nhi_fwnode))
|
||||
return 0;
|
||||
|
||||
link = device_link_add(&port_dev->child->dev, nhi_fwnode->dev,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER |
|
||||
DL_FLAG_RPM_ACTIVE |
|
||||
DL_FLAG_PM_RUNTIME);
|
||||
if (!link) {
|
||||
dev_err(&port_dev->dev, "Failed to created device link from %s to %s\n",
|
||||
dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&port_dev->dev, "Created device link from %s to %s\n",
|
||||
dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private to usb-acpi, all the core needs to know is that
|
||||
* port_dev->location is non-zero when it has been set by the firmware.
|
||||
@ -262,6 +309,12 @@ usb_acpi_find_companion_for_device(struct usb_device *udev)
|
||||
if (!hub)
|
||||
return NULL;
|
||||
|
||||
|
||||
/* Tunneled USB3 devices depend on USB4 Host Interface, set device link to it */
|
||||
if (udev->speed >= USB_SPEED_SUPER &&
|
||||
udev->tunnel_mode != USB_LINK_NATIVE)
|
||||
usb_acpi_add_usb4_devlink(udev);
|
||||
|
||||
/*
|
||||
* This is an embedded USB device connected to a port and such
|
||||
* devices share port's ACPI companion.
|
||||
|
@ -702,6 +702,7 @@ static int params_show(struct seq_file *seq, void *v)
|
||||
print_param(seq, p, uframe_sched);
|
||||
print_param(seq, p, external_id_pin_ctl);
|
||||
print_param(seq, p, power_down);
|
||||
print_param(seq, p, no_clock_gating);
|
||||
print_param(seq, p, lpm);
|
||||
print_param(seq, p, lpm_clock_gating);
|
||||
print_param(seq, p, besl);
|
||||
|
@ -127,6 +127,15 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
|
||||
role = USB_ROLE_DEVICE;
|
||||
}
|
||||
|
||||
if ((IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) ||
|
||||
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) &&
|
||||
dwc2_is_device_mode(hsotg) &&
|
||||
hsotg->lx_state == DWC2_L2 &&
|
||||
hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
|
||||
hsotg->bus_suspended &&
|
||||
!hsotg->params.no_clock_gating)
|
||||
dwc2_gadget_exit_clock_gating(hsotg, 0);
|
||||
|
||||
if (role == USB_ROLE_HOST) {
|
||||
already = dwc2_ovr_avalid(hsotg, true);
|
||||
} else if (role == USB_ROLE_DEVICE) {
|
||||
|
@ -23,6 +23,7 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
|
||||
p->max_transfer_size = 65535;
|
||||
p->max_packet_count = 511;
|
||||
p->ahbcfg = 0x10;
|
||||
p->no_clock_gating = true;
|
||||
}
|
||||
|
||||
static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
|
||||
@ -352,6 +353,7 @@ const struct of_device_id dwc2_of_match_table[] = {
|
||||
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
|
||||
|
||||
const struct acpi_device_id dwc2_acpi_match[] = {
|
||||
/* This ID refers to the same USB IP as of_device_id brcm,bcm2835-usb */
|
||||
{ "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params },
|
||||
{ },
|
||||
};
|
||||
|
@ -469,18 +469,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
|
||||
hsotg->irq = platform_get_irq(dev, 0);
|
||||
if (hsotg->irq < 0)
|
||||
return hsotg->irq;
|
||||
|
||||
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
|
||||
hsotg->irq);
|
||||
retval = devm_request_irq(hsotg->dev, hsotg->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
|
||||
if (IS_ERR(hsotg->vbus_supply)) {
|
||||
retval = PTR_ERR(hsotg->vbus_supply);
|
||||
@ -524,6 +512,20 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
hsotg->irq = platform_get_irq(dev, 0);
|
||||
if (hsotg->irq < 0) {
|
||||
retval = hsotg->irq;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
|
||||
hsotg->irq);
|
||||
retval = devm_request_irq(hsotg->dev, hsotg->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* For OTG cores, set the force mode bits to reflect the value
|
||||
* of dr_mode. Force mode bits should not be touched at any
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2020 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
@ -96,7 +97,8 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
|
||||
writel(value, dwc3_imx->glue_base + USB_CTRL1);
|
||||
}
|
||||
|
||||
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
|
||||
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
|
||||
pm_message_t msg)
|
||||
{
|
||||
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
|
||||
u32 val;
|
||||
@ -106,12 +108,14 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
|
||||
|
||||
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
|
||||
|
||||
if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
|
||||
val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
|
||||
USB_WAKEUP_U3_EN | USB_WAKEUP_DPDM_EN;
|
||||
else if (dwc3->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
|
||||
if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->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, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
|
||||
}
|
||||
@ -144,10 +148,21 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dwc3_imx8mp_set_software_node(struct device *dev)
|
||||
{
|
||||
struct property_entry props[3] = { 0 };
|
||||
int prop_idx = 0;
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk");
|
||||
|
||||
return device_create_managed_software_node(dev, props, NULL);
|
||||
}
|
||||
|
||||
static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dwc3_np, *node = dev->of_node;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct dwc3_imx8mp *dwc3_imx;
|
||||
struct resource *res;
|
||||
int err, irq;
|
||||
@ -178,39 +193,26 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(dwc3_imx->glue_base);
|
||||
}
|
||||
|
||||
dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
|
||||
if (IS_ERR(dwc3_imx->hsio_clk)) {
|
||||
err = PTR_ERR(dwc3_imx->hsio_clk);
|
||||
dev_err(dev, "Failed to get hsio clk, err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
dwc3_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio");
|
||||
if (IS_ERR(dwc3_imx->hsio_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc3_imx->hsio_clk),
|
||||
"Failed to get hsio clk\n");
|
||||
|
||||
err = clk_prepare_enable(dwc3_imx->hsio_clk);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to enable hsio clk, err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
dwc3_imx->suspend_clk = devm_clk_get(dev, "suspend");
|
||||
if (IS_ERR(dwc3_imx->suspend_clk)) {
|
||||
err = PTR_ERR(dwc3_imx->suspend_clk);
|
||||
dev_err(dev, "Failed to get suspend clk, err=%d\n", err);
|
||||
goto disable_hsio_clk;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(dwc3_imx->suspend_clk);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to enable suspend clk, err=%d\n", err);
|
||||
goto disable_hsio_clk;
|
||||
}
|
||||
dwc3_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend");
|
||||
if (IS_ERR(dwc3_imx->suspend_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc3_imx->suspend_clk),
|
||||
"Failed to get suspend clk\n");
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
goto disable_clks;
|
||||
}
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
dwc3_imx->irq = irq;
|
||||
|
||||
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(node,
|
||||
"snps,dwc3");
|
||||
if (!dwc3_np)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to find dwc3 core child\n");
|
||||
|
||||
imx8mp_configure_glue(dwc3_imx);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
@ -219,17 +221,17 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
||||
if (err < 0)
|
||||
goto disable_rpm;
|
||||
|
||||
dwc3_np = of_get_compatible_child(node, "snps,dwc3");
|
||||
if (!dwc3_np) {
|
||||
err = dwc3_imx8mp_set_software_node(dev);
|
||||
if (err) {
|
||||
err = -ENODEV;
|
||||
dev_err(dev, "failed to find dwc3 core child\n");
|
||||
dev_err(dev, "failed to create software node\n");
|
||||
goto disable_rpm;
|
||||
}
|
||||
|
||||
err = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to create dwc3 core\n");
|
||||
goto err_node_put;
|
||||
goto disable_rpm;
|
||||
}
|
||||
|
||||
dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
|
||||
@ -238,7 +240,6 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
||||
err = -ENODEV;
|
||||
goto depopulate;
|
||||
}
|
||||
of_node_put(dwc3_np);
|
||||
|
||||
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
|
||||
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
|
||||
@ -254,51 +255,39 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
||||
|
||||
depopulate:
|
||||
of_platform_depopulate(dev);
|
||||
err_node_put:
|
||||
of_node_put(dwc3_np);
|
||||
disable_rpm:
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
disable_clks:
|
||||
clk_disable_unprepare(dwc3_imx->suspend_clk);
|
||||
disable_hsio_clk:
|
||||
clk_disable_unprepare(dwc3_imx->hsio_clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dwc3_imx8mp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
clk_disable_unprepare(dwc3_imx->suspend_clk);
|
||||
clk_disable_unprepare(dwc3_imx->hsio_clk);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
|
||||
pm_message_t msg)
|
||||
static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
|
||||
{
|
||||
if (dwc3_imx->pm_suspended)
|
||||
return 0;
|
||||
|
||||
/* Wakeup enable */
|
||||
if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc3_imx->dev))
|
||||
dwc3_imx8mp_wakeup_enable(dwc3_imx);
|
||||
dwc3_imx8mp_wakeup_enable(dwc3_imx, msg);
|
||||
|
||||
dwc3_imx->pm_suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
|
||||
pm_message_t msg)
|
||||
static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
|
||||
int ret = 0;
|
||||
@ -331,7 +320,7 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
|
||||
static int dwc3_imx8mp_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -349,7 +338,7 @@ static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
|
||||
static int dwc3_imx8mp_pm_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -379,7 +368,7 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
|
||||
static int dwc3_imx8mp_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
|
||||
|
||||
@ -388,7 +377,7 @@ static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
|
||||
return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
|
||||
static int dwc3_imx8mp_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
|
||||
|
||||
@ -398,9 +387,9 @@ static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend,
|
||||
dwc3_imx8mp_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
|
||||
RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, dwc3_imx8mp_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id dwc3_imx8mp_of_match[] = {
|
||||
@ -414,7 +403,7 @@ static struct platform_driver dwc3_imx8mp_driver = {
|
||||
.remove_new = dwc3_imx8mp_remove,
|
||||
.driver = {
|
||||
.name = "imx8mp-dwc3",
|
||||
.pm = &dwc3_imx8mp_dev_pm_ops,
|
||||
.pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops),
|
||||
.of_match_table = dwc3_imx8mp_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -419,7 +419,7 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
|
||||
int ref_clk_sel, ref_clk_fsel, mpll_mul;
|
||||
int power_active_low, power_gpio;
|
||||
int err, len;
|
||||
u32 clock_rate;
|
||||
u32 clock_rate, gpio_pwr[3];
|
||||
|
||||
if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) {
|
||||
dev_err(dev, "No UCTL \"refclk-frequency\"\n");
|
||||
@ -476,21 +476,10 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
|
||||
|
||||
power_gpio = DWC3_GPIO_POWER_NONE;
|
||||
power_active_low = 0;
|
||||
if (of_find_property(node, "power", &len)) {
|
||||
u32 gpio_pwr[3];
|
||||
|
||||
switch (len) {
|
||||
case 8:
|
||||
of_property_read_u32_array(node, "power", gpio_pwr, 2);
|
||||
break;
|
||||
case 12:
|
||||
of_property_read_u32_array(node, "power", gpio_pwr, 3);
|
||||
len = of_property_read_variable_u32_array(node, "power", gpio_pwr, 2, 3);
|
||||
if (len > 0) {
|
||||
if (len == 3)
|
||||
power_active_low = gpio_pwr[2] & 0x01;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid power configuration\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
power_gpio = gpio_pwr[1];
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Inspired by dwc3-of-simple.c
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
@ -702,11 +703,12 @@ static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
|
||||
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node, *dwc3_np;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
|
||||
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
|
||||
"snps,dwc3");
|
||||
if (!dwc3_np) {
|
||||
dev_err(dev, "failed to find dwc3 core child\n");
|
||||
return -ENODEV;
|
||||
@ -715,7 +717,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
|
||||
goto node_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcom->dwc3 = of_find_device_by_node(dwc3_np);
|
||||
@ -725,9 +727,6 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
||||
of_platform_depopulate(dev);
|
||||
}
|
||||
|
||||
node_put:
|
||||
of_node_put(dwc3_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -736,7 +735,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_qcom *qcom;
|
||||
struct resource *res;
|
||||
int ret, i;
|
||||
bool ignore_pipe_clk;
|
||||
bool wakeup_source;
|
||||
@ -774,9 +772,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
goto reset_assert;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
qcom->qscratch_base = devm_ioremap_resource(dev, res);
|
||||
qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(qcom->qscratch_base)) {
|
||||
ret = PTR_ERR(qcom->qscratch_base);
|
||||
goto clk_disable;
|
||||
|
@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -173,23 +174,20 @@ static const char *const speed_names[] = {
|
||||
|
||||
static enum usb_device_speed __get_dwc3_maximum_speed(struct device_node *np)
|
||||
{
|
||||
struct device_node *dwc3_np;
|
||||
const char *maximum_speed;
|
||||
int ret;
|
||||
|
||||
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
|
||||
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
|
||||
"snps,dwc3");
|
||||
if (!dwc3_np)
|
||||
return USB_SPEED_UNKNOWN;
|
||||
|
||||
ret = of_property_read_string(dwc3_np, "maximum-speed", &maximum_speed);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return USB_SPEED_UNKNOWN;
|
||||
|
||||
ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
|
||||
|
||||
out:
|
||||
of_node_put(dwc3_np);
|
||||
|
||||
return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
|
||||
}
|
||||
|
||||
@ -276,7 +274,6 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *dwc3_pdev;
|
||||
struct device *dwc3_dev;
|
||||
struct device_node *dwc3_node;
|
||||
enum usb_dr_mode dr_mode;
|
||||
int ret = 0;
|
||||
|
||||
@ -290,7 +287,8 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc3_node = of_get_compatible_child(node, "snps,dwc3");
|
||||
struct device_node *dwc3_node __free(device_node) = of_get_compatible_child(node,
|
||||
"snps,dwc3");
|
||||
if (!dwc3_node) {
|
||||
dev_err(dev, "failed to find dwc3 core node\n");
|
||||
ret = -ENODEV;
|
||||
@ -301,7 +299,7 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
|
||||
if (!dwc3_pdev) {
|
||||
dev_err(dev, "failed to find dwc3 core platform_device\n");
|
||||
ret = -ENODEV;
|
||||
goto err_node_put;
|
||||
goto depopulate;
|
||||
}
|
||||
|
||||
dwc3_dev = &dwc3_pdev->dev;
|
||||
@ -343,14 +341,11 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
|
||||
switch_usb2_role(rtk, rtk->cur_role);
|
||||
|
||||
platform_device_put(dwc3_pdev);
|
||||
of_node_put(dwc3_node);
|
||||
|
||||
return 0;
|
||||
|
||||
err_pdev_put:
|
||||
platform_device_put(dwc3_pdev);
|
||||
err_node_put:
|
||||
of_node_put(dwc3_node);
|
||||
depopulate:
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
@ -363,30 +358,18 @@ static int dwc3_rtk_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret = 0;
|
||||
|
||||
rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL);
|
||||
if (!rtk) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!rtk)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, rtk);
|
||||
|
||||
rtk->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs)) {
|
||||
ret = PTR_ERR(regs);
|
||||
goto out;
|
||||
}
|
||||
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
rtk->regs = regs;
|
||||
rtk->regs_size = resource_size(res);
|
||||
@ -394,16 +377,11 @@ static int dwc3_rtk_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
rtk->pm_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(rtk->pm_base)) {
|
||||
ret = PTR_ERR(rtk->pm_base);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(rtk->pm_base))
|
||||
return PTR_ERR(rtk->pm_base);
|
||||
}
|
||||
|
||||
ret = dwc3_rtk_probe_dwc3_core(rtk);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return dwc3_rtk_probe_dwc3_core(rtk);
|
||||
}
|
||||
|
||||
static void dwc3_rtk_remove(struct platform_device *pdev)
|
||||
|
@ -14,6 +14,7 @@
|
||||
* Inspired by dwc3-omap.c and dwc3-exynos.c.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
@ -197,7 +198,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
struct st_dwc3 *dwc3_data;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node, *child;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *child_pdev;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
@ -224,15 +225,21 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
dwc3_data->syscfg_reg_off = res->start;
|
||||
|
||||
dev_vdbg(&pdev->dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
|
||||
dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
|
||||
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
|
||||
|
||||
struct device_node *child __free(device_node) = of_get_compatible_child(node,
|
||||
"snps,dwc3");
|
||||
if (!child) {
|
||||
dev_err(dev, "failed to find dwc3 core node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc3_data->rstc_pwrdn =
|
||||
devm_reset_control_get_exclusive(dev, "powerdown");
|
||||
if (IS_ERR(dwc3_data->rstc_pwrdn)) {
|
||||
dev_err(&pdev->dev, "could not get power controller\n");
|
||||
return PTR_ERR(dwc3_data->rstc_pwrdn);
|
||||
}
|
||||
if (IS_ERR(dwc3_data->rstc_pwrdn))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_pwrdn),
|
||||
"could not get power controller\n");
|
||||
|
||||
/* Manage PowerDown */
|
||||
reset_control_deassert(dwc3_data->rstc_pwrdn);
|
||||
@ -240,26 +247,19 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
dwc3_data->rstc_rst =
|
||||
devm_reset_control_get_shared(dev, "softreset");
|
||||
if (IS_ERR(dwc3_data->rstc_rst)) {
|
||||
dev_err(&pdev->dev, "could not get reset controller\n");
|
||||
ret = PTR_ERR(dwc3_data->rstc_rst);
|
||||
ret = dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_rst),
|
||||
"could not get reset controller\n");
|
||||
goto undo_powerdown;
|
||||
}
|
||||
|
||||
/* Manage SoftReset */
|
||||
reset_control_deassert(dwc3_data->rstc_rst);
|
||||
|
||||
child = of_get_compatible_child(node, "snps,dwc3");
|
||||
if (!child) {
|
||||
dev_err(&pdev->dev, "failed to find dwc3 core node\n");
|
||||
ret = -ENODEV;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
/* Allocate and initialize the core */
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add dwc3 core\n");
|
||||
goto err_node_put;
|
||||
goto undo_softreset;
|
||||
}
|
||||
|
||||
child_pdev = of_find_device_by_node(child);
|
||||
@ -270,7 +270,6 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev);
|
||||
of_node_put(child);
|
||||
platform_device_put(child_pdev);
|
||||
|
||||
/*
|
||||
@ -282,8 +281,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
ret = st_dwc3_drd_init(dwc3_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "drd initialisation failed\n");
|
||||
of_platform_depopulate(dev);
|
||||
goto undo_softreset;
|
||||
goto depopulate;
|
||||
}
|
||||
|
||||
/* ST glue logic init */
|
||||
@ -294,8 +292,6 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
depopulate:
|
||||
of_platform_depopulate(dev);
|
||||
err_node_put:
|
||||
of_node_put(child);
|
||||
undo_softreset:
|
||||
reset_control_assert(dwc3_data->rstc_rst);
|
||||
undo_powerdown:
|
||||
|
@ -285,11 +285,8 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs)) {
|
||||
ret = PTR_ERR(regs);
|
||||
dev_err_probe(dev, ret, "failed to map registers\n");
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(regs))
|
||||
return dev_err_probe(dev, PTR_ERR(regs), "failed to map registers\n");
|
||||
|
||||
match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node);
|
||||
|
||||
|
@ -6,13 +6,13 @@
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
#include <linux/usb/gadget_configfs.h>
|
||||
#include <linux/usb/webusb.h>
|
||||
#include "configfs.h"
|
||||
#include "u_f.h"
|
||||
#include "u_os_desc.h"
|
||||
|
||||
int check_user_usb_string(const char *name,
|
||||
static int check_user_usb_string(const char *name,
|
||||
struct usb_gadget_strings *stringtab_dev)
|
||||
{
|
||||
u16 num;
|
||||
@ -902,7 +902,7 @@ static struct configfs_group_operations gadget_language_langid_group_ops = {
|
||||
.drop_item = gadget_language_string_drop,
|
||||
};
|
||||
|
||||
static struct config_item_type gadget_language_type = {
|
||||
static const struct config_item_type gadget_language_type = {
|
||||
.ct_item_ops = &gadget_language_langid_item_ops,
|
||||
.ct_group_ops = &gadget_language_langid_group_ops,
|
||||
.ct_attrs = gadget_language_langid_attrs,
|
||||
@ -961,7 +961,7 @@ static struct configfs_group_operations gadget_language_group_ops = {
|
||||
.drop_item = &gadget_language_drop,
|
||||
};
|
||||
|
||||
static struct config_item_type gadget_language_strings_type = {
|
||||
static const struct config_item_type gadget_language_strings_type = {
|
||||
.ct_group_ops = &gadget_language_group_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
@ -1106,7 +1106,7 @@ static struct configfs_attribute *webusb_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type webusb_type = {
|
||||
static const struct config_item_type webusb_type = {
|
||||
.ct_attrs = webusb_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
@ -1263,7 +1263,7 @@ static struct configfs_item_operations os_desc_ops = {
|
||||
.drop_link = os_desc_unlink,
|
||||
};
|
||||
|
||||
static struct config_item_type os_desc_type = {
|
||||
static const struct config_item_type os_desc_type = {
|
||||
.ct_item_ops = &os_desc_ops,
|
||||
.ct_attrs = os_desc_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
|
@ -41,6 +41,7 @@ struct f_acm {
|
||||
struct gserial port;
|
||||
u8 ctrl_id, data_id;
|
||||
u8 port_num;
|
||||
u8 bInterfaceProtocol;
|
||||
|
||||
u8 pending;
|
||||
|
||||
@ -89,7 +90,7 @@ acm_iad_descriptor = {
|
||||
.bInterfaceCount = 2, // control + data
|
||||
.bFunctionClass = USB_CLASS_COMM,
|
||||
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
|
||||
/* .bFunctionProtocol = DYNAMIC */
|
||||
/* .iFunction = DYNAMIC */
|
||||
};
|
||||
|
||||
@ -101,7 +102,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = {
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
|
||||
/* .bInterfaceProtocol = DYNAMIC */
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
@ -663,6 +664,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
goto fail;
|
||||
acm->notify = ep;
|
||||
|
||||
acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol;
|
||||
acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol;
|
||||
|
||||
/* allocate notification */
|
||||
acm->notify_req = gs_alloc_req(ep,
|
||||
sizeof(struct usb_cdc_notification) + 2,
|
||||
@ -719,8 +723,14 @@ static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
static void acm_free_func(struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
opts = container_of(f->fi, struct f_serial_opts, func_inst);
|
||||
|
||||
kfree(acm);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->instances--;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void acm_resume(struct usb_function *f)
|
||||
@ -761,7 +771,11 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
|
||||
acm->port.func.disable = acm_disable;
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
acm->port_num = opts->port_num;
|
||||
acm->bInterfaceProtocol = opts->protocol;
|
||||
opts->instances++;
|
||||
mutex_unlock(&opts->lock);
|
||||
acm->port.func.unbind = acm_unbind;
|
||||
acm->port.func.free_func = acm_free_func;
|
||||
acm->port.func.resume = acm_resume;
|
||||
@ -812,11 +826,42 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
|
||||
|
||||
CONFIGFS_ATTR_RO(f_acm_, port_num);
|
||||
|
||||
static ssize_t f_acm_protocol_show(struct config_item *item, char *page)
|
||||
{
|
||||
return sprintf(page, "%u\n", to_f_serial_opts(item)->protocol);
|
||||
}
|
||||
|
||||
static ssize_t f_acm_protocol_store(struct config_item *item,
|
||||
const char *page, size_t count)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
|
||||
if (opts->instances) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kstrtou8(page, 0, &opts->protocol);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = count;
|
||||
|
||||
out:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_acm_, protocol);
|
||||
|
||||
static struct configfs_attribute *acm_attrs[] = {
|
||||
#ifdef CONFIG_U_SERIAL_CONSOLE
|
||||
&f_acm_attr_console,
|
||||
#endif
|
||||
&f_acm_attr_port_num,
|
||||
&f_acm_attr_protocol,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -832,6 +877,7 @@ static void acm_free_instance(struct usb_function_instance *fi)
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
gserial_free_line(opts->port_num);
|
||||
mutex_destroy(&opts->lock);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
@ -843,7 +889,9 @@ static struct usb_function_instance *acm_alloc_instance(void)
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER;
|
||||
opts->func_inst.free_func_inst = acm_free_instance;
|
||||
mutex_init(&opts->lock);
|
||||
ret = gserial_alloc_line(&opts->port_num);
|
||||
if (ret) {
|
||||
kfree(opts);
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/usb/ccid.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
|
||||
#include <linux/aio.h>
|
||||
#include <linux/kthread.h>
|
||||
@ -40,7 +41,6 @@
|
||||
#include <linux/eventfd.h>
|
||||
|
||||
#include "u_fs.h"
|
||||
#include "u_f.h"
|
||||
#include "u_os_desc.h"
|
||||
#include "configfs.h"
|
||||
|
||||
@ -2478,7 +2478,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
|
||||
|
||||
static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
ffs_entity_callback entity,
|
||||
void *priv, int *current_class)
|
||||
void *priv, int *current_class, int *current_subclass)
|
||||
{
|
||||
struct usb_descriptor_header *_ds = (void *)data;
|
||||
u8 length;
|
||||
@ -2535,6 +2535,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
if (ds->iInterface)
|
||||
__entity(STRING, ds->iInterface);
|
||||
*current_class = ds->bInterfaceClass;
|
||||
*current_subclass = ds->bInterfaceSubClass;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2559,6 +2560,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
if (length != sizeof(struct ccid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
} else if (*current_class == USB_CLASS_APP_SPEC &&
|
||||
*current_subclass == USB_SUBCLASS_DFU) {
|
||||
pr_vdebug("dfu functional descriptor\n");
|
||||
if (length != sizeof(struct usb_dfu_functional_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
} else {
|
||||
pr_vdebug("unknown descriptor: %d for class %d\n",
|
||||
_ds->bDescriptorType, *current_class);
|
||||
@ -2621,6 +2628,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
||||
const unsigned _len = len;
|
||||
unsigned long num = 0;
|
||||
int current_class = -1;
|
||||
int current_subclass = -1;
|
||||
|
||||
for (;;) {
|
||||
int ret;
|
||||
@ -2640,7 +2648,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
||||
return _len - len;
|
||||
|
||||
ret = ffs_do_single_desc(data, len, entity, priv,
|
||||
¤t_class);
|
||||
¤t_class, ¤t_subclass);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s returns %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
@ -15,13 +15,21 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
#include <linux/usb/g_hid.h>
|
||||
#include <uapi/linux/usb/g_hid.h>
|
||||
|
||||
#include "u_f.h"
|
||||
#include "u_hid.h"
|
||||
|
||||
#define HIDG_MINORS 4
|
||||
|
||||
/*
|
||||
* Most operating systems seem to allow for 5000ms timeout, we will allow
|
||||
* userspace half that time to respond before we return an empty report.
|
||||
*/
|
||||
#define GET_REPORT_TIMEOUT_MS 2500
|
||||
|
||||
static int major, minors;
|
||||
|
||||
static const struct class hidg_class = {
|
||||
@ -31,6 +39,11 @@ static const struct class hidg_class = {
|
||||
static DEFINE_IDA(hidg_ida);
|
||||
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
|
||||
|
||||
struct report_entry {
|
||||
struct usb_hidg_report report_data;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* HID gadget struct */
|
||||
|
||||
@ -75,6 +88,19 @@ struct f_hidg {
|
||||
wait_queue_head_t write_queue;
|
||||
struct usb_request *req;
|
||||
|
||||
/* get report */
|
||||
struct usb_request *get_req;
|
||||
struct usb_hidg_report get_report;
|
||||
bool get_report_returned;
|
||||
int get_report_req_report_id;
|
||||
int get_report_req_report_length;
|
||||
spinlock_t get_report_spinlock;
|
||||
wait_queue_head_t get_queue; /* Waiting for userspace response */
|
||||
wait_queue_head_t get_id_queue; /* Get ID came in */
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct list_head report_list;
|
||||
|
||||
struct device dev;
|
||||
struct cdev cdev;
|
||||
struct usb_function func;
|
||||
@ -524,6 +550,174 @@ release_write_pending:
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct report_entry *f_hidg_search_for_report(struct f_hidg *hidg, u8 report_id)
|
||||
{
|
||||
struct list_head *ptr;
|
||||
struct report_entry *entry;
|
||||
|
||||
list_for_each(ptr, &hidg->report_list) {
|
||||
entry = list_entry(ptr, struct report_entry, node);
|
||||
if (entry->report_data.report_id == report_id)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void get_report_workqueue_handler(struct work_struct *work)
|
||||
{
|
||||
struct f_hidg *hidg = container_of(work, struct f_hidg, work);
|
||||
struct usb_composite_dev *cdev = hidg->func.config->cdev;
|
||||
struct usb_request *req;
|
||||
struct report_entry *ptr;
|
||||
unsigned long flags;
|
||||
|
||||
int status = 0;
|
||||
|
||||
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
|
||||
req = hidg->get_req;
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
req->zero = 0;
|
||||
req->length = min_t(unsigned int, min_t(unsigned int, hidg->get_report_req_report_length,
|
||||
hidg->report_length),
|
||||
MAX_REPORT_LENGTH);
|
||||
|
||||
/* Check if there is a response available for immediate response */
|
||||
ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
|
||||
if (ptr && !ptr->report_data.userspace_req) {
|
||||
/* Report exists in list and it is to be used for immediate response */
|
||||
req->buf = ptr->report_data.data;
|
||||
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
hidg->get_report_returned = true;
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
} else {
|
||||
/*
|
||||
* Report does not exist in list or should not be immediately sent
|
||||
* i.e. give userspace time to respond
|
||||
*/
|
||||
hidg->get_report_returned = false;
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
wake_up(&hidg->get_id_queue);
|
||||
#define GET_REPORT_COND (!hidg->get_report_returned)
|
||||
/* Wait until userspace has responded or timeout */
|
||||
status = wait_event_interruptible_timeout(hidg->get_queue, !GET_REPORT_COND,
|
||||
msecs_to_jiffies(GET_REPORT_TIMEOUT_MS));
|
||||
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
|
||||
req = hidg->get_req;
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
return;
|
||||
}
|
||||
if (status == 0 && !hidg->get_report_returned) {
|
||||
/* GET_REPORT request was not serviced by userspace within timeout period */
|
||||
VDBG(cdev, "get_report : userspace timeout.\n");
|
||||
hidg->get_report_returned = true;
|
||||
}
|
||||
|
||||
/* Search again for report ID in list and respond to GET_REPORT request */
|
||||
ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
|
||||
if (ptr) {
|
||||
/*
|
||||
* Either get an updated response just serviced by userspace
|
||||
* or send the latest response in the list
|
||||
*/
|
||||
req->buf = ptr->report_data.data;
|
||||
} else {
|
||||
/* If there are no prevoiusly sent reports send empty report */
|
||||
req->buf = hidg->get_report.data;
|
||||
memset(req->buf, 0x0, req->length);
|
||||
}
|
||||
|
||||
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
VDBG(cdev, "usb_ep_queue error on ep0 responding to GET_REPORT\n");
|
||||
}
|
||||
|
||||
static int f_hidg_get_report_id(struct file *file, __u8 __user *buffer)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
int ret = 0;
|
||||
|
||||
ret = put_user(hidg->get_report_req_report_id, buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
struct usb_composite_dev *cdev = hidg->func.config->cdev;
|
||||
unsigned long flags;
|
||||
struct report_entry *entry;
|
||||
struct report_entry *ptr;
|
||||
__u8 report_id;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(&entry->report_data, buffer,
|
||||
sizeof(struct usb_hidg_report))) {
|
||||
ERROR(cdev, "copy_from_user error\n");
|
||||
kfree(entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
report_id = entry->report_data.report_id;
|
||||
|
||||
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
|
||||
ptr = f_hidg_search_for_report(hidg, report_id);
|
||||
|
||||
if (ptr) {
|
||||
/* Report already exists in list - update it */
|
||||
if (copy_from_user(&ptr->report_data, buffer,
|
||||
sizeof(struct usb_hidg_report))) {
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
ERROR(cdev, "copy_from_user error\n");
|
||||
kfree(entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
kfree(entry);
|
||||
} else {
|
||||
/* Report does not exist in list - add it */
|
||||
list_add_tail(&entry->node, &hidg->report_list);
|
||||
}
|
||||
|
||||
/* If there is no response pending then do nothing further */
|
||||
if (hidg->get_report_returned) {
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If this userspace response serves the current pending report */
|
||||
if (hidg->get_report_req_report_id == report_id) {
|
||||
hidg->get_report_returned = true;
|
||||
wake_up(&hidg->get_queue);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg)
|
||||
{
|
||||
switch (code) {
|
||||
case GADGET_HID_READ_GET_REPORT_ID:
|
||||
return f_hidg_get_report_id(file, (__u8 __user *)arg);
|
||||
case GADGET_HID_WRITE_GET_REPORT:
|
||||
return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
@ -531,6 +725,8 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
|
||||
|
||||
poll_wait(file, &hidg->read_queue, wait);
|
||||
poll_wait(file, &hidg->write_queue, wait);
|
||||
poll_wait(file, &hidg->get_queue, wait);
|
||||
poll_wait(file, &hidg->get_id_queue, wait);
|
||||
|
||||
if (WRITE_COND)
|
||||
ret |= EPOLLOUT | EPOLLWRNORM;
|
||||
@ -543,12 +739,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
|
||||
ret |= EPOLLIN | EPOLLRDNORM;
|
||||
}
|
||||
|
||||
if (GET_REPORT_COND)
|
||||
ret |= EPOLLPRI;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef WRITE_COND
|
||||
#undef READ_COND_SSREPORT
|
||||
#undef READ_COND_INTOUT
|
||||
#undef GET_REPORT_COND
|
||||
|
||||
static int f_hidg_release(struct inode *inode, struct file *fd)
|
||||
{
|
||||
@ -641,6 +841,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
wake_up(&hidg->read_queue);
|
||||
}
|
||||
|
||||
static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
}
|
||||
|
||||
static int hidg_setup(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
@ -649,6 +853,7 @@ static int hidg_setup(struct usb_function *f,
|
||||
struct usb_request *req = cdev->req;
|
||||
int status = 0;
|
||||
__u16 value, length;
|
||||
unsigned long flags;
|
||||
|
||||
value = __le16_to_cpu(ctrl->wValue);
|
||||
length = __le16_to_cpu(ctrl->wLength);
|
||||
@ -660,14 +865,20 @@ static int hidg_setup(struct usb_function *f,
|
||||
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_GET_REPORT):
|
||||
VDBG(cdev, "get_report\n");
|
||||
VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength);
|
||||
|
||||
/* send an empty report */
|
||||
length = min_t(unsigned, length, hidg->report_length);
|
||||
memset(req->buf, 0x0, length);
|
||||
/*
|
||||
* Update GET_REPORT ID so that an ioctl can be used to determine what
|
||||
* GET_REPORT the request was actually for.
|
||||
*/
|
||||
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
|
||||
hidg->get_report_req_report_id = value & 0xff;
|
||||
hidg->get_report_req_report_length = length;
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
|
||||
goto respond;
|
||||
break;
|
||||
queue_work(hidg->workqueue, &hidg->work);
|
||||
|
||||
return status;
|
||||
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_GET_PROTOCOL):
|
||||
@ -793,6 +1004,14 @@ static void hidg_disable(struct usb_function *f)
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
|
||||
if (!hidg->get_report_returned) {
|
||||
usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req);
|
||||
hidg->get_req = NULL;
|
||||
hidg->get_report_returned = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
if (!hidg->write_pending) {
|
||||
free_ep_req(hidg->in_ep, hidg->req);
|
||||
@ -902,6 +1121,14 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long f_hidg_compat_ioctl(struct file *file, unsigned int code,
|
||||
unsigned long value)
|
||||
{
|
||||
return f_hidg_ioctl(file, code, value);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations f_hidg_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = f_hidg_open,
|
||||
@ -909,6 +1136,10 @@ static const struct file_operations f_hidg_fops = {
|
||||
.write = f_hidg_write,
|
||||
.read = f_hidg_read,
|
||||
.poll = f_hidg_poll,
|
||||
.unlocked_ioctl = f_hidg_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = f_hidg_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
@ -919,6 +1150,15 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
|
||||
hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC);
|
||||
if (!hidg->get_req)
|
||||
return -ENOMEM;
|
||||
|
||||
hidg->get_req->zero = 0;
|
||||
hidg->get_req->complete = hidg_get_report_complete;
|
||||
hidg->get_req->context = hidg;
|
||||
hidg->get_report_returned = true;
|
||||
|
||||
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||
us = usb_gstrings_attach(c->cdev, ct_func_strings,
|
||||
ARRAY_SIZE(ct_func_string_defs));
|
||||
@ -1004,9 +1244,24 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
hidg->write_pending = 1;
|
||||
hidg->req = NULL;
|
||||
spin_lock_init(&hidg->read_spinlock);
|
||||
spin_lock_init(&hidg->get_report_spinlock);
|
||||
init_waitqueue_head(&hidg->write_queue);
|
||||
init_waitqueue_head(&hidg->read_queue);
|
||||
init_waitqueue_head(&hidg->get_queue);
|
||||
init_waitqueue_head(&hidg->get_id_queue);
|
||||
INIT_LIST_HEAD(&hidg->completed_out_req);
|
||||
INIT_LIST_HEAD(&hidg->report_list);
|
||||
|
||||
INIT_WORK(&hidg->work, get_report_workqueue_handler);
|
||||
hidg->workqueue = alloc_workqueue("report_work",
|
||||
WQ_FREEZABLE |
|
||||
WQ_MEM_RECLAIM,
|
||||
1);
|
||||
|
||||
if (!hidg->workqueue) {
|
||||
status = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* create char device */
|
||||
cdev_init(&hidg->cdev, &f_hidg_fops);
|
||||
@ -1016,12 +1271,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
return 0;
|
||||
fail_free_descs:
|
||||
destroy_workqueue(hidg->workqueue);
|
||||
usb_free_all_descriptors(f);
|
||||
fail:
|
||||
ERROR(f->config->cdev, "hidg_bind FAILED\n");
|
||||
if (hidg->req != NULL)
|
||||
free_ep_req(hidg->in_ep, hidg->req);
|
||||
|
||||
usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req);
|
||||
hidg->get_req = NULL;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1256,7 +1515,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);
|
||||
|
||||
destroy_workqueue(hidg->workqueue);
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,9 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
|
||||
#include "g_zero.h"
|
||||
#include "u_f.h"
|
||||
|
||||
/*
|
||||
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
|
||||
|
@ -3050,7 +3050,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (!common->thread_task) {
|
||||
common->state = FSG_STATE_NORMAL;
|
||||
common->thread_task =
|
||||
kthread_create(fsg_main_thread, common, "file-storage");
|
||||
kthread_run(fsg_main_thread, common, "file-storage");
|
||||
if (IS_ERR(common->thread_task)) {
|
||||
ret = PTR_ERR(common->thread_task);
|
||||
common->thread_task = NULL;
|
||||
@ -3059,7 +3059,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
}
|
||||
DBG(common, "I/O thread pid: %d\n",
|
||||
task_pid_nr(common->thread_task));
|
||||
wake_up_process(common->thread_task);
|
||||
}
|
||||
|
||||
fsg->gadget = gadget;
|
||||
|
@ -30,11 +30,11 @@
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/midi.h>
|
||||
|
||||
#include "u_f.h"
|
||||
#include "u_midi.h"
|
||||
|
||||
MODULE_AUTHOR("Ben Williamson");
|
||||
|
@ -15,11 +15,11 @@
|
||||
#include <sound/ump_convert.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/midi-v2.h>
|
||||
|
||||
#include "u_f.h"
|
||||
#include "u_midi2.h"
|
||||
|
||||
struct f_midi2;
|
||||
|
@ -13,10 +13,10 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "g_zero.h"
|
||||
#include "u_f.h"
|
||||
|
||||
/*
|
||||
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
|
||||
|
@ -377,24 +377,10 @@ enum {
|
||||
STR_AS_OUT_IF_ALT1,
|
||||
STR_AS_IN_IF_ALT0,
|
||||
STR_AS_IN_IF_ALT1,
|
||||
NUM_STR_DESCRIPTORS,
|
||||
};
|
||||
|
||||
static struct usb_string strings_uac1[] = {
|
||||
/* [STR_AC_IF].s = DYNAMIC, */
|
||||
[STR_USB_OUT_IT].s = "Playback Input terminal",
|
||||
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
|
||||
[STR_IO_OUT_OT].s = "Playback Output terminal",
|
||||
[STR_IO_IN_IT].s = "Capture Input terminal",
|
||||
[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
|
||||
[STR_USB_IN_OT].s = "Capture Output terminal",
|
||||
[STR_FU_IN].s = "Capture Volume",
|
||||
[STR_FU_OUT].s = "Playback Volume",
|
||||
[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
|
||||
[STR_AS_OUT_IF_ALT1].s = "Playback Active",
|
||||
[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
|
||||
[STR_AS_IN_IF_ALT1].s = "Capture Active",
|
||||
{ },
|
||||
};
|
||||
static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {};
|
||||
|
||||
static struct usb_gadget_strings str_uac1 = {
|
||||
.language = 0x0409, /* en-us */
|
||||
@ -1265,6 +1251,20 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
strings_uac1[STR_AC_IF].s = audio_opts->function_name;
|
||||
|
||||
strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name;
|
||||
strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name;
|
||||
strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name;
|
||||
strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name;
|
||||
strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive";
|
||||
strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active";
|
||||
|
||||
strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name;
|
||||
strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name;
|
||||
strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name;
|
||||
strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name;
|
||||
strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive";
|
||||
strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active";
|
||||
|
||||
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
@ -1681,8 +1681,19 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_min);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_max);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_res);
|
||||
|
||||
UAC1_ATTRIBUTE_STRING(function_name);
|
||||
|
||||
UAC1_ATTRIBUTE_STRING(p_it_name);
|
||||
UAC1_ATTRIBUTE_STRING(p_it_ch_name);
|
||||
UAC1_ATTRIBUTE_STRING(p_ot_name);
|
||||
UAC1_ATTRIBUTE_STRING(p_fu_vol_name);
|
||||
|
||||
UAC1_ATTRIBUTE_STRING(c_it_name);
|
||||
UAC1_ATTRIBUTE_STRING(c_it_ch_name);
|
||||
UAC1_ATTRIBUTE_STRING(c_ot_name);
|
||||
UAC1_ATTRIBUTE_STRING(c_fu_vol_name);
|
||||
|
||||
static struct configfs_attribute *f_uac1_attrs[] = {
|
||||
&f_uac1_opts_attr_c_chmask,
|
||||
&f_uac1_opts_attr_c_srate,
|
||||
@ -1706,6 +1717,16 @@ static struct configfs_attribute *f_uac1_attrs[] = {
|
||||
|
||||
&f_uac1_opts_attr_function_name,
|
||||
|
||||
&f_uac1_opts_attr_p_it_name,
|
||||
&f_uac1_opts_attr_p_it_ch_name,
|
||||
&f_uac1_opts_attr_p_ot_name,
|
||||
&f_uac1_opts_attr_p_fu_vol_name,
|
||||
|
||||
&f_uac1_opts_attr_c_it_name,
|
||||
&f_uac1_opts_attr_c_it_ch_name,
|
||||
&f_uac1_opts_attr_c_ot_name,
|
||||
&f_uac1_opts_attr_c_fu_vol_name,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -1760,6 +1781,16 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
|
||||
|
||||
scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
|
||||
|
||||
scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal");
|
||||
scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
|
||||
scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal");
|
||||
scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
|
||||
|
||||
scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "Playback Input terminal");
|
||||
scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
|
||||
scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal");
|
||||
scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,9 @@ enum {
|
||||
STR_CLKSRC_IN,
|
||||
STR_CLKSRC_OUT,
|
||||
STR_USB_IT,
|
||||
STR_USB_IT_CH,
|
||||
STR_IO_IT,
|
||||
STR_IO_IT_CH,
|
||||
STR_USB_OT,
|
||||
STR_IO_OT,
|
||||
STR_FU_IN,
|
||||
@ -104,25 +106,10 @@ enum {
|
||||
STR_AS_OUT_ALT1,
|
||||
STR_AS_IN_ALT0,
|
||||
STR_AS_IN_ALT1,
|
||||
NUM_STR_DESCRIPTORS,
|
||||
};
|
||||
|
||||
static struct usb_string strings_fn[] = {
|
||||
/* [STR_ASSOC].s = DYNAMIC, */
|
||||
[STR_IF_CTRL].s = "Topology Control",
|
||||
[STR_CLKSRC_IN].s = "Input Clock",
|
||||
[STR_CLKSRC_OUT].s = "Output Clock",
|
||||
[STR_USB_IT].s = "USBH Out",
|
||||
[STR_IO_IT].s = "USBD Out",
|
||||
[STR_USB_OT].s = "USBH In",
|
||||
[STR_IO_OT].s = "USBD In",
|
||||
[STR_FU_IN].s = "Capture Volume",
|
||||
[STR_FU_OUT].s = "Playback Volume",
|
||||
[STR_AS_OUT_ALT0].s = "Playback Inactive",
|
||||
[STR_AS_OUT_ALT1].s = "Playback Active",
|
||||
[STR_AS_IN_ALT0].s = "Capture Inactive",
|
||||
[STR_AS_IN_ALT1].s = "Capture Active",
|
||||
{ },
|
||||
};
|
||||
static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {};
|
||||
|
||||
static const char *const speed_names[] = {
|
||||
[USB_SPEED_UNKNOWN] = "UNKNOWN",
|
||||
@ -1049,6 +1036,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
return ret;
|
||||
|
||||
strings_fn[STR_ASSOC].s = uac2_opts->function_name;
|
||||
strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name;
|
||||
strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name;
|
||||
strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name;
|
||||
|
||||
strings_fn[STR_USB_IT].s = uac2_opts->c_it_name;
|
||||
strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name;
|
||||
strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name;
|
||||
strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name;
|
||||
strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive";
|
||||
strings_fn[STR_AS_OUT_ALT1].s = "Playback Active";
|
||||
|
||||
strings_fn[STR_IO_IT].s = uac2_opts->p_it_name;
|
||||
strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name;
|
||||
strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name;
|
||||
strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name;
|
||||
strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive";
|
||||
strings_fn[STR_AS_IN_ALT1].s = "Capture Active";
|
||||
|
||||
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
|
||||
if (IS_ERR(us))
|
||||
@ -1072,7 +1076,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
|
||||
out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
|
||||
usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
|
||||
usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id;
|
||||
io_in_it_desc.iTerminal = us[STR_IO_IT].id;
|
||||
io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id;
|
||||
usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
|
||||
io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
|
||||
std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
|
||||
@ -2100,10 +2106,24 @@ UAC2_ATTRIBUTE(s16, c_volume_max);
|
||||
UAC2_ATTRIBUTE(s16, c_volume_res);
|
||||
UAC2_ATTRIBUTE(u32, fb_max);
|
||||
UAC2_ATTRIBUTE_STRING(function_name);
|
||||
UAC2_ATTRIBUTE_STRING(if_ctrl_name);
|
||||
UAC2_ATTRIBUTE_STRING(clksrc_in_name);
|
||||
UAC2_ATTRIBUTE_STRING(clksrc_out_name);
|
||||
|
||||
UAC2_ATTRIBUTE_STRING(p_it_name);
|
||||
UAC2_ATTRIBUTE_STRING(p_it_ch_name);
|
||||
UAC2_ATTRIBUTE_STRING(p_ot_name);
|
||||
UAC2_ATTRIBUTE_STRING(p_fu_vol_name);
|
||||
|
||||
UAC2_ATTRIBUTE_STRING(c_it_name);
|
||||
UAC2_ATTRIBUTE_STRING(c_it_ch_name);
|
||||
UAC2_ATTRIBUTE_STRING(c_ot_name);
|
||||
UAC2_ATTRIBUTE_STRING(c_fu_vol_name);
|
||||
|
||||
UAC2_ATTRIBUTE(s16, p_terminal_type);
|
||||
UAC2_ATTRIBUTE(s16, c_terminal_type);
|
||||
|
||||
|
||||
static struct configfs_attribute *f_uac2_attrs[] = {
|
||||
&f_uac2_opts_attr_p_chmask,
|
||||
&f_uac2_opts_attr_p_srate,
|
||||
@ -2130,6 +2150,19 @@ static struct configfs_attribute *f_uac2_attrs[] = {
|
||||
&f_uac2_opts_attr_c_volume_res,
|
||||
|
||||
&f_uac2_opts_attr_function_name,
|
||||
&f_uac2_opts_attr_if_ctrl_name,
|
||||
&f_uac2_opts_attr_clksrc_in_name,
|
||||
&f_uac2_opts_attr_clksrc_out_name,
|
||||
|
||||
&f_uac2_opts_attr_p_it_name,
|
||||
&f_uac2_opts_attr_p_it_ch_name,
|
||||
&f_uac2_opts_attr_p_ot_name,
|
||||
&f_uac2_opts_attr_p_fu_vol_name,
|
||||
|
||||
&f_uac2_opts_attr_c_it_name,
|
||||
&f_uac2_opts_attr_c_it_ch_name,
|
||||
&f_uac2_opts_attr_c_ot_name,
|
||||
&f_uac2_opts_attr_c_fu_vol_name,
|
||||
|
||||
&f_uac2_opts_attr_p_terminal_type,
|
||||
&f_uac2_opts_attr_c_terminal_type,
|
||||
@ -2191,6 +2224,19 @@ static struct usb_function_instance *afunc_alloc_inst(void)
|
||||
opts->fb_max = FBACK_FAST_MAX;
|
||||
|
||||
scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
|
||||
scnprintf(opts->if_ctrl_name, sizeof(opts->if_ctrl_name), "Topology Control");
|
||||
scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock");
|
||||
scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock");
|
||||
|
||||
scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out");
|
||||
scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
|
||||
scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In");
|
||||
scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
|
||||
|
||||
scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "USBH Out");
|
||||
scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
|
||||
scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "USBD In");
|
||||
scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
|
||||
|
||||
opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE;
|
||||
opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE;
|
||||
|
@ -1140,35 +1140,35 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new u_audio_controls[] = {
|
||||
[UAC_FBACK_CTRL] {
|
||||
[UAC_FBACK_CTRL] = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "Capture Pitch 1000000",
|
||||
.info = u_audio_pitch_info,
|
||||
.get = u_audio_pitch_get,
|
||||
.put = u_audio_pitch_put,
|
||||
},
|
||||
[UAC_P_PITCH_CTRL] {
|
||||
[UAC_P_PITCH_CTRL] = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "Playback Pitch 1000000",
|
||||
.info = u_audio_pitch_info,
|
||||
.get = u_audio_pitch_get,
|
||||
.put = u_audio_pitch_put,
|
||||
},
|
||||
[UAC_MUTE_CTRL] {
|
||||
[UAC_MUTE_CTRL] = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "", /* will be filled later */
|
||||
.info = u_audio_mute_info,
|
||||
.get = u_audio_mute_get,
|
||||
.put = u_audio_mute_put,
|
||||
},
|
||||
[UAC_VOLUME_CTRL] {
|
||||
[UAC_VOLUME_CTRL] = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "", /* will be filled later */
|
||||
.info = u_audio_volume_info,
|
||||
.get = u_audio_volume_get,
|
||||
.put = u_audio_volume_put,
|
||||
},
|
||||
[UAC_RATE_CTRL] {
|
||||
[UAC_RATE_CTRL] = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "", /* will be filled later */
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
@ -126,6 +127,7 @@ struct gs_port {
|
||||
wait_queue_head_t close_wait;
|
||||
bool suspended; /* port suspended */
|
||||
bool start_delayed; /* delay start when suspended */
|
||||
struct async_icount icount;
|
||||
|
||||
/* REVISIT this state ... */
|
||||
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
|
||||
@ -257,6 +259,7 @@ __acquires(&port->port_lock)
|
||||
break;
|
||||
}
|
||||
do_tty_wake = true;
|
||||
port->icount.tx += len;
|
||||
|
||||
req->length = len;
|
||||
list_del(&req->list);
|
||||
@ -408,6 +411,7 @@ static void gs_rx_push(struct work_struct *work)
|
||||
size -= n;
|
||||
}
|
||||
|
||||
port->icount.rx += size;
|
||||
count = tty_insert_flip_string(&port->port, packet,
|
||||
size);
|
||||
if (count)
|
||||
@ -851,6 +855,23 @@ static int gs_break_ctl(struct tty_struct *tty, int duration)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int gs_get_icount(struct tty_struct *tty,
|
||||
struct serial_icounter_struct *icount)
|
||||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
struct async_icount cnow;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
cnow = port->icount;
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
icount->rx = cnow.rx;
|
||||
icount->tx = cnow.tx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tty_operations gs_tty_ops = {
|
||||
.open = gs_open,
|
||||
.close = gs_close,
|
||||
@ -861,6 +882,7 @@ static const struct tty_operations gs_tty_ops = {
|
||||
.chars_in_buffer = gs_chars_in_buffer,
|
||||
.unthrottle = gs_unthrottle,
|
||||
.break_ctl = gs_break_ctl,
|
||||
.get_icount = gs_get_icount,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -17,6 +17,10 @@
|
||||
struct f_serial_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
u8 port_num;
|
||||
u8 protocol;
|
||||
|
||||
struct mutex lock; /* protect instances */
|
||||
int instances;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -52,7 +52,17 @@ struct f_uac1_opts {
|
||||
int req_number;
|
||||
unsigned bound:1;
|
||||
|
||||
char function_name[32];
|
||||
char function_name[USB_MAX_STRING_LEN];
|
||||
|
||||
char p_it_name[USB_MAX_STRING_LEN];
|
||||
char p_it_ch_name[USB_MAX_STRING_LEN];
|
||||
char p_ot_name[USB_MAX_STRING_LEN];
|
||||
char p_fu_vol_name[USB_MAX_STRING_LEN];
|
||||
|
||||
char c_it_name[USB_MAX_STRING_LEN];
|
||||
char c_it_ch_name[USB_MAX_STRING_LEN];
|
||||
char c_ot_name[USB_MAX_STRING_LEN];
|
||||
char c_fu_vol_name[USB_MAX_STRING_LEN];
|
||||
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
|
@ -68,7 +68,20 @@ struct f_uac2_opts {
|
||||
int fb_max;
|
||||
bool bound;
|
||||
|
||||
char function_name[32];
|
||||
char function_name[USB_MAX_STRING_LEN];
|
||||
char if_ctrl_name[USB_MAX_STRING_LEN];
|
||||
char clksrc_in_name[USB_MAX_STRING_LEN];
|
||||
char clksrc_out_name[USB_MAX_STRING_LEN];
|
||||
|
||||
char p_it_name[USB_MAX_STRING_LEN];
|
||||
char p_it_ch_name[USB_MAX_STRING_LEN];
|
||||
char p_ot_name[USB_MAX_STRING_LEN];
|
||||
char p_fu_vol_name[USB_MAX_STRING_LEN];
|
||||
|
||||
char c_it_name[USB_MAX_STRING_LEN];
|
||||
char c_it_ch_name[USB_MAX_STRING_LEN];
|
||||
char c_ot_name[USB_MAX_STRING_LEN];
|
||||
char c_fu_vol_name[USB_MAX_STRING_LEN];
|
||||
|
||||
s16 p_terminal_type;
|
||||
s16 c_terminal_type;
|
||||
|
@ -121,6 +121,9 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
|
||||
list_for_each_entry(format, &uvc->header->formats, entry) {
|
||||
const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
|
||||
|
||||
if (IS_ERR(fmtdesc))
|
||||
continue;
|
||||
|
||||
if (fmtdesc->fcc == pixelformat) {
|
||||
uformat = format->fmt;
|
||||
break;
|
||||
@ -240,6 +243,7 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
struct uvc_video *video = &uvc->video;
|
||||
struct uvcg_format *uformat;
|
||||
struct uvcg_frame *uframe;
|
||||
const struct uvc_format_desc *fmtdesc;
|
||||
u8 *fcc;
|
||||
|
||||
if (fmt->type != video->queue.queue.type)
|
||||
@ -277,7 +281,10 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
fmt->fmt.pix.height = uframe->frame.w_height;
|
||||
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
|
||||
fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
|
||||
fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
|
||||
fmtdesc = to_uvc_format(uformat);
|
||||
if (IS_ERR(fmtdesc))
|
||||
return PTR_ERR(fmtdesc);
|
||||
fmt->fmt.pix.pixelformat = fmtdesc->fcc;
|
||||
}
|
||||
fmt->fmt.pix.field = V4L2_FIELD_NONE;
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
|
||||
@ -389,6 +396,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
|
||||
return -EINVAL;
|
||||
|
||||
fmtdesc = to_uvc_format(uformat);
|
||||
if (IS_ERR(fmtdesc))
|
||||
return PTR_ERR(fmtdesc);
|
||||
|
||||
f->pixelformat = fmtdesc->fcc;
|
||||
|
||||
return 0;
|
||||
|
@ -8,8 +8,8 @@
|
||||
* Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
|
||||
*/
|
||||
|
||||
#include "u_f.h"
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/func_utils.h>
|
||||
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len)
|
||||
{
|
||||
|
@ -639,6 +639,7 @@ static const struct of_device_id bdc_of_match[] = {
|
||||
{ .compatible = "brcm,bdc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bdc_of_match);
|
||||
|
||||
static struct platform_driver bdc_driver = {
|
||||
.driver = {
|
||||
|
@ -2033,8 +2033,8 @@ static void cdns2_quiesce(struct cdns2_device *pdev)
|
||||
set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
|
||||
|
||||
/* Disable interrupt. */
|
||||
writeb(0, &pdev->interrupt_regs->extien),
|
||||
writeb(0, &pdev->interrupt_regs->usbien),
|
||||
writeb(0, &pdev->interrupt_regs->extien);
|
||||
writeb(0, &pdev->interrupt_regs->usbien);
|
||||
writew(0, &pdev->adma_regs->ep_ien);
|
||||
|
||||
/* Clear interrupt line. */
|
||||
|
@ -15,8 +15,7 @@
|
||||
#include "cdns2-gadget.h"
|
||||
|
||||
#define PCI_DRIVER_NAME "cdns-pci-usbhs"
|
||||
#define CDNS_VENDOR_ID 0x17cd
|
||||
#define CDNS_DEVICE_ID 0x0120
|
||||
#define PCI_DEVICE_ID_CDNS_USB2 0x0120
|
||||
#define PCI_BAR_DEV 0
|
||||
#define PCI_DEV_FN_DEVICE 0
|
||||
|
||||
@ -114,8 +113,8 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct pci_device_id cdns2_pci_ids[] = {
|
||||
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB2),
|
||||
.class = PCI_CLASS_SERIAL_USB_DEVICE },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -1304,7 +1304,8 @@ static int dummy_urb_enqueue(
|
||||
|
||||
/* kick the scheduler, it'll do the rest */
|
||||
if (!hrtimer_active(&dum_hcd->timer))
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL);
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
|
||||
@ -1325,7 +1326,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING &&
|
||||
!list_empty(&dum_hcd->urbp_list))
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
|
||||
return rc;
|
||||
@ -1995,7 +1996,8 @@ return_urb:
|
||||
dum_hcd->udev = NULL;
|
||||
} else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
|
||||
/* want a 1 msec delay here */
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL);
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
@ -2389,7 +2391,7 @@ static int dummy_bus_resume(struct usb_hcd *hcd)
|
||||
dum_hcd->rh_state = DUMMY_RH_RUNNING;
|
||||
set_link_state(dum_hcd);
|
||||
if (!list_empty(&dum_hcd->urbp_list))
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
}
|
||||
spin_unlock_irq(&dum_hcd->dum->lock);
|
||||
@ -2467,7 +2469,7 @@ static DEVICE_ATTR_RO(urbs);
|
||||
|
||||
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
|
||||
{
|
||||
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
dum_hcd->timer.function = dummy_timer;
|
||||
dum_hcd->rh_state = DUMMY_RH_RUNNING;
|
||||
dum_hcd->stream_en_ep = 0;
|
||||
@ -2497,7 +2499,7 @@ static int dummy_start(struct usb_hcd *hcd)
|
||||
return dummy_start_ss(dum_hcd);
|
||||
|
||||
spin_lock_init(&dum_hcd->dum->lock);
|
||||
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
dum_hcd->timer.function = dummy_timer;
|
||||
dum_hcd->rh_state = DUMMY_RH_RUNNING;
|
||||
|
||||
|
@ -1487,31 +1487,29 @@ static int udc_ep0_out_req(struct lpc32xx_udc *udc)
|
||||
req = list_entry(ep0->queue.next, struct lpc32xx_request,
|
||||
queue);
|
||||
|
||||
if (req) {
|
||||
if (req->req.length == 0) {
|
||||
/* Just dequeue request */
|
||||
done(ep0, req, 0);
|
||||
udc->ep0state = WAIT_FOR_SETUP;
|
||||
return 1;
|
||||
}
|
||||
if (req->req.length == 0) {
|
||||
/* Just dequeue request */
|
||||
done(ep0, req, 0);
|
||||
udc->ep0state = WAIT_FOR_SETUP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get data from FIFO */
|
||||
bufferspace = req->req.length - req->req.actual;
|
||||
if (bufferspace > ep0->ep.maxpacket)
|
||||
bufferspace = ep0->ep.maxpacket;
|
||||
/* Get data from FIFO */
|
||||
bufferspace = req->req.length - req->req.actual;
|
||||
if (bufferspace > ep0->ep.maxpacket)
|
||||
bufferspace = ep0->ep.maxpacket;
|
||||
|
||||
/* Copy data to buffer */
|
||||
prefetchw(req->req.buf + req->req.actual);
|
||||
tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
|
||||
bufferspace);
|
||||
req->req.actual += bufferspace;
|
||||
/* Copy data to buffer */
|
||||
prefetchw(req->req.buf + req->req.actual);
|
||||
tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
|
||||
bufferspace);
|
||||
req->req.actual += bufferspace;
|
||||
|
||||
if (tr < ep0->ep.maxpacket) {
|
||||
/* This is the last packet */
|
||||
done(ep0, req, 0);
|
||||
udc->ep0state = WAIT_FOR_SETUP;
|
||||
return 1;
|
||||
}
|
||||
if (tr < ep0->ep.maxpacket) {
|
||||
/* This is the last packet */
|
||||
done(ep0, req, 0);
|
||||
udc->ep0state = WAIT_FOR_SETUP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1962,18 +1960,17 @@ static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
|
||||
|
||||
/* If there isn't a request waiting, something went wrong */
|
||||
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
|
||||
if (req) {
|
||||
done(ep, req, 0);
|
||||
|
||||
/* Start another request if ready */
|
||||
if (!list_empty(&ep->queue)) {
|
||||
if (ep->is_in)
|
||||
udc_ep_in_req_dma(udc, ep);
|
||||
else
|
||||
udc_ep_out_req_dma(udc, ep);
|
||||
} else
|
||||
ep->req_pending = 0;
|
||||
}
|
||||
done(ep, req, 0);
|
||||
|
||||
/* Start another request if ready */
|
||||
if (!list_empty(&ep->queue)) {
|
||||
if (ep->is_in)
|
||||
udc_ep_in_req_dma(udc, ep);
|
||||
else
|
||||
udc_ep_out_req_dma(udc, ep);
|
||||
} else
|
||||
ep->req_pending = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1989,10 +1986,6 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
|
||||
#endif
|
||||
|
||||
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
|
||||
if (!req) {
|
||||
ep_err(ep, "DMA interrupt on no req!\n");
|
||||
return;
|
||||
}
|
||||
dd = req->dd_desc_ptr;
|
||||
|
||||
/* DMA descriptor should always be retired for this call */
|
||||
|
@ -947,7 +947,7 @@ static int xudc_ep_disable(struct usb_ep *_ep)
|
||||
ep->desc = NULL;
|
||||
ep->ep_usb.desc = NULL;
|
||||
|
||||
dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber);
|
||||
dev_dbg(udc->dev, "USB Ep %d disable\n", ep->epnumber);
|
||||
/* Disable the endpoint.*/
|
||||
epcfg = udc->read_fn(udc->addr + ep->offset);
|
||||
epcfg &= ~XUSB_EP_CFG_VALID_MASK;
|
||||
|
@ -40,11 +40,11 @@ config USB_XHCI_DBGCAP
|
||||
config USB_XHCI_PCI
|
||||
tristate
|
||||
depends on USB_PCI
|
||||
depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS
|
||||
default y
|
||||
|
||||
config USB_XHCI_PCI_RENESAS
|
||||
tristate "Support for additional Renesas xHCI controller with firmware"
|
||||
depends on USB_XHCI_PCI
|
||||
help
|
||||
Say 'Y' to enable the support for the Renesas xHCI controller with
|
||||
firmware. Make sure you have the firmware for the device and
|
||||
|
@ -246,6 +246,7 @@ static const struct of_device_id brcm_ehci_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7445-ehci", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcm_ehci_of_match);
|
||||
|
||||
static struct platform_driver ehci_brcm_driver = {
|
||||
.probe = ehci_brcm_probe,
|
||||
|
@ -48,7 +48,6 @@ struct exynos_ehci_hcd {
|
||||
static int exynos_ehci_get_phy(struct device *dev,
|
||||
struct exynos_ehci_hcd *exynos_ehci)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct phy *phy;
|
||||
int phy_number, num_phys;
|
||||
int ret;
|
||||
@ -66,26 +65,22 @@ static int exynos_ehci_get_phy(struct device *dev,
|
||||
return 0;
|
||||
|
||||
/* Get PHYs using legacy bindings */
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
for_each_available_child_of_node_scoped(dev->of_node, child) {
|
||||
ret = of_property_read_u32(child, "reg", &phy_number);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse device tree\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (phy_number >= PHY_NUMBER) {
|
||||
dev_err(dev, "Invalid number of PHYs\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy = devm_of_phy_optional_get(dev, child, NULL);
|
||||
exynos_ehci->phy[phy_number] = phy;
|
||||
if (IS_ERR(phy)) {
|
||||
of_node_put(child);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
}
|
||||
|
||||
exynos_ehci->legacy_phy = true;
|
||||
|
@ -37,7 +37,6 @@ struct exynos_ohci_hcd {
|
||||
static int exynos_ohci_get_phy(struct device *dev,
|
||||
struct exynos_ohci_hcd *exynos_ohci)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct phy *phy;
|
||||
int phy_number, num_phys;
|
||||
int ret;
|
||||
@ -55,26 +54,22 @@ static int exynos_ohci_get_phy(struct device *dev,
|
||||
return 0;
|
||||
|
||||
/* Get PHYs using legacy bindings */
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
for_each_available_child_of_node_scoped(dev->of_node, child) {
|
||||
ret = of_property_read_u32(child, "reg", &phy_number);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse device tree\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (phy_number >= PHY_NUMBER) {
|
||||
dev_err(dev, "Invalid number of PHYs\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy = devm_of_phy_optional_get(dev, child, NULL);
|
||||
exynos_ohci->phy[phy_number] = phy;
|
||||
if (IS_ERR(phy)) {
|
||||
of_node_put(child);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
}
|
||||
|
||||
exynos_ohci->legacy_phy = true;
|
||||
|
@ -51,8 +51,6 @@ static struct hc_driver __read_mostly ohci_nxp_hc_driver;
|
||||
|
||||
static struct i2c_client *isp1301_i2c_client;
|
||||
|
||||
static struct clk *usb_host_clk;
|
||||
|
||||
static void isp1301_configure_lpc32xx(void)
|
||||
{
|
||||
/* LPC32XX only supports DAT_SE0 USB mode */
|
||||
@ -155,6 +153,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
int ret = 0, irq;
|
||||
struct device_node *isp1301_node;
|
||||
struct clk *usb_host_clk;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
isp1301_node = of_parse_phandle(pdev->dev.of_node,
|
||||
@ -180,26 +179,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Enable USB host clock */
|
||||
usb_host_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
usb_host_clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(usb_host_clk)) {
|
||||
dev_err(&pdev->dev, "failed to acquire USB OHCI clock\n");
|
||||
dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n");
|
||||
ret = PTR_ERR(usb_host_clk);
|
||||
goto fail_disable;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(usb_host_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to start USB OHCI clock\n");
|
||||
goto fail_disable;
|
||||
}
|
||||
|
||||
isp1301_configure();
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
dev_err(&pdev->dev, "Failed to allocate HC buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_hcd;
|
||||
goto fail_disable;
|
||||
}
|
||||
|
||||
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
@ -229,8 +222,6 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
|
||||
ohci_nxp_stop_hc();
|
||||
fail_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_hcd:
|
||||
clk_disable_unprepare(usb_host_clk);
|
||||
fail_disable:
|
||||
isp1301_i2c_client = NULL;
|
||||
return ret;
|
||||
@ -243,7 +234,6 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev)
|
||||
usb_remove_hcd(hcd);
|
||||
ohci_nxp_stop_hc();
|
||||
usb_put_hcd(hcd);
|
||||
clk_disable_unprepare(usb_host_clk);
|
||||
isp1301_i2c_client = NULL;
|
||||
}
|
||||
|
||||
|
@ -202,10 +202,6 @@ static const struct of_device_id ohci_hcd_ppc_of_match[] = {
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE
|
||||
{
|
||||
.name = "usb",
|
||||
.compatible = "ohci-littledian",
|
||||
},
|
||||
{
|
||||
.name = "usb",
|
||||
.compatible = "ohci-le",
|
||||
|
@ -297,9 +297,9 @@ static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
|
||||
static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
|
||||
{
|
||||
u16 pipenum = pipe->info.pipenum;
|
||||
const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
|
||||
const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
|
||||
const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
|
||||
static const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
|
||||
static const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
|
||||
static const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
|
||||
|
||||
if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */
|
||||
dma_ch = R8A66597_PIPE_NO_DMA;
|
||||
|
@ -173,16 +173,18 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status)
|
||||
spin_lock(&dbc->lock);
|
||||
}
|
||||
|
||||
static void xhci_dbc_flush_single_request(struct dbc_request *req)
|
||||
static void trb_to_noop(union xhci_trb *trb)
|
||||
{
|
||||
union xhci_trb *trb = req->trb;
|
||||
|
||||
trb->generic.field[0] = 0;
|
||||
trb->generic.field[1] = 0;
|
||||
trb->generic.field[2] = 0;
|
||||
trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
|
||||
trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP));
|
||||
}
|
||||
|
||||
static void xhci_dbc_flush_single_request(struct dbc_request *req)
|
||||
{
|
||||
trb_to_noop(req->trb);
|
||||
xhci_dbc_giveback(req, -ESHUTDOWN);
|
||||
}
|
||||
|
||||
@ -649,7 +651,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
|
||||
case DS_DISABLED:
|
||||
return;
|
||||
case DS_CONFIGURED:
|
||||
case DS_STALLED:
|
||||
if (dbc->driver->disconnect)
|
||||
dbc->driver->disconnect(dbc);
|
||||
break;
|
||||
@ -669,6 +670,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
|
||||
pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ep_halt_changes(struct xhci_dbc *dbc, struct dbc_ep *dep, bool halted)
|
||||
{
|
||||
if (halted) {
|
||||
dev_info(dbc->dev, "DbC Endpoint halted\n");
|
||||
dep->halted = 1;
|
||||
|
||||
} else if (dep->halted) {
|
||||
dev_info(dbc->dev, "DbC Endpoint halt cleared\n");
|
||||
dep->halted = 0;
|
||||
|
||||
if (!list_empty(&dep->list_pending))
|
||||
writel(DBC_DOOR_BELL_TARGET(dep->direction),
|
||||
&dbc->regs->doorbell);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event)
|
||||
{
|
||||
@ -697,6 +715,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
|
||||
struct xhci_ring *ring;
|
||||
int ep_id;
|
||||
int status;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
u32 comp_code;
|
||||
size_t remain_length;
|
||||
struct dbc_request *req = NULL, *r;
|
||||
@ -706,8 +725,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
|
||||
ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3]));
|
||||
dep = (ep_id == EPID_OUT) ?
|
||||
get_out_ep(dbc) : get_in_ep(dbc);
|
||||
ep_ctx = (ep_id == EPID_OUT) ?
|
||||
dbc_bulkout_ctx(dbc) : dbc_bulkin_ctx(dbc);
|
||||
ring = dep->ring;
|
||||
|
||||
/* Match the pending request: */
|
||||
list_for_each_entry(r, &dep->list_pending, list_pending) {
|
||||
if (r->trb_dma == event->trans_event.buffer) {
|
||||
req = r;
|
||||
break;
|
||||
}
|
||||
if (r->status == -COMP_STALL_ERROR) {
|
||||
dev_warn(dbc->dev, "Give back stale stalled req\n");
|
||||
ring->num_trbs_free++;
|
||||
xhci_dbc_giveback(r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!req) {
|
||||
dev_warn(dbc->dev, "no matched request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
|
||||
|
||||
switch (comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
remain_length = 0;
|
||||
@ -718,31 +759,49 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
|
||||
case COMP_TRB_ERROR:
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
case COMP_STALL_ERROR:
|
||||
dev_warn(dbc->dev, "tx error %d detected\n", comp_code);
|
||||
status = -comp_code;
|
||||
break;
|
||||
case COMP_STALL_ERROR:
|
||||
dev_warn(dbc->dev, "Stall error at bulk TRB %llx, remaining %zu, ep deq %llx\n",
|
||||
event->trans_event.buffer, remain_length, ep_ctx->deq);
|
||||
status = 0;
|
||||
dep->halted = 1;
|
||||
|
||||
/*
|
||||
* xHC DbC may trigger a STALL bulk xfer event when host sends a
|
||||
* ClearFeature(ENDPOINT_HALT) request even if there wasn't an
|
||||
* active bulk transfer.
|
||||
*
|
||||
* Don't give back this transfer request as hardware will later
|
||||
* start processing TRBs starting from this 'STALLED' TRB,
|
||||
* causing TRBs and requests to be out of sync.
|
||||
*
|
||||
* If STALL event shows some bytes were transferred then assume
|
||||
* it's an actual transfer issue and give back the request.
|
||||
* In this case mark the TRB as No-Op to avoid hw from using the
|
||||
* TRB again.
|
||||
*/
|
||||
|
||||
if ((ep_ctx->deq & ~TRB_CYCLE) == event->trans_event.buffer) {
|
||||
dev_dbg(dbc->dev, "Ep stopped on Stalled TRB\n");
|
||||
if (remain_length == req->length) {
|
||||
dev_dbg(dbc->dev, "Spurious stall event, keep req\n");
|
||||
req->status = -COMP_STALL_ERROR;
|
||||
req->actual = 0;
|
||||
return;
|
||||
}
|
||||
dev_dbg(dbc->dev, "Give back stalled req, but turn TRB to No-op\n");
|
||||
trb_to_noop(req->trb);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dbc->dev, "unknown tx error %d\n", comp_code);
|
||||
status = -comp_code;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Match the pending request: */
|
||||
list_for_each_entry(r, &dep->list_pending, list_pending) {
|
||||
if (r->trb_dma == event->trans_event.buffer) {
|
||||
req = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!req) {
|
||||
dev_warn(dbc->dev, "no matched request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
|
||||
|
||||
ring->num_trbs_free++;
|
||||
req->actual = req->length - remain_length;
|
||||
xhci_dbc_giveback(req, status);
|
||||
@ -762,7 +821,6 @@ static void inc_evt_deq(struct xhci_ring *ring)
|
||||
static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
|
||||
{
|
||||
dma_addr_t deq;
|
||||
struct dbc_ep *dep;
|
||||
union xhci_trb *evt;
|
||||
u32 ctrl, portsc;
|
||||
bool update_erdp = false;
|
||||
@ -814,43 +872,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
|
||||
return EVT_DISC;
|
||||
}
|
||||
|
||||
/* Handle endpoint stall event: */
|
||||
/* Check and handle changes in endpoint halt status */
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
if ((ctrl & DBC_CTRL_HALT_IN_TR) ||
|
||||
(ctrl & DBC_CTRL_HALT_OUT_TR)) {
|
||||
dev_info(dbc->dev, "DbC Endpoint stall\n");
|
||||
dbc->state = DS_STALLED;
|
||||
|
||||
if (ctrl & DBC_CTRL_HALT_IN_TR) {
|
||||
dep = get_in_ep(dbc);
|
||||
xhci_dbc_flush_endpoint_requests(dep);
|
||||
}
|
||||
|
||||
if (ctrl & DBC_CTRL_HALT_OUT_TR) {
|
||||
dep = get_out_ep(dbc);
|
||||
xhci_dbc_flush_endpoint_requests(dep);
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
}
|
||||
handle_ep_halt_changes(dbc, get_in_ep(dbc), ctrl & DBC_CTRL_HALT_IN_TR);
|
||||
handle_ep_halt_changes(dbc, get_out_ep(dbc), ctrl & DBC_CTRL_HALT_OUT_TR);
|
||||
|
||||
/* Clear DbC run change bit: */
|
||||
if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) {
|
||||
writel(ctrl, &dbc->regs->control);
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
}
|
||||
|
||||
break;
|
||||
case DS_STALLED:
|
||||
ctrl = readl(&dbc->regs->control);
|
||||
if (!(ctrl & DBC_CTRL_HALT_IN_TR) &&
|
||||
!(ctrl & DBC_CTRL_HALT_OUT_TR) &&
|
||||
(ctrl & DBC_CTRL_DBC_RUN)) {
|
||||
dbc->state = DS_CONFIGURED;
|
||||
break;
|
||||
}
|
||||
|
||||
return EVT_DONE;
|
||||
default:
|
||||
dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state);
|
||||
break;
|
||||
@ -939,7 +971,6 @@ static const char * const dbc_state_strings[DS_MAX] = {
|
||||
[DS_ENABLED] = "enabled",
|
||||
[DS_CONNECTED] = "connected",
|
||||
[DS_CONFIGURED] = "configured",
|
||||
[DS_STALLED] = "stalled",
|
||||
};
|
||||
|
||||
static ssize_t dbc_show(struct device *dev,
|
||||
|
@ -81,7 +81,6 @@ enum dbc_state {
|
||||
DS_ENABLED,
|
||||
DS_CONNECTED,
|
||||
DS_CONFIGURED,
|
||||
DS_STALLED,
|
||||
DS_MAX
|
||||
};
|
||||
|
||||
@ -90,6 +89,7 @@ struct dbc_ep {
|
||||
struct list_head list_pending;
|
||||
struct xhci_ring *ring;
|
||||
unsigned int direction:1;
|
||||
unsigned int halted:1;
|
||||
};
|
||||
|
||||
#define DBC_QUEUE_SIZE 16
|
||||
|
@ -346,7 +346,7 @@ static void dbc_rx_push(struct tasklet_struct *t)
|
||||
port->n_read = 0;
|
||||
}
|
||||
|
||||
list_move(&req->list_pool, &port->read_pool);
|
||||
list_move_tail(&req->list_pool, &port->read_pool);
|
||||
}
|
||||
|
||||
if (do_push)
|
||||
|
@ -42,6 +42,7 @@
|
||||
#define XHCI_EXT_CAPS_DEBUG 10
|
||||
/* Vendor caps */
|
||||
#define XHCI_EXT_CAPS_VENDOR_INTEL 192
|
||||
#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206
|
||||
/* USB Legacy Support Capability - section 7.1.1 */
|
||||
#define XHCI_HC_BIOS_OWNED (1 << 16)
|
||||
#define XHCI_HC_OS_OWNED (1 << 24)
|
||||
@ -64,6 +65,10 @@
|
||||
#define XHCI_HLC (1 << 19)
|
||||
#define XHCI_BLC (1 << 20)
|
||||
|
||||
/* Intel SPR shadow capability */
|
||||
#define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */
|
||||
#define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */
|
||||
|
||||
/* command register values to disable interrupts and halt the HC */
|
||||
/* start/stop HC execution - do not write unless HC is halted*/
|
||||
#define XHCI_CMD_RUN (1 << 0)
|
||||
|
@ -752,6 +752,42 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
|
||||
return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
|
||||
}
|
||||
|
||||
/**
|
||||
* xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
|
||||
* @xhci: xhci host controller
|
||||
* @port: USB3 port to be checked.
|
||||
*
|
||||
* Some hosts can detect if a USB3 connection is native USB3 or tunneled over
|
||||
* USB4. Intel hosts expose this via vendor specific extended capability 206
|
||||
* eSS PORT registers TUNEN (tunnel enabled) bit.
|
||||
*
|
||||
* A USB3 device must be connected to the port to detect the tunnel.
|
||||
*
|
||||
* Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
|
||||
* detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
|
||||
* otherwise.
|
||||
*/
|
||||
enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
|
||||
struct xhci_port *port)
|
||||
{
|
||||
void __iomem *base;
|
||||
u32 offset;
|
||||
|
||||
base = &xhci->cap_regs->hc_capbase;
|
||||
offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
|
||||
|
||||
if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
|
||||
offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
|
||||
|
||||
if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
|
||||
return USB_LINK_TUNNELED;
|
||||
else
|
||||
return USB_LINK_NATIVE;
|
||||
}
|
||||
|
||||
return USB_LINK_UNKNOWN;
|
||||
}
|
||||
|
||||
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
|
||||
u32 link_state)
|
||||
{
|
||||
|
@ -2332,7 +2332,8 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
}
|
||||
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs)
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
||||
u32 imod_interval)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_interrupter *ir;
|
||||
@ -2365,6 +2366,11 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = xhci_set_interrupter_moderation(ir, imod_interval);
|
||||
if (err)
|
||||
xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n",
|
||||
i, imod_interval);
|
||||
|
||||
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
|
||||
i, xhci->max_interrupters);
|
||||
|
||||
|
@ -50,6 +50,8 @@
|
||||
#define RENESAS_RETRY 10000
|
||||
#define RENESAS_DELAY 10
|
||||
|
||||
#define RENESAS_FW_NAME "renesas_usb_fw.mem"
|
||||
|
||||
static int renesas_fw_download_image(struct pci_dev *dev,
|
||||
const u32 *fw, size_t step, bool rom)
|
||||
{
|
||||
@ -573,12 +575,10 @@ exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int renesas_xhci_check_request_fw(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
static int renesas_xhci_check_request_fw(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct xhci_driver_data *driver_data =
|
||||
(struct xhci_driver_data *)id->driver_data;
|
||||
const char *fw_name = driver_data->firmware;
|
||||
const char fw_name[] = RENESAS_FW_NAME;
|
||||
const struct firmware *fw;
|
||||
bool has_rom;
|
||||
int err;
|
||||
@ -625,7 +625,41 @@ exit:
|
||||
release_firmware(fw);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
|
||||
|
||||
MODULE_DESCRIPTION("Support for Renesas xHCI controller with firmware");
|
||||
static int
|
||||
xhci_pci_renesas_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = renesas_xhci_check_request_fw(dev, id);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return xhci_pci_common_probe(dev, id);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
static struct pci_driver xhci_renesas_pci_driver = {
|
||||
.name = "xhci-pci-renesas",
|
||||
.id_table = pci_ids,
|
||||
|
||||
.probe = xhci_pci_renesas_probe,
|
||||
.remove = xhci_pci_remove,
|
||||
|
||||
.shutdown = usb_hcd_pci_shutdown,
|
||||
.driver = {
|
||||
.pm = pm_ptr(&usb_hcd_pci_pm_ops),
|
||||
},
|
||||
};
|
||||
module_pci_driver(xhci_renesas_pci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas xHCI PCI Host Controller Driver");
|
||||
MODULE_FIRMWARE(RENESAS_FW_NAME);
|
||||
MODULE_IMPORT_NS(xhci);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -55,6 +55,9 @@
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
|
||||
|
||||
#define PCI_VENDOR_ID_PHYTIUM 0x1db7
|
||||
#define PCI_DEVICE_ID_PHYTIUM_XHCI 0xdc27
|
||||
|
||||
/* Thunderbolt */
|
||||
#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5
|
||||
@ -78,6 +81,9 @@
|
||||
#define PCI_DEVICE_ID_ASMEDIA_2142_XHCI 0x2142
|
||||
#define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242
|
||||
|
||||
#define PCI_DEVICE_ID_CADENCE 0x17CD
|
||||
#define PCI_DEVICE_ID_CADENCE_SSP 0x0200
|
||||
|
||||
static const char hcd_name[] = "xhci_hcd";
|
||||
|
||||
static struct hc_driver __read_mostly xhci_pci_hc_driver;
|
||||
@ -93,6 +99,10 @@ static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
|
||||
.update_hub_device = xhci_pci_update_hub_device,
|
||||
};
|
||||
|
||||
/*
|
||||
* Primary Legacy and MSI IRQ are synced in suspend_common().
|
||||
* All MSI-X IRQs and secondary MSI IRQs should be synced here.
|
||||
*/
|
||||
static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
@ -105,13 +115,12 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
|
||||
}
|
||||
}
|
||||
|
||||
/* Free any IRQs and disable MSI-X */
|
||||
/* Legacy IRQ is freed by usb_remove_hcd() or usb_hcd_pci_shutdown() */
|
||||
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
/* return if using legacy interrupt */
|
||||
if (hcd->irq > 0)
|
||||
return;
|
||||
|
||||
@ -235,15 +244,6 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
|
||||
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct xhci_driver_data *driver_data;
|
||||
const struct pci_device_id *id;
|
||||
|
||||
id = pci_match_id(to_pci_driver(pdev->dev.driver)->id_table, pdev);
|
||||
|
||||
if (id && id->driver_data) {
|
||||
driver_data = (struct xhci_driver_data *)id->driver_data;
|
||||
xhci->quirks |= driver_data->quirks;
|
||||
}
|
||||
|
||||
/* Look for vendor-specific quirks */
|
||||
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
|
||||
@ -416,6 +416,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
if (pdev->vendor == PCI_VENDOR_ID_VIA)
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_PHYTIUM &&
|
||||
pdev->device == PCI_DEVICE_ID_PHYTIUM_XHCI)
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
|
||||
/* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
|
||||
if (pdev->vendor == PCI_VENDOR_ID_VIA &&
|
||||
pdev->device == 0x3432)
|
||||
@ -473,6 +477,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_DEVICE_ID_CADENCE &&
|
||||
pdev->device == PCI_DEVICE_ID_CADENCE_SSP)
|
||||
xhci->quirks |= XHCI_CDNS_SCTX_QUIRK;
|
||||
|
||||
/* xHC spec requires PCI devices to support D3hot and D3cold */
|
||||
if (xhci->hci_version >= 0x120)
|
||||
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
|
||||
@ -534,10 +542,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
struct xhci_hcd *xhci;
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
int retval;
|
||||
u8 sbrn;
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
if (!xhci->sbrn)
|
||||
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
|
||||
|
||||
/* imod_interval is the interrupt moderation value in nanoseconds. */
|
||||
xhci->imod_interval = 40000;
|
||||
@ -552,7 +559,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
|
||||
xhci_pme_acpi_rtd3_enable(pdev);
|
||||
|
||||
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
|
||||
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &sbrn);
|
||||
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int)sbrn);
|
||||
|
||||
/* Find any debug ports */
|
||||
return xhci_pci_reinit(xhci, pdev);
|
||||
@ -572,21 +580,13 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
|
||||
* We need to register our own PCI probe function (instead of the USB core's
|
||||
* function) in order to create a second roothub under xHCI.
|
||||
*/
|
||||
static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
struct xhci_hcd *xhci;
|
||||
struct usb_hcd *hcd;
|
||||
struct xhci_driver_data *driver_data;
|
||||
struct reset_control *reset;
|
||||
|
||||
driver_data = (struct xhci_driver_data *)id->driver_data;
|
||||
if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) {
|
||||
retval = renesas_xhci_check_request_fw(dev, id);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return PTR_ERR(reset);
|
||||
@ -651,12 +651,30 @@ put_runtime_pm:
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, xhci);
|
||||
|
||||
static void xhci_pci_remove(struct pci_dev *dev)
|
||||
static const struct pci_device_id pci_ids_reject[] = {
|
||||
/* handled by xhci-pci-renesas */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
|
||||
static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
if (pci_match_id(pci_ids_reject, dev))
|
||||
return -ENODEV;
|
||||
|
||||
return xhci_pci_common_probe(dev, id);
|
||||
}
|
||||
|
||||
void xhci_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct xhci_hcd *xhci;
|
||||
bool set_power_d3;
|
||||
|
||||
xhci = hcd_to_xhci(pci_get_drvdata(dev));
|
||||
set_power_d3 = xhci->quirks & XHCI_SPURIOUS_WAKEUP;
|
||||
|
||||
xhci->xhc_state |= XHCI_STATE_REMOVING;
|
||||
|
||||
@ -669,12 +687,13 @@ static void xhci_pci_remove(struct pci_dev *dev)
|
||||
xhci->shared_hcd = NULL;
|
||||
}
|
||||
|
||||
/* Workaround for spurious wakeups at shutdown with HSW */
|
||||
if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
|
||||
pci_set_power_state(dev, PCI_D3hot);
|
||||
|
||||
usb_hcd_pci_remove(dev);
|
||||
|
||||
/* Workaround for spurious wakeups at shutdown with HSW */
|
||||
if (set_power_d3)
|
||||
pci_set_power_state(dev, PCI_D3hot);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, xhci);
|
||||
|
||||
/*
|
||||
* In some Intel xHCI controllers, in order to get D3 working,
|
||||
@ -783,7 +802,6 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
int retval = 0;
|
||||
|
||||
reset_control_reset(xhci->reset);
|
||||
|
||||
@ -814,8 +832,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
|
||||
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
|
||||
xhci_pme_quirk(hcd);
|
||||
|
||||
retval = xhci_resume(xhci, msg);
|
||||
return retval;
|
||||
return xhci_resume(xhci, msg);
|
||||
}
|
||||
|
||||
static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
|
||||
@ -882,19 +899,8 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct xhci_driver_data reneses_data = {
|
||||
.quirks = XHCI_RENESAS_FW_QUIRK,
|
||||
.firmware = "renesas_usb_fw.mem",
|
||||
};
|
||||
|
||||
/* PCI driver selection metadata; PCI hotplugging uses this */
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014),
|
||||
.driver_data = (unsigned long)&reneses_data,
|
||||
},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015),
|
||||
.driver_data = (unsigned long)&reneses_data,
|
||||
},
|
||||
/* handle any USB 3.0 xHCI controller */
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
|
||||
},
|
||||
@ -902,14 +908,6 @@ static const struct pci_device_id pci_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
/*
|
||||
* Without CONFIG_USB_XHCI_PCI_RENESAS renesas_xhci_check_request_fw() won't
|
||||
* load firmware, so don't encumber the xhci-pci driver with it.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
|
||||
MODULE_FIRMWARE("renesas_usb_fw.mem");
|
||||
#endif
|
||||
|
||||
/* pci driver glue; this is a "new style" PCI driver module */
|
||||
static struct pci_driver xhci_pci_driver = {
|
||||
.name = hcd_name,
|
||||
|
@ -4,22 +4,7 @@
|
||||
#ifndef XHCI_PCI_H
|
||||
#define XHCI_PCI_H
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
|
||||
int renesas_xhci_check_request_fw(struct pci_dev *dev,
|
||||
const struct pci_device_id *id);
|
||||
|
||||
#else
|
||||
static int renesas_xhci_check_request_fw(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct xhci_driver_data {
|
||||
u64 quirks;
|
||||
const char *firmware;
|
||||
};
|
||||
int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id);
|
||||
void xhci_pci_remove(struct pci_dev *dev);
|
||||
|
||||
#endif
|
||||
|
@ -259,6 +259,12 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
|
||||
if (device_property_read_bool(tmpdev, "write-64-hi-lo-quirk"))
|
||||
xhci->quirks |= XHCI_WRITE_64_HI_LO;
|
||||
|
||||
if (device_property_read_bool(tmpdev, "xhci-missing-cas-quirk"))
|
||||
xhci->quirks |= XHCI_MISSING_CAS;
|
||||
|
||||
if (device_property_read_bool(tmpdev, "xhci-skip-phy-init-quirk"))
|
||||
xhci->quirks |= XHCI_SKIP_PHY_INIT;
|
||||
|
||||
device_property_read_u32(tmpdev, "imod-interval-ns",
|
||||
&xhci->imod_interval);
|
||||
}
|
||||
|
@ -1399,6 +1399,20 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
||||
struct xhci_stream_ctx *ctx =
|
||||
&ep->stream_info->stream_ctx_array[stream_id];
|
||||
deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
|
||||
|
||||
/*
|
||||
* Cadence xHCI controllers store some endpoint state
|
||||
* information within Rsvd0 fields of Stream Endpoint
|
||||
* context. This field is not cleared during Set TR
|
||||
* Dequeue Pointer command which causes XDMA to skip
|
||||
* over transfer ring and leads to data loss on stream
|
||||
* pipe.
|
||||
* To fix this issue driver must clear Rsvd0 field.
|
||||
*/
|
||||
if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) {
|
||||
ctx->reserved[0] = 0;
|
||||
ctx->reserved[1] = 0;
|
||||
}
|
||||
} else {
|
||||
deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
|
||||
}
|
||||
@ -2521,9 +2535,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
td->status = 0;
|
||||
break;
|
||||
case COMP_SHORT_PACKET:
|
||||
xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
|
||||
td->urb->ep->desc.bEndpointAddress,
|
||||
requested, remaining);
|
||||
td->status = 0;
|
||||
break;
|
||||
case COMP_STOPPED_SHORT_PACKET:
|
||||
@ -2764,35 +2775,25 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
/* This TRB should be in the TD at the head of this ring's
|
||||
* TD list.
|
||||
if (list_empty(&ep_ring->td_list)) {
|
||||
/*
|
||||
* Don't print wanings if ring is empty due to a stopped endpoint generating an
|
||||
* extra completion event if the device was suspended. Or, a event for the last TRB
|
||||
* of a short TD we already got a short event for. The short TD is already removed
|
||||
* from the TD list.
|
||||
*/
|
||||
if (list_empty(&ep_ring->td_list)) {
|
||||
/*
|
||||
* Don't print wanings if it's due to a stopped endpoint
|
||||
* generating an extra completion event if the device
|
||||
* was suspended. Or, a event for the last TRB of a
|
||||
* short TD we already got a short event for.
|
||||
* The short TD is already removed from the TD list.
|
||||
*/
|
||||
|
||||
if (!(trb_comp_code == COMP_STOPPED ||
|
||||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
|
||||
ep_ring->last_td_was_short)) {
|
||||
xhci_warn(xhci, "WARN Event TRB for slot %u ep %d with no TDs queued?\n",
|
||||
slot_id, ep_index);
|
||||
}
|
||||
if (ep->skip) {
|
||||
ep->skip = false;
|
||||
xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
|
||||
slot_id, ep_index);
|
||||
}
|
||||
|
||||
td = NULL;
|
||||
goto check_endpoint_halted;
|
||||
if (trb_comp_code != COMP_STOPPED &&
|
||||
trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
|
||||
!ep_ring->last_td_was_short) {
|
||||
xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
|
||||
slot_id, ep_index);
|
||||
}
|
||||
|
||||
ep->skip = false;
|
||||
goto check_endpoint_halted;
|
||||
}
|
||||
|
||||
do {
|
||||
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
|
||||
td_list);
|
||||
|
||||
@ -2803,7 +2804,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
|
||||
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||
skip_isoc_td(xhci, td, ep, status);
|
||||
continue;
|
||||
if (!list_empty(&ep_ring->td_list))
|
||||
continue;
|
||||
|
||||
xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n",
|
||||
slot_id, ep_index);
|
||||
ep->skip = false;
|
||||
td = NULL;
|
||||
goto check_endpoint_halted;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3941,10 +3949,6 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
|
||||
start_frame_id = (start_frame_id >> 3) & 0x7ff;
|
||||
end_frame_id = (end_frame_id >> 3) & 0x7ff;
|
||||
|
||||
xhci_dbg(xhci, "%s: index %d, reg 0x%x start_frame_id 0x%x, end_frame_id 0x%x, start_frame 0x%x\n",
|
||||
__func__, index, readl(&xhci->run_regs->microframe_index),
|
||||
start_frame_id, end_frame_id, start_frame);
|
||||
|
||||
if (start_frame_id < end_frame_id) {
|
||||
if (start_frame > end_frame_id ||
|
||||
start_frame < start_frame_id)
|
||||
|
@ -347,8 +347,8 @@ static int xhci_disable_interrupter(struct xhci_interrupter *ir)
|
||||
}
|
||||
|
||||
/* interrupt moderation interval imod_interval in nanoseconds */
|
||||
static int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
|
||||
u32 imod_interval)
|
||||
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
|
||||
u32 imod_interval)
|
||||
{
|
||||
u32 imod;
|
||||
|
||||
@ -4525,6 +4525,20 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
struct xhci_port *port;
|
||||
u32 capability;
|
||||
|
||||
/* Check if USB3 device at root port is tunneled over USB4 */
|
||||
if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
|
||||
port = xhci->usb3_rhub.ports[udev->portnum - 1];
|
||||
|
||||
udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
|
||||
if (udev->tunnel_mode == USB_LINK_UNKNOWN)
|
||||
dev_dbg(&udev->dev, "link tunnel state unknown\n");
|
||||
else if (udev->tunnel_mode == USB_LINK_TUNNELED)
|
||||
dev_dbg(&udev->dev, "tunneled over USB4 link\n");
|
||||
else if (udev->tunnel_mode == USB_LINK_NATIVE)
|
||||
dev_dbg(&udev->dev, "native USB 3.x link\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support)
|
||||
return 0;
|
||||
|
||||
|
@ -1498,15 +1498,10 @@ struct xhci_hcd {
|
||||
spinlock_t lock;
|
||||
|
||||
/* packed release number */
|
||||
u8 sbrn;
|
||||
u16 hci_version;
|
||||
u8 max_slots;
|
||||
u16 max_interrupters;
|
||||
u8 max_ports;
|
||||
u8 isoc_threshold;
|
||||
/* imod_interval in ns (I * 250ns) */
|
||||
u32 imod_interval;
|
||||
int event_ring_max;
|
||||
/* 4KB min, 128MB max */
|
||||
int page_size;
|
||||
/* Valid values are 12 to 20, inclusive */
|
||||
@ -1616,7 +1611,7 @@ struct xhci_hcd {
|
||||
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
|
||||
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
|
||||
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
|
||||
#define XHCI_RENESAS_FW_QUIRK BIT_ULL(36)
|
||||
/* Reserved. It was XHCI_RENESAS_FW_QUIRK */
|
||||
#define XHCI_SKIP_PHY_INIT BIT_ULL(37)
|
||||
#define XHCI_DISABLE_SPARSE BIT_ULL(38)
|
||||
#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39)
|
||||
@ -1628,6 +1623,7 @@ struct xhci_hcd {
|
||||
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
|
||||
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
|
||||
#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
|
||||
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
|
||||
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
@ -1831,7 +1827,8 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs);
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
||||
u32 imod_interval);
|
||||
void xhci_remove_secondary_interrupter(struct usb_hcd
|
||||
*hcd, struct xhci_interrupter *ir);
|
||||
|
||||
@ -1871,6 +1868,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *virt_dev,
|
||||
struct usb_device *hdev,
|
||||
struct usb_tt *tt, gfp_t mem_flags);
|
||||
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
|
||||
u32 imod_interval);
|
||||
|
||||
/* xHCI ring, segment, TRB, and TD functions */
|
||||
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
|
||||
@ -1904,10 +1903,6 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
enum xhci_ep_reset_type reset_type);
|
||||
int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
u32 slot_id);
|
||||
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
|
||||
unsigned int ep_index, unsigned int stream_id,
|
||||
struct xhci_td *td);
|
||||
void xhci_stop_endpoint_command_watchdog(struct timer_list *t);
|
||||
void xhci_handle_command_timeout(struct work_struct *work);
|
||||
|
||||
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
|
||||
@ -1929,7 +1924,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
|
||||
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
|
||||
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
|
||||
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
|
||||
|
||||
enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
|
||||
struct xhci_port *port);
|
||||
void xhci_hc_died(struct xhci_hcd *xhci);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -107,7 +107,12 @@ static void appledisplay_complete(struct urb *urb)
|
||||
case ACD_BTN_BRIGHT_UP:
|
||||
case ACD_BTN_BRIGHT_DOWN:
|
||||
pdata->button_pressed = 1;
|
||||
schedule_delayed_work(&pdata->work, 0);
|
||||
/*
|
||||
* there is a window during which no device
|
||||
* is registered
|
||||
*/
|
||||
if (pdata->bd )
|
||||
schedule_delayed_work(&pdata->work, 0);
|
||||
break;
|
||||
case ACD_BTN_NONE:
|
||||
default:
|
||||
@ -202,6 +207,7 @@ static int appledisplay_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct backlight_properties props;
|
||||
struct backlight_device *backlight;
|
||||
struct appledisplay *pdata;
|
||||
struct usb_device *udev = interface_to_usbdev(iface);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
@ -272,13 +278,14 @@ static int appledisplay_probe(struct usb_interface *iface,
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.max_brightness = 0xff;
|
||||
pdata->bd = backlight_device_register(bl_name, NULL, pdata,
|
||||
backlight = backlight_device_register(bl_name, NULL, pdata,
|
||||
&appledisplay_bl_data, &props);
|
||||
if (IS_ERR(pdata->bd)) {
|
||||
if (IS_ERR(backlight)) {
|
||||
dev_err(&iface->dev, "Backlight registration failed\n");
|
||||
retval = PTR_ERR(pdata->bd);
|
||||
retval = PTR_ERR(backlight);
|
||||
goto error;
|
||||
}
|
||||
pdata->bd = backlight;
|
||||
|
||||
/* Try to get brightness */
|
||||
brightness = appledisplay_bl_get_brightness(pdata->bd);
|
||||
|
@ -335,6 +335,7 @@ static const struct of_device_id brcmstb_usb_pinmap_of_match[] = {
|
||||
{ .compatible = "brcm,usb-pinmap" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcmstb_usb_pinmap_of_match);
|
||||
|
||||
static struct platform_driver brcmstb_usb_pinmap_driver = {
|
||||
.driver = {
|
||||
|
@ -88,6 +88,9 @@ static int vendor_command(struct cypress *dev, unsigned char request,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
address, data, iobuf, CYPRESS_MAX_REQSIZE,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
/* we must not process garbage */
|
||||
if (retval < 2)
|
||||
goto err_buf;
|
||||
|
||||
/* store returned data (more READs to be added) */
|
||||
switch (request) {
|
||||
@ -107,6 +110,7 @@ static int vendor_command(struct cypress *dev, unsigned char request,
|
||||
break;
|
||||
}
|
||||
|
||||
err_buf:
|
||||
kfree(iobuf);
|
||||
error:
|
||||
return retval;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
@ -29,6 +30,17 @@
|
||||
|
||||
#include "onboard_usb_dev.h"
|
||||
|
||||
/* USB5744 register offset and mask */
|
||||
#define USB5744_CMD_ATTACH 0xAA
|
||||
#define USB5744_CMD_ATTACH_LSB 0x56
|
||||
#define USB5744_CMD_CREG_ACCESS 0x99
|
||||
#define USB5744_CMD_CREG_ACCESS_LSB 0x37
|
||||
#define USB5744_CREG_MEM_ADDR 0x00
|
||||
#define USB5744_CREG_WRITE 0x00
|
||||
#define USB5744_CREG_RUNTIMEFLAGS2 0x41
|
||||
#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
|
||||
#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
|
||||
|
||||
static void onboard_dev_attach_usb_driver(struct work_struct *work);
|
||||
|
||||
static struct usb_device_driver onboard_dev_usbdev_driver;
|
||||
@ -98,6 +110,7 @@ static int onboard_dev_power_on(struct onboard_dev *onboard_dev)
|
||||
|
||||
fsleep(onboard_dev->pdata->reset_us);
|
||||
gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0);
|
||||
fsleep(onboard_dev->pdata->power_on_delay_us);
|
||||
|
||||
onboard_dev->is_powered_on = true;
|
||||
|
||||
@ -296,10 +309,50 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work)
|
||||
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
|
||||
}
|
||||
|
||||
static int onboard_dev_5744_i2c_init(struct i2c_client *client)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
|
||||
* and ready to respond to SMBus runtime commands.
|
||||
* The command writes 5 bytes to memory and single data byte in
|
||||
* configuration register.
|
||||
*/
|
||||
char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
|
||||
USB5744_CREG_WRITE, 1,
|
||||
USB5744_CREG_RUNTIMEFLAGS2,
|
||||
USB5744_CREG_RUNTIMEFLAGS2_LSB,
|
||||
USB5744_CREG_BYPASS_UDC_SUSPEND};
|
||||
|
||||
ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
|
||||
|
||||
ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
|
||||
USB5744_CMD_CREG_ACCESS_LSB);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
|
||||
|
||||
/* Send SMBus command to boot hub. */
|
||||
ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
|
||||
USB5744_CMD_ATTACH_LSB);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int onboard_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct onboard_dev *onboard_dev;
|
||||
struct device_node *i2c_node;
|
||||
int err;
|
||||
|
||||
onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL);
|
||||
@ -339,6 +392,27 @@ static int onboard_dev_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
i2c_node = of_parse_phandle(pdev->dev.of_node, "i2c-bus", 0);
|
||||
if (i2c_node) {
|
||||
struct i2c_client *client;
|
||||
|
||||
client = of_find_i2c_device_by_node(i2c_node);
|
||||
of_node_put(i2c_node);
|
||||
|
||||
if (!client) {
|
||||
err = -EPROBE_DEFER;
|
||||
goto err_power_off;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "usb424,2744") ||
|
||||
of_device_is_compatible(pdev->dev.of_node, "usb424,5744"))
|
||||
err = onboard_dev_5744_i2c_init(client);
|
||||
|
||||
put_device(&client->dev);
|
||||
if (err < 0)
|
||||
goto err_power_off;
|
||||
}
|
||||
|
||||
/*
|
||||
* The USB driver might have been detached from the USB devices by
|
||||
* onboard_dev_remove() (e.g. through an 'unbind' by userspace),
|
||||
@ -350,6 +424,10 @@ static int onboard_dev_probe(struct platform_device *pdev)
|
||||
schedule_work(&attach_usb_driver_work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_power_off:
|
||||
onboard_dev_power_off(onboard_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void onboard_dev_remove(struct platform_device *pdev)
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
struct onboard_dev_pdata {
|
||||
unsigned long reset_us; /* reset pulse width in us */
|
||||
unsigned long power_on_delay_us; /* power on delay in us */
|
||||
unsigned int num_supplies; /* number of supplies */
|
||||
const char * const supply_names[MAX_SUPPLIES];
|
||||
bool is_hub;
|
||||
@ -24,6 +25,7 @@ static const struct onboard_dev_pdata microchip_usb424_data = {
|
||||
|
||||
static const struct onboard_dev_pdata microchip_usb5744_data = {
|
||||
.reset_us = 0,
|
||||
.power_on_delay_us = 10000,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vdd", "vdd2" },
|
||||
.is_hub = true,
|
||||
|
@ -232,7 +232,7 @@ static void eud_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id eud_dt_match[] = {
|
||||
{ .compatible = "qcom,sc7280-eud" },
|
||||
{ .compatible = "qcom,eud" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, eud_dt_match);
|
||||
|
@ -404,7 +404,6 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
|
||||
struct usb_yurex *dev;
|
||||
int len = 0;
|
||||
char in_buffer[MAX_S64_STRLEN];
|
||||
unsigned long flags;
|
||||
|
||||
dev = file->private_data;
|
||||
|
||||
@ -419,9 +418,9 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
spin_lock_irq(&dev->lock);
|
||||
scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
spin_unlock_irq(&dev->lock);
|
||||
mutex_unlock(&dev->io_mutex);
|
||||
|
||||
return simple_read_from_buffer(buffer, count, ppos, in_buffer, len);
|
||||
@ -511,8 +510,11 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
|
||||
__func__, retval);
|
||||
goto error;
|
||||
}
|
||||
if (set && timeout)
|
||||
if (set && timeout) {
|
||||
spin_lock_irq(&dev->lock);
|
||||
dev->bbu = c2;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
}
|
||||
return timeout ? count : -EIO;
|
||||
|
||||
error:
|
||||
|
@ -416,10 +416,9 @@ static int mtk_musb_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create child devices at %p\n", np);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to create child devices at %p\n", np);
|
||||
|
||||
ret = mtk_musb_clks_get(glue);
|
||||
if (ret)
|
||||
@ -448,23 +447,19 @@ static int mtk_musb_probe(struct platform_device *pdev)
|
||||
glue->role = USB_ROLE_NONE;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Error 'dr_mode' property\n");
|
||||
return -EINVAL;
|
||||
return dev_err_probe(&pdev->dev, -EINVAL,
|
||||
"Error 'dr_mode' property\n");
|
||||
}
|
||||
|
||||
glue->phy = devm_of_phy_get_by_index(dev, np, 0);
|
||||
if (IS_ERR(glue->phy)) {
|
||||
dev_err(dev, "fail to getting phy %ld\n",
|
||||
PTR_ERR(glue->phy));
|
||||
return PTR_ERR(glue->phy);
|
||||
}
|
||||
if (IS_ERR(glue->phy))
|
||||
return dev_err_probe(dev, PTR_ERR(glue->phy),
|
||||
"fail to getting phy\n");
|
||||
|
||||
glue->usb_phy = usb_phy_generic_register();
|
||||
if (IS_ERR(glue->usb_phy)) {
|
||||
dev_err(dev, "fail to registering usb-phy %ld\n",
|
||||
PTR_ERR(glue->usb_phy));
|
||||
return PTR_ERR(glue->usb_phy);
|
||||
}
|
||||
if (IS_ERR(glue->usb_phy))
|
||||
return dev_err_probe(dev, PTR_ERR(glue->usb_phy),
|
||||
"fail to registering usb-phy\n");
|
||||
|
||||
glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(glue->xceiv)) {
|
||||
|
@ -49,30 +49,6 @@ static const struct musb_hdrc_config mpfs_musb_hdrc_config = {
|
||||
.ram_bits = MPFS_MUSB_RAM_BITS,
|
||||
};
|
||||
|
||||
static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
|
||||
{
|
||||
unsigned long flags;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct musb *musb = __hci;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
|
||||
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
|
||||
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
|
||||
|
||||
if (musb->int_usb || musb->int_tx || musb->int_rx) {
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
|
||||
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
|
||||
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
|
||||
ret = musb_interrupt(musb);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
u8 devctl;
|
||||
@ -111,6 +87,129 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
|
||||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
|
||||
#define POLL_SECONDS 2
|
||||
|
||||
static void otg_timer(struct timer_list *t)
|
||||
{
|
||||
struct musb *musb = from_timer(musb, t, dev_timer);
|
||||
void __iomem *mregs = musb->mregs;
|
||||
u8 devctl;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* We poll because PolarFire SoC won't expose several OTG-critical
|
||||
* status change events (from the transceiver) otherwise.
|
||||
*/
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE) {
|
||||
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
|
||||
MUSB_DEV_MODE(musb);
|
||||
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
|
||||
} else {
|
||||
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
if (devctl & MUSB_DEVCTL_VBUS) {
|
||||
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
|
||||
break;
|
||||
}
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
/*
|
||||
* There's no ID-changed IRQ, so we have no good way to tell
|
||||
* when to switch to the A-Default state machine (by setting
|
||||
* the DEVCTL.Session bit).
|
||||
*
|
||||
* Workaround: whenever we're in B_IDLE, try setting the
|
||||
* session flag every few seconds. If it works, ID was
|
||||
* grounded and we're now in the A-Default state machine.
|
||||
*
|
||||
* NOTE: setting the session flag is _supposed_ to trigger
|
||||
* SRP but clearly it doesn't.
|
||||
*/
|
||||
musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION);
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE)
|
||||
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
|
||||
else
|
||||
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
static unsigned long last_timer;
|
||||
|
||||
if (timeout == 0)
|
||||
timeout = jiffies + msecs_to_jiffies(3);
|
||||
|
||||
/* Never idle if active, or when VBUS timeout is not set as host */
|
||||
if (musb->is_active || (musb->a_wait_bcon == 0 &&
|
||||
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
|
||||
dev_dbg(musb->controller, "%s active, deleting timer\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
del_timer(&musb->dev_timer);
|
||||
last_timer = jiffies;
|
||||
return;
|
||||
}
|
||||
|
||||
if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) {
|
||||
dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n");
|
||||
return;
|
||||
}
|
||||
last_timer = timeout;
|
||||
|
||||
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
jiffies_to_msecs(timeout - jiffies));
|
||||
mod_timer(&musb->dev_timer, timeout);
|
||||
}
|
||||
|
||||
static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
|
||||
{
|
||||
unsigned long flags;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct musb *musb = __hci;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
|
||||
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
|
||||
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
|
||||
|
||||
if (musb->int_usb || musb->int_tx || musb->int_rx) {
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
|
||||
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
|
||||
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
|
||||
ret = musb_interrupt(musb);
|
||||
}
|
||||
|
||||
/* Poll for ID change */
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
|
||||
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mpfs_musb_init(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
@ -121,6 +220,8 @@ static int mpfs_musb_init(struct musb *musb)
|
||||
return PTR_ERR(musb->xceiv);
|
||||
}
|
||||
|
||||
timer_setup(&musb->dev_timer, otg_timer, 0);
|
||||
|
||||
musb->dyn_fifo = true;
|
||||
musb->isr = mpfs_musb_interrupt;
|
||||
|
||||
@ -129,13 +230,24 @@ static int mpfs_musb_init(struct musb *musb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_musb_exit(struct musb *musb)
|
||||
{
|
||||
del_timer_sync(&musb->dev_timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops mpfs_ops = {
|
||||
.quirks = MUSB_DMA_INVENTRA,
|
||||
.init = mpfs_musb_init,
|
||||
.exit = mpfs_musb_exit,
|
||||
.fifo_mode = 2,
|
||||
#ifdef CONFIG_USB_INVENTRA_DMA
|
||||
.dma_init = musbhs_dma_controller_create,
|
||||
.dma_exit = musbhs_dma_controller_destroy,
|
||||
#endif
|
||||
#ifndef CONFIG_USB_MUSB_HOST
|
||||
.try_idle = mpfs_musb_try_idle,
|
||||
#endif
|
||||
.set_vbus = mpfs_musb_set_vbus
|
||||
};
|
||||
|
@ -374,6 +374,7 @@ static const struct of_device_id gpio_vbus_of_match[] = {
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpio_vbus_of_match);
|
||||
|
||||
static struct platform_driver gpio_vbus_driver = {
|
||||
.driver = {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define DRIVER_NAME "mxs_phy"
|
||||
|
||||
@ -70,6 +71,9 @@
|
||||
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
|
||||
|
||||
/* Anatop Registers */
|
||||
#define ANADIG_REG_1P1_SET 0x114
|
||||
#define ANADIG_REG_1P1_CLR 0x118
|
||||
|
||||
#define ANADIG_ANA_MISC0 0x150
|
||||
#define ANADIG_ANA_MISC0_SET 0x154
|
||||
#define ANADIG_ANA_MISC0_CLR 0x158
|
||||
@ -117,6 +121,14 @@
|
||||
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
|
||||
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
|
||||
|
||||
/* System Integration Module (SIM) Registers */
|
||||
#define SIM_GPR1 0x30
|
||||
|
||||
#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
|
||||
|
||||
#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
|
||||
#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
|
||||
|
||||
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
|
||||
|
||||
/* Do disconnection between PHY and controller without vbus */
|
||||
@ -149,6 +161,15 @@
|
||||
#define MXS_PHY_TX_D_CAL_MIN 79
|
||||
#define MXS_PHY_TX_D_CAL_MAX 119
|
||||
|
||||
/*
|
||||
* At imx6q/6sl/6sx, the PHY2's clock is controlled by hardware directly,
|
||||
* eg, according to PHY's suspend status. In these PHYs, we only need to
|
||||
* open the clock at the initialization and close it at its shutdown routine.
|
||||
* These PHYs can send resume signal without software interfere if not
|
||||
* gate clock.
|
||||
*/
|
||||
#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
|
||||
|
||||
struct mxs_phy_data {
|
||||
unsigned int flags;
|
||||
};
|
||||
@ -160,12 +181,14 @@ static const struct mxs_phy_data imx23_phy_data = {
|
||||
static const struct mxs_phy_data imx6q_phy_data = {
|
||||
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
|
||||
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
|
||||
MXS_PHY_NEED_IP_FIX,
|
||||
MXS_PHY_NEED_IP_FIX |
|
||||
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx6sl_phy_data = {
|
||||
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
|
||||
MXS_PHY_NEED_IP_FIX,
|
||||
MXS_PHY_NEED_IP_FIX |
|
||||
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data vf610_phy_data = {
|
||||
@ -174,11 +197,13 @@ static const struct mxs_phy_data vf610_phy_data = {
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx6sx_phy_data = {
|
||||
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
|
||||
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
|
||||
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx6ul_phy_data = {
|
||||
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
|
||||
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
|
||||
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx7ulp_phy_data = {
|
||||
@ -201,9 +226,11 @@ struct mxs_phy {
|
||||
struct clk *clk;
|
||||
const struct mxs_phy_data *data;
|
||||
struct regmap *regmap_anatop;
|
||||
struct regmap *regmap_sim;
|
||||
int port_id;
|
||||
u32 tx_reg_set;
|
||||
u32 tx_reg_mask;
|
||||
struct regulator *phy_3p0;
|
||||
};
|
||||
|
||||
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
|
||||
@ -221,6 +248,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
|
||||
return mxs_phy->data == &imx7ulp_phy_data;
|
||||
}
|
||||
|
||||
static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
|
||||
{
|
||||
return mxs_phy->data == &imx6ul_phy_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* PHY needs some 32K cycles to switch from 32K clock to
|
||||
* bus (such as AHB/AXI, etc) clock.
|
||||
@ -288,6 +320,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
|
||||
if (ret)
|
||||
goto disable_pll;
|
||||
|
||||
if (mxs_phy->phy_3p0) {
|
||||
ret = regulator_enable(mxs_phy->phy_3p0);
|
||||
if (ret) {
|
||||
dev_err(mxs_phy->phy.dev,
|
||||
"Failed to enable 3p0 regulator, ret=%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Power up the PHY */
|
||||
writel(0, base + HW_USBPHY_PWD);
|
||||
|
||||
@ -448,6 +490,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
|
||||
if (is_imx7ulp_phy(mxs_phy))
|
||||
mxs_phy_pll_enable(phy->io_priv, false);
|
||||
|
||||
if (mxs_phy->phy_3p0)
|
||||
regulator_disable(mxs_phy->phy_3p0);
|
||||
|
||||
clk_disable_unprepare(mxs_phy->clk);
|
||||
}
|
||||
|
||||
@ -503,12 +548,19 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
|
||||
}
|
||||
writel(BM_USBPHY_CTRL_CLKGATE,
|
||||
x->io_priv + HW_USBPHY_CTRL_SET);
|
||||
clk_disable_unprepare(mxs_phy->clk);
|
||||
if (!(mxs_phy->port_id == 1 &&
|
||||
(mxs_phy->data->flags &
|
||||
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)))
|
||||
clk_disable_unprepare(mxs_phy->clk);
|
||||
} else {
|
||||
mxs_phy_clock_switch_delay();
|
||||
ret = clk_prepare_enable(mxs_phy->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!(mxs_phy->port_id == 1 &&
|
||||
(mxs_phy->data->flags &
|
||||
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK))) {
|
||||
ret = clk_prepare_enable(mxs_phy->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
writel(BM_USBPHY_CTRL_CLKGATE,
|
||||
x->io_priv + HW_USBPHY_CTRL_CLR);
|
||||
writel(0, x->io_priv + HW_USBPHY_PWD);
|
||||
@ -738,6 +790,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently, only imx7ulp has SIM module */
|
||||
if (of_get_property(np, "nxp,sim", NULL)) {
|
||||
mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
|
||||
(np, "nxp,sim");
|
||||
if (IS_ERR(mxs_phy->regmap_sim)) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"failed to find regmap for sim\n");
|
||||
return PTR_ERR(mxs_phy->regmap_sim);
|
||||
}
|
||||
}
|
||||
|
||||
/* Precompute which bits of the TX register are to be updated, if any */
|
||||
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
|
||||
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
|
||||
@ -789,6 +852,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
||||
mxs_phy->clk = clk;
|
||||
mxs_phy->data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
|
||||
if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV)
|
||||
/* not exist */
|
||||
mxs_phy->phy_3p0 = NULL;
|
||||
else if (IS_ERR(mxs_phy->phy_3p0))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(mxs_phy->phy_3p0),
|
||||
"Getting regulator error\n");
|
||||
|
||||
if (mxs_phy->phy_3p0)
|
||||
regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
|
||||
|
||||
platform_set_drvdata(pdev, mxs_phy);
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
@ -804,28 +878,58 @@ static void mxs_phy_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
|
||||
{
|
||||
u32 mask = USB_PHY_VLLS_WAKEUP_EN;
|
||||
|
||||
/* If the SoCs don't have SIM, quit */
|
||||
if (!mxs_phy->regmap_sim)
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
|
||||
udelay(500);
|
||||
} else {
|
||||
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
|
||||
{
|
||||
unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
|
||||
unsigned int reg;
|
||||
u32 value;
|
||||
|
||||
/* If the SoCs don't have anatop, quit */
|
||||
if (!mxs_phy->regmap_anatop)
|
||||
return;
|
||||
|
||||
if (is_imx6q_phy(mxs_phy))
|
||||
if (is_imx6q_phy(mxs_phy)) {
|
||||
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
|
||||
regmap_write(mxs_phy->regmap_anatop, reg,
|
||||
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
|
||||
else if (is_imx6sl_phy(mxs_phy))
|
||||
} else if (is_imx6sl_phy(mxs_phy)) {
|
||||
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
|
||||
regmap_write(mxs_phy->regmap_anatop,
|
||||
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
|
||||
} else if (is_imx6ul_phy(mxs_phy)) {
|
||||
reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
|
||||
value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
|
||||
BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
|
||||
if (mxs_phy_get_vbus_status(mxs_phy) && on)
|
||||
regmap_write(mxs_phy->regmap_anatop, reg, value);
|
||||
else if (!on)
|
||||
regmap_write(mxs_phy->regmap_anatop, reg, value);
|
||||
}
|
||||
}
|
||||
|
||||
static int mxs_phy_system_suspend(struct device *dev)
|
||||
{
|
||||
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
if (device_may_wakeup(dev)) {
|
||||
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
|
||||
mxs_phy_wakeup_enable(mxs_phy, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -834,8 +938,10 @@ static int mxs_phy_system_resume(struct device *dev)
|
||||
{
|
||||
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
if (device_may_wakeup(dev)) {
|
||||
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
|
||||
mxs_phy_wakeup_enable(mxs_phy, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/usb/role.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
@ -21,6 +22,7 @@ static const struct class role_class = {
|
||||
|
||||
struct usb_role_switch {
|
||||
struct device dev;
|
||||
struct lock_class_key key;
|
||||
struct mutex lock; /* device lock*/
|
||||
struct module *module; /* the module this device depends on */
|
||||
enum usb_role role;
|
||||
@ -326,6 +328,8 @@ static void usb_role_switch_release(struct device *dev)
|
||||
{
|
||||
struct usb_role_switch *sw = to_role_switch(dev);
|
||||
|
||||
mutex_destroy(&sw->lock);
|
||||
lockdep_unregister_key(&sw->key);
|
||||
kfree(sw);
|
||||
}
|
||||
|
||||
@ -364,7 +368,8 @@ usb_role_switch_register(struct device *parent,
|
||||
if (!sw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&sw->lock);
|
||||
lockdep_register_key(&sw->key);
|
||||
mutex_init_with_key(&sw->lock, &sw->key);
|
||||
|
||||
sw->allow_userspace_control = desc->allow_userspace_control;
|
||||
sw->usb2_port = desc->usb2_port;
|
||||
|
@ -138,7 +138,6 @@ static void aircable_process_read_urb(struct urb *urb)
|
||||
|
||||
static struct usb_serial_driver aircable_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "aircable",
|
||||
},
|
||||
.id_table = id_table,
|
||||
|
@ -599,7 +599,6 @@ static void ark3116_process_read_urb(struct urb *urb)
|
||||
|
||||
static struct usb_serial_driver ark3116_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ark3116",
|
||||
},
|
||||
.id_table = id_table,
|
||||
|
@ -66,7 +66,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
|
||||
/* All of the device info needed for the serial converters */
|
||||
static struct usb_serial_driver belkin_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "belkin",
|
||||
},
|
||||
.description = "Belkin / Peracom / GoHubs USB Serial Adapter",
|
||||
|
@ -837,7 +837,6 @@ static int ch341_reset_resume(struct usb_serial *serial)
|
||||
|
||||
static struct usb_serial_driver ch341_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ch341-uart",
|
||||
},
|
||||
.id_table = id_table,
|
||||
|
@ -299,7 +299,6 @@ struct cp210x_port_private {
|
||||
|
||||
static struct usb_serial_driver cp210x_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cp210x",
|
||||
},
|
||||
.id_table = id_table,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user