USB / Thunderbolt changes for 5.16-rc1

Here is the big set of USB and Thunderbolt driver updates for 5.16-rc1.
 
 Nothing major in here, just lots of little cleanups and additions for
 new hardware, all of which have been in linux-next for a while with no
 reported problems.
 
 Included in here are:
 	- tiny Thunderbolt driver updates
 	- USB typec driver updates
 	- USB serial driver updates
 	- USB gadget driver updates
 	- dwc2 and dwc3 controller driver updates
 	- tiny USB host driver updates
 	- minor USB driver fixes and updates
 	- USB dts updates for various platforms
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYYPaPA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylH3wCghhRCsLl5axOKdbkJO2ZUApR71DwAn2Vjwo2Y
 Jgo6v3bVWWhL43YwCkf6
 =UNgI
 -----END PGP SIGNATURE-----

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

Pull USB / Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt driver updates for
  5.16-rc1.

  Nothing major in here, just lots of little cleanups and additions for
  new hardware, all of which have been in linux-next for a while with no
  reported problems.

  Included in here are:

   - tiny Thunderbolt driver updates

   - USB typec driver updates

   - USB serial driver updates

   - USB gadget driver updates

   - dwc2 and dwc3 controller driver updates

   - tiny USB host driver updates

   - minor USB driver fixes and updates

   - USB dts updates for various platforms"

* tag 'usb-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (123 commits)
  usb: gadget: Mark USB_FSL_QE broken on 64-bit
  usb: gadget: f_mass_storage: Disable eps during disconnect
  usb: gadget: udc: core: Revise comments for USB ep enable/disable
  USB: serial: keyspan: fix memleak on probe errors
  USB: serial: cp210x: use usb_control_msg_recv() and usb_control_msg_send()
  USB: serial: ch314: use usb_control_msg_recv()
  USB: iowarrior: fix control-message timeouts
  Documentation: USB: fix example bulk-message timeout
  usb: dwc2: stm32mp15: set otg_rev
  usb: dwc2: add otg_rev and otg_caps information for gadget driver
  dt-bindings: usb: dwc2: adopt otg properties defined in usb-drd.yaml
  dt-bindings: usb: dwc2: Add reference to usb-drd.yaml
  usb: gadget: uvc: implement dwPresentationTime and scrSourceClock
  usb: gadget: uvc: use on returned header len in video_encode_isoc_sg
  usb:gadget: f_uac1: fixed sync playback
  Docs: usb: remove :c:func: for usb_register and usb_deregister
  Docs: usb: update struct usb_driver
  usb: gadget: configfs: change config attributes file operation
  usb: gadget: configfs: add cfg_to_gadget_info() helper
  usb: dwc3: Align DWC3_EP_* flag macros
  ...
This commit is contained in:
Linus Torvalds 2021-11-04 07:50:43 -07:00
commit 048ff8629e
99 changed files with 2080 additions and 1044 deletions

View File

@ -1,74 +0,0 @@
Tegra SOC USB PHY
The device node for Tegra SOC USB PHY:
Required properties :
- compatible : For Tegra20, must contain "nvidia,tegra20-usb-phy".
For Tegra30, must contain "nvidia,tegra30-usb-phy". Otherwise, must contain
"nvidia,<chip>-usb-phy" plus at least one of the above, where <chip> is
tegra114, tegra124, tegra132, or tegra210.
- reg : Defines the following set of registers, in the order listed:
- The PHY's own register set.
Always present.
- The register set of the PHY containing the UTMI pad control registers.
Present if-and-only-if phy_type == utmi.
- phy_type : Should be one of "utmi", "ulpi" or "hsic".
- clocks : Defines the clocks listed in the clock-names property.
- clock-names : The following clock names must be present:
- reg: The clock needed to access the PHY's own registers. This is the
associated EHCI controller's clock. Always present.
- pll_u: PLL_U. Always present.
- timer: The timeout clock (clk_m). Present if phy_type == utmi.
- utmi-pads: The clock needed to access the UTMI pad control registers.
Present if phy_type == utmi.
- ulpi-link: The clock Tegra provides to the ULPI PHY (usually pad DAP_MCLK2
with pad group aka "nvidia,pins" cdev2 and pin mux option config aka
"nvidia,function" pllp_out4).
Present if phy_type == ulpi, and ULPI link mode is in use.
- resets : Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names : Must include the following entries:
- usb: The PHY's own reset signal.
- utmi-pads: The reset of the PHY containing the chip-wide UTMI pad control
registers. Required even if phy_type == ulpi.
Required properties for phy_type == ulpi:
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
Required PHY timing params for utmi phy, for all chips:
- nvidia,hssync-start-delay : Number of 480 Mhz clock cycles to wait before
start of sync launches RxActive
- nvidia,elastic-limit : Variable FIFO Depth of elastic input store
- nvidia,idle-wait-delay : Number of 480 Mhz clock cycles of idle to wait
before declare IDLE.
- nvidia,term-range-adj : Range adjusment on terminations
- Either one of the following for HS driver output control:
- nvidia,xcvr-setup : integer, uses the provided value.
- nvidia,xcvr-setup-use-fuses : boolean, indicates that the value is read
from the on-chip fuses
If both are provided, nvidia,xcvr-setup-use-fuses takes precedence.
- nvidia,xcvr-lsfslew : LS falling slew rate control.
- nvidia,xcvr-lsrslew : LS rising slew rate control.
Required PHY timing params for utmi phy, only on Tegra30 and above:
- nvidia,xcvr-hsslew : HS slew rate control.
- nvidia,hssquelch-level : HS squelch detector level.
- nvidia,hsdiscon-level : HS disconnect detector level.
Optional properties:
- nvidia,has-legacy-mode : boolean indicates whether this controller can
operate in legacy mode (as APX 2500 / 2600). In legacy mode some
registers are accessed through the APB_MISC base address instead of
the USB controller.
- nvidia,is-wired : boolean. Indicates whether we can do certain kind of power
optimizations for the devices that are always connected. e.g. modem.
- dr_mode : dual role mode. Indicates the working mode for the PHY. Can be
"host", "peripheral", or "otg". Defaults to "host" if not defined.
host means this is a host controller
peripheral means it is device controller
otg means it can operate as either ("on the go")
- nvidia,has-utmi-pad-registers : boolean indicates whether this controller
contains the UTMI pad control registers common to all USB controllers.
VBUS control (required for dr_mode == otg, optional for dr_mode == host):
- vbus-supply: regulator for VBUS

View File

@ -0,0 +1,373 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/nvidia,tegra20-usb-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra USB PHY
maintainers:
- Dmitry Osipenko <digetx@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
- Thierry Reding <thierry.reding@gmail.com>
properties:
compatible:
oneOf:
- items:
- enum:
- nvidia,tegra124-usb-phy
- nvidia,tegra114-usb-phy
- enum:
- nvidia,tegra30-usb-phy
- items:
- enum:
- nvidia,tegra30-usb-phy
- nvidia,tegra20-usb-phy
reg:
minItems: 1
maxItems: 2
description: |
PHY0 and PHY2 share power and ground, PHY0 contains shared registers.
PHY0 and PHY2 must specify two register sets, where the first set is
PHY own registers and the second set is the PHY0 registers.
clocks:
anyOf:
- items:
- description: Registers clock
- description: Main PHY clock
- items:
- description: Registers clock
- description: Main PHY clock
- description: ULPI PHY clock
- items:
- description: Registers clock
- description: Main PHY clock
- description: UTMI pads control registers clock
- items:
- description: Registers clock
- description: Main PHY clock
- description: UTMI timeout clock
- description: UTMI pads control registers clock
clock-names:
oneOf:
- items:
- const: reg
- const: pll_u
- items:
- const: reg
- const: pll_u
- const: ulpi-link
- items:
- const: reg
- const: pll_u
- const: utmi-pads
- items:
- const: reg
- const: pll_u
- const: timer
- const: utmi-pads
interrupts:
maxItems: 1
resets:
oneOf:
- maxItems: 1
description: PHY reset
- items:
- description: PHY reset
- description: UTMI pads reset
reset-names:
oneOf:
- const: usb
- items:
- const: usb
- const: utmi-pads
"#phy-cells":
const: 0
phy_type:
$ref: /schemas/types.yaml#/definitions/string
enum: [utmi, ulpi, hsic]
dr_mode:
$ref: /schemas/types.yaml#/definitions/string
enum: [host, peripheral, otg]
default: host
vbus-supply:
description: Regulator controlling USB VBUS.
nvidia,has-legacy-mode:
description: |
Indicates whether this controller can operate in legacy mode
(as APX 2500 / 2600). In legacy mode some registers are accessed
through the APB_MISC base address instead of the USB controller.
type: boolean
nvidia,is-wired:
description: |
Indicates whether we can do certain kind of power optimizations for
the devices that are always connected. e.g. modem.
type: boolean
nvidia,has-utmi-pad-registers:
description: |
Indicates whether this controller contains the UTMI pad control
registers common to all USB controllers.
type: boolean
nvidia,hssync-start-delay:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 31
description: |
Number of 480 MHz clock cycles to wait before start of sync launches
RxActive.
nvidia,elastic-limit:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 31
description: Variable FIFO Depth of elastic input store.
nvidia,idle-wait-delay:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 31
description: |
Number of 480 MHz clock cycles of idle to wait before declare IDLE.
nvidia,term-range-adj:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 15
description: Range adjustment on terminations.
nvidia,xcvr-setup:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 127
description: Input of XCVR cell, HS driver output control.
nvidia,xcvr-setup-use-fuses:
description: Indicates that the value is read from the on-chip fuses.
type: boolean
nvidia,xcvr-lsfslew:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 3
description: LS falling slew rate control.
nvidia,xcvr-lsrslew:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 3
description: LS rising slew rate control.
nvidia,xcvr-hsslew:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 511
description: HS slew rate control.
nvidia,hssquelch-level:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 3
description: HS squelch detector level.
nvidia,hsdiscon-level:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 7
description: HS disconnect detector level.
nvidia,phy-reset-gpio:
maxItems: 1
description: GPIO used to reset the PHY.
nvidia,pmc:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: Phandle to Power Management controller.
- description: USB controller ID.
description:
Phandle to Power Management controller.
required:
- compatible
- reg
- clocks
- clock-names
- resets
- reset-names
- "#phy-cells"
- phy_type
additionalProperties: false
allOf:
- if:
properties:
phy_type:
const: utmi
then:
properties:
reg:
minItems: 2
maxItems: 2
resets:
maxItems: 2
reset-names:
maxItems: 2
required:
- nvidia,hssync-start-delay
- nvidia,elastic-limit
- nvidia,idle-wait-delay
- nvidia,term-range-adj
- nvidia,xcvr-lsfslew
- nvidia,xcvr-lsrslew
anyOf:
- required: ["nvidia,xcvr-setup"]
- required: ["nvidia,xcvr-setup-use-fuses"]
if:
properties:
compatible:
contains:
const: nvidia,tegra30-usb-phy
then:
properties:
clocks:
maxItems: 3
clock-names:
items:
- const: reg
- const: pll_u
- const: utmi-pads
required:
- nvidia,xcvr-hsslew
- nvidia,hssquelch-level
- nvidia,hsdiscon-level
else:
properties:
clocks:
maxItems: 4
clock-names:
items:
- const: reg
- const: pll_u
- const: timer
- const: utmi-pads
- if:
properties:
phy_type:
const: ulpi
then:
properties:
reg:
minItems: 1
maxItems: 1
clocks:
minItems: 2
maxItems: 3
clock-names:
minItems: 2
maxItems: 3
oneOf:
- items:
- const: reg
- const: pll_u
- items:
- const: reg
- const: pll_u
- const: ulpi-link
resets:
minItems: 1
maxItems: 2
reset-names:
minItems: 1
maxItems: 2
examples:
- |
#include <dt-bindings/clock/tegra124-car.h>
usb-phy@7d008000 {
compatible = "nvidia,tegra124-usb-phy", "nvidia,tegra30-usb-phy";
reg = <0x7d008000 0x4000>,
<0x7d000000 0x4000>;
interrupts = <0 97 4>;
phy_type = "utmi";
clocks = <&tegra_car TEGRA124_CLK_USB3>,
<&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
resets = <&tegra_car 59>, <&tegra_car 22>;
reset-names = "usb", "utmi-pads";
#phy-cells = <0>;
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
nvidia,term-range-adj = <6>;
nvidia,xcvr-setup = <9>;
nvidia,xcvr-lsfslew = <0>;
nvidia,xcvr-lsrslew = <3>;
nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>;
nvidia,xcvr-hsslew = <12>;
nvidia,pmc = <&tegra_pmc 2>;
};
- |
#include <dt-bindings/clock/tegra20-car.h>
usb-phy@c5004000 {
compatible = "nvidia,tegra20-usb-phy";
reg = <0xc5004000 0x4000>;
interrupts = <0 21 4>;
phy_type = "ulpi";
clocks = <&tegra_car TEGRA20_CLK_USB2>,
<&tegra_car TEGRA20_CLK_PLL_U>,
<&tegra_car TEGRA20_CLK_CDEV2>;
clock-names = "reg", "pll_u", "ulpi-link";
resets = <&tegra_car 58>, <&tegra_car 22>;
reset-names = "usb", "utmi-pads";
#phy-cells = <0>;
nvidia,pmc = <&tegra_pmc 1>;
};

View File

@ -39,6 +39,10 @@ Required properties:
"ehci_clk" for the peripheral clock "ehci_clk" for the peripheral clock
"usb_clk" for the UTMI clock "usb_clk" for the UTMI clock
Optional properties:
- phy_type : For multi port host USB controllers, should be one of
"utmi", or "hsic".
usb1: ehci@800000 { usb1: ehci@800000 {
compatible = "atmel,at91sam9g45-ehci", "usb-ehci"; compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00800000 0x100000>; reg = <0x00800000 0x100000>;

View File

@ -9,6 +9,9 @@ title: DesignWare HS OTG USB 2.0 controller Bindings
maintainers: maintainers:
- Rob Herring <robh@kernel.org> - Rob Herring <robh@kernel.org>
allOf:
- $ref: usb-drd.yaml#
properties: properties:
compatible: compatible:
oneOf: oneOf:
@ -101,12 +104,15 @@ properties:
description: reference to the VBUS and ID sensing comparators supply, in description: reference to the VBUS and ID sensing comparators supply, in
order to perform OTG operation, used on STM32MP15 SoCs. order to perform OTG operation, used on STM32MP15 SoCs.
dr_mode: dr_mode: true
enum: [host, peripheral, otg]
usb-role-switch: otg-rev: true
$ref: /schemas/types.yaml#/definitions/flag
description: Support role switch. hnp-disable: true
srp-disable: true
usb-role-switch: true
g-rx-fifo-size: g-rx-fifo-size:
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32

View File

@ -13,6 +13,7 @@ properties:
compatible: compatible:
items: items:
- enum: - enum:
- qcom,ipq6018-dwc3
- qcom,msm8996-dwc3 - qcom,msm8996-dwc3
- qcom,msm8998-dwc3 - qcom,msm8998-dwc3
- qcom,sc7180-dwc3 - qcom,sc7180-dwc3

View File

@ -0,0 +1,108 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/smsc,usb3503.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: SMSC USB3503 High-Speed Hub Controller Device Tree Bindings
maintainers:
- Dongjin Kim <tobetter@gmail.com>
properties:
compatible:
enum:
- smsc,usb3503
- smsc,usb3503a
reg:
maxItems: 1
connect-gpios:
maxItems: 1
description: >
GPIO for connect
intn-gpios:
maxItems: 1
description: >
GPIO for interrupt
reset-gpios:
maxItems: 1
description: >
GPIO for reset
disabled-ports:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 3
items:
minimum: 1
maximum: 3
description: >
Specifies the ports unused using their port number. Do not describe this
property if all ports have to be enabled.
initial-mode:
enum: [1, 2]
description: >
Specifies initial mode. 1 for Hub mode, 2 for standby mode.
clocks:
maxItems: 1
description: >
Clock used for driving REFCLK signal. If not provided the driver assumes
that clock signal is always available, its rate is specified by REF_SEL
pins and a value from the primary reference clock frequencies table is
used.
clock-names:
const: refclk
refclk-frequency:
$ref: /schemas/types.yaml#/definitions/uint32
description: >
Frequency of the REFCLK signal as defined by REF_SEL pins. If not
provided, driver will not set rate of the REFCLK signal and assume that a
value from the primary reference clock frequencies table is used.
required:
- compatible
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
usb-hub@8 {
compatible = "smsc,usb3503";
reg = <0x08>;
connect-gpios = <&gpx3 0 1>;
disabled-ports = <2 3>;
intn-gpios = <&gpx3 4 1>;
reset-gpios = <&gpx3 5 1>;
initial-mode = <1>;
clocks = <&clks 80>;
clock-names = "refclk";
};
};
- |
#include <dt-bindings/gpio/gpio.h>
usb-hub {
/* I2C is not connected */
compatible = "smsc,usb3503";
initial-mode = <1>; /* initialize in HUB mode */
disabled-ports = <1>;
intn-gpios = <&pio 7 5 GPIO_ACTIVE_HIGH>; /* PH5 */
reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */
connect-gpios = <&pio 4 17 GPIO_ACTIVE_HIGH>; /* PE17 */
refclk-frequency = <19200000>;
};
...

View File

@ -73,15 +73,15 @@ properties:
phys: phys:
minItems: 1 minItems: 1
items: maxItems: 2
- description: USB2/HS PHY
- description: USB3/SS PHY
phy-names: phy-names:
minItems: 1 minItems: 1
maxItems: 2
items: items:
- const: usb2-phy enum:
- const: usb3-phy - usb2-phy
- usb3-phy
resets: resets:
minItems: 1 minItems: 1
@ -252,6 +252,14 @@ properties:
minimum: 0 minimum: 0
maximum: 0x3f maximum: 0x3f
snps,ref-clock-period-ns:
description:
Value for REFCLKPER field of GUCTL register for reference clock period in
nanoseconds, when the hardware set default does not match the actual
clock.
minimum: 1
maximum: 0x3ff
snps,rx-thr-num-pkt-prd: snps,rx-thr-num-pkt-prd:
description: description:
Periodic ESS RX packet threshold count (host mode only). Set this and Periodic ESS RX packet threshold count (host mode only). Set this and

View File

@ -12,10 +12,14 @@ maintainers:
description: | description: |
Texas Instruments 6598x Type-C Port Switch and Power Delivery controller Texas Instruments 6598x Type-C Port Switch and Power Delivery controller
A variant of this controller known as Apple CD321x or Apple ACE is also
present on hardware with Apple SoCs such as the M1.
properties: properties:
compatible: compatible:
enum: enum:
- ti,tps6598x - ti,tps6598x
- apple,cd321x
reg: reg:
maxItems: 1 maxItems: 1

View File

@ -1,39 +0,0 @@
SMSC USB3503 High-Speed Hub Controller
Required properties:
- compatible: Should be "smsc,usb3503" or "smsc,usb3503a".
Optional properties:
- reg: Specifies the i2c slave address, it is required and should be 0x08
if I2C is used.
- connect-gpios: Should specify GPIO for connect.
- disabled-ports: Should specify the ports unused.
'1' or '2' or '3' are available for this property to describe the port
number. 1~3 property values are possible to be described.
Do not describe this property if all ports have to be enabled.
- intn-gpios: Should specify GPIO for interrupt.
- reset-gpios: Should specify GPIO for reset.
- initial-mode: Should specify initial mode.
(1 for HUB mode, 2 for STANDBY mode)
- refclk: Clock used for driving REFCLK signal (optional, if not provided
the driver assumes that clock signal is always available, its
rate is specified by REF_SEL pins and a value from the primary
reference clock frequencies table is used). Use clocks and
clock-names in order to assign it
- refclk-frequency: Frequency of the REFCLK signal as defined by REF_SEL
pins (optional, if not provided, driver will not set rate of the
REFCLK signal and assume that a value from the primary reference
clock frequencies table is used)
Examples:
usb3503@8 {
compatible = "smsc,usb3503";
reg = <0x08>;
connect-gpios = <&gpx3 0 1>;
disabled-ports = <2 3>;
intn-gpios = <&gpx3 4 1>;
reset-gpios = <&gpx3 5 1>;
initial-mode = <1>;
clocks = <&clks 80>;
clock-names = "refclk";
};

View File

@ -57,9 +57,12 @@ structure. The skeleton driver declares a :c:type:`usb_driver` as::
.name = "skeleton", .name = "skeleton",
.probe = skel_probe, .probe = skel_probe,
.disconnect = skel_disconnect, .disconnect = skel_disconnect,
.fops = &skel_fops, .suspend = skel_suspend,
.minor = USB_SKEL_MINOR_BASE, .resume = skel_resume,
.pre_reset = skel_pre_reset,
.post_reset = skel_post_reset,
.id_table = skel_table, .id_table = skel_table,
.supports_autosuspend = 1,
}; };
@ -81,7 +84,7 @@ this user-space interaction. The skeleton driver needs this kind of
interface, so it provides a minor starting number and a pointer to its interface, so it provides a minor starting number and a pointer to its
:c:type:`file_operations` functions. :c:type:`file_operations` functions.
The USB driver is then registered with a call to :c:func:`usb_register`, The USB driver is then registered with a call to usb_register(),
usually in the driver's init function, as shown here:: usually in the driver's init function, as shown here::
static int __init usb_skel_init(void) static int __init usb_skel_init(void)
@ -102,7 +105,7 @@ usually in the driver's init function, as shown here::
When the driver is unloaded from the system, it needs to deregister When the driver is unloaded from the system, it needs to deregister
itself with the USB subsystem. This is done with the :c:func:`usb_deregister` itself with the USB subsystem. This is done with usb_deregister()
function:: function::
static void __exit usb_skel_exit(void) static void __exit usb_skel_exit(void)
@ -231,7 +234,7 @@ error message. This can be shown with the following code::
skel->bulk_in_endpointAddr), skel->bulk_in_endpointAddr),
skel->bulk_in_buffer, skel->bulk_in_buffer,
skel->bulk_in_size, skel->bulk_in_size,
&count, HZ*10); &count, 5000);
/* if the read was successful, copy the data to user space */ /* if the read was successful, copy the data to user space */
if (!retval) { if (!retval) {
if (copy_to_user (buffer, skel->bulk_in_buffer, count)) if (copy_to_user (buffer, skel->bulk_in_buffer, count))

View File

@ -1085,6 +1085,7 @@
g-np-tx-fifo-size = <32>; g-np-tx-fifo-size = <32>;
g-tx-fifo-size = <256 16 16 16 16 16 16 16>; g-tx-fifo-size = <256 16 16 16 16 16 16 16>;
dr_mode = "otg"; dr_mode = "otg";
otg-rev = <0x200>;
usb33d-supply = <&usb33>; usb33d-supply = <&usb33>;
status = "disabled"; status = "disabled";
}; };

View File

@ -139,29 +139,13 @@ static const struct i2c_inst_data bsg2150_data[] = {
{} {}
}; };
/* static const struct i2c_inst_data int3515_data[] = {
* Device with _HID INT3515 (TI PD controllers) has some unresolved interrupt { "tps6598x", IRQ_RESOURCE_APIC, 0 },
* issues. The most common problem seen is interrupt flood. { "tps6598x", IRQ_RESOURCE_APIC, 1 },
* { "tps6598x", IRQ_RESOURCE_APIC, 2 },
* There are at least two known causes. Firstly, on some boards, the { "tps6598x", IRQ_RESOURCE_APIC, 3 },
* I2CSerialBus resource index does not match the Interrupt resource, i.e. they {}
* are not one-to-one mapped like in the array below. Secondly, on some boards };
* the IRQ line from the PD controller is not actually connected at all. But the
* interrupt flood is also seen on some boards where those are not a problem, so
* there are some other problems as well.
*
* Because of the issues with the interrupt, the device is disabled for now. If
* you wish to debug the issues, uncomment the below, and add an entry for the
* INT3515 device to the i2c_multi_instance_ids table.
*
* static const struct i2c_inst_data int3515_data[] = {
* { "tps6598x", IRQ_RESOURCE_APIC, 0 },
* { "tps6598x", IRQ_RESOURCE_APIC, 1 },
* { "tps6598x", IRQ_RESOURCE_APIC, 2 },
* { "tps6598x", IRQ_RESOURCE_APIC, 3 },
* { }
* };
*/
/* /*
* Note new device-ids must also be added to i2c_multi_instantiate_ids in * Note new device-ids must also be added to i2c_multi_instantiate_ids in
@ -170,6 +154,7 @@ static const struct i2c_inst_data bsg2150_data[] = {
static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = {
{ "BSG1160", (unsigned long)bsg1160_data }, { "BSG1160", (unsigned long)bsg1160_data },
{ "BSG2150", (unsigned long)bsg2150_data }, { "BSG2150", (unsigned long)bsg2150_data },
{ "INT3515", (unsigned long)int3515_data },
{ } { }
}; };
MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids); MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids);

View File

@ -17,7 +17,7 @@
#define TB_CTL_RX_PKG_COUNT 10 #define TB_CTL_RX_PKG_COUNT 10
#define TB_CTL_RETRIES 1 #define TB_CTL_RETRIES 4
/** /**
* struct tb_ctl - Thunderbolt control channel * struct tb_ctl - Thunderbolt control channel

View File

@ -730,7 +730,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
/* Full buffer size except new line and null termination */ /* Full buffer size except new line and null termination */
get_modalias(svc, buf, PAGE_SIZE - 2); get_modalias(svc, buf, PAGE_SIZE - 2);
return sprintf(buf, "%s\n", buf); return strlen(strcat(buf, "\n"));
} }
static DEVICE_ATTR_RO(modalias); static DEVICE_ATTR_RO(modalias);

View File

@ -1015,9 +1015,11 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
int error = -ENOMEM; int error = -ENOMEM;
int i, length; int i, length;
unsigned int maxpacket, num_packets; unsigned int maxpacket, num_packets;
size_t size;
/* instance init */ /* instance init */
instance = kzalloc(sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL); size = struct_size(instance, urbs, num_rcv_urbs + num_snd_urbs);
instance = kzalloc(size, GFP_KERNEL);
if (!instance) if (!instance)
return -ENOMEM; return -ENOMEM;

View File

@ -514,7 +514,7 @@ int hw_device_reset(struct ci_hdrc *ci)
return 0; return 0;
} }
static irqreturn_t ci_irq(int irq, void *data) static irqreturn_t ci_irq_handler(int irq, void *data)
{ {
struct ci_hdrc *ci = data; struct ci_hdrc *ci = data;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
@ -567,6 +567,15 @@ static irqreturn_t ci_irq(int irq, void *data)
return ret; return ret;
} }
static void ci_irq(struct ci_hdrc *ci)
{
unsigned long flags;
local_irq_save(flags);
ci_irq_handler(ci->irq, ci);
local_irq_restore(flags);
}
static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
void *ptr) void *ptr)
{ {
@ -576,7 +585,7 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
cbl->connected = event; cbl->connected = event;
cbl->changed = true; cbl->changed = true;
ci_irq(ci->irq, ci); ci_irq(ci);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
@ -617,7 +626,7 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
if (cable) { if (cable) {
cable->changed = true; cable->changed = true;
cable->connected = false; cable->connected = false;
ci_irq(ci->irq, ci); ci_irq(ci);
spin_unlock_irqrestore(&ci->lock, flags); spin_unlock_irqrestore(&ci->lock, flags);
if (ci->wq && role != USB_ROLE_NONE) if (ci->wq && role != USB_ROLE_NONE)
flush_workqueue(ci->wq); flush_workqueue(ci->wq);
@ -635,7 +644,7 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
if (cable) { if (cable) {
cable->changed = true; cable->changed = true;
cable->connected = true; cable->connected = true;
ci_irq(ci->irq, ci); ci_irq(ci);
} }
spin_unlock_irqrestore(&ci->lock, flags); spin_unlock_irqrestore(&ci->lock, flags);
pm_runtime_put_sync(ci->dev); pm_runtime_put_sync(ci->dev);
@ -1174,7 +1183,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
} }
} }
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
ci->platdata->name, ci); ci->platdata->name, ci);
if (ret) if (ret)
goto stop; goto stop;
@ -1295,11 +1304,11 @@ static void ci_extcon_wakeup_int(struct ci_hdrc *ci)
if (!IS_ERR(cable_id->edev) && ci->is_otg && if (!IS_ERR(cable_id->edev) && ci->is_otg &&
(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
ci_irq(ci->irq, ci); ci_irq(ci);
if (!IS_ERR(cable_vbus->edev) && ci->is_otg && if (!IS_ERR(cable_vbus->edev) && ci->is_otg &&
(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
ci_irq(ci->irq, ci); ci_irq(ci);
} }
static int ci_controller_resume(struct device *dev) static int ci_controller_resume(struct device *dev)

View File

@ -49,6 +49,8 @@ ctrl_endpt_in_desc = {
.wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX),
}; };
static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
struct td_node *node);
/** /**
* hw_ep_bit: calculates the bit number * hw_ep_bit: calculates the bit number
* @num: endpoint number * @num: endpoint number
@ -599,6 +601,12 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
prevlastnode->ptr->next = cpu_to_le32(next); prevlastnode->ptr->next = cpu_to_le32(next);
wmb(); wmb();
if (ci->rev == CI_REVISION_22) {
if (!hw_read(ci, OP_ENDPTSTAT, BIT(n)))
reprime_dtd(ci, hwep, prevlastnode);
}
if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) if (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
goto done; goto done;
do { do {

View File

@ -1859,7 +1859,6 @@ static const struct usb_device_id acm_ids[] = {
{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */ { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */ { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */ { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */ { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */ { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */ { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */

View File

@ -911,7 +911,7 @@ static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb)
return rv; return rv;
} }
static struct wwan_port_ops wdm_wwan_port_ops = { static const struct wwan_port_ops wdm_wwan_port_ops = {
.start = wdm_wwan_port_start, .start = wdm_wwan_port_start,
.stop = wdm_wwan_port_stop, .stop = wdm_wwan_port_stop,
.tx = wdm_wwan_port_tx, .tx = wdm_wwan_port_tx,

View File

@ -986,7 +986,7 @@ int usb_get_bos_descriptor(struct usb_device *dev)
__u8 cap_type; __u8 cap_type;
int ret; int ret;
bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL); bos = kzalloc(sizeof(*bos), GFP_KERNEL);
if (!bos) if (!bos)
return -ENOMEM; return -ENOMEM;
@ -1007,7 +1007,7 @@ int usb_get_bos_descriptor(struct usb_device *dev)
if (total_len < length) if (total_len < length)
return -EINVAL; return -EINVAL;
dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL); dev->bos = kzalloc(sizeof(*dev->bos), GFP_KERNEL);
if (!dev->bos) if (!dev->bos)
return -ENOMEM; return -ENOMEM;

View File

@ -32,6 +32,7 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usbdevice_fs.h> #include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h> /* for usbcore internals */ #include <linux/usb/hcd.h> /* for usbcore internals */
#include <linux/usb/quirks.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/security.h> #include <linux/security.h>
@ -1102,14 +1103,55 @@ static int usbdev_release(struct inode *inode, struct file *file)
return 0; return 0;
} }
static void usbfs_blocking_completion(struct urb *urb)
{
complete((struct completion *) urb->context);
}
/*
* Much like usb_start_wait_urb, but returns status separately from
* actual_length and uses a killable wait.
*/
static int usbfs_start_wait_urb(struct urb *urb, int timeout,
unsigned int *actlen)
{
DECLARE_COMPLETION_ONSTACK(ctx);
unsigned long expire;
int rc;
urb->context = &ctx;
urb->complete = usbfs_blocking_completion;
*actlen = 0;
rc = usb_submit_urb(urb, GFP_KERNEL);
if (unlikely(rc))
return rc;
expire = (timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT);
rc = wait_for_completion_killable_timeout(&ctx, expire);
if (rc <= 0) {
usb_kill_urb(urb);
*actlen = urb->actual_length;
if (urb->status != -ENOENT)
; /* Completed before it was killed */
else if (rc < 0)
return -EINTR;
else
return -ETIMEDOUT;
}
*actlen = urb->actual_length;
return urb->status;
}
static int do_proc_control(struct usb_dev_state *ps, static int do_proc_control(struct usb_dev_state *ps,
struct usbdevfs_ctrltransfer *ctrl) struct usbdevfs_ctrltransfer *ctrl)
{ {
struct usb_device *dev = ps->dev; struct usb_device *dev = ps->dev;
unsigned int tmo; unsigned int tmo;
unsigned char *tbuf; unsigned char *tbuf;
unsigned wLength; unsigned int wLength, actlen;
int i, pipe, ret; int i, pipe, ret;
struct urb *urb = NULL;
struct usb_ctrlrequest *dr = NULL;
ret = check_ctrlrecip(ps, ctrl->bRequestType, ctrl->bRequest, ret = check_ctrlrecip(ps, ctrl->bRequestType, ctrl->bRequest,
ctrl->wIndex); ctrl->wIndex);
@ -1122,51 +1164,63 @@ static int do_proc_control(struct usb_dev_state *ps,
sizeof(struct usb_ctrlrequest)); sizeof(struct usb_ctrlrequest));
if (ret) if (ret)
return ret; return ret;
ret = -ENOMEM;
tbuf = (unsigned char *)__get_free_page(GFP_KERNEL); tbuf = (unsigned char *)__get_free_page(GFP_KERNEL);
if (!tbuf) { if (!tbuf)
ret = -ENOMEM;
goto done; goto done;
} urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
goto done;
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
if (!dr)
goto done;
dr->bRequestType = ctrl->bRequestType;
dr->bRequest = ctrl->bRequest;
dr->wValue = cpu_to_le16(ctrl->wValue);
dr->wIndex = cpu_to_le16(ctrl->wIndex);
dr->wLength = cpu_to_le16(ctrl->wLength);
tmo = ctrl->timeout; tmo = ctrl->timeout;
snoop(&dev->dev, "control urb: bRequestType=%02x " snoop(&dev->dev, "control urb: bRequestType=%02x "
"bRequest=%02x wValue=%04x " "bRequest=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n", "wIndex=%04x wLength=%04x\n",
ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
ctrl->wIndex, ctrl->wLength); ctrl->wIndex, ctrl->wLength);
if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength) {
if ((ctrl->bRequestType & USB_DIR_IN) && wLength) {
pipe = usb_rcvctrlpipe(dev, 0); pipe = usb_rcvctrlpipe(dev, 0);
snoop_urb(dev, NULL, pipe, ctrl->wLength, tmo, SUBMIT, NULL, 0); usb_fill_control_urb(urb, dev, pipe, (unsigned char *) dr, tbuf,
wLength, NULL, NULL);
snoop_urb(dev, NULL, pipe, wLength, tmo, SUBMIT, NULL, 0);
usb_unlock_device(dev); usb_unlock_device(dev);
i = usb_control_msg(dev, pipe, ctrl->bRequest, i = usbfs_start_wait_urb(urb, tmo, &actlen);
ctrl->bRequestType, ctrl->wValue, ctrl->wIndex,
tbuf, ctrl->wLength, tmo);
usb_lock_device(dev); usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, tbuf, actlen);
tbuf, max(i, 0)); if (!i && actlen) {
if ((i > 0) && ctrl->wLength) { if (copy_to_user(ctrl->data, tbuf, actlen)) {
if (copy_to_user(ctrl->data, tbuf, i)) {
ret = -EFAULT; ret = -EFAULT;
goto done; goto recv_fault;
} }
} }
} else { } else {
if (ctrl->wLength) { if (wLength) {
if (copy_from_user(tbuf, ctrl->data, ctrl->wLength)) { if (copy_from_user(tbuf, ctrl->data, wLength)) {
ret = -EFAULT; ret = -EFAULT;
goto done; goto done;
} }
} }
pipe = usb_sndctrlpipe(dev, 0); pipe = usb_sndctrlpipe(dev, 0);
snoop_urb(dev, NULL, pipe, ctrl->wLength, tmo, SUBMIT, usb_fill_control_urb(urb, dev, pipe, (unsigned char *) dr, tbuf,
tbuf, ctrl->wLength); wLength, NULL, NULL);
snoop_urb(dev, NULL, pipe, wLength, tmo, SUBMIT, tbuf, wLength);
usb_unlock_device(dev); usb_unlock_device(dev);
i = usb_control_msg(dev, pipe, ctrl->bRequest, i = usbfs_start_wait_urb(urb, tmo, &actlen);
ctrl->bRequestType, ctrl->wValue, ctrl->wIndex,
tbuf, ctrl->wLength, tmo);
usb_lock_device(dev); usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0); snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, NULL, 0);
} }
if (i < 0 && i != -EPIPE) { if (i < 0 && i != -EPIPE) {
dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL " dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "
@ -1174,8 +1228,15 @@ static int do_proc_control(struct usb_dev_state *ps,
current->comm, ctrl->bRequestType, ctrl->bRequest, current->comm, ctrl->bRequestType, ctrl->bRequest,
ctrl->wLength, i); ctrl->wLength, i);
} }
ret = i; ret = (i < 0 ? i : actlen);
recv_fault:
/* Linger a bit, prior to the next control message. */
if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
msleep(200);
done: done:
kfree(dr);
usb_free_urb(urb);
free_page((unsigned long) tbuf); free_page((unsigned long) tbuf);
usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) + usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) +
sizeof(struct usb_ctrlrequest)); sizeof(struct usb_ctrlrequest));
@ -1195,10 +1256,11 @@ static int do_proc_bulk(struct usb_dev_state *ps,
struct usbdevfs_bulktransfer *bulk) struct usbdevfs_bulktransfer *bulk)
{ {
struct usb_device *dev = ps->dev; struct usb_device *dev = ps->dev;
unsigned int tmo, len1, pipe; unsigned int tmo, len1, len2, pipe;
int len2;
unsigned char *tbuf; unsigned char *tbuf;
int i, ret; int i, ret;
struct urb *urb = NULL;
struct usb_host_endpoint *ep;
ret = findintfep(ps->dev, bulk->ep); ret = findintfep(ps->dev, bulk->ep);
if (ret < 0) if (ret < 0)
@ -1206,14 +1268,17 @@ static int do_proc_bulk(struct usb_dev_state *ps,
ret = checkintf(ps, ret); ret = checkintf(ps, ret);
if (ret) if (ret)
return ret; return ret;
len1 = bulk->len;
if (len1 < 0 || len1 >= (INT_MAX - sizeof(struct urb)))
return -EINVAL;
if (bulk->ep & USB_DIR_IN) if (bulk->ep & USB_DIR_IN)
pipe = usb_rcvbulkpipe(dev, bulk->ep & 0x7f); pipe = usb_rcvbulkpipe(dev, bulk->ep & 0x7f);
else else
pipe = usb_sndbulkpipe(dev, bulk->ep & 0x7f); pipe = usb_sndbulkpipe(dev, bulk->ep & 0x7f);
if (!usb_maxpacket(dev, pipe, !(bulk->ep & USB_DIR_IN))) ep = usb_pipe_endpoint(dev, pipe);
return -EINVAL; if (!ep || !usb_endpoint_maxp(&ep->desc))
len1 = bulk->len;
if (len1 >= (INT_MAX - sizeof(struct urb)))
return -EINVAL; return -EINVAL;
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb)); ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
if (ret) if (ret)
@ -1223,17 +1288,29 @@ static int do_proc_bulk(struct usb_dev_state *ps,
* len1 can be almost arbitrarily large. Don't WARN if it's * len1 can be almost arbitrarily large. Don't WARN if it's
* too big, just fail the request. * too big, just fail the request.
*/ */
ret = -ENOMEM;
tbuf = kmalloc(len1, GFP_KERNEL | __GFP_NOWARN); tbuf = kmalloc(len1, GFP_KERNEL | __GFP_NOWARN);
if (!tbuf) { if (!tbuf)
ret = -ENOMEM;
goto done; goto done;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
goto done;
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT) {
pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
usb_fill_int_urb(urb, dev, pipe, tbuf, len1,
NULL, NULL, ep->desc.bInterval);
} else {
usb_fill_bulk_urb(urb, dev, pipe, tbuf, len1, NULL, NULL);
} }
tmo = bulk->timeout; tmo = bulk->timeout;
if (bulk->ep & 0x80) { if (bulk->ep & 0x80) {
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0); snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
usb_unlock_device(dev); usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); i = usbfs_start_wait_urb(urb, tmo, &len2);
usb_lock_device(dev); usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2); snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2);
@ -1253,12 +1330,13 @@ static int do_proc_bulk(struct usb_dev_state *ps,
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1); snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1);
usb_unlock_device(dev); usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); i = usbfs_start_wait_urb(urb, tmo, &len2);
usb_lock_device(dev); usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0); snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);
} }
ret = (i < 0 ? i : len2); ret = (i < 0 ? i : len2);
done: done:
usb_free_urb(urb);
kfree(tbuf); kfree(tbuf);
usbfs_decrease_memory_usage(len1 + sizeof(struct urb)); usbfs_decrease_memory_usage(len1 + sizeof(struct urb));
return ret; return ret;

View File

@ -2732,14 +2732,14 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
hcd->irq = irqnum; hcd->irq = irqnum;
dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum, dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
(hcd->driver->flags & HCD_MEMORY) ? (hcd->driver->flags & HCD_MEMORY) ?
"io mem" : "io base", "io mem" : "io port",
(unsigned long long)hcd->rsrc_start); (unsigned long long)hcd->rsrc_start);
} else { } else {
hcd->irq = 0; hcd->irq = 0;
if (hcd->rsrc_start) if (hcd->rsrc_start)
dev_info(hcd->self.controller, "%s 0x%08llx\n", dev_info(hcd->self.controller, "%s 0x%08llx\n",
(hcd->driver->flags & HCD_MEMORY) ? (hcd->driver->flags & HCD_MEMORY) ?
"io mem" : "io base", "io mem" : "io port",
(unsigned long long)hcd->rsrc_start); (unsigned long long)hcd->rsrc_start);
} }
return 0; return 0;

View File

@ -238,11 +238,14 @@ enum dwc2_ep0_state {
/** /**
* struct dwc2_core_params - Parameters for configuring the core * struct dwc2_core_params - Parameters for configuring the core
* *
* @otg_cap: Specifies the OTG capabilities. * @otg_caps: Specifies the OTG capabilities. OTG caps from the platform parameters,
* 0 - HNP and SRP capable * used to setup the:
* 1 - SRP Only capable * - HNP and SRP capable
* 2 - No HNP/SRP capable (always available) * - SRP Only capable
* Defaults to best available option (0, 1, then 2) * - No HNP/SRP capable (always available)
* Defaults to best available option
* - OTG revision number the device is compliant with, in binary-coded
* decimal (i.e. 2.0 is 0200H). (see struct usb_otg_caps)
* @host_dma: Specifies whether to use slave or DMA mode for accessing * @host_dma: Specifies whether to use slave or DMA mode for accessing
* the data FIFOs. The driver will automatically detect the * the data FIFOs. The driver will automatically detect the
* value for this parameter if none is specified. * value for this parameter if none is specified.
@ -453,11 +456,7 @@ enum dwc2_ep0_state {
* default described above. * default described above.
*/ */
struct dwc2_core_params { struct dwc2_core_params {
u8 otg_cap; struct usb_otg_caps otg_caps;
#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0
#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1
#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
u8 phy_type; u8 phy_type;
#define DWC2_PHY_TYPE_PARAM_FS 0 #define DWC2_PHY_TYPE_PARAM_FS 0
#define DWC2_PHY_TYPE_PARAM_UTMI 1 #define DWC2_PHY_TYPE_PARAM_UTMI 1

View File

@ -670,7 +670,9 @@ static int params_show(struct seq_file *seq, void *v)
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
int i; int i;
print_param(seq, p, otg_cap); print_param(seq, p, otg_caps.hnp_support);
print_param(seq, p, otg_caps.srp_support);
print_param(seq, p, otg_caps.otg_rev);
print_param(seq, p, dma_desc_enable); print_param(seq, p, dma_desc_enable);
print_param(seq, p, dma_desc_fs_enable); print_param(seq, p, dma_desc_fs_enable);
print_param(seq, p, speed); print_param(seq, p, speed);

View File

@ -7,6 +7,7 @@
* Author(s): Amelie Delaunay <amelie.delaunay@st.com> * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
*/ */
#include <linux/clk.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/usb/role.h> #include <linux/usb/role.h>
@ -25,9 +26,9 @@ static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
dwc2_writel(hsotg, gotgctl, GOTGCTL); dwc2_writel(hsotg, gotgctl, GOTGCTL);
dwc2_force_mode(hsotg, false);
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
} }
static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid) static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
@ -39,6 +40,7 @@ static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
(!valid && !(gotgctl & GOTGCTL_ASESVLD))) (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
return -EALREADY; return -EALREADY;
gotgctl &= ~GOTGCTL_BVALOVAL;
if (valid) if (valid)
gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL; gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
else else
@ -57,6 +59,7 @@ static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
(!valid && !(gotgctl & GOTGCTL_BSESVLD))) (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
return -EALREADY; return -EALREADY;
gotgctl &= ~GOTGCTL_AVALOVAL;
if (valid) if (valid)
gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL; gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
else else
@ -86,6 +89,20 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
} }
#endif #endif
/*
* In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of
* the probe and enabled on udc_start.
* If role-switch set is called before the udc_start, we need to enable
* the clock to read/write GOTGCTL and GUSBCFG registers to override
* mode and sessions. It is the case if cable is plugged at boot.
*/
if (!hsotg->ll_hw_enabled && hsotg->clk) {
int ret = clk_prepare_enable(hsotg->clk);
if (ret)
return ret;
}
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
if (role == USB_ROLE_HOST) { if (role == USB_ROLE_HOST) {
@ -110,6 +127,9 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
/* This will raise a Connector ID Status Change Interrupt */ /* This will raise a Connector ID Status Change Interrupt */
dwc2_force_mode(hsotg, role == USB_ROLE_HOST); dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
if (!hsotg->ll_hw_enabled && hsotg->clk)
clk_disable_unprepare(hsotg->clk);
dev_dbg(hsotg->dev, "%s-session valid\n", dev_dbg(hsotg->dev, "%s-session valid\n",
role == USB_ROLE_NONE ? "No" : role == USB_ROLE_NONE ? "No" :
role == USB_ROLE_HOST ? "A" : "B"); role == USB_ROLE_HOST ? "A" : "B");

View File

@ -4966,6 +4966,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
hsotg->gadget.name = dev_name(dev); hsotg->gadget.name = dev_name(dev);
hsotg->gadget.otg_caps = &hsotg->params.otg_caps;
hsotg->remote_wakeup_allowed = 0; hsotg->remote_wakeup_allowed = 0;
if (hsotg->params.lpm) if (hsotg->params.lpm)

View File

@ -138,19 +138,15 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
switch (hsotg->hw_params.op_mode) { switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
if (hsotg->params.otg_cap == if (hsotg->params.otg_caps.hnp_support &&
DWC2_CAP_PARAM_HNP_SRP_CAPABLE) hsotg->params.otg_caps.srp_support)
usbcfg |= GUSBCFG_HNPCAP; usbcfg |= GUSBCFG_HNPCAP;
if (hsotg->params.otg_cap != fallthrough;
DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
usbcfg |= GUSBCFG_SRPCAP;
break;
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
if (hsotg->params.otg_cap != if (hsotg->params.otg_caps.srp_support)
DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
usbcfg |= GUSBCFG_SRPCAP; usbcfg |= GUSBCFG_SRPCAP;
break; break;

View File

@ -36,6 +36,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/usb/of.h>
#include "core.h" #include "core.h"
@ -53,7 +54,8 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->speed = DWC2_SPEED_PARAM_HIGH; p->speed = DWC2_SPEED_PARAM_HIGH;
p->host_rx_fifo_size = 512; p->host_rx_fifo_size = 512;
p->host_nperio_tx_fifo_size = 512; p->host_nperio_tx_fifo_size = 512;
@ -84,7 +86,8 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->host_rx_fifo_size = 525; p->host_rx_fifo_size = 525;
p->host_nperio_tx_fifo_size = 128; p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 256; p->host_perio_tx_fifo_size = 256;
@ -97,7 +100,8 @@ static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = 2; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->host_rx_fifo_size = 288; p->host_rx_fifo_size = 288;
p->host_nperio_tx_fifo_size = 128; p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 96; p->host_perio_tx_fifo_size = 96;
@ -111,7 +115,8 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->speed = DWC2_SPEED_PARAM_HIGH; p->speed = DWC2_SPEED_PARAM_HIGH;
p->host_rx_fifo_size = 512; p->host_rx_fifo_size = 512;
p->host_nperio_tx_fifo_size = 500; p->host_nperio_tx_fifo_size = 500;
@ -144,7 +149,8 @@ static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->speed = DWC2_SPEED_PARAM_FULL; p->speed = DWC2_SPEED_PARAM_FULL;
p->host_rx_fifo_size = 128; p->host_rx_fifo_size = 128;
p->host_nperio_tx_fifo_size = 96; p->host_nperio_tx_fifo_size = 96;
@ -168,7 +174,9 @@ static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->otg_caps.otg_rev = 0x200;
p->speed = DWC2_SPEED_PARAM_FULL; p->speed = DWC2_SPEED_PARAM_FULL;
p->host_rx_fifo_size = 128; p->host_rx_fifo_size = 128;
p->host_nperio_tx_fifo_size = 96; p->host_nperio_tx_fifo_size = 96;
@ -188,7 +196,9 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
{ {
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->otg_caps.otg_rev = 0x200;
p->activate_stm_id_vb_detection = !device_property_read_bool(hsotg->dev, "usb-role-switch"); p->activate_stm_id_vb_detection = !device_property_read_bool(hsotg->dev, "usb-role-switch");
p->host_rx_fifo_size = 440; p->host_rx_fifo_size = 440;
p->host_nperio_tx_fifo_size = 256; p->host_nperio_tx_fifo_size = 256;
@ -241,23 +251,22 @@ MODULE_DEVICE_TABLE(acpi, dwc2_acpi_match);
static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg) static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg)
{ {
u8 val;
switch (hsotg->hw_params.op_mode) { switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE; hsotg->params.otg_caps.hnp_support = true;
hsotg->params.otg_caps.srp_support = true;
break; break;
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE; hsotg->params.otg_caps.hnp_support = false;
hsotg->params.otg_caps.srp_support = true;
break; break;
default: default:
val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; hsotg->params.otg_caps.hnp_support = false;
hsotg->params.otg_caps.srp_support = false;
break; break;
} }
hsotg->params.otg_cap = val;
} }
static void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg) static void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg)
@ -463,6 +472,8 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
&p->g_tx_fifo_size[1], &p->g_tx_fifo_size[1],
num); num);
} }
of_usb_update_otg_caps(hsotg->dev->of_node, &p->otg_caps);
} }
if (of_find_property(hsotg->dev->of_node, "disable-over-current", NULL)) if (of_find_property(hsotg->dev->of_node, "disable-over-current", NULL))
@ -473,29 +484,27 @@ static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg)
{ {
int valid = 1; int valid = 1;
switch (hsotg->params.otg_cap) { if (hsotg->params.otg_caps.hnp_support && hsotg->params.otg_caps.srp_support) {
case DWC2_CAP_PARAM_HNP_SRP_CAPABLE: /* check HNP && SRP capable */
if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE)
valid = 0; valid = 0;
break; } else if (!hsotg->params.otg_caps.hnp_support) {
case DWC2_CAP_PARAM_SRP_ONLY_CAPABLE: /* check SRP only capable */
switch (hsotg->hw_params.op_mode) { if (hsotg->params.otg_caps.srp_support) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
break; case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
default: break;
valid = 0; default:
break; valid = 0;
break;
}
} }
break; /* else: NO HNP && NO SRP capable: always valid */
case DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE: } else {
/* always valid */
break;
default:
valid = 0; valid = 0;
break;
} }
if (!valid) if (!valid)

View File

@ -66,12 +66,13 @@ config USB_DWC3_OMAP
Say 'Y' or 'M' here if you have one such device Say 'Y' or 'M' here if you have one such device
config USB_DWC3_EXYNOS config USB_DWC3_EXYNOS
tristate "Samsung Exynos Platform" tristate "Samsung Exynos SoC Platform"
depends on (ARCH_EXYNOS || COMPILE_TEST) && OF depends on (ARCH_EXYNOS || COMPILE_TEST) && OF
default USB_DWC3 default USB_DWC3
help help
Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside, Recent Samsung Exynos SoCs (Exynos5250, Exynos5410, Exynos542x,
say 'Y' or 'M' if you have one such device. Exynos5800, Exynos5433, Exynos7) ship with one DesignWare Core USB3
IP inside, say 'Y' or 'M' if you have one such device.
config USB_DWC3_PCI config USB_DWC3_PCI
tristate "PCIe-based Platforms" tristate "PCIe-based Platforms"

View File

@ -26,6 +26,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/bitfield.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
@ -335,6 +336,29 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
} }
} }
/**
* dwc3_ref_clk_period - Reference clock period configuration
* Default reference clock period depends on hardware
* configuration. For systems with reference clock that differs
* from the default, this will set clock period in DWC3_GUCTL
* register.
* @dwc: Pointer to our controller context structure
* @ref_clk_per: reference clock period in ns
*/
static void dwc3_ref_clk_period(struct dwc3 *dwc)
{
u32 reg;
if (dwc->ref_clk_per == 0)
return;
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per);
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
}
/** /**
* dwc3_free_one_event_buffer - Frees one event buffer * dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure * @dwc: Pointer to our controller context structure
@ -1007,6 +1031,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
/* Adjust Frame Length */ /* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc); dwc3_frame_length_adjustment(dwc);
/* Adjust Reference Clock Period */
dwc3_ref_clk_period(dwc);
dwc3_set_incr_burst_type(dwc); dwc3_set_incr_burst_type(dwc);
usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb2_phy, 0);
@ -1389,6 +1416,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&dwc->hsphy_interface); &dwc->hsphy_interface);
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
&dwc->fladj); &dwc->fladj);
device_property_read_u32(dev, "snps,ref-clock-period-ns",
&dwc->ref_clk_per);
dwc->dis_metastability_quirk = device_property_read_bool(dev, dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk"); "snps,dis_metastability_quirk");

View File

@ -387,6 +387,10 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f #define DWC3_GFLADJ_30MHZ_MASK 0x3f
/* Global User Control Register*/
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
#define DWC3_GUCTL_REFCLKPER_SEL 22
/* Global User Control Register 2 */ /* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) #define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
@ -711,21 +715,22 @@ struct dwc3_ep {
u32 saved_state; u32 saved_state;
unsigned int flags; unsigned int flags;
#define DWC3_EP_ENABLED BIT(0) #define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1) #define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2) #define DWC3_EP_WEDGE BIT(2)
#define DWC3_EP_TRANSFER_STARTED BIT(3) #define DWC3_EP_TRANSFER_STARTED BIT(3)
#define DWC3_EP_END_TRANSFER_PENDING BIT(4) #define DWC3_EP_END_TRANSFER_PENDING BIT(4)
#define DWC3_EP_PENDING_REQUEST BIT(5) #define DWC3_EP_PENDING_REQUEST BIT(5)
#define DWC3_EP_DELAY_START BIT(6) #define DWC3_EP_DELAY_START BIT(6)
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7) #define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) #define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9) #define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) #define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11) #define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
/* This last one is specific to EP0 */ /* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31) #define DWC3_EP0_DIR_IN BIT(31)
/* /*
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will * IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
@ -970,6 +975,7 @@ struct dwc3_scratchpad_array {
* @regs: base address for our registers * @regs: base address for our registers
* @regs_size: address space size * @regs_size: address space size
* @fladj: frame length adjustment * @fladj: frame length adjustment
* @ref_clk_per: reference clock period configuration
* @irq_gadget: peripheral controller's IRQ number * @irq_gadget: peripheral controller's IRQ number
* @otg_irq: IRQ number for OTG IRQs * @otg_irq: IRQ number for OTG IRQs
* @current_otg_role: current role of operation while using the OTG block * @current_otg_role: current role of operation while using the OTG block
@ -1027,6 +1033,7 @@ struct dwc3_scratchpad_array {
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
* @hsphy_interface: "utmi" or "ulpi" * @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise * @connected: true when we're connected to a host, false otherwise
* @softconnect: true when gadget connect is called, false when disconnect runs
* @delayed_status: true when gadget driver asks for delayed status * @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer * @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer * @ep0_expect_in: true when we expect a DATA IN transfer
@ -1149,6 +1156,7 @@ struct dwc3 {
struct power_supply *usb_psy; struct power_supply *usb_psy;
u32 fladj; u32 fladj;
u32 ref_clk_per;
u32 irq_gadget; u32 irq_gadget;
u32 otg_irq; u32 otg_irq;
u32 current_otg_role; u32 current_otg_role;
@ -1246,6 +1254,7 @@ struct dwc3 {
const char *hsphy_interface; const char *hsphy_interface;
unsigned connected:1; unsigned connected:1;
unsigned softconnect:1;
unsigned delayed_status:1; unsigned delayed_status:1;
unsigned ep0_bounced:1; unsigned ep0_bounced:1;
unsigned ep0_expect_in:1; unsigned ep0_expect_in:1;

View File

@ -702,6 +702,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
DWC31_GTXFIFOSIZ_TXFRAMNUM; DWC31_GTXFIFOSIZ_TXFRAMNUM;
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size); dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
} }
dwc->num_ep_resized = 0; dwc->num_ep_resized = 0;
} }
@ -747,6 +748,10 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1) if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
return 0; return 0;
/* bail if already resized */
if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
return 0;
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
if ((dep->endpoint.maxburst > 1 && if ((dep->endpoint.maxburst > 1 &&
@ -807,6 +812,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
} }
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size); dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
dep->flags |= DWC3_EP_TXFIFO_RESIZED;
dwc->num_ep_resized++; dwc->num_ep_resized++;
return 0; return 0;
@ -995,7 +1001,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false; dep->stream_capable = false;
dep->type = 0; dep->type = 0;
dep->flags = 0; dep->flags &= DWC3_EP_TXFIFO_RESIZED;
return 0; return 0;
} }
@ -1813,7 +1819,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name); dep->name);
return -ESHUTDOWN; return -ESHUTDOWN;
} }
@ -2418,7 +2424,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
int ret; int ret;
is_on = !!is_on; is_on = !!is_on;
dwc->softconnect = is_on;
/* /*
* Per databook, when we want to stop the gadget, if a control transfer * Per databook, when we want to stop the gadget, if a control transfer
* is still in process, complete it and get the core into setup phase. * is still in process, complete it and get the core into setup phase.
@ -4352,7 +4358,7 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
{ {
int ret; int ret;
if (!dwc->gadget_driver) if (!dwc->gadget_driver || !dwc->softconnect)
return 0; return 0;
ret = __dwc3_gadget_start(dwc); ret = __dwc3_gadget_start(dwc);

View File

@ -73,6 +73,11 @@ static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item)
group); group);
} }
static inline struct gadget_info *cfg_to_gadget_info(struct config_usb_cfg *cfg)
{
return container_of(cfg->c.cdev, struct gadget_info, cdev);
}
struct gadget_strings { struct gadget_strings {
struct usb_gadget_strings stringtab_dev; struct usb_gadget_strings stringtab_dev;
struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX];
@ -413,8 +418,7 @@ static int config_usb_cfg_link(
struct config_item *usb_func_ci) struct config_item *usb_func_ci)
{ {
struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci);
struct usb_composite_dev *cdev = cfg->c.cdev; struct gadget_info *gi = cfg_to_gadget_info(cfg);
struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
struct config_group *group = to_config_group(usb_func_ci); struct config_group *group = to_config_group(usb_func_ci);
struct usb_function_instance *fi = container_of(group, struct usb_function_instance *fi = container_of(group,
@ -464,8 +468,7 @@ static void config_usb_cfg_unlink(
struct config_item *usb_func_ci) struct config_item *usb_func_ci)
{ {
struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci);
struct usb_composite_dev *cdev = cfg->c.cdev; struct gadget_info *gi = cfg_to_gadget_info(cfg);
struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
struct config_group *group = to_config_group(usb_func_ci); struct config_group *group = to_config_group(usb_func_ci);
struct usb_function_instance *fi = container_of(group, struct usb_function_instance *fi = container_of(group,
@ -505,12 +508,15 @@ static struct configfs_item_operations gadget_config_item_ops = {
static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item, static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item,
char *page) char *page)
{ {
return sprintf(page, "%u\n", to_config_usb_cfg(item)->c.MaxPower); struct config_usb_cfg *cfg = to_config_usb_cfg(item);
return sprintf(page, "%u\n", cfg->c.MaxPower);
} }
static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item, static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item,
const char *page, size_t len) const char *page, size_t len)
{ {
struct config_usb_cfg *cfg = to_config_usb_cfg(item);
u16 val; u16 val;
int ret; int ret;
ret = kstrtou16(page, 0, &val); ret = kstrtou16(page, 0, &val);
@ -518,20 +524,22 @@ static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item,
return ret; return ret;
if (DIV_ROUND_UP(val, 8) > 0xff) if (DIV_ROUND_UP(val, 8) > 0xff)
return -ERANGE; return -ERANGE;
to_config_usb_cfg(item)->c.MaxPower = val; cfg->c.MaxPower = val;
return len; return len;
} }
static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item, static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item,
char *page) char *page)
{ {
return sprintf(page, "0x%02x\n", struct config_usb_cfg *cfg = to_config_usb_cfg(item);
to_config_usb_cfg(item)->c.bmAttributes);
return sprintf(page, "0x%02x\n", cfg->c.bmAttributes);
} }
static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item, static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item,
const char *page, size_t len) const char *page, size_t len)
{ {
struct config_usb_cfg *cfg = to_config_usb_cfg(item);
u8 val; u8 val;
int ret; int ret;
ret = kstrtou8(page, 0, &val); ret = kstrtou8(page, 0, &val);
@ -542,7 +550,7 @@ static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item,
if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER |
USB_CONFIG_ATT_WAKEUP)) USB_CONFIG_ATT_WAKEUP))
return -EINVAL; return -EINVAL;
to_config_usb_cfg(item)->c.bmAttributes = val; cfg->c.bmAttributes = val;
return len; return len;
} }

View File

@ -181,7 +181,7 @@ EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
* This function can be used during function bind for endpoints obtained * This function can be used during function bind for endpoints obtained
* from usb_ep_autoconfig(). It unclaims endpoint claimed by * from usb_ep_autoconfig(). It unclaims endpoint claimed by
* usb_ep_autoconfig() to make it available for other functions. Endpoint * usb_ep_autoconfig() to make it available for other functions. Endpoint
* which was released is no longer invalid and shouldn't be used in * which was released is no longer valid and shouldn't be used in
* context of function which released it. * context of function which released it.
*/ */
void usb_ep_autoconfig_release(struct usb_ep *ep) void usb_ep_autoconfig_release(struct usb_ep *ep)

View File

@ -588,7 +588,7 @@ static int sleep_thread(struct fsg_common *common, bool can_freeze,
static int do_read(struct fsg_common *common) static int do_read(struct fsg_common *common)
{ {
struct fsg_lun *curlun = common->curlun; struct fsg_lun *curlun = common->curlun;
u32 lba; u64 lba;
struct fsg_buffhd *bh; struct fsg_buffhd *bh;
int rc; int rc;
u32 amount_left; u32 amount_left;
@ -603,7 +603,10 @@ static int do_read(struct fsg_common *common)
if (common->cmnd[0] == READ_6) if (common->cmnd[0] == READ_6)
lba = get_unaligned_be24(&common->cmnd[1]); lba = get_unaligned_be24(&common->cmnd[1]);
else { else {
lba = get_unaligned_be32(&common->cmnd[2]); if (common->cmnd[0] == READ_16)
lba = get_unaligned_be64(&common->cmnd[2]);
else /* READ_10 or READ_12 */
lba = get_unaligned_be32(&common->cmnd[2]);
/* /*
* We allow DPO (Disable Page Out = don't save data in the * We allow DPO (Disable Page Out = don't save data in the
@ -716,7 +719,7 @@ static int do_read(struct fsg_common *common)
static int do_write(struct fsg_common *common) static int do_write(struct fsg_common *common)
{ {
struct fsg_lun *curlun = common->curlun; struct fsg_lun *curlun = common->curlun;
u32 lba; u64 lba;
struct fsg_buffhd *bh; struct fsg_buffhd *bh;
int get_some_more; int get_some_more;
u32 amount_left_to_req, amount_left_to_write; u32 amount_left_to_req, amount_left_to_write;
@ -740,7 +743,10 @@ static int do_write(struct fsg_common *common)
if (common->cmnd[0] == WRITE_6) if (common->cmnd[0] == WRITE_6)
lba = get_unaligned_be24(&common->cmnd[1]); lba = get_unaligned_be24(&common->cmnd[1]);
else { else {
lba = get_unaligned_be32(&common->cmnd[2]); if (common->cmnd[0] == WRITE_16)
lba = get_unaligned_be64(&common->cmnd[2]);
else /* WRITE_10 or WRITE_12 */
lba = get_unaligned_be32(&common->cmnd[2]);
/* /*
* We allow DPO (Disable Page Out = don't save data in the * We allow DPO (Disable Page Out = don't save data in the
@ -1115,6 +1121,7 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
u32 lba = get_unaligned_be32(&common->cmnd[2]); u32 lba = get_unaligned_be32(&common->cmnd[2]);
int pmi = common->cmnd[8]; int pmi = common->cmnd[8];
u8 *buf = (u8 *)bh->buf; u8 *buf = (u8 *)bh->buf;
u32 max_lba;
/* Check the PMI and LBA fields */ /* Check the PMI and LBA fields */
if (pmi > 1 || (pmi == 0 && lba != 0)) { if (pmi > 1 || (pmi == 0 && lba != 0)) {
@ -1122,12 +1129,37 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
return -EINVAL; return -EINVAL;
} }
put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); if (curlun->num_sectors < 0x100000000ULL)
/* Max logical block */ max_lba = curlun->num_sectors - 1;
put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ else
max_lba = 0xffffffff;
put_unaligned_be32(max_lba, &buf[0]); /* Max logical block */
put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */
return 8; return 8;
} }
static int do_read_capacity_16(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
u64 lba = get_unaligned_be64(&common->cmnd[2]);
int pmi = common->cmnd[14];
u8 *buf = (u8 *)bh->buf;
/* Check the PMI and LBA fields */
if (pmi > 1 || (pmi == 0 && lba != 0)) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
put_unaligned_be64(curlun->num_sectors - 1, &buf[0]);
/* Max logical block */
put_unaligned_be32(curlun->blksize, &buf[8]); /* Block length */
/* It is safe to keep other fields zeroed */
memset(&buf[12], 0, 32 - 12);
return 32;
}
static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
{ {
struct fsg_lun *curlun = common->curlun; struct fsg_lun *curlun = common->curlun;
@ -1874,6 +1906,17 @@ static int do_scsi_command(struct fsg_common *common)
reply = do_read(common); reply = do_read(common);
break; break;
case READ_16:
common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[10]);
reply = check_command_size_in_blocks(common, 16,
DATA_DIR_TO_HOST,
(1<<1) | (0xff<<2) | (0xf<<10), 1,
"READ(16)");
if (reply == 0)
reply = do_read(common);
break;
case READ_CAPACITY: case READ_CAPACITY:
common->data_size_from_cmnd = 8; common->data_size_from_cmnd = 8;
reply = check_command(common, 10, DATA_DIR_TO_HOST, reply = check_command(common, 10, DATA_DIR_TO_HOST,
@ -1926,6 +1969,25 @@ static int do_scsi_command(struct fsg_common *common)
reply = do_request_sense(common, bh); reply = do_request_sense(common, bh);
break; break;
case SERVICE_ACTION_IN_16:
switch (common->cmnd[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[10]);
reply = check_command(common, 16, DATA_DIR_TO_HOST,
(1<<1) | (0xff<<2) | (0xf<<10) |
(1<<14), 1,
"READ CAPACITY(16)");
if (reply == 0)
reply = do_read_capacity_16(common, bh);
break;
default:
goto unknown_cmnd;
}
break;
case START_STOP: case START_STOP:
common->data_size_from_cmnd = 0; common->data_size_from_cmnd = 0;
reply = check_command(common, 6, DATA_DIR_NONE, reply = check_command(common, 6, DATA_DIR_NONE,
@ -1997,6 +2059,17 @@ static int do_scsi_command(struct fsg_common *common)
reply = do_write(common); reply = do_write(common);
break; break;
case WRITE_16:
common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[10]);
reply = check_command_size_in_blocks(common, 16,
DATA_DIR_FROM_HOST,
(1<<1) | (0xff<<2) | (0xf<<10), 1,
"WRITE(16)");
if (reply == 0)
reply = do_write(common);
break;
/* /*
* Some mandatory commands that we recognize but don't implement. * Some mandatory commands that we recognize but don't implement.
* They don't mean much in this setting. It's left as an exercise * They don't mean much in this setting. It's left as an exercise
@ -2269,6 +2342,16 @@ static void fsg_disable(struct usb_function *f)
{ {
struct fsg_dev *fsg = fsg_from_func(f); struct fsg_dev *fsg = fsg_from_func(f);
/* Disable the endpoints */
if (fsg->bulk_in_enabled) {
usb_ep_disable(fsg->bulk_in);
fsg->bulk_in_enabled = 0;
}
if (fsg->bulk_out_enabled) {
usb_ep_disable(fsg->bulk_out);
fsg->bulk_out_enabled = 0;
}
__raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL); __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL);
} }

View File

@ -1321,6 +1321,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.c_fu.volume_res = audio_opts->c_volume_res; audio->params.c_fu.volume_res = audio_opts->c_volume_res;
} }
audio->params.req_number = audio_opts->req_number; audio->params.req_number = audio_opts->req_number;
audio->params.fb_max = FBACK_FAST_MAX;
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts))
audio->notify = audio_notify; audio->notify = audio_notify;

View File

@ -15,6 +15,7 @@
#include <linux/module.h> #include <linux/module.h>
#include "u_audio.h" #include "u_audio.h"
#include "u_uac2.h" #include "u_uac2.h"
/* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ /* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */
@ -674,8 +675,9 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
ssize = uac2_opts->c_ssize; ssize = uac2_opts->c_ssize;
} }
if (!is_playback && (uac2_opts->c_sync == USB_ENDPOINT_SYNC_ASYNC)) { if (is_playback || (uac2_opts->c_sync == USB_ENDPOINT_SYNC_ASYNC)) {
// Win10 requires max packet size + 1 frame // playback is always async, capture only when configured
// Win10 requires max packet size + 1 frame
srate = srate * (1000 + uac2_opts->fb_max) / 1000; srate = srate * (1000 + uac2_opts->fb_max) / 1000;
// updated srate is always bigger, therefore DIV_ROUND_UP always yields +1 // updated srate is always bigger, therefore DIV_ROUND_UP always yields +1
max_size_bw = num_channels(chmask) * ssize * max_size_bw = num_channels(chmask) * ssize *
@ -760,15 +762,15 @@ static void setup_headers(struct f_uac2_opts *opts,
headers[i++] = USBDHDR(&out_clk_src_desc); headers[i++] = USBDHDR(&out_clk_src_desc);
headers[i++] = USBDHDR(&usb_out_it_desc); headers[i++] = USBDHDR(&usb_out_it_desc);
if (FUOUT_EN(opts)) if (FUOUT_EN(opts))
headers[i++] = USBDHDR(out_feature_unit_desc); headers[i++] = USBDHDR(out_feature_unit_desc);
} }
if (EPIN_EN(opts)) { if (EPIN_EN(opts)) {
headers[i++] = USBDHDR(&io_in_it_desc); headers[i++] = USBDHDR(&io_in_it_desc);
if (FUIN_EN(opts)) if (FUIN_EN(opts))
headers[i++] = USBDHDR(in_feature_unit_desc); headers[i++] = USBDHDR(in_feature_unit_desc);
headers[i++] = USBDHDR(&usb_in_ot_desc); headers[i++] = USBDHDR(&usb_in_ot_desc);
} }
@ -776,10 +778,10 @@ static void setup_headers(struct f_uac2_opts *opts,
if (EPOUT_EN(opts)) if (EPOUT_EN(opts))
headers[i++] = USBDHDR(&io_out_ot_desc); headers[i++] = USBDHDR(&io_out_ot_desc);
if (FUOUT_EN(opts) || FUIN_EN(opts)) if (FUOUT_EN(opts) || FUIN_EN(opts))
headers[i++] = USBDHDR(ep_int_desc); headers[i++] = USBDHDR(ep_int_desc);
if (EPOUT_EN(opts)) { if (EPOUT_EN(opts)) {
headers[i++] = USBDHDR(&std_as_out_if0_desc); headers[i++] = USBDHDR(&std_as_out_if0_desc);
headers[i++] = USBDHDR(&std_as_out_if1_desc); headers[i++] = USBDHDR(&std_as_out_if1_desc);
headers[i++] = USBDHDR(&as_out_hdr_desc); headers[i++] = USBDHDR(&as_out_hdr_desc);
@ -1931,7 +1933,7 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->c_volume_res = UAC2_DEF_RES_DB; opts->c_volume_res = UAC2_DEF_RES_DB;
opts->req_number = UAC2_DEF_REQ_NUM; opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = UAC2_DEF_FB_MAX; opts->fb_max = FBACK_FAST_MAX;
return &opts->func_inst; return &opts->func_inst;
} }

View File

@ -417,6 +417,7 @@ uvc_register_video(struct uvc_device *uvc)
int ret; int ret;
/* TODO reference counting. */ /* TODO reference counting. */
memset(&uvc->vdev, 0, sizeof(uvc->video));
uvc->vdev.v4l2_dev = &uvc->v4l2_dev; uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops; uvc->vdev.fops = &uvc_v4l2_fops;
@ -884,12 +885,13 @@ static void uvc_free(struct usb_function *f)
kfree(uvc); kfree(uvc);
} }
static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) static void uvc_function_unbind(struct usb_configuration *c,
struct usb_function *f)
{ {
struct usb_composite_dev *cdev = c->cdev; struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f); struct uvc_device *uvc = to_uvc(f);
uvcg_info(f, "%s\n", __func__); uvcg_info(f, "%s()\n", __func__);
device_remove_file(&uvc->vdev.dev, &dev_attr_function_name); device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
video_unregister_device(&uvc->vdev); video_unregister_device(&uvc->vdev);
@ -943,7 +945,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
/* Register the function. */ /* Register the function. */
uvc->func.name = "uvc"; uvc->func.name = "uvc";
uvc->func.bind = uvc_function_bind; uvc->func.bind = uvc_function_bind;
uvc->func.unbind = uvc_unbind; uvc->func.unbind = uvc_function_unbind;
uvc->func.get_alt = uvc_function_get_alt; uvc->func.get_alt = uvc_function_get_alt;
uvc->func.set_alt = uvc_function_set_alt; uvc->func.set_alt = uvc_function_set_alt;
uvc->func.disable = uvc_function_disable; uvc->func.disable = uvc_function_disable;

View File

@ -29,6 +29,7 @@
enum { enum {
UAC_FBACK_CTRL, UAC_FBACK_CTRL,
UAC_P_PITCH_CTRL,
UAC_MUTE_CTRL, UAC_MUTE_CTRL,
UAC_VOLUME_CTRL, UAC_VOLUME_CTRL,
}; };
@ -74,13 +75,9 @@ struct snd_uac_chip {
struct snd_card *card; struct snd_card *card;
struct snd_pcm *pcm; struct snd_pcm *pcm;
/* timekeeping for the playback endpoint */
unsigned int p_interval;
unsigned int p_residue;
/* pre-calculated values for playback iso completion */ /* pre-calculated values for playback iso completion */
unsigned int p_pktsize; unsigned long long p_interval_mil;
unsigned int p_pktsize_residue; unsigned long long p_residue_mil;
unsigned int p_framesize; unsigned int p_framesize;
}; };
@ -153,6 +150,11 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = req->context; struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac; struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
struct uac_params *params = &audio_dev->params;
unsigned int frames, p_pktsize;
unsigned long long pitched_rate_mil, p_pktsize_residue_mil,
residue_frames_mil, div_result;
/* i/f shutting down */ /* i/f shutting down */
if (!prm->ep_enabled) { if (!prm->ep_enabled) {
@ -192,19 +194,42 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
* If there is a residue from this division, add it to the * If there is a residue from this division, add it to the
* residue accumulator. * residue accumulator.
*/ */
req->length = uac->p_pktsize; pitched_rate_mil = (unsigned long long)
uac->p_residue += uac->p_pktsize_residue; params->p_srate * prm->pitch;
div_result = pitched_rate_mil;
do_div(div_result, uac->p_interval_mil);
frames = (unsigned int) div_result;
pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n",
params->p_srate, prm->pitch, uac->p_interval_mil, frames);
p_pktsize = min_t(unsigned int,
uac->p_framesize * frames,
ep->maxpacket);
if (p_pktsize < ep->maxpacket) {
residue_frames_mil = pitched_rate_mil - frames * uac->p_interval_mil;
p_pktsize_residue_mil = uac->p_framesize * residue_frames_mil;
} else
p_pktsize_residue_mil = 0;
req->length = p_pktsize;
uac->p_residue_mil += p_pktsize_residue_mil;
/* /*
* Whenever there are more bytes in the accumulator than we * Whenever there are more bytes in the accumulator p_residue_mil than we
* need to add one more sample frame, increase this packet's * need to add one more sample frame, increase this packet's
* size and decrease the accumulator. * size and decrease the accumulator.
*/ */
if (uac->p_residue / uac->p_interval >= uac->p_framesize) { div_result = uac->p_residue_mil;
do_div(div_result, uac->p_interval_mil);
if ((unsigned int) div_result >= uac->p_framesize) {
req->length += uac->p_framesize; req->length += uac->p_framesize;
uac->p_residue -= uac->p_framesize * uac->p_residue_mil -= uac->p_framesize *
uac->p_interval; uac->p_interval_mil;
pr_debug("increased req length to %d\n", req->length);
} }
pr_debug("remains uac->p_residue_mil %llu\n", uac->p_residue_mil);
req->actual = req->length; req->actual = req->length;
} }
@ -371,7 +396,7 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
c_srate = params->c_srate; c_srate = params->c_srate;
p_chmask = params->p_chmask; p_chmask = params->p_chmask;
c_chmask = params->c_chmask; c_chmask = params->c_chmask;
uac->p_residue = 0; uac->p_residue_mil = 0;
runtime->hw = uac_pcm_hardware; runtime->hw = uac_pcm_hardware;
@ -566,12 +591,17 @@ int u_audio_start_playback(struct g_audio *audio_dev)
unsigned int factor; unsigned int factor;
const struct usb_endpoint_descriptor *ep_desc; const struct usb_endpoint_descriptor *ep_desc;
int req_len, i; int req_len, i;
unsigned int p_interval, p_pktsize;
ep = audio_dev->in_ep; ep = audio_dev->in_ep;
prm = &uac->p_prm; prm = &uac->p_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep); config_ep_by_speed(gadget, &audio_dev->func, ep);
ep_desc = ep->desc; ep_desc = ep->desc;
/*
* Always start with original frequency
*/
prm->pitch = 1000000;
/* pre-calculate the playback endpoint's interval */ /* pre-calculate the playback endpoint's interval */
if (gadget->speed == USB_SPEED_FULL) if (gadget->speed == USB_SPEED_FULL)
@ -582,20 +612,15 @@ int u_audio_start_playback(struct g_audio *audio_dev)
/* pre-compute some values for iso_complete() */ /* pre-compute some values for iso_complete() */
uac->p_framesize = params->p_ssize * uac->p_framesize = params->p_ssize *
num_channels(params->p_chmask); num_channels(params->p_chmask);
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); p_interval = factor / (1 << (ep_desc->bInterval - 1));
uac->p_pktsize = min_t(unsigned int, uac->p_interval_mil = (unsigned long long) p_interval * 1000000;
p_pktsize = min_t(unsigned int,
uac->p_framesize * uac->p_framesize *
(params->p_srate / uac->p_interval), (params->p_srate / p_interval),
ep->maxpacket); ep->maxpacket);
if (uac->p_pktsize < ep->maxpacket) req_len = p_pktsize;
uac->p_pktsize_residue = uac->p_framesize * uac->p_residue_mil = 0;
(params->p_srate % uac->p_interval);
else
uac->p_pktsize_residue = 0;
req_len = uac->p_pktsize;
uac->p_residue = 0;
prm->ep_enabled = true; prm->ep_enabled = true;
usb_ep_enable(ep); usb_ep_enable(ep);
@ -925,6 +950,13 @@ static struct snd_kcontrol_new u_audio_controls[] = {
.get = u_audio_pitch_get, .get = u_audio_pitch_get,
.put = u_audio_pitch_put, .put = u_audio_pitch_put,
}, },
[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, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */ .name = "", /* will be filled later */
@ -1062,6 +1094,22 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
goto snd_fail; goto snd_fail;
} }
if (p_chmask) {
kctl = snd_ctl_new1(&u_audio_controls[UAC_P_PITCH_CTRL],
&uac->p_prm);
if (!kctl) {
err = -ENOMEM;
goto snd_fail;
}
kctl->id.device = pcm->device;
kctl->id.subdevice = 0;
err = snd_ctl_add(card, kctl);
if (err < 0)
goto snd_fail;
}
for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) {
struct uac_rtd_params *prm; struct uac_rtd_params *prm;
struct uac_fu_params *fu; struct uac_fu_params *fu;

View File

@ -14,11 +14,17 @@
/* /*
* Same maximum frequency deviation on the slower side as in * Same maximum frequency deviation on the slower side as in
* sound/usb/endpoint.c. Value is expressed in per-mil deviation. * sound/usb/endpoint.c. Value is expressed in per-mil deviation.
* The maximum deviation on the faster side will be provided as
* parameter, as it impacts the endpoint required bandwidth.
*/ */
#define FBACK_SLOW_MAX 250 #define FBACK_SLOW_MAX 250
/*
* Maximum frequency deviation on the faster side, default value for UAC1/2.
* Value is expressed in per-mil deviation.
* UAC2 provides the value as a parameter as it impacts the endpoint required
* bandwidth.
*/
#define FBACK_FAST_MAX 5
/* Feature Unit parameters */ /* Feature Unit parameters */
struct uac_fu_params { struct uac_fu_params {
int id; /* Feature Unit ID */ int id; /* Feature Unit ID */

View File

@ -754,6 +754,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
struct eth_dev *dev; struct eth_dev *dev;
struct net_device *net; struct net_device *net;
int status; int status;
u8 addr[ETH_ALEN];
net = alloc_etherdev(sizeof *dev); net = alloc_etherdev(sizeof *dev);
if (!net) if (!net)
@ -773,9 +774,10 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
dev->qmult = qmult; dev->qmult = qmult;
snprintf(net->name, sizeof(net->name), "%s%%d", netname); snprintf(net->name, sizeof(net->name), "%s%%d", netname);
if (get_ether_addr(dev_addr, net->dev_addr)) if (get_ether_addr(dev_addr, addr))
dev_warn(&g->dev, dev_warn(&g->dev,
"using random %s ethernet address\n", "self"); "using random %s ethernet address\n", "self");
eth_hw_addr_set(net, addr);
if (get_ether_addr(host_addr, dev->host_mac)) if (get_ether_addr(host_addr, dev->host_mac))
dev_warn(&g->dev, dev_warn(&g->dev,
"using random %s ethernet address\n", "host"); "using random %s ethernet address\n", "host");

View File

@ -30,7 +30,6 @@
#define UAC2_DEF_RES_DB (1*256) /* 1 dB */ #define UAC2_DEF_RES_DB (1*256) /* 1 dB */
#define UAC2_DEF_REQ_NUM 2 #define UAC2_DEF_REQ_NUM 2
#define UAC2_DEF_FB_MAX 5
#define UAC2_DEF_INT_REQ_NUM 10 #define UAC2_DEF_INT_REQ_NUM 10
struct f_uac2_opts { struct f_uac2_opts {

View File

@ -68,6 +68,8 @@ extern unsigned int uvc_gadget_trace_param;
#define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_REQUEST_SIZE 64
#define UVC_MAX_EVENTS 4 #define UVC_MAX_EVENTS 4
#define UVCG_REQUEST_HEADER_LEN 12
/* ------------------------------------------------------------------------ /* ------------------------------------------------------------------------
* Structures * Structures
*/ */
@ -76,7 +78,7 @@ struct uvc_request {
u8 *req_buffer; u8 *req_buffer;
struct uvc_video *video; struct uvc_video *video;
struct sg_table sgt; struct sg_table sgt;
u8 header[2]; u8 header[UVCG_REQUEST_HEADER_LEN];
}; };
struct uvc_video { struct uvc_video {
@ -126,6 +128,7 @@ struct uvc_device {
enum uvc_state state; enum uvc_state state;
struct usb_function func; struct usb_function func;
struct uvc_video video; struct uvc_video video;
bool func_connected;
/* Descriptors */ /* Descriptors */
struct { struct {
@ -156,6 +159,7 @@ static inline struct uvc_device *to_uvc(struct usb_function *f)
struct uvc_file_handle { struct uvc_file_handle {
struct v4l2_fh vfh; struct v4l2_fh vfh;
struct uvc_video *device; struct uvc_video *device;
bool is_uvc_app_handle;
}; };
#define to_uvc_file_handle(handle) \ #define to_uvc_file_handle(handle) \

View File

@ -142,7 +142,7 @@ int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2
queue->queue.mem_ops = &vb2_vmalloc_memops; queue->queue.mem_ops = &vb2_vmalloc_memops;
} }
queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY
| V4L2_BUF_FLAG_TSTAMP_SRC_EOF; | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
queue->queue.dev = dev; queue->queue.dev = dev;

View File

@ -169,7 +169,8 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
if (ret < 0) if (ret < 0)
return ret; return ret;
schedule_work(&video->pump); if (uvc->state == UVC_STATE_STREAMING)
schedule_work(&video->pump);
return ret; return ret;
} }
@ -227,17 +228,55 @@ static int
uvc_v4l2_subscribe_event(struct v4l2_fh *fh, uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub) const struct v4l2_event_subscription *sub)
{ {
struct uvc_device *uvc = video_get_drvdata(fh->vdev);
struct uvc_file_handle *handle = to_uvc_file_handle(fh);
int ret;
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL; return -EINVAL;
return v4l2_event_subscribe(fh, sub, 2, NULL); if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
return -EBUSY;
ret = v4l2_event_subscribe(fh, sub, 2, NULL);
if (ret < 0)
return ret;
if (sub->type == UVC_EVENT_SETUP) {
uvc->func_connected = true;
handle->is_uvc_app_handle = true;
uvc_function_connect(uvc);
}
return 0;
}
static void uvc_v4l2_disable(struct uvc_device *uvc)
{
uvc->func_connected = false;
uvc_function_disconnect(uvc);
uvcg_video_enable(&uvc->video, 0);
uvcg_free_buffers(&uvc->video.queue);
} }
static int static int
uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh, uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub) const struct v4l2_event_subscription *sub)
{ {
return v4l2_event_unsubscribe(fh, sub); struct uvc_device *uvc = video_get_drvdata(fh->vdev);
struct uvc_file_handle *handle = to_uvc_file_handle(fh);
int ret;
ret = v4l2_event_unsubscribe(fh, sub);
if (ret < 0)
return ret;
if (sub->type == UVC_EVENT_SETUP && handle->is_uvc_app_handle) {
uvc_v4l2_disable(uvc);
handle->is_uvc_app_handle = false;
}
return 0;
} }
static long static long
@ -292,7 +331,6 @@ uvc_v4l2_open(struct file *file)
handle->device = &uvc->video; handle->device = &uvc->video;
file->private_data = &handle->vfh; file->private_data = &handle->vfh;
uvc_function_connect(uvc);
return 0; return 0;
} }
@ -304,11 +342,9 @@ uvc_v4l2_release(struct file *file)
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
struct uvc_video *video = handle->device; struct uvc_video *video = handle->device;
uvc_function_disconnect(uvc);
mutex_lock(&video->mutex); mutex_lock(&video->mutex);
uvcg_video_enable(video, 0); if (handle->is_uvc_app_handle)
uvcg_free_buffers(&video->queue); uvc_v4l2_disable(uvc);
mutex_unlock(&video->mutex); mutex_unlock(&video->mutex);
file->private_data = NULL; file->private_data = NULL;

View File

@ -12,6 +12,7 @@
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/video.h> #include <linux/usb/video.h>
#include <asm/unaligned.h>
#include <media/v4l2-dev.h> #include <media/v4l2-dev.h>
@ -27,13 +28,41 @@ static int
uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
u8 *data, int len) u8 *data, int len)
{ {
data[0] = UVCG_REQUEST_HEADER_LEN; struct uvc_device *uvc = container_of(video, struct uvc_device, video);
struct usb_composite_dev *cdev = uvc->func.config->cdev;
struct timespec64 ts = ns_to_timespec64(buf->buf.vb2_buf.timestamp);
int pos = 2;
data[1] = UVC_STREAM_EOH | video->fid; data[1] = UVC_STREAM_EOH | video->fid;
if (buf->bytesused - video->queue.buf_used <= len - UVCG_REQUEST_HEADER_LEN) if (video->queue.buf_used == 0 && ts.tv_sec) {
/* dwClockFrequency is 48 MHz */
u32 pts = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48;
data[1] |= UVC_STREAM_PTS;
put_unaligned_le32(pts, &data[pos]);
pos += 4;
}
if (cdev->gadget->ops->get_frame) {
u32 sof, stc;
sof = usb_gadget_frame_number(cdev->gadget);
ktime_get_ts64(&ts);
stc = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48;
data[1] |= UVC_STREAM_SCR;
put_unaligned_le32(stc, &data[pos]);
put_unaligned_le16(sof, &data[pos+4]);
pos += 6;
}
data[0] = pos;
if (buf->bytesused - video->queue.buf_used <= len - pos)
data[1] |= UVC_STREAM_EOF; data[1] |= UVC_STREAM_EOF;
return 2; return pos;
} }
static int static int
@ -104,22 +133,22 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
unsigned int len = video->req_size; unsigned int len = video->req_size;
unsigned int sg_left, part = 0; unsigned int sg_left, part = 0;
unsigned int i; unsigned int i;
int ret; int header_len;
sg = ureq->sgt.sgl; sg = ureq->sgt.sgl;
sg_init_table(sg, ureq->sgt.nents); sg_init_table(sg, ureq->sgt.nents);
/* Init the header. */ /* Init the header. */
ret = uvc_video_encode_header(video, buf, ureq->header, header_len = uvc_video_encode_header(video, buf, ureq->header,
video->req_size); video->req_size);
sg_set_buf(sg, ureq->header, UVCG_REQUEST_HEADER_LEN); sg_set_buf(sg, ureq->header, header_len);
len -= ret; len -= header_len;
if (pending <= len) if (pending <= len)
len = pending; len = pending;
req->length = (len == pending) ? req->length = (len == pending) ?
len + UVCG_REQUEST_HEADER_LEN : video->req_size; len + header_len : video->req_size;
/* Init the pending sgs with payload */ /* Init the pending sgs with payload */
sg = sg_next(sg); sg = sg_next(sg);
@ -148,7 +177,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
req->num_sgs = i + 1; req->num_sgs = i + 1;
req->length -= len; req->length -= len;
video->queue.buf_used += req->length - UVCG_REQUEST_HEADER_LEN; video->queue.buf_used += req->length - header_len;
if (buf->bytesused == video->queue.buf_used || !buf->sg) { if (buf->bytesused == video->queue.buf_used || !buf->sg) {
video->queue.buf_used = 0; video->queue.buf_used = 0;
@ -199,9 +228,12 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n", uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n",
ret); ret);
/* Isochronous endpoints can't be halted. */ /* If the endpoint is disabled the descriptor may be NULL. */
if (usb_endpoint_xfer_bulk(video->ep->desc)) if (video->ep->desc) {
usb_ep_set_halt(video->ep); /* Isochronous endpoints can't be halted. */
if (usb_endpoint_xfer_bulk(video->ep->desc))
usb_ep_set_halt(video->ep);
}
} }
return ret; return ret;
@ -213,6 +245,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
struct uvc_request *ureq = req->context; struct uvc_request *ureq = req->context;
struct uvc_video *video = ureq->video; struct uvc_video *video = ureq->video;
struct uvc_video_queue *queue = &video->queue; struct uvc_video_queue *queue = &video->queue;
struct uvc_device *uvc = video->uvc;
unsigned long flags; unsigned long flags;
switch (req->status) { switch (req->status) {
@ -235,7 +268,8 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
list_add_tail(&req->list, &video->req_free); list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags); spin_unlock_irqrestore(&video->req_lock, flags);
schedule_work(&video->pump); if (uvc->state == UVC_STATE_STREAMING)
schedule_work(&video->pump);
} }
static int static int
@ -302,8 +336,8 @@ uvc_video_alloc_requests(struct uvc_video *video)
list_add_tail(&video->ureq[i].req->list, &video->req_free); list_add_tail(&video->ureq[i].req->list, &video->req_free);
/* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
sg_alloc_table(&video->ureq[i].sgt, sg_alloc_table(&video->ureq[i].sgt,
DIV_ROUND_UP(req_size - 2, PAGE_SIZE) + 2, DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
GFP_KERNEL); PAGE_SIZE) + 2, GFP_KERNEL);
} }
video->req_size = req_size; video->req_size = req_size;
@ -329,12 +363,12 @@ static void uvcg_video_pump(struct work_struct *work)
{ {
struct uvc_video *video = container_of(work, struct uvc_video, pump); struct uvc_video *video = container_of(work, struct uvc_video, pump);
struct uvc_video_queue *queue = &video->queue; struct uvc_video_queue *queue = &video->queue;
struct usb_request *req; struct usb_request *req = NULL;
struct uvc_buffer *buf; struct uvc_buffer *buf;
unsigned long flags; unsigned long flags;
int ret; int ret;
while (1) { while (video->ep->enabled) {
/* Retrieve the first available USB request, protected by the /* Retrieve the first available USB request, protected by the
* request lock. * request lock.
*/ */
@ -384,6 +418,9 @@ static void uvcg_video_pump(struct work_struct *work)
video->req_int_count++; video->req_int_count++;
} }
if (!req)
return;
spin_lock_irqsave(&video->req_lock, flags); spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free); list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags); spin_unlock_irqrestore(&video->req_lock, flags);

View File

@ -12,8 +12,6 @@
#ifndef __UVC_VIDEO_H__ #ifndef __UVC_VIDEO_H__
#define __UVC_VIDEO_H__ #define __UVC_VIDEO_H__
#define UVCG_REQUEST_HEADER_LEN 2
struct uvc_video; struct uvc_video;
int uvcg_video_enable(struct uvc_video *video, int enable); int uvcg_video_enable(struct uvc_video *video, int enable);

View File

@ -99,8 +99,10 @@ static int do_config(struct usb_configuration *c)
list_for_each_entry(e, &hidg_func_list, node) { list_for_each_entry(e, &hidg_func_list, node) {
e->f = usb_get_function(e->fi); e->f = usb_get_function(e->fi);
if (IS_ERR(e->f)) if (IS_ERR(e->f)) {
status = PTR_ERR(e->f);
goto put; goto put;
}
status = usb_add_function(c, e->f); status = usb_add_function(c, e->f);
if (status < 0) { if (status < 0) {
usb_put_function(e->f); usb_put_function(e->f);

View File

@ -330,6 +330,7 @@ config USB_AMD5536UDC
config USB_FSL_QE config USB_FSL_QE
tristate "Freescale QE/CPM USB Device Controller" tristate "Freescale QE/CPM USB Device Controller"
depends on FSL_SOC && (QUICC_ENGINE || CPM) depends on FSL_SOC && (QUICC_ENGINE || CPM)
depends on !64BIT || BROKEN
help help
Some of Freescale PowerPC processors have a Full Speed Some of Freescale PowerPC processors have a Full Speed
QE/CPM2 USB controller, which support device mode with 4 QE/CPM2 USB controller, which support device mode with 4

View File

@ -572,7 +572,6 @@ struct udc {
struct extcon_specific_cable_nb extcon_nb; struct extcon_specific_cable_nb extcon_nb;
struct notifier_block nb; struct notifier_block nb;
struct delayed_work drd_work; struct delayed_work drd_work;
struct workqueue_struct *drd_wq;
u32 conn_type; u32 conn_type;
}; };

View File

@ -6,6 +6,8 @@
* Author: Felipe Balbi <balbi@ti.com> * Author: Felipe Balbi <balbi@ti.com>
*/ */
#define pr_fmt(fmt) "UDC core: " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
@ -89,7 +91,7 @@ EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
* configurable, with more generic names like "ep-a". (remember that for * configurable, with more generic names like "ep-a". (remember that for
* USB, "in" means "towards the USB host".) * USB, "in" means "towards the USB host".)
* *
* This routine must be called in process context. * This routine may be called in an atomic (interrupt) context.
* *
* returns zero, or a negative error code. * returns zero, or a negative error code.
*/ */
@ -134,7 +136,7 @@ EXPORT_SYMBOL_GPL(usb_ep_enable);
* gadget drivers must call usb_ep_enable() again before queueing * gadget drivers must call usb_ep_enable() again before queueing
* requests to the endpoint. * requests to the endpoint.
* *
* This routine must be called in process context. * This routine may be called in an atomic (interrupt) context.
* *
* returns zero, or a negative error code. * returns zero, or a negative error code.
*/ */
@ -1555,14 +1557,14 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
if (!driver->match_existing_only) { if (!driver->match_existing_only) {
list_add_tail(&driver->pending, &gadget_driver_pending_list); list_add_tail(&driver->pending, &gadget_driver_pending_list);
pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n", pr_info("couldn't find an available UDC - added [%s] to list of pending drivers\n",
driver->function); driver->function);
ret = 0; ret = 0;
} }
mutex_unlock(&udc_lock); mutex_unlock(&udc_lock);
if (ret) if (ret)
pr_warn("udc-core: couldn't find an available UDC or it's busy\n"); pr_warn("couldn't find an available UDC or it's busy: %d\n", ret);
return ret; return ret;
found: found:
ret = udc_bind_to_driver(udc, driver); ret = udc_bind_to_driver(udc, driver);

View File

@ -553,12 +553,12 @@ static int start_dma(struct goku_ep *ep, struct goku_request *req)
master &= ~MST_R_BITS; master &= ~MST_R_BITS;
if (unlikely(req->req.length == 0)) if (unlikely(req->req.length == 0))
master = MST_RD_ENA | MST_RD_EOPB; master |= MST_RD_ENA | MST_RD_EOPB;
else if ((req->req.length % ep->ep.maxpacket) != 0 else if ((req->req.length % ep->ep.maxpacket) != 0
|| req->req.zero) || req->req.zero)
master = MST_RD_ENA | MST_EOPB_ENA; master |= MST_RD_ENA | MST_EOPB_ENA;
else else
master = MST_RD_ENA | MST_EOPB_DIS; master |= MST_RD_ENA | MST_EOPB_DIS;
ep->dev->int_enable |= INT_MSTRDEND; ep->dev->int_enable |= INT_MSTRDEND;

View File

@ -2325,7 +2325,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
/* insist on Intel/ARM/XScale */ /* insist on Intel/ARM/XScale */
asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); asm("mrc p15, 0, %0, c0, c0" : "=r" (chiprev));
if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
pr_err("%s: not XScale!\n", driver_name); pr_err("%s: not XScale!\n", driver_name);
return -ENODEV; return -ENODEV;

View File

@ -243,11 +243,6 @@ static int udc_plat_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
if (dev->drd_wq) {
flush_workqueue(dev->drd_wq);
destroy_workqueue(dev->drd_wq);
}
phy_power_off(dev->udc_phy); phy_power_off(dev->udc_phy);
phy_exit(dev->udc_phy); phy_exit(dev->udc_phy);
extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb); extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);

View File

