USB/Thunderbolt update for 6.12-rc1

Here is the large set of USB and Thunderbolt changes for 6.12-rc1.
 
 Nothing "major" in here, except for a new 9p network gadget that has
 been worked on for a long time (all of the needed acks are here.)  Other
 than that, it's the usual set of:
   - Thunderbolt / USB4 driver updates and additions for new hardware
   - dwc3 driver updates and new features added
   - xhci driver updates
   - typec driver updates
   - USB gadget updates and api additions to make some gadgets more
     configurable by userspace
   - dwc2 driver updates
   - usb phy driver updates
   - usbip feature additions
   - other minor USB driver updates
 
 All of these have been in linux-next for a long time with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZvU0/g8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykGcACfSqouxRg8FRtq+nIKHWXI9lOTnVcAoKd9PAgq
 1i7yCNopPEPEW8sjz1GX
 =mY+S
 -----END PGP SIGNATURE-----

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

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

  Nothing "major" in here, except for a new 9p network gadget that has
  been worked on for a long time (all of the needed acks are here)

  Other than that, it's the usual set of:

   - Thunderbolt / USB4 driver updates and additions for new hardware

   - dwc3 driver updates and new features added

   - xhci driver updates

   - typec driver updates

   - USB gadget updates and api additions to make some gadgets more
     configurable by userspace

   - dwc2 driver updates

   - usb phy driver updates

   - usbip feature additions

   - other minor USB driver updates

  All of these have been in linux-next for a long time with no reported
  issues"

* tag 'usb-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (145 commits)
  sub: cdns3: Use predefined PCI vendor ID constant
  sub: cdns2: Use predefined PCI vendor ID constant
  USB: misc: yurex: fix race between read and write
  USB: misc: cypress_cy7c63: check for short transfer
  USB: appledisplay: close race between probe and completion handler
  USB: class: CDC-ACM: fix race between get_serial and set_serial
  usb: r8a66597-hcd: make read-only const arrays static
  usb: typec: ucsi: Fix busy loop on ASUS VivoBooks
  usb: dwc3: rtk: Clean up error code in __get_dwc3_maximum_speed()
  usb: storage: ene_ub6250: Fix right shift warnings
  usb: roles: Improve the fix for a false positive recursive locking complaint
  locking/mutex: Introduce mutex_init_with_key()
  locking/mutex: Define mutex_init() once
  net/9p/usbg: fix CONFIG_USB_GADGET dependency
  usb: xhci: fix loss of data on Cadence xHC
  usb: xHCI: add XHCI_RESET_ON_RESUME quirk for Phytium xHCI host
  usb: dwc3: imx8mp: disable SS_CON and U3 wakeup for system sleep
  usb: dwc3: imx8mp: add 2 software managed quirk properties for host mode
  usb: host: xhci-plat: Parse xhci-missing_cas_quirk and apply quirk
  usb: misc: onboard_usb_dev: add Microchip usb5744 SMBus programming support
  ...
This commit is contained in:
Linus Torvalds 2024-09-26 09:45:36 -07:00
commit 4965ddb166
189 changed files with 3860 additions and 1248 deletions

View File

@ -6,3 +6,10 @@ Description:
This item contains just one readonly attribute: port_num.
It contains the port number of the /dev/ttyGS<n> device
associated with acm function's instance "name".
What: /config/usb-gadget/gadget/functions/acm.name/protocol
Date: Aug 2024
KernelVersion: 6.13
Description:
Reported bInterfaceProtocol for the ACM device. For legacy
reasons, this defaults to 1 (USB_CDC_ACM_PROTO_AT_V25TER).

View File

@ -30,4 +30,12 @@ Description:
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
p_it_name playback input terminal name
p_it_ch_name playback channels name
p_ot_name playback output terminal name
p_fu_vol_name playback mute/volume functional unit name
c_it_name capture input terminal name
c_it_ch_name capture channels name
c_ot_name capture output terminal name
c_fu_vol_name capture mute/volume functional unit name
===================== =======================================

View File

@ -35,6 +35,17 @@ Description:
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
if_ctrl_name topology control name
clksrc_in_name input clock name
clksrc_out_name output clock name
p_it_name playback input terminal name
p_it_ch_name playback input first channel name
p_ot_name playback output terminal name
p_fu_vol_name playback mute/volume function unit name
c_it_name capture input terminal name
c_it_ch_name capture input first channel name
c_ot_name capture output terminal name
c_fu_vol_name capture mute/volume functional unit name
c_terminal_type code of the capture terminal type
p_terminal_type code of the playback terminal type
===================== =======================================

View File

@ -87,6 +87,12 @@ properties:
maximum: 119
default: 100
nxp,sim:
description:
The system integration module (SIM) provides system control and chip
configuration registers.
$ref: /schemas/types.yaml#/definitions/phandle
required:
- compatible
- reg
@ -110,6 +116,17 @@ allOf:
required:
- fsl,anatop
- if:
properties:
compatible:
const: fsl,imx7ulp-usbphy
then:
required:
- nxp,sim
else:
properties:
nxp,sim: false
additionalProperties: false
examples:

View File

@ -0,0 +1,52 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/fsl,ls1028a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale layerscape SuperSpeed DWC3 USB SoC controller
maintainers:
- Frank Li <Frank.Li@nxp.com>
select:
properties:
compatible:
contains:
enum:
- fsl,ls1028a-dwc3
required:
- compatible
properties:
compatible:
items:
- enum:
- fsl,ls1028a-dwc3
- const: snps,dwc3
reg:
maxItems: 1
interrupts:
maxItems: 1
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
allOf:
- $ref: snps,dwc3.yaml#
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb@fe800000 {
compatible = "fsl,ls1028a-dwc3", "snps,dwc3";
reg = <0xfe800000 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@ -1,110 +0,0 @@
MSM SoC HSUSB controllers
EHCI
Required properties:
- compatible: Should contain "qcom,ehci-host"
- regs: offset and length of the register set in the memory map
- usb-phy: phandle for the PHY device
Example EHCI controller device node:
ehci: ehci@f9a55000 {
compatible = "qcom,ehci-host";
reg = <0xf9a55000 0x400>;
usb-phy = <&usb_otg>;
};
USB PHY with optional OTG:
Required properties:
- compatible: Should contain:
"qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY
"qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY
- regs: Offset and length of the register set in the memory map
- interrupts: interrupt-specifier for the OTG interrupt.
- clocks: A list of phandle + clock-specifier pairs for the
clocks listed in clock-names
- clock-names: Should contain the following:
"phy" USB PHY reference clock
"core" Protocol engine clock
"iface" Interface bus clock
"alt_core" Protocol engine clock for targets with asynchronous
reset methodology. (optional)
- vdccx-supply: phandle to the regulator for the vdd supply for
digital circuit operation.
- v1p8-supply: phandle to the regulator for the 1.8V supply
- v3p3-supply: phandle to the regulator for the 3.3V supply
- resets: A list of phandle + reset-specifier pairs for the
resets listed in reset-names
- reset-names: Should contain the following:
"phy" USB PHY controller reset
"link" USB LINK controller reset
- qcom,otg-control: OTG control (VBUS and ID notifications) can be one of
1 - PHY control
2 - PMIC control
Optional properties:
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
- switch-gpio: A phandle + gpio-specifier pair. Some boards are using Dual
SPDT USB Switch, witch is controlled by GPIO to de/multiplex
D+/D- USB lines between connectors.
- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device
Mode Eye Diagram test. Start address at which these values will be
written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as
"do not overwrite default value at this address".
For example: qcom,phy-init-sequence = < -1 0x63 >;
Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1.
- qcom,phy-num: Select number of pyco-phy to use, can be one of
0 - PHY one, default
1 - Second PHY
Some platforms may have configuration to allow USB
controller work with any of the two HSPHYs present.
- qcom,vdd-levels: This property must be a list of three integer values
(no, min, max) where each value represents either a voltage
in microvolts or a value corresponding to voltage corner.
- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy
and controller driver therefore enables pull-up explicitly
before starting controller using usbcmd run/stop bit.
- extcon: phandles to external connector devices. First phandle
should point to external connector, which provide "USB"
cable events, the second should point to external connector
device, which provide "USB-HOST" cable events. If one of
the external connector devices is not required empty <0>
phandle should be specified.
Example HSUSB OTG controller device node:
usb@f9a55000 {
compatible = "qcom,usb-otg-snps";
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0>;
dr_mode = "peripheral";
clocks = <&gcc GCC_XO_CLK>, <&gcc GCC_USB_HS_SYSTEM_CLK>,
<&gcc GCC_USB_HS_AHB_CLK>;
clock-names = "phy", "core", "iface";
vddcx-supply = <&pm8841_s2_corner>;
v1p8-supply = <&pm8941_l6>;
v3p3-supply = <&pm8941_l24>;
resets = <&gcc GCC_USB2A_PHY_BCR>, <&gcc GCC_USB_HS_BCR>;
reset-names = "phy", "link";
qcom,otg-control = <1>;
qcom,phy-init-sequence = < -1 0x63 >;
qcom,vdd-levels = <1 5 7>;
};

View File

@ -52,6 +52,7 @@ properties:
- qcom,sm8550-dwc3
- qcom,sm8650-dwc3
- qcom,x1e80100-dwc3
- qcom,x1e80100-dwc3-mp
- const: qcom,dwc3
reg:
@ -289,6 +290,7 @@ allOf:
- qcom,sc8280xp-dwc3
- qcom,sc8280xp-dwc3-mp
- qcom,x1e80100-dwc3
- qcom,x1e80100-dwc3-mp
then:
properties:
clocks:
@ -428,6 +430,21 @@ allOf:
contains:
enum:
- qcom,ipq5332-dwc3
then:
properties:
interrupts:
maxItems: 3
interrupt-names:
items:
- const: pwr_event
- const: dp_hs_phy_irq
- const: dm_hs_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,x1e80100-dwc3
then:
properties:
@ -486,6 +503,7 @@ allOf:
contains:
enum:
- qcom,sc8180x-dwc3-mp
- qcom,x1e80100-dwc3-mp
then:
properties:
interrupts:

View File

@ -13,10 +13,9 @@ properties:
compatible:
oneOf:
- const: ti,j721e-usb
- const: ti,am64-usb
- items:
- const: ti,j721e-usb
- const: ti,am64-usb
- const: ti,j721e-usb
reg:
maxItems: 1

View File

@ -48,11 +48,66 @@ For server running on QEMU host with virtio transport::
mount -t 9p -o trans=virtio <mount_tag> /mnt/9
where mount_tag is the tag associated by the server to each of the exported
where mount_tag is the tag generated by the server to each of the exported
mount points. Each 9P export is seen by the client as a virtio device with an
associated "mount_tag" property. Available mount tags can be
seen by reading /sys/bus/virtio/drivers/9pnet_virtio/virtio<n>/mount_tag files.
USBG Usage
==========
To mount a 9p FS on a USB Host accessible via the gadget at runtime::
mount -t 9p -o trans=usbg,aname=/path/to/fs <device> /mnt/9
To mount a 9p FS on a USB Host accessible via the gadget as root filesystem::
root=<device> rootfstype=9p rootflags=trans=usbg,cache=loose,uname=root,access=0,dfltuid=0,dfltgid=0,aname=/path/to/rootfs
where <device> is the tag associated by the usb gadget transport.
It is defined by the configfs instance name.
USBG Example
============
The USB host exports a filesystem, while the gadget on the USB device
side makes it mountable.
Diod (9pfs server) and the forwarder are on the development host, where
the root filesystem is actually stored. The gadget is initialized during
boot (or later) on the embedded board. Then the forwarder will find it
on the USB bus and start forwarding requests.
In this case the 9p requests come from the device and are handled by the
host. The reason is that USB device ports are normally not available on
PCs, so a connection in the other direction would not work.
When using the usbg transport, for now there is no native usb host
service capable to handle the requests from the gadget driver. For
this we have to use the extra python tool p9_fwd.py from tools/usb.
Just start the 9pfs capable network server like diod/nfs-ganesha e.g.::
$ diod -f -n -d 0 -S -l 0.0.0.0:9999 -e $PWD
Optionaly scan your bus if there are more then one usbg gadgets to find their path::
$ python $kernel_dir/tools/usb/p9_fwd.py list
Bus | Addr | Manufacturer | Product | ID | Path
--- | ---- | ---------------- | ---------------- | --------- | ----
2 | 67 | unknown | unknown | 1d6b:0109 | 2-1.1.2
2 | 68 | unknown | unknown | 1d6b:0109 | 2-1.1.3
Then start the python transport::
$ python $kernel_dir/tools/usb/p9_fwd.py --path 2-1.1.2 connect -p 9999
After that the gadget driver can be used as described above.
One use-case is to use it as an alternative to NFS root booting during
the development of embedded Linux devices.
Options
=======
@ -68,6 +123,7 @@ Options
virtio connect to the next virtio channel available
(from QEMU with trans_virtio module)
rdma connect to a specified RDMA channel
usbg connect to a specified usb gadget channel
======== ============================================
uname=name user name to attempt mount as on the remote server. The

View File

@ -0,0 +1,39 @@
======================
FunctionFS Descriptors
======================
Some of the descriptors that can be written to the FFS gadget are
described below. Device and configuration descriptors are handled
by the composite gadget and are not written by the user to the
FFS gadget.
Descriptors are written to the "ep0" file in the FFS gadget
following the descriptor header.
.. kernel-doc:: include/uapi/linux/usb/functionfs.h
:doc: descriptors
Interface Descriptors
---------------------
Standard USB interface descriptors may be written. The class/subclass of the
most recent interface descriptor determines what type of class-specific
descriptors are accepted.
Class-Specific Descriptors
--------------------------
Class-specific descriptors are accepted only for the class/subclass of the
most recent interface descriptor. The following are some of the
class-specific descriptors that are supported.
DFU Functional Descriptor
~~~~~~~~~~~~~~~~~~~~~~~~~
When the interface class is USB_CLASS_APP_SPEC and the interface subclass
is USB_SUBCLASS_DFU, a DFU functional descriptor can be provided.
The DFU functional descriptor is a described in the USB specification for
Device Firmware Upgrade (DFU), version 1.1 as of this writing.
.. kernel-doc:: include/uapi/linux/usb/functionfs.h
:doc: usb_dfu_functional_descriptor

View File

@ -25,6 +25,8 @@ interface numbers starting from zero). The FunctionFS changes
them as needed also handling situation when numbers differ in
different configurations.
For more information about FunctionFS descriptors see :doc:`functionfs-desc`
When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint. Again, FunctionFS takes care of the real

View File

@ -765,6 +765,17 @@ The uac2 function provides these attributes in its function directory:
req_number the number of pre-allocated request for both capture
and playback
function_name name of the interface
if_ctrl_name topology control name
clksrc_in_name input clock name
clksrc_out_name output clock name
p_it_name playback input terminal name
p_it_ch_name playback input first channel name
p_ot_name playback output terminal name
p_fu_vol_name playback function unit name
c_it_name capture input terminal name
c_it_ch_name capture input first channel name
c_ot_name capture output terminal name
c_fu_vol_name capture functional unit name
c_terminal_type code of the capture terminal type
p_terminal_type code of the playback terminal type
================ ====================================================
@ -957,6 +968,14 @@ The uac1 function provides these attributes in its function directory:
req_number the number of pre-allocated requests for both capture
and playback
function_name name of the interface
p_it_name playback input terminal name
p_it_ch_name playback channels name
p_ot_name playback output terminal name
p_fu_vol_name playback mute/volume functional unit name
c_it_name capture input terminal name
c_it_ch_name capture channels name
c_ot_name capture output terminal name
c_fu_vol_name capture mute/volume functional unit name
================ ====================================================
The attributes have sane default values.

View File

@ -11,6 +11,7 @@ USB support
dwc3
ehci
functionfs
functionfs-desc
gadget_configfs
gadget_hid
gadget_multi

View File

@ -32,40 +32,20 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
goto out_put;
/*
* Try to find physical device walking upwards to the hierarcy.
* We need to do this because the xHCI driver might not yet be
* bound so the USB3 SuperSpeed ports are not yet created.
* Ignore USB3 ports here as USB core will set up device links between
* tunneled USB3 devices and NHI host during USB device creation.
* USB3 ports might not even have a physical device yet if xHCI driver
* isn't bound yet.
*/
do {
dev = acpi_get_first_physical_node(adev);
if (dev)
break;
adev = acpi_dev_parent(adev);
} while (adev);
/*
* Check that the device is PCIe. This is because USB3
* SuperSpeed ports have this property and they are not power
* managed with the xHCI and the SuperSpeed hub so we create the
* link from xHCI instead.
*/
while (dev && !dev_is_pci(dev))
dev = dev->parent;
if (!dev)
dev = acpi_get_first_physical_node(adev);
if (!dev || !dev_is_pci(dev))
goto out_put;
/*
* Check that this actually matches the type of device we
* expect. It should either be xHCI or PCIe root/downstream
* port.
*/
/* Check that this matches a PCIe root/downstream port. */
pdev = to_pci_dev(dev);
if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI ||
(pci_is_pcie(pdev) &&
(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) {
if (pci_is_pcie(pdev) &&
(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)) {
const struct device_link *link;
/*

View File

@ -9,6 +9,7 @@
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/uaccess.h>
@ -34,6 +35,14 @@
#define COUNTER_SET_LEN 3
/*
* USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms
* probed to give good results.
*/
#define MIN_DWELL_TIME 100 /* ms */
#define MAX_DWELL_TIME 500 /* ms */
#define DWELL_SAMPLE_INTERVAL 10
/* Sideband registers and their sizes as defined in the USB4 spec */
struct sb_reg {
unsigned int reg;
@ -394,8 +403,15 @@ out:
* @ber_level: Current BER level contour value
* @voltage_steps: Number of mandatory voltage steps
* @max_voltage_offset: Maximum mandatory voltage offset (in mV)
* @voltage_steps_optional_range: Number of voltage steps for optional range
* @max_voltage_offset_optional_range: Maximum voltage offset for the optional
* range (in mV).
* @time_steps: Number of time margin steps
* @max_time_offset: Maximum time margin offset (in mUI)
* @voltage_time_offset: Offset for voltage / time for software margining
* @dwell_time: Dwell time for software margining (in ms)
* @error_counter: Error counter operation for software margining
* @optional_voltage_offset_range: Enable optional extended voltage range
* @software: %true if software margining is used instead of hardware
* @time: %true if time margining is used instead of voltage
* @right_high: %false if left/low margin test is performed, %true if
@ -414,13 +430,37 @@ struct tb_margining {
unsigned int ber_level;
unsigned int voltage_steps;
unsigned int max_voltage_offset;
unsigned int voltage_steps_optional_range;
unsigned int max_voltage_offset_optional_range;
unsigned int time_steps;
unsigned int max_time_offset;
unsigned int voltage_time_offset;
unsigned int dwell_time;
enum usb4_margin_sw_error_counter error_counter;
bool optional_voltage_offset_range;
bool software;
bool time;
bool right_high;
};
static int margining_modify_error_counter(struct tb_margining *margining,
u32 lanes, enum usb4_margin_sw_error_counter error_counter)
{
struct usb4_port_margining_params params = { 0 };
struct tb_port *port = margining->port;
u32 result;
if (error_counter != USB4_MARGIN_SW_ERROR_COUNTER_CLEAR &&
error_counter != USB4_MARGIN_SW_ERROR_COUNTER_STOP)
return -EOPNOTSUPP;
params.error_counter = error_counter;
params.lanes = lanes;
return usb4_port_sw_margin(port, margining->target, margining->index,
&params, &result);
}
static bool supports_software(const struct tb_margining *margining)
{
return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
@ -454,6 +494,12 @@ independent_time_margins(const struct tb_margining *margining)
return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]);
}
static bool
supports_optional_voltage_offset_range(const struct tb_margining *margining)
{
return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT;
}
static ssize_t
margining_ber_level_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
@ -553,6 +599,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
margining->voltage_steps);
seq_printf(s, "# maximum voltage offset: %u mV\n",
margining->max_voltage_offset);
seq_printf(s, "# optional voltage offset range support: %s\n",
str_yes_no(supports_optional_voltage_offset_range(margining)));
if (supports_optional_voltage_offset_range(margining)) {
seq_printf(s, "# voltage margin steps, optional range: %u\n",
margining->voltage_steps_optional_range);
seq_printf(s, "# maximum voltage offset, optional range: %u mV\n",
margining->max_voltage_offset_optional_range);
}
switch (independent_voltage_margins(margining)) {
case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
@ -667,6 +721,198 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
}
DEBUGFS_ATTR_RW(margining_lanes);
static ssize_t
margining_voltage_time_offset_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
unsigned int max_margin;
unsigned int val;
int ret;
ret = kstrtouint_from_user(user_buf, count, 10, &val);
if (ret)
return ret;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
if (!margining->software)
return -EOPNOTSUPP;
if (margining->time)
max_margin = margining->time_steps;
else
if (margining->optional_voltage_offset_range)
max_margin = margining->voltage_steps_optional_range;
else
max_margin = margining->voltage_steps;
margining->voltage_time_offset = clamp(val, 0, max_margin);
}
return count;
}
static int margining_voltage_time_offset_show(struct seq_file *s,
void *not_used)
{
const struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
if (!margining->software)
return -EOPNOTSUPP;
seq_printf(s, "%d\n", margining->voltage_time_offset);
}
return 0;
}
DEBUGFS_ATTR_RW(margining_voltage_time_offset);
static ssize_t
margining_error_counter_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
enum usb4_margin_sw_error_counter error_counter;
struct seq_file *s = file->private_data;
struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
char *buf;
buf = validate_and_copy_from_user(user_buf, &count);
if (IS_ERR(buf))
return PTR_ERR(buf);
buf[count - 1] = '\0';
if (!strcmp(buf, "nop"))
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_NOP;
else if (!strcmp(buf, "clear"))
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR;
else if (!strcmp(buf, "start"))
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_START;
else if (!strcmp(buf, "stop"))
error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP;
else
return -EINVAL;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
if (!margining->software)
return -EOPNOTSUPP;
margining->error_counter = error_counter;
}
return count;
}
static int margining_error_counter_show(struct seq_file *s, void *not_used)
{
const struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
if (!margining->software)
return -EOPNOTSUPP;
switch (margining->error_counter) {
case USB4_MARGIN_SW_ERROR_COUNTER_NOP:
seq_puts(s, "[nop] clear start stop\n");
break;
case USB4_MARGIN_SW_ERROR_COUNTER_CLEAR:
seq_puts(s, "nop [clear] start stop\n");
break;
case USB4_MARGIN_SW_ERROR_COUNTER_START:
seq_puts(s, "nop clear [start] stop\n");
break;
case USB4_MARGIN_SW_ERROR_COUNTER_STOP:
seq_puts(s, "nop clear start [stop]\n");
break;
}
}
return 0;
}
DEBUGFS_ATTR_RW(margining_error_counter);
static ssize_t
margining_dwell_time_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
unsigned int val;
int ret;
ret = kstrtouint_from_user(user_buf, count, 10, &val);
if (ret)
return ret;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
if (!margining->software)
return -EOPNOTSUPP;
margining->dwell_time = clamp(val, MIN_DWELL_TIME, MAX_DWELL_TIME);
}
return count;
}
static int margining_dwell_time_show(struct seq_file *s, void *not_used)
{
struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
if (!margining->software)
return -EOPNOTSUPP;
seq_printf(s, "%d\n", margining->dwell_time);
}
return 0;
}
DEBUGFS_ATTR_RW(margining_dwell_time);
static ssize_t
margining_optional_voltage_offset_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
bool val;
int ret;
ret = kstrtobool_from_user(user_buf, count, &val);
if (ret)
return ret;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
margining->optional_voltage_offset_range = val;
}
return count;
}
static int margining_optional_voltage_offset_show(struct seq_file *s,
void *not_used)
{
struct tb_margining *margining = s->private;
struct tb *tb = margining->port->sw->tb;
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
seq_printf(s, "%u\n", margining->optional_voltage_offset_range);
}
return 0;
}
DEBUGFS_ATTR_RW(margining_optional_voltage_offset);
static ssize_t margining_mode_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
@ -739,6 +985,51 @@ static int margining_mode_show(struct seq_file *s, void *not_used)
}
DEBUGFS_ATTR_RW(margining_mode);
static int margining_run_sw(struct tb_margining *margining,
struct usb4_port_margining_params *params)
{
u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL;
int ret, i;
ret = usb4_port_sw_margin(margining->port, margining->target, margining->index,
params, margining->results);
if (ret)
goto out_stop;
for (i = 0; i <= nsamples; i++) {
u32 errors = 0;
ret = usb4_port_sw_margin_errors(margining->port, margining->target,
margining->index, &margining->results[1]);
if (ret)
break;
if (margining->lanes == USB4_MARGIN_SW_LANE_0)
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
margining->results[1]);
else if (margining->lanes == USB4_MARGIN_SW_LANE_1)
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
margining->results[1]);
else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES)
errors = margining->results[1];
/* Any errors stop the test */
if (errors)
break;
fsleep(DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC);
}
out_stop:
/*
* Stop the counters but don't clear them to allow the
* different error counter configurations.
*/
margining_modify_error_counter(margining, margining->lanes,
USB4_MARGIN_SW_ERROR_COUNTER_STOP);
return ret;
}
static int margining_run_write(void *data, u64 val)
{
struct tb_margining *margining = data;
@ -779,36 +1070,43 @@ static int margining_run_write(void *data, u64 val)
clx = ret;
}
/* Clear the results */
memset(margining->results, 0, sizeof(margining->results));
if (margining->software) {
struct usb4_port_margining_params params = {
.error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR,
.lanes = margining->lanes,
.time = margining->time,
.voltage_time_offset = margining->voltage_time_offset,
.right_high = margining->right_high,
.optional_voltage_offset_range = margining->optional_voltage_offset_range,
};
tb_port_dbg(port,
"running software %s lane margining for %s lanes %u\n",
margining->time ? "time" : "voltage", dev_name(dev),
margining->lanes);
ret = usb4_port_sw_margin(port, margining->target, margining->index,
margining->lanes, margining->time,
margining->right_high,
USB4_MARGIN_SW_COUNTER_CLEAR);
if (ret)
goto out_clx;
ret = usb4_port_sw_margin_errors(port, margining->target,
margining->index,
&margining->results[0]);
ret = margining_run_sw(margining, &params);
} else {
struct usb4_port_margining_params params = {
.ber_level = margining->ber_level,
.lanes = margining->lanes,
.time = margining->time,
.right_high = margining->right_high,
.optional_voltage_offset_range = margining->optional_voltage_offset_range,
};
tb_port_dbg(port,
"running hardware %s lane margining for %s lanes %u\n",
margining->time ? "time" : "voltage", dev_name(dev),
margining->lanes);
/* Clear the results */
margining->results[0] = 0;
margining->results[1] = 0;
ret = usb4_port_hw_margin(port, margining->target, margining->index,
margining->lanes, margining->ber_level,
margining->time, margining->right_high,
ret = usb4_port_hw_margin(port, margining->target, margining->index, &params,
margining->results);
}
out_clx:
if (down_sw)
tb_switch_clx_enable(down_sw, clx);
out_unlock:
@ -837,6 +1135,13 @@ static ssize_t margining_results_write(struct file *file,
margining->results[0] = 0;
margining->results[1] = 0;
if (margining->software) {
/* Clear the error counters */
margining_modify_error_counter(margining,
USB4_MARGIN_SW_ALL_LANES,
USB4_MARGIN_SW_ERROR_COUNTER_CLEAR);
}
mutex_unlock(&tb->lock);
return count;
}
@ -852,6 +1157,8 @@ static void voltage_margin_show(struct seq_file *s,
if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
seq_puts(s, " exceeds maximum");
seq_puts(s, "\n");
if (margining->optional_voltage_offset_range)
seq_puts(s, " optional voltage offset range enabled\n");
}
static void time_margin_show(struct seq_file *s,
@ -924,6 +1231,24 @@ static int margining_results_show(struct seq_file *s, void *not_used)
voltage_margin_show(s, margining, val);
}
}
} else {
u32 lane_errors, result;
seq_printf(s, "0x%08x\n", margining->results[1]);
result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]);
if (result == USB4_MARGIN_SW_LANE_0 ||
result == USB4_MARGIN_SW_ALL_LANES) {
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
margining->results[1]);
seq_printf(s, "# lane 0 errors: %u\n", lane_errors);
}
if (result == USB4_MARGIN_SW_LANE_1 ||
result == USB4_MARGIN_SW_ALL_LANES) {
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
margining->results[1]);
seq_printf(s, "# lane 1 errors: %u\n", lane_errors);
}
}
mutex_unlock(&tb->lock);
@ -1091,6 +1416,15 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]);
margining->max_voltage_offset = 74 + val * 2;
if (supports_optional_voltage_offset_range(margining)) {
val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK,
margining->caps[0]);
margining->voltage_steps_optional_range = val;
val = FIELD_GET(USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK,
margining->caps[1]);
margining->max_voltage_offset_optional_range = 74 + val * 2;
}
if (supports_time(margining)) {
val = FIELD_GET(USB4_MARGIN_CAP_1_TIME_STEPS_MASK, margining->caps[1]);
margining->time_steps = val;
@ -1127,6 +1461,22 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
debugfs_create_file("margin", 0600, dir, margining,
&margining_margin_fops);
margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR;
margining->dwell_time = MIN_DWELL_TIME;
if (supports_optional_voltage_offset_range(margining))
debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining,
&margining_optional_voltage_offset_fops);
if (supports_software(margining)) {
debugfs_create_file("voltage_time_offset", DEBUGFS_MODE, dir, margining,
&margining_voltage_time_offset_fops);
debugfs_create_file("error_counter", DEBUGFS_MODE, dir, margining,
&margining_error_counter_fops);
debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining,
&margining_dwell_time_fops);
}
return margining;
}

View File

@ -57,6 +57,9 @@ enum usb4_sb_opcode {
#define USB4_MARGIN_CAP_0_TIME BIT(5)
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6)
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
#define USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT BIT(19)
#define USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK GENMASK(26, 20)
#define USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK GENMASK(7, 0)
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
#define USB4_MARGIN_CAP_1_TIME_MIN 0x0
@ -72,6 +75,7 @@ enum usb4_sb_opcode {
#define USB4_MARGIN_HW_RH BIT(4)
#define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5)
#define USB4_MARGIN_HW_BER_SHIFT 5
#define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10)
/* Applicable to all margin values */
#define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0)
@ -82,13 +86,17 @@ enum usb4_sb_opcode {
#define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24
/* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */
#define USB4_MARGIN_SW_LANES_MASK GENMASK(2, 0)
#define USB4_MARGIN_SW_LANE_0 0x0
#define USB4_MARGIN_SW_LANE_1 0x1
#define USB4_MARGIN_SW_ALL_LANES 0x7
#define USB4_MARGIN_SW_TIME BIT(3)
#define USB4_MARGIN_SW_RH BIT(4)
#define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5)
#define USB4_MARGIN_SW_VT_MASK GENMASK(12, 6)
#define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13)
#define USB4_MARGIN_SW_COUNTER_SHIFT 13
#define USB4_MARGIN_SW_COUNTER_NOP 0x0
#define USB4_MARGIN_SW_COUNTER_CLEAR 0x1
#define USB4_MARGIN_SW_COUNTER_START 0x2
#define USB4_MARGIN_SW_COUNTER_STOP 0x3
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0)
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4)
#endif

View File

@ -1353,14 +1353,48 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index
int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
u8 index, u8 reg, const void *buf, u8 size);
/**
* enum usb4_margin_sw_error_counter - Software margining error counter operation
* @USB4_MARGIN_SW_ERROR_COUNTER_NOP: No change in counter setup
* @USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: Set the error counter to 0, enable counter
* @USB4_MARGIN_SW_ERROR_COUNTER_START: Start counter, count from last value
* @USB4_MARGIN_SW_ERROR_COUNTER_STOP: Stop counter, do not clear value
*/
enum usb4_margin_sw_error_counter {
USB4_MARGIN_SW_ERROR_COUNTER_NOP,
USB4_MARGIN_SW_ERROR_COUNTER_CLEAR,
USB4_MARGIN_SW_ERROR_COUNTER_START,
USB4_MARGIN_SW_ERROR_COUNTER_STOP,
};
/**
* struct usb4_port_margining_params - USB4 margining parameters
* @error_counter: Error counter operation for software margining
* @ber_level: Current BER level contour value
* @lanes: %0, %1 or %7 (all)
* @voltage_time_offset: Offset for voltage / time for software margining
* @optional_voltage_offset_range: Enable optional extended voltage range
* @right_high: %false if left/low margin test is performed, %true if right/high
* @time: %true if time margining is used instead of voltage
*/
struct usb4_port_margining_params {
enum usb4_margin_sw_error_counter error_counter;
u32 ber_level;
u32 lanes;
u32 voltage_time_offset;
bool optional_voltage_offset_range;
bool right_high;
bool time;
};
int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *caps);
int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, unsigned int ber_level,
bool timing, bool right_high, u32 *results);
u8 index, const struct usb4_port_margining_params *params,
u32 *results);
int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, bool timing,
bool right_high, u32 counter);
u8 index, const struct usb4_port_margining_params *params,
u32 *results);
int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *errors);

View File

@ -1653,31 +1653,31 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
* @port: USB4 port
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @lanes: Which lanes to run (must match the port capabilities). Can be
* %0, %1 or %7.
* @ber_level: BER level contour value
* @timing: Perform timing margining instead of voltage
* @right_high: Use Right/high margin instead of left/low
* @params: Parameters for USB4 hardware margining
* @results: Array with at least two elements to hold the results
*
* Runs hardware lane margining on USB4 port and returns the result in
* @results.
*/
int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, unsigned int ber_level,
bool timing, bool right_high, u32 *results)
u8 index, const struct usb4_port_margining_params *params,
u32 *results)
{
u32 val;
int ret;
val = lanes;
if (timing)
if (WARN_ON_ONCE(!params))
return -EINVAL;
val = params->lanes;
if (params->time)
val |= USB4_MARGIN_HW_TIME;
if (right_high)
if (params->right_high)
val |= USB4_MARGIN_HW_RH;
if (ber_level)
val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) &
USB4_MARGIN_HW_BER_MASK;
if (params->ber_level)
val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level);
if (params->optional_voltage_offset_range)
val |= USB4_MARGIN_HW_OPT_VOLTAGE;
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
sizeof(val));
@ -1698,38 +1698,46 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
* @port: USB4 port
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @lanes: Which lanes to run (must match the port capabilities). Can be
* %0, %1 or %7.
* @timing: Perform timing margining instead of voltage
* @right_high: Use Right/high margin instead of left/low
* @counter: What to do with the error counter
* @params: Parameters for USB4 software margining
* @results: Data word for the operation completion data
*
* Runs software lane margining on USB4 port. Read back the error
* counters by calling usb4_port_sw_margin_errors(). Returns %0 in
* success and negative errno otherwise.
*/
int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, bool timing,
bool right_high, u32 counter)
u8 index, const struct usb4_port_margining_params *params,
u32 *results)
{
u32 val;
int ret;
val = lanes;
if (timing)
if (WARN_ON_ONCE(!params))
return -EINVAL;
val = params->lanes;
if (params->time)
val |= USB4_MARGIN_SW_TIME;
if (right_high)
if (params->optional_voltage_offset_range)
val |= USB4_MARGIN_SW_OPT_VOLTAGE;
if (params->right_high)
val |= USB4_MARGIN_SW_RH;
val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) &
USB4_MARGIN_SW_COUNTER_MASK;
val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter);
val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset);
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
sizeof(val));
if (ret)
return ret;
return usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
if (ret)
return ret;
return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results,
sizeof(*results));
}
/**

View File

@ -37,8 +37,7 @@ struct cdns3_wrap {
#define PCI_DRIVER_NAME "cdns3-pci-usbss"
#define PLAT_DRIVER_NAME "cdns-usb3"
#define CDNS_VENDOR_ID 0x17cd
#define CDNS_DEVICE_ID 0x0100
#define PCI_DEVICE_ID_CDNS_USB3 0x0100
static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
{
@ -190,7 +189,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev)
}
static const struct pci_device_id cdns3_pci_ids[] = {
{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
{ PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USB3) },
{ 0, }
};

View File

@ -28,10 +28,11 @@
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
#define PLAT_DRIVER_NAME "cdns-usbssp"
#define CDNS_VENDOR_ID 0x17cd
#define CDNS_DEVICE_ID 0x0200
#define CDNS_DRD_ID 0x0100
#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
#define PCI_DEVICE_ID_CDNS_USB3 0x0100
#define PCI_DEVICE_ID_CDNS_UDC 0x0200
#define PCI_CLASS_SERIAL_USB_CDNS_USB3 (PCI_CLASS_SERIAL_USB << 8 | 0x80)
#define PCI_CLASS_SERIAL_USB_CDNS_UDC PCI_CLASS_SERIAL_USB_DEVICE
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
{
@ -40,10 +41,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
* Platform has two function. The fist keeps resources for
* Host/Device while the secon keeps resources for DRD/OTG.
*/
if (pdev->device == CDNS_DEVICE_ID)
return pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL);
else if (pdev->device == CDNS_DRD_ID)
return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL);
if (pdev->device == PCI_DEVICE_ID_CDNS_UDC)
return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USB3, NULL);
if (pdev->device == PCI_DEVICE_ID_CDNS_USB3)
return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_UDC, NULL);
return NULL;
}
@ -220,12 +221,12 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = {
};
static const struct pci_device_id cdnsp_pci_ids[] = {
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
CDNS_DRD_IF, PCI_ANY_ID },
{ PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID,
CDNS_DRD_IF, PCI_ANY_ID },
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC),
.class = PCI_CLASS_SERIAL_USB_CDNS_UDC },
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC),
.class = PCI_CLASS_SERIAL_USB_CDNS_USB3 },
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB3),
.class = PCI_CLASS_SERIAL_USB_CDNS_USB3 },
{ 0, }
};

