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:
commit
048ff8629e
@ -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
|
|
@ -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>;
|
||||||
|
};
|
@ -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>;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
108
Documentation/devicetree/bindings/usb/smsc,usb3503.yaml
Normal file
108
Documentation/devicetree/bindings/usb/smsc,usb3503.yaml
Normal 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>;
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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";
|
|
||||||
};
|
|
@ -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))
|
||||||
|
@ -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";
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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 */
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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 */
|
||||||
|
@ -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");
|
||||||
|
@ -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 {
|
||||||
|
@ -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) \
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)"
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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},
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------*/
|
/*---------------------*/
|
||||||
|
@ -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"
|
||||||
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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 */
|
||||||
|
@ -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) {
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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))
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user