@ -11,6 +11,7 @@
* USB peripheral controller (at91_udc.c). * USB peripheral controller (at91_udc.c).
*/ */
#include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
@ -171,6 +172,7 @@ struct xusb_ep {
* @addr: the usb device base address * @addr: the usb device base address
* @lock: instance of spinlock * @lock: instance of spinlock
* @dma_enabled: flag indicating whether the dma is included in the system * @dma_enabled: flag indicating whether the dma is included in the system
* @clk: pointer to struct clk
* @read_fn: function pointer to read device registers * @read_fn: function pointer to read device registers
* @write_fn: function pointer to write to device registers * @write_fn: function pointer to write to device registers
*/ */
@ -188,6 +190,7 @@ struct xusb_udc {
void __iomem *addr; void __iomem *addr;
spinlock_t lock; spinlock_t lock;
bool dma_enabled; bool dma_enabled;
struct clk *clk;
unsigned int (*read_fn)(void __iomem *); unsigned int (*read_fn)(void __iomem *);
void (*write_fn)(void __iomem *, u32, u32); void (*write_fn)(void __iomem *, u32, u32);
@ -2092,6 +2095,27 @@ static int xudc_probe(struct platform_device *pdev)
udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb; udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb;
udc->gadget.name = driver_name; udc->gadget.name = driver_name;
udc->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
if (IS_ERR(udc->clk)) {
if (PTR_ERR(udc->clk) != -ENOENT) {
ret = PTR_ERR(udc->clk);
goto fail;
}
/*
* Clock framework support is optional, continue on,
* anyways if we don't find a matching clock
*/
dev_warn(&pdev->dev, "s_axi_aclk clock property is not found\n");
udc->clk = NULL;
}
ret = clk_prepare_enable(udc->clk);
if (ret) {
dev_err(&pdev->dev, "Unable to enable clock.\n");
return ret;
}
spin_lock_init(&udc->lock); spin_lock_init(&udc->lock);
/* Check for IP endianness */ /* Check for IP endianness */
@ -2147,6 +2171,7 @@ static int xudc_remove(struct platform_device *pdev)
struct xusb_udc *udc = platform_get_drvdata(pdev); struct xusb_udc *udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget); usb_del_gadget_udc(&udc->gadget);
clk_disable_unprepare(udc->clk);
return 0; return 0;
} }

View File

@ -290,7 +290,8 @@ config USB_EHCI_EXYNOS
tristate "EHCI support for Samsung S5P/Exynos SoC Series" tristate "EHCI support for Samsung S5P/Exynos SoC Series"
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
help help
Enable support for the Samsung Exynos SOC's on-chip EHCI controller. Enable support for the Samsung S5Pv210 and Exynos SOC's on-chip EHCI
controller.
config USB_EHCI_MV config USB_EHCI_MV
tristate "EHCI support for Marvell PXA/MMP USB controller" tristate "EHCI support for Marvell PXA/MMP USB controller"
@ -563,7 +564,8 @@ config USB_OHCI_EXYNOS
tristate "OHCI support for Samsung S5P/Exynos SoC Series" tristate "OHCI support for Samsung S5P/Exynos SoC Series"
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
help help
Enable support for the Samsung Exynos SOC's on-chip OHCI controller. Enable support for the Samsung S5Pv210 and Exynos SOC's on-chip OHCI
controller.
config USB_CNS3XXX_OHCI config USB_CNS3XXX_OHCI
bool "Cavium CNS3XXX OHCI Module (DEPRECATED)" bool "Cavium CNS3XXX OHCI Module (DEPRECATED)"

View File

@ -18,6 +18,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
#include <linux/usb/phy.h>
#include <linux/usb/of.h>
#include "ehci.h" #include "ehci.h"
@ -25,6 +27,9 @@
static const char hcd_name[] = "ehci-atmel"; static const char hcd_name[] = "ehci-atmel";
#define EHCI_INSNREG(index) ((index) * 4 + 0x90)
#define EHCI_INSNREG08_HSIC_EN BIT(2)
/* interface and function clocks */ /* interface and function clocks */
#define hcd_to_atmel_ehci_priv(h) \ #define hcd_to_atmel_ehci_priv(h) \
((struct atmel_ehci_priv *)hcd_to_ehci(h)->priv) ((struct atmel_ehci_priv *)hcd_to_ehci(h)->priv)
@ -154,6 +159,9 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
goto fail_add_hcd; goto fail_add_hcd;
device_wakeup_enable(hcd->self.controller); device_wakeup_enable(hcd->self.controller);
if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC)
writel(EHCI_INSNREG08_HSIC_EN, hcd->regs + EHCI_INSNREG(8));
return retval; return retval;
fail_add_hcd: fail_add_hcd:

View File

@ -588,7 +588,7 @@ static int ehci_run (struct usb_hcd *hcd)
* hcc_params controls whether ehci->regs->segment must (!!!) * hcc_params controls whether ehci->regs->segment must (!!!)
* be used; it constrains QH/ITD/SITD and QTD locations. * be used; it constrains QH/ITD/SITD and QTD locations.
* dma_pool consistent memory always uses segment zero. * dma_pool consistent memory always uses segment zero.
* streaming mappings for I/O buffers, like pci_map_single(), * streaming mappings for I/O buffers, like dma_map_single(),
* can return segments above 4GB, if the device allows. * can return segments above 4GB, if the device allows.
* *
* NOTE: the dma mask is visible through dev->dma_mask, so * NOTE: the dma mask is visible through dev->dma_mask, so
@ -635,7 +635,16 @@ static int ehci_run (struct usb_hcd *hcd)
/* Wait until HC become operational */ /* Wait until HC become operational */
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
msleep(5); msleep(5);
rc = ehci_handshake(ehci, &ehci->regs->status, STS_HALT, 0, 100 * 1000);
/* For Aspeed, STS_HALT also depends on ASS/PSS status.
* Check CMD_RUN instead.
*/
if (ehci->is_aspeed)
rc = ehci_handshake(ehci, &ehci->regs->command, CMD_RUN,
1, 100 * 1000);
else
rc = ehci_handshake(ehci, &ehci->regs->status, STS_HALT,
0, 100 * 1000);
up_write(&ehci_cf_port_reset_rwsem); up_write(&ehci_cf_port_reset_rwsem);

View File

@ -745,12 +745,13 @@ int ehci_hub_control(
unsigned selector; unsigned selector;
/* /*
* Avoid underflow while calculating (wIndex & 0xff) - 1. * Avoid out-of-bounds values while calculating the port index
* The compiler might deduce that wIndex can never be 0 and then * from wIndex. The compiler doesn't like pointers to invalid
* optimize away the tests for !wIndex below. * addresses, even if they are never used.
*/ */
temp = wIndex & 0xff; temp = (wIndex - 1) & 0xff;
temp -= (temp > 0); if (temp >= HCS_N_PORTS_MAX)
temp = 0;
status_reg = &ehci->regs->port_status[temp]; status_reg = &ehci->regs->port_status[temp];
hostpc_reg = &ehci->regs->hostpc[temp]; hostpc_reg = &ehci->regs->hostpc[temp];

View File

@ -73,10 +73,9 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
if (!qh) if (!qh)
goto done; goto done;
qh->hw = (struct ehci_qh_hw *) qh->hw = (struct ehci_qh_hw *)
dma_pool_alloc(ehci->qh_pool, flags, &dma); dma_pool_zalloc(ehci->qh_pool, flags, &dma);
if (!qh->hw) if (!qh->hw)
goto fail; goto fail;
memset(qh->hw, 0, sizeof *qh->hw);
qh->qh_dma = dma; qh->qh_dma = dma;
// INIT_LIST_HEAD (&qh->qh_list); // INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD (&qh->qtd_list); INIT_LIST_HEAD (&qh->qtd_list);

View File

@ -258,8 +258,6 @@ static int mv_ehci_remove(struct platform_device *pdev)
return 0; return 0;
} }
MODULE_ALIAS("mv-ehci");
static const struct platform_device_id ehci_id_table[] = { static const struct platform_device_id ehci_id_table[] = {
{"pxa-u2oehci", 0}, {"pxa-u2oehci", 0},
{"pxa-sph", 0}, {"pxa-sph", 0},

View File

@ -297,6 +297,12 @@ static int ehci_platform_probe(struct platform_device *dev)
"has-transaction-translator")) "has-transaction-translator"))
hcd->has_tt = 1; hcd->has_tt = 1;
if (of_device_is_compatible(dev->dev.of_node,
"aspeed,ast2500-ehci") ||
of_device_is_compatible(dev->dev.of_node,
"aspeed,ast2600-ehci"))
ehci->is_aspeed = 1;
if (soc_device_match(quirk_poll_match)) if (soc_device_match(quirk_poll_match))
priv->quirk_poll = true; priv->quirk_poll = true;

View File

@ -219,6 +219,7 @@ struct ehci_hcd { /* one per controller */
unsigned need_oc_pp_cycle:1; /* MPC834X port power */ unsigned need_oc_pp_cycle:1; /* MPC834X port power */
unsigned imx28_write_fix:1; /* For Freescale i.MX28 */ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
unsigned spurious_oc:1; unsigned spurious_oc:1;
unsigned is_aspeed:1;
/* required for usb32 quirk */ /* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6) #define OHCI_CTRL_HCFS (3 << 6)

View File

@ -1859,10 +1859,9 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
if (!qh) if (!qh)
goto done; goto done;
qh->hw = (struct fotg210_qh_hw *) qh->hw = (struct fotg210_qh_hw *)
dma_pool_alloc(fotg210->qh_pool, flags, &dma); dma_pool_zalloc(fotg210->qh_pool, flags, &dma);
if (!qh->hw) if (!qh->hw)
goto fail; goto fail;
memset(qh->hw, 0, sizeof(*qh->hw));
qh->qh_dma = dma; qh->qh_dma = dma;
INIT_LIST_HEAD(&qh->qtd_list); INIT_LIST_HEAD(&qh->qtd_list);
@ -5023,7 +5022,7 @@ static int fotg210_run(struct usb_hcd *hcd)
* hcc_params controls whether fotg210->regs->segment must (!!!) * hcc_params controls whether fotg210->regs->segment must (!!!)
* be used; it constrains QH/ITD/SITD and QTD locations. * be used; it constrains QH/ITD/SITD and QTD locations.
* dma_pool consistent memory always uses segment zero. * dma_pool consistent memory always uses segment zero.
* streaming mappings for I/O buffers, like pci_map_single(), * streaming mappings for I/O buffers, like dma_map_single(),
* can return segments above 4GB, if the device allows. * can return segments above 4GB, if the device allows.
* *
* NOTE: the dma mask is visible through dev->dma_mask, so * NOTE: the dma mask is visible through dev->dma_mask, so

View File

@ -125,8 +125,6 @@ struct max3421_hcd {
struct task_struct *spi_thread; struct task_struct *spi_thread;
struct max3421_hcd *next;
enum max3421_rh_state rh_state; enum max3421_rh_state rh_state;
/* lower 16 bits contain port status, upper 16 bits the change mask: */ /* lower 16 bits contain port status, upper 16 bits the change mask: */
u32 port_status; u32 port_status;
@ -174,8 +172,6 @@ struct max3421_ep {
u8 retransmit; /* packet needs retransmission */ u8 retransmit; /* packet needs retransmission */
}; };
static struct max3421_hcd *max3421_hcd_list;
#define MAX3421_FIFO_SIZE 64 #define MAX3421_FIFO_SIZE 64
#define MAX3421_SPI_DIR_RD 0 /* read register from MAX3421 */ #define MAX3421_SPI_DIR_RD 0 /* read register from MAX3421 */
@ -1882,9 +1878,8 @@ max3421_probe(struct spi_device *spi)
} }
set_bit(HCD_FLAG_POLL_RH, &hcd->flags); set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
max3421_hcd = hcd_to_max3421(hcd); max3421_hcd = hcd_to_max3421(hcd);
max3421_hcd->next = max3421_hcd_list;
max3421_hcd_list = max3421_hcd;
INIT_LIST_HEAD(&max3421_hcd->ep_list); INIT_LIST_HEAD(&max3421_hcd->ep_list);
spi_set_drvdata(spi, max3421_hcd);
max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL); max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL);
if (!max3421_hcd->tx) if (!max3421_hcd->tx)
@ -1934,28 +1929,18 @@ error:
static int static int
max3421_remove(struct spi_device *spi) max3421_remove(struct spi_device *spi)
{ {
struct max3421_hcd *max3421_hcd = NULL, **prev; struct max3421_hcd *max3421_hcd;
struct usb_hcd *hcd = NULL; struct usb_hcd *hcd;
unsigned long flags; unsigned long flags;
for (prev = &max3421_hcd_list; *prev; prev = &(*prev)->next) { max3421_hcd = spi_get_drvdata(spi);
max3421_hcd = *prev; hcd = max3421_to_hcd(max3421_hcd);
hcd = max3421_to_hcd(max3421_hcd);
if (hcd->self.controller == &spi->dev)
break;
}
if (!max3421_hcd) {
dev_err(&spi->dev, "no MAX3421 HCD found for SPI device %p\n",
spi);
return -ENODEV;
}
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
spin_lock_irqsave(&max3421_hcd->lock, flags); spin_lock_irqsave(&max3421_hcd->lock, flags);
kthread_stop(max3421_hcd->spi_thread); kthread_stop(max3421_hcd->spi_thread);
*prev = max3421_hcd->next;
spin_unlock_irqrestore(&max3421_hcd->lock, flags); spin_unlock_irqrestore(&max3421_hcd->lock, flags);

View File

@ -191,8 +191,7 @@ static int ohci_urb_enqueue (
} }
/* allocate the private part of the URB */ /* allocate the private part of the URB */
urb_priv = kzalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), urb_priv = kzalloc(struct_size(urb_priv, td, size), mem_flags);
mem_flags);
if (!urb_priv) if (!urb_priv)
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD (&urb_priv->pending); INIT_LIST_HEAD (&urb_priv->pending);

View File

@ -91,6 +91,9 @@ __acquires(ohci->lock)
update_done_list(ohci); update_done_list(ohci);
ohci_work(ohci); ohci_work(ohci);
/* All ED unlinks should be finished, no need for SOF interrupts */
ohci_writel(ohci, OHCI_INTR_SF, &ohci->regs->intrdisable);
/* /*
* Some controllers don't handle "global" suspend properly if * Some controllers don't handle "global" suspend properly if
* there are unsuspended ports. For these controllers, put all * there are unsuspended ports. For these controllers, put all

View File

@ -199,7 +199,7 @@ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev)
if (usb_disabled()) if (usb_disabled())
return -ENODEV; return -ENODEV;
if (!cell) if (!cell || !regs || !config || !sram)
return -EINVAL; return -EINVAL;
if (irq < 0) if (irq < 0)

View File

@ -3131,7 +3131,7 @@ static int oxu_run(struct usb_hcd *hcd)
/* hcc_params controls whether oxu->regs->segment must (!!!) /* hcc_params controls whether oxu->regs->segment must (!!!)
* be used; it constrains QH/ITD/SITD and QTD locations. * be used; it constrains QH/ITD/SITD and QTD locations.
* dma_pool consistent memory always uses segment zero. * dma_pool consistent memory always uses segment zero.
* streaming mappings for I/O buffers, like pci_map_single(), * streaming mappings for I/O buffers, like dma_map_single(),
* can return segments above 4GB, if the device allows. * can return segments above 4GB, if the device allows.
* *
* NOTE: the dma mask is visible through dev->dma_mask, so * NOTE: the dma mask is visible through dev->dma_mask, so

View File

@ -734,7 +734,7 @@ static void drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
if (!need_bw_sch(udev, ep)) if (!need_bw_sch(udev, ep))
return; return;
xhci_err(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep, hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep,
hn, hentry, (unsigned long)ep) { hn, hentry, (unsigned long)ep) {

View File

@ -65,6 +65,13 @@
#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb #define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc #define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc
#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_1 0x161a
#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_2 0x161b
#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_3 0x161d
#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_4 0x161e
#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_5 0x15d6
#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_6 0x15d7
#define PCI_DEVICE_ID_ASMEDIA_1042_XHCI 0x1042 #define PCI_DEVICE_ID_ASMEDIA_1042_XHCI 0x1042
#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142 #define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142
#define PCI_DEVICE_ID_ASMEDIA_1142_XHCI 0x1242 #define PCI_DEVICE_ID_ASMEDIA_1142_XHCI 0x1242
@ -317,6 +324,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4)) pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4))
xhci->quirks |= XHCI_NO_SOFT_RETRY; xhci->quirks |= XHCI_NO_SOFT_RETRY;
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
(pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_1 ||
pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_2 ||
pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_3 ||
pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_4 ||
pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_5 ||
pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_6))
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
if (xhci->quirks & XHCI_RESET_ON_RESUME) if (xhci->quirks & XHCI_RESET_ON_RESUME)
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"QUIRK: Resetting on resume"); "QUIRK: Resetting on resume");

View File

@ -99,10 +99,6 @@ struct iowarrior {
/* globals */ /* globals */
/*--------------*/ /*--------------*/
/*
* USB spec identifies 5 second timeouts.
*/
#define GET_TIMEOUT 5
#define USB_REQ_GET_REPORT 0x01 #define USB_REQ_GET_REPORT 0x01
//#if 0 //#if 0
static int usb_get_report(struct usb_device *dev, static int usb_get_report(struct usb_device *dev,
@ -114,7 +110,7 @@ static int usb_get_report(struct usb_device *dev,
USB_DIR_IN | USB_TYPE_CLASS | USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, (type << 8) + id, USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size, inter->desc.bInterfaceNumber, buf, size,
GET_TIMEOUT*HZ); USB_CTRL_GET_TIMEOUT);
} }
//#endif //#endif
@ -129,7 +125,7 @@ static int usb_set_report(struct usb_interface *intf, unsigned char type,
USB_TYPE_CLASS | USB_RECIP_INTERFACE, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id, (type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf, intf->cur_altsetting->desc.bInterfaceNumber, buf,
size, HZ); size, 1000);
} }
/*---------------------*/ /*---------------------*/

View File

@ -120,7 +120,7 @@ config USB_MUSB_MEDIATEK
tristate "MediaTek platforms" tristate "MediaTek platforms"
depends on ARCH_MEDIATEK || COMPILE_TEST depends on ARCH_MEDIATEK || COMPILE_TEST
depends on NOP_USB_XCEIV depends on NOP_USB_XCEIV
depends on GENERIC_PHY select GENERIC_PHY
select USB_ROLE_SWITCH select USB_ROLE_SWITCH
comment "MUSB DMA mode" comment "MUSB DMA mode"

View File

@ -185,6 +185,7 @@ static int mtk_otg_switch_init(struct mtk_glue *glue)
role_sx_desc.set = musb_usb_role_sx_set; role_sx_desc.set = musb_usb_role_sx_set;
role_sx_desc.get = musb_usb_role_sx_get; role_sx_desc.get = musb_usb_role_sx_get;
role_sx_desc.allow_userspace_control = true;
role_sx_desc.fwnode = dev_fwnode(glue->dev); role_sx_desc.fwnode = dev_fwnode(glue->dev);
role_sx_desc.driver_data = glue; role_sx_desc.driver_data = glue;
glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc); glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc);

View File

@ -1247,9 +1247,11 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
status = musb_queue_resume_work(musb, status = musb_queue_resume_work(musb,
musb_ep_restart_resume_work, musb_ep_restart_resume_work,
request); request);
if (status < 0) if (status < 0) {
dev_err(musb->controller, "%s resume work: %i\n", dev_err(musb->controller, "%s resume work: %i\n",
__func__, status); __func__, status);
list_del(&request->list);
}
} }
unlock: unlock:

View File

@ -440,6 +440,10 @@ static u8 sunxi_musb_readb(void __iomem *addr, u32 offset)
return 0xde; return 0xde;
return readb(addr + SUNXI_MUSB_CONFIGDATA); return readb(addr + SUNXI_MUSB_CONFIGDATA);
case MUSB_ULPI_BUSCONTROL:
dev_warn(sunxi_musb->controller->parent,
"sunxi-musb does not have ULPI bus control register\n");
return 0;
/* Offset for these is fixed by sunxi_musb_busctl_offset() */ /* Offset for these is fixed by sunxi_musb_busctl_offset() */
case SUNXI_MUSB_TXFUNCADDR: case SUNXI_MUSB_TXFUNCADDR:
case SUNXI_MUSB_TXHUBADDR: case SUNXI_MUSB_TXHUBADDR:
@ -494,6 +498,10 @@ static void sunxi_musb_writeb(void __iomem *addr, unsigned offset, u8 data)
return writeb(data, addr + SUNXI_MUSB_TXFIFOSZ); return writeb(data, addr + SUNXI_MUSB_TXFIFOSZ);
case MUSB_RXFIFOSZ: case MUSB_RXFIFOSZ:
return writeb(data, addr + SUNXI_MUSB_RXFIFOSZ); return writeb(data, addr + SUNXI_MUSB_RXFIFOSZ);
case MUSB_ULPI_BUSCONTROL:
dev_warn(sunxi_musb->controller->parent,
"sunxi-musb does not have ULPI bus control register\n");
return;
/* Offset for these is fixed by sunxi_musb_busctl_offset() */ /* Offset for these is fixed by sunxi_musb_busctl_offset() */
case SUNXI_MUSB_TXFUNCADDR: case SUNXI_MUSB_TXFUNCADDR:
case SUNXI_MUSB_TXHUBADDR: case SUNXI_MUSB_TXHUBADDR:

View File

@ -1104,6 +1104,11 @@ static int tusb_musb_init(struct musb *musb)
/* dma address for async dma */ /* dma address for async dma */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
pr_debug("no async dma resource?\n");
ret = -ENODEV;
goto done;
}
musb->async = mem->start; musb->async = mem->start;
/* dma address for sync dma */ /* dma address for sync dma */

View File