View File

@ -718,7 +718,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
seg = cdnsp_trb_in_td(pdev, cur_td->start_seg, cur_td->first_trb,
cur_td->last_trb, hw_deq);
if (seg && (pep->ep_state & EP_ENABLED))
if (seg && (pep->ep_state & EP_ENABLED) &&
!(pep->ep_state & EP_DIS_IN_RROGRESS))
cdnsp_find_new_dequeue_state(pdev, pep, preq->request.stream_id,
cur_td, &deq_state);
else
@ -736,7 +737,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
* During disconnecting all endpoint will be disabled so we don't
* have to worry about updating dequeue pointer.
*/
if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING) {
if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING ||
pep->ep_state & EP_DIS_IN_RROGRESS) {
status = -ESHUTDOWN;
ret = cdnsp_cmd_set_deq(pdev, pep, &deq_state);
}

View File

@ -62,7 +62,9 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
.resume_quirk = xhci_cdns3_resume_quirk,
};
static const struct xhci_plat_priv xhci_plat_cdnsp_xhci;
static const struct xhci_plat_priv xhci_plat_cdnsp_xhci = {
.quirks = XHCI_CDNS_SCTX_QUIRK,
};
static int __cdns_host_init(struct cdns *cdns)
{

View File

@ -128,7 +128,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
* In case the fsl,usbmisc property is not present this device doesn't
* need usbmisc. Return NULL (which is no error here)
*/
if (!of_get_property(np, "fsl,usbmisc", NULL))
if (!of_property_present(np, "fsl,usbmisc"))
return NULL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);

View File

@ -18,7 +18,7 @@ struct npcm_udc_data {
struct ci_hdrc_platform_data pdata;
};
static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned int event)
{
struct device *dev = ci->dev->parent;
@ -28,7 +28,7 @@ static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
hw_write(ci, OP_USBMODE, 0xffffffff, 0x0);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event (%d)\n",event);
dev_dbg(dev, "unknown ci_hdrc event (%d)\n", event);
break;
}

View File

@ -86,7 +86,7 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
hw_write(ci, OP_ENDPTLISTADDR, ~0, dma);
/* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0,
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI);
} else {
hw_write(ci, OP_USBINTR, ~0, 0);
}
@ -877,6 +877,7 @@ __releases(ci->lock)
__acquires(ci->lock)
{
int retval;
u32 intr;
spin_unlock(&ci->lock);
if (ci->gadget.speed != USB_SPEED_UNKNOWN)
@ -890,6 +891,11 @@ __acquires(ci->lock)
if (retval)
goto done;
/* clear SLI */
hw_write(ci, OP_USBSTS, USBi_SLI, USBi_SLI);
intr = hw_read(ci, OP_USBINTR, ~0);
hw_write(ci, OP_USBINTR, ~0, intr | USBi_SLI);
ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC);
if (ci->status == NULL)
retval = -ENOMEM;

View File

@ -962,10 +962,12 @@ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss)
struct acm *acm = tty->driver_data;
ss->line = acm->minor;
mutex_lock(&acm->port.mutex);
ss->close_delay = jiffies_to_msecs(acm->port.close_delay) / 10;
ss->closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
jiffies_to_msecs(acm->port.closing_wait) / 10;
mutex_unlock(&acm->port.mutex);
return 0;
}

View File

@ -754,7 +754,7 @@ static struct urb *usbtmc_create_urb(void)
if (!urb)
return NULL;
dmabuf = kmalloc(bufsize, GFP_KERNEL);
dmabuf = kzalloc(bufsize, GFP_KERNEL);
if (!dmabuf) {
usb_free_urb(urb);
return NULL;

View File

@ -107,19 +107,18 @@ EXPORT_SYMBOL_GPL(usb_speed_string);
*/
enum usb_device_speed usb_get_maximum_speed(struct device *dev)
{
const char *maximum_speed;
const char *p = "maximum-speed";
int ret;
ret = device_property_read_string(dev, "maximum-speed", &maximum_speed);
if (ret < 0)
return USB_SPEED_UNKNOWN;
ret = match_string(ssp_rate, ARRAY_SIZE(ssp_rate), maximum_speed);
ret = device_property_match_property_string(dev, p, ssp_rate, ARRAY_SIZE(ssp_rate));
if (ret > 0)
return USB_SPEED_SUPER_PLUS;
ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
ret = device_property_match_property_string(dev, p, speed_names, ARRAY_SIZE(speed_names));
if (ret > 0)
return ret;
return USB_SPEED_UNKNOWN;
}
EXPORT_SYMBOL_GPL(usb_get_maximum_speed);
@ -276,14 +275,13 @@ EXPORT_SYMBOL_GPL(usb_decode_interval);
*/
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
{
struct device_node *controller = NULL;
struct device_node *controller;
struct of_phandle_args args;
const char *dr_mode;
int index;
int err;
do {
controller = of_find_node_with_property(controller, "phys");
for_each_node_with_property(controller, "phys") {
if (!of_device_is_available(controller))
continue;
index = 0;
@ -306,7 +304,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
goto finish;
index++;
} while (args.np);
} while (controller);
}
finish:
err = of_property_read_string(controller, "dr_mode", &dr_mode);

View File

@ -142,6 +142,53 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
}
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
/**
* usb_acpi_add_usb4_devlink - add device link to USB4 Host Interface for tunneled USB3 devices
*
* @udev: Tunneled USB3 device connected to a roothub.
*
* Adds a device link between a tunneled USB3 device and the USB4 Host Interface
* device to ensure correct runtime PM suspend and resume order. This function
* should only be called for tunneled USB3 devices.
* The USB4 Host Interface this tunneled device depends on is found from the roothub
* port ACPI device specific data _DSD entry.
*
* Return: negative error code on failure, 0 otherwise
*/
static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
{
const struct device_link *link;
struct usb_port *port_dev;
struct usb_hub *hub;
if (!udev->parent || udev->parent->parent)
return 0;
hub = usb_hub_to_struct_hub(udev->parent);
port_dev = hub->ports[udev->portnum - 1];
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
fwnode_find_reference(dev_fwnode(&port_dev->dev), "usb4-host-interface", 0);
if (IS_ERR(nhi_fwnode))
return 0;
link = device_link_add(&port_dev->child->dev, nhi_fwnode->dev,
DL_FLAG_AUTOREMOVE_CONSUMER |
DL_FLAG_RPM_ACTIVE |
DL_FLAG_PM_RUNTIME);
if (!link) {
dev_err(&port_dev->dev, "Failed to created device link from %s to %s\n",
dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
return -EINVAL;
}
dev_dbg(&port_dev->dev, "Created device link from %s to %s\n",
dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
return 0;
}
/*
* Private to usb-acpi, all the core needs to know is that
* port_dev->location is non-zero when it has been set by the firmware.
@ -262,6 +309,12 @@ usb_acpi_find_companion_for_device(struct usb_device *udev)
if (!hub)
return NULL;
/* Tunneled USB3 devices depend on USB4 Host Interface, set device link to it */
if (udev->speed >= USB_SPEED_SUPER &&
udev->tunnel_mode != USB_LINK_NATIVE)
usb_acpi_add_usb4_devlink(udev);
/*
* This is an embedded USB device connected to a port and such
* devices share port's ACPI companion.

View File

@ -702,6 +702,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, uframe_sched);
print_param(seq, p, external_id_pin_ctl);
print_param(seq, p, power_down);
print_param(seq, p, no_clock_gating);
print_param(seq, p, lpm);
print_param(seq, p, lpm_clock_gating);
print_param(seq, p, besl);

View File

@ -127,6 +127,15 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
role = USB_ROLE_DEVICE;
}
if ((IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) ||
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) &&
dwc2_is_device_mode(hsotg) &&
hsotg->lx_state == DWC2_L2 &&
hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
hsotg->bus_suspended &&
!hsotg->params.no_clock_gating)
dwc2_gadget_exit_clock_gating(hsotg, 0);
if (role == USB_ROLE_HOST) {
already = dwc2_ovr_avalid(hsotg, true);
} else if (role == USB_ROLE_DEVICE) {

View File

@ -23,6 +23,7 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = 0x10;
p->no_clock_gating = true;
}
static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
@ -352,6 +353,7 @@ const struct of_device_id dwc2_of_match_table[] = {
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
const struct acpi_device_id dwc2_acpi_match[] = {
/* This ID refers to the same USB IP as of_device_id brcm,bcm2835-usb */
{ "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params },
{ },
};

View File

@ -469,18 +469,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
hsotg->irq = platform_get_irq(dev, 0);
if (hsotg->irq < 0)
return hsotg->irq;
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
hsotg->irq);
retval = devm_request_irq(hsotg->dev, hsotg->irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
if (IS_ERR(hsotg->vbus_supply)) {
retval = PTR_ERR(hsotg->vbus_supply);
@ -524,6 +512,20 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval)
goto error;
hsotg->irq = platform_get_irq(dev, 0);
if (hsotg->irq < 0) {
retval = hsotg->irq;
goto error;
}
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
hsotg->irq);
retval = devm_request_irq(hsotg->dev, hsotg->irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
goto error;
/*
* For OTG cores, set the force mode bits to reflect the value
* of dr_mode. Force mode bits should not be touched at any

View File

@ -5,6 +5,7 @@
* Copyright (c) 2020 NXP.
*/
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -96,7 +97,8 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
writel(value, dwc3_imx->glue_base + USB_CTRL1);
}
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
pm_message_t msg)
{
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
u32 val;
@ -106,12 +108,14 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
USB_WAKEUP_U3_EN | USB_WAKEUP_DPDM_EN;
else if (dwc3->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci) {
val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN;
if (PMSG_IS_AUTO(msg))
val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN;
} else {
val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
USB_WAKEUP_VBUS_SRC_SESS_VAL;
}
writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
}
@ -144,10 +148,21 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
return IRQ_HANDLED;
}
static int dwc3_imx8mp_set_software_node(struct device *dev)
{
struct property_entry props[3] = { 0 };
int prop_idx = 0;
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk");
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk");
return device_create_managed_software_node(dev, props, NULL);
}
static int dwc3_imx8mp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *dwc3_np, *node = dev->of_node;
struct device_node *node = dev->of_node;
struct dwc3_imx8mp *dwc3_imx;
struct resource *res;
int err, irq;
@ -178,39 +193,26 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
return PTR_ERR(dwc3_imx->glue_base);
}
dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
if (IS_ERR(dwc3_imx->hsio_clk)) {
err = PTR_ERR(dwc3_imx->hsio_clk);
dev_err(dev, "Failed to get hsio clk, err=%d\n", err);
return err;
}
dwc3_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio");
if (IS_ERR(dwc3_imx->hsio_clk))
return dev_err_probe(dev, PTR_ERR(dwc3_imx->hsio_clk),
"Failed to get hsio clk\n");
err = clk_prepare_enable(dwc3_imx->hsio_clk);
if (err) {
dev_err(dev, "Failed to enable hsio clk, err=%d\n", err);
return err;
}
dwc3_imx->suspend_clk = devm_clk_get(dev, "suspend");
if (IS_ERR(dwc3_imx->suspend_clk)) {
err = PTR_ERR(dwc3_imx->suspend_clk);
dev_err(dev, "Failed to get suspend clk, err=%d\n", err);
goto disable_hsio_clk;
}
err = clk_prepare_enable(dwc3_imx->suspend_clk);
if (err) {
dev_err(dev, "Failed to enable suspend clk, err=%d\n", err);
goto disable_hsio_clk;
}
dwc3_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend");
if (IS_ERR(dwc3_imx->suspend_clk))
return dev_err_probe(dev, PTR_ERR(dwc3_imx->suspend_clk),
"Failed to get suspend clk\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
err = irq;
goto disable_clks;
}
if (irq < 0)
return irq;
dwc3_imx->irq = irq;
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(node,
"snps,dwc3");
if (!dwc3_np)
return dev_err_probe(dev, -ENODEV, "failed to find dwc3 core child\n");
imx8mp_configure_glue(dwc3_imx);
pm_runtime_set_active(dev);
@ -219,17 +221,17 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
if (err < 0)
goto disable_rpm;
dwc3_np = of_get_compatible_child(node, "snps,dwc3");
if (!dwc3_np) {
err = dwc3_imx8mp_set_software_node(dev);
if (err) {
err = -ENODEV;
dev_err(dev, "failed to find dwc3 core child\n");
dev_err(dev, "failed to create software node\n");
goto disable_rpm;
}
err = of_platform_populate(node, NULL, NULL, dev);
if (err) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
goto err_node_put;
goto disable_rpm;
}
dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
@ -238,7 +240,6 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
err = -ENODEV;
goto depopulate;
}
of_node_put(dwc3_np);
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
@ -254,51 +255,39 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
depopulate:
of_platform_depopulate(dev);
err_node_put:
of_node_put(dwc3_np);
disable_rpm:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
disable_clks:
clk_disable_unprepare(dwc3_imx->suspend_clk);
disable_hsio_clk:
clk_disable_unprepare(dwc3_imx->hsio_clk);
return err;
}
static void dwc3_imx8mp_remove(struct platform_device *pdev)
{
struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
pm_runtime_get_sync(dev);
of_platform_depopulate(dev);
clk_disable_unprepare(dwc3_imx->suspend_clk);
clk_disable_unprepare(dwc3_imx->hsio_clk);
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
}
static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
pm_message_t msg)
static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
if (dwc3_imx->pm_suspended)
return 0;
/* Wakeup enable */
if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc3_imx->dev))
dwc3_imx8mp_wakeup_enable(dwc3_imx);
dwc3_imx8mp_wakeup_enable(dwc3_imx, msg);
dwc3_imx->pm_suspended = true;
return 0;
}
static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
pm_message_t msg)
static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
int ret = 0;
@ -331,7 +320,7 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
return ret;
}
static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
static int dwc3_imx8mp_pm_suspend(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
int ret;
@ -349,7 +338,7 @@ static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
return ret;
}
static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
static int dwc3_imx8mp_pm_resume(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
int ret;
@ -379,7 +368,7 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
return ret;
}
static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
static int dwc3_imx8mp_runtime_suspend(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
@ -388,7 +377,7 @@ static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND);
}
static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
static int dwc3_imx8mp_runtime_resume(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
@ -398,9 +387,9 @@ static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend,
dwc3_imx8mp_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, dwc3_imx8mp_runtime_resume,
NULL)
};
static const struct of_device_id dwc3_imx8mp_of_match[] = {
@ -414,7 +403,7 @@ static struct platform_driver dwc3_imx8mp_driver = {
.remove_new = dwc3_imx8mp_remove,
.driver = {
.name = "imx8mp-dwc3",
.pm = &dwc3_imx8mp_dev_pm_ops,
.pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops),
.of_match_table = dwc3_imx8mp_of_match,
},
};

View File

