Commit 0ec57cfa authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 18940c88 50213832
=======================
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 @@ static int dwc2_driver_probe(struct platform_device *dev)
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 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
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 = &microchip_usb424_data, },
{ .compatible = "usb424,2517", .data = &microchip_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 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
*/
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 @@ static int ucsi_init(struct ucsi *ucsi)
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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment