mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
USB/Thunderbolt driver fixes for 6.3-rc4
Here are a small set of USB and Thunderbolt driver fixes for reported problems and a documentation update, for 6.3-rc4. Included in here are: - documentation update for uvc gadget driver - small thunderbolt driver fixes - cdns3 driver fixes - dwc3 driver fixes - dwc2 driver fixes - chipidea driver fixes - typec driver fixes - onboard_usb_hub device id updates - quirk updates All of these have been in linux-next with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZCBKPg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylpVwCgyqeDRhlJBlVnqhB1yAT+JTRW5oEAoL1HrTuf 6RJ3n8NbIXKRSZ49/b43 =4u7n -----END PGP SIGNATURE----- Merge tag 'usb-6.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB / Thunderbolt driver fixes from Greg KH: "Here are a small set of USB and Thunderbolt driver fixes for reported problems and a documentation update, for 6.3-rc4. Included in here are: - documentation update for uvc gadget driver - small thunderbolt driver fixes - cdns3 driver fixes - dwc3 driver fixes - dwc2 driver fixes - chipidea driver fixes - typec driver fixes - onboard_usb_hub device id updates - quirk updates All of these have been in linux-next with no reported problems" * tag 'usb-6.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (30 commits) usb: dwc2: fix a race, don't power off/on phy for dual-role mode usb: dwc2: fix a devres leak in hw_enable upon suspend resume usb: chipidea: core: fix possible concurrent when switch role usb: chipdea: core: fix return -EINVAL if request role is the same with current role thunderbolt: Rename shadowed variables bit to interrupt_bit and auto_clear_bit thunderbolt: Disable interrupt auto clear for rings thunderbolt: Use const qualifier for `ring_interrupt_index` usb: gadget: Use correct endianness of the wLength field for WebUSB uas: Add US_FL_NO_REPORT_OPCODES for JMicron JMS583Gen 2 usb: cdnsp: changes PCI Device ID to fix conflict with CNDS3 driver usb: cdns3: Fix issue with using incorrect PCI device function usb: cdnsp: Fixes issue with redundant Status Stage MAINTAINERS: make me a reviewer of USB/IP thunderbolt: Use scale field when allocating USB3 bandwidth thunderbolt: Limit USB3 bandwidth of certain Intel USB4 host routers thunderbolt: Call tb_check_quirks() after initializing adapters thunderbolt: Add missing UNSET_INBOUND_SBTX for retimer access thunderbolt: Fix memory leak in margining usb: dwc2: drd: fix inconsistent mode if role-switch-default-mode="host" docs: usb: Add documentation for the UVC Gadget ...
This commit is contained in:
commit
0ec57cfa72
352
Documentation/usb/gadget_uvc.rst
Normal file
352
Documentation/usb/gadget_uvc.rst
Normal file
@ -0,0 +1,352 @@
|
||||
=======================
|
||||
Linux UVC Gadget Driver
|
||||
=======================
|
||||
|
||||
Overview
|
||||
--------
|
||||
The UVC Gadget driver is a driver for hardware on the *device* side of a USB
|
||||
connection. It is intended to run on a Linux system that has USB device-side
|
||||
hardware such as boards with an OTG port.
|
||||
|
||||
On the device system, once the driver is bound it appears as a V4L2 device with
|
||||
the output capability.
|
||||
|
||||
On the host side (once connected via USB cable), a device running the UVC Gadget
|
||||
driver *and controlled by an appropriate userspace program* should appear as a UVC
|
||||
specification compliant camera, and function appropriately with any program
|
||||
designed to handle them. The userspace program running on the device system can
|
||||
queue image buffers from a variety of sources to be transmitted via the USB
|
||||
connection. Typically this would mean forwarding the buffers from a camera sensor
|
||||
peripheral, but the source of the buffer is entirely dependent on the userspace
|
||||
companion program.
|
||||
|
||||
Configuring the device kernel
|
||||
-----------------------------
|
||||
The Kconfig options USB_CONFIGFS, USB_LIBCOMPOSITE, USB_CONFIGFS_F_UVC and
|
||||
USB_F_UVC must be selected to enable support for the UVC gadget.
|
||||
|
||||
Configuring the gadget through configfs
|
||||
---------------------------------------
|
||||
The UVC Gadget expects to be configured through configfs using the UVC function.
|
||||
This allows a significant degree of flexibility, as many of a UVC device's
|
||||
settings can be controlled this way.
|
||||
|
||||
Not all of the available attributes are described here. For a complete enumeration
|
||||
see Documentation/ABI/testing/configfs-usb-gadget-uvc
|
||||
|
||||
Assumptions
|
||||
~~~~~~~~~~~
|
||||
This section assumes that you have mounted configfs at `/sys/kernel/config` and
|
||||
created a gadget as `/sys/kernel/config/usb_gadget/g1`.
|
||||
|
||||
The UVC Function
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The first step is to create the UVC function:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# These variables will be assumed throughout the rest of the document
|
||||
CONFIGFS="/sys/kernel/config"
|
||||
GADGET="$CONFIGFS/usb_gadget/g1"
|
||||
FUNCTION="$GADGET/functions/uvc.0"
|
||||
|
||||
mkdir -p $FUNCTION
|
||||
|
||||
Formats and Frames
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You must configure the gadget by telling it which formats you support, as well
|
||||
as the frame sizes and frame intervals that are supported for each format. In
|
||||
the current implementation there is no way for the gadget to refuse to set a
|
||||
format that the host instructs it to set, so it is important that this step is
|
||||
completed *accurately* to ensure that the host never asks for a format that
|
||||
can't be provided.
|
||||
|
||||
Formats are created under the streaming/uncompressed and streaming/mjpeg configfs
|
||||
groups, with the framesizes created under the formats in the following
|
||||
structure:
|
||||
|
||||
::
|
||||
|
||||
uvc.0 +
|
||||
|
|
||||
+ streaming +
|
||||
|
|
||||
+ mjpeg +
|
||||
| |
|
||||
| + mjpeg +
|
||||
| |
|
||||
| + 720p
|
||||
| |
|
||||
| + 1080p
|
||||
|
|
||||
+ uncompressed +
|
||||
|
|
||||
+ yuyv +
|
||||
|
|
||||
+ 720p
|
||||
|
|
||||
+ 1080p
|
||||
|
||||
Each frame can then be configured with a width and height, plus the maximum
|
||||
buffer size required to store a single frame, and finally with the supported
|
||||
frame intervals for that format and framesize. Width and height are enumerated in
|
||||
units of pixels, frame interval in units of 100ns. To create the structure
|
||||
above with 2, 15 and 100 fps frameintervals for each framesize for example you
|
||||
might do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
create_frame() {
|
||||
# Example usage:
|
||||
# create_frame <width> <height> <group> <format name>
|
||||
|
||||
WIDTH=$1
|
||||
HEIGHT=$2
|
||||
FORMAT=$3
|
||||
NAME=$4
|
||||
|
||||
wdir=$FUNCTION/streaming/$FORMAT/$NAME/${HEIGHT}p
|
||||
|
||||
mkdir -p $wdir
|
||||
echo $WIDTH > $wdir/wWidth
|
||||
echo $HEIGHT > $wdir/wHeight
|
||||
echo $(( $WIDTH * $HEIGHT * 2 )) > $wdir/dwMaxVideoFrameBufferSize
|
||||
cat <<EOF > $wdir/dwFrameInterval
|
||||
666666
|
||||
100000
|
||||
5000000
|
||||
EOF
|
||||
}
|
||||
|
||||
create_frame 1280 720 mjpeg mjpeg
|
||||
create_frame 1920 1080 mjpeg mjpeg
|
||||
create_frame 1280 720 uncompressed yuyv
|
||||
create_frame 1920 1080 uncompressed yuyv
|
||||
|
||||
The only uncompressed format currently supported is YUYV, which is detailed at
|
||||
Documentation/userspace-api/media/v4l/pixfmt-packed.yuv.rst.
|
||||
|
||||
Color Matching Descriptors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
It's possible to specify some colometry information for each format you create.
|
||||
This step is optional, and default information will be included if this step is
|
||||
skipped; those default values follow those defined in the Color Matching Descriptor
|
||||
section of the UVC specification.
|
||||
|
||||
To create a Color Matching Descriptor, create a configfs item and set its three
|
||||
attributes to your desired settings and then link to it from the format you wish
|
||||
it to be associated with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Create a new Color Matching Descriptor
|
||||
|
||||
mkdir $FUNCTION/streaming/color_matching/yuyv
|
||||
pushd $FUNCTION/streaming/color_matching/yuyv
|
||||
|
||||
echo 1 > bColorPrimaries
|
||||
echo 1 > bTransferCharacteristics
|
||||
echo 4 > bMatrixCoefficients
|
||||
|
||||
popd
|
||||
|
||||
# Create a symlink to the Color Matching Descriptor from the format's config item
|
||||
ln -s $FUNCTION/streaming/color_matching/yuyv $FUNCTION/streaming/uncompressed/yuyv
|
||||
|
||||
For details about the valid values, consult the UVC specification. Note that a
|
||||
default color matching descriptor exists and is used by any format which does
|
||||
not have a link to a different Color Matching Descriptor. It's possible to
|
||||
change the attribute settings for the default descriptor, so bear in mind that if
|
||||
you do that you are altering the defaults for any format that does not link to
|
||||
a different one.
|
||||
|
||||
|
||||
Header linking
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The UVC specification requires that Format and Frame descriptors be preceded by
|
||||
Headers detailing things such as the number and cumulative size of the different
|
||||
Format descriptors that follow. This and similar operations are acheived in
|
||||
configfs by linking between the configfs item representing the header and the
|
||||
config items representing those other descriptors, in this manner:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir $FUNCTION/streaming/header/h
|
||||
|
||||
# This section links the format descriptors and their associated frames
|
||||
# to the header
|
||||
cd $FUNCTION/streaming/header/h
|
||||
ln -s ../../uncompressed/yuyv
|
||||
ln -s ../../mjpeg/mjpeg
|
||||
|
||||
# This section ensures that the header will be transmitted for each
|
||||
# speed's set of descriptors. If support for a particular speed is not
|
||||
# needed then it can be skipped here.
|
||||
cd ../../class/fs
|
||||
ln -s ../../header/h
|
||||
cd ../../class/hs
|
||||
ln -s ../../header/h
|
||||
cd ../../class/ss
|
||||
ln -s ../../header/h
|
||||
cd ../../../control
|
||||
mkdir header/h
|
||||
ln -s header/h class/fs
|
||||
ln -s header/h class/ss
|
||||
|
||||
|
||||
Extension Unit Support
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A UVC Extension Unit (XU) basically provides a distinct unit to which control set
|
||||
and get requests can be addressed. The meaning of those control requests is
|
||||
entirely implementation dependent, but may be used to control settings outside
|
||||
of the UVC specification (for example enabling or disabling video effects). An
|
||||
XU can be inserted into the UVC unit chain or left free-hanging.
|
||||
|
||||
Configuring an extension unit involves creating an entry in the appropriate
|
||||
directory and setting its attributes appropriately, like so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir $FUNCTION/control/extensions/xu.0
|
||||
pushd $FUNCTION/control/extensions/xu.0
|
||||
|
||||
# Set the bUnitID of the Processing Unit as the source for this
|
||||
# Extension Unit
|
||||
echo 2 > baSourceID
|
||||
|
||||
# Set this XU as the source of the default output terminal. This inserts
|
||||
# the XU into the UVC chain between the PU and OT such that the final
|
||||
# chain is IT > PU > XU.0 > OT
|
||||
cat bUnitID > ../../terminal/output/default/baSourceID
|
||||
|
||||
# Flag some controls as being available for use. The bmControl field is
|
||||
# a bitmap with each bit denoting the availability of a particular
|
||||
# control. For example to flag the 0th, 2nd and 3rd controls available:
|
||||
echo 0x0d > bmControls
|
||||
|
||||
# Set the GUID; this is a vendor-specific code identifying the XU.
|
||||
echo -e -n "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" > guidExtensionCode
|
||||
|
||||
popd
|
||||
|
||||
The bmControls attribute and the baSourceID attribute are multi-value attributes.
|
||||
This means that you may write multiple newline separated values to them. For
|
||||
example to flag the 1st, 2nd, 9th and 10th controls as being available you would
|
||||
need to write two values to bmControls, like so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cat << EOF > bmControls
|
||||
0x03
|
||||
0x03
|
||||
EOF
|
||||
|
||||
The multi-value nature of the baSourceID attribute belies the fact that XUs can
|
||||
be multiple-input, though note that this currently has no significant effect.
|
||||
|
||||
The bControlSize attribute reflects the size of the bmControls attribute, and
|
||||
similarly bNrInPins reflects the size of the baSourceID attributes. Both
|
||||
attributes are automatically increased / decreased as you set bmControls and
|
||||
baSourceID. It is also possible to manually increase or decrease bControlSize
|
||||
which has the effect of truncating entries to the new size, or padding entries
|
||||
out with 0x00, for example:
|
||||
|
||||
::
|
||||
|
||||
$ cat bmControls
|
||||
0x03
|
||||
0x05
|
||||
|
||||
$ cat bControlSize
|
||||
2
|
||||
|
||||
$ echo 1 > bControlSize
|
||||
$ cat bmControls
|
||||
0x03
|
||||
|
||||
$ echo 2 > bControlSize
|
||||
$ cat bmControls
|
||||
0x03
|
||||
0x00
|
||||
|
||||
bNrInPins and baSourceID function in the same way.
|
||||
|
||||
Custom Strings Support
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
String descriptors that provide a textual description for various parts of a
|
||||
USB device can be defined in the usual place within USB configfs, and may then
|
||||
be linked to from the UVC function root or from Extension Unit directories to
|
||||
assign those strings as descriptors:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Create a string descriptor in us-EN and link to it from the function
|
||||
# root. The name of the link is significant here, as it declares this
|
||||
# descriptor to be intended for the Interface Association Descriptor.
|
||||
# Other significant link names at function root are vs0_desc and vs1_desc
|
||||
# For the VideoStreaming Interface 0/1 Descriptors.
|
||||
|
||||
mkdir -p $GADGET/strings/0x409/iad_desc
|
||||
echo -n "Interface Associaton Descriptor" > $GADGET/strings/0x409/iad_desc/s
|
||||
ln -s $GADGET/strings/0x409/iad_desc $FUNCTION/iad_desc
|
||||
|
||||
# Because the link to a String Descriptor from an Extension Unit clearly
|
||||
# associates the two, the name of this link is not significant and may
|
||||
# be set freely.
|
||||
|
||||
mkdir -p $GADGET/strings/0x409/xu.0
|
||||
echo -n "A Very Useful Extension Unit" > $GADGET/strings/0x409/xu.0/s
|
||||
ln -s $GADGET/strings/0x409/xu.0 $FUNCTION/control/extensions/xu.0
|
||||
|
||||
The interrupt endpoint
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The VideoControl interface has an optional interrupt endpoint which is by default
|
||||
disabled. This is intended to support delayed response control set requests for
|
||||
UVC (which should respond through the interrupt endpoint rather than tying up
|
||||
endpoint 0). At present support for sending data through this endpoint is missing
|
||||
and so it is left disabled to avoid confusion. If you wish to enable it you can
|
||||
do so through the configfs attribute:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
echo 1 > $FUNCTION/control/enable_interrupt_ep
|
||||
|
||||
Bandwidth configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are three attributes which control the bandwidth of the USB connection.
|
||||
These live in the function root and can be set within limits:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# streaming_interval sets bInterval. Values range from 1..255
|
||||
echo 1 > $FUNCTION/streaming_interval
|
||||
|
||||
# streaming_maxpacket sets wMaxPacketSize. Valid values are 1024/2048/3072
|
||||
echo 3072 > $FUNCTION/streaming_maxpacket
|
||||
|
||||
# streaming_maxburst sets bMaxBurst. Valid values are 1..15
|
||||
echo 1 > $FUNCTION/streaming_maxburst
|
||||
|
||||
|
||||
The values passed here will be clamped to valid values according to the UVC
|
||||
specification (which depend on the speed of the USB connection). To understand
|
||||
how the settings influence bandwidth you should consult the UVC specifications,
|
||||
but a rule of thumb is that increasing the streaming_maxpacket setting will
|
||||
improve bandwidth (and thus the maximum possible framerate), whilst the same is
|
||||
true for streaming_maxburst provided the USB connection is running at SuperSpeed.
|
||||
Increasing streaming_interval will reduce bandwidth and framerate.
|
||||
|
||||
The userspace application
|
||||
-------------------------
|
||||
By itself, the UVC Gadget driver cannot do anything particularly interesting. It
|
||||
must be paired with a userspace program that responds to UVC control requests and
|
||||
fills buffers to be queued to the V4L2 device that the driver creates. How those
|
||||
things are achieved is implementation dependent and beyond the scope of this
|
||||
document, but a reference application can be found at https://gitlab.freedesktop.org/camera/uvc-gadget
|
@ -16,6 +16,7 @@ USB support
|
||||
gadget_multi
|
||||
gadget_printer
|
||||
gadget_serial
|
||||
gadget_uvc
|
||||
gadget-testing
|
||||
iuu_phoenix
|
||||
mass-storage
|
||||
|
@ -21643,6 +21643,7 @@ USB OVER IP DRIVER
|
||||
M: Valentina Manea <valentina.manea.m@gmail.com>
|
||||
M: Shuah Khan <shuah@kernel.org>
|
||||
M: Shuah Khan <skhan@linuxfoundation.org>
|
||||
R: Hongren Zheng <i@zenithal.me>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/usb/usbip_protocol.rst
|
||||
|
@ -942,7 +942,8 @@ static void margining_port_remove(struct tb_port *port)
|
||||
|
||||
snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
|
||||
parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
|
||||
debugfs_remove_recursive(debugfs_lookup("margining", parent));
|
||||
if (parent)
|
||||
debugfs_remove_recursive(debugfs_lookup("margining", parent));
|
||||
|
||||
kfree(port->usb4->margining);
|
||||
port->usb4->margining = NULL;
|
||||
@ -967,19 +968,18 @@ static void margining_switch_init(struct tb_switch *sw)
|
||||
|
||||
static void margining_switch_remove(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *upstream, *downstream;
|
||||
struct tb_switch *parent_sw;
|
||||
struct tb_port *downstream;
|
||||
u64 route = tb_route(sw);
|
||||
|
||||
if (!route)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Upstream is removed with the router itself but we need to
|
||||
* remove the downstream port margining directory.
|
||||
*/
|
||||
upstream = tb_upstream_port(sw);
|
||||
parent_sw = tb_switch_parent(sw);
|
||||
downstream = tb_port_at(route, parent_sw);
|
||||
|
||||
margining_port_remove(upstream);
|
||||
margining_port_remove(downstream);
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@
|
||||
#define QUIRK_AUTO_CLEAR_INT BIT(0)
|
||||
#define QUIRK_E2E BIT(1)
|
||||
|
||||
static int ring_interrupt_index(struct tb_ring *ring)
|
||||
static int ring_interrupt_index(const struct tb_ring *ring)
|
||||
{
|
||||
int bit = ring->hop;
|
||||
if (!ring->is_tx)
|
||||
@ -63,13 +63,14 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
||||
{
|
||||
int reg = REG_RING_INTERRUPT_BASE +
|
||||
ring_interrupt_index(ring) / 32 * 4;
|
||||
int bit = ring_interrupt_index(ring) & 31;
|
||||
int mask = 1 << bit;
|
||||
int interrupt_bit = ring_interrupt_index(ring) & 31;
|
||||
int mask = 1 << interrupt_bit;
|
||||
u32 old, new;
|
||||
|
||||
if (ring->irq > 0) {
|
||||
u32 step, shift, ivr, misc;
|
||||
void __iomem *ivr_base;
|
||||
int auto_clear_bit;
|
||||
int index;
|
||||
|
||||
if (ring->is_tx)
|
||||
@ -77,18 +78,25 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
||||
else
|
||||
index = ring->hop + ring->nhi->hop_count;
|
||||
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
|
||||
/*
|
||||
* Ask the hardware to clear interrupt status
|
||||
* bits automatically since we already know
|
||||
* which interrupt was triggered.
|
||||
*/
|
||||
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
|
||||
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
|
||||
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Intel routers support a bit that isn't part of
|
||||
* the USB4 spec to ask the hardware to clear
|
||||
* interrupt status bits automatically since
|
||||
* we already know which interrupt was triggered.
|
||||
*
|
||||
* Other routers explicitly disable auto-clear
|
||||
* to prevent conditions that may occur where two
|
||||
* MSIX interrupts are simultaneously active and
|
||||
* reading the register clears both of them.
|
||||
*/
|
||||
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
|
||||
auto_clear_bit = REG_DMA_MISC_INT_AUTO_CLEAR;
|
||||
else
|
||||
auto_clear_bit = REG_DMA_MISC_DISABLE_AUTO_CLEAR;
|
||||
if (!(misc & auto_clear_bit))
|
||||
iowrite32(misc | auto_clear_bit,
|
||||
ring->nhi->iobase + REG_DMA_MISC);
|
||||
|
||||
ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
|
||||
step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
|
||||
@ -108,7 +116,7 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
||||
|
||||
dev_dbg(&ring->nhi->pdev->dev,
|
||||
"%s interrupt at register %#x bit %d (%#x -> %#x)\n",
|
||||
active ? "enabling" : "disabling", reg, bit, old, new);
|
||||
active ? "enabling" : "disabling", reg, interrupt_bit, old, new);
|
||||
|
||||
if (new == old)
|
||||
dev_WARN(&ring->nhi->pdev->dev,
|
||||
@ -393,14 +401,17 @@ EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
|
||||
|
||||
static void ring_clear_msix(const struct tb_ring *ring)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
|
||||
return;
|
||||
|
||||
bit = ring_interrupt_index(ring) & 31;
|
||||
if (ring->is_tx)
|
||||
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE);
|
||||
iowrite32(BIT(bit), ring->nhi->iobase + REG_RING_INT_CLEAR);
|
||||
else
|
||||
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE +
|
||||
4 * (ring->nhi->hop_count / 32));
|
||||
iowrite32(BIT(bit), ring->nhi->iobase + REG_RING_INT_CLEAR +
|
||||
4 * (ring->nhi->hop_count / 32));
|
||||
}
|
||||
|
||||
static irqreturn_t ring_msix(int irq, void *data)
|
||||
|
@ -77,12 +77,13 @@ struct ring_desc {
|
||||
|
||||
/*
|
||||
* three bitfields: tx, rx, rx overflow
|
||||
* Every bitfield contains one bit for every hop (REG_HOP_COUNT). Registers are
|
||||
* cleared on read. New interrupts are fired only after ALL registers have been
|
||||
* Every bitfield contains one bit for every hop (REG_HOP_COUNT).
|
||||
* New interrupts are fired only after ALL registers have been
|
||||
* read (even those containing only disabled rings).
|
||||
*/
|
||||
#define REG_RING_NOTIFY_BASE 0x37800
|
||||
#define RING_NOTIFY_REG_COUNT(nhi) ((31 + 3 * nhi->hop_count) / 32)
|
||||
#define REG_RING_INT_CLEAR 0x37808
|
||||
|
||||
/*
|
||||
* two bitfields: rx, tx
|
||||
@ -105,6 +106,7 @@ struct ring_desc {
|
||||
|
||||
#define REG_DMA_MISC 0x39864
|
||||
#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
|
||||
#define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17)
|
||||
|
||||
#define REG_INMAIL_DATA 0x39900
|
||||
|
||||
|
@ -20,6 +20,25 @@ static void quirk_dp_credit_allocation(struct tb_switch *sw)
|
||||
}
|
||||
}
|
||||
|
||||
static void quirk_clx_disable(struct tb_switch *sw)
|
||||
{
|
||||
sw->quirks |= QUIRK_NO_CLX;
|
||||
tb_sw_dbg(sw, "disabling CL states\n");
|
||||
}
|
||||
|
||||
static void quirk_usb3_maximum_bandwidth(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *port;
|
||||
|
||||
tb_switch_for_each_port(sw, port) {
|
||||
if (!tb_port_is_usb3_down(port))
|
||||
continue;
|
||||
port->max_bw = 16376;
|
||||
tb_port_dbg(port, "USB3 maximum bandwidth limited to %u Mb/s\n",
|
||||
port->max_bw);
|
||||
}
|
||||
}
|
||||
|
||||
struct tb_quirk {
|
||||
u16 hw_vendor_id;
|
||||
u16 hw_device_id;
|
||||
@ -37,6 +56,31 @@ static const struct tb_quirk tb_quirks[] = {
|
||||
* DP buffers.
|
||||
*/
|
||||
{ 0x8087, 0x0b26, 0x0000, 0x0000, quirk_dp_credit_allocation },
|
||||
/*
|
||||
* Limit the maximum USB3 bandwidth for the following Intel USB4
|
||||
* host routers due to a hardware issue.
|
||||
*/
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_ADL_NHI0, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_ADL_NHI1, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_RPL_NHI0, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_RPL_NHI1, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_MTL_M_NHI0, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI0, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI1, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
/*
|
||||
* CLx is not supported on AMD USB4 Yellow Carp and Pink Sardine platforms.
|
||||
*/
|
||||
{ 0x0438, 0x0208, 0x0000, 0x0000, quirk_clx_disable },
|
||||
{ 0x0438, 0x0209, 0x0000, 0x0000, quirk_clx_disable },
|
||||
{ 0x0438, 0x020a, 0x0000, 0x0000, quirk_clx_disable },
|
||||
{ 0x0438, 0x020b, 0x0000, 0x0000, quirk_clx_disable },
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -187,6 +187,22 @@ static ssize_t nvm_authenticate_show(struct device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tb_retimer_set_inbound_sbtx(struct tb_port *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++)
|
||||
usb4_port_retimer_set_inbound_sbtx(port, i);
|
||||
}
|
||||
|
||||
static void tb_retimer_unset_inbound_sbtx(struct tb_port *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--)
|
||||
usb4_port_retimer_unset_inbound_sbtx(port, i);
|
||||
}
|
||||
|
||||
static ssize_t nvm_authenticate_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
@ -213,6 +229,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
|
||||
rt->auth_status = 0;
|
||||
|
||||
if (val) {
|
||||
tb_retimer_set_inbound_sbtx(rt->port);
|
||||
if (val == AUTHENTICATE_ONLY) {
|
||||
ret = tb_retimer_nvm_authenticate(rt, true);
|
||||
} else {
|
||||
@ -232,6 +249,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
|
||||
}
|
||||
|
||||
exit_unlock:
|
||||
tb_retimer_unset_inbound_sbtx(rt->port);
|
||||
mutex_unlock(&rt->tb->lock);
|
||||
exit_rpm:
|
||||
pm_runtime_mark_last_busy(&rt->dev);
|
||||
@ -440,8 +458,7 @@ int tb_retimer_scan(struct tb_port *port, bool add)
|
||||
* Enable sideband channel for each retimer. We can do this
|
||||
* regardless whether there is device connected or not.
|
||||
*/
|
||||
for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++)
|
||||
usb4_port_retimer_set_inbound_sbtx(port, i);
|
||||
tb_retimer_set_inbound_sbtx(port);
|
||||
|
||||
/*
|
||||
* Before doing anything else, read the authentication status.
|
||||
@ -464,6 +481,8 @@ int tb_retimer_scan(struct tb_port *port, bool add)
|
||||
break;
|
||||
}
|
||||
|
||||
tb_retimer_unset_inbound_sbtx(port);
|
||||
|
||||
if (!last_idx)
|
||||
return 0;
|
||||
|
||||
|
@ -20,6 +20,7 @@ enum usb4_sb_opcode {
|
||||
USB4_SB_OPCODE_ROUTER_OFFLINE = 0x4e45534c, /* "LSEN" */
|
||||
USB4_SB_OPCODE_ENUMERATE_RETIMERS = 0x4d554e45, /* "ENUM" */
|
||||
USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */
|
||||
USB4_SB_OPCODE_UNSET_INBOUND_SBTX = 0x50555355, /* "USUP" */
|
||||
USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */
|
||||
USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
|
||||
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
|
||||
|
@ -2968,8 +2968,6 @@ int tb_switch_add(struct tb_switch *sw)
|
||||
dev_warn(&sw->dev, "reading DROM failed: %d\n", ret);
|
||||
tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
|
||||
|
||||
tb_check_quirks(sw);
|
||||
|
||||
ret = tb_switch_set_uuid(sw);
|
||||
if (ret) {
|
||||
dev_err(&sw->dev, "failed to set UUID\n");
|
||||
@ -2988,6 +2986,8 @@ int tb_switch_add(struct tb_switch *sw)
|
||||
}
|
||||
}
|
||||
|
||||
tb_check_quirks(sw);
|
||||
|
||||
tb_switch_default_link_ports(sw);
|
||||
|
||||
ret = tb_switch_update_link_attributes(sw);
|
||||
|
@ -23,6 +23,11 @@
|
||||
#define NVM_MAX_SIZE SZ_512K
|
||||
#define NVM_DATA_DWORDS 16
|
||||
|
||||
/* Keep link controller awake during update */
|
||||
#define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0)
|
||||
/* Disable CLx if not supported */
|
||||
#define QUIRK_NO_CLX BIT(1)
|
||||
|
||||
/**
|
||||
* struct tb_nvm - Structure holding NVM information
|
||||
* @dev: Owner of the NVM
|
||||
@ -267,6 +272,8 @@ struct tb_bandwidth_group {
|
||||
* @group: Bandwidth allocation group the adapter is assigned to. Only
|
||||
* used for DP IN adapters for now.
|
||||
* @group_list: The adapter is linked to the group's list of ports through this
|
||||
* @max_bw: Maximum possible bandwidth through this adapter if set to
|
||||
* non-zero.
|
||||
*
|
||||
* In USB4 terminology this structure represents an adapter (protocol or
|
||||
* lane adapter).
|
||||
@ -294,6 +301,7 @@ struct tb_port {
|
||||
unsigned int dma_credits;
|
||||
struct tb_bandwidth_group *group;
|
||||
struct list_head group_list;
|
||||
unsigned int max_bw;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1019,6 +1027,9 @@ static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw,
|
||||
*/
|
||||
static inline bool tb_switch_is_clx_supported(const struct tb_switch *sw)
|
||||
{
|
||||
if (sw->quirks & QUIRK_NO_CLX)
|
||||
return false;
|
||||
|
||||
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
|
||||
}
|
||||
|
||||
@ -1234,6 +1245,7 @@ int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
|
||||
int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors);
|
||||
|
||||
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
|
||||
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);
|
||||
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
|
||||
u8 size);
|
||||
int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
|
||||
@ -1291,9 +1303,6 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port);
|
||||
void usb4_port_device_remove(struct usb4_port *usb4);
|
||||
int usb4_port_device_resume(struct usb4_port *usb4);
|
||||
|
||||
/* Keep link controller awake during update */
|
||||
#define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0)
|
||||
|
||||
void tb_check_quirks(struct tb_switch *sw);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
@ -1578,6 +1578,20 @@ int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index)
|
||||
500);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_retimer_unset_inbound_sbtx() - Disable sideband channel transactions
|
||||
* @port: USB4 port
|
||||
* @index: Retimer index
|
||||
*
|
||||
* Disables sideband channel transations on SBTX. The reverse of
|
||||
* usb4_port_retimer_set_inbound_sbtx().
|
||||
*/
|
||||
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index)
|
||||
{
|
||||
return usb4_port_retimer_op(port, index,
|
||||
USB4_SB_OPCODE_UNSET_INBOUND_SBTX, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_retimer_read() - Read from retimer sideband registers
|
||||
* @port: USB4 port
|
||||
@ -1868,6 +1882,15 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index,
|
||||
usb4_port_retimer_nvm_read_block, &info);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
usb4_usb3_port_max_bandwidth(const struct tb_port *port, unsigned int bw)
|
||||
{
|
||||
/* Take the possible bandwidth limitation into account */
|
||||
if (port->max_bw)
|
||||
return min(bw, port->max_bw);
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate
|
||||
* @port: USB3 adapter port
|
||||
@ -1889,7 +1912,9 @@ int usb4_usb3_port_max_link_rate(struct tb_port *port)
|
||||
return ret;
|
||||
|
||||
lr = (val & ADP_USB3_CS_4_MSLR_MASK) >> ADP_USB3_CS_4_MSLR_SHIFT;
|
||||
return lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000;
|
||||
ret = lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000;
|
||||
|
||||
return usb4_usb3_port_max_bandwidth(port, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1916,7 +1941,9 @@ int usb4_usb3_port_actual_link_rate(struct tb_port *port)
|
||||
return 0;
|
||||
|
||||
lr = val & ADP_USB3_CS_4_ALR_MASK;
|
||||
return lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000;
|
||||
ret = lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000;
|
||||
|
||||
return usb4_usb3_port_max_bandwidth(port, ret);
|
||||
}
|
||||
|
||||
static int usb4_usb3_port_cm_request(struct tb_port *port, bool request)
|
||||
@ -2067,18 +2094,30 @@ static int usb4_usb3_port_write_allocated_bandwidth(struct tb_port *port,
|
||||
int downstream_bw)
|
||||
{
|
||||
u32 val, ubw, dbw, scale;
|
||||
int ret;
|
||||
int ret, max_bw;
|
||||
|
||||
/* Read the used scale, hardware default is 0 */
|
||||
ret = tb_port_read(port, &scale, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_USB3_CS_3, 1);
|
||||
/* Figure out suitable scale */
|
||||
scale = 0;
|
||||
max_bw = max(upstream_bw, downstream_bw);
|
||||
while (scale < 64) {
|
||||
if (mbps_to_usb3_bw(max_bw, scale) < 4096)
|
||||
break;
|
||||
scale++;
|
||||
}
|
||||
|
||||
if (WARN_ON(scale >= 64))
|
||||
return -EINVAL;
|
||||
|
||||
ret = tb_port_write(port, &scale, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_USB3_CS_3, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scale &= ADP_USB3_CS_3_SCALE_MASK;
|
||||
ubw = mbps_to_usb3_bw(upstream_bw, scale);
|
||||
dbw = mbps_to_usb3_bw(downstream_bw, scale);
|
||||
|
||||
tb_port_dbg(port, "scaled bandwidth %u/%u, scale %u\n", ubw, dbw, scale);
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_USB3_CS_2, 1);
|
||||
if (ret)
|
||||
|
@ -60,6 +60,11 @@ static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (func->devfn != PCI_DEV_FN_HOST_DEVICE &&
|
||||
func->devfn != PCI_DEV_FN_OTG) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
|
@ -403,20 +403,6 @@ static int cdnsp_ep0_std_request(struct cdnsp_device *pdev,
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
ret = cdnsp_ep0_set_isoch_delay(pdev, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
/*
|
||||
* Add request into pending list to block sending status stage
|
||||
* by libcomposite.
|
||||
*/
|
||||
list_add_tail(&pdev->ep0_preq.list,
|
||||
&pdev->ep0_preq.pep->pending_list);
|
||||
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
if (ret == -EBUSY)
|
||||
ret = 0;
|
||||
|
||||
list_del(&pdev->ep0_preq.list);
|
||||
break;
|
||||
default:
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
break;
|
||||
@ -474,9 +460,6 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev)
|
||||
else
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
|
||||
if (!len)
|
||||
pdev->ep0_stage = CDNSP_STATUS_STAGE;
|
||||
|
||||
if (ret == USB_GADGET_DELAYED_STATUS) {
|
||||
trace_cdnsp_ep0_status_stage("delayed");
|
||||
return;
|
||||
@ -484,6 +467,6 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev)
|
||||
out:
|
||||
if (ret < 0)
|
||||
cdnsp_ep0_stall(pdev);
|
||||
else if (pdev->ep0_stage == CDNSP_STATUS_STAGE)
|
||||
else if (!len && pdev->ep0_stage != CDNSP_STATUS_STAGE)
|
||||
cdnsp_status_stage(pdev);
|
||||
}
|
||||
|
@ -29,30 +29,23 @@
|
||||
#define PLAT_DRIVER_NAME "cdns-usbssp"
|
||||
|
||||
#define CDNS_VENDOR_ID 0x17cd
|
||||
#define CDNS_DEVICE_ID 0x0100
|
||||
#define CDNS_DEVICE_ID 0x0200
|
||||
#define CDNS_DRD_ID 0x0100
|
||||
#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
|
||||
|
||||
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *func;
|
||||
|
||||
/*
|
||||
* Gets the second function.
|
||||
* It's little tricky, but this platform has two function.
|
||||
* The fist keeps resources for Host/Device while the second
|
||||
* keeps resources for DRD/OTG.
|
||||
* Platform has two function. The fist keeps resources for
|
||||
* Host/Device while the secon keeps resources for DRD/OTG.
|
||||
*/
|
||||
func = pci_get_device(pdev->vendor, pdev->device, NULL);
|
||||
if (!func)
|
||||
return NULL;
|
||||
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 (func->devfn == pdev->devfn) {
|
||||
func = pci_get_device(pdev->vendor, pdev->device, func);
|
||||
if (!func)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return func;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int cdnsp_pci_probe(struct pci_dev *pdev,
|
||||
@ -230,6 +223,8 @@ static const struct pci_device_id cdnsp_pci_ids[] = {
|
||||
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 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -208,6 +208,7 @@ struct hw_bank {
|
||||
* @in_lpm: if the core in low power mode
|
||||
* @wakeup_int: if wakeup interrupt occur
|
||||
* @rev: The revision number for controller
|
||||
* @mutex: protect code from concorrent running when doing role switch
|
||||
*/
|
||||
struct ci_hdrc {
|
||||
struct device *dev;
|
||||
@ -260,6 +261,7 @@ struct ci_hdrc {
|
||||
bool in_lpm;
|
||||
bool wakeup_int;
|
||||
enum ci_revision rev;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
|
||||
|
@ -984,9 +984,16 @@ static ssize_t role_store(struct device *dev,
|
||||
strlen(ci->roles[role]->name)))
|
||||
break;
|
||||
|
||||
if (role == CI_ROLE_END || role == ci->role)
|
||||
if (role == CI_ROLE_END)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ci->mutex);
|
||||
|
||||
if (role == ci->role) {
|
||||
mutex_unlock(&ci->mutex);
|
||||
return n;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
disable_irq(ci->irq);
|
||||
ci_role_stop(ci);
|
||||
@ -995,6 +1002,7 @@ static ssize_t role_store(struct device *dev,
|
||||
ci_handle_vbus_change(ci);
|
||||
enable_irq(ci->irq);
|
||||
pm_runtime_put_sync(dev);
|
||||
mutex_unlock(&ci->mutex);
|
||||
|
||||
return (ret == 0) ? n : ret;
|
||||
}
|
||||
@ -1030,6 +1038,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&ci->lock);
|
||||
mutex_init(&ci->mutex);
|
||||
ci->dev = dev;
|
||||
ci->platdata = dev_get_platdata(dev);
|
||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||
|
@ -167,8 +167,10 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
|
||||
|
||||
void ci_handle_id_switch(struct ci_hdrc *ci)
|
||||
{
|
||||
enum ci_role role = ci_otg_role(ci);
|
||||
enum ci_role role;
|
||||
|
||||
mutex_lock(&ci->mutex);
|
||||
role = ci_otg_role(ci);
|
||||
if (role != ci->role) {
|
||||
dev_dbg(ci->dev, "switching from %s to %s\n",
|
||||
ci_role(ci)->name, ci->roles[role]->name);
|
||||
@ -198,6 +200,7 @@ void ci_handle_id_switch(struct ci_hdrc *ci)
|
||||
if (role == CI_ROLE_GADGET)
|
||||
ci_handle_vbus_change(ci);
|
||||
}
|
||||
mutex_unlock(&ci->mutex);
|
||||
}
|
||||
/**
|
||||
* ci_otg_work - perform otg (vbus/id) event handle
|
||||
|
@ -35,7 +35,8 @@ static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
|
||||
dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST) ||
|
||||
(hsotg->role_sw_default_mode == USB_DR_MODE_HOST));
|
||||
}
|
||||
|
||||
static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
|
||||
|
@ -4549,8 +4549,7 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
hsotg->gadget.dev.of_node = hsotg->dev->of_node;
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL ||
|
||||
(hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg))) {
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
ret = dwc2_lowlevel_hw_enable(hsotg);
|
||||
if (ret)
|
||||
goto err;
|
||||
@ -4612,8 +4611,7 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
|
||||
if (!IS_ERR_OR_NULL(hsotg->uphy))
|
||||
otg_set_peripheral(hsotg->uphy->otg, NULL);
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL ||
|
||||
(hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)))
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
|
||||
return 0;
|
||||
|
@ -91,13 +91,6 @@ static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __dwc2_disable_regulators(void *data)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = data;
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
}
|
||||
|
||||
static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
@ -108,11 +101,6 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&pdev->dev,
|
||||
__dwc2_disable_regulators, hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (hsotg->clk) {
|
||||
ret = clk_prepare_enable(hsotg->clk);
|
||||
if (ret)
|
||||
@ -168,7 +156,7 @@ static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
|
||||
if (hsotg->clk)
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
|
||||
return 0;
|
||||
return regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -576,8 +564,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
dwc2_debugfs_init(hsotg);
|
||||
|
||||
/* Gadget code manages lowlevel hw on its own */
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL ||
|
||||
(hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)))
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
|
||||
@ -608,7 +595,7 @@ error_init:
|
||||
if (hsotg->params.activate_stm_id_vb_detection)
|
||||
regulator_disable(hsotg->usb33d);
|
||||
error:
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL)
|
||||
if (hsotg->ll_hw_enabled)
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
return retval;
|
||||
}
|
||||
|
@ -1098,7 +1098,7 @@ struct dwc3_scratchpad_array {
|
||||
* change quirk.
|
||||
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
|
||||
* check during HS transmit.
|
||||
* @resume-hs-terminations: Set if we enable quirk for fixing improper crc
|
||||
* @resume_hs_terminations: Set if we enable quirk for fixing improper crc
|
||||
* generation after resume from suspend.
|
||||
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
|
||||
* instances in park mode.
|
||||
|
@ -1699,6 +1699,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
||||
*/
|
||||
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
int ret;
|
||||
@ -1722,10 +1723,13 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
|
||||
if (!interrupt)
|
||||
if (!interrupt) {
|
||||
if (!DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC3, 310A))
|
||||
mdelay(1);
|
||||
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
|
||||
else if (!ret)
|
||||
} else if (!ret) {
|
||||
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
|
||||
}
|
||||
|
||||
dep->flags &= ~DWC3_EP_DELAY_STOP;
|
||||
return ret;
|
||||
@ -3774,7 +3778,11 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
* enabled, the EndTransfer command will have completed upon
|
||||
* returning from this function.
|
||||
*
|
||||
* This mode is NOT available on the DWC_usb31 IP.
|
||||
* This mode is NOT available on the DWC_usb31 IP. In this
|
||||
* case, if the IOC bit is not set, then delay by 1ms
|
||||
* after issuing the EndTransfer command. This allows for the
|
||||
* controller to handle the command completely before DWC3
|
||||
* remove requests attempts to unmap USB request buffers.
|
||||
*/
|
||||
|
||||
__dwc3_stop_active_transfer(dep, force, interrupt);
|
||||
|
@ -2079,10 +2079,9 @@ unknown:
|
||||
sizeof(url_descriptor->URL)
|
||||
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset);
|
||||
|
||||
if (ctrl->wLength < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH
|
||||
+ landing_page_length)
|
||||
landing_page_length = ctrl->wLength
|
||||
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset;
|
||||
if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length)
|
||||
landing_page_length = w_length
|
||||
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset;
|
||||
|
||||
memcpy(url_descriptor->URL,
|
||||
cdev->landing_page + landing_page_offset,
|
||||
|
@ -1422,7 +1422,7 @@ void g_audio_cleanup(struct g_audio *g_audio)
|
||||
uac = g_audio->uac;
|
||||
card = uac->card;
|
||||
if (card)
|
||||
snd_card_free(card);
|
||||
snd_card_free_when_closed(card);
|
||||
|
||||
kfree(uac->p_prm.reqs);
|
||||
kfree(uac->c_prm.reqs);
|
||||
|
@ -410,6 +410,7 @@ static const struct usb_device_id onboard_hub_id_table[] = {
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 */
|
||||
|
@ -36,6 +36,7 @@ static const struct onboard_hub_pdata vialab_vl817_data = {
|
||||
|
||||
static const struct of_device_id onboard_hub_match[] = {
|
||||
{ .compatible = "usb424,2514", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2517", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
|
||||
{ .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
|
||||
{ .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
|
||||
|
@ -111,6 +111,13 @@ UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999,
|
||||
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
|
||||
US_FL_BROKEN_FUA),
|
||||
|
||||
/* Reported by: Yaroslav Furman <yaro330@gmail.com> */
|
||||
UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999,
|
||||
"JMicron",
|
||||
"JMS583Gen 2",
|
||||
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
|
||||
US_FL_NO_REPORT_OPCODES),
|
||||
|
||||
/* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */
|
||||
UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999,
|
||||
"PNY",
|
||||
|
@ -1445,10 +1445,18 @@ static int tcpm_ams_start(struct tcpm_port *port, enum tcpm_ams ams)
|
||||
static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
|
||||
const u32 *data, int cnt)
|
||||
{
|
||||
u32 vdo_hdr = port->vdo_data[0];
|
||||
|
||||
WARN_ON(!mutex_is_locked(&port->lock));
|
||||
|
||||
/* Make sure we are not still processing a previous VDM packet */
|
||||
WARN_ON(port->vdm_state > VDM_STATE_DONE);
|
||||
/* If is sending discover_identity, handle received message first */
|
||||
if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) {
|
||||
port->send_discover = true;
|
||||
mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS);
|
||||
} else {
|
||||
/* Make sure we are not still processing a previous VDM packet */
|
||||
WARN_ON(port->vdm_state > VDM_STATE_DONE);
|
||||
}
|
||||
|
||||
port->vdo_count = cnt + 1;
|
||||
port->vdo_data[0] = header;
|
||||
@ -1948,11 +1956,13 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||||
switch (PD_VDO_CMD(vdo_hdr)) {
|
||||
case CMD_DISCOVER_IDENT:
|
||||
res = tcpm_ams_start(port, DISCOVER_IDENTITY);
|
||||
if (res == 0)
|
||||
if (res == 0) {
|
||||
port->send_discover = false;
|
||||
else if (res == -EAGAIN)
|
||||
} else if (res == -EAGAIN) {
|
||||
port->vdo_data[0] = 0;
|
||||
mod_send_discover_delayed_work(port,
|
||||
SEND_DISCOVER_RETRY_MS);
|
||||
}
|
||||
break;
|
||||
case CMD_DISCOVER_SVID:
|
||||
res = tcpm_ams_start(port, DISCOVER_SVIDS);
|
||||
@ -2035,6 +2045,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||||
unsigned long timeout;
|
||||
|
||||
port->vdm_retries = 0;
|
||||
port->vdo_data[0] = 0;
|
||||
port->vdm_state = VDM_STATE_BUSY;
|
||||
timeout = vdm_ready_timeout(vdo_hdr);
|
||||
mod_vdm_delayed_work(port, timeout);
|
||||
@ -4570,6 +4581,9 @@ static void run_state_machine(struct tcpm_port *port)
|
||||
case SOFT_RESET:
|
||||
port->message_id = 0;
|
||||
port->rx_msgid = -1;
|
||||
/* remove existing capabilities */
|
||||
usb_power_delivery_unregister_capabilities(port->partner_source_caps);
|
||||
port->partner_source_caps = NULL;
|
||||
tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
|
||||
tcpm_ams_finish(port);
|
||||
if (port->pwr_role == TYPEC_SOURCE) {
|
||||
@ -4589,6 +4603,9 @@ static void run_state_machine(struct tcpm_port *port)
|
||||
case SOFT_RESET_SEND:
|
||||
port->message_id = 0;
|
||||
port->rx_msgid = -1;
|
||||
/* remove existing capabilities */
|
||||
usb_power_delivery_unregister_capabilities(port->partner_source_caps);
|
||||
port->partner_source_caps = NULL;
|
||||
if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET))
|
||||
tcpm_set_state_cond(port, hard_reset_state(port), 0);
|
||||
else
|
||||
@ -4718,6 +4735,9 @@ static void run_state_machine(struct tcpm_port *port)
|
||||
tcpm_set_state(port, SNK_STARTUP, 0);
|
||||
break;
|
||||
case PR_SWAP_SNK_SRC_SINK_OFF:
|
||||
/* will be source, remove existing capabilities */
|
||||
usb_power_delivery_unregister_capabilities(port->partner_source_caps);
|
||||
port->partner_source_caps = NULL;
|
||||
/*
|
||||
* Prevent vbus discharge circuit from turning on during PR_SWAP
|
||||
* as this is not a disconnect.
|
||||
|
@ -1125,12 +1125,11 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||
static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
|
||||
{
|
||||
struct usb_power_delivery_desc desc = { ucsi->cap.pd_version};
|
||||
struct usb_power_delivery_capabilities_desc pd_caps;
|
||||
struct usb_power_delivery_capabilities *pd_cap;
|
||||
struct ucsi_connector *con = &ucsi->connector[index];
|
||||
struct typec_capability *cap = &con->typec_cap;
|
||||
enum typec_accessory *accessory = cap->accessory;
|
||||
enum usb_role u_role = USB_ROLE_NONE;
|
||||
@ -1151,7 +1150,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||
init_completion(&con->complete);
|
||||
mutex_init(&con->lock);
|
||||
INIT_LIST_HEAD(&con->partner_tasks);
|
||||
con->num = index + 1;
|
||||
con->ucsi = ucsi;
|
||||
|
||||
cap->fwnode = ucsi_find_fwnode(con);
|
||||
@ -1328,8 +1326,8 @@ out_unlock:
|
||||
*/
|
||||
static int ucsi_init(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_connector *con;
|
||||
u64 command;
|
||||
struct ucsi_connector *con, *connector;
|
||||
u64 command, ntfy;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -1341,8 +1339,8 @@ static int ucsi_init(struct ucsi *ucsi)
|
||||
}
|
||||
|
||||
/* Enable basic notifications */
|
||||
ucsi->ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
|
||||
ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;
|
||||
ret = ucsi_send_command(ucsi, command, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err_reset;
|
||||
@ -1359,31 +1357,33 @@ static int ucsi_init(struct ucsi *ucsi)
|
||||
}
|
||||
|
||||
/* Allocate the connectors. Released in ucsi_unregister() */
|
||||
ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1,
|
||||
sizeof(*ucsi->connector), GFP_KERNEL);
|
||||
if (!ucsi->connector) {
|
||||
connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL);
|
||||
if (!connector) {
|
||||
ret = -ENOMEM;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
/* Register all connectors */
|
||||
for (i = 0; i < ucsi->cap.num_connectors; i++) {
|
||||
ret = ucsi_register_port(ucsi, i);
|
||||
connector[i].num = i + 1;
|
||||
ret = ucsi_register_port(ucsi, &connector[i]);
|
||||
if (ret)
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
/* Enable all notifications */
|
||||
ucsi->ntfy = UCSI_ENABLE_NTFY_ALL;
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
|
||||
ntfy = UCSI_ENABLE_NTFY_ALL;
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;
|
||||
ret = ucsi_send_command(ucsi, command, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err_unregister;
|
||||
|
||||
ucsi->connector = connector;
|
||||
ucsi->ntfy = ntfy;
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
for (con = ucsi->connector; con->port; con++) {
|
||||
for (con = connector; con->port; con++) {
|
||||
ucsi_unregister_partner(con);
|
||||
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
|
||||
ucsi_unregister_port_psy(con);
|
||||
@ -1399,10 +1399,7 @@ err_unregister:
|
||||
typec_unregister_port(con->port);
|
||||
con->port = NULL;
|
||||
}
|
||||
|
||||
kfree(ucsi->connector);
|
||||
ucsi->connector = NULL;
|
||||
|
||||
kfree(connector);
|
||||
err_reset:
|
||||
memset(&ucsi->cap, 0, sizeof(ucsi->cap));
|
||||
ucsi_reset_ppm(ucsi);
|
||||
|
@ -78,7 +78,7 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
if (ret)
|
||||
goto out_clear_bit;
|
||||
|
||||
if (!wait_for_completion_timeout(&ua->complete, HZ))
|
||||
if (!wait_for_completion_timeout(&ua->complete, 5 * HZ))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
out_clear_bit:
|
||||
|
Loading…
Reference in New Issue
Block a user