@ -419,7 +419,7 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
int ref_clk_sel, ref_clk_fsel, mpll_mul;
int power_active_low, power_gpio;
int err, len;
u32 clock_rate;
u32 clock_rate, gpio_pwr[3];
if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) {
dev_err(dev, "No UCTL \"refclk-frequency\"\n");
@ -476,21 +476,10 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
power_gpio = DWC3_GPIO_POWER_NONE;
power_active_low = 0;
if (of_find_property(node, "power", &len)) {
u32 gpio_pwr[3];
switch (len) {
case 8:
of_property_read_u32_array(node, "power", gpio_pwr, 2);
break;
case 12:
of_property_read_u32_array(node, "power", gpio_pwr, 3);
len = of_property_read_variable_u32_array(node, "power", gpio_pwr, 2, 3);
if (len > 0) {
if (len == 3)
power_active_low = gpio_pwr[2] & 0x01;
break;
default:
dev_err(dev, "invalid power configuration\n");
return -EINVAL;
}
power_gpio = gpio_pwr[1];
}

View File

@ -4,6 +4,7 @@
* Inspired by dwc3-of-simple.c
*/
#include <linux/cleanup.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/clk.h>
@ -702,11 +703,12 @@ static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
{
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node, *dwc3_np;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
"snps,dwc3");
if (!dwc3_np) {
dev_err(dev, "failed to find dwc3 core child\n");
return -ENODEV;
@ -715,7 +717,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
goto node_put;
return ret;
}
qcom->dwc3 = of_find_device_by_node(dwc3_np);
@ -725,9 +727,6 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
of_platform_depopulate(dev);
}
node_put:
of_node_put(dwc3_np);
return ret;
}
@ -736,7 +735,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct dwc3_qcom *qcom;
struct resource *res;
int ret, i;
bool ignore_pipe_clk;
bool wakeup_source;
@ -774,9 +772,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
goto reset_assert;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
qcom->qscratch_base = devm_ioremap_resource(dev, res);
qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qcom->qscratch_base)) {
ret = PTR_ERR(qcom->qscratch_base);
goto clk_disable;

View File

@ -6,6 +6,7 @@
*
*/
#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
@ -173,23 +174,20 @@ static const char *const speed_names[] = {
static enum usb_device_speed __get_dwc3_maximum_speed(struct device_node *np)
{
struct device_node *dwc3_np;
const char *maximum_speed;
int ret;
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
"snps,dwc3");
if (!dwc3_np)
return USB_SPEED_UNKNOWN;
ret = of_property_read_string(dwc3_np, "maximum-speed", &maximum_speed);
if (ret < 0)
goto out;
return USB_SPEED_UNKNOWN;
ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
out:
of_node_put(dwc3_np);
return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
}
@ -276,7 +274,6 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
struct device_node *node = dev->of_node;
struct platform_device *dwc3_pdev;
struct device *dwc3_dev;
struct device_node *dwc3_node;
enum usb_dr_mode dr_mode;
int ret = 0;
@ -290,7 +287,8 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
return ret;
}
dwc3_node = of_get_compatible_child(node, "snps,dwc3");
struct device_node *dwc3_node __free(device_node) = of_get_compatible_child(node,
"snps,dwc3");
if (!dwc3_node) {
dev_err(dev, "failed to find dwc3 core node\n");
ret = -ENODEV;
@ -301,7 +299,7 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
if (!dwc3_pdev) {
dev_err(dev, "failed to find dwc3 core platform_device\n");
ret = -ENODEV;
goto err_node_put;
goto depopulate;
}
dwc3_dev = &dwc3_pdev->dev;
@ -343,14 +341,11 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
switch_usb2_role(rtk, rtk->cur_role);
platform_device_put(dwc3_pdev);
of_node_put(dwc3_node);
return 0;
err_pdev_put:
platform_device_put(dwc3_pdev);
err_node_put:
of_node_put(dwc3_node);
depopulate:
of_platform_depopulate(dev);
@ -363,30 +358,18 @@ static int dwc3_rtk_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *regs;
int ret = 0;
rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL);
if (!rtk) {
ret = -ENOMEM;
goto out;
}
if (!rtk)
return -ENOMEM;
platform_set_drvdata(pdev, rtk);
rtk->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
ret = -ENODEV;
goto out;
}
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto out;
}
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
rtk->regs = regs;
rtk->regs_size = resource_size(res);
@ -394,16 +377,11 @@ static int dwc3_rtk_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
rtk->pm_base = devm_ioremap_resource(dev, res);
if (IS_ERR(rtk->pm_base)) {
ret = PTR_ERR(rtk->pm_base);
goto out;
}
if (IS_ERR(rtk->pm_base))
return PTR_ERR(rtk->pm_base);
}
ret = dwc3_rtk_probe_dwc3_core(rtk);
out:
return ret;
return dwc3_rtk_probe_dwc3_core(rtk);
}
static void dwc3_rtk_remove(struct platform_device *pdev)

View File

@ -14,6 +14,7 @@
* Inspired by dwc3-omap.c and dwc3-exynos.c.
*/
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -197,7 +198,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
struct st_dwc3 *dwc3_data;
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node, *child;
struct device_node *node = dev->of_node;
struct platform_device *child_pdev;
struct regmap *regmap;
int ret;
@ -224,15 +225,21 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->syscfg_reg_off = res->start;
dev_vdbg(&pdev->dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
struct device_node *child __free(device_node) = of_get_compatible_child(node,
"snps,dwc3");
if (!child) {
dev_err(dev, "failed to find dwc3 core node\n");
return -ENODEV;
}
dwc3_data->rstc_pwrdn =
devm_reset_control_get_exclusive(dev, "powerdown");
if (IS_ERR(dwc3_data->rstc_pwrdn)) {
dev_err(&pdev->dev, "could not get power controller\n");
return PTR_ERR(dwc3_data->rstc_pwrdn);
}
if (IS_ERR(dwc3_data->rstc_pwrdn))
return dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_pwrdn),
"could not get power controller\n");
/* Manage PowerDown */
reset_control_deassert(dwc3_data->rstc_pwrdn);
@ -240,26 +247,19 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->rstc_rst =
devm_reset_control_get_shared(dev, "softreset");
if (IS_ERR(dwc3_data->rstc_rst)) {
dev_err(&pdev->dev, "could not get reset controller\n");
ret = PTR_ERR(dwc3_data->rstc_rst);
ret = dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_rst),
"could not get reset controller\n");
goto undo_powerdown;
}
/* Manage SoftReset */
reset_control_deassert(dwc3_data->rstc_rst);
child = of_get_compatible_child(node, "snps,dwc3");
if (!child) {
dev_err(&pdev->dev, "failed to find dwc3 core node\n");
ret = -ENODEV;
goto err_node_put;
}
/* Allocate and initialize the core */
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
goto err_node_put;
goto undo_softreset;
}
child_pdev = of_find_device_by_node(child);
@ -270,7 +270,6 @@ static int st_dwc3_probe(struct platform_device *pdev)
}
dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev);
of_node_put(child);
platform_device_put(child_pdev);
/*
@ -282,8 +281,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
ret = st_dwc3_drd_init(dwc3_data);
if (ret) {
dev_err(dev, "drd initialisation failed\n");
of_platform_depopulate(dev);
goto undo_softreset;
goto depopulate;
}
/* ST glue logic init */
@ -294,8 +292,6 @@ static int st_dwc3_probe(struct platform_device *pdev)
depopulate:
of_platform_depopulate(dev);
err_node_put:
of_node_put(child);
undo_softreset:
reset_control_assert(dwc3_data->rstc_rst);
undo_powerdown:

View File

@ -285,11 +285,8 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
return -ENOMEM;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
dev_err_probe(dev, ret, "failed to map registers\n");
return ret;
}
if (IS_ERR(regs))
return dev_err_probe(dev, PTR_ERR(regs), "failed to map registers\n");
match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node);

View File

@ -6,13 +6,13 @@
#include <linux/kstrtox.h>
#include <linux/nls.h>
#include <linux/usb/composite.h>
#include <linux/usb/func_utils.h>
#include <linux/usb/gadget_configfs.h>
#include <linux/usb/webusb.h>
#include "configfs.h"
#include "u_f.h"
#include "u_os_desc.h"
int check_user_usb_string(const char *name,
static int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
{
u16 num;
@ -902,7 +902,7 @@ static struct configfs_group_operations gadget_language_langid_group_ops = {
.drop_item = gadget_language_string_drop,
};
static struct config_item_type gadget_language_type = {
static const struct config_item_type gadget_language_type = {
.ct_item_ops = &gadget_language_langid_item_ops,
.ct_group_ops = &gadget_language_langid_group_ops,
.ct_attrs = gadget_language_langid_attrs,
@ -961,7 +961,7 @@ static struct configfs_group_operations gadget_language_group_ops = {
.drop_item = &gadget_language_drop,
};
static struct config_item_type gadget_language_strings_type = {
static const struct config_item_type gadget_language_strings_type = {
.ct_group_ops = &gadget_language_group_ops,
.ct_owner = THIS_MODULE,
};
@ -1106,7 +1106,7 @@ static struct configfs_attribute *webusb_attrs[] = {
NULL,
};
static struct config_item_type webusb_type = {
static const struct config_item_type webusb_type = {
.ct_attrs = webusb_attrs,
.ct_owner = THIS_MODULE,
};
@ -1263,7 +1263,7 @@ static struct configfs_item_operations os_desc_ops = {
.drop_link = os_desc_unlink,
};
static struct config_item_type os_desc_type = {
static const struct config_item_type os_desc_type = {
.ct_item_ops = &os_desc_ops,
.ct_attrs = os_desc_attrs,
.ct_owner = THIS_MODULE,

View File

@ -41,6 +41,7 @@ struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
u8 port_num;
u8 bInterfaceProtocol;
u8 pending;
@ -89,7 +90,7 @@ acm_iad_descriptor = {
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .bFunctionProtocol = DYNAMIC */
/* .iFunction = DYNAMIC */
};
@ -101,7 +102,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = {
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .bInterfaceProtocol = DYNAMIC */
/* .iInterface = DYNAMIC */
};
@ -663,6 +664,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
acm->notify = ep;
acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol;
acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol;
/* allocate notification */
acm->notify_req = gs_alloc_req(ep,
sizeof(struct usb_cdc_notification) + 2,
@ -719,8 +723,14 @@ static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
static void acm_free_func(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
struct f_serial_opts *opts;
opts = container_of(f->fi, struct f_serial_opts, func_inst);
kfree(acm);
mutex_lock(&opts->lock);
opts->instances--;
mutex_unlock(&opts->lock);
}
static void acm_resume(struct usb_function *f)
@ -761,7 +771,11 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
acm->port.func.disable = acm_disable;
opts = container_of(fi, struct f_serial_opts, func_inst);
mutex_lock(&opts->lock);
acm->port_num = opts->port_num;
acm->bInterfaceProtocol = opts->protocol;
opts->instances++;
mutex_unlock(&opts->lock);
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
acm->port.func.resume = acm_resume;
@ -812,11 +826,42 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
CONFIGFS_ATTR_RO(f_acm_, port_num);
static ssize_t f_acm_protocol_show(struct config_item *item, char *page)
{
return sprintf(page, "%u\n", to_f_serial_opts(item)->protocol);
}
static ssize_t f_acm_protocol_store(struct config_item *item,
const char *page, size_t count)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
int ret;
mutex_lock(&opts->lock);
if (opts->instances) {
ret = -EBUSY;
goto out;
}
ret = kstrtou8(page, 0, &opts->protocol);
if (ret)
goto out;
ret = count;
out:
mutex_unlock(&opts->lock);
return ret;
}
CONFIGFS_ATTR(f_acm_, protocol);
static struct configfs_attribute *acm_attrs[] = {
#ifdef CONFIG_U_SERIAL_CONSOLE
&f_acm_attr_console,
#endif
&f_acm_attr_port_num,
&f_acm_attr_protocol,
NULL,
};
@ -832,6 +877,7 @@ static void acm_free_instance(struct usb_function_instance *fi)
opts = container_of(fi, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
mutex_destroy(&opts->lock);
kfree(opts);
}
@ -843,7 +889,9 @@ static struct usb_function_instance *acm_alloc_instance(void)
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER;
opts->func_inst.free_func_inst = acm_free_instance;
mutex_init(&opts->lock);
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);

View File

@ -33,6 +33,7 @@
#include <linux/usb/ccid.h>
#include <linux/usb/composite.h>
#include <linux/usb/functionfs.h>
#include <linux/usb/func_utils.h>
#include <linux/aio.h>
#include <linux/kthread.h>
@ -40,7 +41,6 @@
#include <linux/eventfd.h>
#include "u_fs.h"
#include "u_f.h"
#include "u_os_desc.h"
#include "configfs.h"
@ -2478,7 +2478,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
static int __must_check ffs_do_single_desc(char *data, unsigned len,
ffs_entity_callback entity,
void *priv, int *current_class)
void *priv, int *current_class, int *current_subclass)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
@ -2535,6 +2535,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
if (ds->iInterface)
__entity(STRING, ds->iInterface);
*current_class = ds->bInterfaceClass;
*current_subclass = ds->bInterfaceSubClass;
}
break;
@ -2559,6 +2560,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
if (length != sizeof(struct ccid_descriptor))
goto inv_length;
break;
} else if (*current_class == USB_CLASS_APP_SPEC &&
*current_subclass == USB_SUBCLASS_DFU) {
pr_vdebug("dfu functional descriptor\n");
if (length != sizeof(struct usb_dfu_functional_descriptor))
goto inv_length;
break;
} else {
pr_vdebug("unknown descriptor: %d for class %d\n",
_ds->bDescriptorType, *current_class);
@ -2621,6 +2628,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
const unsigned _len = len;
unsigned long num = 0;
int current_class = -1;
int current_subclass = -1;
for (;;) {
int ret;
@ -2640,7 +2648,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
return _len - len;
ret = ffs_do_single_desc(data, len, entity, priv,
&current_class);
&current_class, &current_subclass);
if (ret < 0) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;

View File

@ -15,13 +15,21 @@
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/usb/func_utils.h>
#include <linux/usb/g_hid.h>
#include <uapi/linux/usb/g_hid.h>
#include "u_f.h"
#include "u_hid.h"
#define HIDG_MINORS 4
/*
* Most operating systems seem to allow for 5000ms timeout, we will allow
* userspace half that time to respond before we return an empty report.
*/
#define GET_REPORT_TIMEOUT_MS 2500
static int major, minors;
static const struct class hidg_class = {
@ -31,6 +39,11 @@ static const struct class hidg_class = {
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
struct report_entry {
struct usb_hidg_report report_data;
struct list_head node;
};
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
@ -75,6 +88,19 @@ struct f_hidg {
wait_queue_head_t write_queue;
struct usb_request *req;
/* get report */
struct usb_request *get_req;
struct usb_hidg_report get_report;
bool get_report_returned;
int get_report_req_report_id;
int get_report_req_report_length;
spinlock_t get_report_spinlock;
wait_queue_head_t get_queue; /* Waiting for userspace response */
wait_queue_head_t get_id_queue; /* Get ID came in */
struct work_struct work;
struct workqueue_struct *workqueue;
struct list_head report_list;
struct device dev;
struct cdev cdev;
struct usb_function func;
@ -524,6 +550,174 @@ release_write_pending:
return status;
}
static struct report_entry *f_hidg_search_for_report(struct f_hidg *hidg, u8 report_id)
{
struct list_head *ptr;
struct report_entry *entry;
list_for_each(ptr, &hidg->report_list) {
entry = list_entry(ptr, struct report_entry, node);
if (entry->report_data.report_id == report_id)
return entry;
}
return NULL;
}
static void get_report_workqueue_handler(struct work_struct *work)
{
struct f_hidg *hidg = container_of(work, struct f_hidg, work);
struct usb_composite_dev *cdev = hidg->func.config->cdev;
struct usb_request *req;
struct report_entry *ptr;
unsigned long flags;
int status = 0;
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
req = hidg->get_req;
if (!req) {
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
return;
}
req->zero = 0;
req->length = min_t(unsigned int, min_t(unsigned int, hidg->get_report_req_report_length,
hidg->report_length),
MAX_REPORT_LENGTH);
/* Check if there is a response available for immediate response */
ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
if (ptr && !ptr->report_data.userspace_req) {
/* Report exists in list and it is to be used for immediate response */
req->buf = ptr->report_data.data;
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
hidg->get_report_returned = true;
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
} else {
/*
* Report does not exist in list or should not be immediately sent
* i.e. give userspace time to respond
*/
hidg->get_report_returned = false;
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
wake_up(&hidg->get_id_queue);
#define GET_REPORT_COND (!hidg->get_report_returned)
/* Wait until userspace has responded or timeout */
status = wait_event_interruptible_timeout(hidg->get_queue, !GET_REPORT_COND,
msecs_to_jiffies(GET_REPORT_TIMEOUT_MS));
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
req = hidg->get_req;
if (!req) {
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
return;
}
if (status == 0 && !hidg->get_report_returned) {
/* GET_REPORT request was not serviced by userspace within timeout period */
VDBG(cdev, "get_report : userspace timeout.\n");
hidg->get_report_returned = true;
}
/* Search again for report ID in list and respond to GET_REPORT request */
ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
if (ptr) {
/*
* Either get an updated response just serviced by userspace
* or send the latest response in the list
*/
req->buf = ptr->report_data.data;
} else {
/* If there are no prevoiusly sent reports send empty report */
req->buf = hidg->get_report.data;
memset(req->buf, 0x0, req->length);
}
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
}
if (status < 0)
VDBG(cdev, "usb_ep_queue error on ep0 responding to GET_REPORT\n");
}
static int f_hidg_get_report_id(struct file *file, __u8 __user *buffer)
{
struct f_hidg *hidg = file->private_data;
int ret = 0;
ret = put_user(hidg->get_report_req_report_id, buffer);
return ret;
}
static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer)
{
struct f_hidg *hidg = file->private_data;
struct usb_composite_dev *cdev = hidg->func.config->cdev;
unsigned long flags;
struct report_entry *entry;
struct report_entry *ptr;
__u8 report_id;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
if (copy_from_user(&entry->report_data, buffer,
sizeof(struct usb_hidg_report))) {
ERROR(cdev, "copy_from_user error\n");
kfree(entry);
return -EINVAL;
}
report_id = entry->report_data.report_id;
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
ptr = f_hidg_search_for_report(hidg, report_id);
if (ptr) {
/* Report already exists in list - update it */
if (copy_from_user(&ptr->report_data, buffer,
sizeof(struct usb_hidg_report))) {
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
ERROR(cdev, "copy_from_user error\n");
kfree(entry);
return -EINVAL;
}
kfree(entry);
} else {
/* Report does not exist in list - add it */
list_add_tail(&entry->node, &hidg->report_list);
}
/* If there is no response pending then do nothing further */
if (hidg->get_report_returned) {
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
return 0;
}
/* If this userspace response serves the current pending report */
if (hidg->get_report_req_report_id == report_id) {
hidg->get_report_returned = true;
wake_up(&hidg->get_queue);
}
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
return 0;
}
static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg)
{
switch (code) {
case GADGET_HID_READ_GET_REPORT_ID:
return f_hidg_get_report_id(file, (__u8 __user *)arg);
case GADGET_HID_WRITE_GET_REPORT:
return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg);
default:
return -ENOTTY;
}
}
static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
{
struct f_hidg *hidg = file->private_data;
@ -531,6 +725,8 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
poll_wait(file, &hidg->read_queue, wait);
poll_wait(file, &hidg->write_queue, wait);
poll_wait(file, &hidg->get_queue, wait);
poll_wait(file, &hidg->get_id_queue, wait);
if (WRITE_COND)
ret |= EPOLLOUT | EPOLLWRNORM;
@ -543,12 +739,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
ret |= EPOLLIN | EPOLLRDNORM;
}
if (GET_REPORT_COND)
ret |= EPOLLPRI;
return ret;
}
#undef WRITE_COND
#undef READ_COND_SSREPORT
#undef READ_COND_INTOUT
#undef GET_REPORT_COND
static int f_hidg_release(struct inode *inode, struct file *fd)
{
@ -641,6 +841,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
wake_up(&hidg->read_queue);
}
static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req)
{
}
static int hidg_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
@ -649,6 +853,7 @@ static int hidg_setup(struct usb_function *f,
struct usb_request *req = cdev->req;
int status = 0;
__u16 value, length;
unsigned long flags;
value = __le16_to_cpu(ctrl->wValue);
length = __le16_to_cpu(ctrl->wLength);
@ -660,14 +865,20 @@ static int hidg_setup(struct usb_function *f,
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_REPORT):
VDBG(cdev, "get_report\n");
VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength);
/* send an empty report */
length = min_t(unsigned, length, hidg->report_length);
memset(req->buf, 0x0, length);
/*
* Update GET_REPORT ID so that an ioctl can be used to determine what
* GET_REPORT the request was actually for.
*/
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
hidg->get_report_req_report_id = value & 0xff;
hidg->get_report_req_report_length = length;
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
goto respond;
break;
queue_work(hidg->workqueue, &hidg->work);
return status;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL):
@ -793,6 +1004,14 @@ static void hidg_disable(struct usb_function *f)
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
}
spin_lock_irqsave(&hidg->get_report_spinlock, flags);
if (!hidg->get_report_returned) {
usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req);
hidg->get_req = NULL;
hidg->get_report_returned = true;
}
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
spin_lock_irqsave(&hidg->write_spinlock, flags);
if (!hidg->write_pending) {
free_ep_req(hidg->in_ep, hidg->req);
@ -902,6 +1121,14 @@ fail:
return status;
}
#ifdef CONFIG_COMPAT
static long f_hidg_compat_ioctl(struct file *file, unsigned int code,
unsigned long value)
{
return f_hidg_ioctl(file, code, value);
}
#endif
static const struct file_operations f_hidg_fops = {
.owner = THIS_MODULE,
.open = f_hidg_open,
@ -909,6 +1136,10 @@ static const struct file_operations f_hidg_fops = {
.write = f_hidg_write,
.read = f_hidg_read,
.poll = f_hidg_poll,
.unlocked_ioctl = f_hidg_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = f_hidg_compat_ioctl,
#endif
.llseek = noop_llseek,
};
@ -919,6 +1150,15 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_string *us;
int status;
hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC);
if (!hidg->get_req)
return -ENOMEM;
hidg->get_req->zero = 0;
hidg->get_req->complete = hidg_get_report_complete;
hidg->get_req->context = hidg;
hidg->get_report_returned = true;
/* maybe allocate device-global string IDs, and patch descriptors */
us = usb_gstrings_attach(c->cdev, ct_func_strings,
ARRAY_SIZE(ct_func_string_defs));
@ -1004,9 +1244,24 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg->write_pending = 1;
hidg->req = NULL;
spin_lock_init(&hidg->read_spinlock);
spin_lock_init(&hidg->get_report_spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
init_waitqueue_head(&hidg->get_queue);
init_waitqueue_head(&hidg->get_id_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
INIT_LIST_HEAD(&hidg->report_list);
INIT_WORK(&hidg->work, get_report_workqueue_handler);
hidg->workqueue = alloc_workqueue("report_work",
WQ_FREEZABLE |
WQ_MEM_RECLAIM,
1);
if (!hidg->workqueue) {
status = -ENOMEM;
goto fail;
}
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
@ -1016,12 +1271,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail_free_descs:
destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
if (hidg->req != NULL)
free_ep_req(hidg->in_ep, hidg->req);
usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req);
hidg->get_req = NULL;
return status;
}
@ -1256,7 +1515,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
struct f_hidg *hidg = func_to_hidg(f);
cdev_device_del(&hidg->cdev, &hidg->dev);
destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
}