@ -194,8 +194,6 @@ static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb, struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
phy); phy);
dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host);
mutex_lock(&tu->serialize); mutex_lock(&tu->serialize);
if (host == NULL) { if (host == NULL) {
@ -224,8 +222,6 @@ static int tahvo_usb_set_peripheral(struct usb_otg *otg,
struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb, struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
phy); phy);
dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget);
mutex_lock(&tu->serialize); mutex_lock(&tu->serialize);
if (!gadget) { if (!gadget) {

View File

@ -63,6 +63,10 @@
#define A_VBUS_VLD_WAKEUP_EN BIT(30) #define A_VBUS_VLD_WAKEUP_EN BIT(30)
#define USB_PHY_VBUS_WAKEUP_ID 0x408 #define USB_PHY_VBUS_WAKEUP_ID 0x408
#define ID_INT_EN BIT(0)
#define ID_CHG_DET BIT(1)
#define VBUS_WAKEUP_INT_EN BIT(8)
#define VBUS_WAKEUP_CHG_DET BIT(9)
#define VBUS_WAKEUP_STS BIT(10) #define VBUS_WAKEUP_STS BIT(10)
#define VBUS_WAKEUP_WAKEUP_EN BIT(30) #define VBUS_WAKEUP_WAKEUP_EN BIT(30)
@ -158,6 +162,10 @@
#define USB_USBMODE_HOST (3 << 0) #define USB_USBMODE_HOST (3 << 0)
#define USB_USBMODE_DEVICE (2 << 0) #define USB_USBMODE_DEVICE (2 << 0)
#define PMC_USB_AO 0xf0
#define VBUS_WAKEUP_PD_P0 BIT(2)
#define ID_PD_P0 BIT(3)
static DEFINE_SPINLOCK(utmip_pad_lock); static DEFINE_SPINLOCK(utmip_pad_lock);
static unsigned int utmip_pad_count; static unsigned int utmip_pad_count;
@ -533,13 +541,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val &= ~USB_WAKE_ON_RESUME_EN; val &= ~USB_WAKE_ON_RESUME_EN;
writel_relaxed(val, base + USB_SUSP_CTRL); writel_relaxed(val, base + USB_SUSP_CTRL);
if (phy->mode == USB_DR_MODE_PERIPHERAL) { if (phy->mode != USB_DR_MODE_HOST) {
val = readl_relaxed(base + USB_SUSP_CTRL); val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
writel_relaxed(val, base + USB_SUSP_CTRL); writel_relaxed(val, base + USB_SUSP_CTRL);
val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID); val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID);
val &= ~VBUS_WAKEUP_WAKEUP_EN; val &= ~VBUS_WAKEUP_WAKEUP_EN;
val &= ~(ID_CHG_DET | VBUS_WAKEUP_CHG_DET);
writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID); writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID);
val = readl_relaxed(base + USB_PHY_VBUS_SENSORS); val = readl_relaxed(base + USB_PHY_VBUS_SENSORS);
@ -687,9 +696,10 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy)
* Ask VBUS sensor to generate wake event once cable is * Ask VBUS sensor to generate wake event once cable is
* connected. * connected.
*/ */
if (phy->mode == USB_DR_MODE_PERIPHERAL) { if (phy->mode != USB_DR_MODE_HOST) {
val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID); val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID);
val |= VBUS_WAKEUP_WAKEUP_EN; val |= VBUS_WAKEUP_WAKEUP_EN;
val &= ~(ID_CHG_DET | VBUS_WAKEUP_CHG_DET);
writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID); writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID);
val = readl_relaxed(base + USB_PHY_VBUS_SENSORS); val = readl_relaxed(base + USB_PHY_VBUS_SENSORS);
@ -893,6 +903,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
if (WARN_ON(!phy->freq)) if (WARN_ON(!phy->freq))
return; return;
usb_phy_set_wakeup(u_phy, false);
tegra_usb_phy_power_off(phy); tegra_usb_phy_power_off(phy);
if (!phy->is_ulpi_phy) if (!phy->is_ulpi_phy)
@ -904,26 +915,146 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
phy->freq = NULL; phy->freq = NULL;
} }
static irqreturn_t tegra_usb_phy_isr(int irq, void *data)
{
u32 val, int_mask = ID_CHG_DET | VBUS_WAKEUP_CHG_DET;
struct tegra_usb_phy *phy = data;
void __iomem *base = phy->regs;
/*
* The PHY interrupt also wakes the USB controller driver since
* interrupt is shared. We don't do anything in the PHY driver,
* so just clear the interrupt.
*/
val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID);
writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID);
return val & int_mask ? IRQ_HANDLED : IRQ_NONE;
}
static int tegra_usb_phy_set_wakeup(struct usb_phy *u_phy, bool enable) static int tegra_usb_phy_set_wakeup(struct usb_phy *u_phy, bool enable)
{ {
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
void __iomem *base = phy->regs;
int ret = 0;
u32 val;
if (phy->wakeup_enabled && phy->mode != USB_DR_MODE_HOST &&
phy->irq > 0) {
disable_irq(phy->irq);
val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID);
val &= ~(ID_INT_EN | VBUS_WAKEUP_INT_EN);
writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID);
enable_irq(phy->irq);
free_irq(phy->irq, phy);
phy->wakeup_enabled = false;
}
if (enable && phy->mode != USB_DR_MODE_HOST && phy->irq > 0) {
ret = request_irq(phy->irq, tegra_usb_phy_isr, IRQF_SHARED,
dev_name(phy->u_phy.dev), phy);
if (!ret) {
disable_irq(phy->irq);
/*
* USB clock will be resumed once wake event will be
* generated. The ID-change event requires to have
* interrupts enabled, otherwise it won't be generated.
*/
val = readl_relaxed(base + USB_PHY_VBUS_WAKEUP_ID);
val |= ID_INT_EN | VBUS_WAKEUP_INT_EN;
writel_relaxed(val, base + USB_PHY_VBUS_WAKEUP_ID);
enable_irq(phy->irq);
} else {
dev_err(phy->u_phy.dev,
"Failed to request interrupt: %d", ret);
enable = false;
}
}
phy->wakeup_enabled = enable; phy->wakeup_enabled = enable;
return 0; return ret;
} }
static int tegra_usb_phy_set_suspend(struct usb_phy *u_phy, int suspend) static int tegra_usb_phy_set_suspend(struct usb_phy *u_phy, int suspend)
{ {
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
int ret;
if (WARN_ON(!phy->freq)) if (WARN_ON(!phy->freq))
return -EINVAL; return -EINVAL;
/*
* PHY is sharing IRQ with the CI driver, hence here we either
* disable interrupt for both PHY and CI or for CI only. The
* interrupt needs to be disabled while hardware is reprogrammed
* because interrupt touches the programmed registers, and thus,
* there could be a race condition.
*/
if (phy->irq > 0)
disable_irq(phy->irq);
if (suspend) if (suspend)
return tegra_usb_phy_power_off(phy); ret = tegra_usb_phy_power_off(phy);
else else
return tegra_usb_phy_power_on(phy); ret = tegra_usb_phy_power_on(phy);
if (phy->irq > 0)
enable_irq(phy->irq);
return ret;
}
static int tegra_usb_phy_configure_pmc(struct tegra_usb_phy *phy)
{
int err, val = 0;
/* older device-trees don't have PMC regmap */
if (!phy->pmc_regmap)
return 0;
/*
* Tegra20 has a different layout of PMC USB register bits and AO is
* enabled by default after system reset on Tegra20, so assume nothing
* to do on Tegra20.
*/
if (!phy->soc_config->requires_pmc_ao_power_up)
return 0;
/* enable VBUS wake-up detector */
if (phy->mode != USB_DR_MODE_HOST)
val |= VBUS_WAKEUP_PD_P0 << phy->instance * 4;
/* enable ID-pin ACC detector for OTG mode switching */
if (phy->mode == USB_DR_MODE_OTG)
val |= ID_PD_P0 << phy->instance * 4;
/* disable detectors to reset them */
err = regmap_set_bits(phy->pmc_regmap, PMC_USB_AO, val);
if (err) {
dev_err(phy->u_phy.dev, "Failed to disable PMC AO: %d\n", err);
return err;
}
usleep_range(10, 100);
/* enable detectors */
err = regmap_clear_bits(phy->pmc_regmap, PMC_USB_AO, val);
if (err) {
dev_err(phy->u_phy.dev, "Failed to enable PMC AO: %d\n", err);
return err;
}
/* detectors starts to work after 10ms */
usleep_range(10000, 15000);
return 0;
} }
static int tegra_usb_phy_init(struct usb_phy *u_phy) static int tegra_usb_phy_init(struct usb_phy *u_phy)
@ -967,6 +1098,10 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
goto disable_vbus; goto disable_vbus;
} }
err = tegra_usb_phy_configure_pmc(phy);
if (err)
goto close_phy;
err = tegra_usb_phy_power_on(phy); err = tegra_usb_phy_power_on(phy);
if (err) if (err)
goto close_phy; goto close_phy;
@ -1135,11 +1270,56 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
return 0; return 0;
} }
static void tegra_usb_phy_put_pmc_device(void *dev)
{
put_device(dev);
}
static int tegra_usb_phy_parse_pmc(struct device *dev,
struct tegra_usb_phy *phy)
{
struct platform_device *pmc_pdev;
struct of_phandle_args args;
int err;
err = of_parse_phandle_with_fixed_args(dev->of_node, "nvidia,pmc",
1, 0, &args);
if (err) {
if (err != -ENOENT)
return err;
dev_warn_once(dev, "nvidia,pmc is missing, please update your device-tree\n");
return 0;
}
pmc_pdev = of_find_device_by_node(args.np);
of_node_put(args.np);
if (!pmc_pdev)
return -ENODEV;
err = devm_add_action_or_reset(dev, tegra_usb_phy_put_pmc_device,
&pmc_pdev->dev);
if (err)
return err;
if (!platform_get_drvdata(pmc_pdev))
return -EPROBE_DEFER;
phy->pmc_regmap = dev_get_regmap(&pmc_pdev->dev, "usb_sleepwalk");
if (!phy->pmc_regmap)
return -EINVAL;
phy->instance = args.args[0];
return 0;
}
static const struct tegra_phy_soc_config tegra20_soc_config = { static const struct tegra_phy_soc_config tegra20_soc_config = {
.utmi_pll_config_in_car_module = false, .utmi_pll_config_in_car_module = false,
.has_hostpc = false, .has_hostpc = false,
.requires_usbmode_setup = false, .requires_usbmode_setup = false,
.requires_extra_tuning_parameters = false, .requires_extra_tuning_parameters = false,
.requires_pmc_ao_power_up = false,
}; };
static const struct tegra_phy_soc_config tegra30_soc_config = { static const struct tegra_phy_soc_config tegra30_soc_config = {
@ -1147,6 +1327,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
.has_hostpc = true, .has_hostpc = true,
.requires_usbmode_setup = true, .requires_usbmode_setup = true,
.requires_extra_tuning_parameters = true, .requires_extra_tuning_parameters = true,
.requires_pmc_ao_power_up = true,
}; };
static const struct of_device_id tegra_usb_phy_id_table[] = { static const struct of_device_id tegra_usb_phy_id_table[] = {
@ -1172,6 +1353,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
tegra_phy->soc_config = of_device_get_match_data(&pdev->dev); tegra_phy->soc_config = of_device_get_match_data(&pdev->dev);
tegra_phy->irq = platform_get_irq_optional(pdev, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
@ -1215,6 +1397,12 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
return err; return err;
} }
err = tegra_usb_phy_parse_pmc(&pdev->dev, tegra_phy);
if (err) {
dev_err_probe(&pdev->dev, err, "Failed to get PMC regmap\n");
return err;
}
phy_type = of_usb_get_phy_mode(np); phy_type = of_usb_get_phy_mode(np);
switch (phy_type) { switch (phy_type) {
case USBPHY_INTERFACE_MODE_UTMI: case USBPHY_INTERFACE_MODE_UTMI:

View File

@ -131,17 +131,11 @@ static int ch341_control_in(struct usb_device *dev,
dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__, dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__,
request, value, index, bufsize); request, value, index, bufsize);
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, r = usb_control_msg_recv(dev, 0, request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT); value, index, buf, bufsize, DEFAULT_TIMEOUT,
if (r < (int)bufsize) { GFP_KERNEL);
if (r >= 0) { if (r) {
dev_err(&dev->dev,
"short control message received (%d < %u)\n",
r, bufsize);
r = -EIO;
}
dev_err(&dev->dev, "failed to receive control message: %d\n", dev_err(&dev->dev, "failed to receive control message: %d\n",
r); r);
return r; return r;
@ -287,24 +281,19 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control)
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{ {
const unsigned int size = 2; const unsigned int size = 2;
char *buffer; u8 buffer[2];
int r; int r;
unsigned long flags; unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size); r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
if (r < 0) if (r)
goto out; return r;
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT; priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT;
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
out: kfree(buffer); return 0;
return r;
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
@ -312,31 +301,28 @@ out: kfree(buffer);
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{ {
const unsigned int size = 2; const unsigned int size = 2;
char *buffer; u8 buffer[2];
int r; int r;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
/* expect two bytes 0x27 0x00 */ /* expect two bytes 0x27 0x00 */
r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size); r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
if (r < 0) if (r)
goto out; return r;
dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]); dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0); r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
if (r < 0) if (r < 0)
goto out; return r;
r = ch341_set_baudrate_lcr(dev, priv, priv->baud_rate, priv->lcr); r = ch341_set_baudrate_lcr(dev, priv, priv->baud_rate, priv->lcr);
if (r < 0) if (r < 0)
goto out; return r;
r = ch341_set_handshake(dev, priv->mcr); r = ch341_set_handshake(dev, priv->mcr);
if (r < 0)
return r;
out: kfree(buffer); return 0;
return r;
} }
static int ch341_detect_quirks(struct usb_serial_port *port) static int ch341_detect_quirks(struct usb_serial_port *port)
@ -345,40 +331,27 @@ static int ch341_detect_quirks(struct usb_serial_port *port)
struct usb_device *udev = port->serial->dev; struct usb_device *udev = port->serial->dev;
const unsigned int size = 2; const unsigned int size = 2;
unsigned long quirks = 0; unsigned long quirks = 0;
char *buffer; u8 buffer[2];
int r; int r;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
/* /*
* A subset of CH34x devices does not support all features. The * A subset of CH34x devices does not support all features. The
* prescaler is limited and there is no support for sending a RS232 * prescaler is limited and there is no support for sending a RS232
* break condition. A read failure when trying to set up the latter is * break condition. A read failure when trying to set up the latter is
* used to detect these devices. * used to detect these devices.
*/ */
r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), CH341_REQ_READ_REG, r = usb_control_msg_recv(udev, 0, CH341_REQ_READ_REG,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT); CH341_REG_BREAK, 0, &buffer, size,
DEFAULT_TIMEOUT, GFP_KERNEL);
if (r == -EPIPE) { if (r == -EPIPE) {
dev_info(&port->dev, "break control not supported, using simulated break\n"); dev_info(&port->dev, "break control not supported, using simulated break\n");
quirks = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK; quirks = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
r = 0; r = 0;
goto out; } else if (r) {
}
if (r != size) {
if (r >= 0)
r = -EIO;
dev_err(&port->dev, "failed to read break control: %d\n", r); dev_err(&port->dev, "failed to read break control: %d\n", r);
goto out;
} }
r = 0;
out:
kfree(buffer);
if (quirks) { if (quirks) {
dev_dbg(&port->dev, "enabling quirk flags: 0x%02lx\n", quirks); dev_dbg(&port->dev, "enabling quirk flags: 0x%02lx\n", quirks);
priv->quirks |= quirks; priv->quirks |= quirks;
@ -647,23 +620,19 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
struct ch341_private *priv = usb_get_serial_port_data(port); struct ch341_private *priv = usb_get_serial_port_data(port);
int r; int r;
uint16_t reg_contents; uint16_t reg_contents;
uint8_t *break_reg; uint8_t break_reg[2];
if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) { if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) {
ch341_simulate_break(tty, break_state); ch341_simulate_break(tty, break_state);
return; return;
} }
break_reg = kmalloc(2, GFP_KERNEL);
if (!break_reg)
return;
r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG, r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
ch341_break_reg, 0, break_reg, 2); ch341_break_reg, 0, break_reg, 2);
if (r < 0) { if (r) {
dev_err(&port->dev, "%s - USB control read error (%d)\n", dev_err(&port->dev, "%s - USB control read error (%d)\n",
__func__, r); __func__, r);
goto out; return;
} }
dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n", dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
__func__, break_reg[0], break_reg[1]); __func__, break_reg[0], break_reg[1]);
@ -684,8 +653,6 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
if (r < 0) if (r < 0)
dev_err(&port->dev, "%s - USB control write error (%d)\n", dev_err(&port->dev, "%s - USB control write error (%d)\n",
__func__, r); __func__, r);
out:
kfree(break_reg);
} }
static int ch341_tiocmset(struct tty_struct *tty, static int ch341_tiocmset(struct tty_struct *tty,

View File

@ -631,30 +631,20 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
{ {
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
void *dmabuf;
int result; int result;
dmabuf = kmalloc(bufsize, GFP_KERNEL);
if (!dmabuf)
return -ENOMEM;
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), result = usb_control_msg_recv(serial->dev, 0, req,
req, REQTYPE_INTERFACE_TO_HOST, 0, REQTYPE_INTERFACE_TO_HOST, 0,
port_priv->bInterfaceNumber, dmabuf, bufsize, port_priv->bInterfaceNumber, buf, bufsize,
USB_CTRL_GET_TIMEOUT); USB_CTRL_SET_TIMEOUT, GFP_KERNEL);
if (result == bufsize) { if (result) {
memcpy(buf, dmabuf, bufsize);
result = 0;
} else {
dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n", dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n",
req, bufsize, result); req, bufsize, result);
if (result >= 0) return result;
result = -EIO;
} }
kfree(dmabuf); return 0;
return result;
} }
/* /*
@ -672,31 +662,19 @@ static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val)
static int cp210x_read_vendor_block(struct usb_serial *serial, u8 type, u16 val, static int cp210x_read_vendor_block(struct usb_serial *serial, u8 type, u16 val,
void *buf, int bufsize) void *buf, int bufsize)
{ {
void *dmabuf;
int result; int result;
dmabuf = kmalloc(bufsize, GFP_KERNEL); result = usb_control_msg_recv(serial->dev, 0, CP210X_VENDOR_SPECIFIC,
if (!dmabuf) type, val, cp210x_interface_num(serial), buf, bufsize,
return -ENOMEM; USB_CTRL_GET_TIMEOUT, GFP_KERNEL);
if (result) {
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
CP210X_VENDOR_SPECIFIC, type, val,
cp210x_interface_num(serial), dmabuf, bufsize,
USB_CTRL_GET_TIMEOUT);
if (result == bufsize) {
memcpy(buf, dmabuf, bufsize);
result = 0;
} else {
dev_err(&serial->interface->dev, dev_err(&serial->interface->dev,
"failed to get vendor val 0x%04x size %d: %d\n", val, "failed to get vendor val 0x%04x size %d: %d\n", val,
bufsize, result); bufsize, result);
if (result >= 0) return result;
result = -EIO;
} }
kfree(dmabuf); return 0;
return result;
} }
/* /*
@ -730,21 +708,13 @@ static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req,
{ {
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
void *dmabuf;
int result; int result;
dmabuf = kmemdup(buf, bufsize, GFP_KERNEL); result = usb_control_msg_send(serial->dev, 0, req,
if (!dmabuf) REQTYPE_HOST_TO_INTERFACE, 0,
return -ENOMEM; port_priv->bInterfaceNumber, buf, bufsize,
USB_CTRL_SET_TIMEOUT, GFP_KERNEL);
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), if (result) {
req, REQTYPE_HOST_TO_INTERFACE, 0,
port_priv->bInterfaceNumber, dmabuf, bufsize,
USB_CTRL_SET_TIMEOUT);
kfree(dmabuf);
if (result < 0) {
dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n", dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n",
req, bufsize, result); req, bufsize, result);
return result; return result;
@ -773,21 +743,12 @@ static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val)
static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type, static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type,
u16 val, void *buf, int bufsize) u16 val, void *buf, int bufsize)
{ {
void *dmabuf;
int result; int result;
dmabuf = kmemdup(buf, bufsize, GFP_KERNEL); result = usb_control_msg_send(serial->dev, 0, CP210X_VENDOR_SPECIFIC,
if (!dmabuf) type, val, cp210x_interface_num(serial), buf, bufsize,
return -ENOMEM; USB_CTRL_SET_TIMEOUT, GFP_KERNEL);
if (result) {
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
CP210X_VENDOR_SPECIFIC, type, val,
cp210x_interface_num(serial), dmabuf, bufsize,
USB_CTRL_SET_TIMEOUT);
kfree(dmabuf);
if (result < 0) {
dev_err(&serial->interface->dev, dev_err(&serial->interface->dev,
"failed to set vendor val 0x%04x size %d: %d\n", val, "failed to set vendor val 0x%04x size %d: %d\n", val,
bufsize, result); bufsize, result);
@ -952,29 +913,21 @@ static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
{ {
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
struct cp210x_comm_status *sts; struct cp210x_comm_status sts;
int result; int result;
sts = kmalloc(sizeof(*sts), GFP_KERNEL); result = usb_control_msg_recv(serial->dev, 0, CP210X_GET_COMM_STATUS,
if (!sts) REQTYPE_INTERFACE_TO_HOST, 0,
return -ENOMEM; port_priv->bInterfaceNumber, &sts, sizeof(sts),
USB_CTRL_GET_TIMEOUT, GFP_KERNEL);
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), if (result) {
CP210X_GET_COMM_STATUS, REQTYPE_INTERFACE_TO_HOST,
0, port_priv->bInterfaceNumber, sts, sizeof(*sts),
USB_CTRL_GET_TIMEOUT);
if (result == sizeof(*sts)) {
*count = le32_to_cpu(sts->ulAmountInOutQueue);
result = 0;
} else {
dev_err(&port->dev, "failed to get comm status: %d\n", result); dev_err(&port->dev, "failed to get comm status: %d\n", result);
if (result >= 0) return result;
result = -EIO;
} }
kfree(sts); *count = le32_to_cpu(sts.ulAmountInOutQueue);
return result; return 0;
} }
static bool cp210x_tx_empty(struct usb_serial_port *port) static bool cp210x_tx_empty(struct usb_serial_port *port)

View File

@ -139,67 +139,46 @@ static int calc_baud_divisor(speed_t baudrate, speed_t clockrate)
static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val)
{ {
int status; int status;
u8 *tmp;
struct usb_device *dev = port->serial->dev; struct usb_device *dev = port->serial->dev;
tmp = kmalloc(sizeof(*val), GFP_KERNEL); status = usb_control_msg_recv(dev,
if (!tmp) 0,
return -ENOMEM; F81232_REGISTER_REQUEST,
F81232_GET_REGISTER,
status = usb_control_msg(dev, reg,
usb_rcvctrlpipe(dev, 0), 0,
F81232_REGISTER_REQUEST, val,
F81232_GET_REGISTER, sizeof(*val),
reg, USB_CTRL_GET_TIMEOUT,
0, GFP_KERNEL);
tmp, if (status) {
sizeof(*val),
USB_CTRL_GET_TIMEOUT);
if (status != sizeof(*val)) {
dev_err(&port->dev, "%s failed status: %d\n", __func__, status); dev_err(&port->dev, "%s failed status: %d\n", __func__, status);
status = usb_translate_errors(status);
if (status < 0)
status = usb_translate_errors(status);
else
status = -EIO;
} else {
status = 0;
*val = *tmp;
} }
kfree(tmp);
return status; return status;
} }
static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val)
{ {
int status; int status;
u8 *tmp;
struct usb_device *dev = port->serial->dev; struct usb_device *dev = port->serial->dev;
tmp = kmalloc(sizeof(val), GFP_KERNEL); status = usb_control_msg_send(dev,
if (!tmp) 0,
return -ENOMEM; F81232_REGISTER_REQUEST,
F81232_SET_REGISTER,
*tmp = val; reg,
0,
status = usb_control_msg(dev, &val,
usb_sndctrlpipe(dev, 0), sizeof(val),
F81232_REGISTER_REQUEST, USB_CTRL_SET_TIMEOUT,
F81232_SET_REGISTER, GFP_KERNEL);
reg, if (status) {
0,
tmp,
sizeof(val),
USB_CTRL_SET_TIMEOUT);
if (status < 0) {
dev_err(&port->dev, "%s failed status: %d\n", __func__, status); dev_err(&port->dev, "%s failed status: %d\n", __func__, status);
status = usb_translate_errors(status); status = usb_translate_errors(status);
} else {
status = 0;
} }
kfree(tmp);
return status; return status;
} }
@ -857,28 +836,22 @@ static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
int retry = F81534A_ACCESS_REG_RETRY; int retry = F81534A_ACCESS_REG_RETRY;
int status; int status;
u8 *tmp;
tmp = kmemdup(val, size, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
while (retry--) { while (retry--) {
status = usb_control_msg(dev, status = usb_control_msg_send(dev,
usb_sndctrlpipe(dev, 0), 0,
F81232_REGISTER_REQUEST, F81232_REGISTER_REQUEST,
F81232_SET_REGISTER, F81232_SET_REGISTER,
reg, reg,
0, 0,
tmp, val,
size, size,
USB_CTRL_SET_TIMEOUT); USB_CTRL_SET_TIMEOUT,
if (status < 0) { GFP_KERNEL);
if (status) {
status = usb_translate_errors(status); status = usb_translate_errors(status);
if (status == -EIO) if (status == -EIO)
continue; continue;
} else {
status = 0;
} }
break; break;
@ -889,7 +862,6 @@ static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
reg, status); reg, status);
} }
kfree(tmp);
return status; return status;
} }

View File

@ -1437,27 +1437,15 @@ static int _read_latency_timer(struct usb_serial_port *port)
{ {
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
struct usb_device *udev = port->serial->dev; struct usb_device *udev = port->serial->dev;
unsigned char *buf; u8 buf;
int rv; int rv;
buf = kmalloc(1, GFP_KERNEL); rv = usb_control_msg_recv(udev, 0, FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
if (!buf) FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE, 0,
return -ENOMEM; priv->interface, &buf, 1, WDR_TIMEOUT,
GFP_KERNEL);
rv = usb_control_msg(udev, if (rv == 0)
usb_rcvctrlpipe(udev, 0), rv = buf;
FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
0, priv->interface,
buf, 1, WDR_TIMEOUT);
if (rv < 1) {
if (rv >= 0)
rv = -EIO;
} else {
rv = buf[0];
}
kfree(buf);
return rv; return rv;
} }
@ -1852,32 +1840,21 @@ static int ftdi_read_cbus_pins(struct usb_serial_port *port)
{ {
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
unsigned char *buf; u8 buf;
int result; int result;
result = usb_autopm_get_interface(serial->interface); result = usb_autopm_get_interface(serial->interface);
if (result) if (result)
return result; return result;
buf = kmalloc(1, GFP_KERNEL); result = usb_control_msg_recv(serial->dev, 0,
if (!buf) { FTDI_SIO_READ_PINS_REQUEST,
usb_autopm_put_interface(serial->interface); FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
return -ENOMEM; priv->interface, &buf, 1, WDR_TIMEOUT,
} GFP_KERNEL);
if (result == 0)
result = buf;
result = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_READ_PINS_REQUEST,
FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
priv->interface, buf, 1, WDR_TIMEOUT);
if (result < 1) {
if (result >= 0)
result = -EIO;
} else {
result = buf[0];
}
kfree(buf);
usb_autopm_put_interface(serial->interface); usb_autopm_put_interface(serial->interface);
return result; return result;

View File

@ -2890,22 +2890,22 @@ static int keyspan_port_probe(struct usb_serial_port *port)
for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) { for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) {
p_priv->in_buffer[i] = kzalloc(IN_BUFLEN, GFP_KERNEL); p_priv->in_buffer[i] = kzalloc(IN_BUFLEN, GFP_KERNEL);
if (!p_priv->in_buffer[i]) if (!p_priv->in_buffer[i])
goto err_in_buffer; goto err_free_in_buffer;
} }
for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) { for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) {
p_priv->out_buffer[i] = kzalloc(OUT_BUFLEN, GFP_KERNEL); p_priv->out_buffer[i] = kzalloc(OUT_BUFLEN, GFP_KERNEL);
if (!p_priv->out_buffer[i]) if (!p_priv->out_buffer[i])
goto err_out_buffer; goto err_free_out_buffer;
} }
p_priv->inack_buffer = kzalloc(INACK_BUFLEN, GFP_KERNEL); p_priv->inack_buffer = kzalloc(INACK_BUFLEN, GFP_KERNEL);
if (!p_priv->inack_buffer) if (!p_priv->inack_buffer)
goto err_inack_buffer; goto err_free_out_buffer;
p_priv->outcont_buffer = kzalloc(OUTCONT_BUFLEN, GFP_KERNEL); p_priv->outcont_buffer = kzalloc(OUTCONT_BUFLEN, GFP_KERNEL);
if (!p_priv->outcont_buffer) if (!p_priv->outcont_buffer)
goto err_outcont_buffer; goto err_free_inack_buffer;
p_priv->device_details = d_details; p_priv->device_details = d_details;
@ -2951,15 +2951,14 @@ static int keyspan_port_probe(struct usb_serial_port *port)
return 0; return 0;
err_outcont_buffer: err_free_inack_buffer:
kfree(p_priv->inack_buffer); kfree(p_priv->inack_buffer);
err_inack_buffer: err_free_out_buffer:
for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i)
kfree(p_priv->out_buffer[i]); kfree(p_priv->out_buffer[i]);
err_out_buffer: err_free_in_buffer:
for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i)
kfree(p_priv->in_buffer[i]); kfree(p_priv->in_buffer[i]);
err_in_buffer:
kfree(p_priv); kfree(p_priv);
return -ENOMEM; return -ENOMEM;

View File

@ -77,36 +77,27 @@ static int keyspan_pda_get_write_room(struct keyspan_pda_private *priv)
{ {
struct usb_serial_port *port = priv->port; struct usb_serial_port *port = priv->port;
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
u8 *room; u8 room;
int rc; int rc;
room = kmalloc(1, GFP_KERNEL); rc = usb_control_msg_recv(serial->dev,
if (!room) 0,
return -ENOMEM; 6, /* write_room */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_IN,
rc = usb_control_msg(serial->dev, 0, /* value: 0 means "remaining room" */
usb_rcvctrlpipe(serial->dev, 0), 0, /* index */
6, /* write_room */ &room,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE 1,
| USB_DIR_IN, 2000,
0, /* value: 0 means "remaining room" */ GFP_KERNEL);
0, /* index */ if (rc) {
room,
1,
2000);
if (rc != 1) {
if (rc >= 0)
rc = -EIO;
dev_dbg(&port->dev, "roomquery failed: %d\n", rc); dev_dbg(&port->dev, "roomquery failed: %d\n", rc);
goto out_free; return rc;
} }
dev_dbg(&port->dev, "roomquery says %d\n", *room); dev_dbg(&port->dev, "roomquery says %d\n", room);
rc = *room;
out_free:
kfree(room);
return rc; return room;
} }
static void keyspan_pda_request_unthrottle(struct work_struct *work) static void keyspan_pda_request_unthrottle(struct work_struct *work)
@ -381,22 +372,20 @@ static int keyspan_pda_get_modem_info(struct usb_serial *serial,
unsigned char *value) unsigned char *value)
{ {
int rc; int rc;
u8 *data; u8 data;
data = kmalloc(1, GFP_KERNEL); rc = usb_control_msg_recv(serial->dev, 0,
if (!data) 3, /* get pins */
return -ENOMEM; USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_IN,
0,
0,
&data,
1,
2000,
GFP_KERNEL);
if (rc == 0)
*value = data;
rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
3, /* get pins */
USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
0, 0, data, 1, 2000);
if (rc == 1)
*value = *data;
else if (rc >= 0)
rc = -EIO;
kfree(data);
return rc; return rc;
} }