View File

@ -14,9 +14,9 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/usb/composite.h>
#include <linux/usb/func_utils.h>
#include "g_zero.h"
#include "u_f.h"
/*
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,

View File

@ -3050,7 +3050,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
if (!common->thread_task) {
common->state = FSG_STATE_NORMAL;
common->thread_task =
kthread_create(fsg_main_thread, common, "file-storage");
kthread_run(fsg_main_thread, common, "file-storage");
if (IS_ERR(common->thread_task)) {
ret = PTR_ERR(common->thread_task);
common->thread_task = NULL;
@ -3059,7 +3059,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
}
DBG(common, "I/O thread pid: %d\n",
task_pid_nr(common->thread_task));
wake_up_process(common->thread_task);
}
fsg->gadget = gadget;

View File

@ -30,11 +30,11 @@
#include <sound/rawmidi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/func_utils.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
#include "u_f.h"
#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");

View File

@ -15,11 +15,11 @@
#include <sound/ump_convert.h>
#include <linux/usb/ch9.h>
#include <linux/usb/func_utils.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi-v2.h>
#include "u_f.h"
#include "u_midi2.h"
struct f_midi2;

View File

@ -13,10 +13,10 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/usb/composite.h>
#include <linux/usb/func_utils.h>
#include <linux/err.h>
#include "g_zero.h"
#include "u_f.h"
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral

View File

@ -377,24 +377,10 @@ enum {
STR_AS_OUT_IF_ALT1,
STR_AS_IN_IF_ALT0,
STR_AS_IN_IF_ALT1,
NUM_STR_DESCRIPTORS,
};
static struct usb_string strings_uac1[] = {
/* [STR_AC_IF].s = DYNAMIC, */
[STR_USB_OUT_IT].s = "Playback Input terminal",
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
[STR_IO_OUT_OT].s = "Playback Output terminal",
[STR_IO_IN_IT].s = "Capture Input terminal",
[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
[STR_USB_IN_OT].s = "Capture Output terminal",
[STR_FU_IN].s = "Capture Volume",
[STR_FU_OUT].s = "Playback Volume",
[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
[STR_AS_OUT_IF_ALT1].s = "Playback Active",
[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
[STR_AS_IN_IF_ALT1].s = "Capture Active",
{ },
};
static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {};
static struct usb_gadget_strings str_uac1 = {
.language = 0x0409, /* en-us */
@ -1265,6 +1251,20 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
strings_uac1[STR_AC_IF].s = audio_opts->function_name;
strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name;
strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name;
strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name;
strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name;
strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive";
strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active";
strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name;
strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name;
strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name;
strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name;
strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive";
strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active";
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@ -1681,8 +1681,19 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
UAC1_ATTRIBUTE_STRING(function_name);
UAC1_ATTRIBUTE_STRING(p_it_name);
UAC1_ATTRIBUTE_STRING(p_it_ch_name);
UAC1_ATTRIBUTE_STRING(p_ot_name);
UAC1_ATTRIBUTE_STRING(p_fu_vol_name);
UAC1_ATTRIBUTE_STRING(c_it_name);
UAC1_ATTRIBUTE_STRING(c_it_ch_name);
UAC1_ATTRIBUTE_STRING(c_ot_name);
UAC1_ATTRIBUTE_STRING(c_fu_vol_name);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
&f_uac1_opts_attr_c_srate,
@ -1706,6 +1717,16 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_function_name,
&f_uac1_opts_attr_p_it_name,
&f_uac1_opts_attr_p_it_ch_name,
&f_uac1_opts_attr_p_ot_name,
&f_uac1_opts_attr_p_fu_vol_name,
&f_uac1_opts_attr_c_it_name,
&f_uac1_opts_attr_c_it_ch_name,
&f_uac1_opts_attr_c_ot_name,
&f_uac1_opts_attr_c_fu_vol_name,
NULL,
};
@ -1760,6 +1781,16 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal");
scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal");
scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "Playback Input terminal");
scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal");
scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
return &opts->func_inst;
}

View File

@ -95,7 +95,9 @@ enum {
STR_CLKSRC_IN,
STR_CLKSRC_OUT,
STR_USB_IT,
STR_USB_IT_CH,
STR_IO_IT,
STR_IO_IT_CH,
STR_USB_OT,
STR_IO_OT,
STR_FU_IN,
@ -104,25 +106,10 @@ enum {
STR_AS_OUT_ALT1,
STR_AS_IN_ALT0,
STR_AS_IN_ALT1,
NUM_STR_DESCRIPTORS,
};
static struct usb_string strings_fn[] = {
/* [STR_ASSOC].s = DYNAMIC, */
[STR_IF_CTRL].s = "Topology Control",
[STR_CLKSRC_IN].s = "Input Clock",
[STR_CLKSRC_OUT].s = "Output Clock",
[STR_USB_IT].s = "USBH Out",
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
[STR_IO_OT].s = "USBD In",
[STR_FU_IN].s = "Capture Volume",
[STR_FU_OUT].s = "Playback Volume",
[STR_AS_OUT_ALT0].s = "Playback Inactive",
[STR_AS_OUT_ALT1].s = "Playback Active",
[STR_AS_IN_ALT0].s = "Capture Inactive",
[STR_AS_IN_ALT1].s = "Capture Active",
{ },
};
static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {};
static const char *const speed_names[] = {
[USB_SPEED_UNKNOWN] = "UNKNOWN",
@ -1049,6 +1036,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
return ret;
strings_fn[STR_ASSOC].s = uac2_opts->function_name;
strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name;
strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name;
strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name;
strings_fn[STR_USB_IT].s = uac2_opts->c_it_name;
strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name;
strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name;
strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name;
strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive";
strings_fn[STR_AS_OUT_ALT1].s = "Playback Active";
strings_fn[STR_IO_IT].s = uac2_opts->p_it_name;
strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name;
strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name;
strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name;
strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive";
strings_fn[STR_AS_IN_ALT1].s = "Capture Active";
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
@ -1072,7 +1076,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id;
io_in_it_desc.iTerminal = us[STR_IO_IT].id;
io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id;
usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
@ -2100,10 +2106,24 @@ UAC2_ATTRIBUTE(s16, c_volume_max);
UAC2_ATTRIBUTE(s16, c_volume_res);
UAC2_ATTRIBUTE(u32, fb_max);
UAC2_ATTRIBUTE_STRING(function_name);
UAC2_ATTRIBUTE_STRING(if_ctrl_name);
UAC2_ATTRIBUTE_STRING(clksrc_in_name);
UAC2_ATTRIBUTE_STRING(clksrc_out_name);
UAC2_ATTRIBUTE_STRING(p_it_name);
UAC2_ATTRIBUTE_STRING(p_it_ch_name);
UAC2_ATTRIBUTE_STRING(p_ot_name);
UAC2_ATTRIBUTE_STRING(p_fu_vol_name);
UAC2_ATTRIBUTE_STRING(c_it_name);
UAC2_ATTRIBUTE_STRING(c_it_ch_name);
UAC2_ATTRIBUTE_STRING(c_ot_name);
UAC2_ATTRIBUTE_STRING(c_fu_vol_name);
UAC2_ATTRIBUTE(s16, p_terminal_type);
UAC2_ATTRIBUTE(s16, c_terminal_type);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
&f_uac2_opts_attr_p_srate,
@ -2130,6 +2150,19 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_volume_res,
&f_uac2_opts_attr_function_name,
&f_uac2_opts_attr_if_ctrl_name,
&f_uac2_opts_attr_clksrc_in_name,
&f_uac2_opts_attr_clksrc_out_name,
&f_uac2_opts_attr_p_it_name,
&f_uac2_opts_attr_p_it_ch_name,
&f_uac2_opts_attr_p_ot_name,
&f_uac2_opts_attr_p_fu_vol_name,
&f_uac2_opts_attr_c_it_name,
&f_uac2_opts_attr_c_it_ch_name,
&f_uac2_opts_attr_c_ot_name,
&f_uac2_opts_attr_c_fu_vol_name,
&f_uac2_opts_attr_p_terminal_type,
&f_uac2_opts_attr_c_terminal_type,
@ -2191,6 +2224,19 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->fb_max = FBACK_FAST_MAX;
scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
scnprintf(opts->if_ctrl_name, sizeof(opts->if_ctrl_name), "Topology Control");
scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock");
scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock");
scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out");
scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In");
scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "USBH Out");
scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "USBD In");
scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE;
opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE;

View File

@ -1140,35 +1140,35 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
}
static struct snd_kcontrol_new u_audio_controls[] = {
[UAC_FBACK_CTRL] {
[UAC_FBACK_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Capture Pitch 1000000",
.info = u_audio_pitch_info,
.get = u_audio_pitch_get,
.put = u_audio_pitch_put,
},
[UAC_P_PITCH_CTRL] {
[UAC_P_PITCH_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback Pitch 1000000",
.info = u_audio_pitch_info,
.get = u_audio_pitch_get,
.put = u_audio_pitch_put,
},
[UAC_MUTE_CTRL] {
[UAC_MUTE_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = u_audio_mute_info,
.get = u_audio_mute_get,
.put = u_audio_mute_put,
},
[UAC_VOLUME_CTRL] {
[UAC_VOLUME_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = u_audio_volume_info,
.get = u_audio_volume_get,
.put = u_audio_volume_put,
},
[UAC_RATE_CTRL] {
[UAC_RATE_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "", /* will be filled later */
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,

View File

@ -28,6 +28,7 @@
#include <linux/kthread.h>
#include <linux/workqueue.h>
#include <linux/kfifo.h>
#include <linux/serial.h>
#include "u_serial.h"
@ -126,6 +127,7 @@ struct gs_port {
wait_queue_head_t close_wait;
bool suspended; /* port suspended */
bool start_delayed; /* delay start when suspended */
struct async_icount icount;
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@ -257,6 +259,7 @@ __acquires(&port->port_lock)
break;
}
do_tty_wake = true;
port->icount.tx += len;
req->length = len;
list_del(&req->list);
@ -408,6 +411,7 @@ static void gs_rx_push(struct work_struct *work)
size -= n;
}
port->icount.rx += size;
count = tty_insert_flip_string(&port->port, packet,
size);
if (count)
@ -851,6 +855,23 @@ static int gs_break_ctl(struct tty_struct *tty, int duration)
return status;
}
static int gs_get_icount(struct tty_struct *tty,
struct serial_icounter_struct *icount)
{
struct gs_port *port = tty->driver_data;
struct async_icount cnow;
unsigned long flags;
spin_lock_irqsave(&port->port_lock, flags);
cnow = port->icount;
spin_unlock_irqrestore(&port->port_lock, flags);
icount->rx = cnow.rx;
icount->tx = cnow.tx;
return 0;
}
static const struct tty_operations gs_tty_ops = {
.open = gs_open,
.close = gs_close,
@ -861,6 +882,7 @@ static const struct tty_operations gs_tty_ops = {
.chars_in_buffer = gs_chars_in_buffer,
.unthrottle = gs_unthrottle,
.break_ctl = gs_break_ctl,
.get_icount = gs_get_icount,
};
/*-------------------------------------------------------------------------*/

View File

@ -17,6 +17,10 @@
struct f_serial_opts {
struct usb_function_instance func_inst;
u8 port_num;
u8 protocol;
struct mutex lock; /* protect instances */
int instances;
};
/*

View File

@ -52,7 +52,17 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
char function_name[32];
char function_name[USB_MAX_STRING_LEN];
char p_it_name[USB_MAX_STRING_LEN];
char p_it_ch_name[USB_MAX_STRING_LEN];
char p_ot_name[USB_MAX_STRING_LEN];
char p_fu_vol_name[USB_MAX_STRING_LEN];
char c_it_name[USB_MAX_STRING_LEN];
char c_it_ch_name[USB_MAX_STRING_LEN];
char c_ot_name[USB_MAX_STRING_LEN];
char c_fu_vol_name[USB_MAX_STRING_LEN];
struct mutex lock;
int refcnt;

View File

@ -68,7 +68,20 @@ struct f_uac2_opts {
int fb_max;
bool bound;
char function_name[32];
char function_name[USB_MAX_STRING_LEN];
char if_ctrl_name[USB_MAX_STRING_LEN];
char clksrc_in_name[USB_MAX_STRING_LEN];
char clksrc_out_name[USB_MAX_STRING_LEN];
char p_it_name[USB_MAX_STRING_LEN];
char p_it_ch_name[USB_MAX_STRING_LEN];
char p_ot_name[USB_MAX_STRING_LEN];
char p_fu_vol_name[USB_MAX_STRING_LEN];
char c_it_name[USB_MAX_STRING_LEN];
char c_it_ch_name[USB_MAX_STRING_LEN];
char c_ot_name[USB_MAX_STRING_LEN];
char c_fu_vol_name[USB_MAX_STRING_LEN];
s16 p_terminal_type;
s16 c_terminal_type;

View File

@ -121,6 +121,9 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
list_for_each_entry(format, &uvc->header->formats, entry) {
const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
if (IS_ERR(fmtdesc))
continue;
if (fmtdesc->fcc == pixelformat) {
uformat = format->fmt;
break;
@ -240,6 +243,7 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
struct uvc_video *video = &uvc->video;
struct uvcg_format *uformat;
struct uvcg_frame *uframe;
const struct uvc_format_desc *fmtdesc;
u8 *fcc;
if (fmt->type != video->queue.queue.type)
@ -277,7 +281,10 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
fmt->fmt.pix.height = uframe->frame.w_height;
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
fmtdesc = to_uvc_format(uformat);
if (IS_ERR(fmtdesc))
return PTR_ERR(fmtdesc);
fmt->fmt.pix.pixelformat = fmtdesc->fcc;
}
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
@ -389,6 +396,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
return -EINVAL;
fmtdesc = to_uvc_format(uformat);
if (IS_ERR(fmtdesc))
return PTR_ERR(fmtdesc);
f->pixelformat = fmtdesc->fcc;
return 0;

View File

@ -8,8 +8,8 @@
* Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/
#include "u_f.h"
#include <linux/usb/ch9.h>
#include <linux/usb/func_utils.h>
struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len)
{

View File

@ -639,6 +639,7 @@ static const struct of_device_id bdc_of_match[] = {
{ .compatible = "brcm,bdc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, bdc_of_match);
static struct platform_driver bdc_driver = {
.driver = {

View File

@ -2033,8 +2033,8 @@ static void cdns2_quiesce(struct cdns2_device *pdev)
set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
/* Disable interrupt. */
writeb(0, &pdev->interrupt_regs->extien),
writeb(0, &pdev->interrupt_regs->usbien),
writeb(0, &pdev->interrupt_regs->extien);
writeb(0, &pdev->interrupt_regs->usbien);
writew(0, &pdev->adma_regs->ep_ien);
/* Clear interrupt line. */

View File

@ -15,8 +15,7 @@
#include "cdns2-gadget.h"
#define PCI_DRIVER_NAME "cdns-pci-usbhs"
#define CDNS_VENDOR_ID 0x17cd
#define CDNS_DEVICE_ID 0x0120
#define PCI_DEVICE_ID_CDNS_USB2 0x0120
#define PCI_BAR_DEV 0
#define PCI_DEV_FN_DEVICE 0
@ -114,8 +113,8 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = {
};
static const struct pci_device_id cdns2_pci_ids[] = {
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB2),
.class = PCI_CLASS_SERIAL_USB_DEVICE },
{ 0, }
};

View File

@ -1304,7 +1304,8 @@ static int dummy_urb_enqueue(
/* kick the scheduler, it'll do the rest */
if (!hrtimer_active(&dum_hcd->timer))
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL);
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
HRTIMER_MODE_REL_SOFT);
done:
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
@ -1325,7 +1326,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING &&
!list_empty(&dum_hcd->urbp_list))
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
return rc;
@ -1995,7 +1996,8 @@ return_urb:
dum_hcd->udev = NULL;
} else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
/* want a 1 msec delay here */
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL);
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
HRTIMER_MODE_REL_SOFT);
}
spin_unlock_irqrestore(&dum->lock, flags);
@ -2389,7 +2391,7 @@ static int dummy_bus_resume(struct usb_hcd *hcd)
dum_hcd->rh_state = DUMMY_RH_RUNNING;
set_link_state(dum_hcd);
if (!list_empty(&dum_hcd->urbp_list))
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
hcd->state = HC_STATE_RUNNING;
}
spin_unlock_irq(&dum_hcd->dum->lock);
@ -2467,7 +2469,7 @@ static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->timer.function = dummy_timer;
dum_hcd->rh_state = DUMMY_RH_RUNNING;
dum_hcd->stream_en_ep = 0;
@ -2497,7 +2499,7 @@ static int dummy_start(struct usb_hcd *hcd)
return dummy_start_ss(dum_hcd);
spin_lock_init(&dum_hcd->dum->lock);
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->timer.function = dummy_timer;
dum_hcd->rh_state = DUMMY_RH_RUNNING;

View File

@ -1487,31 +1487,29 @@ static int udc_ep0_out_req(struct lpc32xx_udc *udc)
req = list_entry(ep0->queue.next, struct lpc32xx_request,
queue);
if (req) {
if (req->req.length == 0) {
/* Just dequeue request */
done(ep0, req, 0);
udc->ep0state = WAIT_FOR_SETUP;
return 1;
}
if (req->req.length == 0) {
/* Just dequeue request */
done(ep0, req, 0);
udc->ep0state = WAIT_FOR_SETUP;
return 1;
}
/* Get data from FIFO */
bufferspace = req->req.length - req->req.actual;
if (bufferspace > ep0->ep.maxpacket)
bufferspace = ep0->ep.maxpacket;
/* Get data from FIFO */
bufferspace = req->req.length - req->req.actual;
if (bufferspace > ep0->ep.maxpacket)
bufferspace = ep0->ep.maxpacket;
/* Copy data to buffer */
prefetchw(req->req.buf + req->req.actual);
tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
bufferspace);
req->req.actual += bufferspace;
/* Copy data to buffer */
prefetchw(req->req.buf + req->req.actual);
tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
bufferspace);
req->req.actual += bufferspace;
if (tr < ep0->ep.maxpacket) {
/* This is the last packet */
done(ep0, req, 0);
udc->ep0state = WAIT_FOR_SETUP;
return 1;
}
if (tr < ep0->ep.maxpacket) {
/* This is the last packet */
done(ep0, req, 0);
udc->ep0state = WAIT_FOR_SETUP;
return 1;
}
return 0;
@ -1962,18 +1960,17 @@ static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
/* If there isn't a request waiting, something went wrong */
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
if (req) {
done(ep, req, 0);
/* Start another request if ready */
if (!list_empty(&ep->queue)) {
if (ep->is_in)
udc_ep_in_req_dma(udc, ep);
else
udc_ep_out_req_dma(udc, ep);
} else
ep->req_pending = 0;
}
done(ep, req, 0);
/* Start another request if ready */
if (!list_empty(&ep->queue)) {
if (ep->is_in)
udc_ep_in_req_dma(udc, ep);
else
udc_ep_out_req_dma(udc, ep);
} else
ep->req_pending = 0;
}
@ -1989,10 +1986,6 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
#endif
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
if (!req) {
ep_err(ep, "DMA interrupt on no req!\n");
return;
}
dd = req->dd_desc_ptr;
/* DMA descriptor should always be retired for this call */

View File

@ -947,7 +947,7 @@ static int xudc_ep_disable(struct usb_ep *_ep)
ep->desc = NULL;
ep->ep_usb.desc = NULL;
dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber);
dev_dbg(udc->dev, "USB Ep %d disable\n", ep->epnumber);
/* Disable the endpoint.*/
epcfg = udc->read_fn(udc->addr + ep->offset);
epcfg &= ~XUSB_EP_CFG_VALID_MASK;

View File

@ -40,11 +40,11 @@ config USB_XHCI_DBGCAP
config USB_XHCI_PCI
tristate
depends on USB_PCI
depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS
default y
config USB_XHCI_PCI_RENESAS
tristate "Support for additional Renesas xHCI controller with firmware"
depends on USB_XHCI_PCI
help
Say 'Y' to enable the support for the Renesas xHCI controller with
firmware. Make sure you have the firmware for the device and

View File

@ -246,6 +246,7 @@ static const struct of_device_id brcm_ehci_of_match[] = {
{ .compatible = "brcm,bcm7445-ehci", },
{}
};
MODULE_DEVICE_TABLE(of, brcm_ehci_of_match);
static struct platform_driver ehci_brcm_driver = {
.probe = ehci_brcm_probe,

View File

@ -48,7 +48,6 @@ struct exynos_ehci_hcd {
static int exynos_ehci_get_phy(struct device *dev,
struct exynos_ehci_hcd *exynos_ehci)
{
struct device_node *child;
struct phy *phy;
int phy_number, num_phys;
int ret;
@ -66,26 +65,22 @@ static int exynos_ehci_get_phy(struct device *dev,
return 0;
/* Get PHYs using legacy bindings */
for_each_available_child_of_node(dev->of_node, child) {
for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
dev_err(dev, "Failed to parse device tree\n");
of_node_put(child);
return ret;
}
if (phy_number >= PHY_NUMBER) {
dev_err(dev, "Invalid number of PHYs\n");
of_node_put(child);
return -EINVAL;
}
phy = devm_of_phy_optional_get(dev, child, NULL);
exynos_ehci->phy[phy_number] = phy;
if (IS_ERR(phy)) {
of_node_put(child);
if (IS_ERR(phy))
return PTR_ERR(phy);
}
}
exynos_ehci->legacy_phy = true;

View File

@ -37,7 +37,6 @@ struct exynos_ohci_hcd {
static int exynos_ohci_get_phy(struct device *dev,
struct exynos_ohci_hcd *exynos_ohci)
{
struct device_node *child;
struct phy *phy;
int phy_number, num_phys;
int ret;
@ -55,26 +54,22 @@ static int exynos_ohci_get_phy(struct device *dev,
return 0;
/* Get PHYs using legacy bindings */
for_each_available_child_of_node(dev->of_node, child) {
for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
dev_err(dev, "Failed to parse device tree\n");
of_node_put(child);
return ret;
}
if (phy_number >= PHY_NUMBER) {
dev_err(dev, "Invalid number of PHYs\n");
of_node_put(child);
return -EINVAL;
}
phy = devm_of_phy_optional_get(dev, child, NULL);
exynos_ohci->phy[phy_number] = phy;
if (IS_ERR(phy)) {
of_node_put(child);
if (IS_ERR(phy))
return PTR_ERR(phy);
}
}
exynos_ohci->legacy_phy = true;

View File

@ -51,8 +51,6 @@ static struct hc_driver __read_mostly ohci_nxp_hc_driver;
static struct i2c_client *isp1301_i2c_client;
static struct clk *usb_host_clk;
static void isp1301_configure_lpc32xx(void)
{
/* LPC32XX only supports DAT_SE0 USB mode */
@ -155,6 +153,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0, irq;
struct device_node *isp1301_node;
struct clk *usb_host_clk;
if (pdev->dev.of_node) {
isp1301_node = of_parse_phandle(pdev->dev.of_node,
@ -180,26 +179,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
}
/* Enable USB host clock */
usb_host_clk = devm_clk_get(&pdev->dev, NULL);
usb_host_clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(usb_host_clk)) {
dev_err(&pdev->dev, "failed to acquire USB OHCI clock\n");
dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n");
ret = PTR_ERR(usb_host_clk);
goto fail_disable;
}
ret = clk_prepare_enable(usb_host_clk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to start USB OHCI clock\n");
goto fail_disable;
}
isp1301_configure();
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Failed to allocate HC buffer\n");
ret = -ENOMEM;
goto fail_hcd;
goto fail_disable;
}
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
@ -229,8 +222,6 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
ohci_nxp_stop_hc();
fail_resource:
usb_put_hcd(hcd);
fail_hcd:
clk_disable_unprepare(usb_host_clk);
fail_disable:
isp1301_i2c_client = NULL;
return ret;
@ -243,7 +234,6 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
ohci_nxp_stop_hc();
usb_put_hcd(hcd);
clk_disable_unprepare(usb_host_clk);
isp1301_i2c_client = NULL;
}

View File

@ -202,10 +202,6 @@ static const struct of_device_id ohci_hcd_ppc_of_match[] = {
},
#endif
#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE
{
.name = "usb",
.compatible = "ohci-littledian",
},
{
.name = "usb",
.compatible = "ohci-le",

View File

@ -297,9 +297,9 @@ static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
{
u16 pipenum = pipe->info.pipenum;
const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
static const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
static const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
static const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */
dma_ch = R8A66597_PIPE_NO_DMA;

View File

@ -173,16 +173,18 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status)
spin_lock(&dbc->lock);
}
static void xhci_dbc_flush_single_request(struct dbc_request *req)
static void trb_to_noop(union xhci_trb *trb)
{
union xhci_trb *trb = req->trb;
trb->generic.field[0] = 0;
trb->generic.field[1] = 0;
trb->generic.field[2] = 0;
trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP));
}
static void xhci_dbc_flush_single_request(struct dbc_request *req)
{
trb_to_noop(req->trb);
xhci_dbc_giveback(req, -ESHUTDOWN);
}
@ -649,7 +651,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
case DS_DISABLED:
return;
case DS_CONFIGURED:
case DS_STALLED:
if (dbc->driver->disconnect)
dbc->driver->disconnect(dbc);
break;
@ -669,6 +670,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
}
static void
handle_ep_halt_changes(struct xhci_dbc *dbc, struct dbc_ep *dep, bool halted)
{
if (halted) {
dev_info(dbc->dev, "DbC Endpoint halted\n");
dep->halted = 1;
} else if (dep->halted) {
dev_info(dbc->dev, "DbC Endpoint halt cleared\n");
dep->halted = 0;
if (!list_empty(&dep->list_pending))
writel(DBC_DOOR_BELL_TARGET(dep->direction),
&dbc->regs->doorbell);
}
}
static void
dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event)
{
@ -697,6 +715,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
struct xhci_ring *ring;
int ep_id;
int status;
struct xhci_ep_ctx *ep_ctx;
u32 comp_code;
size_t remain_length;
struct dbc_request *req = NULL, *r;
@ -706,8 +725,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3]));
dep = (ep_id == EPID_OUT) ?
get_out_ep(dbc) : get_in_ep(dbc);
ep_ctx = (ep_id == EPID_OUT) ?
dbc_bulkout_ctx(dbc) : dbc_bulkin_ctx(dbc);
ring = dep->ring;
/* Match the pending request: */
list_for_each_entry(r, &dep->list_pending, list_pending) {
if (r->trb_dma == event->trans_event.buffer) {
req = r;
break;
}
if (r->status == -COMP_STALL_ERROR) {
dev_warn(dbc->dev, "Give back stale stalled req\n");
ring->num_trbs_free++;
xhci_dbc_giveback(r, 0);
}
}
if (!req) {
dev_warn(dbc->dev, "no matched request\n");
return;
}
trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
switch (comp_code) {
case COMP_SUCCESS:
remain_length = 0;
@ -718,31 +759,49 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
case COMP_TRB_ERROR:
case COMP_BABBLE_DETECTED_ERROR:
case COMP_USB_TRANSACTION_ERROR:
case COMP_STALL_ERROR:
dev_warn(dbc->dev, "tx error %d detected\n", comp_code);
status = -comp_code;
break;
case COMP_STALL_ERROR:
dev_warn(dbc->dev, "Stall error at bulk TRB %llx, remaining %zu, ep deq %llx\n",
event->trans_event.buffer, remain_length, ep_ctx->deq);
status = 0;
dep->halted = 1;
/*
* xHC DbC may trigger a STALL bulk xfer event when host sends a
* ClearFeature(ENDPOINT_HALT) request even if there wasn't an
* active bulk transfer.
*
* Don't give back this transfer request as hardware will later
* start processing TRBs starting from this 'STALLED' TRB,
* causing TRBs and requests to be out of sync.
*
* If STALL event shows some bytes were transferred then assume
* it's an actual transfer issue and give back the request.
* In this case mark the TRB as No-Op to avoid hw from using the
* TRB again.
*/
if ((ep_ctx->deq & ~TRB_CYCLE) == event->trans_event.buffer) {
dev_dbg(dbc->dev, "Ep stopped on Stalled TRB\n");
if (remain_length == req->length) {
dev_dbg(dbc->dev, "Spurious stall event, keep req\n");
req->status = -COMP_STALL_ERROR;
req->actual = 0;
return;
}
dev_dbg(dbc->dev, "Give back stalled req, but turn TRB to No-op\n");
trb_to_noop(req->trb);
}
break;
default:
dev_err(dbc->dev, "unknown tx error %d\n", comp_code);
status = -comp_code;
break;
}
/* Match the pending request: */
list_for_each_entry(r, &dep->list_pending, list_pending) {
if (r->trb_dma == event->trans_event.buffer) {
req = r;
break;
}
}
if (!req) {
dev_warn(dbc->dev, "no matched request\n");
return;
}
trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
ring->num_trbs_free++;
req->actual = req->length - remain_length;
xhci_dbc_giveback(req, status);
@ -762,7 +821,6 @@ static void inc_evt_deq(struct xhci_ring *ring)
static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
{
dma_addr_t deq;
struct dbc_ep *dep;
union xhci_trb *evt;
u32 ctrl, portsc;
bool update_erdp = false;
@ -814,43 +872,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
return EVT_DISC;
}
/* Handle endpoint stall event: */
/* Check and handle changes in endpoint halt status */
ctrl = readl(&dbc->regs->control);
if ((ctrl & DBC_CTRL_HALT_IN_TR) ||
(ctrl & DBC_CTRL_HALT_OUT_TR)) {
dev_info(dbc->dev, "DbC Endpoint stall\n");
dbc->state = DS_STALLED;
if (ctrl & DBC_CTRL_HALT_IN_TR) {
dep = get_in_ep(dbc);
xhci_dbc_flush_endpoint_requests(dep);
}
if (ctrl & DBC_CTRL_HALT_OUT_TR) {
dep = get_out_ep(dbc);
xhci_dbc_flush_endpoint_requests(dep);
}
return EVT_DONE;
}
handle_ep_halt_changes(dbc, get_in_ep(dbc), ctrl & DBC_CTRL_HALT_IN_TR);
handle_ep_halt_changes(dbc, get_out_ep(dbc), ctrl & DBC_CTRL_HALT_OUT_TR);
/* Clear DbC run change bit: */
if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) {
writel(ctrl, &dbc->regs->control);
ctrl = readl(&dbc->regs->control);
}
break;
case DS_STALLED:
ctrl = readl(&dbc->regs->control);
if (!(ctrl & DBC_CTRL_HALT_IN_TR) &&
!(ctrl & DBC_CTRL_HALT_OUT_TR) &&
(ctrl & DBC_CTRL_DBC_RUN)) {
dbc->state = DS_CONFIGURED;
break;
}
return EVT_DONE;
default:
dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state);
break;
@ -939,7 +971,6 @@ static const char * const dbc_state_strings[DS_MAX] = {
[DS_ENABLED] = "enabled",
[DS_CONNECTED] = "connected",
[DS_CONFIGURED] = "configured",
[DS_STALLED] = "stalled",
};
static ssize_t dbc_show(struct device *dev,

View File

@ -81,7 +81,6 @@ enum dbc_state {
DS_ENABLED,
DS_CONNECTED,
DS_CONFIGURED,
DS_STALLED,
DS_MAX
};
@ -90,6 +89,7 @@ struct dbc_ep {
struct list_head list_pending;
struct xhci_ring *ring;
unsigned int direction:1;
unsigned int halted:1;
};
#define DBC_QUEUE_SIZE 16

View File

@ -346,7 +346,7 @@ static void dbc_rx_push(struct tasklet_struct *t)
port->n_read = 0;
}
list_move(&req->list_pool, &port->read_pool);
list_move_tail(&req->list_pool, &port->read_pool);
}
if (do_push)