View File

@ -124,16 +124,18 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port,
{ {
int rc; int rc;
rc = usb_control_msg(port->serial->dev, rc = usb_control_msg_send(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0), 0,
KL5KUSB105A_SIO_SET_DATA, KL5KUSB105A_SIO_SET_DATA,
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE, USB_TYPE_VENDOR | USB_DIR_OUT |
0, /* value */ USB_RECIP_INTERFACE,
0, /* index */ 0, /* value */
settings, 0, /* index */
sizeof(struct klsi_105_port_settings), settings,
KLSI_TIMEOUT); sizeof(struct klsi_105_port_settings),
if (rc < 0) KLSI_TIMEOUT,
GFP_KERNEL);
if (rc)
dev_err(&port->dev, dev_err(&port->dev,
"Change port settings failed (error = %d)\n", rc); "Change port settings failed (error = %d)\n", rc);
@ -145,61 +147,37 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port,
return rc; return rc;
} }
/* translate a 16-bit status value from the device to linux's TIO bits */
static unsigned long klsi_105_status2linestate(const __u16 status)
{
unsigned long res = 0;
res = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
| ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
;
return res;
}
/* /*
* Read line control via vendor command and return result through * Read line control via vendor command and return result through
* *line_state_p * the state pointer.
*/ */
/* It seems that the status buffer has always only 2 bytes length */
#define KLSI_STATUSBUF_LEN 2
static int klsi_105_get_line_state(struct usb_serial_port *port, static int klsi_105_get_line_state(struct usb_serial_port *port,
unsigned long *line_state_p) unsigned long *state)
{ {
u16 status;
int rc; int rc;
u8 *status_buf;
__u16 status;
status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL); rc = usb_control_msg_recv(port->serial->dev, 0,
if (!status_buf) KL5KUSB105A_SIO_POLL,
return -ENOMEM; USB_TYPE_VENDOR | USB_DIR_IN,
0, /* value */
status_buf[0] = 0xff; 0, /* index */
status_buf[1] = 0xff; &status, sizeof(status),
rc = usb_control_msg(port->serial->dev, 10000,
usb_rcvctrlpipe(port->serial->dev, 0), GFP_KERNEL);
KL5KUSB105A_SIO_POLL, if (rc) {
USB_TYPE_VENDOR | USB_DIR_IN,
0, /* value */
0, /* index */
status_buf, KLSI_STATUSBUF_LEN,
10000
);
if (rc != KLSI_STATUSBUF_LEN) {
dev_err(&port->dev, "reading line status failed: %d\n", rc); dev_err(&port->dev, "reading line status failed: %d\n", rc);
if (rc >= 0) return rc;
rc = -EIO;
} else {
status = get_unaligned_le16(status_buf);
dev_dbg(&port->dev, "read status %02x %02x\n",
status_buf[0], status_buf[1]);
*line_state_p = klsi_105_status2linestate(status);
} }
kfree(status_buf); le16_to_cpus(&status);
return rc;
dev_dbg(&port->dev, "read status %04x\n", status);
*state = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0) |
((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0);
return 0;
} }
@ -245,7 +223,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
int retval = 0; int retval = 0;
int rc; int rc;
unsigned long line_state; unsigned long line_state;
struct klsi_105_port_settings *cfg; struct klsi_105_port_settings cfg;
unsigned long flags; unsigned long flags;
/* Do a defined restart: /* Do a defined restart:
@ -255,27 +233,22 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
* Then read the modem line control and store values in * Then read the modem line control and store values in
* priv->line_state. * priv->line_state.
*/ */
cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
cfg->pktlen = 5; cfg.pktlen = 5;
cfg->baudrate = kl5kusb105a_sio_b9600; cfg.baudrate = kl5kusb105a_sio_b9600;
cfg->databits = kl5kusb105a_dtb_8; cfg.databits = kl5kusb105a_dtb_8;
cfg->unknown1 = 0; cfg.unknown1 = 0;
cfg->unknown2 = 1; cfg.unknown2 = 1;
klsi_105_chg_port_settings(port, cfg); klsi_105_chg_port_settings(port, &cfg);
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
priv->cfg.pktlen = cfg->pktlen; priv->cfg.pktlen = cfg.pktlen;
priv->cfg.baudrate = cfg->baudrate; priv->cfg.baudrate = cfg.baudrate;
priv->cfg.databits = cfg->databits; priv->cfg.databits = cfg.databits;
priv->cfg.unknown1 = cfg->unknown1; priv->cfg.unknown1 = cfg.unknown1;
priv->cfg.unknown2 = cfg->unknown2; priv->cfg.unknown2 = cfg.unknown2;
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
kfree(cfg);
/* READ_ON and urb submission */ /* READ_ON and urb submission */
rc = usb_serial_generic_open(tty, port); rc = usb_serial_generic_open(tty, port);
if (rc) if (rc)

View File

@ -208,8 +208,8 @@ void usb_serial_put(struct usb_serial *serial)
* *
* This is the first place a new tty gets used. Hence this is where we * This is the first place a new tty gets used. Hence this is where we
* acquire references to the usb_serial structure and the driver module, * acquire references to the usb_serial structure and the driver module,
* where we store a pointer to the port, and where we do an autoresume. * where we store a pointer to the port. All these actions are reversed
* All these actions are reversed in serial_cleanup(). * in serial_cleanup().
*/ */
static int serial_install(struct tty_driver *driver, struct tty_struct *tty) static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
{ {
@ -225,17 +225,13 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
serial = port->serial; serial = port->serial;
if (!try_module_get(serial->type->driver.owner)) if (!try_module_get(serial->type->driver.owner))
goto error_module_get; goto err_put_serial;
retval = usb_autopm_get_interface(serial->interface);
if (retval)
goto error_get_interface;
init_termios = (driver->termios[idx] == NULL); init_termios = (driver->termios[idx] == NULL);
retval = tty_standard_install(driver, tty); retval = tty_standard_install(driver, tty);
if (retval) if (retval)
goto error_init_termios; goto err_put_module;
mutex_unlock(&serial->disc_mutex); mutex_unlock(&serial->disc_mutex);
@ -247,11 +243,9 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
return retval; return retval;
error_init_termios: err_put_module:
usb_autopm_put_interface(serial->interface);
error_get_interface:
module_put(serial->type->driver.owner); module_put(serial->type->driver.owner);
error_module_get: err_put_serial:
usb_serial_put(serial); usb_serial_put(serial);
mutex_unlock(&serial->disc_mutex); mutex_unlock(&serial->disc_mutex);
return retval; return retval;
@ -265,10 +259,19 @@ static int serial_port_activate(struct tty_port *tport, struct tty_struct *tty)
int retval; int retval;
mutex_lock(&serial->disc_mutex); mutex_lock(&serial->disc_mutex);
if (serial->disconnected) if (serial->disconnected) {
retval = -ENODEV; retval = -ENODEV;
else goto out_unlock;
retval = port->serial->type->open(tty, port); }
retval = usb_autopm_get_interface(serial->interface);
if (retval)
goto out_unlock;
retval = port->serial->type->open(tty, port);
if (retval)
usb_autopm_put_interface(serial->interface);
out_unlock:
mutex_unlock(&serial->disc_mutex); mutex_unlock(&serial->disc_mutex);
if (retval < 0) if (retval < 0)
@ -304,6 +307,8 @@ static void serial_port_shutdown(struct tty_port *tport)
if (drv->close) if (drv->close)
drv->close(port); drv->close(port);
usb_autopm_put_interface(port->serial->interface);
} }
static void serial_hangup(struct tty_struct *tty) static void serial_hangup(struct tty_struct *tty)
@ -352,8 +357,6 @@ static void serial_cleanup(struct tty_struct *tty)
serial = port->serial; serial = port->serial;
owner = serial->type->driver.owner; owner = serial->type->driver.owner;
usb_autopm_put_interface(serial->interface);
usb_serial_put(serial); usb_serial_put(serial);
module_put(owner); module_put(owner);
} }
@ -1328,7 +1331,7 @@ static int __init usb_serial_init(void)
result = bus_register(&usb_serial_bus_type); result = bus_register(&usb_serial_bus_type);
if (result) { if (result) {
pr_err("%s - registering bus driver failed\n", __func__); pr_err("%s - registering bus driver failed\n", __func__);
goto exit_bus; goto err_put_driver;
} }
usb_serial_tty_driver->driver_name = "usbserial"; usb_serial_tty_driver->driver_name = "usbserial";
@ -1346,25 +1349,23 @@ static int __init usb_serial_init(void)
result = tty_register_driver(usb_serial_tty_driver); result = tty_register_driver(usb_serial_tty_driver);
if (result) { if (result) {
pr_err("%s - tty_register_driver failed\n", __func__); pr_err("%s - tty_register_driver failed\n", __func__);
goto exit_reg_driver; goto err_unregister_bus;
} }
/* register the generic driver, if we should */ /* register the generic driver, if we should */
result = usb_serial_generic_register(); result = usb_serial_generic_register();
if (result < 0) { if (result < 0) {
pr_err("%s - registering generic driver failed\n", __func__); pr_err("%s - registering generic driver failed\n", __func__);
goto exit_generic; goto err_unregister_driver;
} }
return result; return result;
exit_generic: err_unregister_driver:
tty_unregister_driver(usb_serial_tty_driver); tty_unregister_driver(usb_serial_tty_driver);
err_unregister_bus:
exit_reg_driver:
bus_unregister(&usb_serial_bus_type); bus_unregister(&usb_serial_bus_type);
err_put_driver:
exit_bus:
pr_err("%s - returning with error %d\n", __func__, result); pr_err("%s - returning with error %d\n", __func__, result);
tty_driver_kref_put(usb_serial_tty_driver); tty_driver_kref_put(usb_serial_tty_driver);
return result; return result;
@ -1509,13 +1510,13 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[]
rc = usb_register(udriver); rc = usb_register(udriver);
if (rc) if (rc)
goto failed_usb_register; goto err_free_driver;
for (sd = serial_drivers; *sd; ++sd) { for (sd = serial_drivers; *sd; ++sd) {
(*sd)->usb_driver = udriver; (*sd)->usb_driver = udriver;
rc = usb_serial_register(*sd); rc = usb_serial_register(*sd);
if (rc) if (rc)
goto failed; goto err_deregister_drivers;
} }
/* Now set udriver's id_table and look for matches */ /* Now set udriver's id_table and look for matches */
@ -1523,11 +1524,11 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[]
rc = driver_attach(&udriver->drvwrap.driver); rc = driver_attach(&udriver->drvwrap.driver);
return 0; return 0;
failed: err_deregister_drivers:
while (sd-- > serial_drivers) while (sd-- > serial_drivers)
usb_serial_deregister(*sd); usb_serial_deregister(*sd);
usb_deregister(udriver); usb_deregister(udriver);
failed_usb_register: err_free_driver:
kfree(udriver); kfree(udriver);
return rc; return rc;
} }

View File