View File

@ -42,6 +42,7 @@
#define XHCI_EXT_CAPS_DEBUG 10
/* Vendor caps */
#define XHCI_EXT_CAPS_VENDOR_INTEL 192
#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206
/* USB Legacy Support Capability - section 7.1.1 */
#define XHCI_HC_BIOS_OWNED (1 << 16)
#define XHCI_HC_OS_OWNED (1 << 24)
@ -64,6 +65,10 @@
#define XHCI_HLC (1 << 19)
#define XHCI_BLC (1 << 20)
/* Intel SPR shadow capability */
#define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */
#define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */
/* command register values to disable interrupts and halt the HC */
/* start/stop HC execution - do not write unless HC is halted*/
#define XHCI_CMD_RUN (1 << 0)

View File

@ -752,6 +752,42 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
}
/**
* xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
* @xhci: xhci host controller
* @port: USB3 port to be checked.
*
* Some hosts can detect if a USB3 connection is native USB3 or tunneled over
* USB4. Intel hosts expose this via vendor specific extended capability 206
* eSS PORT registers TUNEN (tunnel enabled) bit.
*
* A USB3 device must be connected to the port to detect the tunnel.
*
* Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
* detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
* otherwise.
*/
enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
struct xhci_port *port)
{
void __iomem *base;
u32 offset;
base = &xhci->cap_regs->hc_capbase;
offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
return USB_LINK_TUNNELED;
else
return USB_LINK_NATIVE;
}
return USB_LINK_UNKNOWN;
}
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
u32 link_state)
{

View File

@ -2332,7 +2332,8 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
}
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs)
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
u32 imod_interval)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir;
@ -2365,6 +2366,11 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs)
return NULL;
}
err = xhci_set_interrupter_moderation(ir, imod_interval);
if (err)
xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n",
i, imod_interval);
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
i, xhci->max_interrupters);

View File

@ -50,6 +50,8 @@
#define RENESAS_RETRY 10000
#define RENESAS_DELAY 10
#define RENESAS_FW_NAME "renesas_usb_fw.mem"
static int renesas_fw_download_image(struct pci_dev *dev,
const u32 *fw, size_t step, bool rom)
{
@ -573,12 +575,10 @@ exit:
return err;
}
int renesas_xhci_check_request_fw(struct pci_dev *pdev,
const struct pci_device_id *id)
static int renesas_xhci_check_request_fw(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct xhci_driver_data *driver_data =
(struct xhci_driver_data *)id->driver_data;
const char *fw_name = driver_data->firmware;
const char fw_name[] = RENESAS_FW_NAME;
const struct firmware *fw;
bool has_rom;
int err;
@ -625,7 +625,41 @@ exit:
release_firmware(fw);
return err;
}
EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
MODULE_DESCRIPTION("Support for Renesas xHCI controller with firmware");
static int
xhci_pci_renesas_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
retval = renesas_xhci_check_request_fw(dev, id);
if (retval)
return retval;
return xhci_pci_common_probe(dev, id);
}
static const struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
static struct pci_driver xhci_renesas_pci_driver = {
.name = "xhci-pci-renesas",
.id_table = pci_ids,
.probe = xhci_pci_renesas_probe,
.remove = xhci_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
.driver = {
.pm = pm_ptr(&usb_hcd_pci_pm_ops),
},
};
module_pci_driver(xhci_renesas_pci_driver);
MODULE_DESCRIPTION("Renesas xHCI PCI Host Controller Driver");
MODULE_FIRMWARE(RENESAS_FW_NAME);
MODULE_IMPORT_NS(xhci);
MODULE_LICENSE("GPL v2");

View File

@ -55,6 +55,9 @@
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
#define PCI_VENDOR_ID_PHYTIUM 0x1db7
#define PCI_DEVICE_ID_PHYTIUM_XHCI 0xdc27
/* Thunderbolt */
#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5
@ -78,6 +81,9 @@
#define PCI_DEVICE_ID_ASMEDIA_2142_XHCI 0x2142
#define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242
#define PCI_DEVICE_ID_CADENCE 0x17CD
#define PCI_DEVICE_ID_CADENCE_SSP 0x0200
static const char hcd_name[] = "xhci_hcd";
static struct hc_driver __read_mostly xhci_pci_hc_driver;
@ -93,6 +99,10 @@ static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
.update_hub_device = xhci_pci_update_hub_device,
};
/*
* Primary Legacy and MSI IRQ are synced in suspend_common().
* All MSI-X IRQs and secondary MSI IRQs should be synced here.
*/
static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
@ -105,13 +115,12 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
}
}
/* Free any IRQs and disable MSI-X */
/* Legacy IRQ is freed by usb_remove_hcd() or usb_hcd_pci_shutdown() */
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
/* return if using legacy interrupt */
if (hcd->irq > 0)
return;
@ -235,15 +244,6 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct xhci_driver_data *driver_data;
const struct pci_device_id *id;
id = pci_match_id(to_pci_driver(pdev->dev.driver)->id_table, pdev);
if (id && id->driver_data) {
driver_data = (struct xhci_driver_data *)id->driver_data;
xhci->quirks |= driver_data->quirks;
}
/* Look for vendor-specific quirks */
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
@ -416,6 +416,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
if (pdev->vendor == PCI_VENDOR_ID_PHYTIUM &&
pdev->device == PCI_DEVICE_ID_PHYTIUM_XHCI)
xhci->quirks |= XHCI_RESET_ON_RESUME;
/* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
if (pdev->vendor == PCI_VENDOR_ID_VIA &&
pdev->device == 0x3432)
@ -473,6 +477,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
}
if (pdev->vendor == PCI_DEVICE_ID_CADENCE &&
pdev->device == PCI_DEVICE_ID_CADENCE_SSP)
xhci->quirks |= XHCI_CDNS_SCTX_QUIRK;
/* xHC spec requires PCI devices to support D3hot and D3cold */
if (xhci->hci_version >= 0x120)
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
@ -534,10 +542,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
struct xhci_hcd *xhci;
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval;
u8 sbrn;
xhci = hcd_to_xhci(hcd);
if (!xhci->sbrn)
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
/* imod_interval is the interrupt moderation value in nanoseconds. */
xhci->imod_interval = 40000;
@ -552,7 +559,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_acpi_rtd3_enable(pdev);
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &sbrn);
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int)sbrn);
/* Find any debug ports */
return xhci_pci_reinit(xhci, pdev);
@ -572,21 +580,13 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
* We need to register our own PCI probe function (instead of the USB core's
* function) in order to create a second roothub under xHCI.
*/
static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
struct xhci_hcd *xhci;
struct usb_hcd *hcd;
struct xhci_driver_data *driver_data;
struct reset_control *reset;
driver_data = (struct xhci_driver_data *)id->driver_data;
if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) {
retval = renesas_xhci_check_request_fw(dev, id);
if (retval)
return retval;
}
reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
if (IS_ERR(reset))
return PTR_ERR(reset);
@ -651,12 +651,30 @@ put_runtime_pm:
pm_runtime_put_noidle(&dev->dev);
return retval;
}
EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, xhci);
static void xhci_pci_remove(struct pci_dev *dev)
static const struct pci_device_id pci_ids_reject[] = {
/* handled by xhci-pci-renesas */
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
{ /* end: all zeroes */ }
};
static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (pci_match_id(pci_ids_reject, dev))
return -ENODEV;
return xhci_pci_common_probe(dev, id);
}
void xhci_pci_remove(struct pci_dev *dev)
{
struct xhci_hcd *xhci;
bool set_power_d3;
xhci = hcd_to_xhci(pci_get_drvdata(dev));
set_power_d3 = xhci->quirks & XHCI_SPURIOUS_WAKEUP;
xhci->xhc_state |= XHCI_STATE_REMOVING;
@ -669,12 +687,13 @@ static void xhci_pci_remove(struct pci_dev *dev)
xhci->shared_hcd = NULL;
}
/* Workaround for spurious wakeups at shutdown with HSW */
if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
pci_set_power_state(dev, PCI_D3hot);
usb_hcd_pci_remove(dev);
/* Workaround for spurious wakeups at shutdown with HSW */
if (set_power_d3)
pci_set_power_state(dev, PCI_D3hot);
}
EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, xhci);
/*
* In some Intel xHCI controllers, in order to get D3 working,
@ -783,7 +802,6 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval = 0;
reset_control_reset(xhci->reset);
@ -814,8 +832,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_quirk(hcd);
retval = xhci_resume(xhci, msg);
return retval;
return xhci_resume(xhci, msg);
}
static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
@ -882,19 +899,8 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
static const struct xhci_driver_data reneses_data = {
.quirks = XHCI_RENESAS_FW_QUIRK,
.firmware = "renesas_usb_fw.mem",
};
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014),
.driver_data = (unsigned long)&reneses_data,
},
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015),
.driver_data = (unsigned long)&reneses_data,
},
/* handle any USB 3.0 xHCI controller */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
},
@ -902,14 +908,6 @@ static const struct pci_device_id pci_ids[] = {
};
MODULE_DEVICE_TABLE(pci, pci_ids);
/*
* Without CONFIG_USB_XHCI_PCI_RENESAS renesas_xhci_check_request_fw() won't
* load firmware, so don't encumber the xhci-pci driver with it.
*/
#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
MODULE_FIRMWARE("renesas_usb_fw.mem");
#endif
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver xhci_pci_driver = {
.name = hcd_name,

View File

@ -4,22 +4,7 @@
#ifndef XHCI_PCI_H
#define XHCI_PCI_H
#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
int renesas_xhci_check_request_fw(struct pci_dev *dev,
const struct pci_device_id *id);
#else
static int renesas_xhci_check_request_fw(struct pci_dev *dev,
const struct pci_device_id *id)
{
return 0;
}
#endif
struct xhci_driver_data {
u64 quirks;
const char *firmware;
};
int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id);
void xhci_pci_remove(struct pci_dev *dev);
#endif

View File

@ -259,6 +259,12 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
if (device_property_read_bool(tmpdev, "write-64-hi-lo-quirk"))
xhci->quirks |= XHCI_WRITE_64_HI_LO;
if (device_property_read_bool(tmpdev, "xhci-missing-cas-quirk"))
xhci->quirks |= XHCI_MISSING_CAS;
if (device_property_read_bool(tmpdev, "xhci-skip-phy-init-quirk"))
xhci->quirks |= XHCI_SKIP_PHY_INIT;
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}

View File

@ -1399,6 +1399,20 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
struct xhci_stream_ctx *ctx =
&ep->stream_info->stream_ctx_array[stream_id];
deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
/*
* Cadence xHCI controllers store some endpoint state
* information within Rsvd0 fields of Stream Endpoint
* context. This field is not cleared during Set TR
* Dequeue Pointer command which causes XDMA to skip
* over transfer ring and leads to data loss on stream
* pipe.
* To fix this issue driver must clear Rsvd0 field.
*/
if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) {
ctx->reserved[0] = 0;
ctx->reserved[1] = 0;
}
} else {
deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
}
@ -2521,9 +2535,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
td->status = 0;
break;
case COMP_SHORT_PACKET:
xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
td->urb->ep->desc.bEndpointAddress,
requested, remaining);
td->status = 0;
break;
case COMP_STOPPED_SHORT_PACKET:
@ -2764,35 +2775,25 @@ static int handle_tx_event(struct xhci_hcd *xhci,
return 0;
}
do {
/* This TRB should be in the TD at the head of this ring's
* TD list.
if (list_empty(&ep_ring->td_list)) {
/*
* Don't print wanings if ring is empty due to a stopped endpoint generating an
* extra completion event if the device was suspended. Or, a event for the last TRB
* of a short TD we already got a short event for. The short TD is already removed
* from the TD list.
*/
if (list_empty(&ep_ring->td_list)) {
/*
* Don't print wanings if it's due to a stopped endpoint
* generating an extra completion event if the device
* was suspended. Or, a event for the last TRB of a
* short TD we already got a short event for.
* The short TD is already removed from the TD list.
*/
if (!(trb_comp_code == COMP_STOPPED ||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
ep_ring->last_td_was_short)) {
xhci_warn(xhci, "WARN Event TRB for slot %u ep %d with no TDs queued?\n",
slot_id, ep_index);
}
if (ep->skip) {
ep->skip = false;
xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
slot_id, ep_index);
}
td = NULL;
goto check_endpoint_halted;
if (trb_comp_code != COMP_STOPPED &&
trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
!ep_ring->last_td_was_short) {
xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
slot_id, ep_index);
}
ep->skip = false;
goto check_endpoint_halted;
}
do {
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
td_list);
@ -2803,7 +2804,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
skip_isoc_td(xhci, td, ep, status);
continue;
if (!list_empty(&ep_ring->td_list))
continue;
xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n",
slot_id, ep_index);
ep->skip = false;
td = NULL;
goto check_endpoint_halted;
}
/*
@ -3941,10 +3949,6 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
start_frame_id = (start_frame_id >> 3) & 0x7ff;
end_frame_id = (end_frame_id >> 3) & 0x7ff;
xhci_dbg(xhci, "%s: index %d, reg 0x%x start_frame_id 0x%x, end_frame_id 0x%x, start_frame 0x%x\n",
__func__, index, readl(&xhci->run_regs->microframe_index),
start_frame_id, end_frame_id, start_frame);
if (start_frame_id < end_frame_id) {
if (start_frame > end_frame_id ||
start_frame < start_frame_id)

View File

@ -347,8 +347,8 @@ static int xhci_disable_interrupter(struct xhci_interrupter *ir)
}
/* interrupt moderation interval imod_interval in nanoseconds */
static int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
u32 imod_interval)
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
u32 imod_interval)
{
u32 imod;
@ -4525,6 +4525,20 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_port *port;
u32 capability;
/* Check if USB3 device at root port is tunneled over USB4 */
if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
port = xhci->usb3_rhub.ports[udev->portnum - 1];
udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
if (udev->tunnel_mode == USB_LINK_UNKNOWN)
dev_dbg(&udev->dev, "link tunnel state unknown\n");
else if (udev->tunnel_mode == USB_LINK_TUNNELED)
dev_dbg(&udev->dev, "tunneled over USB4 link\n");
else if (udev->tunnel_mode == USB_LINK_NATIVE)
dev_dbg(&udev->dev, "native USB 3.x link\n");
return 0;
}
if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support)
return 0;

View File

@ -1498,15 +1498,10 @@ struct xhci_hcd {
spinlock_t lock;
/* packed release number */
u8 sbrn;
u16 hci_version;
u8 max_slots;
u16 max_interrupters;
u8 max_ports;
u8 isoc_threshold;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
int event_ring_max;
/* 4KB min, 128MB max */
int page_size;
/* Valid values are 12 to 20, inclusive */
@ -1616,7 +1611,7 @@ struct xhci_hcd {
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
#define XHCI_RENESAS_FW_QUIRK BIT_ULL(36)
/* Reserved. It was XHCI_RENESAS_FW_QUIRK */
#define XHCI_SKIP_PHY_INIT BIT_ULL(37)
#define XHCI_DISABLE_SPARSE BIT_ULL(38)
#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39)
@ -1628,6 +1623,7 @@ struct xhci_hcd {
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@ -1831,7 +1827,8 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
void xhci_free_container_ctx(struct xhci_hcd *xhci,
struct xhci_container_ctx *ctx);
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs);
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
u32 imod_interval);
void xhci_remove_secondary_interrupter(struct usb_hcd
*hcd, struct xhci_interrupter *ir);
@ -1871,6 +1868,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
u32 imod_interval);
/* xHCI ring, segment, TRB, and TD functions */
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
@ -1904,10 +1903,6 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
enum xhci_ep_reset_type reset_type);
int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
u32 slot_id);
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id,
struct xhci_td *td);
void xhci_stop_endpoint_command_watchdog(struct timer_list *t);
void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
@ -1929,7 +1924,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
struct xhci_port *port);
void xhci_hc_died(struct xhci_hcd *xhci);
#ifdef CONFIG_PM

View File

@ -107,7 +107,12 @@ static void appledisplay_complete(struct urb *urb)
case ACD_BTN_BRIGHT_UP:
case ACD_BTN_BRIGHT_DOWN:
pdata->button_pressed = 1;
schedule_delayed_work(&pdata->work, 0);
/*
* there is a window during which no device
* is registered
*/
if (pdata->bd )
schedule_delayed_work(&pdata->work, 0);
break;
case ACD_BTN_NONE:
default:
@ -202,6 +207,7 @@ static int appledisplay_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct backlight_properties props;
struct backlight_device *backlight;
struct appledisplay *pdata;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_endpoint_descriptor *endpoint;
@ -272,13 +278,14 @@ static int appledisplay_probe(struct usb_interface *iface,
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
pdata->bd = backlight_device_register(bl_name, NULL, pdata,
backlight = backlight_device_register(bl_name, NULL, pdata,
&appledisplay_bl_data, &props);
if (IS_ERR(pdata->bd)) {
if (IS_ERR(backlight)) {
dev_err(&iface->dev, "Backlight registration failed\n");
retval = PTR_ERR(pdata->bd);
retval = PTR_ERR(backlight);
goto error;
}
pdata->bd = backlight;
/* Try to get brightness */
brightness = appledisplay_bl_get_brightness(pdata->bd);

View File

@ -335,6 +335,7 @@ static const struct of_device_id brcmstb_usb_pinmap_of_match[] = {
{ .compatible = "brcm,usb-pinmap" },
{ },
};
MODULE_DEVICE_TABLE(of, brcmstb_usb_pinmap_of_match);
static struct platform_driver brcmstb_usb_pinmap_driver = {
.driver = {

View File

@ -88,6 +88,9 @@ static int vendor_command(struct cypress *dev, unsigned char request,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
address, data, iobuf, CYPRESS_MAX_REQSIZE,
USB_CTRL_GET_TIMEOUT);
/* we must not process garbage */
if (retval < 2)
goto err_buf;
/* store returned data (more READs to be added) */
switch (request) {
@ -107,6 +110,7 @@ static int vendor_command(struct cypress *dev, unsigned char request,
break;
}
err_buf:
kfree(iobuf);
error:
return retval;

View File

@ -11,6 +11,7 @@
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@ -29,6 +30,17 @@
#include "onboard_usb_dev.h"
/* USB5744 register offset and mask */
#define USB5744_CMD_ATTACH 0xAA
#define USB5744_CMD_ATTACH_LSB 0x56
#define USB5744_CMD_CREG_ACCESS 0x99
#define USB5744_CMD_CREG_ACCESS_LSB 0x37
#define USB5744_CREG_MEM_ADDR 0x00
#define USB5744_CREG_WRITE 0x00
#define USB5744_CREG_RUNTIMEFLAGS2 0x41
#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
static void onboard_dev_attach_usb_driver(struct work_struct *work);
static struct usb_device_driver onboard_dev_usbdev_driver;
@ -98,6 +110,7 @@ static int onboard_dev_power_on(struct onboard_dev *onboard_dev)
fsleep(onboard_dev->pdata->reset_us);
gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0);
fsleep(onboard_dev->pdata->power_on_delay_us);
onboard_dev->is_powered_on = true;
@ -296,10 +309,50 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work)
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
}
static int onboard_dev_5744_i2c_init(struct i2c_client *client)
{
#if IS_ENABLED(CONFIG_I2C)
struct device *dev = &client->dev;
int ret;
/*
* Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
* and ready to respond to SMBus runtime commands.
* The command writes 5 bytes to memory and single data byte in
* configuration register.
*/
char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
USB5744_CREG_WRITE, 1,
USB5744_CREG_RUNTIMEFLAGS2,
USB5744_CREG_RUNTIMEFLAGS2_LSB,
USB5744_CREG_BYPASS_UDC_SUSPEND};
ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
if (ret)
return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
USB5744_CMD_CREG_ACCESS_LSB);
if (ret)
return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
/* Send SMBus command to boot hub. */
ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
USB5744_CMD_ATTACH_LSB);
if (ret < 0)
return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
return ret;
#else
return -ENODEV;
#endif
}
static int onboard_dev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct onboard_dev *onboard_dev;
struct device_node *i2c_node;
int err;
onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL);
@ -339,6 +392,27 @@ static int onboard_dev_probe(struct platform_device *pdev)
if (err)
return err;
i2c_node = of_parse_phandle(pdev->dev.of_node, "i2c-bus", 0);
if (i2c_node) {
struct i2c_client *client;
client = of_find_i2c_device_by_node(i2c_node);
of_node_put(i2c_node);
if (!client) {
err = -EPROBE_DEFER;
goto err_power_off;
}
if (of_device_is_compatible(pdev->dev.of_node, "usb424,2744") ||
of_device_is_compatible(pdev->dev.of_node, "usb424,5744"))
err = onboard_dev_5744_i2c_init(client);
put_device(&client->dev);
if (err < 0)
goto err_power_off;
}
/*
* The USB driver might have been detached from the USB devices by
* onboard_dev_remove() (e.g. through an 'unbind' by userspace),
@ -350,6 +424,10 @@ static int onboard_dev_probe(struct platform_device *pdev)
schedule_work(&attach_usb_driver_work);
return 0;
err_power_off:
onboard_dev_power_off(onboard_dev);
return err;
}
static void onboard_dev_remove(struct platform_device *pdev)

View File

@ -10,6 +10,7 @@
struct onboard_dev_pdata {
unsigned long reset_us; /* reset pulse width in us */
unsigned long power_on_delay_us; /* power on delay in us */
unsigned int num_supplies; /* number of supplies */
const char * const supply_names[MAX_SUPPLIES];
bool is_hub;
@ -24,6 +25,7 @@ static const struct onboard_dev_pdata microchip_usb424_data = {
static const struct onboard_dev_pdata microchip_usb5744_data = {
.reset_us = 0,
.power_on_delay_us = 10000,
.num_supplies = 2,
.supply_names = { "vdd", "vdd2" },
.is_hub = true,

View File

@ -232,7 +232,7 @@ static void eud_remove(struct platform_device *pdev)
}
static const struct of_device_id eud_dt_match[] = {
{ .compatible = "qcom,sc7280-eud" },
{ .compatible = "qcom,eud" },
{ }
};
MODULE_DEVICE_TABLE(of, eud_dt_match);

View File

@ -404,7 +404,6 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
struct usb_yurex *dev;
int len = 0;
char in_buffer[MAX_S64_STRLEN];
unsigned long flags;
dev = file->private_data;
@ -419,9 +418,9 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
return -EIO;
}
spin_lock_irqsave(&dev->lock, flags);
spin_lock_irq(&dev->lock);
scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu);
spin_unlock_irqrestore(&dev->lock, flags);
spin_unlock_irq(&dev->lock);
mutex_unlock(&dev->io_mutex);
return simple_read_from_buffer(buffer, count, ppos, in_buffer, len);
@ -511,8 +510,11 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
__func__, retval);
goto error;
}
if (set && timeout)
if (set && timeout) {
spin_lock_irq(&dev->lock);
dev->bbu = c2;
spin_unlock_irq(&dev->lock);
}
return timeout ? count : -EIO;
error:

View File

@ -416,10 +416,9 @@ static int mtk_musb_probe(struct platform_device *pdev)
return -ENOMEM;
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to create child devices at %p\n", np);
return ret;
}
if (ret)
return dev_err_probe(dev, ret,
"failed to create child devices at %p\n", np);
ret = mtk_musb_clks_get(glue);
if (ret)
@ -448,23 +447,19 @@ static int mtk_musb_probe(struct platform_device *pdev)
glue->role = USB_ROLE_NONE;
break;
default:
dev_err(&pdev->dev, "Error 'dr_mode' property\n");
return -EINVAL;
return dev_err_probe(&pdev->dev, -EINVAL,
"Error 'dr_mode' property\n");
}
glue->phy = devm_of_phy_get_by_index(dev, np, 0);
if (IS_ERR(glue->phy)) {
dev_err(dev, "fail to getting phy %ld\n",
PTR_ERR(glue->phy));
return PTR_ERR(glue->phy);
}
if (IS_ERR(glue->phy))
return dev_err_probe(dev, PTR_ERR(glue->phy),
"fail to getting phy\n");
glue->usb_phy = usb_phy_generic_register();
if (IS_ERR(glue->usb_phy)) {
dev_err(dev, "fail to registering usb-phy %ld\n",
PTR_ERR(glue->usb_phy));
return PTR_ERR(glue->usb_phy);
}
if (IS_ERR(glue->usb_phy))
return dev_err_probe(dev, PTR_ERR(glue->usb_phy),
"fail to registering usb-phy\n");
glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR(glue->xceiv)) {

View File

@ -49,30 +49,6 @@ static const struct musb_hdrc_config mpfs_musb_hdrc_config = {
.ram_bits = MPFS_MUSB_RAM_BITS,
};
static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
{
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
struct musb *musb = __hci;
spin_lock_irqsave(&musb->lock, flags);
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
if (musb->int_usb || musb->int_tx || musb->int_rx) {
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
ret = musb_interrupt(musb);
}
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
{
u8 devctl;
@ -111,6 +87,129 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
musb_readb(musb->mregs, MUSB_DEVCTL));
}
#define POLL_SECONDS 2
static void otg_timer(struct timer_list *t)
{
struct musb *musb = from_timer(musb, t, dev_timer);
void __iomem *mregs = musb->mregs;
u8 devctl;
unsigned long flags;
/*
* We poll because PolarFire SoC won't expose several OTG-critical
* status change events (from the transceiver) otherwise.
*/
devctl = musb_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
usb_otg_state_string(musb->xceiv->otg->state));
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
} else {
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
case OTG_STATE_A_WAIT_VFALL:
if (devctl & MUSB_DEVCTL_VBUS) {
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
break;
}
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
break;
case OTG_STATE_B_IDLE:
/*
* There's no ID-changed IRQ, so we have no good way to tell
* when to switch to the A-Default state machine (by setting
* the DEVCTL.Session bit).
*
* Workaround: whenever we're in B_IDLE, try setting the
* session flag every few seconds. If it works, ID was
* grounded and we're now in the A-Default state machine.
*
* NOTE: setting the session flag is _supposed_ to trigger
* SRP but clearly it doesn't.
*/
musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION);
devctl = musb_readb(mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
else
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
break;
default:
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || (musb->a_wait_bcon == 0 &&
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
del_timer(&musb->dev_timer);
last_timer = jiffies;
return;
}
if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) {
dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n");
return;
}
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
usb_otg_state_string(musb->xceiv->otg->state),
jiffies_to_msecs(timeout - jiffies));
mod_timer(&musb->dev_timer, timeout);
}
static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
{
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
struct musb *musb = __hci;
spin_lock_irqsave(&musb->lock, flags);
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
if (musb->int_usb || musb->int_tx || musb->int_rx) {
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
ret = musb_interrupt(musb);
}
/* Poll for ID change */
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
static int mpfs_musb_init(struct musb *musb)
{
struct device *dev = musb->controller;
@ -121,6 +220,8 @@ static int mpfs_musb_init(struct musb *musb)
return PTR_ERR(musb->xceiv);
}
timer_setup(&musb->dev_timer, otg_timer, 0);
musb->dyn_fifo = true;
musb->isr = mpfs_musb_interrupt;
@ -129,13 +230,24 @@ static int mpfs_musb_init(struct musb *musb)
return 0;
}
static int mpfs_musb_exit(struct musb *musb)
{
del_timer_sync(&musb->dev_timer);
return 0;
}
static const struct musb_platform_ops mpfs_ops = {
.quirks = MUSB_DMA_INVENTRA,
.init = mpfs_musb_init,
.exit = mpfs_musb_exit,
.fifo_mode = 2,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma_init = musbhs_dma_controller_create,
.dma_exit = musbhs_dma_controller_destroy,
#endif
#ifndef CONFIG_USB_MUSB_HOST
.try_idle = mpfs_musb_try_idle,
#endif
.set_vbus = mpfs_musb_set_vbus
};

View File

@ -374,6 +374,7 @@ static const struct of_device_id gpio_vbus_of_match[] = {
},
{},
};
MODULE_DEVICE_TABLE(of, gpio_vbus_of_match);
static struct platform_driver gpio_vbus_driver = {
.driver = {

View File

@ -18,6 +18,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
#include <linux/regulator/consumer.h>
#define DRIVER_NAME "mxs_phy"
@ -70,6 +71,9 @@
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
/* Anatop Registers */
#define ANADIG_REG_1P1_SET 0x114
#define ANADIG_REG_1P1_CLR 0x118
#define ANADIG_ANA_MISC0 0x150
#define ANADIG_ANA_MISC0_SET 0x154
#define ANADIG_ANA_MISC0_CLR 0x158
@ -117,6 +121,14 @@
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
/* System Integration Module (SIM) Registers */
#define SIM_GPR1 0x30
#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@ -149,6 +161,15 @@
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
/*
* At imx6q/6sl/6sx, the PHY2's clock is controlled by hardware directly,
* eg, according to PHY's suspend status. In these PHYs, we only need to
* open the clock at the initialization and close it at its shutdown routine.
* These PHYs can send resume signal without software interfere if not
* gate clock.
*/
#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
struct mxs_phy_data {
unsigned int flags;
};
@ -160,12 +181,14 @@ static const struct mxs_phy_data imx23_phy_data = {
static const struct mxs_phy_data imx6q_phy_data = {
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
MXS_PHY_NEED_IP_FIX |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6sl_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
MXS_PHY_NEED_IP_FIX |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data vf610_phy_data = {
@ -174,11 +197,13 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6ul_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx7ulp_phy_data = {
@ -201,9 +226,11 @@ struct mxs_phy {
struct clk *clk;
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
struct regmap *regmap_sim;
int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
struct regulator *phy_3p0;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@ -221,6 +248,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
return mxs_phy->data == &imx7ulp_phy_data;
}
static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
{
return mxs_phy->data == &imx6ul_phy_data;
}
/*
* PHY needs some 32K cycles to switch from 32K clock to
* bus (such as AHB/AXI, etc) clock.
@ -288,6 +320,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (ret)
goto disable_pll;
if (mxs_phy->phy_3p0) {
ret = regulator_enable(mxs_phy->phy_3p0);
if (ret) {
dev_err(mxs_phy->phy.dev,
"Failed to enable 3p0 regulator, ret=%d\n",
ret);
return ret;
}
}
/* Power up the PHY */
writel(0, base + HW_USBPHY_PWD);
@ -448,6 +490,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
if (is_imx7ulp_phy(mxs_phy))
mxs_phy_pll_enable(phy->io_priv, false);
if (mxs_phy->phy_3p0)
regulator_disable(mxs_phy->phy_3p0);
clk_disable_unprepare(mxs_phy->clk);
}
@ -503,12 +548,19 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
}
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
clk_disable_unprepare(mxs_phy->clk);
if (!(mxs_phy->port_id == 1 &&
(mxs_phy->data->flags &
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)))
clk_disable_unprepare(mxs_phy->clk);
} else {
mxs_phy_clock_switch_delay();
ret = clk_prepare_enable(mxs_phy->clk);
if (ret)
return ret;
if (!(mxs_phy->port_id == 1 &&
(mxs_phy->data->flags &
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK))) {
ret = clk_prepare_enable(mxs_phy->clk);
if (ret)
return ret;
}
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@ -738,6 +790,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
/* Currently, only imx7ulp has SIM module */
if (of_get_property(np, "nxp,sim", NULL)) {
mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
(np, "nxp,sim");
if (IS_ERR(mxs_phy->regmap_sim)) {
dev_dbg(&pdev->dev,
"failed to find regmap for sim\n");
return PTR_ERR(mxs_phy->regmap_sim);
}
}
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
@ -789,6 +852,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->clk = clk;
mxs_phy->data = of_device_get_match_data(&pdev->dev);
mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV)
/* not exist */
mxs_phy->phy_3p0 = NULL;
else if (IS_ERR(mxs_phy->phy_3p0))
return dev_err_probe(&pdev->dev, PTR_ERR(mxs_phy->phy_3p0),
"Getting regulator error\n");
if (mxs_phy->phy_3p0)
regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
platform_set_drvdata(pdev, mxs_phy);
device_set_wakeup_capable(&pdev->dev, true);
@ -804,28 +878,58 @@ static void mxs_phy_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
{
u32 mask = USB_PHY_VLLS_WAKEUP_EN;
/* If the SoCs don't have SIM, quit */
if (!mxs_phy->regmap_sim)
return;
if (on) {
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
udelay(500);
} else {
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
}
}
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{
unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
unsigned int reg;
u32 value;
/* If the SoCs don't have anatop, quit */
if (!mxs_phy->regmap_anatop)
return;
if (is_imx6q_phy(mxs_phy))
if (is_imx6q_phy(mxs_phy)) {
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop, reg,
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
else if (is_imx6sl_phy(mxs_phy))
} else if (is_imx6sl_phy(mxs_phy)) {
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop,
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
} else if (is_imx6ul_phy(mxs_phy)) {
reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
if (mxs_phy_get_vbus_status(mxs_phy) && on)
regmap_write(mxs_phy->regmap_anatop, reg, value);
else if (!on)
regmap_write(mxs_phy->regmap_anatop, reg, value);
}
}
static int mxs_phy_system_suspend(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
mxs_phy_wakeup_enable(mxs_phy, true);
}
return 0;
}
@ -834,8 +938,10 @@ static int mxs_phy_system_resume(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
mxs_phy_wakeup_enable(mxs_phy, false);
}
return 0;
}

View File

@ -11,6 +11,7 @@
#include <linux/usb/role.h>
#include <linux/property.h>
#include <linux/device.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@ -21,6 +22,7 @@ static const struct class role_class = {
struct usb_role_switch {
struct device dev;
struct lock_class_key key;
struct mutex lock; /* device lock*/
struct module *module; /* the module this device depends on */
enum usb_role role;
@ -326,6 +328,8 @@ static void usb_role_switch_release(struct device *dev)
{
struct usb_role_switch *sw = to_role_switch(dev);
mutex_destroy(&sw->lock);
lockdep_unregister_key(&sw->key);
kfree(sw);
}
@ -364,7 +368,8 @@ usb_role_switch_register(struct device *parent,
if (!sw)
return ERR_PTR(-ENOMEM);
mutex_init(&sw->lock);
lockdep_register_key(&sw->key);
mutex_init_with_key(&sw->lock, &sw->key);
sw->allow_userspace_control = desc->allow_userspace_control;
sw->usb2_port = desc->usb2_port;

View File

@ -138,7 +138,6 @@ static void aircable_process_read_urb(struct urb *urb)
static struct usb_serial_driver aircable_device = {
.driver = {
.owner = THIS_MODULE,
.name = "aircable",
},
.id_table = id_table,

View File

@ -599,7 +599,6 @@ static void ark3116_process_read_urb(struct urb *urb)
static struct usb_serial_driver ark3116_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ark3116",
},
.id_table = id_table,

View File

@ -66,7 +66,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
/* All of the device info needed for the serial converters */
static struct usb_serial_driver belkin_device = {
.driver = {
.owner = THIS_MODULE,
.name = "belkin",
},
.description = "Belkin / Peracom / GoHubs USB Serial Adapter",

View File

@ -837,7 +837,6 @@ static int ch341_reset_resume(struct usb_serial *serial)
static struct usb_serial_driver ch341_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ch341-uart",
},
.id_table = id_table,

View File

@ -299,7 +299,6 @@ struct cp210x_port_private {
static struct usb_serial_driver cp210x_device = {
.driver = {
.owner = THIS_MODULE,
.name = "cp210x",
},
.id_table = id_table,

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