@ -406,6 +406,16 @@ UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110,
"785EPX Storage", "785EPX Storage",
USB_SC_SCSI, USB_PR_BULK, NULL, US_FL_SINGLE_LUN), USB_SC_SCSI, USB_PR_BULK, NULL, US_FL_SINGLE_LUN),
/*
* Reported by James Buren <braewoods+lkml@braewoods.net>
* Virtual ISOs cannot be remounted if ejected while the device is locked
* Disable locking to mimic Windows behavior that bypasses the issue
*/
UNUSUAL_DEV( 0x04c5, 0x2028, 0x0001, 0x0001,
"iODD",
"2531/2541",
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE),
/* /*
* Not sure who reported this originally but * Not sure who reported this originally but
* Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN * Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN

View File

@ -65,9 +65,9 @@ config TYPEC_HD3SS3220
config TYPEC_STUSB160X config TYPEC_STUSB160X
tristate "STMicroelectronics STUSB160x Type-C controller driver" tristate "STMicroelectronics STUSB160x Type-C controller driver"
depends on I2C
depends on REGMAP_I2C
depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
depends on I2C
select REGMAP_I2C
help help
Say Y or M here if your system has STMicroelectronics STUSB160x Say Y or M here if your system has STMicroelectronics STUSB160x
Type-C port controller. Type-C port controller.

View File

@ -125,11 +125,9 @@ static irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220)
int err; int err;
hd3ss3220_set_role(hd3ss3220); hd3ss3220_set_role(hd3ss3220);
err = regmap_update_bits_base(hd3ss3220->regmap, err = regmap_write_bits(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL,
HD3SS3220_REG_CN_STAT_CTRL, HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS, HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
NULL, false, true);
if (err < 0) if (err < 0)
return IRQ_NONE; return IRQ_NONE;

View File

@ -258,7 +258,7 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
* When port has drp toggling enabled, ROLE_CONTROL would only have the initial * When port has drp toggling enabled, ROLE_CONTROL would only have the initial
* terminations for the toggling and does not indicate the final cc * terminations for the toggling and does not indicate the final cc
* terminations when ConnectionResult is 0 i.e. drp toggling stops and * terminations when ConnectionResult is 0 i.e. drp toggling stops and
* the connection is resolbed. Infer port role from TCPC_CC_STATUS based on the * the connection is resolved. Infer port role from TCPC_CC_STATUS based on the
* terminations seen. The port role is then used to set the cc terminations. * terminations seen. The port role is then used to set the cc terminations.
*/ */
if (reg & TCPC_ROLE_CTRL_DRP) { if (reg & TCPC_ROLE_CTRL_DRP) {

View File

@ -9,6 +9,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
@ -29,6 +30,7 @@
#define TPS_REG_INT_MASK2 0x17 #define TPS_REG_INT_MASK2 0x17
#define TPS_REG_INT_CLEAR1 0x18 #define TPS_REG_INT_CLEAR1 0x18
#define TPS_REG_INT_CLEAR2 0x19 #define TPS_REG_INT_CLEAR2 0x19
#define TPS_REG_SYSTEM_POWER_STATE 0x20
#define TPS_REG_STATUS 0x1a #define TPS_REG_STATUS 0x1a
#define TPS_REG_SYSTEM_CONF 0x28 #define TPS_REG_SYSTEM_CONF 0x28
#define TPS_REG_CTRL_CONF 0x29 #define TPS_REG_CTRL_CONF 0x29
@ -117,13 +119,13 @@ tps6598x_block_read(struct tps6598x *tps, u8 reg, void *val, size_t len)
u8 data[TPS_MAX_LEN + 1]; u8 data[TPS_MAX_LEN + 1];
int ret; int ret;
if (WARN_ON(len + 1 > sizeof(data))) if (len + 1 > sizeof(data))
return -EINVAL; return -EINVAL;
if (!tps->i2c_protocol) if (!tps->i2c_protocol)
return regmap_raw_read(tps->regmap, reg, val, len); return regmap_raw_read(tps->regmap, reg, val, len);
ret = regmap_raw_read(tps->regmap, reg, data, sizeof(data)); ret = regmap_raw_read(tps->regmap, reg, data, len + 1);
if (ret) if (ret)
return ret; return ret;
@ -139,13 +141,21 @@ static int tps6598x_block_write(struct tps6598x *tps, u8 reg,
{ {
u8 data[TPS_MAX_LEN + 1]; u8 data[TPS_MAX_LEN + 1];
if (len + 1 > sizeof(data))
return -EINVAL;
if (!tps->i2c_protocol) if (!tps->i2c_protocol)
return regmap_raw_write(tps->regmap, reg, val, len); return regmap_raw_write(tps->regmap, reg, val, len);
data[0] = len; data[0] = len;
memcpy(&data[1], val, len); memcpy(&data[1], val, len);
return regmap_raw_write(tps->regmap, reg, data, sizeof(data)); return regmap_raw_write(tps->regmap, reg, data, len + 1);
}
static inline int tps6598x_read8(struct tps6598x *tps, u8 reg, u8 *val)
{
return tps6598x_block_read(tps, reg, val, sizeof(u8));
} }
static inline int tps6598x_read16(struct tps6598x *tps, u8 reg, u16 *val) static inline int tps6598x_read16(struct tps6598x *tps, u8 reg, u16 *val)
@ -401,13 +411,114 @@ static const struct typec_operations tps6598x_ops = {
.pr_set = tps6598x_pr_set, .pr_set = tps6598x_pr_set,
}; };
static bool tps6598x_read_status(struct tps6598x *tps, u32 *status)
{
int ret;
ret = tps6598x_read32(tps, TPS_REG_STATUS, status);
if (ret) {
dev_err(tps->dev, "%s: failed to read status\n", __func__);
return false;
}
trace_tps6598x_status(*status);
return true;
}
static bool tps6598x_read_data_status(struct tps6598x *tps)
{
u32 data_status;
int ret;
ret = tps6598x_read32(tps, TPS_REG_DATA_STATUS, &data_status);
if (ret < 0) {
dev_err(tps->dev, "failed to read data status: %d\n", ret);
return false;
}
trace_tps6598x_data_status(data_status);
return true;
}
static bool tps6598x_read_power_status(struct tps6598x *tps)
{
u16 pwr_status;
int ret;
ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status);
if (ret < 0) {
dev_err(tps->dev, "failed to read power status: %d\n", ret);
return false;
}
trace_tps6598x_power_status(pwr_status);
return true;
}
static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status)
{
int ret;
if (status & TPS_STATUS_PLUG_PRESENT) {
ret = tps6598x_connect(tps, status);
if (ret)
dev_err(tps->dev, "failed to register partner\n");
} else {
tps6598x_disconnect(tps, status);
}
}
static irqreturn_t cd321x_interrupt(int irq, void *data)
{
struct tps6598x *tps = data;
u64 event;
u32 status;
int ret;
mutex_lock(&tps->lock);
ret = tps6598x_read64(tps, TPS_REG_INT_EVENT1, &event);
if (ret) {
dev_err(tps->dev, "%s: failed to read events\n", __func__);
goto err_unlock;
}
trace_cd321x_irq(event);
if (!event)
goto err_unlock;
if (!tps6598x_read_status(tps, &status))
goto err_clear_ints;
if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE)
if (!tps6598x_read_power_status(tps))
goto err_clear_ints;
if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE)
if (!tps6598x_read_data_status(tps))
goto err_clear_ints;
/* Handle plug insert or removal */
if (event & APPLE_CD_REG_INT_PLUG_EVENT)
tps6598x_handle_plug_event(tps, status);
err_clear_ints:
tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event);
err_unlock:
mutex_unlock(&tps->lock);
if (event)
return IRQ_HANDLED;
return IRQ_NONE;
}
static irqreturn_t tps6598x_interrupt(int irq, void *data) static irqreturn_t tps6598x_interrupt(int irq, void *data)
{ {
struct tps6598x *tps = data; struct tps6598x *tps = data;
u64 event1; u64 event1;
u64 event2; u64 event2;
u32 status, data_status; u32 status;
u16 pwr_status;
int ret; int ret;
mutex_lock(&tps->lock); mutex_lock(&tps->lock);
@ -420,42 +531,23 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data)
} }
trace_tps6598x_irq(event1, event2); trace_tps6598x_irq(event1, event2);
ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); if (!(event1 | event2))
if (ret) { goto err_unlock;
dev_err(tps->dev, "%s: failed to read status\n", __func__);
if (!tps6598x_read_status(tps, &status))
goto err_clear_ints; goto err_clear_ints;
}
trace_tps6598x_status(status);
if ((event1 | event2) & TPS_REG_INT_POWER_STATUS_UPDATE) { if ((event1 | event2) & TPS_REG_INT_POWER_STATUS_UPDATE)
ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); if (!tps6598x_read_power_status(tps))
if (ret < 0) {
dev_err(tps->dev, "failed to read power status: %d\n", ret);
goto err_clear_ints; goto err_clear_ints;
}
trace_tps6598x_power_status(pwr_status);
}
if ((event1 | event2) & TPS_REG_INT_DATA_STATUS_UPDATE) { if ((event1 | event2) & TPS_REG_INT_DATA_STATUS_UPDATE)
ret = tps6598x_read32(tps, TPS_REG_DATA_STATUS, &data_status); if (!tps6598x_read_data_status(tps))
if (ret < 0) {
dev_err(tps->dev, "failed to read data status: %d\n", ret);
goto err_clear_ints; goto err_clear_ints;
}
trace_tps6598x_data_status(data_status);
}
/* Handle plug insert or removal */ /* Handle plug insert or removal */
if ((event1 | event2) & TPS_REG_INT_PLUG_EVENT) { if ((event1 | event2) & TPS_REG_INT_PLUG_EVENT)
if (status & TPS_STATUS_PLUG_PRESENT) { tps6598x_handle_plug_event(tps, status);
ret = tps6598x_connect(tps, status);
if (ret)
dev_err(tps->dev,
"failed to register partner\n");
} else {
tps6598x_disconnect(tps, status);
}
}
err_clear_ints: err_clear_ints:
tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event1); tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event1);
@ -464,7 +556,9 @@ err_clear_ints:
err_unlock: err_unlock:
mutex_unlock(&tps->lock); mutex_unlock(&tps->lock);
return IRQ_HANDLED; if (event1 | event2)
return IRQ_HANDLED;
return IRQ_NONE;
} }
static int tps6598x_check_mode(struct tps6598x *tps) static int tps6598x_check_mode(struct tps6598x *tps)
@ -547,6 +641,32 @@ static int tps6598x_psy_get_prop(struct power_supply *psy,
return ret; return ret;
} }
static int cd321x_switch_power_state(struct tps6598x *tps, u8 target_state)
{
u8 state;
int ret;
ret = tps6598x_read8(tps, TPS_REG_SYSTEM_POWER_STATE, &state);
if (ret)
return ret;
if (state == target_state)
return 0;
ret = tps6598x_exec_cmd(tps, "SPSS", sizeof(u8), &target_state, 0, NULL);
if (ret)
return ret;
ret = tps6598x_read8(tps, TPS_REG_SYSTEM_POWER_STATE, &state);
if (ret)
return ret;
if (state != target_state)
return -EINVAL;
return 0;
}
static int devm_tps6598_psy_register(struct tps6598x *tps) static int devm_tps6598_psy_register(struct tps6598x *tps)
{ {
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
@ -578,6 +698,8 @@ static int devm_tps6598_psy_register(struct tps6598x *tps)
static int tps6598x_probe(struct i2c_client *client) static int tps6598x_probe(struct i2c_client *client)
{ {
irq_handler_t irq_handler = tps6598x_interrupt;
struct device_node *np = client->dev.of_node;
struct typec_capability typec_cap = { }; struct typec_capability typec_cap = { };
struct tps6598x *tps; struct tps6598x *tps;
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
@ -604,9 +726,6 @@ static int tps6598x_probe(struct i2c_client *client)
/* /*
* Checking can the adapter handle SMBus protocol. If it can not, the * Checking can the adapter handle SMBus protocol. If it can not, the
* driver needs to take care of block reads separately. * driver needs to take care of block reads separately.
*
* FIXME: Testing with I2C_FUNC_I2C. regmap-i2c uses I2C protocol
* unconditionally if the adapter has I2C_FUNC_I2C set.
*/ */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
tps->i2c_protocol = true; tps->i2c_protocol = true;
@ -616,6 +735,31 @@ static int tps6598x_probe(struct i2c_client *client)
if (ret) if (ret)
return ret; return ret;
if (np && of_device_is_compatible(np, "apple,cd321x")) {
/* Switch CD321X chips to the correct system power state */
ret = cd321x_switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0);
if (ret)
return ret;
/* CD321X chips have all interrupts masked initially */
ret = tps6598x_write64(tps, TPS_REG_INT_MASK1,
APPLE_CD_REG_INT_POWER_STATUS_UPDATE |
APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
APPLE_CD_REG_INT_PLUG_EVENT);
if (ret)
return ret;
irq_handler = cd321x_interrupt;
} else {
/* Enable power status, data status and plug event interrupts */
ret = tps6598x_write64(tps, TPS_REG_INT_MASK1,
TPS_REG_INT_POWER_STATUS_UPDATE |
TPS_REG_INT_DATA_STATUS_UPDATE |
TPS_REG_INT_PLUG_EVENT);
if (ret)
return ret;
}
ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); ret = tps6598x_read32(tps, TPS_REG_STATUS, &status);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -695,7 +839,7 @@ static int tps6598x_probe(struct i2c_client *client)
} }
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
tps6598x_interrupt, irq_handler,
IRQF_SHARED | IRQF_ONESHOT, IRQF_SHARED | IRQF_ONESHOT,
dev_name(&client->dev), tps); dev_name(&client->dev), tps);
if (ret) { if (ret) {
@ -729,6 +873,7 @@ static int tps6598x_remove(struct i2c_client *client)
static const struct of_device_id tps6598x_of_match[] = { static const struct of_device_id tps6598x_of_match[] = {
{ .compatible = "ti,tps6598x", }, { .compatible = "ti,tps6598x", },
{ .compatible = "apple,cd321x", },
{} {}
}; };
MODULE_DEVICE_TABLE(of, tps6598x_of_match); MODULE_DEVICE_TABLE(of, tps6598x_of_match);

View File

@ -129,6 +129,18 @@
#define TPS_REG_INT_HARD_RESET BIT(1) #define TPS_REG_INT_HARD_RESET BIT(1)
#define TPS_REG_INT_PD_SOFT_RESET BIT(0) #define TPS_REG_INT_PD_SOFT_RESET BIT(0)
/* Apple-specific TPS_REG_INT_* bits */
#define APPLE_CD_REG_INT_DATA_STATUS_UPDATE BIT(10)
#define APPLE_CD_REG_INT_POWER_STATUS_UPDATE BIT(9)
#define APPLE_CD_REG_INT_STATUS_UPDATE BIT(8)
#define APPLE_CD_REG_INT_PLUG_EVENT BIT(1)
/* TPS_REG_SYSTEM_POWER_STATE states */
#define TPS_SYSTEM_POWER_STATE_S0 0x00
#define TPS_SYSTEM_POWER_STATE_S3 0x03
#define TPS_SYSTEM_POWER_STATE_S4 0x04
#define TPS_SYSTEM_POWER_STATE_S5 0x05
/* TPS_REG_POWER_STATUS bits */ /* TPS_REG_POWER_STATUS bits */
#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x)) #define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x))
#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x)) #define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x))

View File

@ -67,6 +67,13 @@
{ TPS_REG_INT_USER_VID_ALT_MODE_ATTN_VDM, "USER_VID_ALT_MODE_ATTN_VDM" }, \ { TPS_REG_INT_USER_VID_ALT_MODE_ATTN_VDM, "USER_VID_ALT_MODE_ATTN_VDM" }, \
{ TPS_REG_INT_USER_VID_ALT_MODE_OTHER_VDM, "USER_VID_ALT_MODE_OTHER_VDM" }) { TPS_REG_INT_USER_VID_ALT_MODE_OTHER_VDM, "USER_VID_ALT_MODE_OTHER_VDM" })
#define show_cd321x_irq_flags(flags) \
__print_flags_u64(flags, "|", \
{ APPLE_CD_REG_INT_PLUG_EVENT, "PLUG_EVENT" }, \
{ APPLE_CD_REG_INT_POWER_STATUS_UPDATE, "POWER_STATUS_UPDATE" }, \
{ APPLE_CD_REG_INT_DATA_STATUS_UPDATE, "DATA_STATUS_UPDATE" }, \
{ APPLE_CD_REG_INT_STATUS_UPDATE, "STATUS_UPDATE" })
#define TPS6598X_STATUS_FLAGS_MASK (GENMASK(31, 0) ^ (TPS_STATUS_CONN_STATE_MASK | \ #define TPS6598X_STATUS_FLAGS_MASK (GENMASK(31, 0) ^ (TPS_STATUS_CONN_STATE_MASK | \
TPS_STATUS_PP_5V0_SWITCH_MASK | \ TPS_STATUS_PP_5V0_SWITCH_MASK | \
TPS_STATUS_PP_HV_SWITCH_MASK | \ TPS_STATUS_PP_HV_SWITCH_MASK | \
@ -207,6 +214,22 @@ TRACE_EVENT(tps6598x_irq,
show_irq_flags(__entry->event2)) show_irq_flags(__entry->event2))
); );
TRACE_EVENT(cd321x_irq,
TP_PROTO(u64 event),
TP_ARGS(event),
TP_STRUCT__entry(
__field(u64, event)
),
TP_fast_assign(
__entry->event = event;
),
TP_printk("event=%s",
show_cd321x_irq_flags(__entry->event))
);
TRACE_EVENT(tps6598x_status, TRACE_EVENT(tps6598x_status,
TP_PROTO(u32 status), TP_PROTO(u32 status),
TP_ARGS(status), TP_ARGS(status),

View File

@ -128,8 +128,10 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
if (ret) if (ret)
return ret; return ret;
if (cci & UCSI_CCI_BUSY) if (cci & UCSI_CCI_BUSY) {
ucsi->ops->async_write(ucsi, UCSI_CANCEL, NULL, 0);
return -EBUSY; return -EBUSY;
}
if (!(cci & UCSI_CCI_COMMAND_COMPLETE)) if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
return -EIO; return -EIO;
@ -189,6 +191,64 @@ int ucsi_resume(struct ucsi *ucsi)
EXPORT_SYMBOL_GPL(ucsi_resume); EXPORT_SYMBOL_GPL(ucsi_resume);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
struct ucsi_work {
struct delayed_work work;
unsigned long delay;
unsigned int count;
struct ucsi_connector *con;
int (*cb)(struct ucsi_connector *);
};
static void ucsi_poll_worker(struct work_struct *work)
{
struct ucsi_work *uwork = container_of(work, struct ucsi_work, work.work);
struct ucsi_connector *con = uwork->con;
int ret;
mutex_lock(&con->lock);
if (!con->partner) {
mutex_unlock(&con->lock);
kfree(uwork);
return;
}
ret = uwork->cb(con);
if (uwork->count-- && (ret == -EBUSY || ret == -ETIMEDOUT))
queue_delayed_work(con->wq, &uwork->work, uwork->delay);
else
kfree(uwork);
mutex_unlock(&con->lock);
}
static int ucsi_partner_task(struct ucsi_connector *con,
int (*cb)(struct ucsi_connector *),
int retries, unsigned long delay)
{
struct ucsi_work *uwork;
if (!con->partner)
return 0;
uwork = kzalloc(sizeof(*uwork), GFP_KERNEL);
if (!uwork)
return -ENOMEM;
INIT_DELAYED_WORK(&uwork->work, ucsi_poll_worker);
uwork->count = retries;
uwork->delay = delay;
uwork->con = con;
uwork->cb = cb;
queue_delayed_work(con->wq, &uwork->work, delay);
return 0;
}
/* -------------------------------------------------------------------------- */
void ucsi_altmode_update_active(struct ucsi_connector *con) void ucsi_altmode_update_active(struct ucsi_connector *con)
{ {
const struct typec_altmode *altmode = NULL; const struct typec_altmode *altmode = NULL;
@ -435,6 +495,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
command |= UCSI_GET_ALTMODE_OFFSET(i); command |= UCSI_GET_ALTMODE_OFFSET(i);
len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt)); len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt));
if (len == -EBUSY)
continue;
if (len <= 0) if (len <= 0)
return len; return len;
@ -509,7 +571,7 @@ static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
command |= UCSI_GET_PDOS_SRC_PDOS; command |= UCSI_GET_PDOS_SRC_PDOS;
ret = ucsi_send_command(ucsi, command, pdos + offset, ret = ucsi_send_command(ucsi, command, pdos + offset,
num_pdos * sizeof(u32)); num_pdos * sizeof(u32));
if (ret < 0) if (ret < 0 && ret != -ETIMEDOUT)
dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret); dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
if (ret == 0 && offset == 0) if (ret == 0 && offset == 0)
dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n"); dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n");
@ -517,26 +579,49 @@ static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
return ret; return ret;
} }
static void ucsi_get_src_pdos(struct ucsi_connector *con, int is_partner) static int ucsi_get_src_pdos(struct ucsi_connector *con)
{ {
int ret; int ret;
/* UCSI max payload means only getting at most 4 PDOs at a time */ /* UCSI max payload means only getting at most 4 PDOs at a time */
ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS); ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS);
if (ret < 0) if (ret < 0)
return; return ret;
con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */ con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
if (con->num_pdos < UCSI_MAX_PDOS) if (con->num_pdos < UCSI_MAX_PDOS)
return; return 0;
/* get the remaining PDOs, if any */ /* get the remaining PDOs, if any */
ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS, ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS,
PDO_MAX_OBJECTS - UCSI_MAX_PDOS); PDO_MAX_OBJECTS - UCSI_MAX_PDOS);
if (ret < 0) if (ret < 0)
return; return ret;
con->num_pdos += ret / sizeof(u32); con->num_pdos += ret / sizeof(u32);
ucsi_port_psy_changed(con);
return 0;
}
static int ucsi_check_altmodes(struct ucsi_connector *con)
{
int ret;
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
if (ret && ret != -ETIMEDOUT)
dev_err(con->ucsi->dev,
"con%d: failed to register partner alt modes (%d)\n",
con->num, ret);
/* Ignoring the errors in this case. */
if (con->partner_altmode[0]) {
ucsi_altmode_update_active(con);
return 0;
}
return ret;
} }
static void ucsi_pwr_opmode_change(struct ucsi_connector *con) static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
@ -545,7 +630,8 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
case UCSI_CONSTAT_PWR_OPMODE_PD: case UCSI_CONSTAT_PWR_OPMODE_PD:
con->rdo = con->status.request_data_obj; con->rdo = con->status.request_data_obj;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
ucsi_get_src_pdos(con, 1); ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0);
ucsi_partner_task(con, ucsi_check_altmodes, 30, 0);
break; break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
con->rdo = 0; con->rdo = 0;
@ -614,9 +700,6 @@ static void ucsi_partner_change(struct ucsi_connector *con)
enum usb_role u_role = USB_ROLE_NONE; enum usb_role u_role = USB_ROLE_NONE;
int ret; int ret;
if (!con->partner)
return;
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_UFP:
case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
@ -633,10 +716,6 @@ static void ucsi_partner_change(struct ucsi_connector *con)
break; break;
} }
/* Complete pending data role swap */
if (!completion_done(&con->complete))
complete(&con->complete);
/* Only notify USB controller if partner supports USB data */ /* Only notify USB controller if partner supports USB data */
if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB))
u_role = USB_ROLE_NONE; u_role = USB_ROLE_NONE;
@ -645,15 +724,31 @@ static void ucsi_partner_change(struct ucsi_connector *con)
if (ret) if (ret)
dev_err(con->ucsi->dev, "con:%d: failed to set usb role:%d\n", dev_err(con->ucsi->dev, "con:%d: failed to set usb role:%d\n",
con->num, u_role); con->num, u_role);
}
/* Can't rely on Partner Flags field. Always checking the alt modes. */ static int ucsi_check_connection(struct ucsi_connector *con)
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); {
if (ret) u64 command;
dev_err(con->ucsi->dev, int ret;
"con%d: failed to register partner alternate modes\n",
con->num); command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
else ret = ucsi_send_command(con->ucsi, command, &con->status, sizeof(con->status));
ucsi_altmode_update_active(con); if (ret < 0) {
dev_err(con->ucsi->dev, "GET_CONNECTOR_STATUS failed (%d)\n", ret);
return ret;
}
if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
UCSI_CONSTAT_PWR_OPMODE_PD)
ucsi_partner_task(con, ucsi_check_altmodes, 30, 0);
} else {
ucsi_partner_change(con);
ucsi_port_psy_changed(con);
ucsi_unregister_partner(con);
}
return 0;
} }
static void ucsi_handle_connector_change(struct work_struct *work) static void ucsi_handle_connector_change(struct work_struct *work)
@ -661,122 +756,24 @@ static void ucsi_handle_connector_change(struct work_struct *work)
struct ucsi_connector *con = container_of(work, struct ucsi_connector, struct ucsi_connector *con = container_of(work, struct ucsi_connector,
work); work);
struct ucsi *ucsi = con->ucsi; struct ucsi *ucsi = con->ucsi;
struct ucsi_connector_status pre_ack_status;
struct ucsi_connector_status post_ack_status;
enum typec_role role; enum typec_role role;
enum usb_role u_role = USB_ROLE_NONE;
u16 inferred_changes;
u16 changed_flags;
u64 command; u64 command;
int ret; int ret;
mutex_lock(&con->lock); mutex_lock(&con->lock);
/*
* Some/many PPMs have an issue where all fields in the change bitfield
* are cleared when an ACK is send. This will causes any change
* between GET_CONNECTOR_STATUS and ACK to be lost.
*
* We work around this by re-fetching the connector status afterwards.
* We then infer any changes that we see have happened but that may not
* be represented in the change bitfield.
*
* Also, even though we don't need to know the currently supported alt
* modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does
* not get stuck in case it assumes we do.
* Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be
* set in the change bitfield.
*
* We end up with the following actions:
* 1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes
* 2. UCSI_GET_CAM_SUPPORTED, discard result
* 3. ACK connector change
* 4. UCSI_GET_CONNECTOR_STATUS, store result
* 5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results
* 6. If PPM reported a new change, then restart in order to ACK
* 7. Process everything as usual.
*
* We may end up seeing a change twice, but we can only miss extremely
* short transitional changes.
*/
/* 1. First UCSI_GET_CONNECTOR_STATUS */
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
ret = ucsi_send_command(ucsi, command, &pre_ack_status, ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status));
sizeof(pre_ack_status));
if (ret < 0) {
dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
__func__, ret);
goto out_unlock;
}
con->unprocessed_changes |= pre_ack_status.change;
/* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */
command = UCSI_GET_CAM_SUPPORTED;
command |= UCSI_CONNECTOR_NUMBER(con->num);
ucsi_send_command(con->ucsi, command, NULL, 0);
/* 3. ACK connector change */
ret = ucsi_acknowledge_connector_change(ucsi);
clear_bit(EVENT_PENDING, &ucsi->flags);
if (ret) {
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
goto out_unlock;
}
/* 4. Second UCSI_GET_CONNECTOR_STATUS */
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
ret = ucsi_send_command(ucsi, command, &post_ack_status,
sizeof(post_ack_status));
if (ret < 0) { if (ret < 0) {
dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
__func__, ret); __func__, ret);
goto out_unlock; goto out_unlock;
} }
/* 5. Inferre any missing changes */ trace_ucsi_connector_change(con->num, &con->status);
changed_flags = pre_ack_status.flags ^ post_ack_status.flags;
inferred_changes = 0;
if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0)
inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE;
if (changed_flags & UCSI_CONSTAT_CONNECTED)
inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE;
if (changed_flags & UCSI_CONSTAT_PWR_DIR)
inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE;
if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0)
inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0)
inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
/* Mask out anything that was correctly notified in the later call. */
inferred_changes &= ~post_ack_status.change;
if (inferred_changes)
dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n",
__func__, inferred_changes);
con->unprocessed_changes |= inferred_changes;
/* 6. If PPM reported a new change, then restart in order to ACK */
if (post_ack_status.change)
goto out_unlock;
/* 7. Continue as if nothing happened */
con->status = post_ack_status;
con->status.change = con->unprocessed_changes;
con->unprocessed_changes = 0;
role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE) {
ucsi_pwr_opmode_change(con);
ucsi_port_psy_changed(con);
}
if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
typec_set_pwr_role(con->port, role); typec_set_pwr_role(con->port, role);
@ -787,54 +784,39 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) {
typec_set_pwr_role(con->port, role); typec_set_pwr_role(con->port, role);
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
u_role = USB_ROLE_HOST;
fallthrough;
case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
typec_set_data_role(con->port, TYPEC_HOST);
break;
case UCSI_CONSTAT_PARTNER_TYPE_DFP:
u_role = USB_ROLE_DEVICE;
typec_set_data_role(con->port, TYPEC_DEVICE);
break;
default:
break;
}
if (con->status.flags & UCSI_CONSTAT_CONNECTED)
ucsi_register_partner(con);
else
ucsi_unregister_partner(con);
ucsi_port_psy_changed(con); ucsi_port_psy_changed(con);
/* Only notify USB controller if partner supports USB data */
if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) &
UCSI_CONSTAT_PARTNER_FLAG_USB))
u_role = USB_ROLE_NONE;
ret = usb_role_switch_set_role(con->usb_role_sw, u_role);
if (ret)
dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n",
con->num, u_role);
}
if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
ucsi_partner_change(con); ucsi_partner_change(con);
trace_ucsi_connector_change(con->num, &con->status); if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
ucsi_register_partner(con);
out_unlock: ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) { } else {
schedule_work(&con->work); ucsi_unregister_partner(con);
mutex_unlock(&con->lock); }
return;
} }
clear_bit(EVENT_PROCESSING, &ucsi->flags); if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE)
ucsi_pwr_opmode_change(con);
if (con->partner && con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) {
ucsi_partner_change(con);
/* Complete pending data role swap */
if (!completion_done(&con->complete))
complete(&con->complete);
}
if (con->status.change & UCSI_CONSTAT_CAM_CHANGE)
ucsi_partner_task(con, ucsi_check_altmodes, 1, 0);
clear_bit(EVENT_PENDING, &con->ucsi->flags);
ret = ucsi_acknowledge_connector_change(ucsi);
if (ret)
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
out_unlock:
mutex_unlock(&con->lock); mutex_unlock(&con->lock);
} }
@ -852,9 +834,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num)
return; return;
} }
set_bit(EVENT_PENDING, &ucsi->flags); if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags))
schedule_work(&con->work); schedule_work(&con->work);
} }
EXPORT_SYMBOL_GPL(ucsi_connector_change); EXPORT_SYMBOL_GPL(ucsi_connector_change);
@ -1041,8 +1021,18 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
enum typec_accessory *accessory = cap->accessory; enum typec_accessory *accessory = cap->accessory;
enum usb_role u_role = USB_ROLE_NONE; enum usb_role u_role = USB_ROLE_NONE;
u64 command; u64 command;
char *name;
int ret; int ret;
name = kasprintf(GFP_KERNEL, "%s-con%d", dev_name(ucsi->dev), con->num);
if (!name)
return -ENOMEM;
con->wq = create_singlethread_workqueue(name);
kfree(name);
if (!con->wq)
return -ENOMEM;
INIT_WORK(&con->work, ucsi_handle_connector_change); INIT_WORK(&con->work, ucsi_handle_connector_change);
init_completion(&con->complete); init_completion(&con->complete);
mutex_init(&con->lock); mutex_init(&con->lock);
@ -1160,16 +1150,9 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
ret = 0; ret = 0;
} }
if (con->partner) { if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == UCSI_CONSTAT_PWR_OPMODE_PD) {
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); ucsi_get_src_pdos(con);
if (ret) { ucsi_check_altmodes(con);
dev_err(ucsi->dev,
"con%d: failed to register alternate modes\n",
con->num);
ret = 0;
} else {
ucsi_altmode_update_active(con);
}
} }
trace_ucsi_register_port(con->num, &con->status); trace_ucsi_register_port(con->num, &con->status);
@ -1178,6 +1161,12 @@ out:
fwnode_handle_put(cap->fwnode); fwnode_handle_put(cap->fwnode);
out_unlock: out_unlock:
mutex_unlock(&con->lock); mutex_unlock(&con->lock);
if (ret && con->wq) {
destroy_workqueue(con->wq);
con->wq = NULL;
}
return ret; return ret;
} }
@ -1248,6 +1237,8 @@ err_unregister:
ucsi_unregister_partner(con); ucsi_unregister_partner(con);
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
ucsi_unregister_port_psy(con); ucsi_unregister_port_psy(con);
if (con->wq)
destroy_workqueue(con->wq);
typec_unregister_port(con->port); typec_unregister_port(con->port);
con->port = NULL; con->port = NULL;
} }
@ -1370,6 +1361,8 @@ void ucsi_unregister(struct ucsi *ucsi)
ucsi_unregister_altmodes(&ucsi->connector[i], ucsi_unregister_altmodes(&ucsi->connector[i],
UCSI_RECIPIENT_CON); UCSI_RECIPIENT_CON);
ucsi_unregister_port_psy(&ucsi->connector[i]); ucsi_unregister_port_psy(&ucsi->connector[i]);
if (ucsi->connector[i].wq)
destroy_workqueue(ucsi->connector[i].wq);
typec_unregister_port(ucsi->connector[i].port); typec_unregister_port(ucsi->connector[i].port);
} }

View File

@ -300,7 +300,6 @@ struct ucsi {
#define EVENT_PENDING 0 #define EVENT_PENDING 0
#define COMMAND_PENDING 1 #define COMMAND_PENDING 1
#define ACK_PENDING 2 #define ACK_PENDING 2
#define EVENT_PROCESSING 3
}; };
#define UCSI_MAX_SVID 5 #define UCSI_MAX_SVID 5
@ -317,6 +316,7 @@ struct ucsi_connector {
struct mutex lock; /* port lock */ struct mutex lock; /* port lock */
struct work_struct work; struct work_struct work;
struct completion complete; struct completion complete;
struct workqueue_struct *wq;
struct typec_port *port; struct typec_port *port;
struct typec_partner *partner; struct typec_partner *partner;
@ -326,7 +326,6 @@ struct ucsi_connector {
struct typec_capability typec_cap; struct typec_capability typec_cap;
u16 unprocessed_changes;
struct ucsi_connector_status status; struct ucsi_connector_status status;
struct ucsi_connector_capability cap; struct ucsi_connector_capability cap;
struct power_supply *psy; struct power_supply *psy;

View File

@ -78,7 +78,7 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
if (ret) if (ret)
goto out_clear_bit; goto out_clear_bit;
if (!wait_for_completion_timeout(&ua->complete, 60 * HZ)) if (!wait_for_completion_timeout(&ua->complete, HZ))
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
out_clear_bit: out_clear_bit:

View File

@ -363,7 +363,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer,
int retval = 0; int retval = 0;
struct urb *urb = NULL; struct urb *urb = NULL;
char *buf = NULL; char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER); size_t writesize = min_t(size_t, count, MAX_TRANSFER);
dev = file->private_data; dev = file->private_data;

View File

@ -18,6 +18,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
@ -30,6 +31,7 @@
* enter host mode * enter host mode
* requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level * requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level
* and hsdiscon_level should be set for adequate signal quality * and hsdiscon_level should be set for adequate signal quality
* requires_pmc_ao_power_up: true if USB AO is powered down by default
*/ */
struct tegra_phy_soc_config { struct tegra_phy_soc_config {
@ -37,6 +39,7 @@ struct tegra_phy_soc_config {
bool has_hostpc; bool has_hostpc;
bool requires_usbmode_setup; bool requires_usbmode_setup;
bool requires_extra_tuning_parameters; bool requires_extra_tuning_parameters;
bool requires_pmc_ao_power_up;
}; };
struct tegra_utmip_config { struct tegra_utmip_config {
@ -62,6 +65,7 @@ enum tegra_usb_phy_port_speed {
struct tegra_xtal_freq; struct tegra_xtal_freq;
struct tegra_usb_phy { struct tegra_usb_phy {
int irq;
int instance; int instance;
const struct tegra_xtal_freq *freq; const struct tegra_xtal_freq *freq;
void __iomem *regs; void __iomem *regs;
@ -70,6 +74,7 @@ struct tegra_usb_phy {
struct clk *pll_u; struct clk *pll_u;
struct clk *pad_clk; struct clk *pad_clk;
struct regulator *vbus; struct regulator *vbus;
struct regmap *pmc_regmap;
enum usb_dr_mode mode; enum usb_dr_mode mode;
void *config; void *config;
const struct tegra_phy_soc_config *soc_config; const struct tegra_phy_soc_config *soc_config;