USB/Thunderbolt patches for 5.15-rc1
Here is the big set of USB and Thunderbolt patches for 5.15-rc1. Nothing huge in here, just lots of constant forward progress on a number of different drivers and hardware support: - more USB 4/Thunderbolt support added - dwc3 driver updates and additions - usb gadget fixes and addtions for new types - udc gadget driver updates - host controller updates - removal of obsolete drivers - other minor driver updates All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYS9+Tw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymqyQCgxWjp4VD9Ycbz1XsHRIkkERWj6WgAnRe4mCpG n5csYXATbYUD0UdH0hru =xZCV -----END PGP SIGNATURE----- Merge tag 'usb-5.15-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 patches for 5.15-rc1. Nothing huge in here, just lots of constant forward progress on a number of different drivers and hardware support: - more USB 4/Thunderbolt support added - dwc3 driver updates and additions - usb gadget fixes and addtions for new types - udc gadget driver updates - host controller updates - removal of obsolete drivers - other minor driver updates All of these have been in linux-next for a while with no reported issues" * tag 'usb-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (148 commits) usb: isp1760: otg control register access usb: isp1760: use the right irq status bit usb: isp1760: write to status and address register usb: isp1760: fix qtd fill length usb: isp1760: fix memory pool initialization usb: typec: tcpm: Fix spelling mistake "atleast" -> "at least" usb: dwc2: Fix spelling mistake "was't" -> "wasn't" usb: renesas_usbhs: Fix spelling mistake "faile" -> "failed" usb: host: xhci-rcar: Don't reload firmware after the completion usb: xhci-mtk: allow bandwidth table rollover usb: mtu3: fix random remote wakeup usb: mtu3: return successful suspend status usb: xhci-mtk: Do not use xhci's virt_dev in drop_endpoint usb: xhci-mtk: modify the SOF/ITP interval for mt8195 usb: xhci-mtk: add a member of num_esit usb: xhci-mtk: check boundary before check tt usb: xhci-mtk: update fs bus bandwidth by bw_budget_table usb: xhci-mtk: fix issue of out-of-bounds array access usb: xhci-mtk: support option to disable usb2 ports usb: xhci-mtk: fix use-after-free of mtk->hcd ...
This commit is contained in:
commit
07281a257a
|
@ -8,9 +8,19 @@ Description:
|
|||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
c_mute_present capture mute control enable
|
||||
c_volume_present capture volume control enable
|
||||
c_volume_min capture volume control min value (in 1/256 dB)
|
||||
c_volume_max capture volume control max value (in 1/256 dB)
|
||||
c_volume_res capture volume control resolution (in 1/256 dB)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
p_volume_min playback volume control min value (in 1/256 dB)
|
||||
p_volume_max playback volume control max value (in 1/256 dB)
|
||||
p_volume_res playback volume control resolution (in 1/256 dB)
|
||||
req_number the number of pre-allocated request
|
||||
for both capture and playback
|
||||
========== ===================================
|
||||
|
|
|
@ -9,8 +9,18 @@ Description:
|
|||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
c_sync capture synchronization type (async/adaptive)
|
||||
c_mute_present capture mute control enable
|
||||
c_volume_present capture volume control enable
|
||||
c_volume_min capture volume control min value (in 1/256 dB)
|
||||
c_volume_max capture volume control max value (in 1/256 dB)
|
||||
c_volume_res capture volume control resolution (in 1/256 dB)
|
||||
fb_max maximum extra bandwidth in async mode
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
p_volume_min playback volume control min value (in 1/256 dB)
|
||||
p_volume_max playback volume control max value (in 1/256 dB)
|
||||
p_volume_res playback volume control resolution (in 1/256 dB)
|
||||
========= ============================
|
||||
|
|
|
@ -111,6 +111,10 @@ properties:
|
|||
- 1.5A
|
||||
- 3.0A
|
||||
|
||||
pd-disable:
|
||||
description: Set this property if the Type-C connector has no power delivery support.
|
||||
type: boolean
|
||||
|
||||
# The following are optional properties for "usb-c-connector" with power
|
||||
# delivery support.
|
||||
source-pdos:
|
||||
|
|
|
@ -14,6 +14,7 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- qcom,sc7180-qmp-usb3-dp-phy
|
||||
- qcom,sc7280-qmp-usb3-dp-phy
|
||||
- qcom,sc8180x-qmp-usb3-dp-phy
|
||||
- qcom,sdm845-qmp-usb3-dp-phy
|
||||
- qcom,sm8250-qmp-usb3-dp-phy
|
||||
|
|
|
@ -34,6 +34,7 @@ properties:
|
|||
- allwinner,sun6i-a31-ehci
|
||||
- allwinner,sun7i-a20-ehci
|
||||
- allwinner,sun8i-a23-ehci
|
||||
- allwinner,sun8i-a83t-ehci
|
||||
- allwinner,sun8i-h3-ehci
|
||||
- allwinner,sun8i-r40-ehci
|
||||
- allwinner,sun9i-a80-ehci
|
||||
|
@ -142,6 +143,11 @@ properties:
|
|||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
dr_mode:
|
||||
enum:
|
||||
- host
|
||||
- otg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
@ -24,6 +24,7 @@ properties:
|
|||
- allwinner,sun6i-a31-ohci
|
||||
- allwinner,sun7i-a20-ohci
|
||||
- allwinner,sun8i-a23-ohci
|
||||
- allwinner,sun8i-a83t-ohci
|
||||
- allwinner,sun8i-h3-ohci
|
||||
- allwinner,sun8i-r40-ohci
|
||||
- allwinner,sun9i-a80-ohci
|
||||
|
@ -109,6 +110,11 @@ properties:
|
|||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
dr_mode:
|
||||
enum:
|
||||
- host
|
||||
- otg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
@ -31,6 +31,7 @@ properties:
|
|||
- mediatek,mt8173-xhci
|
||||
- mediatek,mt8183-xhci
|
||||
- mediatek,mt8192-xhci
|
||||
- mediatek,mt8195-xhci
|
||||
- const: mediatek,mtk-xhci
|
||||
|
||||
reg:
|
||||
|
@ -152,6 +153,11 @@ properties:
|
|||
description: The mask to disable u3ports, bit0 for u3port0,
|
||||
bit1 for u3port1, ... etc
|
||||
|
||||
mediatek,u2p-dis-msk:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: The mask to disable u2ports, bit0 for u2port0,
|
||||
bit1 for u2port1, ... etc
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
|
|
|
@ -38,7 +38,18 @@ properties:
|
|||
- const: ippc
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
use "interrupts-extended" when the interrupts are connected to the
|
||||
separate interrupt controllers
|
||||
minItems: 1
|
||||
items:
|
||||
- description: SSUSB device controller interrupt
|
||||
- description: optional, wakeup interrupt used to support runtime PM
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: device
|
||||
- const: wakeup
|
||||
|
||||
power-domains:
|
||||
description: A phandle to USB power domain node to control USB's MTCMOS
|
||||
|
@ -106,7 +117,7 @@ properties:
|
|||
extcon:
|
||||
deprecated: true
|
||||
description: |
|
||||
Phandle to the extcon device detecting the IDDIG/VBUS state, neede
|
||||
Phandle to the extcon device detecting the IDDIG state, needed
|
||||
when supports dual-role mode.
|
||||
It's considered valid for compatibility reasons, not allowed for
|
||||
new bindings, and use "usb-role-switch" property instead.
|
||||
|
@ -116,6 +127,10 @@ properties:
|
|||
description: Support role switch.
|
||||
type: boolean
|
||||
|
||||
role-switch-default-mode:
|
||||
enum: [host, peripheral]
|
||||
default: host
|
||||
|
||||
connector:
|
||||
$ref: /connector/usb-connector.yaml#
|
||||
description:
|
||||
|
@ -166,6 +181,12 @@ properties:
|
|||
description: The mask to disable u3ports, bit0 for u3port0,
|
||||
bit1 for u3port1, ... etc
|
||||
|
||||
mediatek,u2p-dis-msk:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: The mask to disable u2ports, bit0 for u2port0,
|
||||
bit1 for u2port1, ... etc; but can't disable u2port0 if dual role mode
|
||||
is enabled, so will be skipped in this case.
|
||||
|
||||
# Required child node when support dual-role
|
||||
patternProperties:
|
||||
"^usb@[0-9a-f]+$":
|
||||
|
@ -178,6 +199,7 @@ patternProperties:
|
|||
dependencies:
|
||||
connector: [ 'usb-role-switch' ]
|
||||
port: [ 'usb-role-switch' ]
|
||||
role-switch-default-mode: [ 'usb-role-switch' ]
|
||||
wakeup-source: [ 'mediatek,syscon-wakeup' ]
|
||||
|
||||
required:
|
||||
|
@ -230,7 +252,7 @@ examples:
|
|||
};
|
||||
};
|
||||
|
||||
# Enable/disable device by an input gpio for VBUS pin
|
||||
# Dual role switch by gpio-usb-b-connector
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/power/mt2712-power.h>
|
||||
|
@ -244,13 +266,27 @@ examples:
|
|||
power-domains = <&scpsys MT2712_POWER_DOMAIN_USB2>;
|
||||
clocks = <&topckgen CLK_TOP_USB30_SEL>;
|
||||
clock-names = "sys_ck";
|
||||
dr_mode = "peripheral";
|
||||
dr_mode = "otg";
|
||||
usb-role-switch;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
host0: usb@11270000 {
|
||||
compatible = "mediatek,mt2712-xhci", "mediatek,mtk-xhci";
|
||||
reg = <0x11270000 0x1000>;
|
||||
reg-names = "mac";
|
||||
interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_LOW>;
|
||||
power-domains = <&scpsys MT2712_POWER_DOMAIN_USB>;
|
||||
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
|
||||
clock-names = "sys_ck", "ref_ck";
|
||||
};
|
||||
|
||||
connector {
|
||||
compatible = "gpio-usb-b-connector", "usb-b-connector";
|
||||
type = "micro";
|
||||
vbus-gpios = <&pio 13 GPIO_ACTIVE_HIGH>;
|
||||
id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>;
|
||||
vbus-supply = <&usb_p0_vbus>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -268,6 +304,7 @@ examples:
|
|||
wakeup-source;
|
||||
dr_mode = "otg";
|
||||
usb-role-switch;
|
||||
role-switch-default-mode = "host";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
|
|
@ -19,6 +19,7 @@ properties:
|
|||
- enum:
|
||||
- mediatek,mt8516-musb
|
||||
- mediatek,mt2701-musb
|
||||
- mediatek,mt7623-musb
|
||||
- const: mediatek,mtk-musb
|
||||
|
||||
reg:
|
||||
|
|
|
@ -17,6 +17,7 @@ properties:
|
|||
- qcom,msm8998-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sdm660-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sm4250-dwc3
|
||||
|
|
|
@ -17,7 +17,9 @@ properties:
|
|||
- const: renesas,rza1-usbhs
|
||||
|
||||
- items:
|
||||
- const: renesas,usbhs-r7s9210 # RZ/A2
|
||||
- enum:
|
||||
- renesas,usbhs-r7s9210 # RZ/A2
|
||||
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
|
||||
- const: renesas,rza2-usbhs
|
||||
|
||||
- items:
|
||||
|
@ -59,7 +61,8 @@ properties:
|
|||
- description: USB 2.0 clock selector
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
renesas,buswait:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
@ -108,6 +111,25 @@ required:
|
|||
- clocks
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,usbhs-r9a07g044
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
items:
|
||||
- description: U2P_IXL_INT
|
||||
- description: U2P_INT_DMA[0]
|
||||
- description: U2P_INT_DMA[1]
|
||||
- description: U2P_INT_DMAERR
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
@ -289,10 +289,21 @@ properties:
|
|||
maximum: 16
|
||||
|
||||
tx-fifo-resize:
|
||||
description: Determines if the FIFO *has* to be reallocated
|
||||
deprecated: true
|
||||
description: Determines if the TX fifos can be dynamically resized depending
|
||||
on the number of IN endpoints used and if bursting is supported. This
|
||||
may help improve bandwidth on platforms with higher system latencies, as
|
||||
increased fifo space allows for the controller to prefetch data into its
|
||||
internal memory.
|
||||
type: boolean
|
||||
|
||||
tx-fifo-max-num:
|
||||
description: Specifies the max number of packets the txfifo resizing logic
|
||||
can account for when higher endpoint bursting is used. (bMaxBurst > 6) The
|
||||
higher the number, the more fifo space the txfifo resizing logic will
|
||||
allocate for that endpoint.
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
minimum: 3
|
||||
|
||||
snps,incr-burst-type-adjustment:
|
||||
description:
|
||||
Value for INCR burst type of GSBUSCFG0 register, undefined length INCR
|
||||
|
|
|
@ -724,18 +724,28 @@ Function-specific configfs interface
|
|||
The function name to use when creating the function directory is "uac2".
|
||||
The uac2 function provides these attributes in its function directory:
|
||||
|
||||
=============== ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
c_sync capture synchronization type (async/adaptive)
|
||||
fb_max maximum extra bandwidth in async mode
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
=============== ====================================================
|
||||
================ ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
c_sync capture synchronization type (async/adaptive)
|
||||
c_mute_present capture mute control enable
|
||||
c_volume_present capture volume control enable
|
||||
c_volume_min capture volume control min value (in 1/256 dB)
|
||||
c_volume_max capture volume control max value (in 1/256 dB)
|
||||
c_volume_res capture volume control resolution (in 1/256 dB)
|
||||
fb_max maximum extra bandwidth in async mode
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
p_volume_min playback volume control min value (in 1/256 dB)
|
||||
p_volume_max playback volume control max value (in 1/256 dB)
|
||||
p_volume_res playback volume control resolution (in 1/256 dB)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
================ ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
||||
|
@ -904,16 +914,26 @@ Function-specific configfs interface
|
|||
The function name to use when creating the function directory is "uac1".
|
||||
The uac1 function provides these attributes in its function directory:
|
||||
|
||||
========== ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
========== ====================================================
|
||||
================ ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
c_mute_present capture mute control enable
|
||||
c_volume_present capture volume control enable
|
||||
c_volume_min capture volume control min value (in 1/256 dB)
|
||||
c_volume_max capture volume control max value (in 1/256 dB)
|
||||
c_volume_res capture volume control resolution (in 1/256 dB)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
p_volume_min playback volume control min value (in 1/256 dB)
|
||||
p_volume_max playback volume control max value (in 1/256 dB)
|
||||
p_volume_res playback volume control resolution (in 1/256 dB)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
================ ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
||||
|
|
|
@ -276,6 +276,45 @@
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
&usb_1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usb_1_dwc3 {
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
&usb_1_hsphy {
|
||||
status = "okay";
|
||||
|
||||
vdda-pll-supply = <&vreg_l10c_0p8>;
|
||||
vdda33-supply = <&vreg_l2b_3p0>;
|
||||
vdda18-supply = <&vreg_l1c_1p8>;
|
||||
};
|
||||
|
||||
&usb_1_qmpphy {
|
||||
status = "okay";
|
||||
|
||||
vdda-phy-supply = <&vreg_l6b_1p2>;
|
||||
vdda-pll-supply = <&vreg_l1b_0p8>;
|
||||
};
|
||||
|
||||
&usb_2 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usb_2_dwc3 {
|
||||
dr_mode = "peripheral";
|
||||
};
|
||||
|
||||
&usb_2_hsphy {
|
||||
status = "okay";
|
||||
|
||||
vdda-pll-supply = <&vreg_l10c_0p8>;
|
||||
vdda33-supply = <&vreg_l2b_3p0>;
|
||||
vdda18-supply = <&vreg_l1c_1p8>;
|
||||
};
|
||||
|
||||
/* PINCTRL - additions to nodes defined in sc7280.dtsi */
|
||||
|
||||
&qup_uart5_default {
|
||||
|
|
|
@ -1035,6 +1035,125 @@
|
|||
};
|
||||
};
|
||||
|
||||
usb_1_hsphy: phy@88e3000 {
|
||||
compatible = "qcom,sc7280-usb-hs-phy",
|
||||
"qcom,usb-snps-hs-7nm-phy";
|
||||
reg = <0 0x088e3000 0 0x400>;
|
||||
status = "disabled";
|
||||
#phy-cells = <0>;
|
||||
|
||||
clocks = <&rpmhcc RPMH_CXO_CLK>;
|
||||
clock-names = "ref";
|
||||
|
||||
resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
|
||||
};
|
||||
|
||||
usb_2_hsphy: phy@88e4000 {
|
||||
compatible = "qcom,sc7280-usb-hs-phy",
|
||||
"qcom,usb-snps-hs-7nm-phy";
|
||||
reg = <0 0x088e4000 0 0x400>;
|
||||
status = "disabled";
|
||||
#phy-cells = <0>;
|
||||
|
||||
clocks = <&rpmhcc RPMH_CXO_CLK>;
|
||||
clock-names = "ref";
|
||||
|
||||
resets = <&gcc GCC_QUSB2PHY_SEC_BCR>;
|
||||
};
|
||||
|
||||
usb_1_qmpphy: phy-wrapper@88e9000 {
|
||||
compatible = "qcom,sc7280-qmp-usb3-dp-phy",
|
||||
"qcom,sm8250-qmp-usb3-dp-phy";
|
||||
reg = <0 0x088e9000 0 0x200>,
|
||||
<0 0x088e8000 0 0x40>,
|
||||
<0 0x088ea000 0 0x200>;
|
||||
status = "disabled";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
|
||||
<&rpmhcc RPMH_CXO_CLK>,
|
||||
<&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>;
|
||||
clock-names = "aux", "ref_clk_src", "com_aux";
|
||||
|
||||
resets = <&gcc GCC_USB3_DP_PHY_PRIM_BCR>,
|
||||
<&gcc GCC_USB3_PHY_PRIM_BCR>;
|
||||
reset-names = "phy", "common";
|
||||
|
||||
usb_1_ssphy: usb3-phy@88e9200 {
|
||||
reg = <0 0x088e9200 0 0x200>,
|
||||
<0 0x088e9400 0 0x200>,
|
||||
<0 0x088e9c00 0 0x400>,
|
||||
<0 0x088e9600 0 0x200>,
|
||||
<0 0x088e9800 0 0x200>,
|
||||
<0 0x088e9a00 0 0x100>;
|
||||
#clock-cells = <0>;
|
||||
#phy-cells = <0>;
|
||||
clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
|
||||
clock-names = "pipe0";
|
||||
clock-output-names = "usb3_phy_pipe_clk_src";
|
||||
};
|
||||
|
||||
dp_phy: dp-phy@88ea200 {
|
||||
reg = <0 0x088ea200 0 0x200>,
|
||||
<0 0x088ea400 0 0x200>,
|
||||
<0 0x088eac00 0 0x400>,
|
||||
<0 0x088ea600 0 0x200>,
|
||||
<0 0x088ea800 0 0x200>,
|
||||
<0 0x088eaa00 0 0x100>;
|
||||
#phy-cells = <0>;
|
||||
#clock-cells = <1>;
|
||||
clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
|
||||
clock-names = "pipe0";
|
||||
clock-output-names = "usb3_phy_pipe_clk_src";
|
||||
};
|
||||
};
|
||||
|
||||
usb_2: usb@8cf8800 {
|
||||
compatible = "qcom,sc7280-dwc3", "qcom,dwc3";
|
||||
reg = <0 0x08cf8800 0 0x400>;
|
||||
status = "disabled";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
dma-ranges;
|
||||
|
||||
clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
|
||||
<&gcc GCC_USB30_SEC_MASTER_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
|
||||
<&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_SEC_SLEEP_CLK>;
|
||||
clock-names = "cfg_noc", "core", "iface","mock_utmi",
|
||||
"sleep";
|
||||
|
||||
assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_SEC_MASTER_CLK>;
|
||||
assigned-clock-rates = <19200000>, <200000000>;
|
||||
|
||||
interrupts-extended = <&intc GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<&pdc 13 IRQ_TYPE_EDGE_RISING>,
|
||||
<&pdc 12 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "hs_phy_irq",
|
||||
"dm_hs_phy_irq", "dp_hs_phy_irq";
|
||||
|
||||
power-domains = <&gcc GCC_USB30_SEC_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_USB30_SEC_BCR>;
|
||||
|
||||
usb_2_dwc3: usb@8c00000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0 0x08c00000 0 0xe000>;
|
||||
interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>;
|
||||
iommus = <&apps_smmu 0xa0 0x0>;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,dis_enblslpm_quirk;
|
||||
phys = <&usb_2_hsphy>;
|
||||
phy-names = "usb2-phy";
|
||||
maximum-speed = "high-speed";
|
||||
};
|
||||
};
|
||||
|
||||
dc_noc: interconnect@90e0000 {
|
||||
reg = <0 0x090e0000 0 0x5080>;
|
||||
compatible = "qcom,sc7280-dc-noc";
|
||||
|
@ -1063,6 +1182,51 @@
|
|||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
usb_1: usb@a6f8800 {
|
||||
compatible = "qcom,sc7280-dwc3", "qcom,dwc3";
|
||||
reg = <0 0x0a6f8800 0 0x400>;
|
||||
status = "disabled";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
dma-ranges;
|
||||
|
||||
clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_SLEEP_CLK>;
|
||||
clock-names = "cfg_noc", "core", "iface", "mock_utmi",
|
||||
"sleep";
|
||||
|
||||
assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>;
|
||||
assigned-clock-rates = <19200000>, <200000000>;
|
||||
|
||||
interrupts-extended = <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<&pdc 14 IRQ_TYPE_EDGE_BOTH>,
|
||||
<&pdc 15 IRQ_TYPE_EDGE_BOTH>,
|
||||
<&pdc 17 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "hs_phy_irq", "dp_hs_phy_irq",
|
||||
"dm_hs_phy_irq", "ss_phy_irq";
|
||||
|
||||
power-domains = <&gcc GCC_USB30_PRIM_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_USB30_PRIM_BCR>;
|
||||
|
||||
usb_1_dwc3: usb@a600000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0 0x0a600000 0 0xe000>;
|
||||
interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
|
||||
iommus = <&apps_smmu 0xe0 0x0>;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,dis_enblslpm_quirk;
|
||||
phys = <&usb_1_hsphy>, <&usb_1_ssphy>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
maximum-speed = "super-speed";
|
||||
};
|
||||
};
|
||||
|
||||
videocc: clock-controller@aaf0000 {
|
||||
compatible = "qcom,sc7280-videocc";
|
||||
reg = <0 0xaaf0000 0 0x10000>;
|
||||
|
|
|
@ -1821,6 +1821,7 @@ int of_add_property(struct device_node *np, struct property *prop)
|
|||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_add_property);
|
||||
|
||||
int __of_remove_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
#define NHI_MAILBOX_TIMEOUT 500 /* ms */
|
||||
|
||||
#define QUIRK_AUTO_CLEAR_INT BIT(0)
|
||||
|
||||
static int ring_interrupt_index(struct tb_ring *ring)
|
||||
{
|
||||
int bit = ring->hop;
|
||||
|
@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
|||
else
|
||||
index = ring->hop + ring->nhi->hop_count;
|
||||
|
||||
/*
|
||||
* Ask the hardware to clear interrupt status bits automatically
|
||||
* since we already know which interrupt was triggered.
|
||||
*/
|
||||
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
|
||||
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
|
||||
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
|
||||
/*
|
||||
* Ask the hardware to clear interrupt status
|
||||
* bits automatically since we already know
|
||||
* which interrupt was triggered.
|
||||
*/
|
||||
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
|
||||
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
|
||||
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
|
||||
}
|
||||
}
|
||||
|
||||
ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
|
||||
|
@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
|
||||
|
||||
static void ring_clear_msix(const struct tb_ring *ring)
|
||||
{
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
|
||||
return;
|
||||
|
||||
if (ring->is_tx)
|
||||
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE);
|
||||
else
|
||||
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE +
|
||||
4 * (ring->nhi->hop_count / 32));
|
||||
}
|
||||
|
||||
static irqreturn_t ring_msix(int irq, void *data)
|
||||
{
|
||||
struct tb_ring *ring = data;
|
||||
|
||||
spin_lock(&ring->nhi->lock);
|
||||
ring_clear_msix(ring);
|
||||
spin_lock(&ring->lock);
|
||||
__ring_interrupt(ring);
|
||||
spin_unlock(&ring->lock);
|
||||
|
@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi)
|
|||
nhi->ops->shutdown(nhi);
|
||||
}
|
||||
|
||||
static void nhi_check_quirks(struct tb_nhi *nhi)
|
||||
{
|
||||
/*
|
||||
* Intel hardware supports auto clear of the interrupt status
|
||||
* reqister right after interrupt is being issued.
|
||||
*/
|
||||
if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||
nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
|
||||
}
|
||||
|
||||
static int nhi_init_msi(struct tb_nhi *nhi)
|
||||
{
|
||||
struct pci_dev *pdev = nhi->pdev;
|
||||
|
@ -1190,6 +1218,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
if (!nhi->tx_rings || !nhi->rx_rings)
|
||||
return -ENOMEM;
|
||||
|
||||
nhi_check_quirks(nhi);
|
||||
|
||||
res = nhi_init_msi(nhi);
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
|
||||
|
|
|
@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port)
|
|||
int res;
|
||||
int cap;
|
||||
|
||||
INIT_LIST_HEAD(&port->list);
|
||||
|
||||
/* Control adapter does not have configuration space */
|
||||
if (!port->port)
|
||||
return 0;
|
||||
|
||||
res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
|
||||
if (res) {
|
||||
if (res == -ENODEV) {
|
||||
|
@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port)
|
|||
}
|
||||
|
||||
/* Port 0 is the switch itself and has no PHY. */
|
||||
if (port->config.type == TB_TYPE_PORT && port->port != 0) {
|
||||
if (port->config.type == TB_TYPE_PORT) {
|
||||
cap = tb_port_find_cap(port, TB_PORT_CAP_PHY);
|
||||
|
||||
if (cap > 0)
|
||||
|
@ -762,7 +768,7 @@ static int tb_init_port(struct tb_port *port)
|
|||
if (!port->ctl_credits)
|
||||
port->ctl_credits = 2;
|
||||
|
||||
} else if (port->port != 0) {
|
||||
} else {
|
||||
cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
|
||||
if (cap > 0)
|
||||
port->cap_adap = cap;
|
||||
|
@ -773,10 +779,7 @@ static int tb_init_port(struct tb_port *port)
|
|||
ADP_CS_4_TOTAL_BUFFERS_SHIFT;
|
||||
|
||||
tb_dump_port(port->sw->tb, port);
|
||||
|
||||
INIT_LIST_HEAD(&port->list);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
|
||||
|
@ -1498,6 +1501,7 @@ static ssize_t authorized_show(struct device *dev,
|
|||
|
||||
static int disapprove_switch(struct device *dev, void *not_used)
|
||||
{
|
||||
char *envp[] = { "AUTHORIZED=0", NULL };
|
||||
struct tb_switch *sw;
|
||||
|
||||
sw = tb_to_switch(dev);
|
||||
|
@ -1514,7 +1518,7 @@ static int disapprove_switch(struct device *dev, void *not_used)
|
|||
return ret;
|
||||
|
||||
sw->authorized = 0;
|
||||
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
|
||||
kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1522,7 +1526,9 @@ static int disapprove_switch(struct device *dev, void *not_used)
|
|||
|
||||
static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
|
||||
{
|
||||
char envp_string[13];
|
||||
int ret = -EINVAL;
|
||||
char *envp[] = { envp_string, NULL };
|
||||
|
||||
if (!mutex_trylock(&sw->tb->lock))
|
||||
return restart_syscall();
|
||||
|
@ -1559,8 +1565,12 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
|
|||
|
||||
if (!ret) {
|
||||
sw->authorized = val;
|
||||
/* Notify status change to the userspace */
|
||||
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
|
||||
/*
|
||||
* Notify status change to the userspace, informing the new
|
||||
* value of /sys/bus/thunderbolt/devices/.../authorized.
|
||||
*/
|
||||
sprintf(envp_string, "AUTHORIZED=%u", sw->authorized);
|
||||
kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
unlock:
|
||||
|
@ -2443,7 +2453,7 @@ static void tb_switch_default_link_ports(struct tb_switch *sw)
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= sw->config.max_port_number; i += 2) {
|
||||
for (i = 1; i <= sw->config.max_port_number; i++) {
|
||||
struct tb_port *port = &sw->ports[i];
|
||||
struct tb_port *subordinate;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ config USB_OHCI_BIG_ENDIAN_MMIO
|
|||
|
||||
config USB_OHCI_LITTLE_ENDIAN
|
||||
bool
|
||||
default n if STB03xxx || PPC_MPC52xx
|
||||
default n if PPC_MPC52xx
|
||||
default y
|
||||
|
||||
config USB_EHCI_BIG_ENDIAN_MMIO
|
||||
|
|
|
@ -31,7 +31,6 @@ obj-$(CONFIG_USB_SL811_HCD) += host/
|
|||
obj-$(CONFIG_USB_ISP1362_HCD) += host/
|
||||
obj-$(CONFIG_USB_U132_HCD) += host/
|
||||
obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
obj-$(CONFIG_USB_HWA_HCD) += host/
|
||||
obj-$(CONFIG_USB_FSL_USB2) += host/
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += host/
|
||||
obj-$(CONFIG_USB_MAX3421_HCD) += host/
|
||||
|
|
|
@ -882,7 +882,7 @@ static u32 cdnsp_get_endpoint_max_burst(struct usb_gadget *g,
|
|||
if (g->speed == USB_SPEED_HIGH &&
|
||||
(usb_endpoint_xfer_isoc(pep->endpoint.desc) ||
|
||||
usb_endpoint_xfer_int(pep->endpoint.desc)))
|
||||
return (usb_endpoint_maxp(pep->endpoint.desc) & 0x1800) >> 11;
|
||||
return usb_endpoint_maxp_mult(pep->endpoint.desc) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -240,15 +240,18 @@ static int ci_ehci_hub_control(
|
|||
)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
unsigned int ports = HCS_N_PORTS(ehci->hcs_params);
|
||||
u32 __iomem *status_reg;
|
||||
u32 temp;
|
||||
u32 temp, port_index;
|
||||
unsigned long flags;
|
||||
int retval = 0;
|
||||
bool done = false;
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
|
||||
port_index = wIndex & 0xff;
|
||||
port_index -= (port_index > 0);
|
||||
status_reg = &ehci->regs->port_status[port_index];
|
||||
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
|
||||
|
@ -260,6 +263,11 @@ static int ci_ehci_hub_control(
|
|||
}
|
||||
|
||||
if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
|
||||
if (!wIndex || wIndex > ports) {
|
||||
retval = -EPIPE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
temp = ehci_readl(ehci, status_reg);
|
||||
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
|
||||
retval = -EPIPE;
|
||||
|
@ -288,7 +296,7 @@ static int ci_ehci_hub_control(
|
|||
ehci_writel(ehci, temp, status_reg);
|
||||
}
|
||||
|
||||
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
|
||||
set_bit(port_index, &ehci->suspended_ports);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
|
@ -200,6 +200,26 @@ enum usb_dr_mode usb_get_dr_mode(struct device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_dr_mode);
|
||||
|
||||
/**
|
||||
* usb_get_role_switch_default_mode - Get default mode for given device
|
||||
* @dev: Pointer to the given device
|
||||
*
|
||||
* The function gets string from property 'role-switch-default-mode',
|
||||
* and returns the corresponding enum usb_dr_mode.
|
||||
*/
|
||||
enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev)
|
||||
{
|
||||
const char *str;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_string(dev, "role-switch-default-mode", &str);
|
||||
if (ret < 0)
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
|
||||
return usb_get_dr_mode_from_string(str);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_role_switch_default_mode);
|
||||
|
||||
/**
|
||||
* usb_decode_interval - Decode bInterval into the time expressed in 1us unit
|
||||
* @epd: The descriptor of the endpoint
|
||||
|
|
|
@ -295,7 +295,7 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
|||
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE,
|
||||
20000)) {
|
||||
dev_dbg(hsotg->dev,
|
||||
"%s: Restore Done wan't generated here\n",
|
||||
"%s: Restore Done wasn't generated here\n",
|
||||
__func__);
|
||||
} else {
|
||||
dev_dbg(hsotg->dev, "restore done generated here\n");
|
||||
|
|
|
@ -122,6 +122,7 @@ struct dwc2_hsotg_req;
|
|||
* @periodic: Set if this is a periodic ep, such as Interrupt
|
||||
* @isochronous: Set if this is a isochronous ep
|
||||
* @send_zlp: Set if we need to send a zero-length packet.
|
||||
* @wedged: Set if ep is wedged.
|
||||
* @desc_list_dma: The DMA address of descriptor chain currently in use.
|
||||
* @desc_list: Pointer to descriptor DMA chain head currently in use.
|
||||
* @desc_count: Count of entries within the DMA descriptor chain of EP.
|
||||
|
@ -172,6 +173,7 @@ struct dwc2_hsotg_ep {
|
|||
unsigned int periodic:1;
|
||||
unsigned int isochronous:1;
|
||||
unsigned int send_zlp:1;
|
||||
unsigned int wedged:1;
|
||||
unsigned int target_frame;
|
||||
#define TARGET_FRAME_INITIAL 0xFFFFFFFF
|
||||
bool frame_overrun;
|
||||
|
|
|
@ -1806,7 +1806,8 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
|
|||
case USB_ENDPOINT_HALT:
|
||||
halted = ep->halted;
|
||||
|
||||
dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
|
||||
if (!ep->wedged)
|
||||
dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
|
||||
|
||||
ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);
|
||||
if (ret) {
|
||||
|
@ -4066,6 +4067,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
|||
hs_ep->isochronous = 0;
|
||||
hs_ep->periodic = 0;
|
||||
hs_ep->halted = 0;
|
||||
hs_ep->wedged = 0;
|
||||
hs_ep->interval = desc->bInterval;
|
||||
|
||||
switch (ep_type) {
|
||||
|
@ -4306,6 +4308,27 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_ep_set_wedge - set wedge on a given endpoint
|
||||
* @ep: The endpoint to be wedged.
|
||||
*
|
||||
*/
|
||||
static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep)
|
||||
{
|
||||
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&hs->lock, flags);
|
||||
hs_ep->wedged = 1;
|
||||
ret = dwc2_hsotg_ep_sethalt(ep, 1, false);
|
||||
spin_unlock_irqrestore(&hs->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_ep_sethalt - set halt on a given endpoint
|
||||
* @ep: The endpoint to set halt.
|
||||
|
@ -4357,6 +4380,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
|
|||
epctl |= DXEPCTL_EPDIS;
|
||||
} else {
|
||||
epctl &= ~DXEPCTL_STALL;
|
||||
hs_ep->wedged = 0;
|
||||
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
|
||||
if (xfertype == DXEPCTL_EPTYPE_BULK ||
|
||||
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
|
||||
|
@ -4376,6 +4400,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
|
|||
// STALL bit will be set in GOUTNAKEFF interrupt handler
|
||||
} else {
|
||||
epctl &= ~DXEPCTL_STALL;
|
||||
hs_ep->wedged = 0;
|
||||
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
|
||||
if (xfertype == DXEPCTL_EPTYPE_BULK ||
|
||||
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
|
||||
|
@ -4415,6 +4440,7 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
|
|||
.queue = dwc2_hsotg_ep_queue_lock,
|
||||
.dequeue = dwc2_hsotg_ep_dequeue,
|
||||
.set_halt = dwc2_hsotg_ep_sethalt_lock,
|
||||
.set_wedge = dwc2_gadget_ep_set_wedge,
|
||||
/* note, don't believe we have any call for the fifo routines */
|
||||
};
|
||||
|
||||
|
@ -4683,12 +4709,35 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
|
|||
return usb_phy_set_power(hsotg->uphy, mA);
|
||||
}
|
||||
|
||||
static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(g);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
switch (speed) {
|
||||
case USB_SPEED_HIGH:
|
||||
hsotg->params.speed = DWC2_SPEED_PARAM_HIGH;
|
||||
break;
|
||||
case USB_SPEED_FULL:
|
||||
hsotg->params.speed = DWC2_SPEED_PARAM_FULL;
|
||||
break;
|
||||
case USB_SPEED_LOW:
|
||||
hsotg->params.speed = DWC2_SPEED_PARAM_LOW;
|
||||
break;
|
||||
default:
|
||||
dev_err(hsotg->dev, "invalid speed (%d)\n", speed);
|
||||
}
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
|
||||
.get_frame = dwc2_hsotg_gadget_getframe,
|
||||
.set_selfpowered = dwc2_hsotg_set_selfpowered,
|
||||
.udc_start = dwc2_hsotg_udc_start,
|
||||
.udc_stop = dwc2_hsotg_udc_stop,
|
||||
.pullup = dwc2_hsotg_pullup,
|
||||
.udc_set_speed = dwc2_gadget_set_speed,
|
||||
.vbus_session = dwc2_hsotg_vbus_session,
|
||||
.vbus_draw = dwc2_hsotg_vbus_draw,
|
||||
};
|
||||
|
|
|
@ -1050,6 +1050,15 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
|
||||
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
|
||||
|
||||
/*
|
||||
* Decouple USB 2.0 L1 & L2 events which will allow for
|
||||
* gadget driver to only receive U3/L2 suspend & wakeup
|
||||
* events and prevent the more frequent L1 LPM transitions
|
||||
* from interrupting the driver.
|
||||
*/
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 300A))
|
||||
reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT;
|
||||
|
||||
if (dwc->dis_tx_ipgap_linecheck_quirk)
|
||||
reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
|
||||
|
||||
|
@ -1267,6 +1276,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
u8 rx_max_burst_prd;
|
||||
u8 tx_thr_num_pkt_prd;
|
||||
u8 tx_max_burst_prd;
|
||||
u8 tx_fifo_resize_max_num;
|
||||
const char *usb_psy_name;
|
||||
int ret;
|
||||
|
||||
|
@ -1282,6 +1292,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
*/
|
||||
hird_threshold = 12;
|
||||
|
||||
/*
|
||||
* default to a TXFIFO size large enough to fit 6 max packets. This
|
||||
* allows for systems with larger bus latencies to have some headroom
|
||||
* for endpoints that have a large bMaxBurst value.
|
||||
*/
|
||||
tx_fifo_resize_max_num = 6;
|
||||
|
||||
dwc->maximum_speed = usb_get_maximum_speed(dev);
|
||||
dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
|
||||
dwc->dr_mode = usb_get_dr_mode(dev);
|
||||
|
@ -1325,6 +1342,11 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
&tx_thr_num_pkt_prd);
|
||||
device_property_read_u8(dev, "snps,tx-max-burst-prd",
|
||||
&tx_max_burst_prd);
|
||||
dwc->do_fifo_resize = device_property_read_bool(dev,
|
||||
"tx-fifo-resize");
|
||||
if (dwc->do_fifo_resize)
|
||||
device_property_read_u8(dev, "tx-fifo-max-num",
|
||||
&tx_fifo_resize_max_num);
|
||||
|
||||
dwc->disable_scramble_quirk = device_property_read_bool(dev,
|
||||
"snps,disable_scramble_quirk");
|
||||
|
@ -1390,6 +1412,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
dwc->tx_max_burst_prd = tx_max_burst_prd;
|
||||
|
||||
dwc->imod_interval = 0;
|
||||
|
||||
dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
|
||||
}
|
||||
|
||||
/* check whether the core supports IMOD */
|
||||
|
|
|
@ -256,9 +256,10 @@
|
|||
#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
|
||||
|
||||
/* Global User Control 1 Register */
|
||||
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
|
||||
#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
|
||||
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
|
||||
|
||||
/* Global Status Register */
|
||||
#define DWC3_GSTS_OTG_IP BIT(10)
|
||||
|
@ -1023,6 +1024,7 @@ struct dwc3_scratchpad_array {
|
|||
* @rx_max_burst_prd: max periodic ESS receive burst size
|
||||
* @tx_thr_num_pkt_prd: periodic ESS transmit packet count
|
||||
* @tx_max_burst_prd: max periodic ESS transmit burst size
|
||||
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
|
||||
* @hsphy_interface: "utmi" or "ulpi"
|
||||
* @connected: true when we're connected to a host, false otherwise
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
|
@ -1037,6 +1039,7 @@ struct dwc3_scratchpad_array {
|
|||
* 1 - utmi_l1_suspend_n
|
||||
* @is_fpga: true when we are using the FPGA board
|
||||
* @pending_events: true when we have pending IRQs to be handled
|
||||
* @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
|
@ -1079,6 +1082,11 @@ struct dwc3_scratchpad_array {
|
|||
* @dis_split_quirk: set to disable split boundary.
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
* increments or 0 to disable.
|
||||
* @max_cfg_eps: current max number of IN eps used across all USB configs.
|
||||
* @last_fifo_depth: last fifo depth used to determine next fifo ram start
|
||||
* address.
|
||||
* @num_ep_resized: carries the current number endpoints which have had its tx
|
||||
* fifo resized.
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct work_struct drd_work;
|
||||
|
@ -1233,6 +1241,7 @@ struct dwc3 {
|
|||
u8 rx_max_burst_prd;
|
||||
u8 tx_thr_num_pkt_prd;
|
||||
u8 tx_max_burst_prd;
|
||||
u8 tx_fifo_resize_max_num;
|
||||
|
||||
const char *hsphy_interface;
|
||||
|
||||
|
@ -1246,6 +1255,7 @@ struct dwc3 {
|
|||
unsigned is_utmi_l1_suspend:1;
|
||||
unsigned is_fpga:1;
|
||||
unsigned pending_events:1;
|
||||
unsigned do_fifo_resize:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned three_stage_setup:1;
|
||||
|
@ -1282,6 +1292,10 @@ struct dwc3 {
|
|||
unsigned async_callbacks:1;
|
||||
|
||||
u16 imod_interval;
|
||||
|
||||
int max_cfg_eps;
|
||||
int last_fifo_depth;
|
||||
int num_ep_resized;
|
||||
};
|
||||
|
||||
#define INCRX_BURST_MODE 0
|
||||
|
@ -1513,6 +1527,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
|
||||
u32 param);
|
||||
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
|
@ -1532,6 +1547,8 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
|||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
int cmd, u32 param)
|
||||
{ return 0; }
|
||||
static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
|
|
|
@ -541,14 +541,10 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
|
|||
static int dwc3_setup_role_switch(struct dwc3 *dwc)
|
||||
{
|
||||
struct usb_role_switch_desc dwc3_role_switch = {NULL};
|
||||
const char *str;
|
||||
u32 mode;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_string(dwc->dev, "role-switch-default-mode",
|
||||
&str);
|
||||
if (ret >= 0 && !strncmp(str, "host", strlen("host"))) {
|
||||
dwc->role_switch_default_mode = USB_DR_MODE_HOST;
|
||||
dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev);
|
||||
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) {
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
} else {
|
||||
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
|
|
@ -152,13 +152,6 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
}
|
||||
dwc3_imx->irq = irq;
|
||||
|
||||
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
|
||||
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err);
|
||||
goto disable_clks;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
err = pm_runtime_get_sync(dev);
|
||||
|
@ -186,6 +179,13 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
}
|
||||
of_node_put(dwc3_np);
|
||||
|
||||
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
|
||||
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err);
|
||||
goto depopulate;
|
||||
}
|
||||
|
||||
device_set_wakeup_capable(dev, true);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
|
|
@ -598,6 +598,8 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
|
|||
USB_R5_ID_DIG_IRQ, 0);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
dwc3_meson_g12a_irq_thread,
|
||||
IRQF_ONESHOT, pdev->name, priv);
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_ADLM 0x54ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
|
||||
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
|
||||
#define PCI_DEVICE_ID_AMD_MR 0x163a
|
||||
|
||||
#define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
|
||||
#define PCI_INTEL_BXT_FUNC_PMU_PWR 4
|
||||
|
@ -148,6 +149,14 @@ static const struct property_entry dwc3_pci_amd_properties[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct property_entry dwc3_pci_mr_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("dr_mode", "otg"),
|
||||
PROPERTY_ENTRY_BOOL("usb-role-switch"),
|
||||
PROPERTY_ENTRY_STRING("role-switch-default-mode", "host"),
|
||||
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct software_node dwc3_pci_intel_swnode = {
|
||||
.properties = dwc3_pci_intel_properties,
|
||||
};
|
||||
|
@ -160,6 +169,10 @@ static const struct software_node dwc3_pci_amd_swnode = {
|
|||
.properties = dwc3_pci_amd_properties,
|
||||
};
|
||||
|
||||
static const struct software_node dwc3_pci_amd_mr_swnode = {
|
||||
.properties = dwc3_pci_mr_properties,
|
||||
};
|
||||
|
||||
static int dwc3_pci_quirks(struct dwc3_pci *dwc)
|
||||
{
|
||||
struct pci_dev *pdev = dwc->pci;
|
||||
|
@ -401,6 +414,10 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
|||
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB),
|
||||
(kernel_ulong_t) &dwc3_pci_amd_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MR),
|
||||
(kernel_ulong_t)&dwc3_pci_amd_mr_swnode, },
|
||||
|
||||
{ } /* Terminating Entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
|
||||
|
|
|
@ -115,7 +115,7 @@ static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val)
|
|||
readl(base + offset);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_vbus_overrride_enable(struct dwc3_qcom *qcom, bool enable)
|
||||
static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
|
||||
|
@ -136,7 +136,7 @@ static int dwc3_qcom_vbus_notifier(struct notifier_block *nb,
|
|||
struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb);
|
||||
|
||||
/* enable vbus override for device mode */
|
||||
dwc3_qcom_vbus_overrride_enable(qcom, event);
|
||||
dwc3_qcom_vbus_override_enable(qcom, event);
|
||||
qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
|
@ -148,7 +148,7 @@ static int dwc3_qcom_host_notifier(struct notifier_block *nb,
|
|||
struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb);
|
||||
|
||||
/* disable vbus override in host mode */
|
||||
dwc3_qcom_vbus_overrride_enable(qcom, !event);
|
||||
dwc3_qcom_vbus_override_enable(qcom, !event);
|
||||
qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
|
@ -614,6 +614,10 @@ static int dwc3_qcom_acpi_register_core(struct platform_device *pdev)
|
|||
qcom->acpi_pdata->dwc3_core_base_size;
|
||||
|
||||
irq = platform_get_irq(pdev_irq, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto out;
|
||||
}
|
||||
child_res[1].flags = IORESOURCE_IRQ;
|
||||
child_res[1].start = child_res[1].end = irq;
|
||||
|
||||
|
@ -645,6 +649,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
|||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node, *dwc3_np;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct property *prop;
|
||||
int ret;
|
||||
|
||||
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
|
||||
|
@ -653,6 +658,20 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL);
|
||||
if (!prop) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(dev, "unable to allocate memory for property\n");
|
||||
goto node_put;
|
||||
}
|
||||
|
||||
prop->name = "tx-fifo-resize";
|
||||
ret = of_add_property(dwc3_np, prop);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to add property\n");
|
||||
goto node_put;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
|
||||
|
@ -811,7 +830,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
|||
|
||||
/* enable vbus override for device mode */
|
||||
if (qcom->mode == USB_DR_MODE_PERIPHERAL)
|
||||
dwc3_qcom_vbus_overrride_enable(qcom, true);
|
||||
dwc3_qcom_vbus_override_enable(qcom, true);
|
||||
|
||||
/* register extcon to override sw_vbus on Vbus change later */
|
||||
ret = dwc3_qcom_register_extcon(qcom);
|
||||
|
@ -916,6 +935,7 @@ static const struct of_device_id dwc3_qcom_of_match[] = {
|
|||
{ .compatible = "qcom,dwc3" },
|
||||
{ .compatible = "qcom,msm8996-dwc3" },
|
||||
{ .compatible = "qcom,msm8998-dwc3" },
|
||||
{ .compatible = "qcom,sdm660-dwc3" },
|
||||
{ .compatible = "qcom,sdm845-dwc3" },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -621,6 +621,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
return -EINVAL;
|
||||
|
||||
case USB_STATE_ADDRESS:
|
||||
dwc3_gadget_clear_tx_fifos(dwc);
|
||||
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
/* if the cfg matches and the cfg is non zero */
|
||||
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
|
||||
|
|
|
@ -631,6 +631,187 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
|
|||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
|
||||
* @dwc: pointer to the DWC3 context
|
||||
* @nfifos: number of fifos to calculate for
|
||||
*
|
||||
* Calculates the size value based on the equation below:
|
||||
*
|
||||
* DWC3 revision 280A and prior:
|
||||
* fifo_size = mult * (max_packet / mdwidth) + 1;
|
||||
*
|
||||
* DWC3 revision 290A and onwards:
|
||||
* fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
|
||||
*
|
||||
* The max packet size is set to 1024, as the txfifo requirements mainly apply
|
||||
* to super speed USB use cases. However, it is safe to overestimate the fifo
|
||||
* allocations for other scenarios, i.e. high speed USB.
|
||||
*/
|
||||
static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
|
||||
{
|
||||
int max_packet = 1024;
|
||||
int fifo_size;
|
||||
int mdwidth;
|
||||
|
||||
mdwidth = dwc3_mdwidth(dwc);
|
||||
|
||||
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||
mdwidth >>= 3;
|
||||
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 290A))
|
||||
fifo_size = mult * (max_packet / mdwidth) + 1;
|
||||
else
|
||||
fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
|
||||
return fifo_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation
|
||||
* @dwc: pointer to the DWC3 context
|
||||
*
|
||||
* Iterates through all the endpoint registers and clears the previous txfifo
|
||||
* allocations.
|
||||
*/
|
||||
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
int fifo_depth;
|
||||
int size;
|
||||
int num;
|
||||
|
||||
if (!dwc->do_fifo_resize)
|
||||
return;
|
||||
|
||||
/* Read ep0IN related TXFIFO size */
|
||||
dep = dwc->eps[1];
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
|
||||
else
|
||||
fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size);
|
||||
|
||||
dwc->last_fifo_depth = fifo_depth;
|
||||
/* Clear existing TXFIFO for all IN eps except ep0 */
|
||||
for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
|
||||
num += 2) {
|
||||
dep = dwc->eps[num];
|
||||
/* Don't change TXFRAMNUM on usb31 version */
|
||||
size = DWC3_IP_IS(DWC3) ? 0 :
|
||||
dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
|
||||
DWC31_GTXFIFOSIZ_TXFRAMNUM;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
|
||||
}
|
||||
dwc->num_ep_resized = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
|
||||
* @dwc: pointer to our context structure
|
||||
*
|
||||
* This function will a best effort FIFO allocation in order
|
||||
* to improve FIFO usage and throughput, while still allowing
|
||||
* us to enable as many endpoints as possible.
|
||||
*
|
||||
* Keep in mind that this operation will be highly dependent
|
||||
* on the configured size for RAM1 - which contains TxFifo -,
|
||||
* the amount of endpoints enabled on coreConsultant tool, and
|
||||
* the width of the Master Bus.
|
||||
*
|
||||
* In general, FIFO depths are represented with the following equation:
|
||||
*
|
||||
* fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
|
||||
*
|
||||
* In conjunction with dwc3_gadget_check_config(), this resizing logic will
|
||||
* ensure that all endpoints will have enough internal memory for one max
|
||||
* packet per endpoint.
|
||||
*/
|
||||
static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int fifo_0_start;
|
||||
int ram1_depth;
|
||||
int fifo_size;
|
||||
int min_depth;
|
||||
int num_in_ep;
|
||||
int remaining;
|
||||
int num_fifos = 1;
|
||||
int fifo;
|
||||
int tmp;
|
||||
|
||||
if (!dwc->do_fifo_resize)
|
||||
return 0;
|
||||
|
||||
/* resize IN endpoints except ep0 */
|
||||
if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
|
||||
return 0;
|
||||
|
||||
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
|
||||
|
||||
if ((dep->endpoint.maxburst > 1 &&
|
||||
usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
num_fifos = 3;
|
||||
|
||||
if (dep->endpoint.maxburst > 6 &&
|
||||
usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
|
||||
num_fifos = dwc->tx_fifo_resize_max_num;
|
||||
|
||||
/* FIFO size for a single buffer */
|
||||
fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
|
||||
|
||||
/* Calculate the number of remaining EPs w/o any FIFO */
|
||||
num_in_ep = dwc->max_cfg_eps;
|
||||
num_in_ep -= dwc->num_ep_resized;
|
||||
|
||||
/* Reserve at least one FIFO for the number of IN EPs */
|
||||
min_depth = num_in_ep * (fifo + 1);
|
||||
remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
|
||||
remaining = max_t(int, 0, remaining);
|
||||
/*
|
||||
* We've already reserved 1 FIFO per EP, so check what we can fit in
|
||||
* addition to it. If there is not enough remaining space, allocate
|
||||
* all the remaining space to the EP.
|
||||
*/
|
||||
fifo_size = (num_fifos - 1) * fifo;
|
||||
if (remaining < fifo_size)
|
||||
fifo_size = remaining;
|
||||
|
||||
fifo_size += fifo;
|
||||
/* Last increment according to the TX FIFO size equation */
|
||||
fifo_size++;
|
||||
|
||||
/* Check if TXFIFOs start at non-zero addr */
|
||||
tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
||||
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
|
||||
|
||||
fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
|
||||
else
|
||||
dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
|
||||
|
||||
/* Check fifo size allocation doesn't exceed available RAM size. */
|
||||
if (dwc->last_fifo_depth >= ram1_depth) {
|
||||
dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
|
||||
dwc->last_fifo_depth, ram1_depth,
|
||||
dep->endpoint.name, fifo_size);
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
|
||||
else
|
||||
fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
|
||||
|
||||
dwc->last_fifo_depth -= fifo_size;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
|
||||
dwc->num_ep_resized++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __dwc3_gadget_ep_enable - initializes a hw endpoint
|
||||
* @dep: endpoint to be initialized
|
||||
|
@ -648,6 +829,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
int ret;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
ret = dwc3_gadget_resize_tx_fifos(dep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_gadget_start_config(dep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -2508,6 +2693,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->gadget_driver = NULL;
|
||||
dwc->max_cfg_eps = 0;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
free_irq(dwc->irq_gadget, dwc->ev_buf);
|
||||
|
@ -2595,6 +2781,51 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_check_config - ensure dwc3 can support the USB configuration
|
||||
* @g: pointer to the USB gadget
|
||||
*
|
||||
* Used to record the maximum number of endpoints being used in a USB composite
|
||||
* device. (across all configurations) This is to be used in the calculation
|
||||
* of the TXFIFO sizes when resizing internal memory for individual endpoints.
|
||||
* It will help ensured that the resizing logic reserves enough space for at
|
||||
* least one max packet.
|
||||
*/
|
||||
static int dwc3_gadget_check_config(struct usb_gadget *g)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
struct usb_ep *ep;
|
||||
int fifo_size = 0;
|
||||
int ram1_depth;
|
||||
int ep_num = 0;
|
||||
|
||||
if (!dwc->do_fifo_resize)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(ep, &g->ep_list, ep_list) {
|
||||
/* Only interested in the IN endpoints */
|
||||
if (ep->claimed && (ep->address & USB_DIR_IN))
|
||||
ep_num++;
|
||||
}
|
||||
|
||||
if (ep_num <= dwc->max_cfg_eps)
|
||||
return 0;
|
||||
|
||||
/* Update the max number of eps in the composition */
|
||||
dwc->max_cfg_eps = ep_num;
|
||||
|
||||
fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps);
|
||||
/* Based on the equation, increment by one for every ep */
|
||||
fifo_size += dwc->max_cfg_eps;
|
||||
|
||||
/* Check if we can fit a single fifo per endpoint */
|
||||
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
|
||||
if (fifo_size > ram1_depth)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
|
@ -2616,6 +2847,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|||
.udc_set_ssp_rate = dwc3_gadget_set_ssp_rate,
|
||||
.get_config_params = dwc3_gadget_config_params,
|
||||
.vbus_draw = dwc3_gadget_vbus_draw,
|
||||
.check_config = dwc3_gadget_check_config,
|
||||
.udc_async_callbacks = dwc3_gadget_async_callbacks,
|
||||
};
|
||||
|
||||
|
@ -4011,7 +4243,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
|
||||
usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
|
||||
usb_initialize_gadget(dwc->sysdev, dwc->gadget, dwc_gadget_release);
|
||||
dev = &dwc->gadget->dev;
|
||||
dev->platform_data = dwc;
|
||||
dwc->gadget->ops = &dwc3_gadget_ops;
|
||||
|
|
|
@ -450,6 +450,7 @@ config USB_CONFIGFS_F_UVC
|
|||
depends on USB_CONFIGFS
|
||||
depends on VIDEO_V4L2
|
||||
depends on VIDEO_DEV
|
||||
select VIDEOBUF2_DMA_SG
|
||||
select VIDEOBUF2_VMALLOC
|
||||
select USB_F_UVC
|
||||
help
|
||||
|
|
|
@ -482,7 +482,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
|
|||
{
|
||||
unsigned val;
|
||||
|
||||
if (c->MaxPower)
|
||||
if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
|
||||
val = c->MaxPower;
|
||||
else
|
||||
val = CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
|
@ -936,7 +936,11 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||
}
|
||||
|
||||
/* when we return, be sure our power usage is valid */
|
||||
power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
|
||||
power = c->MaxPower;
|
||||
else
|
||||
power = CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
|
||||
if (gadget->speed < USB_SPEED_SUPER)
|
||||
power = min(power, 500U);
|
||||
else
|
||||
|
|
|
@ -55,7 +55,7 @@ struct gadget_info {
|
|||
|
||||
static inline struct gadget_info *to_gadget_info(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct gadget_info, group);
|
||||
return container_of(to_config_group(item), struct gadget_info, group);
|
||||
}
|
||||
|
||||
struct config_usb_cfg {
|
||||
|
@ -365,21 +365,21 @@ static struct configfs_attribute *gadget_root_attrs[] = {
|
|||
|
||||
static inline struct gadget_strings *to_gadget_strings(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct gadget_strings,
|
||||
return container_of(to_config_group(item), struct gadget_strings,
|
||||
group);
|
||||
}
|
||||
|
||||
static inline struct gadget_config_name *to_gadget_config_name(
|
||||
struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct gadget_config_name,
|
||||
return container_of(to_config_group(item), struct gadget_config_name,
|
||||
group);
|
||||
}
|
||||
|
||||
static inline struct usb_function_instance *to_usb_function_instance(
|
||||
struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item),
|
||||
return container_of(to_config_group(item),
|
||||
struct usb_function_instance, group);
|
||||
}
|
||||
|
||||
|
@ -1404,6 +1404,10 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
|
|||
goto err_purge_funcs;
|
||||
}
|
||||
}
|
||||
ret = usb_gadget_check_config(cdev->gadget);
|
||||
if (ret)
|
||||
goto err_purge_funcs;
|
||||
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
}
|
||||
if (cdev->use_os_string) {
|
||||
|
|
|
@ -2081,7 +2081,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
|||
break;
|
||||
|
||||
case USB_TYPE_CLASS | 0x01:
|
||||
if (*current_class == USB_INTERFACE_CLASS_HID) {
|
||||
if (*current_class == USB_INTERFACE_CLASS_HID) {
|
||||
pr_vdebug("hid descriptor\n");
|
||||
if (length != sizeof(struct hid_descriptor))
|
||||
goto inv_length;
|
||||
|
@ -3831,7 +3831,7 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len)
|
|||
|
||||
data = memdup_user(buf, len);
|
||||
if (IS_ERR(data))
|
||||
return ERR_PTR(PTR_ERR(data));
|
||||
return data;
|
||||
|
||||
pr_vdebug("Buffer from user space:\n");
|
||||
ffs_dump_mem("", data, len);
|
||||
|
|
|
@ -45,12 +45,25 @@ struct f_hidg {
|
|||
unsigned short report_desc_length;
|
||||
char *report_desc;
|
||||
unsigned short report_length;
|
||||
/*
|
||||
* use_out_ep - if true, the OUT Endpoint (interrupt out method)
|
||||
* will be used to receive reports from the host
|
||||
* using functions with the "intout" suffix.
|
||||
* Otherwise, the OUT Endpoint will not be configured
|
||||
* and the SETUP/SET_REPORT method ("ssreport" suffix)
|
||||
* will be used to receive reports.
|
||||
*/
|
||||
bool use_out_ep;
|
||||
|
||||
/* recv report */
|
||||
struct list_head completed_out_req;
|
||||
spinlock_t read_spinlock;
|
||||
wait_queue_head_t read_queue;
|
||||
/* recv report - interrupt out only (use_out_ep == 1) */
|
||||
struct list_head completed_out_req;
|
||||
unsigned int qlen;
|
||||
/* recv report - setup set_report only (use_out_ep == 0) */
|
||||
char *set_report_buf;
|
||||
unsigned int set_report_length;
|
||||
|
||||
/* send report */
|
||||
spinlock_t write_spinlock;
|
||||
|
@ -79,7 +92,7 @@ static struct usb_interface_descriptor hidg_interface_desc = {
|
|||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
/* .bNumEndpoints = DYNAMIC (depends on use_out_ep) */
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
/* .bInterfaceSubClass = DYNAMIC */
|
||||
/* .bInterfaceProtocol = DYNAMIC */
|
||||
|
@ -140,7 +153,7 @@ static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
|
|||
/* .wBytesPerInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_ss_descriptors[] = {
|
||||
static struct usb_descriptor_header *hidg_ss_descriptors_intout[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_ss_in_ep_desc,
|
||||
|
@ -150,6 +163,14 @@ static struct usb_descriptor_header *hidg_ss_descriptors[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_ss_descriptors_ssreport[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_ss_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&hidg_ss_in_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* High-Speed Support */
|
||||
|
||||
static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
|
||||
|
@ -176,7 +197,7 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
|
|||
*/
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_hs_descriptors[] = {
|
||||
static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
|
||||
|
@ -184,6 +205,13 @@ static struct usb_descriptor_header *hidg_hs_descriptors[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_hs_descriptors_ssreport[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Full-Speed Support */
|
||||
|
||||
static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
|
||||
|
@ -210,7 +238,7 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
|
|||
*/
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
||||
static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
|
||||
|
@ -218,6 +246,13 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_fs_descriptors_ssreport[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Strings */
|
||||
|
||||
|
@ -241,8 +276,8 @@ static struct usb_gadget_strings *ct_func_strings[] = {
|
|||
/*-------------------------------------------------------------------------*/
|
||||
/* Char Device */
|
||||
|
||||
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
struct f_hidg_req_list *list;
|
||||
|
@ -255,15 +290,15 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
|||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
|
||||
#define READ_COND (!list_empty(&hidg->completed_out_req))
|
||||
#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req))
|
||||
|
||||
/* wait for at least one buffer to complete */
|
||||
while (!READ_COND) {
|
||||
while (!READ_COND_INTOUT) {
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
if (wait_event_interruptible(hidg->read_queue, READ_COND))
|
||||
if (wait_event_interruptible(hidg->read_queue, READ_COND_INTOUT))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
|
@ -313,6 +348,60 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
|||
return count;
|
||||
}
|
||||
|
||||
#define READ_COND_SSREPORT (hidg->set_report_buf != NULL)
|
||||
|
||||
static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
char *tmp_buf = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
|
||||
while (!READ_COND_SSREPORT) {
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
if (wait_event_interruptible(hidg->read_queue, READ_COND_SSREPORT))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
}
|
||||
|
||||
count = min_t(unsigned int, count, hidg->set_report_length);
|
||||
tmp_buf = hidg->set_report_buf;
|
||||
hidg->set_report_buf = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
|
||||
if (tmp_buf != NULL) {
|
||||
count -= copy_to_user(buffer, tmp_buf, count);
|
||||
kfree(tmp_buf);
|
||||
} else {
|
||||
count = -ENOMEM;
|
||||
}
|
||||
|
||||
wake_up(&hidg->read_queue);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
|
||||
if (hidg->use_out_ep)
|
||||
return f_hidg_intout_read(file, buffer, count, ptr);
|
||||
else
|
||||
return f_hidg_ssreport_read(file, buffer, count, ptr);
|
||||
}
|
||||
|
||||
static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
|
||||
|
@ -433,14 +522,20 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
|
|||
if (WRITE_COND)
|
||||
ret |= EPOLLOUT | EPOLLWRNORM;
|
||||
|
||||
if (READ_COND)
|
||||
ret |= EPOLLIN | EPOLLRDNORM;
|
||||
if (hidg->use_out_ep) {
|
||||
if (READ_COND_INTOUT)
|
||||
ret |= EPOLLIN | EPOLLRDNORM;
|
||||
} else {
|
||||
if (READ_COND_SSREPORT)
|
||||
ret |= EPOLLIN | EPOLLRDNORM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef WRITE_COND
|
||||
#undef READ_COND
|
||||
#undef READ_COND_SSREPORT
|
||||
#undef READ_COND_INTOUT
|
||||
|
||||
static int f_hidg_release(struct inode *inode, struct file *fd)
|
||||
{
|
||||
|
@ -467,7 +562,7 @@ static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
|
|||
return alloc_ep_req(ep, length);
|
||||
}
|
||||
|
||||
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
static void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *) req->context;
|
||||
struct usb_composite_dev *cdev = hidg->func.config->cdev;
|
||||
|
@ -502,6 +597,37 @@ free_req:
|
|||
}
|
||||
}
|
||||
|
||||
static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)req->context;
|
||||
struct usb_composite_dev *cdev = hidg->func.config->cdev;
|
||||
char *new_buf = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (req->status != 0 || req->buf == NULL || req->actual == 0) {
|
||||
ERROR(cdev,
|
||||
"%s FAILED: status=%d, buf=%p, actual=%d\n",
|
||||
__func__, req->status, req->buf, req->actual);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
|
||||
new_buf = krealloc(hidg->set_report_buf, req->actual, GFP_ATOMIC);
|
||||
if (new_buf == NULL) {
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
return;
|
||||
}
|
||||
hidg->set_report_buf = new_buf;
|
||||
|
||||
hidg->set_report_length = req->actual;
|
||||
memcpy(hidg->set_report_buf, req->buf, req->actual);
|
||||
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
|
||||
wake_up(&hidg->read_queue);
|
||||
}
|
||||
|
||||
static int hidg_setup(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
|
@ -549,7 +675,11 @@ static int hidg_setup(struct usb_function *f,
|
|||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_SET_REPORT):
|
||||
VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength);
|
||||
goto stall;
|
||||
if (hidg->use_out_ep)
|
||||
goto stall;
|
||||
req->complete = hidg_ssreport_complete;
|
||||
req->context = hidg;
|
||||
goto respond;
|
||||
break;
|
||||
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
|
@ -637,15 +767,18 @@ static void hidg_disable(struct usb_function *f)
|
|||
unsigned long flags;
|
||||
|
||||
usb_ep_disable(hidg->in_ep);
|
||||
usb_ep_disable(hidg->out_ep);
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
|
||||
free_ep_req(hidg->out_ep, list->req);
|
||||
list_del(&list->list);
|
||||
kfree(list);
|
||||
if (hidg->out_ep) {
|
||||
usb_ep_disable(hidg->out_ep);
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
|
||||
free_ep_req(hidg->out_ep, list->req);
|
||||
list_del(&list->list);
|
||||
kfree(list);
|
||||
}
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
if (!hidg->write_pending) {
|
||||
|
@ -691,8 +824,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (hidg->out_ep != NULL) {
|
||||
if (hidg->use_out_ep && hidg->out_ep != NULL) {
|
||||
/* restart endpoint */
|
||||
usb_ep_disable(hidg->out_ep);
|
||||
|
||||
|
@ -717,7 +849,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
hidg_alloc_ep_req(hidg->out_ep,
|
||||
hidg->report_length);
|
||||
if (req) {
|
||||
req->complete = hidg_set_report_complete;
|
||||
req->complete = hidg_intout_complete;
|
||||
req->context = hidg;
|
||||
status = usb_ep_queue(hidg->out_ep, req,
|
||||
GFP_ATOMIC);
|
||||
|
@ -743,7 +875,8 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
}
|
||||
return 0;
|
||||
disable_out_ep:
|
||||
usb_ep_disable(hidg->out_ep);
|
||||
if (hidg->out_ep)
|
||||
usb_ep_disable(hidg->out_ep);
|
||||
free_req_in:
|
||||
if (req_in)
|
||||
free_ep_req(hidg->in_ep, req_in);
|
||||
|
@ -795,14 +928,21 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
goto fail;
|
||||
hidg->in_ep = ep;
|
||||
|
||||
ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
hidg->out_ep = ep;
|
||||
hidg->out_ep = NULL;
|
||||
if (hidg->use_out_ep) {
|
||||
ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
hidg->out_ep = ep;
|
||||
}
|
||||
|
||||
/* used only if use_out_ep == 1 */
|
||||
hidg->set_report_buf = NULL;
|
||||
|
||||
/* set descriptor dynamic values */
|
||||
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
|
||||
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
|
||||
hidg_interface_desc.bNumEndpoints = hidg->use_out_ep ? 2 : 1;
|
||||
hidg->protocol = HID_REPORT_PROTOCOL;
|
||||
hidg->idle = 1;
|
||||
hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
|
@ -833,9 +973,19 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
hidg_ss_out_ep_desc.bEndpointAddress =
|
||||
hidg_fs_out_ep_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, hidg_fs_descriptors,
|
||||
hidg_hs_descriptors, hidg_ss_descriptors,
|
||||
hidg_ss_descriptors);
|
||||
if (hidg->use_out_ep)
|
||||
status = usb_assign_descriptors(f,
|
||||
hidg_fs_descriptors_intout,
|
||||
hidg_hs_descriptors_intout,
|
||||
hidg_ss_descriptors_intout,
|
||||
hidg_ss_descriptors_intout);
|
||||
else
|
||||
status = usb_assign_descriptors(f,
|
||||
hidg_fs_descriptors_ssreport,
|
||||
hidg_hs_descriptors_ssreport,
|
||||
hidg_ss_descriptors_ssreport,
|
||||
hidg_ss_descriptors_ssreport);
|
||||
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
@ -950,6 +1100,7 @@ CONFIGFS_ATTR(f_hid_opts_, name)
|
|||
|
||||
F_HID_OPT(subclass, 8, 255);
|
||||
F_HID_OPT(protocol, 8, 255);
|
||||
F_HID_OPT(no_out_endpoint, 8, 1);
|
||||
F_HID_OPT(report_length, 16, 65535);
|
||||
|
||||
static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page)
|
||||
|
@ -1009,6 +1160,7 @@ CONFIGFS_ATTR_RO(f_hid_opts_, dev);
|
|||
static struct configfs_attribute *hid_attrs[] = {
|
||||
&f_hid_opts_attr_subclass,
|
||||
&f_hid_opts_attr_protocol,
|
||||
&f_hid_opts_attr_no_out_endpoint,
|
||||
&f_hid_opts_attr_report_length,
|
||||
&f_hid_opts_attr_report_desc,
|
||||
&f_hid_opts_attr_dev,
|
||||
|
@ -1093,6 +1245,7 @@ static void hidg_free(struct usb_function *f)
|
|||
hidg = func_to_hidg(f);
|
||||
opts = container_of(f->fi, struct f_hid_opts, func_inst);
|
||||
kfree(hidg->report_desc);
|
||||
kfree(hidg->set_report_buf);
|
||||
kfree(hidg);
|
||||
mutex_lock(&opts->lock);
|
||||
--opts->refcnt;
|
||||
|
@ -1139,6 +1292,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
hidg->use_out_ep = !opts->no_out_endpoint;
|
||||
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
|
|
|
@ -6,36 +6,6 @@
|
|||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <mina86@mina86.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
|
@ -72,9 +72,7 @@ struct f_ncm {
|
|||
struct sk_buff *skb_tx_data;
|
||||
struct sk_buff *skb_tx_ndp;
|
||||
u16 ndp_dgram_count;
|
||||
bool timer_force_tx;
|
||||
struct hrtimer task_timer;
|
||||
bool timer_stopping;
|
||||
};
|
||||
|
||||
static inline struct f_ncm *func_to_ncm(struct usb_function *f)
|
||||
|
@ -890,7 +888,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
|
||||
if (ncm->port.in_ep->enabled) {
|
||||
DBG(cdev, "reset ncm\n");
|
||||
ncm->timer_stopping = true;
|
||||
ncm->netdev = NULL;
|
||||
gether_disconnect(&ncm->port);
|
||||
ncm_reset_values(ncm);
|
||||
|
@ -928,7 +925,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
if (IS_ERR(net))
|
||||
return PTR_ERR(net);
|
||||
ncm->netdev = net;
|
||||
ncm->timer_stopping = false;
|
||||
}
|
||||
|
||||
spin_lock(&ncm->lock);
|
||||
|
@ -1017,22 +1013,20 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
|||
{
|
||||
struct f_ncm *ncm = func_to_ncm(&port->func);
|
||||
struct sk_buff *skb2 = NULL;
|
||||
int ncb_len = 0;
|
||||
__le16 *ntb_data;
|
||||
__le16 *ntb_ndp;
|
||||
int dgram_pad;
|
||||
|
||||
unsigned max_size = ncm->port.fixed_in_len;
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
|
||||
const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
|
||||
const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
|
||||
const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
|
||||
|
||||
if (!skb && !ncm->skb_tx_data)
|
||||
return NULL;
|
||||
|
||||
if (skb) {
|
||||
int ncb_len = 0;
|
||||
__le16 *ntb_data;
|
||||
__le16 *ntb_ndp;
|
||||
int dgram_pad;
|
||||
|
||||
unsigned max_size = ncm->port.fixed_in_len;
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
|
||||
const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
|
||||
const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
|
||||
const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
|
||||
|
||||
/* Add the CRC if required up front */
|
||||
if (ncm->is_crc) {
|
||||
uint32_t crc;
|
||||
|
@ -1126,8 +1120,11 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
|||
dev_consume_skb_any(skb);
|
||||
skb = NULL;
|
||||
|
||||
} else if (ncm->skb_tx_data && ncm->timer_force_tx) {
|
||||
/* If the tx was requested because of a timeout then send */
|
||||
} else if (ncm->skb_tx_data) {
|
||||
/* If we get here ncm_wrap_ntb() was called with NULL skb,
|
||||
* because eth_start_xmit() was called with NULL skb by
|
||||
* ncm_tx_timeout() - hence, this is our signal to flush/send.
|
||||
*/
|
||||
skb2 = package_for_tx(ncm);
|
||||
if (!skb2)
|
||||
goto err;
|
||||
|
@ -1155,20 +1152,18 @@ err:
|
|||
static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
|
||||
{
|
||||
struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
|
||||
struct net_device *netdev = READ_ONCE(ncm->netdev);
|
||||
|
||||
/* Only send if data is available. */
|
||||
if (!ncm->timer_stopping && ncm->skb_tx_data) {
|
||||
ncm->timer_force_tx = true;
|
||||
|
||||
if (netdev) {
|
||||
/* XXX This allowance of a NULL skb argument to ndo_start_xmit
|
||||
* XXX is not sane. The gadget layer should be redesigned so
|
||||
* XXX that the dev->wrap() invocations to build SKBs is transparent
|
||||
* XXX and performed in some way outside of the ndo_start_xmit
|
||||
* XXX interface.
|
||||
*
|
||||
* This will call directly into u_ether's eth_start_xmit()
|
||||
*/
|
||||
ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
|
||||
|
||||
ncm->timer_force_tx = false;
|
||||
netdev->netdev_ops->ndo_start_xmit(NULL, netdev);
|
||||
}
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
@ -1357,7 +1352,6 @@ static void ncm_disable(struct usb_function *f)
|
|||
DBG(cdev, "ncm deactivated\n");
|
||||
|
||||
if (ncm->port.in_ep->enabled) {
|
||||
ncm->timer_stopping = true;
|
||||
ncm->netdev = NULL;
|
||||
gether_disconnect(&ncm->port);
|
||||
}
|
||||
|
|
|
@ -22,13 +22,26 @@
|
|||
/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
|
||||
#define UAC1_CHANNEL_MASK 0x0FFF
|
||||
|
||||
#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID)
|
||||
#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID)
|
||||
|
||||
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
|
||||
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
|
||||
#define FUIN_EN(_opts) ((_opts)->p_mute_present \
|
||||
|| (_opts)->p_volume_present)
|
||||
#define FUOUT_EN(_opts) ((_opts)->c_mute_present \
|
||||
|| (_opts)->c_volume_present)
|
||||
|
||||
struct f_uac1 {
|
||||
struct g_audio g_audio;
|
||||
u8 ac_intf, as_in_intf, as_out_intf;
|
||||
u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
|
||||
|
||||
struct usb_ctrlrequest setup_cr; /* will be used in data stage */
|
||||
|
||||
/* Interrupt IN endpoint of AC interface */
|
||||
struct usb_ep *int_ep;
|
||||
atomic_t int_count;
|
||||
};
|
||||
|
||||
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
|
||||
|
@ -58,7 +71,7 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio)
|
|||
static struct usb_interface_descriptor ac_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bNumEndpoints = 0,
|
||||
/* .bNumEndpoints = DYNAMIC */
|
||||
.bInterfaceClass = USB_CLASS_AUDIO,
|
||||
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
|
||||
};
|
||||
|
@ -106,6 +119,19 @@ static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
|
|||
/* .bSourceID = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct uac_feature_unit_descriptor *in_feature_unit_desc;
|
||||
static struct uac_feature_unit_descriptor *out_feature_unit_desc;
|
||||
|
||||
/* AC IN Interrupt Endpoint */
|
||||
static struct usb_endpoint_descriptor ac_int_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(2),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
/* B.4.1 Standard AS Interface Descriptor */
|
||||
static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
|
@ -232,8 +258,13 @@ static struct usb_descriptor_header *f_audio_desc[] = {
|
|||
|
||||
(struct usb_descriptor_header *)&usb_out_it_desc,
|
||||
(struct usb_descriptor_header *)&io_out_ot_desc,
|
||||
(struct usb_descriptor_header *)&out_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&io_in_it_desc,
|
||||
(struct usb_descriptor_header *)&usb_in_ot_desc,
|
||||
(struct usb_descriptor_header *)&in_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&ac_int_ep_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
|
||||
|
@ -263,6 +294,8 @@ enum {
|
|||
STR_IO_IN_IT,
|
||||
STR_IO_IN_IT_CH_NAMES,
|
||||
STR_USB_IN_OT,
|
||||
STR_FU_IN,
|
||||
STR_FU_OUT,
|
||||
STR_AS_OUT_IF_ALT0,
|
||||
STR_AS_OUT_IF_ALT1,
|
||||
STR_AS_IN_IF_ALT0,
|
||||
|
@ -277,6 +310,8 @@ static struct usb_string strings_uac1[] = {
|
|||
[STR_IO_IN_IT].s = "Capture Input terminal",
|
||||
[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
|
||||
[STR_USB_IN_OT].s = "Capture Output terminal",
|
||||
[STR_FU_IN].s = "Capture Volume",
|
||||
[STR_FU_OUT].s = "Playback Volume",
|
||||
[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
|
||||
[STR_AS_OUT_IF_ALT1].s = "Playback Active",
|
||||
[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
|
||||
|
@ -298,6 +333,376 @@ static struct usb_gadget_strings *uac1_strings[] = {
|
|||
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
|
||||
*/
|
||||
|
||||
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
|
||||
{
|
||||
struct g_audio *audio = req->context;
|
||||
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
|
||||
|
||||
atomic_dec(&uac1->int_count);
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(_ep, req);
|
||||
}
|
||||
|
||||
static int audio_notify(struct g_audio *audio, int unit_id, int cs)
|
||||
{
|
||||
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
|
||||
struct usb_request *req;
|
||||
struct uac1_status_word *msg;
|
||||
int ret;
|
||||
|
||||
if (!uac1->int_ep->enabled)
|
||||
return 0;
|
||||
|
||||
if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) {
|
||||
atomic_dec(&uac1->int_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC);
|
||||
if (req == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_dec_int_count;
|
||||
}
|
||||
|
||||
msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
|
||||
if (msg == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_request;
|
||||
}
|
||||
|
||||
msg->bStatusType = UAC1_STATUS_TYPE_IRQ_PENDING
|
||||
| UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF;
|
||||
msg->bOriginator = unit_id;
|
||||
|
||||
req->length = sizeof(*msg);
|
||||
req->buf = msg;
|
||||
req->context = audio;
|
||||
req->complete = audio_notify_complete;
|
||||
|
||||
ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC);
|
||||
|
||||
if (ret)
|
||||
goto err_free_msg;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_msg:
|
||||
kfree(msg);
|
||||
err_free_request:
|
||||
usb_ep_free_request(uac1->int_ep, req);
|
||||
err_dec_int_count:
|
||||
atomic_dec(&uac1->int_count);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct g_audio *audio = func_to_g_audio(fn);
|
||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
|
||||
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
unsigned int is_playback = 0;
|
||||
|
||||
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
|
||||
is_playback = 1;
|
||||
|
||||
if (control_selector == UAC_FU_MUTE) {
|
||||
unsigned int mute;
|
||||
|
||||
u_audio_get_mute(audio, is_playback, &mute);
|
||||
|
||||
*(u8 *)req->buf = mute;
|
||||
value = min_t(unsigned int, w_length, 1);
|
||||
} else if (control_selector == UAC_FU_VOLUME) {
|
||||
__le16 c;
|
||||
s16 volume;
|
||||
|
||||
u_audio_get_volume(audio, is_playback, &volume);
|
||||
|
||||
c = cpu_to_le16(volume);
|
||||
|
||||
value = min_t(unsigned int, w_length, sizeof(c));
|
||||
memcpy(req->buf, &c, value);
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, control_selector);
|
||||
}
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d entity_id=%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, entity_id, control_selector);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct g_audio *audio = func_to_g_audio(fn);
|
||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
|
||||
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
unsigned int is_playback = 0;
|
||||
|
||||
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
|
||||
is_playback = 1;
|
||||
|
||||
if (control_selector == UAC_FU_VOLUME) {
|
||||
__le16 r;
|
||||
s16 min_db;
|
||||
|
||||
if (is_playback)
|
||||
min_db = opts->p_volume_min;
|
||||
else
|
||||
min_db = opts->c_volume_min;
|
||||
|
||||
r = cpu_to_le16(min_db);
|
||||
|
||||
value = min_t(unsigned int, w_length, sizeof(r));
|
||||
memcpy(req->buf, &r, value);
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, control_selector);
|
||||
}
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d entity_id=%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, entity_id, control_selector);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct g_audio *audio = func_to_g_audio(fn);
|
||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
|
||||
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
unsigned int is_playback = 0;
|
||||
|
||||
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
|
||||
is_playback = 1;
|
||||
|
||||
if (control_selector == UAC_FU_VOLUME) {
|
||||
__le16 r;
|
||||
s16 max_db;
|
||||
|
||||
if (is_playback)
|
||||
max_db = opts->p_volume_max;
|
||||
else
|
||||
max_db = opts->c_volume_max;
|
||||
|
||||
r = cpu_to_le16(max_db);
|
||||
|
||||
value = min_t(unsigned int, w_length, sizeof(r));
|
||||
memcpy(req->buf, &r, value);
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, control_selector);
|
||||
}
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d entity_id=%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, entity_id, control_selector);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct g_audio *audio = func_to_g_audio(fn);
|
||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
|
||||
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
unsigned int is_playback = 0;
|
||||
|
||||
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
|
||||
is_playback = 1;
|
||||
|
||||
if (control_selector == UAC_FU_VOLUME) {
|
||||
__le16 r;
|
||||
s16 res_db;
|
||||
|
||||
if (is_playback)
|
||||
res_db = opts->p_volume_res;
|
||||
else
|
||||
res_db = opts->c_volume_res;
|
||||
|
||||
r = cpu_to_le16(res_db);
|
||||
|
||||
value = min_t(unsigned int, w_length, sizeof(r));
|
||||
memcpy(req->buf, &r, value);
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, control_selector);
|
||||
}
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d entity_id=%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, entity_id, control_selector);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct g_audio *audio = req->context;
|
||||
struct usb_composite_dev *cdev = audio->func.config->cdev;
|
||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
|
||||
struct usb_ctrlrequest *cr = &uac1->setup_cr;
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
|
||||
if (req->status != 0) {
|
||||
dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
unsigned int is_playback = 0;
|
||||
|
||||
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
|
||||
is_playback = 1;
|
||||
|
||||
if (control_selector == UAC_FU_MUTE) {
|
||||
u8 mute = *(u8 *)req->buf;
|
||||
|
||||
u_audio_set_mute(audio, is_playback, mute);
|
||||
|
||||
return;
|
||||
} else if (control_selector == UAC_FU_VOLUME) {
|
||||
__le16 *c = req->buf;
|
||||
s16 volume;
|
||||
|
||||
volume = le16_to_cpu(*c);
|
||||
u_audio_set_volume(audio, is_playback, volume);
|
||||
|
||||
return;
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, control_selector);
|
||||
usb_ep_set_halt(ep);
|
||||
}
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d entity_id=%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, entity_id, control_selector);
|
||||
usb_ep_set_halt(ep);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct g_audio *audio = func_to_g_audio(fn);
|
||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
|
||||
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
memcpy(&uac1->setup_cr, cr, sizeof(*cr));
|
||||
req->context = audio;
|
||||
req->complete = out_rq_cur_complete;
|
||||
|
||||
return w_length;
|
||||
} else {
|
||||
dev_err(&audio->gadget->dev,
|
||||
"%s:%d entity_id=%d control_selector=%d TODO!\n",
|
||||
__func__, __LINE__, entity_id, control_selector);
|
||||
}
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int ac_rq_in(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
int value = -EOPNOTSUPP;
|
||||
u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
|
||||
ctrl->bRequest, w_value, len, ep);
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case UAC_GET_CUR:
|
||||
return in_rq_cur(f, ctrl);
|
||||
case UAC_GET_MIN:
|
||||
return in_rq_min(f, ctrl);
|
||||
case UAC_GET_MAX:
|
||||
return in_rq_max(f, ctrl);
|
||||
case UAC_GET_RES:
|
||||
return in_rq_res(f, ctrl);
|
||||
case UAC_GET_MEM:
|
||||
break;
|
||||
case UAC_GET_STAT:
|
||||
value = len;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int audio_set_endpoint_req(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
|
@ -383,7 +788,13 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
|||
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
|
||||
value = audio_get_endpoint_req(f, ctrl);
|
||||
break;
|
||||
|
||||
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
|
||||
if (ctrl->bRequest == UAC_SET_CUR)
|
||||
value = out_rq_cur(f, ctrl);
|
||||
break;
|
||||
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
|
||||
value = ac_rq_in(f, ctrl);
|
||||
break;
|
||||
default:
|
||||
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
|
@ -411,6 +822,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct device *dev = &gadget->dev;
|
||||
struct g_audio *audio = func_to_g_audio(f);
|
||||
struct f_uac1 *uac1 = func_to_uac1(f);
|
||||
int ret = 0;
|
||||
|
||||
|
@ -426,6 +838,14 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* restart interrupt endpoint */
|
||||
if (uac1->int_ep) {
|
||||
usb_ep_disable(uac1->int_ep);
|
||||
config_ep_by_speed(gadget, &audio->func, uac1->int_ep);
|
||||
usb_ep_enable(uac1->int_ep);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -481,10 +901,33 @@ static void f_audio_disable(struct usb_function *f)
|
|||
|
||||
u_audio_stop_playback(&uac1->g_audio);
|
||||
u_audio_stop_capture(&uac1->g_audio);
|
||||
if (uac1->int_ep)
|
||||
usb_ep_disable(uac1->int_ep);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
|
||||
{
|
||||
struct uac_feature_unit_descriptor *fu_desc;
|
||||
int channels = num_channels(chmask);
|
||||
int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels);
|
||||
|
||||
fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
|
||||
if (!fu_desc)
|
||||
return NULL;
|
||||
|
||||
fu_desc->bLength = fu_desc_size;
|
||||
fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
|
||||
|
||||
fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
|
||||
fu_desc->bControlSize = 2;
|
||||
|
||||
/* bUnitID, bSourceID and bmaControls will be defined later */
|
||||
|
||||
return fu_desc;
|
||||
}
|
||||
|
||||
/* B.3.2 Class-Specific AC Interface Descriptor */
|
||||
static struct
|
||||
uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
|
||||
{
|
||||
|
@ -530,9 +973,23 @@ static void setup_descriptor(struct f_uac1_opts *opts)
|
|||
io_out_ot_desc.bTerminalID = i++;
|
||||
if (EPIN_EN(opts))
|
||||
usb_in_ot_desc.bTerminalID = i++;
|
||||
if (FUOUT_EN(opts))
|
||||
out_feature_unit_desc->bUnitID = i++;
|
||||
if (FUIN_EN(opts))
|
||||
in_feature_unit_desc->bUnitID = i++;
|
||||
|
||||
usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
|
||||
io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
|
||||
if (FUIN_EN(opts)) {
|
||||
usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID;
|
||||
in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID;
|
||||
} else {
|
||||
usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
|
||||
}
|
||||
if (FUOUT_EN(opts)) {
|
||||
io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID;
|
||||
out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID;
|
||||
} else {
|
||||
io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
|
||||
}
|
||||
|
||||
as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
|
||||
as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
|
||||
|
@ -544,6 +1001,8 @@ static void setup_descriptor(struct f_uac1_opts *opts)
|
|||
|
||||
len += sizeof(usb_in_ot_desc);
|
||||
len += sizeof(io_in_it_desc);
|
||||
if (FUIN_EN(opts))
|
||||
len += in_feature_unit_desc->bLength;
|
||||
ac_header_desc->wTotalLength = cpu_to_le16(len);
|
||||
}
|
||||
if (EPOUT_EN(opts)) {
|
||||
|
@ -551,6 +1010,8 @@ static void setup_descriptor(struct f_uac1_opts *opts)
|
|||
|
||||
len += sizeof(usb_out_it_desc);
|
||||
len += sizeof(io_out_ot_desc);
|
||||
if (FUOUT_EN(opts))
|
||||
len += out_feature_unit_desc->bLength;
|
||||
ac_header_desc->wTotalLength = cpu_to_le16(len);
|
||||
}
|
||||
|
||||
|
@ -561,13 +1022,20 @@ static void setup_descriptor(struct f_uac1_opts *opts)
|
|||
if (EPOUT_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
|
||||
if (FUOUT_EN(opts))
|
||||
f_audio_desc[i++] = USBDHDR(out_feature_unit_desc);
|
||||
}
|
||||
|
||||
if (EPIN_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&io_in_it_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
|
||||
if (FUIN_EN(opts))
|
||||
f_audio_desc[i++] = USBDHDR(in_feature_unit_desc);
|
||||
}
|
||||
|
||||
if (FUOUT_EN(opts) || FUIN_EN(opts))
|
||||
f_audio_desc[i++] = USBDHDR(&ac_int_ep_desc);
|
||||
|
||||
if (EPOUT_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc);
|
||||
|
@ -614,6 +1082,28 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (opts->p_volume_max <= opts->p_volume_min) {
|
||||
dev_err(dev, "Error: incorrect playback volume max/min\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->c_volume_max <= opts->c_volume_min) {
|
||||
dev_err(dev, "Error: incorrect capture volume max/min\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->p_volume_res <= 0) {
|
||||
dev_err(dev, "Error: negative/zero playback volume resolution\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->c_volume_res <= 0) {
|
||||
dev_err(dev, "Error: negative/zero capture volume resolution\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
|
||||
dev_err(dev, "Error: incorrect playback volume resolution\n");
|
||||
return -EINVAL;
|
||||
} else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
|
||||
dev_err(dev, "Error: incorrect capture volume resolution\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -647,6 +1137,21 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (!ac_header_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
if (FUOUT_EN(audio_opts)) {
|
||||
out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask);
|
||||
if (!out_feature_unit_desc) {
|
||||
status = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (FUIN_EN(audio_opts)) {
|
||||
in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask);
|
||||
if (!in_feature_unit_desc) {
|
||||
status = -ENOMEM;
|
||||
goto err_free_fu;
|
||||
}
|
||||
}
|
||||
|
||||
ac_interface_desc.iInterface = us[STR_AC_IF].id;
|
||||
usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
|
||||
usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
|
||||
|
@ -659,6 +1164,21 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
|
||||
as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
|
||||
|
||||
if (FUOUT_EN(audio_opts)) {
|
||||
u8 *i_feature;
|
||||
|
||||
i_feature = (u8 *)out_feature_unit_desc +
|
||||
out_feature_unit_desc->bLength - 1;
|
||||
*i_feature = us[STR_FU_OUT].id;
|
||||
}
|
||||
if (FUIN_EN(audio_opts)) {
|
||||
u8 *i_feature;
|
||||
|
||||
i_feature = (u8 *)in_feature_unit_desc +
|
||||
in_feature_unit_desc->bLength - 1;
|
||||
*i_feature = us[STR_FU_IN].id;
|
||||
}
|
||||
|
||||
/* Set channel numbers */
|
||||
usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
|
||||
usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
|
||||
|
@ -671,6 +1191,27 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
|
||||
as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
|
||||
|
||||
if (FUOUT_EN(audio_opts)) {
|
||||
__le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0];
|
||||
u32 control = 0;
|
||||
|
||||
if (audio_opts->c_mute_present)
|
||||
control |= UAC_FU_MUTE;
|
||||
if (audio_opts->c_volume_present)
|
||||
control |= UAC_FU_VOLUME;
|
||||
*bma = cpu_to_le16(control);
|
||||
}
|
||||
if (FUIN_EN(audio_opts)) {
|
||||
__le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0];
|
||||
u32 control = 0;
|
||||
|
||||
if (audio_opts->p_mute_present)
|
||||
control |= UAC_FU_MUTE;
|
||||
if (audio_opts->p_volume_present)
|
||||
control |= UAC_FU_VOLUME;
|
||||
*bma = cpu_to_le16(control);
|
||||
}
|
||||
|
||||
/* Set sample rates */
|
||||
rate = audio_opts->c_srate;
|
||||
sam_freq = as_out_type_i_desc.tSamFreq[0];
|
||||
|
@ -682,7 +1223,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto err_free_fu;
|
||||
ac_interface_desc.bInterfaceNumber = status;
|
||||
uac1->ac_intf = status;
|
||||
uac1->ac_alt = 0;
|
||||
|
@ -692,7 +1233,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (EPOUT_EN(audio_opts)) {
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto err_free_fu;
|
||||
as_out_interface_alt_0_desc.bInterfaceNumber = status;
|
||||
as_out_interface_alt_1_desc.bInterfaceNumber = status;
|
||||
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
|
||||
|
@ -703,7 +1244,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (EPIN_EN(audio_opts)) {
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto err_free_fu;
|
||||
as_in_interface_alt_0_desc.bInterfaceNumber = status;
|
||||
as_in_interface_alt_1_desc.bInterfaceNumber = status;
|
||||
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
|
||||
|
@ -715,11 +1256,24 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
status = -ENODEV;
|
||||
|
||||
ac_interface_desc.bNumEndpoints = 0;
|
||||
|
||||
/* allocate AC interrupt endpoint */
|
||||
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) {
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc);
|
||||
if (!ep)
|
||||
goto err_free_fu;
|
||||
uac1->int_ep = ep;
|
||||
uac1->int_ep->desc = &ac_int_ep_desc;
|
||||
|
||||
ac_interface_desc.bNumEndpoints = 1;
|
||||
}
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
if (EPOUT_EN(audio_opts)) {
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
goto err_free_fu;
|
||||
audio->out_ep = ep;
|
||||
audio->out_ep->desc = &as_out_ep_desc;
|
||||
}
|
||||
|
@ -727,7 +1281,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (EPIN_EN(audio_opts)) {
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
goto err_free_fu;
|
||||
audio->in_ep = ep;
|
||||
audio->in_ep->desc = &as_in_ep_desc;
|
||||
}
|
||||
|
@ -738,17 +1292,37 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
|
||||
NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
goto err_free_fu;
|
||||
|
||||
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
|
||||
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
|
||||
audio->params.c_chmask = audio_opts->c_chmask;
|
||||
audio->params.c_srate = audio_opts->c_srate;
|
||||
audio->params.c_ssize = audio_opts->c_ssize;
|
||||
if (FUIN_EN(audio_opts)) {
|
||||
audio->params.p_fu.id = USB_IN_FU_ID;
|
||||
audio->params.p_fu.mute_present = audio_opts->p_mute_present;
|
||||
audio->params.p_fu.volume_present =
|
||||
audio_opts->p_volume_present;
|
||||
audio->params.p_fu.volume_min = audio_opts->p_volume_min;
|
||||
audio->params.p_fu.volume_max = audio_opts->p_volume_max;
|
||||
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
|
||||
}
|
||||
audio->params.p_chmask = audio_opts->p_chmask;
|
||||
audio->params.p_srate = audio_opts->p_srate;
|
||||
audio->params.p_ssize = audio_opts->p_ssize;
|
||||
if (FUOUT_EN(audio_opts)) {
|
||||
audio->params.c_fu.id = USB_OUT_FU_ID;
|
||||
audio->params.c_fu.mute_present = audio_opts->c_mute_present;
|
||||
audio->params.c_fu.volume_present =
|
||||
audio_opts->c_volume_present;
|
||||
audio->params.c_fu.volume_min = audio_opts->c_volume_min;
|
||||
audio->params.c_fu.volume_max = audio_opts->c_volume_max;
|
||||
audio->params.c_fu.volume_res = audio_opts->c_volume_res;
|
||||
}
|
||||
audio->params.req_number = audio_opts->req_number;
|
||||
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts))
|
||||
audio->notify = audio_notify;
|
||||
|
||||
status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
|
||||
if (status)
|
||||
|
@ -758,6 +1332,11 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
err_card_register:
|
||||
usb_free_all_descriptors(f);
|
||||
err_free_fu:
|
||||
kfree(out_feature_unit_desc);
|
||||
out_feature_unit_desc = NULL;
|
||||
kfree(in_feature_unit_desc);
|
||||
in_feature_unit_desc = NULL;
|
||||
fail:
|
||||
kfree(ac_header_desc);
|
||||
ac_header_desc = NULL;
|
||||
|
@ -783,7 +1362,15 @@ static struct configfs_item_operations f_uac1_item_ops = {
|
|||
.release = f_uac1_attr_release,
|
||||
};
|
||||
|
||||
#define UAC1_ATTRIBUTE(name) \
|
||||
#define uac1_kstrtou32 kstrtou32
|
||||
#define uac1_kstrtos16 kstrtos16
|
||||
#define uac1_kstrtobool(s, base, res) kstrtobool((s), (res))
|
||||
|
||||
static const char *u32_fmt = "%u\n";
|
||||
static const char *s16_fmt = "%hd\n";
|
||||
static const char *bool_fmt = "%u\n";
|
||||
|
||||
#define UAC1_ATTRIBUTE(type, name) \
|
||||
static ssize_t f_uac1_opts_##name##_show( \
|
||||
struct config_item *item, \
|
||||
char *page) \
|
||||
|
@ -792,7 +1379,7 @@ static ssize_t f_uac1_opts_##name##_show( \
|
|||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = sprintf(page, "%u\n", opts->name); \
|
||||
result = sprintf(page, type##_fmt, opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
|
@ -804,7 +1391,7 @@ static ssize_t f_uac1_opts_##name##_store( \
|
|||
{ \
|
||||
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
|
||||
int ret; \
|
||||
u32 num; \
|
||||
type num; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
|
@ -812,7 +1399,7 @@ static ssize_t f_uac1_opts_##name##_store( \
|
|||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou32(page, 0, &num); \
|
||||
ret = uac1_kstrto##type(page, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
|
@ -826,13 +1413,25 @@ end: \
|
|||
\
|
||||
CONFIGFS_ATTR(f_uac1_opts_, name)
|
||||
|
||||
UAC1_ATTRIBUTE(c_chmask);
|
||||
UAC1_ATTRIBUTE(c_srate);
|
||||
UAC1_ATTRIBUTE(c_ssize);
|
||||
UAC1_ATTRIBUTE(p_chmask);
|
||||
UAC1_ATTRIBUTE(p_srate);
|
||||
UAC1_ATTRIBUTE(p_ssize);
|
||||
UAC1_ATTRIBUTE(req_number);
|
||||
UAC1_ATTRIBUTE(u32, c_chmask);
|
||||
UAC1_ATTRIBUTE(u32, c_srate);
|
||||
UAC1_ATTRIBUTE(u32, c_ssize);
|
||||
UAC1_ATTRIBUTE(u32, p_chmask);
|
||||
UAC1_ATTRIBUTE(u32, p_srate);
|
||||
UAC1_ATTRIBUTE(u32, p_ssize);
|
||||
UAC1_ATTRIBUTE(u32, req_number);
|
||||
|
||||
UAC1_ATTRIBUTE(bool, p_mute_present);
|
||||
UAC1_ATTRIBUTE(bool, p_volume_present);
|
||||
UAC1_ATTRIBUTE(s16, p_volume_min);
|
||||
UAC1_ATTRIBUTE(s16, p_volume_max);
|
||||
UAC1_ATTRIBUTE(s16, p_volume_res);
|
||||
|
||||
UAC1_ATTRIBUTE(bool, c_mute_present);
|
||||
UAC1_ATTRIBUTE(bool, c_volume_present);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_min);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_max);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_res);
|
||||
|
||||
static struct configfs_attribute *f_uac1_attrs[] = {
|
||||
&f_uac1_opts_attr_c_chmask,
|
||||
|
@ -842,6 +1441,19 @@ static struct configfs_attribute *f_uac1_attrs[] = {
|
|||
&f_uac1_opts_attr_p_srate,
|
||||
&f_uac1_opts_attr_p_ssize,
|
||||
&f_uac1_opts_attr_req_number,
|
||||
|
||||
&f_uac1_opts_attr_p_mute_present,
|
||||
&f_uac1_opts_attr_p_volume_present,
|
||||
&f_uac1_opts_attr_p_volume_min,
|
||||
&f_uac1_opts_attr_p_volume_max,
|
||||
&f_uac1_opts_attr_p_volume_res,
|
||||
|
||||
&f_uac1_opts_attr_c_mute_present,
|
||||
&f_uac1_opts_attr_c_volume_present,
|
||||
&f_uac1_opts_attr_c_volume_min,
|
||||
&f_uac1_opts_attr_c_volume_max,
|
||||
&f_uac1_opts_attr_c_volume_res,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -879,6 +1491,19 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
|
|||
opts->p_chmask = UAC1_DEF_PCHMASK;
|
||||
opts->p_srate = UAC1_DEF_PSRATE;
|
||||
opts->p_ssize = UAC1_DEF_PSSIZE;
|
||||
|
||||
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
|
||||
opts->p_volume_present = UAC1_DEF_VOLUME_PRESENT;
|
||||
opts->p_volume_min = UAC1_DEF_MIN_DB;
|
||||
opts->p_volume_max = UAC1_DEF_MAX_DB;
|
||||
opts->p_volume_res = UAC1_DEF_RES_DB;
|
||||
|
||||
opts->c_mute_present = UAC1_DEF_MUTE_PRESENT;
|
||||
opts->c_volume_present = UAC1_DEF_VOLUME_PRESENT;
|
||||
opts->c_volume_min = UAC1_DEF_MIN_DB;
|
||||
opts->c_volume_max = UAC1_DEF_MAX_DB;
|
||||
opts->c_volume_res = UAC1_DEF_RES_DB;
|
||||
|
||||
opts->req_number = UAC1_DEF_REQ_NUM;
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
@ -903,6 +1528,11 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
g_audio_cleanup(audio);
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(out_feature_unit_desc);
|
||||
out_feature_unit_desc = NULL;
|
||||
kfree(in_feature_unit_desc);
|
||||
in_feature_unit_desc = NULL;
|
||||
|
||||
kfree(ac_header_desc);
|
||||
ac_header_desc = NULL;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -418,6 +418,7 @@ uvc_register_video(struct uvc_device *uvc)
|
|||
|
||||
/* TODO reference counting. */
|
||||
uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
|
||||
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
|
||||
uvc->vdev.fops = &uvc_v4l2_fops;
|
||||
uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
|
||||
uvc->vdev.release = video_device_release_empty;
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
* Jaswinder Singh (jaswinder.singh@linaro.org)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/usb/audio.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
|
||||
|
@ -24,6 +27,12 @@
|
|||
#define PRD_SIZE_MAX PAGE_SIZE
|
||||
#define MIN_PERIODS 4
|
||||
|
||||
enum {
|
||||
UAC_FBACK_CTRL,
|
||||
UAC_MUTE_CTRL,
|
||||
UAC_VOLUME_CTRL,
|
||||
};
|
||||
|
||||
/* Runtime data params for one stream */
|
||||
struct uac_rtd_params {
|
||||
struct snd_uac_chip *uac; /* parent chip */
|
||||
|
@ -43,6 +52,17 @@ struct uac_rtd_params {
|
|||
|
||||
struct usb_request *req_fback; /* Feedback endpoint request */
|
||||
bool fb_ep_enabled; /* if the ep is enabled */
|
||||
|
||||
/* Volume/Mute controls and their state */
|
||||
int fu_id; /* Feature Unit ID */
|
||||
struct snd_kcontrol *snd_kctl_volume;
|
||||
struct snd_kcontrol *snd_kctl_mute;
|
||||
s16 volume_min, volume_max, volume_res;
|
||||
s16 volume;
|
||||
int mute;
|
||||
|
||||
spinlock_t lock; /* lock for control transfers */
|
||||
|
||||
};
|
||||
|
||||
struct snd_uac_chip {
|
||||
|
@ -604,6 +624,103 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
|
||||
|
||||
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
|
||||
if (playback)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
*val = prm->volume;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_get_volume);
|
||||
|
||||
int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
int change = 0;
|
||||
|
||||
if (playback)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
val = clamp(val, prm->volume_min, prm->volume_max);
|
||||
if (prm->volume != val) {
|
||||
prm->volume = val;
|
||||
change = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
if (change)
|
||||
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&prm->snd_kctl_volume->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_set_volume);
|
||||
|
||||
int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
|
||||
if (playback)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
*val = prm->mute;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_get_mute);
|
||||
|
||||
int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
int change = 0;
|
||||
int mute;
|
||||
|
||||
if (playback)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
mute = val ? 1 : 0;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
if (prm->mute != mute) {
|
||||
prm->mute = mute;
|
||||
change = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
if (change)
|
||||
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&prm->snd_kctl_mute->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_set_mute);
|
||||
|
||||
|
||||
static int u_audio_pitch_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
|
@ -663,14 +780,158 @@ static int u_audio_pitch_put(struct snd_kcontrol *kcontrol,
|
|||
return change;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new u_audio_controls[] = {
|
||||
static int u_audio_mute_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "Capture Pitch 1000000",
|
||||
.info = u_audio_pitch_info,
|
||||
.get = u_audio_pitch_get,
|
||||
.put = u_audio_pitch_put,
|
||||
},
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
uinfo->value.integer.step = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
ucontrol->value.integer.value[0] = !prm->mute;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
struct g_audio *audio_dev = uac->audio_dev;
|
||||
unsigned int val;
|
||||
unsigned long flags;
|
||||
int change = 0;
|
||||
|
||||
val = !ucontrol->value.integer.value[0];
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
if (val != prm->mute) {
|
||||
prm->mute = val;
|
||||
change = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
if (change && audio_dev->notify)
|
||||
audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
/*
|
||||
* TLV callback for mixer volume controls
|
||||
*/
|
||||
static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
unsigned int size, unsigned int __user *_tlv)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
DECLARE_TLV_DB_MINMAX(scale, 0, 0);
|
||||
|
||||
if (size < sizeof(scale))
|
||||
return -ENOMEM;
|
||||
|
||||
/* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */
|
||||
scale[2] = (prm->volume_min * 100) / 256;
|
||||
scale[3] = (prm->volume_max * 100) / 256;
|
||||
if (copy_to_user(_tlv, scale, sizeof(scale)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_volume_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max =
|
||||
(prm->volume_max - prm->volume_min + prm->volume_res - 1)
|
||||
/ prm->volume_res;
|
||||
uinfo->value.integer.step = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
ucontrol->value.integer.value[0] =
|
||||
(prm->volume - prm->volume_min) / prm->volume_res;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
struct g_audio *audio_dev = uac->audio_dev;
|
||||
unsigned int val;
|
||||
s16 volume;
|
||||
unsigned long flags;
|
||||
int change = 0;
|
||||
|
||||
val = ucontrol->value.integer.value[0];
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
volume = (val * prm->volume_res) + prm->volume_min;
|
||||
volume = clamp(volume, prm->volume_min, prm->volume_max);
|
||||
if (volume != prm->volume) {
|
||||
prm->volume = volume;
|
||||
change = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
if (change && audio_dev->notify)
|
||||
audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
static struct snd_kcontrol_new u_audio_controls[] = {
|
||||
[UAC_FBACK_CTRL] {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "Capture Pitch 1000000",
|
||||
.info = u_audio_pitch_info,
|
||||
.get = u_audio_pitch_get,
|
||||
.put = u_audio_pitch_put,
|
||||
},
|
||||
[UAC_MUTE_CTRL] {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "", /* will be filled later */
|
||||
.info = u_audio_mute_info,
|
||||
.get = u_audio_mute_get,
|
||||
.put = u_audio_mute_put,
|
||||
},
|
||||
[UAC_VOLUME_CTRL] {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "", /* will be filled later */
|
||||
.info = u_audio_volume_info,
|
||||
.get = u_audio_volume_get,
|
||||
.put = u_audio_volume_put,
|
||||
},
|
||||
};
|
||||
|
||||
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
|
@ -682,7 +943,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
struct snd_kcontrol *kctl;
|
||||
struct uac_params *params;
|
||||
int p_chmask, c_chmask;
|
||||
int err;
|
||||
int i, err;
|
||||
|
||||
if (!g_audio)
|
||||
return -EINVAL;
|
||||
|
@ -700,7 +961,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
if (c_chmask) {
|
||||
struct uac_rtd_params *prm = &uac->c_prm;
|
||||
|
||||
uac->c_prm.uac = uac;
|
||||
spin_lock_init(&prm->lock);
|
||||
uac->c_prm.uac = uac;
|
||||
prm->max_psize = g_audio->out_ep_maxpsize;
|
||||
|
||||
prm->reqs = kcalloc(params->req_number,
|
||||
|
@ -723,6 +985,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
if (p_chmask) {
|
||||
struct uac_rtd_params *prm = &uac->p_prm;
|
||||
|
||||
spin_lock_init(&prm->lock);
|
||||
uac->p_prm.uac = uac;
|
||||
prm->max_psize = g_audio->in_ep_maxpsize;
|
||||
|
||||
|
@ -767,10 +1030,18 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
|
||||
|
||||
if (c_chmask && g_audio->in_ep_fback) {
|
||||
/*
|
||||
* Create mixer and controls
|
||||
* Create only if it's required on USB side
|
||||
*/
|
||||
if ((c_chmask && g_audio->in_ep_fback)
|
||||
|| (p_chmask && params->p_fu.id)
|
||||
|| (c_chmask && params->c_fu.id))
|
||||
strscpy(card->mixername, card_name, sizeof(card->driver));
|
||||
|
||||
kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm);
|
||||
if (c_chmask && g_audio->in_ep_fback) {
|
||||
kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL],
|
||||
&uac->c_prm);
|
||||
if (!kctl) {
|
||||
err = -ENOMEM;
|
||||
goto snd_fail;
|
||||
|
@ -784,6 +1055,82 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
goto snd_fail;
|
||||
}
|
||||
|
||||
for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) {
|
||||
struct uac_rtd_params *prm;
|
||||
struct uac_fu_params *fu;
|
||||
char ctrl_name[24];
|
||||
char *direction;
|
||||
|
||||
if (!pcm->streams[i].substream_count)
|
||||
continue;
|
||||
|
||||
if (i == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
prm = &uac->p_prm;
|
||||
fu = ¶ms->p_fu;
|
||||
direction = "Playback";
|
||||
} else {
|
||||
prm = &uac->c_prm;
|
||||
fu = ¶ms->c_fu;
|
||||
direction = "Capture";
|
||||
}
|
||||
|
||||
prm->fu_id = fu->id;
|
||||
|
||||
if (fu->mute_present) {
|
||||
snprintf(ctrl_name, sizeof(ctrl_name),
|
||||
"PCM %s Switch", direction);
|
||||
|
||||
u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name;
|
||||
|
||||
kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL],
|
||||
prm);
|
||||
if (!kctl) {
|
||||
err = -ENOMEM;
|
||||
goto snd_fail;
|
||||
}
|
||||
|
||||
kctl->id.device = pcm->device;
|
||||
kctl->id.subdevice = i;
|
||||
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
prm->snd_kctl_mute = kctl;
|
||||
prm->mute = 0;
|
||||
}
|
||||
|
||||
if (fu->volume_present) {
|
||||
snprintf(ctrl_name, sizeof(ctrl_name),
|
||||
"PCM %s Volume", direction);
|
||||
|
||||
u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name;
|
||||
|
||||
kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL],
|
||||
prm);
|
||||
if (!kctl) {
|
||||
err = -ENOMEM;
|
||||
goto snd_fail;
|
||||
}
|
||||
|
||||
kctl->id.device = pcm->device;
|
||||
kctl->id.subdevice = i;
|
||||
|
||||
|
||||
kctl->tlv.c = u_audio_volume_tlv;
|
||||
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
|
||||
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
prm->snd_kctl_volume = kctl;
|
||||
prm->volume = fu->volume_max;
|
||||
prm->volume_max = fu->volume_max;
|
||||
prm->volume_min = fu->volume_min;
|
||||
prm->volume_res = fu->volume_res;
|
||||
}
|
||||
}
|
||||
|
||||
strscpy(card->driver, card_name, sizeof(card->driver));
|
||||
strscpy(card->shortname, card_name, sizeof(card->shortname));
|
||||
sprintf(card->longname, "%s %i", card_name, card->dev->id);
|
||||
|
|
|
@ -19,16 +19,30 @@
|
|||
*/
|
||||
#define FBACK_SLOW_MAX 250
|
||||
|
||||
/* Feature Unit parameters */
|
||||
struct uac_fu_params {
|
||||
int id; /* Feature Unit ID */
|
||||
|
||||
bool mute_present; /* mute control enable */
|
||||
|
||||
bool volume_present; /* volume control enable */
|
||||
s16 volume_min; /* min volume in 1/256 dB */
|
||||
s16 volume_max; /* max volume in 1/256 dB */
|
||||
s16 volume_res; /* volume resolution in 1/256 dB */
|
||||
};
|
||||
|
||||
struct uac_params {
|
||||
/* playback */
|
||||
int p_chmask; /* channel mask */
|
||||
int p_srate; /* rate in Hz */
|
||||
int p_ssize; /* sample size */
|
||||
struct uac_fu_params p_fu; /* Feature Unit parameters */
|
||||
|
||||
/* capture */
|
||||
int c_chmask; /* channel mask */
|
||||
int c_srate; /* rate in Hz */
|
||||
int c_ssize; /* sample size */
|
||||
struct uac_fu_params c_fu; /* Feature Unit parameters */
|
||||
|
||||
int req_number; /* number of preallocated requests */
|
||||
int fb_max; /* upper frequency drift feedback limit per-mil */
|
||||
|
@ -49,6 +63,9 @@ struct g_audio {
|
|||
/* Max packet size for all out_ep possible speeds */
|
||||
unsigned int out_ep_maxpsize;
|
||||
|
||||
/* Notify UAC driver about control change */
|
||||
int (*notify)(struct g_audio *g_audio, int unit_id, int cs);
|
||||
|
||||
/* The ALSA Sound Card it represents on the USB-Client side */
|
||||
struct snd_uac_chip *uac;
|
||||
|
||||
|
@ -94,4 +111,9 @@ void u_audio_stop_capture(struct g_audio *g_audio);
|
|||
int u_audio_start_playback(struct g_audio *g_audio);
|
||||
void u_audio_stop_playback(struct g_audio *g_audio);
|
||||
|
||||
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
|
||||
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
|
||||
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
|
||||
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
|
||||
|
||||
#endif /* __U_AUDIO_H */
|
||||
|
|
|
@ -492,8 +492,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
|||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (skb && !in) {
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!in) {
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ struct f_hid_opts {
|
|||
int minor;
|
||||
unsigned char subclass;
|
||||
unsigned char protocol;
|
||||
unsigned char no_out_endpoint;
|
||||
unsigned short report_length;
|
||||
unsigned short report_desc_length;
|
||||
unsigned char *report_desc;
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
#define UAC1_DEF_PSRATE 48000
|
||||
#define UAC1_DEF_PSSIZE 2
|
||||
#define UAC1_DEF_REQ_NUM 2
|
||||
#define UAC1_DEF_INT_REQ_NUM 10
|
||||
|
||||
#define UAC1_DEF_MUTE_PRESENT 1
|
||||
#define UAC1_DEF_VOLUME_PRESENT 1
|
||||
#define UAC1_DEF_MIN_DB (-100*256) /* -100 dB */
|
||||
#define UAC1_DEF_MAX_DB 0 /* 0 dB */
|
||||
#define UAC1_DEF_RES_DB (1*256) /* 1 dB */
|
||||
|
||||
|
||||
struct f_uac1_opts {
|
||||
|
@ -28,6 +35,19 @@ struct f_uac1_opts {
|
|||
int p_chmask;
|
||||
int p_srate;
|
||||
int p_ssize;
|
||||
|
||||
bool p_mute_present;
|
||||
bool p_volume_present;
|
||||
s16 p_volume_min;
|
||||
s16 p_volume_max;
|
||||
s16 p_volume_res;
|
||||
|
||||
bool c_mute_present;
|
||||
bool c_volume_present;
|
||||
s16 c_volume_min;
|
||||
s16 c_volume_max;
|
||||
s16 c_volume_res;
|
||||
|
||||
int req_number;
|
||||
unsigned bound:1;
|
||||
|
||||
|
|
|
@ -22,8 +22,16 @@
|
|||
#define UAC2_DEF_CSRATE 64000
|
||||
#define UAC2_DEF_CSSIZE 2
|
||||
#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
|
||||
|
||||
#define UAC2_DEF_MUTE_PRESENT 1
|
||||
#define UAC2_DEF_VOLUME_PRESENT 1
|
||||
#define UAC2_DEF_MIN_DB (-100*256) /* -100 dB */
|
||||
#define UAC2_DEF_MAX_DB 0 /* 0 dB */
|
||||
#define UAC2_DEF_RES_DB (1*256) /* 1 dB */
|
||||
|
||||
#define UAC2_DEF_REQ_NUM 2
|
||||
#define UAC2_DEF_FB_MAX 5
|
||||
#define UAC2_DEF_INT_REQ_NUM 10
|
||||
|
||||
struct f_uac2_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
|
@ -34,9 +42,22 @@ struct f_uac2_opts {
|
|||
int c_srate;
|
||||
int c_ssize;
|
||||
int c_sync;
|
||||
|
||||
bool p_mute_present;
|
||||
bool p_volume_present;
|
||||
s16 p_volume_min;
|
||||
s16 p_volume_max;
|
||||
s16 p_volume_res;
|
||||
|
||||
bool c_mute_present;
|
||||
bool c_volume_present;
|
||||
s16 c_volume_min;
|
||||
s16 c_volume_max;
|
||||
s16 c_volume_res;
|
||||
|
||||
int req_number;
|
||||
int fb_max;
|
||||
bool bound;
|
||||
bool bound;
|
||||
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
|
|
|
@ -65,13 +65,19 @@ extern unsigned int uvc_gadget_trace_param;
|
|||
* Driver specific constants
|
||||
*/
|
||||
|
||||
#define UVC_NUM_REQUESTS 4
|
||||
#define UVC_MAX_REQUEST_SIZE 64
|
||||
#define UVC_MAX_EVENTS 4
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Structures
|
||||
*/
|
||||
struct uvc_request {
|
||||
struct usb_request *req;
|
||||
u8 *req_buffer;
|
||||
struct uvc_video *video;
|
||||
struct sg_table sgt;
|
||||
u8 header[2];
|
||||
};
|
||||
|
||||
struct uvc_video {
|
||||
struct uvc_device *uvc;
|
||||
|
@ -87,13 +93,16 @@ struct uvc_video {
|
|||
unsigned int imagesize;
|
||||
struct mutex mutex; /* protects frame parameters */
|
||||
|
||||
unsigned int uvc_num_requests;
|
||||
|
||||
/* Requests */
|
||||
unsigned int req_size;
|
||||
struct usb_request *req[UVC_NUM_REQUESTS];
|
||||
__u8 *req_buffer[UVC_NUM_REQUESTS];
|
||||
struct uvc_request *ureq;
|
||||
struct list_head req_free;
|
||||
spinlock_t req_lock;
|
||||
|
||||
unsigned int req_int_count;
|
||||
|
||||
void (*encode) (struct usb_request *req, struct uvc_video *video,
|
||||
struct uvc_buffer *buf);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/wait.h>
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/videobuf2-dma-sg.h>
|
||||
#include <media/videobuf2-vmalloc.h>
|
||||
|
||||
#include "uvc.h"
|
||||
|
@ -43,6 +44,7 @@ static int uvc_queue_setup(struct vb2_queue *vq,
|
|||
{
|
||||
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
|
||||
struct uvc_video *video = container_of(queue, struct uvc_video, queue);
|
||||
struct usb_composite_dev *cdev = video->uvc->func.config->cdev;
|
||||
|
||||
if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
|
||||
*nbuffers = UVC_MAX_VIDEO_BUFFERS;
|
||||
|
@ -51,6 +53,11 @@ static int uvc_queue_setup(struct vb2_queue *vq,
|
|||
|
||||
sizes[0] = video->imagesize;
|
||||
|
||||
if (cdev->gadget->speed < USB_SPEED_SUPER)
|
||||
video->uvc_num_requests = 4;
|
||||
else
|
||||
video->uvc_num_requests = 64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -70,7 +77,12 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
|
|||
return -ENODEV;
|
||||
|
||||
buf->state = UVC_BUF_STATE_QUEUED;
|
||||
buf->mem = vb2_plane_vaddr(vb, 0);
|
||||
if (queue->use_sg) {
|
||||
buf->sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
buf->sg = buf->sgt->sgl;
|
||||
} else {
|
||||
buf->mem = vb2_plane_vaddr(vb, 0);
|
||||
}
|
||||
buf->length = vb2_plane_size(vb, 0);
|
||||
if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
buf->bytesused = 0;
|
||||
|
@ -110,9 +122,11 @@ static const struct vb2_ops uvc_queue_qops = {
|
|||
.wait_finish = vb2_ops_wait_finish,
|
||||
};
|
||||
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type,
|
||||
struct mutex *lock)
|
||||
{
|
||||
struct uvc_video *video = container_of(queue, struct uvc_video, queue);
|
||||
struct usb_composite_dev *cdev = video->uvc->func.config->cdev;
|
||||
int ret;
|
||||
|
||||
queue->queue.type = type;
|
||||
|
@ -121,9 +135,17 @@ int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
|
|||
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
|
||||
queue->queue.ops = &uvc_queue_qops;
|
||||
queue->queue.lock = lock;
|
||||
queue->queue.mem_ops = &vb2_vmalloc_memops;
|
||||
if (cdev->gadget->sg_supported) {
|
||||
queue->queue.mem_ops = &vb2_dma_sg_memops;
|
||||
queue->use_sg = 1;
|
||||
} else {
|
||||
queue->queue.mem_ops = &vb2_vmalloc_memops;
|
||||
}
|
||||
|
||||
queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
|
||||
| V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
|
||||
queue->queue.dev = dev;
|
||||
|
||||
ret = vb2_queue_init(&queue->queue);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -34,6 +34,9 @@ struct uvc_buffer {
|
|||
|
||||
enum uvc_buffer_state state;
|
||||
void *mem;
|
||||
struct sg_table *sgt;
|
||||
struct scatterlist *sg;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned int bytesused;
|
||||
};
|
||||
|
@ -50,6 +53,8 @@ struct uvc_video_queue {
|
|||
|
||||
unsigned int buf_used;
|
||||
|
||||
bool use_sg;
|
||||
|
||||
spinlock_t irqlock; /* Protects flags and irqqueue */
|
||||
struct list_head irqqueue;
|
||||
};
|
||||
|
@ -59,7 +64,7 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
|
|||
return vb2_is_streaming(&queue->queue);
|
||||
}
|
||||
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type,
|
||||
struct mutex *lock);
|
||||
|
||||
void uvcg_free_buffers(struct uvc_video_queue *queue);
|
||||
|
|
|
@ -27,10 +27,10 @@ static int
|
|||
uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
|
||||
u8 *data, int len)
|
||||
{
|
||||
data[0] = 2;
|
||||
data[0] = UVCG_REQUEST_HEADER_LEN;
|
||||
data[1] = UVC_STREAM_EOH | video->fid;
|
||||
|
||||
if (buf->bytesused - video->queue.buf_used <= len - 2)
|
||||
if (buf->bytesused - video->queue.buf_used <= len - UVCG_REQUEST_HEADER_LEN)
|
||||
data[1] |= UVC_STREAM_EOF;
|
||||
|
||||
return 2;
|
||||
|
@ -94,6 +94,71 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
|
|||
video->payload_size = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
|
||||
struct uvc_buffer *buf)
|
||||
{
|
||||
unsigned int pending = buf->bytesused - video->queue.buf_used;
|
||||
struct uvc_request *ureq = req->context;
|
||||
struct scatterlist *sg, *iter;
|
||||
unsigned int len = video->req_size;
|
||||
unsigned int sg_left, part = 0;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
sg = ureq->sgt.sgl;
|
||||
sg_init_table(sg, ureq->sgt.nents);
|
||||
|
||||
/* Init the header. */
|
||||
ret = uvc_video_encode_header(video, buf, ureq->header,
|
||||
video->req_size);
|
||||
sg_set_buf(sg, ureq->header, UVCG_REQUEST_HEADER_LEN);
|
||||
len -= ret;
|
||||
|
||||
if (pending <= len)
|
||||
len = pending;
|
||||
|
||||
req->length = (len == pending) ?
|
||||
len + UVCG_REQUEST_HEADER_LEN : video->req_size;
|
||||
|
||||
/* Init the pending sgs with payload */
|
||||
sg = sg_next(sg);
|
||||
|
||||
for_each_sg(sg, iter, ureq->sgt.nents - 1, i) {
|
||||
if (!len || !buf->sg)
|
||||
break;
|
||||
|
||||
sg_left = sg_dma_len(buf->sg) - buf->offset;
|
||||
part = min_t(unsigned int, len, sg_left);
|
||||
|
||||
sg_set_page(iter, sg_page(buf->sg), part, buf->offset);
|
||||
|
||||
if (part == sg_left) {
|
||||
buf->offset = 0;
|
||||
buf->sg = sg_next(buf->sg);
|
||||
} else {
|
||||
buf->offset += part;
|
||||
}
|
||||
len -= part;
|
||||
}
|
||||
|
||||
/* Assign the video data with header. */
|
||||
req->buf = NULL;
|
||||
req->sg = ureq->sgt.sgl;
|
||||
req->num_sgs = i + 1;
|
||||
|
||||
req->length -= len;
|
||||
video->queue.buf_used += req->length - UVCG_REQUEST_HEADER_LEN;
|
||||
|
||||
if (buf->bytesused == video->queue.buf_used || !buf->sg) {
|
||||
video->queue.buf_used = 0;
|
||||
buf->state = UVC_BUF_STATE_DONE;
|
||||
buf->offset = 0;
|
||||
uvcg_queue_next_buffer(&video->queue, buf);
|
||||
video->fid ^= UVC_STREAM_FID;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
|
||||
struct uvc_buffer *buf)
|
||||
|
@ -145,7 +210,8 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
|
|||
static void
|
||||
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct uvc_video *video = req->context;
|
||||
struct uvc_request *ureq = req->context;
|
||||
struct uvc_video *video = ureq->video;
|
||||
struct uvc_video_queue *queue = &video->queue;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -177,16 +243,23 @@ uvc_video_free_requests(struct uvc_video *video)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
||||
if (video->req[i]) {
|
||||
usb_ep_free_request(video->ep, video->req[i]);
|
||||
video->req[i] = NULL;
|
||||
if (video->ureq) {
|
||||
for (i = 0; i < video->uvc_num_requests; ++i) {
|
||||
sg_free_table(&video->ureq[i].sgt);
|
||||
|
||||
if (video->ureq[i].req) {
|
||||
usb_ep_free_request(video->ep, video->ureq[i].req);
|
||||
video->ureq[i].req = NULL;
|
||||
}
|
||||
|
||||
if (video->ureq[i].req_buffer) {
|
||||
kfree(video->ureq[i].req_buffer);
|
||||
video->ureq[i].req_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (video->req_buffer[i]) {
|
||||
kfree(video->req_buffer[i]);
|
||||
video->req_buffer[i] = NULL;
|
||||
}
|
||||
kfree(video->ureq);
|
||||
video->ureq = NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&video->req_free);
|
||||
|
@ -207,21 +280,30 @@ uvc_video_alloc_requests(struct uvc_video *video)
|
|||
* max_t(unsigned int, video->ep->maxburst, 1)
|
||||
* (video->ep->mult);
|
||||
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
||||
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
|
||||
if (video->req_buffer[i] == NULL)
|
||||
video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL);
|
||||
if (video->ureq == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < video->uvc_num_requests; ++i) {
|
||||
video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL);
|
||||
if (video->ureq[i].req_buffer == NULL)
|
||||
goto error;
|
||||
|
||||
video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
|
||||
if (video->req[i] == NULL)
|
||||
video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
|
||||
if (video->ureq[i].req == NULL)
|
||||
goto error;
|
||||
|
||||
video->req[i]->buf = video->req_buffer[i];
|
||||
video->req[i]->length = 0;
|
||||
video->req[i]->complete = uvc_video_complete;
|
||||
video->req[i]->context = video;
|
||||
video->ureq[i].req->buf = video->ureq[i].req_buffer;
|
||||
video->ureq[i].req->length = 0;
|
||||
video->ureq[i].req->complete = uvc_video_complete;
|
||||
video->ureq[i].req->context = &video->ureq[i];
|
||||
video->ureq[i].video = video;
|
||||
|
||||
list_add_tail(&video->req[i]->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 */
|
||||
sg_alloc_table(&video->ureq[i].sgt,
|
||||
DIV_ROUND_UP(req_size - 2, PAGE_SIZE) + 2,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
video->req_size = req_size;
|
||||
|
@ -278,6 +360,19 @@ static void uvcg_video_pump(struct work_struct *work)
|
|||
|
||||
video->encode(req, video, buf);
|
||||
|
||||
/* With usb3 we have more requests. This will decrease the
|
||||
* interrupt load to a quarter but also catches the corner
|
||||
* cases, which needs to be handled */
|
||||
if (list_empty(&video->req_free) ||
|
||||
buf->state == UVC_BUF_STATE_DONE ||
|
||||
!(video->req_int_count %
|
||||
DIV_ROUND_UP(video->uvc_num_requests, 4))) {
|
||||
video->req_int_count = 0;
|
||||
req->no_interrupt = 0;
|
||||
} else {
|
||||
req->no_interrupt = 1;
|
||||
}
|
||||
|
||||
/* Queue the USB request */
|
||||
ret = uvcg_video_ep_queue(video, req);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
|
@ -286,6 +381,7 @@ static void uvcg_video_pump(struct work_struct *work)
|
|||
uvcg_queue_cancel(queue, 0);
|
||||
break;
|
||||
}
|
||||
video->req_int_count++;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
|
@ -312,9 +408,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
|||
cancel_work_sync(&video->pump);
|
||||
uvcg_queue_cancel(&video->queue, 0);
|
||||
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
|
||||
if (video->req[i])
|
||||
usb_ep_dequeue(video->ep, video->req[i]);
|
||||
for (i = 0; i < video->uvc_num_requests; ++i)
|
||||
if (video->ureq && video->ureq[i].req)
|
||||
usb_ep_dequeue(video->ep, video->ureq[i].req);
|
||||
|
||||
uvc_video_free_requests(video);
|
||||
uvcg_queue_enable(&video->queue, 0);
|
||||
|
@ -331,7 +427,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
|||
video->encode = uvc_video_encode_bulk;
|
||||
video->payload_size = 0;
|
||||
} else
|
||||
video->encode = uvc_video_encode_isoc;
|
||||
video->encode = video->queue.use_sg ?
|
||||
uvc_video_encode_isoc_sg : uvc_video_encode_isoc;
|
||||
|
||||
video->req_int_count = 0;
|
||||
|
||||
schedule_work(&video->pump);
|
||||
|
||||
|
@ -355,8 +454,8 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
|
|||
video->imagesize = 320 * 240 * 2;
|
||||
|
||||
/* Initialize the video buffers queue. */
|
||||
uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT,
|
||||
&video->mutex);
|
||||
uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
|
||||
V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef __UVC_VIDEO_H__
|
||||
#define __UVC_VIDEO_H__
|
||||
|
||||
#define UVCG_REQUEST_HEADER_LEN 2
|
||||
|
||||
struct uvc_video;
|
||||
|
||||
int uvcg_video_enable(struct uvc_video *video, int enable);
|
||||
|
|
|
@ -502,6 +502,7 @@ config USB_G_WEBCAM
|
|||
tristate "USB Webcam Gadget"
|
||||
depends on VIDEO_V4L2
|
||||
select USB_LIBCOMPOSITE
|
||||
select VIDEOBUF2_DMA_SG
|
||||
select VIDEOBUF2_VMALLOC
|
||||
select USB_F_UVC
|
||||
help
|
||||
|
|
|
@ -1214,8 +1214,8 @@ dev_release (struct inode *inode, struct file *fd)
|
|||
static __poll_t
|
||||
ep0_poll (struct file *fd, poll_table *wait)
|
||||
{
|
||||
struct dev_data *dev = fd->private_data;
|
||||
__poll_t mask = 0;
|
||||
struct dev_data *dev = fd->private_data;
|
||||
__poll_t mask = 0;
|
||||
|
||||
if (dev->state <= STATE_DEV_OPENED)
|
||||
return DEFAULT_POLLMASK;
|
||||
|
|
|
@ -50,6 +50,7 @@ MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;");
|
|||
/* Number of requests to allocate per endpoint, not used for ep0. */
|
||||
static unsigned qlen = 10;
|
||||
module_param(qlen, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(qlen, "The number of 8k buffers to use per endpoint");
|
||||
|
||||
#define QLEN qlen
|
||||
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* core.c - Top level support
|
||||
*
|
||||
* Copyright 2017 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* dev.c - Individual device/gadget management (ie, a port = a gadget)
|
||||
*
|
||||
* Copyright 2017 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* ep0.c - Endpoint 0 handling
|
||||
*
|
||||
* Copyright 2017 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* epn.c - Generic endpoints management
|
||||
*
|
||||
* Copyright 2017 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* hub.c - virtual hub handling
|
||||
*
|
||||
* Copyright 2017 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -1876,7 +1876,9 @@ static int at91udc_probe(struct platform_device *pdev)
|
|||
clk_disable(udc->iclk);
|
||||
|
||||
/* request UDC and maybe VBUS irqs */
|
||||
udc->udp_irq = platform_get_irq(pdev, 0);
|
||||
udc->udp_irq = retval = platform_get_irq(pdev, 0);
|
||||
if (retval < 0)
|
||||
goto err_unprepare_iclk;
|
||||
retval = devm_request_irq(dev, udc->udp_irq, at91_udc_irq, 0,
|
||||
driver_name, udc);
|
||||
if (retval) {
|
||||
|
|
|
@ -153,7 +153,6 @@ int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep)
|
|||
si = clamp_val(si, 1, 16) - 1;
|
||||
|
||||
mps = usb_endpoint_maxp(desc);
|
||||
mps &= 0x7ff;
|
||||
param2 |= mps << MP_SHIFT;
|
||||
param2 |= usb_endpoint_type(desc) << EPT_SHIFT;
|
||||
|
||||
|
|
|
@ -488,27 +488,14 @@ static int bdc_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
u32 temp;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
int phy_num;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
clk = devm_clk_get_optional(dev, "sw_usbd");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
|
||||
if (!bdc)
|
||||
return -ENOMEM;
|
||||
|
||||
bdc->clk = clk;
|
||||
|
||||
bdc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(bdc->regs))
|
||||
return PTR_ERR(bdc->regs);
|
||||
|
@ -545,10 +532,20 @@ static int bdc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
bdc->clk = devm_clk_get_optional(dev, "sw_usbd");
|
||||
if (IS_ERR(bdc->clk))
|
||||
return PTR_ERR(bdc->clk);
|
||||
|
||||
ret = clk_prepare_enable(bdc->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdc_phy_init(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "BDC phy init failure:%d\n", ret);
|
||||
return ret;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
|
||||
|
@ -560,7 +557,8 @@ static int bdc_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(dev,
|
||||
"No suitable DMA config available, abort\n");
|
||||
return -ENOTSUPP;
|
||||
ret = -ENOTSUPP;
|
||||
goto phycleanup;
|
||||
}
|
||||
dev_dbg(dev, "Using 32-bit address\n");
|
||||
}
|
||||
|
@ -580,6 +578,8 @@ cleanup:
|
|||
bdc_hw_exit(bdc);
|
||||
phycleanup:
|
||||
bdc_phy_exit(bdc);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(bdc->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1003,6 +1003,25 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
|
||||
|
||||
/**
|
||||
* usb_gadget_check_config - checks if the UDC can support the binded
|
||||
* configuration
|
||||
* @gadget: controller to check the USB configuration
|
||||
*
|
||||
* Ensure that a UDC is able to support the requested resources by a
|
||||
* configuration, and that there are no resource limitations, such as
|
||||
* internal memory allocated to all requested endpoints.
|
||||
*
|
||||
* Returns zero on success, else a negative errno.
|
||||
*/
|
||||
int usb_gadget_check_config(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget->ops->check_config)
|
||||
return gadget->ops->check_config(gadget);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_check_config);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void usb_gadget_state_work(struct work_struct *work)
|
||||
|
|
|
@ -1921,14 +1921,6 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
goto err_get_irq;
|
||||
}
|
||||
u3d->irq = r->start;
|
||||
if (request_irq(u3d->irq, mv_u3d_irq,
|
||||
IRQF_SHARED, driver_name, u3d)) {
|
||||
u3d->irq = 0;
|
||||
dev_err(&dev->dev, "Request irq %d for u3d failed\n",
|
||||
u3d->irq);
|
||||
retval = -ENODEV;
|
||||
goto err_request_irq;
|
||||
}
|
||||
|
||||
/* initialize gadget structure */
|
||||
u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */
|
||||
|
@ -1941,6 +1933,15 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
|
||||
mv_u3d_eps_init(u3d);
|
||||
|
||||
if (request_irq(u3d->irq, mv_u3d_irq,
|
||||
IRQF_SHARED, driver_name, u3d)) {
|
||||
u3d->irq = 0;
|
||||
dev_err(&dev->dev, "Request irq %d for u3d failed\n",
|
||||
u3d->irq);
|
||||
retval = -ENODEV;
|
||||
goto err_request_irq;
|
||||
}
|
||||
|
||||
/* external vbus detection */
|
||||
if (u3d->vbus) {
|
||||
u3d->clock_gating = 1;
|
||||
|
@ -1964,8 +1965,8 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
|
||||
err_unregister:
|
||||
free_irq(u3d->irq, u3d);
|
||||
err_request_irq:
|
||||
err_get_irq:
|
||||
err_request_irq:
|
||||
kfree(u3d->status_req);
|
||||
err_alloc_status_req:
|
||||
kfree(u3d->eps);
|
||||
|
|
|
@ -1093,7 +1093,7 @@ static void pxa25x_ep_fifo_flush(struct usb_ep *_ep)
|
|||
}
|
||||
|
||||
|
||||
static struct usb_ep_ops pxa25x_ep_ops = {
|
||||
static const struct usb_ep_ops pxa25x_ep_ops = {
|
||||
.enable = pxa25x_ep_enable,
|
||||
.disable = pxa25x_ep_disable,
|
||||
|
||||
|
|
|
@ -2707,10 +2707,15 @@ static const struct renesas_usb3_priv renesas_usb3_priv_r8a77990 = {
|
|||
|
||||
static const struct of_device_id usb3_of_match[] = {
|
||||
{
|
||||
.compatible = "renesas,r8a774c0-usb3-peri",
|
||||
.data = &renesas_usb3_priv_r8a77990,
|
||||
}, {
|
||||
.compatible = "renesas,r8a7795-usb3-peri",
|
||||
.data = &renesas_usb3_priv_gen3,
|
||||
},
|
||||
{
|
||||
}, {
|
||||
.compatible = "renesas,r8a77990-usb3-peri",
|
||||
.data = &renesas_usb3_priv_r8a77990,
|
||||
}, {
|
||||
.compatible = "renesas,rcar-gen3-usb3-peri",
|
||||
.data = &renesas_usb3_priv_gen3,
|
||||
},
|
||||
|
@ -2719,18 +2724,10 @@ static const struct of_device_id usb3_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, usb3_of_match);
|
||||
|
||||
static const struct soc_device_attribute renesas_usb3_quirks_match[] = {
|
||||
{
|
||||
.soc_id = "r8a774c0",
|
||||
.data = &renesas_usb3_priv_r8a77990,
|
||||
},
|
||||
{
|
||||
.soc_id = "r8a7795", .revision = "ES1.*",
|
||||
.data = &renesas_usb3_priv_r8a7795_es1,
|
||||
},
|
||||
{
|
||||
.soc_id = "r8a77990",
|
||||
.data = &renesas_usb3_priv_r8a77990,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
|
|
|
@ -1784,6 +1784,10 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
|
|||
s3c2410_udc_reinit(udc);
|
||||
|
||||
irq_usbd = platform_get_irq(pdev, 0);
|
||||
if (irq_usbd < 0) {
|
||||
retval = irq_usbd;
|
||||
goto err_udc_clk;
|
||||
}
|
||||
|
||||
/* irq setup after old hardware state is cleaned up */
|
||||
retval = request_irq(irq_usbd, s3c2410_udc_irq,
|
||||
|
|
|
@ -1610,7 +1610,7 @@ static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep)
|
|||
u16 maxpacket, maxburst = 0, esit = 0;
|
||||
u32 val;
|
||||
|
||||
maxpacket = usb_endpoint_maxp(desc) & 0x7ff;
|
||||
maxpacket = usb_endpoint_maxp(desc);
|
||||
if (xudc->gadget.speed == USB_SPEED_SUPER) {
|
||||
if (!usb_endpoint_xfer_control(desc))
|
||||
maxburst = comp_desc->bMaxBurst;
|
||||
|
@ -1621,7 +1621,7 @@ static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep)
|
|||
(usb_endpoint_xfer_int(desc) ||
|
||||
usb_endpoint_xfer_isoc(desc))) {
|
||||
if (xudc->gadget.speed == USB_SPEED_HIGH) {
|
||||
maxburst = (usb_endpoint_maxp(desc) >> 11) & 0x3;
|
||||
maxburst = usb_endpoint_maxp_mult(desc) - 1;
|
||||
if (maxburst == 0x3) {
|
||||
dev_warn(xudc->dev,
|
||||
"invalid endpoint maxburst\n");
|
||||
|
|
|
@ -187,15 +187,6 @@ config USB_EHCI_PCI
|
|||
depends on USB_PCI
|
||||
default y
|
||||
|
||||
config USB_EHCI_HCD_PMC_MSP
|
||||
tristate "EHCI support for on-chip PMC MSP71xx USB controller"
|
||||
depends on MSP_HAS_USB
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
help
|
||||
Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's.
|
||||
If unsure, say N.
|
||||
|
||||
config XPS_USB_HCD_XILINX
|
||||
bool "Use Xilinx usb host EHCI controller core"
|
||||
depends on (PPC32 || MICROBLAZE)
|
||||
|
|
|
@ -108,10 +108,9 @@ static int ehci_brcm_reset(struct usb_hcd *hcd)
|
|||
/*
|
||||
* SWLINUX-1705: Avoid OUT packet underflows during high memory
|
||||
* bus usage
|
||||
* port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00 @ 0x90
|
||||
*/
|
||||
ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]);
|
||||
ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]);
|
||||
ehci_writel(ehci, 0x00800040, &ehci->regs->brcm_insnreg[1]);
|
||||
ehci_writel(ehci, 0x00000001, &ehci->regs->brcm_insnreg[3]);
|
||||
|
||||
return ehci_setup(hcd);
|
||||
}
|
||||
|
@ -223,11 +222,9 @@ static int __maybe_unused ehci_brcm_resume(struct device *dev)
|
|||
/*
|
||||
* SWLINUX-1705: Avoid OUT packet underflows during high memory
|
||||
* bus usage
|
||||
* port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00
|
||||
* @ 0x90
|
||||
*/
|
||||
ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]);
|
||||
ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]);
|
||||
ehci_writel(ehci, 0x00800040, &ehci->regs->brcm_insnreg[1]);
|
||||
ehci_writel(ehci, 0x00000001, &ehci->regs->brcm_insnreg[3]);
|
||||
|
||||
ehci_resume(hcd, false);
|
||||
|
||||
|
|
|
@ -1296,11 +1296,6 @@ MODULE_LICENSE ("GPL");
|
|||
#define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
|
||||
#include "ehci-pmcmsp.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_msp_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPARC_LEON
|
||||
#include "ehci-grlib.c"
|
||||
#define PLATFORM_DRIVER ehci_grlib_driver
|
||||
|
|
|
@ -42,26 +42,25 @@ struct ehci_hcd_mv {
|
|||
int (*set_vbus)(unsigned int vbus);
|
||||
};
|
||||
|
||||
static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
clk_prepare_enable(ehci_mv->clk);
|
||||
}
|
||||
|
||||
static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
clk_disable_unprepare(ehci_mv->clk);
|
||||
}
|
||||
|
||||
static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
ehci_clock_enable(ehci_mv);
|
||||
return phy_init(ehci_mv->phy);
|
||||
int retval;
|
||||
|
||||
retval = clk_prepare_enable(ehci_mv->clk);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = phy_init(ehci_mv->phy);
|
||||
if (retval)
|
||||
clk_disable_unprepare(ehci_mv->clk);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
phy_exit(ehci_mv->phy);
|
||||
ehci_clock_disable(ehci_mv);
|
||||
clk_disable_unprepare(ehci_mv->clk);
|
||||
}
|
||||
|
||||
static int mv_ehci_reset(struct usb_hcd *hcd)
|
||||
|
|
|
@ -264,8 +264,11 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
* the clock does not exists.
|
||||
*/
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
if (!IS_ERR(priv->clk)) {
|
||||
err = clk_prepare_enable(priv->clk);
|
||||
if (err)
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
priv->phy = devm_phy_optional_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(priv->phy)) {
|
||||
|
@ -311,6 +314,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
err_dis_clk:
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
err:
|
||||
dev_err(&pdev->dev, "init %s fail, %d\n",
|
||||
|
|
|
@ -1,328 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PMC MSP EHCI (Host Controller Driver) for USB.
|
||||
*
|
||||
* (C) Copyright 2006-2010 PMC-Sierra Inc
|
||||
*/
|
||||
|
||||
/* includes */
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/usb.h>
|
||||
#include <msp_usb.h>
|
||||
|
||||
/* stream disable*/
|
||||
#define USB_CTRL_MODE_STREAM_DISABLE 0x10
|
||||
|
||||
/* threshold */
|
||||
#define USB_CTRL_FIFO_THRESH 0x00300000
|
||||
|
||||
/* register offset for usb_mode */
|
||||
#define USB_EHCI_REG_USB_MODE 0x68
|
||||
|
||||
/* register offset for usb fifo */
|
||||
#define USB_EHCI_REG_USB_FIFO 0x24
|
||||
|
||||
/* register offset for usb status */
|
||||
#define USB_EHCI_REG_USB_STATUS 0x44
|
||||
|
||||
/* serial/parallel transceiver */
|
||||
#define USB_EHCI_REG_BIT_STAT_STS (1<<29)
|
||||
|
||||
/* TWI USB0 host device pin */
|
||||
#define MSP_PIN_USB0_HOST_DEV 49
|
||||
|
||||
/* TWI USB1 host device pin */
|
||||
#define MSP_PIN_USB1_HOST_DEV 50
|
||||
|
||||
|
||||
static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci)
|
||||
{
|
||||
u8 *base;
|
||||
u8 *statreg;
|
||||
u8 *fiforeg;
|
||||
u32 val;
|
||||
struct ehci_regs *reg_base = ehci->regs;
|
||||
|
||||
/* get register base */
|
||||
base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE;
|
||||
statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS;
|
||||
fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO;
|
||||
|
||||
/* Disable controller mode stream */
|
||||
val = ehci_readl(ehci, (u32 *)base);
|
||||
ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE),
|
||||
(u32 *)base);
|
||||
|
||||
/* clear STS to select parallel transceiver interface */
|
||||
val = ehci_readl(ehci, (u32 *)statreg);
|
||||
val = val & ~USB_EHCI_REG_BIT_STAT_STS;
|
||||
ehci_writel(ehci, val, (u32 *)statreg);
|
||||
|
||||
/* write to set the proper fifo threshold */
|
||||
ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg);
|
||||
|
||||
/* set TWI GPIO USB_HOST_DEV pin high */
|
||||
gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1);
|
||||
}
|
||||
|
||||
/* called during probe() after chip reset completes */
|
||||
static int ehci_msp_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval;
|
||||
|
||||
ehci->big_endian_mmio = 1;
|
||||
ehci->big_endian_desc = 1;
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
hcd->has_tt = 1;
|
||||
|
||||
retval = ehci_setup(hcd);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
usb_hcd_tdi_set_mode(ehci);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* configure so an HC device and id are always provided
|
||||
* always called with process context; sleeping is OK
|
||||
*/
|
||||
|
||||
static int usb_hcd_msp_map_regs(struct mspusb_device *dev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct platform_device *pdev = &dev->dev;
|
||||
u32 res_len;
|
||||
int retval;
|
||||
|
||||
/* MAB register space */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res == NULL)
|
||||
return -ENOMEM;
|
||||
res_len = resource_size(res);
|
||||
if (!request_mem_region(res->start, res_len, "mab regs"))
|
||||
return -EBUSY;
|
||||
|
||||
dev->mab_regs = ioremap(res->start, res_len);
|
||||
if (dev->mab_regs == NULL) {
|
||||
retval = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* MSP USB register space */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
if (res == NULL) {
|
||||
retval = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
res_len = resource_size(res);
|
||||
if (!request_mem_region(res->start, res_len, "usbid regs")) {
|
||||
retval = -EBUSY;
|
||||
goto err2;
|
||||
}
|
||||
dev->usbid_regs = ioremap(res->start, res_len);
|
||||
if (dev->usbid_regs == NULL) {
|
||||
retval = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err3:
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
res_len = resource_size(res);
|
||||
release_mem_region(res->start, res_len);
|
||||
err2:
|
||||
iounmap(dev->mab_regs);
|
||||
err1:
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
res_len = resource_size(res);
|
||||
release_mem_region(res->start, res_len);
|
||||
dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_hcd_msp_probe - initialize PMC MSP-based HCDs
|
||||
* @driver: Pointer to hc driver instance
|
||||
* @dev: USB controller to probe
|
||||
*
|
||||
* Context: task context, might sleep
|
||||
*
|
||||
* Allocates basic resources for this USB host controller, and
|
||||
* then invokes the start() method for the HCD associated with it
|
||||
* through the hotplug entry's driver_data.
|
||||
*/
|
||||
int usb_hcd_msp_probe(const struct hc_driver *driver,
|
||||
struct platform_device *dev)
|
||||
{
|
||||
int retval;
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
struct ehci_hcd *ehci ;
|
||||
|
||||
hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
pr_debug("No IOMEM resource info for %s.\n", dev->name);
|
||||
retval = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
|
||||
retval = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
pr_debug("ioremap failed");
|
||||
retval = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
|
||||
retval = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
/* Map non-EHCI register spaces */
|
||||
retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
|
||||
if (retval != 0)
|
||||
goto err3;
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->big_endian_mmio = 1;
|
||||
ehci->big_endian_desc = 1;
|
||||
|
||||
|
||||
retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
|
||||
if (retval == 0) {
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
return 0;
|
||||
}
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
err3:
|
||||
iounmap(hcd->regs);
|
||||
err2:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err1:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
|
||||
* @hcd: USB Host Controller being removed
|
||||
*
|
||||
* Context: task context, might sleep
|
||||
*
|
||||
* Reverses the effect of usb_hcd_msp_probe(), first invoking
|
||||
* the HCD's stop() method. It is always called from a thread
|
||||
* context, normally "rmmod", "apmd", or something similar.
|
||||
*
|
||||
* may be called without controller electrically present
|
||||
* may be called with controller, bus, and devices active
|
||||
*/
|
||||
static void usb_hcd_msp_remove(struct usb_hcd *hcd)
|
||||
{
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_msp_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "PMC MSP EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_msp_setup,
|
||||
.shutdown = ehci_shutdown,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_debug("In ehci_hcd_msp_drv_probe");
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
|
||||
|
||||
ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_hcd_msp_remove(hcd);
|
||||
|
||||
/* free TWI GPIO USB_HOST_DEV pin */
|
||||
gpio_free(MSP_PIN_USB0_HOST_DEV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("pmcmsp-ehci");
|
||||
|
||||
static struct platform_driver ehci_hcd_msp_driver = {
|
||||
.probe = ehci_hcd_msp_drv_probe,
|
||||
.remove = ehci_hcd_msp_drv_remove,
|
||||
.driver = {
|
||||
.name = "pmcmsp-ehci",
|
||||
},
|
||||
};
|
|
@ -1858,9 +1858,11 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
|
|||
qh = kzalloc(sizeof(*qh), GFP_ATOMIC);
|
||||
if (!qh)
|
||||
goto done;
|
||||
qh->hw = dma_pool_zalloc(fotg210->qh_pool, flags, &dma);
|
||||
qh->hw = (struct fotg210_qh_hw *)
|
||||
dma_pool_alloc(fotg210->qh_pool, flags, &dma);
|
||||
if (!qh->hw)
|
||||
goto fail;
|
||||
memset(qh->hw, 0, sizeof(*qh->hw));
|
||||
qh->qh_dma = dma;
|
||||
INIT_LIST_HEAD(&qh->qtd_list);
|
||||
|
||||
|
@ -2510,11 +2512,6 @@ retry_xacterr:
|
|||
return count;
|
||||
}
|
||||
|
||||
/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
|
||||
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
|
||||
/* ... and packet size, for any kind of endpoint descriptor */
|
||||
#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
|
||||
|
||||
/* reverse of qh_urb_transaction: free a list of TDs.
|
||||
* used for cleanup after errors, before HC sees an URB's TDs.
|
||||
*/
|
||||
|
@ -2600,7 +2597,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210,
|
|||
token |= (1 /* "in" */ << 8);
|
||||
/* else it's already initted to "out" pid (0 << 8) */
|
||||
|
||||
maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
|
||||
maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input);
|
||||
|
||||
/*
|
||||
* buffer gets wrapped in one or more qtds;
|
||||
|
@ -2714,9 +2711,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
|
|||
gfp_t flags)
|
||||
{
|
||||
struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags);
|
||||
struct usb_host_endpoint *ep;
|
||||
u32 info1 = 0, info2 = 0;
|
||||
int is_input, type;
|
||||
int maxp = 0;
|
||||
int mult;
|
||||
struct usb_tt *tt = urb->dev->tt;
|
||||
struct fotg210_qh_hw *hw;
|
||||
|
||||
|
@ -2731,14 +2730,15 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
|
|||
|
||||
is_input = usb_pipein(urb->pipe);
|
||||
type = usb_pipetype(urb->pipe);
|
||||
maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
|
||||
ep = usb_pipe_endpoint(urb->dev, urb->pipe);
|
||||
maxp = usb_endpoint_maxp(&ep->desc);
|
||||
mult = usb_endpoint_maxp_mult(&ep->desc);
|
||||
|
||||
/* 1024 byte maxpacket is a hardware ceiling. High bandwidth
|
||||
* acts like up to 3KB, but is built from smaller packets.
|
||||
*/
|
||||
if (max_packet(maxp) > 1024) {
|
||||
fotg210_dbg(fotg210, "bogus qh maxpacket %d\n",
|
||||
max_packet(maxp));
|
||||
if (maxp > 1024) {
|
||||
fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -2752,8 +2752,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
|
|||
*/
|
||||
if (type == PIPE_INTERRUPT) {
|
||||
qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
|
||||
is_input, 0,
|
||||
hb_mult(maxp) * max_packet(maxp)));
|
||||
is_input, 0, mult * maxp));
|
||||
qh->start = NO_FRAME;
|
||||
|
||||
if (urb->dev->speed == USB_SPEED_HIGH) {
|
||||
|
@ -2790,7 +2789,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
|
|||
think_time = tt ? tt->think_time : 0;
|
||||
qh->tt_usecs = NS_TO_US(think_time +
|
||||
usb_calc_bus_time(urb->dev->speed,
|
||||
is_input, 0, max_packet(maxp)));
|
||||
is_input, 0, maxp));
|
||||
qh->period = urb->interval;
|
||||
if (qh->period > fotg210->periodic_size) {
|
||||
qh->period = fotg210->periodic_size;
|
||||
|
@ -2853,11 +2852,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
|
|||
* to help them do so. So now people expect to use
|
||||
* such nonconformant devices with Linux too; sigh.
|
||||
*/
|
||||
info1 |= max_packet(maxp) << 16;
|
||||
info1 |= maxp << 16;
|
||||
info2 |= (FOTG210_TUNE_MULT_HS << 30);
|
||||
} else { /* PIPE_INTERRUPT */
|
||||
info1 |= max_packet(maxp) << 16;
|
||||
info2 |= hb_mult(maxp) << 30;
|
||||
info1 |= maxp << 16;
|
||||
info2 |= mult << 30;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -3927,6 +3926,7 @@ static void iso_stream_init(struct fotg210_hcd *fotg210,
|
|||
int is_input;
|
||||
long bandwidth;
|
||||
unsigned multi;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
/*
|
||||
* this might be a "high bandwidth" highspeed endpoint,
|
||||
|
@ -3934,14 +3934,14 @@ static void iso_stream_init(struct fotg210_hcd *fotg210,
|
|||
*/
|
||||
epnum = usb_pipeendpoint(pipe);
|
||||
is_input = usb_pipein(pipe) ? USB_DIR_IN : 0;
|
||||
maxp = usb_maxpacket(dev, pipe, !is_input);
|
||||
ep = usb_pipe_endpoint(dev, pipe);
|
||||
maxp = usb_endpoint_maxp(&ep->desc);
|
||||
if (is_input)
|
||||
buf1 = (1 << 11);
|
||||
else
|
||||
buf1 = 0;
|
||||
|
||||
maxp = max_packet(maxp);
|
||||
multi = hb_mult(maxp);
|
||||
multi = usb_endpoint_maxp_mult(&ep->desc);
|
||||
buf1 |= maxp;
|
||||
maxp *= multi;
|
||||
|
||||
|
@ -4112,7 +4112,7 @@ static int itd_urb_transaction(struct fotg210_iso_stream *stream,
|
|||
} else {
|
||||
alloc_itd:
|
||||
spin_unlock_irqrestore(&fotg210->lock, flags);
|
||||
itd = dma_pool_zalloc(fotg210->itd_pool, mem_flags,
|
||||
itd = dma_pool_alloc(fotg210->itd_pool, mem_flags,
|
||||
&itd_dma);
|
||||
spin_lock_irqsave(&fotg210->lock, flags);
|
||||
if (!itd) {
|
||||
|
@ -4122,6 +4122,7 @@ alloc_itd:
|
|||
}
|
||||
}
|
||||
|
||||
memset(itd, 0, sizeof(*itd));
|
||||
itd->itd_dma = itd_dma;
|
||||
list_add(&itd->itd_list, &sched->td_list);
|
||||
}
|
||||
|
@ -4462,13 +4463,12 @@ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd)
|
|||
|
||||
/* HC need not update length with this error */
|
||||
if (!(t & FOTG210_ISOC_BABBLE)) {
|
||||
desc->actual_length =
|
||||
fotg210_itdlen(urb, desc, t);
|
||||
desc->actual_length = FOTG210_ITD_LENGTH(t);
|
||||
urb->actual_length += desc->actual_length;
|
||||
}
|
||||
} else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) {
|
||||
desc->status = 0;
|
||||
desc->actual_length = fotg210_itdlen(urb, desc, t);
|
||||
desc->actual_length = FOTG210_ITD_LENGTH(t);
|
||||
urb->actual_length += desc->actual_length;
|
||||
} else {
|
||||
/* URB was too late */
|
||||
|
|
|
@ -683,11 +683,6 @@ static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210)
|
|||
return fotg210_readl(fotg210, &fotg210->regs->frame_index);
|
||||
}
|
||||
|
||||
#define fotg210_itdlen(urb, desc, t) ({ \
|
||||
usb_pipein((urb)->pipe) ? \
|
||||
(desc)->length - FOTG210_ITD_LENGTH(t) : \
|
||||
FOTG210_ITD_LENGTH(t); \
|
||||
})
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#endif /* __LINUX_FOTG210_H */
|
||||
|
|
|
@ -84,7 +84,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
|
|||
|
||||
clk_prepare_enable(sohci_p->clk);
|
||||
|
||||
retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
|
||||
retval = usb_add_hcd(hcd, irq, 0);
|
||||
if (retval == 0) {
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
return retval;
|
||||
|
|
|
@ -202,6 +202,9 @@ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev)
|
|||
if (!cell)
|
||||
return -EINVAL;
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev));
|
||||
if (!hcd) {
|
||||
ret = -ENOMEM;
|
||||
|
|
|
@ -198,12 +198,13 @@ static void xhci_ring_dump_segment(struct seq_file *s,
|
|||
int i;
|
||||
dma_addr_t dma;
|
||||
union xhci_trb *trb;
|
||||
char str[XHCI_MSG_MAX];
|
||||
|
||||
for (i = 0; i < TRBS_PER_SEGMENT; i++) {
|
||||
trb = &seg->trbs[i];
|
||||
dma = seg->dma + i * sizeof(*trb);
|
||||
seq_printf(s, "%pad: %s\n", &dma,
|
||||
xhci_decode_trb(le32_to_cpu(trb->generic.field[0]),
|
||||
xhci_decode_trb(str, XHCI_MSG_MAX, le32_to_cpu(trb->generic.field[0]),
|
||||
le32_to_cpu(trb->generic.field[1]),
|
||||
le32_to_cpu(trb->generic.field[2]),
|
||||
le32_to_cpu(trb->generic.field[3])));
|
||||
|
@ -260,11 +261,13 @@ static int xhci_slot_context_show(struct seq_file *s, void *unused)
|
|||
struct xhci_slot_ctx *slot_ctx;
|
||||
struct xhci_slot_priv *priv = s->private;
|
||||
struct xhci_virt_device *dev = priv->dev;
|
||||
char str[XHCI_MSG_MAX];
|
||||
|
||||
xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
|
||||
slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
|
||||
seq_printf(s, "%pad: %s\n", &dev->out_ctx->dma,
|
||||
xhci_decode_slot_context(le32_to_cpu(slot_ctx->dev_info),
|
||||
xhci_decode_slot_context(str,
|
||||
le32_to_cpu(slot_ctx->dev_info),
|
||||
le32_to_cpu(slot_ctx->dev_info2),
|
||||
le32_to_cpu(slot_ctx->tt_info),
|
||||
le32_to_cpu(slot_ctx->dev_state)));
|
||||
|
@ -280,6 +283,7 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused)
|
|||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct xhci_slot_priv *priv = s->private;
|
||||
struct xhci_virt_device *dev = priv->dev;
|
||||
char str[XHCI_MSG_MAX];
|
||||
|
||||
xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
|
||||
|
||||
|
@ -287,7 +291,8 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused)
|
|||
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
|
||||
dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params);
|
||||
seq_printf(s, "%pad: %s\n", &dma,
|
||||
xhci_decode_ep_context(le32_to_cpu(ep_ctx->ep_info),
|
||||
xhci_decode_ep_context(str,
|
||||
le32_to_cpu(ep_ctx->ep_info),
|
||||
le32_to_cpu(ep_ctx->ep_info2),
|
||||
le64_to_cpu(ep_ctx->deq),
|
||||
le32_to_cpu(ep_ctx->tx_info)));
|
||||
|
@ -341,9 +346,10 @@ static int xhci_portsc_show(struct seq_file *s, void *unused)
|
|||
{
|
||||
struct xhci_port *port = s->private;
|
||||
u32 portsc;
|
||||
char str[XHCI_MSG_MAX];
|
||||
|
||||
portsc = readl(port->addr);
|
||||
seq_printf(s, "%s\n", xhci_decode_portsc(portsc));
|
||||
seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1667,7 +1667,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
status = 1;
|
||||
}
|
||||
if (!status && !reset_change) {
|
||||
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
|
||||
xhci_dbg(xhci, "%s: stopping usb%d port polling\n",
|
||||
__func__, hcd->self.busnum);
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
@ -1699,7 +1700,8 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|||
if (bus_state->resuming_ports || /* USB2 */
|
||||
bus_state->port_remote_wakeup) { /* USB3 */
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
xhci_dbg(xhci, "suspend failed because a port is resuming\n");
|
||||
xhci_dbg(xhci, "usb%d bus suspend to fail because a port is resuming\n",
|
||||
hcd->self.busnum);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ decode_ep(struct usb_host_endpoint *ep, enum usb_device_speed speed)
|
|||
interval /= 1000;
|
||||
}
|
||||
|
||||
snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s\n",
|
||||
snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s",
|
||||
usb_speed_string(speed), usb_endpoint_num(epd),
|
||||
usb_endpoint_dir_in(epd) ? "in" : "out",
|
||||
usb_ep_type_string(usb_endpoint_type(epd)),
|
||||
|
@ -129,6 +129,10 @@ get_bw_info(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
|
|||
int bw_index;
|
||||
|
||||
virt_dev = xhci->devs[udev->slot_id];
|
||||
if (!virt_dev->real_port) {
|
||||
WARN_ONCE(1, "%s invalid real_port\n", dev_name(&udev->dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (udev->speed >= USB_SPEED_SUPER) {
|
||||
if (usb_endpoint_dir_out(&ep->desc))
|
||||
|
@ -236,14 +240,20 @@ static void drop_tt(struct usb_device *udev)
|
|||
}
|
||||
}
|
||||
|
||||
static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx)
|
||||
static struct mu3h_sch_ep_info *
|
||||
create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx)
|
||||
{
|
||||
struct mu3h_sch_ep_info *sch_ep;
|
||||
struct mu3h_sch_bw_info *bw_info;
|
||||
struct mu3h_sch_tt *tt = NULL;
|
||||
u32 len_bw_budget_table;
|
||||
size_t mem_size;
|
||||
|
||||
bw_info = get_bw_info(mtk, udev, ep);
|
||||
if (!bw_info)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (is_fs_or_ls(udev->speed))
|
||||
len_bw_budget_table = TT_MICROFRAMES_MAX;
|
||||
else if ((udev->speed >= USB_SPEED_SUPER)
|
||||
|
@ -266,11 +276,13 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
|
|||
}
|
||||
}
|
||||
|
||||
sch_ep->bw_info = bw_info;
|
||||
sch_ep->sch_tt = tt;
|
||||
sch_ep->ep = ep;
|
||||
sch_ep->speed = udev->speed;
|
||||
INIT_LIST_HEAD(&sch_ep->endpoint);
|
||||
INIT_LIST_HEAD(&sch_ep->tt_endpoint);
|
||||
INIT_HLIST_NODE(&sch_ep->hentry);
|
||||
|
||||
return sch_ep;
|
||||
}
|
||||
|
@ -297,6 +309,7 @@ static void setup_sch_info(struct xhci_ep_ctx *ep_ctx,
|
|||
CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info));
|
||||
|
||||
sch_ep->esit = get_esit(ep_ctx);
|
||||
sch_ep->num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
sch_ep->ep_type = ep_type;
|
||||
sch_ep->maxpkt = maxpkt;
|
||||
sch_ep->offset = 0;
|
||||
|
@ -401,19 +414,16 @@ static void setup_sch_info(struct xhci_ep_ctx *ep_ctx,
|
|||
static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
struct mu3h_sch_ep_info *sch_ep, u32 offset)
|
||||
{
|
||||
u32 num_esit;
|
||||
u32 max_bw = 0;
|
||||
u32 bw;
|
||||
int i;
|
||||
int j;
|
||||
int i, j, k;
|
||||
|
||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
for (i = 0; i < num_esit; i++) {
|
||||
for (i = 0; i < sch_ep->num_esit; i++) {
|
||||
u32 base = offset + i * sch_ep->esit;
|
||||
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
|
||||
bw = sch_bw->bus_bw[base + j] +
|
||||
sch_ep->bw_budget_table[j];
|
||||
k = XHCI_MTK_BW_INDEX(base + j);
|
||||
bw = sch_bw->bus_bw[k] + sch_ep->bw_budget_table[j];
|
||||
if (bw > max_bw)
|
||||
max_bw = bw;
|
||||
}
|
||||
|
@ -424,21 +434,17 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
|
|||
static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
struct mu3h_sch_ep_info *sch_ep, bool used)
|
||||
{
|
||||
u32 num_esit;
|
||||
u32 base;
|
||||
int i;
|
||||
int j;
|
||||
int i, j, k;
|
||||
|
||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
for (i = 0; i < num_esit; i++) {
|
||||
for (i = 0; i < sch_ep->num_esit; i++) {
|
||||
base = sch_ep->offset + i * sch_ep->esit;
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
|
||||
k = XHCI_MTK_BW_INDEX(base + j);
|
||||
if (used)
|
||||
sch_bw->bus_bw[base + j] +=
|
||||
sch_ep->bw_budget_table[j];
|
||||
sch_bw->bus_bw[k] += sch_ep->bw_budget_table[j];
|
||||
else
|
||||
sch_bw->bus_bw[base + j] -=
|
||||
sch_ep->bw_budget_table[j];
|
||||
sch_bw->bus_bw[k] -= sch_ep->bw_budget_table[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -446,20 +452,20 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
|
|||
static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
|
||||
{
|
||||
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
|
||||
u32 num_esit, tmp;
|
||||
u32 tmp;
|
||||
int base;
|
||||
int i, j;
|
||||
int i, j, k;
|
||||
|
||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
for (i = 0; i < num_esit; i++) {
|
||||
for (i = 0; i < sch_ep->num_esit; i++) {
|
||||
base = offset + i * sch_ep->esit;
|
||||
|
||||
/*
|
||||
* Compared with hs bus, no matter what ep type,
|
||||
* the hub will always delay one uframe to send data
|
||||
*/
|
||||
for (j = 0; j < sch_ep->cs_count; j++) {
|
||||
tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe;
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
|
||||
k = XHCI_MTK_BW_INDEX(base + j);
|
||||
tmp = tt->fs_bus_bw[k] + sch_ep->bw_budget_table[j];
|
||||
if (tmp > FS_PAYLOAD_MAX)
|
||||
return -ESCH_BW_OVERFLOW;
|
||||
}
|
||||
|
@ -533,22 +539,19 @@ static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset)
|
|||
static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used)
|
||||
{
|
||||
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
|
||||
u32 base, num_esit;
|
||||
int bw_updated;
|
||||
int i, j;
|
||||
u32 base;
|
||||
int i, j, k;
|
||||
|
||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
|
||||
if (used)
|
||||
bw_updated = sch_ep->bw_cost_per_microframe;
|
||||
else
|
||||
bw_updated = -sch_ep->bw_cost_per_microframe;
|
||||
|
||||
for (i = 0; i < num_esit; i++) {
|
||||
for (i = 0; i < sch_ep->num_esit; i++) {
|
||||
base = sch_ep->offset + i * sch_ep->esit;
|
||||
|
||||
for (j = 0; j < sch_ep->cs_count; j++)
|
||||
tt->fs_bus_bw[base + j] += bw_updated;
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
|
||||
k = XHCI_MTK_BW_INDEX(base + j);
|
||||
if (used)
|
||||
tt->fs_bus_bw[k] += sch_ep->bw_budget_table[j];
|
||||
else
|
||||
tt->fs_bus_bw[k] -= sch_ep->bw_budget_table[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (used)
|
||||
|
@ -570,25 +573,9 @@ static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep)
|
||||
static int check_sch_bw(struct mu3h_sch_ep_info *sch_ep)
|
||||
{
|
||||
u32 boundary = sch_ep->esit;
|
||||
|
||||
if (sch_ep->sch_tt) { /* LS/FS with TT */
|
||||
/* tune for CS */
|
||||
if (sch_ep->ep_type != ISOC_OUT_EP)
|
||||
boundary++;
|
||||
else if (boundary > 1) /* normally esit >= 8 for FS/LS */
|
||||
boundary--;
|
||||
}
|
||||
|
||||
return boundary;
|
||||
}
|
||||
|
||||
static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
struct mu3h_sch_ep_info *sch_ep)
|
||||
{
|
||||
const u32 esit_boundary = get_esit_boundary(sch_ep);
|
||||
struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info;
|
||||
const u32 bw_boundary = get_bw_boundary(sch_ep->speed);
|
||||
u32 offset;
|
||||
u32 worst_bw;
|
||||
|
@ -605,9 +592,6 @@ static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw,
|
|||
if (ret)
|
||||
continue;
|
||||
|
||||
if ((offset + sch_ep->num_budget_microframes) > esit_boundary)
|
||||
break;
|
||||
|
||||
worst_bw = get_max_bw(sch_bw, sch_ep, offset);
|
||||
if (worst_bw > bw_boundary)
|
||||
continue;
|
||||
|
@ -633,23 +617,26 @@ static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw,
|
|||
return load_ep_bw(sch_bw, sch_ep, true);
|
||||
}
|
||||
|
||||
static void destroy_sch_ep(struct usb_device *udev,
|
||||
struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
|
||||
static void destroy_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
|
||||
struct mu3h_sch_ep_info *sch_ep)
|
||||
{
|
||||
/* only release ep bw check passed by check_sch_bw() */
|
||||
if (sch_ep->allocated)
|
||||
load_ep_bw(sch_bw, sch_ep, false);
|
||||
load_ep_bw(sch_ep->bw_info, sch_ep, false);
|
||||
|
||||
if (sch_ep->sch_tt)
|
||||
drop_tt(udev);
|
||||
|
||||
list_del(&sch_ep->endpoint);
|
||||
hlist_del(&sch_ep->hentry);
|
||||
kfree(sch_ep);
|
||||
}
|
||||
|
||||
static bool need_bw_sch(struct usb_host_endpoint *ep,
|
||||
enum usb_device_speed speed, int has_tt)
|
||||
static bool need_bw_sch(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
bool has_tt = udev->tt && udev->tt->hub->parent;
|
||||
|
||||
/* only for periodic endpoints */
|
||||
if (usb_endpoint_xfer_control(&ep->desc)
|
||||
|| usb_endpoint_xfer_bulk(&ep->desc))
|
||||
|
@ -660,7 +647,7 @@ static bool need_bw_sch(struct usb_host_endpoint *ep,
|
|||
* a TT are also ignored, root-hub will schedule them directly,
|
||||
* but need set @bpkts field of endpoint context to 1.
|
||||
*/
|
||||
if (is_fs_or_ls(speed) && !has_tt)
|
||||
if (is_fs_or_ls(udev->speed) && !has_tt)
|
||||
return false;
|
||||
|
||||
/* skip endpoint with zero maxpkt */
|
||||
|
@ -675,7 +662,6 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
|
|||
struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
|
||||
struct mu3h_sch_bw_info *sch_array;
|
||||
int num_usb_bus;
|
||||
int i;
|
||||
|
||||
/* ss IN and OUT are separated */
|
||||
num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports;
|
||||
|
@ -684,12 +670,10 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
|
|||
if (sch_array == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_usb_bus; i++)
|
||||
INIT_LIST_HEAD(&sch_array[i].bw_ep_list);
|
||||
|
||||
mtk->sch_array = sch_array;
|
||||
|
||||
INIT_LIST_HEAD(&mtk->bw_ep_chk_list);
|
||||
hash_init(mtk->sch_ep_hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -713,9 +697,7 @@ static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
ep_index = xhci_get_endpoint_index(&ep->desc);
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
|
||||
|
||||
xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
|
||||
|
||||
if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) {
|
||||
if (!need_bw_sch(udev, ep)) {
|
||||
/*
|
||||
* set @bpkts to 1 if it is LS or FS periodic endpoint, and its
|
||||
* device does not connected through an external HS hub
|
||||
|
@ -727,13 +709,16 @@ static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
sch_ep = create_sch_ep(udev, ep, ep_ctx);
|
||||
xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
|
||||
|
||||
sch_ep = create_sch_ep(mtk, udev, ep, ep_ctx);
|
||||
if (IS_ERR_OR_NULL(sch_ep))
|
||||
return -ENOMEM;
|
||||
|
||||
setup_sch_info(ep_ctx, sch_ep);
|
||||
|
||||
list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_chk_list);
|
||||
hash_add(mtk->sch_ep_hash, &sch_ep->hentry, (unsigned long)ep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -743,22 +728,18 @@ static void drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
{
|
||||
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_virt_device *virt_dev;
|
||||
struct mu3h_sch_bw_info *sch_bw;
|
||||
struct mu3h_sch_ep_info *sch_ep, *tmp;
|
||||
struct mu3h_sch_ep_info *sch_ep;
|
||||
struct hlist_node *hn;
|
||||
|
||||
virt_dev = xhci->devs[udev->slot_id];
|
||||
|
||||
xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
|
||||
|
||||
if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info))
|
||||
if (!need_bw_sch(udev, ep))
|
||||
return;
|
||||
|
||||
sch_bw = get_bw_info(mtk, udev, ep);
|
||||
xhci_err(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
|
||||
|
||||
list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) {
|
||||
hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep,
|
||||
hn, hentry, (unsigned long)ep) {
|
||||
if (sch_ep->ep == ep) {
|
||||
destroy_sch_ep(udev, sch_bw, sch_ep);
|
||||
destroy_sch_ep(mtk, udev, sch_ep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -769,30 +750,22 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
|
||||
struct mu3h_sch_bw_info *sch_bw;
|
||||
struct mu3h_sch_ep_info *sch_ep, *tmp;
|
||||
struct mu3h_sch_ep_info *sch_ep;
|
||||
int ret;
|
||||
|
||||
xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev));
|
||||
|
||||
list_for_each_entry(sch_ep, &mtk->bw_ep_chk_list, endpoint) {
|
||||
sch_bw = get_bw_info(mtk, udev, sch_ep->ep);
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct usb_host_endpoint *ep = sch_ep->ep;
|
||||
unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);
|
||||
|
||||
ret = check_sch_bw(sch_bw, sch_ep);
|
||||
ret = check_sch_bw(sch_ep);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "Not enough bandwidth! (%s)\n",
|
||||
sch_error_string(-ret));
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) {
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct usb_host_endpoint *ep = sch_ep->ep;
|
||||
unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);
|
||||
|
||||
sch_bw = get_bw_info(mtk, udev, ep);
|
||||
list_move_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
|
||||
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
|
||||
ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(sch_ep->pkts)
|
||||
|
@ -806,22 +779,23 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
sch_ep->offset, sch_ep->repeat);
|
||||
}
|
||||
|
||||
return xhci_check_bandwidth(hcd, udev);
|
||||
ret = xhci_check_bandwidth(hcd, udev);
|
||||
if (!ret)
|
||||
INIT_LIST_HEAD(&mtk->bw_ep_chk_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
{
|
||||
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct mu3h_sch_bw_info *sch_bw;
|
||||
struct mu3h_sch_ep_info *sch_ep, *tmp;
|
||||
|
||||
xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev));
|
||||
|
||||
list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) {
|
||||
sch_bw = get_bw_info(mtk, udev, sch_ep->ep);
|
||||
destroy_sch_ep(udev, sch_bw, sch_ep);
|
||||
}
|
||||
list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint)
|
||||
destroy_sch_ep(mtk, udev, sch_ep);
|
||||
|
||||
xhci_reset_bandwidth(hcd, udev);
|
||||
}
|
||||
|
|
|
@ -56,6 +56,27 @@
|
|||
/* u2_phy_pll register */
|
||||
#define CTRL_U2_FORCE_PLL_STB BIT(28)
|
||||
|
||||
/* xHCI CSR */
|
||||
#define LS_EOF_CFG 0x930
|
||||
#define LSEOF_OFFSET 0x89
|
||||
|
||||
#define FS_EOF_CFG 0x934
|
||||
#define FSEOF_OFFSET 0x2e
|
||||
|
||||
#define SS_GEN1_EOF_CFG 0x93c
|
||||
#define SSG1EOF_OFFSET 0x78
|
||||
|
||||
#define HFCNTR_CFG 0x944
|
||||
#define ITP_DELTA_CLK (0xa << 1)
|
||||
#define ITP_DELTA_CLK_MASK GENMASK(5, 1)
|
||||
#define FRMCNT_LEV1_RANG (0x12b << 8)
|
||||
#define FRMCNT_LEV1_RANG_MASK GENMASK(19, 8)
|
||||
|
||||
#define SS_GEN2_EOF_CFG 0x990
|
||||
#define SSG2EOF_OFFSET 0x3c
|
||||
|
||||
#define XSEOF_OFFSET_MASK GENMASK(11, 0)
|
||||
|
||||
/* usb remote wakeup registers in syscon */
|
||||
|
||||
/* mt8173 etc */
|
||||
|
@ -86,6 +107,46 @@ enum ssusb_uwk_vers {
|
|||
SSUSB_UWK_V1_2, /* specific revision 1.2 */
|
||||
};
|
||||
|
||||
/*
|
||||
* MT8195 has 4 controllers, the controller1~3's default SOF/ITP interval
|
||||
* is calculated from the frame counter clock 24M, but in fact, the clock
|
||||
* is 48M, add workaround for it.
|
||||
*/
|
||||
static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
struct device *dev = mtk->dev;
|
||||
struct usb_hcd *hcd = mtk->hcd;
|
||||
u32 value;
|
||||
|
||||
if (!of_device_is_compatible(dev->of_node, "mediatek,mt8195-xhci"))
|
||||
return;
|
||||
|
||||
value = readl(hcd->regs + HFCNTR_CFG);
|
||||
value &= ~(ITP_DELTA_CLK_MASK | FRMCNT_LEV1_RANG_MASK);
|
||||
value |= (ITP_DELTA_CLK | FRMCNT_LEV1_RANG);
|
||||
writel(value, hcd->regs + HFCNTR_CFG);
|
||||
|
||||
value = readl(hcd->regs + LS_EOF_CFG);
|
||||
value &= ~XSEOF_OFFSET_MASK;
|
||||
value |= LSEOF_OFFSET;
|
||||
writel(value, hcd->regs + LS_EOF_CFG);
|
||||
|
||||
value = readl(hcd->regs + FS_EOF_CFG);
|
||||
value &= ~XSEOF_OFFSET_MASK;
|
||||
value |= FSEOF_OFFSET;
|
||||
writel(value, hcd->regs + FS_EOF_CFG);
|
||||
|
||||
value = readl(hcd->regs + SS_GEN1_EOF_CFG);
|
||||
value &= ~XSEOF_OFFSET_MASK;
|
||||
value |= SSG1EOF_OFFSET;
|
||||
writel(value, hcd->regs + SS_GEN1_EOF_CFG);
|
||||
|
||||
value = readl(hcd->regs + SS_GEN2_EOF_CFG);
|
||||
value &= ~XSEOF_OFFSET_MASK;
|
||||
value |= SSG2EOF_OFFSET;
|
||||
writel(value, hcd->regs + SS_GEN2_EOF_CFG);
|
||||
}
|
||||
|
||||
static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
|
||||
|
@ -115,8 +176,11 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
|
|||
writel(value, &ippc->u3_ctrl_p[i]);
|
||||
}
|
||||
|
||||
/* power on and enable all u2 ports */
|
||||
/* power on and enable all u2 ports except skipped ones */
|
||||
for (i = 0; i < mtk->num_u2_ports; i++) {
|
||||
if (BIT(i) & mtk->u2p_dis_msk)
|
||||
continue;
|
||||
|
||||
value = readl(&ippc->u2_ctrl_p[i]);
|
||||
value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS);
|
||||
value |= CTRL_U2_PORT_HOST_SEL;
|
||||
|
@ -163,8 +227,11 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
|
|||
writel(value, &ippc->u3_ctrl_p[i]);
|
||||
}
|
||||
|
||||
/* power down all u2 ports */
|
||||
/* power down all u2 ports except skipped ones */
|
||||
for (i = 0; i < mtk->num_u2_ports; i++) {
|
||||
if (BIT(i) & mtk->u2p_dis_msk)
|
||||
continue;
|
||||
|
||||
value = readl(&ippc->u2_ctrl_p[i]);
|
||||
value |= CTRL_U2_PORT_PDN;
|
||||
writel(value, &ippc->u2_ctrl_p[i]);
|
||||
|
@ -361,6 +428,9 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
|
|||
ret = xhci_mtk_ssusb_config(mtk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* workaround only for mt8195 */
|
||||
xhci_mtk_set_frame_interval(mtk);
|
||||
}
|
||||
|
||||
ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
|
||||
|
@ -444,6 +514,8 @@ static int xhci_mtk_probe(struct platform_device *pdev)
|
|||
/* optional property, ignore the error if it does not exist */
|
||||
of_property_read_u32(node, "mediatek,u3p-dis-msk",
|
||||
&mtk->u3p_dis_msk);
|
||||
of_property_read_u32(node, "mediatek,u2p-dis-msk",
|
||||
&mtk->u2p_dis_msk);
|
||||
|
||||
ret = usb_wakeup_of_property_parse(mtk, node);
|
||||
if (ret) {
|
||||
|
@ -569,7 +641,7 @@ disable_ldos:
|
|||
xhci_mtk_ldos_disable(mtk);
|
||||
|
||||
disable_pm:
|
||||
pm_runtime_put_sync_autosuspend(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
@ -704,6 +776,7 @@ static const struct dev_pm_ops xhci_mtk_pm_ops = {
|
|||
|
||||
static const struct of_device_id mtk_xhci_of_match[] = {
|
||||
{ .compatible = "mediatek,mt8173-xhci"},
|
||||
{ .compatible = "mediatek,mt8195-xhci"},
|
||||
{ .compatible = "mediatek,mtk-xhci"},
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -10,18 +10,23 @@
|
|||
#define _XHCI_MTK_H_
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
#define BULK_CLKS_NUM 5
|
||||
|
||||
/* support at most 64 ep, use 32 size hash table */
|
||||
#define SCH_EP_HASH_BITS 5
|
||||
|
||||
/**
|
||||
* To simplify scheduler algorithm, set a upper limit for ESIT,
|
||||
* if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT,
|
||||
* round down to the limit value, that means allocating more
|
||||
* bandwidth to it.
|
||||
*/
|
||||
#define XHCI_MTK_MAX_ESIT 64
|
||||
#define XHCI_MTK_MAX_ESIT (1 << 6)
|
||||
#define XHCI_MTK_BW_INDEX(x) ((x) & (XHCI_MTK_MAX_ESIT - 1))
|
||||
|
||||
/**
|
||||
* @fs_bus_bw: array to keep track of bandwidth already used for FS
|
||||
|
@ -36,25 +41,26 @@ struct mu3h_sch_tt {
|
|||
* struct mu3h_sch_bw_info: schedule information for bandwidth domain
|
||||
*
|
||||
* @bus_bw: array to keep track of bandwidth already used at each uframes
|
||||
* @bw_ep_list: eps in the bandwidth domain
|
||||
*
|
||||
* treat a HS root port as a bandwidth domain, but treat a SS root port as
|
||||
* two bandwidth domains, one for IN eps and another for OUT eps.
|
||||
*/
|
||||
struct mu3h_sch_bw_info {
|
||||
u32 bus_bw[XHCI_MTK_MAX_ESIT];
|
||||
struct list_head bw_ep_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mu3h_sch_ep_info: schedule information for endpoint
|
||||
*
|
||||
* @esit: unit is 125us, equal to 2 << Interval field in ep-context
|
||||
* @num_esit: number of @esit in a period
|
||||
* @num_budget_microframes: number of continuous uframes
|
||||
* (@repeat==1) scheduled within the interval
|
||||
* @bw_cost_per_microframe: bandwidth cost per microframe
|
||||
* @hentry: hash table entry
|
||||
* @endpoint: linked into bandwidth domain which it belongs to
|
||||
* @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to
|
||||
* @bw_info: bandwidth domain which this endpoint belongs
|
||||
* @sch_tt: mu3h_sch_tt linked into
|
||||
* @ep_type: endpoint type
|
||||
* @maxpkt: max packet size of endpoint
|
||||
|
@ -79,10 +85,13 @@ struct mu3h_sch_bw_info {
|
|||
*/
|
||||
struct mu3h_sch_ep_info {
|
||||
u32 esit;
|
||||
u32 num_esit;
|
||||
u32 num_budget_microframes;
|
||||
u32 bw_cost_per_microframe;
|
||||
struct list_head endpoint;
|
||||
struct hlist_node hentry;
|
||||
struct list_head tt_endpoint;
|
||||
struct mu3h_sch_bw_info *bw_info;
|
||||
struct mu3h_sch_tt *sch_tt;
|
||||
u32 ep_type;
|
||||
u32 maxpkt;
|
||||
|
@ -135,9 +144,11 @@ struct xhci_hcd_mtk {
|
|||
struct usb_hcd *hcd;
|
||||
struct mu3h_sch_bw_info *sch_array;
|
||||
struct list_head bw_ep_chk_list;
|
||||
DECLARE_HASHTABLE(sch_ep_hash, SCH_EP_HASH_BITS);
|
||||
struct mu3c_ippc_regs __iomem *ippc_regs;
|
||||
int num_u2_ports;
|
||||
int num_u3_ports;
|
||||
int u2p_dis_msk;
|
||||
int u3p_dis_msk;
|
||||
struct regulator *vusb33;
|
||||
struct regulator *vbus;
|
||||
|
|
|
@ -599,7 +599,7 @@ int renesas_xhci_check_request_fw(struct pci_dev *pdev,
|
|||
|
||||
err = renesas_fw_check_running(pdev);
|
||||
/* Continue ahead, if the firmware is already running. */
|
||||
if (err == 0)
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
/* no firmware interface available */
|
||||
|
@ -631,9 +631,4 @@ exit:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
|
||||
|
||||
void renesas_xhci_pci_exit(struct pci_dev *dev)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(renesas_xhci_pci_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -449,8 +449,6 @@ static void xhci_pci_remove(struct pci_dev *dev)
|
|||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(pci_get_drvdata(dev));
|
||||
if (xhci->quirks & XHCI_RENESAS_FW_QUIRK)
|
||||
renesas_xhci_pci_exit(dev);
|
||||
|
||||
xhci->xhc_state |= XHCI_STATE_REMOVING;
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
|
||||
int renesas_xhci_check_request_fw(struct pci_dev *dev,
|
||||
const struct pci_device_id *id);
|
||||
void renesas_xhci_pci_exit(struct pci_dev *dev);
|
||||
|
||||
#else
|
||||
static int renesas_xhci_check_request_fw(struct pci_dev *dev,
|
||||
|
@ -16,8 +15,6 @@ static int renesas_xhci_check_request_fw(struct pci_dev *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void renesas_xhci_pci_exit(struct pci_dev *dev) { };
|
||||
|
||||
#endif
|
||||
|
||||
struct xhci_driver_data {
|
||||
|
|
|
@ -134,6 +134,13 @@ static int xhci_rcar_download_firmware(struct usb_hcd *hcd)
|
|||
const struct soc_device_attribute *attr;
|
||||
const char *firmware_name;
|
||||
|
||||
/*
|
||||
* According to the datasheet, "Upon the completion of FW Download,
|
||||
* there is no need to write or reload FW".
|
||||
*/
|
||||
if (readl(regs + RCAR_USB3_DL_CTRL) & RCAR_USB3_DL_CTRL_FW_SUCCESS)
|
||||
return 0;
|
||||
|
||||
attr = soc_device_match(rcar_quirks_match);
|
||||
if (attr)
|
||||
quirks = (uintptr_t)attr->data;
|
||||
|
|
|
@ -830,9 +830,14 @@ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep)
|
|||
|
||||
ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb);
|
||||
|
||||
if (td->cancel_status == TD_CLEARED)
|
||||
if (td->cancel_status == TD_CLEARED) {
|
||||
xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
|
||||
__func__, td->urb);
|
||||
xhci_td_cleanup(ep->xhci, td, ring, td->status);
|
||||
|
||||
} else {
|
||||
xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||||
__func__, td->urb, td->cancel_status);
|
||||
}
|
||||
if (ep->xhci->xhc_state & XHCI_STATE_DYING)
|
||||
return;
|
||||
}
|
||||
|
@ -850,6 +855,10 @@ static int xhci_reset_halted_ep(struct xhci_hcd *xhci, unsigned int slot_id,
|
|||
goto done;
|
||||
}
|
||||
|
||||
xhci_dbg(xhci, "%s-reset ep %u, slot %u\n",
|
||||
(reset_type == EP_HARD_RESET) ? "Hard" : "Soft",
|
||||
ep_index, slot_id);
|
||||
|
||||
ret = xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type);
|
||||
done:
|
||||
if (ret)
|
||||
|
@ -883,7 +892,8 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
|||
}
|
||||
|
||||
if (ep->ep_state & EP_HALTED) {
|
||||
xhci_dbg(xhci, "Reset ep command already pending\n");
|
||||
xhci_dbg(xhci, "Reset ep command for ep_index %d already pending\n",
|
||||
ep->ep_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -922,9 +932,10 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
|||
|
||||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Removing canceled TD starting at 0x%llx (dma).",
|
||||
(unsigned long long)xhci_trb_virt_to_dma(
|
||||
td->start_seg, td->first_trb));
|
||||
"Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p",
|
||||
(unsigned long long)xhci_trb_virt_to_dma(
|
||||
td->start_seg, td->first_trb),
|
||||
td->urb->stream_id, td->urb);
|
||||
list_del_init(&td->td_list);
|
||||
ring = xhci_urb_to_transfer_ring(xhci, td->urb);
|
||||
if (!ring) {
|
||||
|
@ -942,17 +953,21 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
|||
td->urb->stream_id);
|
||||
hw_deq &= ~0xf;
|
||||
|
||||
if (td->cancel_status == TD_HALTED) {
|
||||
cached_td = td;
|
||||
} else if (trb_in_td(xhci, td->start_seg, td->first_trb,
|
||||
td->last_trb, hw_deq, false)) {
|
||||
if (td->cancel_status == TD_HALTED ||
|
||||
trb_in_td(xhci, td->start_seg, td->first_trb, td->last_trb, hw_deq, false)) {
|
||||
switch (td->cancel_status) {
|
||||
case TD_CLEARED: /* TD is already no-op */
|
||||
case TD_CLEARING_CACHE: /* set TR deq command already queued */
|
||||
break;
|
||||
case TD_DIRTY: /* TD is cached, clear it */
|
||||
case TD_HALTED:
|
||||
/* FIXME stream case, several stopped rings */
|
||||
td->cancel_status = TD_CLEARING_CACHE;
|
||||
if (cached_td)
|
||||
/* FIXME stream case, several stopped rings */
|
||||
xhci_dbg(xhci,
|
||||
"Move dq past stream %u URB %p instead of stream %u URB %p\n",
|
||||
td->urb->stream_id, td->urb,
|
||||
cached_td->urb->stream_id, cached_td->urb);
|
||||
cached_td = td;
|
||||
break;
|
||||
}
|
||||
|
@ -961,18 +976,24 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
|||
td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
}
|
||||
if (cached_td) {
|
||||
cached_td->cancel_status = TD_CLEARING_CACHE;
|
||||
|
||||
err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index,
|
||||
cached_td->urb->stream_id,
|
||||
cached_td);
|
||||
/* Failed to move past cached td, try just setting it noop */
|
||||
if (err) {
|
||||
td_to_noop(xhci, ring, cached_td, false);
|
||||
cached_td->cancel_status = TD_CLEARED;
|
||||
/* If there's no need to move the dequeue pointer then we're done */
|
||||
if (!cached_td)
|
||||
return 0;
|
||||
|
||||
err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index,
|
||||
cached_td->urb->stream_id,
|
||||
cached_td);
|
||||
if (err) {
|
||||
/* Failed to move past cached td, just set cached TDs to no-op */
|
||||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
|
||||
if (td->cancel_status != TD_CLEARING_CACHE)
|
||||
continue;
|
||||
xhci_dbg(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
|
||||
td->urb);
|
||||
td_to_noop(xhci, ring, td, false);
|
||||
td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
cached_td = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1069,6 +1090,8 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
|||
return;
|
||||
case EP_STATE_RUNNING:
|
||||
/* Race, HW handled stop ep cmd before ep was running */
|
||||
xhci_dbg(xhci, "Stop ep completion ctx error, ep is running\n");
|
||||
|
||||
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command)
|
||||
xhci_stop_watchdog_timer_in_irq(xhci, ep);
|
||||
|
@ -1212,6 +1235,7 @@ void xhci_stop_endpoint_command_watchdog(struct timer_list *t)
|
|||
struct xhci_hcd *xhci = ep->xhci;
|
||||
unsigned long flags;
|
||||
u32 usbsts;
|
||||
char str[XHCI_MSG_MAX];
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
|
@ -1225,7 +1249,7 @@ void xhci_stop_endpoint_command_watchdog(struct timer_list *t)
|
|||
usbsts = readl(&xhci->op_regs->status);
|
||||
|
||||
xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n");
|
||||
xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(usbsts));
|
||||
xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(str, usbsts));
|
||||
|
||||
ep->ep_state &= ~EP_STOP_CMD_PENDING;
|
||||
|
||||
|
@ -1389,7 +1413,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb);
|
||||
if (td->cancel_status == TD_CLEARING_CACHE) {
|
||||
td->cancel_status = TD_CLEARED;
|
||||
xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
|
||||
__func__, td->urb);
|
||||
xhci_td_cleanup(ep->xhci, td, ep_ring, td->status);
|
||||
} else {
|
||||
xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||||
__func__, td->urb, td->cancel_status);
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
|
@ -2002,7 +2031,8 @@ cleanup:
|
|||
* bits are still set. When an event occurs, switch over to
|
||||
* polling to avoid losing status changes.
|
||||
*/
|
||||
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
|
||||
xhci_dbg(xhci, "%s: starting usb%d port polling.\n",
|
||||
__func__, hcd->self.busnum);
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
spin_unlock(&xhci->lock);
|
||||
/* Pass this up to the core */
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include "xhci.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
|
||||
#define XHCI_MSG_MAX 500
|
||||
|
||||
DECLARE_EVENT_CLASS(xhci_log_msg,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf),
|
||||
|
@ -122,6 +120,7 @@ DECLARE_EVENT_CLASS(xhci_log_trb,
|
|||
__field(u32, field1)
|
||||
__field(u32, field2)
|
||||
__field(u32, field3)
|
||||
__dynamic_array(char, str, XHCI_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->type = ring->type;
|
||||
|
@ -131,7 +130,7 @@ DECLARE_EVENT_CLASS(xhci_log_trb,
|
|||
__entry->field3 = le32_to_cpu(trb->field[3]);
|
||||
),
|
||||
TP_printk("%s: %s", xhci_ring_type_string(__entry->type),
|
||||
xhci_decode_trb(__entry->field0, __entry->field1,
|
||||
xhci_decode_trb(__get_str(str), XHCI_MSG_MAX, __entry->field0, __entry->field1,
|
||||
__entry->field2, __entry->field3)
|
||||
)
|
||||
);
|
||||
|
@ -323,6 +322,7 @@ DECLARE_EVENT_CLASS(xhci_log_ep_ctx,
|
|||
__field(u32, info2)
|
||||
__field(u64, deq)
|
||||
__field(u32, tx_info)
|
||||
__dynamic_array(char, str, XHCI_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->info = le32_to_cpu(ctx->ep_info);
|
||||
|
@ -330,8 +330,8 @@ DECLARE_EVENT_CLASS(xhci_log_ep_ctx,
|
|||
__entry->deq = le64_to_cpu(ctx->deq);
|
||||
__entry->tx_info = le32_to_cpu(ctx->tx_info);
|
||||
),
|
||||
TP_printk("%s", xhci_decode_ep_context(__entry->info,
|
||||
__entry->info2, __entry->deq, __entry->tx_info)
|
||||
TP_printk("%s", xhci_decode_ep_context(__get_str(str),
|
||||
__entry->info, __entry->info2, __entry->deq, __entry->tx_info)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -368,6 +368,7 @@ DECLARE_EVENT_CLASS(xhci_log_slot_ctx,
|
|||
__field(u32, info2)
|
||||
__field(u32, tt_info)
|
||||
__field(u32, state)
|
||||
__dynamic_array(char, str, XHCI_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->info = le32_to_cpu(ctx->dev_info);
|
||||
|
@ -375,9 +376,9 @@ DECLARE_EVENT_CLASS(xhci_log_slot_ctx,
|
|||
__entry->tt_info = le64_to_cpu(ctx->tt_info);
|
||||
__entry->state = le32_to_cpu(ctx->dev_state);
|
||||
),
|
||||
TP_printk("%s", xhci_decode_slot_context(__entry->info,
|
||||
__entry->info2, __entry->tt_info,
|
||||
__entry->state)
|
||||
TP_printk("%s", xhci_decode_slot_context(__get_str(str),
|
||||
__entry->info, __entry->info2,
|
||||
__entry->tt_info, __entry->state)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -432,12 +433,13 @@ DECLARE_EVENT_CLASS(xhci_log_ctrl_ctx,
|
|||
TP_STRUCT__entry(
|
||||
__field(u32, drop)
|
||||
__field(u32, add)
|
||||
__dynamic_array(char, str, XHCI_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->drop = le32_to_cpu(ctrl_ctx->drop_flags);
|
||||
__entry->add = le32_to_cpu(ctrl_ctx->add_flags);
|
||||
),
|
||||
TP_printk("%s", xhci_decode_ctrl_ctx(__entry->drop, __entry->add)
|
||||
TP_printk("%s", xhci_decode_ctrl_ctx(__get_str(str), __entry->drop, __entry->add)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -523,6 +525,7 @@ DECLARE_EVENT_CLASS(xhci_log_portsc,
|
|||
TP_STRUCT__entry(
|
||||
__field(u32, portnum)
|
||||
__field(u32, portsc)
|
||||
__dynamic_array(char, str, XHCI_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->portnum = portnum;
|
||||
|
@ -530,7 +533,7 @@ DECLARE_EVENT_CLASS(xhci_log_portsc,
|
|||
),
|
||||
TP_printk("port-%d: %s",
|
||||
__entry->portnum,
|
||||
xhci_decode_portsc(__entry->portsc)
|
||||
xhci_decode_portsc(__get_str(str), __entry->portsc)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -555,13 +558,14 @@ DECLARE_EVENT_CLASS(xhci_log_doorbell,
|
|||
TP_STRUCT__entry(
|
||||
__field(u32, slot)
|
||||
__field(u32, doorbell)
|
||||
__dynamic_array(char, str, XHCI_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->slot = slot;
|
||||
__entry->doorbell = doorbell;
|
||||
),
|
||||
TP_printk("Ring doorbell for %s",
|
||||
xhci_decode_doorbell(__entry->slot, __entry->doorbell)
|
||||
xhci_decode_doorbell(__get_str(str), __entry->slot, __entry->doorbell)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -993,7 +993,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
|
|||
xhci_dbc_suspend(xhci);
|
||||
|
||||
/* Don't poll the roothubs on bus suspend. */
|
||||
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
|
||||
xhci_dbg(xhci, "%s: stopping usb%d port polling.\n",
|
||||
__func__, hcd->self.busnum);
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
|
||||
|
@ -1257,7 +1258,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
|||
usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller));
|
||||
|
||||
/* Re-enable port polling. */
|
||||
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
|
||||
xhci_dbg(xhci, "%s: starting usb%d port polling.\n",
|
||||
__func__, hcd->self.busnum);
|
||||
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
|
||||
usb_hcd_poll_rh_status(xhci->shared_hcd);
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
@ -4705,19 +4707,19 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
|
|||
{
|
||||
unsigned long long timeout_ns;
|
||||
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
|
||||
else
|
||||
timeout_ns = udev->u1_params.sel;
|
||||
|
||||
/* Prevent U1 if service interval is shorter than U1 exit latency */
|
||||
if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
|
||||
if (xhci_service_interval_to_ns(desc) <= timeout_ns) {
|
||||
if (xhci_service_interval_to_ns(desc) <= udev->u1_params.mel) {
|
||||
dev_dbg(&udev->dev, "Disable U1, ESIT shorter than exit latency\n");
|
||||
return USB3_LPM_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
|
||||
else
|
||||
timeout_ns = udev->u1_params.sel;
|
||||
|
||||
/* The U1 timeout is encoded in 1us intervals.
|
||||
* Don't return a timeout of zero, because that's USB3_LPM_DISABLED.
|
||||
*/
|
||||
|
@ -4769,19 +4771,19 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
|
|||
{
|
||||
unsigned long long timeout_ns;
|
||||
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
|
||||
else
|
||||
timeout_ns = udev->u2_params.sel;
|
||||
|
||||
/* Prevent U2 if service interval is shorter than U2 exit latency */
|
||||
if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
|
||||
if (xhci_service_interval_to_ns(desc) <= timeout_ns) {
|
||||
if (xhci_service_interval_to_ns(desc) <= udev->u2_params.mel) {
|
||||
dev_dbg(&udev->dev, "Disable U2, ESIT shorter than exit latency\n");
|
||||
return USB3_LPM_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
|
||||
else
|
||||
timeout_ns = udev->u2_params.sel;
|
||||
|
||||
/* The U2 timeout is encoded in 256us intervals */
|
||||
timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000);
|
||||
/* If the necessary timeout value is bigger than what we can set in the
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#include "xhci-ext-caps.h"
|
||||
#include "pci-quirks.h"
|
||||
|
||||
/* max buffer size for trace and debug messages */
|
||||
#define XHCI_MSG_MAX 500
|
||||
|
||||
/* xHCI PCI Configuration Registers */
|
||||
#define XHCI_SBRN_OFFSET (0x60)
|
||||
|
||||
|
@ -2235,15 +2238,14 @@ static inline char *xhci_slot_state_string(u32 state)
|
|||
}
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
||||
u32 field3)
|
||||
static inline const char *xhci_decode_trb(char *str, size_t size,
|
||||
u32 field0, u32 field1, u32 field2, u32 field3)
|
||||
{
|
||||
static char str[256];
|
||||
int type = TRB_FIELD_TO_TYPE(field3);
|
||||
|
||||
switch (type) {
|
||||
case TRB_LINK:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"LINK %08x%08x intr %d type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, GET_INTR_TARGET(field2),
|
||||
xhci_trb_type_string(type),
|
||||
|
@ -2260,7 +2262,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
case TRB_HC_EVENT:
|
||||
case TRB_DEV_NOTE:
|
||||
case TRB_MFINDEX_WRAP:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"TRB %08x%08x status '%s' len %d slot %d ep %d type '%s' flags %c:%c",
|
||||
field1, field0,
|
||||
xhci_trb_comp_code_string(GET_COMP_CODE(field2)),
|
||||
|
@ -2273,7 +2275,8 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
|
||||
break;
|
||||
case TRB_SETUP:
|
||||
sprintf(str, "bRequestType %02x bRequest %02x wValue %02x%02x wIndex %02x%02x wLength %d length %d TD size %d intr %d type '%s' flags %c:%c:%c",
|
||||
snprintf(str, size,
|
||||
"bRequestType %02x bRequest %02x wValue %02x%02x wIndex %02x%02x wLength %d length %d TD size %d intr %d type '%s' flags %c:%c:%c",
|
||||
field0 & 0xff,
|
||||
(field0 & 0xff00) >> 8,
|
||||
(field0 & 0xff000000) >> 24,
|
||||
|
@ -2290,7 +2293,8 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_DATA:
|
||||
sprintf(str, "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c",
|
||||
snprintf(str, size,
|
||||
"Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c",
|
||||
field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
xhci_trb_type_string(type),
|
||||
|
@ -2303,7 +2307,8 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_STATUS:
|
||||
sprintf(str, "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c",
|
||||
snprintf(str, size,
|
||||
"Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
xhci_trb_type_string(type),
|
||||
|
@ -2316,7 +2321,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
case TRB_ISOC:
|
||||
case TRB_EVENT_DATA:
|
||||
case TRB_TR_NOOP:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c:%c",
|
||||
field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
|
@ -2333,21 +2338,21 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
|
||||
case TRB_CMD_NOOP:
|
||||
case TRB_ENABLE_SLOT:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_DISABLE_SLOT:
|
||||
case TRB_NEG_BANDWIDTH:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: slot %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_ADDR_DEV:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %d flags %c:%c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2356,7 +2361,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_CONFIG_EP:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %d flags %c:%c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2365,7 +2370,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_EVAL_CONTEXT:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2373,7 +2378,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_RESET_EP:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %d ep %d flags %c:%c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2394,7 +2399,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_SET_DEQ:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: deq %08x%08x stream %d slot %d ep %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2405,14 +2410,14 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_RESET_DEV:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: slot %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_FORCE_EVENT:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: event %08x%08x vf intr %d vf id %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2421,14 +2426,14 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_SET_LT:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: belt %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
TRB_TO_BELT(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_GET_BW:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %d speed %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
field1, field0,
|
||||
|
@ -2437,7 +2442,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_FORCE_HEADER:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"%s: info %08x%08x%08x pkt type %d roothub port %d flags %c",
|
||||
xhci_trb_type_string(type),
|
||||
field2, field1, field0 & 0xffffffe0,
|
||||
|
@ -2446,7 +2451,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
default:
|
||||
sprintf(str,
|
||||
snprintf(str, size,
|
||||
"type '%s' -> raw %08x %08x %08x %08x",
|
||||
xhci_trb_type_string(type),
|
||||
field0, field1, field2, field3);
|
||||
|
@ -2455,10 +2460,9 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
|
|||
return str;
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_ctrl_ctx(unsigned long drop,
|
||||
unsigned long add)
|
||||
static inline const char *xhci_decode_ctrl_ctx(char *str,
|
||||
unsigned long drop, unsigned long add)
|
||||
{
|
||||
static char str[1024];
|
||||
unsigned int bit;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -2484,10 +2488,9 @@ static inline const char *xhci_decode_ctrl_ctx(unsigned long drop,
|
|||
return str;
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_slot_context(u32 info, u32 info2,
|
||||
u32 tt_info, u32 state)
|
||||
static inline const char *xhci_decode_slot_context(char *str,
|
||||
u32 info, u32 info2, u32 tt_info, u32 state)
|
||||
{
|
||||
static char str[1024];
|
||||
u32 speed;
|
||||
u32 hub;
|
||||
u32 mtt;
|
||||
|
@ -2571,9 +2574,8 @@ static inline const char *xhci_portsc_link_state_string(u32 portsc)
|
|||
return "Unknown";
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_portsc(u32 portsc)
|
||||
static inline const char *xhci_decode_portsc(char *str, u32 portsc)
|
||||
{
|
||||
static char str[256];
|
||||
int ret;
|
||||
|
||||
ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ",
|
||||
|
@ -2617,9 +2619,8 @@ static inline const char *xhci_decode_portsc(u32 portsc)
|
|||
return str;
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_usbsts(u32 usbsts)
|
||||
static inline const char *xhci_decode_usbsts(char *str, u32 usbsts)
|
||||
{
|
||||
static char str[256];
|
||||
int ret = 0;
|
||||
|
||||
if (usbsts == ~(u32)0)
|
||||
|
@ -2646,9 +2647,8 @@ static inline const char *xhci_decode_usbsts(u32 usbsts)
|
|||
return str;
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_doorbell(u32 slot, u32 doorbell)
|
||||
static inline const char *xhci_decode_doorbell(char *str, u32 slot, u32 doorbell)
|
||||
{
|
||||
static char str[256];
|
||||
u8 ep;
|
||||
u16 stream;
|
||||
int ret;
|
||||
|
@ -2715,10 +2715,9 @@ static inline const char *xhci_ep_type_string(u8 type)
|
|||
}
|
||||
}
|
||||
|
||||
static inline const char *xhci_decode_ep_context(u32 info, u32 info2, u64 deq,
|
||||
u32 tx_info)
|
||||
static inline const char *xhci_decode_ep_context(char *str, u32 info,
|
||||
u32 info2, u64 deq, u32 tx_info)
|
||||
{
|
||||
static char str[1024];
|
||||
int ret;
|
||||
|
||||
u32 esit;
|
||||
|
|
|
@ -30,6 +30,7 @@ static int isp1760_init_core(struct isp1760_device *isp)
|
|||
{
|
||||
struct isp1760_hcd *hcd = &isp->hcd;
|
||||
struct isp1760_udc *udc = &isp->udc;
|
||||
u32 otg_ctrl;
|
||||
|
||||
/* Low-level chip reset */
|
||||
if (isp->rst_gpio) {
|
||||
|
@ -83,16 +84,17 @@ static int isp1760_init_core(struct isp1760_device *isp)
|
|||
*
|
||||
* TODO: Really support OTG. For now we configure port 1 in device mode
|
||||
*/
|
||||
if (((isp->devflags & ISP1760_FLAG_ISP1761) ||
|
||||
(isp->devflags & ISP1760_FLAG_ISP1763)) &&
|
||||
(isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) {
|
||||
isp1760_field_set(hcd->fields, HW_DM_PULLDOWN);
|
||||
isp1760_field_set(hcd->fields, HW_DP_PULLDOWN);
|
||||
isp1760_field_set(hcd->fields, HW_OTG_DISABLE);
|
||||
} else {
|
||||
isp1760_field_set(hcd->fields, HW_SW_SEL_HC_DC);
|
||||
isp1760_field_set(hcd->fields, HW_VBUS_DRV);
|
||||
isp1760_field_set(hcd->fields, HW_SEL_CP_EXT);
|
||||
if (isp->devflags & ISP1760_FLAG_ISP1761) {
|
||||
if (isp->devflags & ISP1760_FLAG_PERIPHERAL_EN) {
|
||||
otg_ctrl = (ISP176x_HW_DM_PULLDOWN_CLEAR |
|
||||
ISP176x_HW_DP_PULLDOWN_CLEAR |
|
||||
ISP176x_HW_OTG_DISABLE);
|
||||
} else {
|
||||
otg_ctrl = (ISP176x_HW_SW_SEL_HC_DC_CLEAR |
|
||||
ISP176x_HW_VBUS_DRV |
|
||||
ISP176x_HW_SEL_CP_EXT);
|
||||
}
|
||||
isp1760_reg_write(hcd->regs, ISP176x_HC_OTG_CTRL, otg_ctrl);
|
||||
}
|
||||
|
||||
dev_info(isp->dev, "%s bus width: %u, oc: %s\n",
|
||||
|
@ -235,20 +237,20 @@ static const struct reg_field isp1760_hc_reg_fields[] = {
|
|||
[HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_AND, 0, 31),
|
||||
[HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_AND, 0, 31),
|
||||
[HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_AND, 0, 31),
|
||||
[HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 10, 10),
|
||||
[HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 7, 7),
|
||||
[HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 4, 4),
|
||||
[HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 3, 3),
|
||||
[HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 2, 2),
|
||||
[HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 1, 1),
|
||||
[HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 0, 0),
|
||||
[HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 10, 10),
|
||||
[HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 7, 7),
|
||||
[HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 4, 4),
|
||||
[HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 3, 3),
|
||||
[HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 2, 2),
|
||||
[HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 1, 1),
|
||||
[HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 0, 0),
|
||||
[HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 26, 26),
|
||||
[HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 23, 23),
|
||||
[HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 20, 20),
|
||||
[HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 19, 19),
|
||||
[HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 18, 18),
|
||||
[HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 17, 17),
|
||||
[HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 16, 16),
|
||||
[HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL, 10, 10),
|
||||
[HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL, 7, 7),
|
||||
[HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL, 4, 4),
|
||||
[HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL, 3, 3),
|
||||
[HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL, 2, 2),
|
||||
[HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL, 1, 1),
|
||||
[HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL, 0, 0),
|
||||
};
|
||||
|
||||
static const struct reg_field isp1763_hc_reg_fields[] = {
|
||||
|
@ -491,7 +493,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
|
|||
(devflags & ISP1760_FLAG_ISP1761));
|
||||
|
||||
if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
|
||||
(!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || !udc_enabled))
|
||||
(!udc_enabled || !IS_ENABLED(CONFIG_USB_ISP1761_UDC)))
|
||||
return -ENODEV;
|
||||
|
||||
isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
|
||||
|
@ -571,7 +573,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && udc_enabled) {
|
||||
if (udc_enabled && IS_ENABLED(CONFIG_USB_ISP1761_UDC)) {
|
||||
ret = isp1760_udc_register(isp, irq, irqflags);
|
||||
if (ret < 0) {
|
||||
isp1760_hcd_unregister(hcd);
|
||||
|
|
|
@ -182,7 +182,7 @@ struct urb_listitem {
|
|||
struct urb *urb;
|
||||
};
|
||||
|
||||
static const u32 isp1763_hc_portsc1_fields[] = {
|
||||
static const u32 isp176x_hc_portsc1_fields[] = {
|
||||
[PORT_OWNER] = BIT(13),
|
||||
[PORT_POWER] = BIT(12),
|
||||
[PORT_LSTATUS] = BIT(10),
|
||||
|
@ -205,27 +205,28 @@ static u32 isp1760_hcd_read(struct usb_hcd *hcd, u32 field)
|
|||
}
|
||||
|
||||
/*
|
||||
* We need, in isp1763, to write directly the values to the portsc1
|
||||
* We need, in isp176x, to write directly the values to the portsc1
|
||||
* register so it will make the other values to trigger.
|
||||
*/
|
||||
static void isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field,
|
||||
u32 val)
|
||||
{
|
||||
u32 bit = isp1763_hc_portsc1_fields[field];
|
||||
u32 port_status = readl(priv->base + ISP1763_HC_PORTSC1);
|
||||
u32 bit = isp176x_hc_portsc1_fields[field];
|
||||
u16 portsc1_reg = priv->is_isp1763 ? ISP1763_HC_PORTSC1 :
|
||||
ISP176x_HC_PORTSC1;
|
||||
u32 port_status = readl(priv->base + portsc1_reg);
|
||||
|
||||
if (val)
|
||||
writel(port_status | bit, priv->base + ISP1763_HC_PORTSC1);
|
||||
writel(port_status | bit, priv->base + portsc1_reg);
|
||||
else
|
||||
writel(port_status & ~bit, priv->base + ISP1763_HC_PORTSC1);
|
||||
writel(port_status & ~bit, priv->base + portsc1_reg);
|
||||
}
|
||||
|
||||
static void isp1760_hcd_write(struct usb_hcd *hcd, u32 field, u32 val)
|
||||
{
|
||||
struct isp1760_hcd *priv = hcd_to_priv(hcd);
|
||||
|
||||
if (unlikely(priv->is_isp1763 &&
|
||||
(field >= PORT_OWNER && field <= PORT_CONNECT)))
|
||||
if (unlikely((field >= PORT_OWNER && field <= PORT_CONNECT)))
|
||||
return isp1760_hcd_portsc1_set_clear(priv, field, val);
|
||||
|
||||
isp1760_field_write(priv->fields, field, val);
|
||||
|
@ -250,7 +251,7 @@ static int isp1760_hcd_set_and_wait(struct usb_hcd *hcd, u32 field,
|
|||
isp1760_hcd_set(hcd, field);
|
||||
|
||||
return regmap_field_read_poll_timeout(priv->fields[field], val,
|
||||
val, 10, timeout_us);
|
||||
val, 0, timeout_us);
|
||||
}
|
||||
|
||||
static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field,
|
||||
|
@ -262,7 +263,7 @@ static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field,
|
|||
isp1760_hcd_set(hcd, field);
|
||||
|
||||
return regmap_field_read_poll_timeout(priv->fields[field], val,
|
||||
!val, 10, timeout_us);
|
||||
!val, 0, timeout_us);
|
||||
}
|
||||
|
||||
static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field,
|
||||
|
@ -274,7 +275,7 @@ static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field,
|
|||
isp1760_hcd_clear(hcd, field);
|
||||
|
||||
return regmap_field_read_poll_timeout(priv->fields[field], val,
|
||||
!val, 10, timeout_us);
|
||||
!val, 0, timeout_us);
|
||||
}
|
||||
|
||||
static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field)
|
||||
|
@ -367,8 +368,7 @@ static void isp1760_mem_read(struct usb_hcd *hcd, u32 src_offset, void *dst,
|
|||
{
|
||||
struct isp1760_hcd *priv = hcd_to_priv(hcd);
|
||||
|
||||
isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0);
|
||||
isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset);
|
||||
isp1760_reg_write(priv->regs, ISP176x_HC_MEMORY, src_offset);
|
||||
ndelay(100);
|
||||
|
||||
bank_reads8(priv->base, src_offset, ISP_BANK_0, dst, bytes);
|
||||
|
@ -496,8 +496,7 @@ static void isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
|
|||
u16 src_offset = ptd_offset + slot * sizeof(*ptd);
|
||||
struct isp1760_hcd *priv = hcd_to_priv(hcd);
|
||||
|
||||
isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0);
|
||||
isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset);
|
||||
isp1760_reg_write(priv->regs, ISP176x_HC_MEMORY, src_offset);
|
||||
ndelay(90);
|
||||
|
||||
bank_reads8(priv->base, src_offset, ISP_BANK_0, (void *)ptd,
|
||||
|
@ -588,8 +587,8 @@ static void init_memory(struct isp1760_hcd *priv)
|
|||
|
||||
payload_addr = PAYLOAD_OFFSET;
|
||||
|
||||
for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++) {
|
||||
for (j = 0; j < mem->blocks[i]; j++, curr++) {
|
||||
for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++, curr += j) {
|
||||
for (j = 0; j < mem->blocks[i]; j++) {
|
||||
priv->memory_pool[curr + j].start = payload_addr;
|
||||
priv->memory_pool[curr + j].size = mem->blocks_size[i];
|
||||
priv->memory_pool[curr + j].free = 1;
|
||||
|
@ -731,12 +730,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
|
|||
|
||||
isp1760_hcd_write(hcd, HC_SCRATCH, pattern);
|
||||
|
||||
/* Change bus pattern */
|
||||
scratch = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
|
||||
dev_err(hcd->self.controller, "Scratch test 0x%08x\n", scratch);
|
||||
/*
|
||||
* we do not care about the read value here we just want to
|
||||
* change bus pattern.
|
||||
*/
|
||||
isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
|
||||
scratch = isp1760_hcd_read(hcd, HC_SCRATCH);
|
||||
if (scratch != pattern) {
|
||||
dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n", scratch);
|
||||
dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n",
|
||||
scratch);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -1826,9 +1828,11 @@ static void packetize_urb(struct usb_hcd *hcd,
|
|||
goto cleanup;
|
||||
|
||||
if (len > mem->blocks_size[ISP176x_BLOCK_NUM - 1])
|
||||
len = mem->blocks_size[ISP176x_BLOCK_NUM - 1];
|
||||
this_qtd_len = mem->blocks_size[ISP176x_BLOCK_NUM - 1];
|
||||
else
|
||||
this_qtd_len = len;
|
||||
|
||||
this_qtd_len = qtd_fill(qtd, buf, len);
|
||||
this_qtd_len = qtd_fill(qtd, buf, this_qtd_len);
|
||||
list_add_tail(&qtd->qtd_list, head);
|
||||
|
||||
len -= this_qtd_len;
|
||||
|
@ -1851,7 +1855,7 @@ static void packetize_urb(struct usb_hcd *hcd,
|
|||
packet_type = OUT_PID;
|
||||
else
|
||||
packet_type = IN_PID;
|
||||
} else if (usb_pipebulk(urb->pipe)
|
||||
} else if (usb_pipebulk(urb->pipe) && maxpacketsize
|
||||
&& (urb->transfer_flags & URB_ZERO_PACKET)
|
||||
&& !(urb->transfer_buffer_length %
|
||||
maxpacketsize)) {
|
||||
|
@ -1916,7 +1920,6 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
|||
if (list_empty(&new_qtds))
|
||||
return -ENOMEM;
|
||||
|
||||
retval = 0;
|
||||
spin_lock_irqsave(&priv->lock, spinflags);
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
|
@ -1972,16 +1975,20 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
|
|||
/* We need to forcefully reclaim the slot since some transfers never
|
||||
return, e.g. interrupt transfers and NAKed bulk transfers. */
|
||||
if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) {
|
||||
skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
|
||||
skip_map |= (1 << qh->slot);
|
||||
isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
|
||||
ndelay(100);
|
||||
if (qh->slot != -1) {
|
||||
skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
|
||||
skip_map |= (1 << qh->slot);
|
||||
isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
|
||||
ndelay(100);
|
||||
}
|
||||
priv->atl_slots[qh->slot].qh = NULL;
|
||||
priv->atl_slots[qh->slot].qtd = NULL;
|
||||
} else {
|
||||
skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
|
||||
skip_map |= (1 << qh->slot);
|
||||
isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
|
||||
if (qh->slot != -1) {
|
||||
skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
|
||||
skip_map |= (1 << qh->slot);
|
||||
isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
|
||||
}
|
||||
priv->int_slots[qh->slot].qh = NULL;
|
||||
priv->int_slots[qh->slot].qtd = NULL;
|
||||
}
|
||||
|
@ -2528,17 +2535,23 @@ int __init isp1760_init_kmem_once(void)
|
|||
SLAB_MEM_SPREAD, NULL);
|
||||
|
||||
if (!qtd_cachep)
|
||||
return -ENOMEM;
|
||||
goto destroy_urb_listitem;
|
||||
|
||||
qh_cachep = kmem_cache_create("isp1760_qh", sizeof(struct isp1760_qh),
|
||||
0, SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL);
|
||||
|
||||
if (!qh_cachep) {
|
||||
kmem_cache_destroy(qtd_cachep);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!qh_cachep)
|
||||
goto destroy_qtd;
|
||||
|
||||
return 0;
|
||||
|
||||
destroy_qtd:
|
||||
kmem_cache_destroy(qtd_cachep);
|
||||
|
||||
destroy_urb_listitem:
|
||||
kmem_cache_destroy(urb_listitem_cachep);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void isp1760_deinit_kmem_cache(void)
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#define ISP176x_HC_INT_IRQ_MASK_AND 0x328
|
||||
#define ISP176x_HC_ATL_IRQ_MASK_AND 0x32c
|
||||
|
||||
#define ISP176x_HC_OTG_CTRL 0x374
|
||||
#define ISP176x_HC_OTG_CTRL_SET 0x374
|
||||
#define ISP176x_HC_OTG_CTRL_CLEAR 0x376
|
||||
|
||||
|
@ -179,6 +180,21 @@ enum isp176x_host_controller_fields {
|
|||
#define ISP176x_DC_IESUSP BIT(3)
|
||||
#define ISP176x_DC_IEBRST BIT(0)
|
||||
|
||||
#define ISP176x_HW_OTG_DISABLE_CLEAR BIT(26)
|
||||
#define ISP176x_HW_SW_SEL_HC_DC_CLEAR BIT(23)
|
||||
#define ISP176x_HW_VBUS_DRV_CLEAR BIT(20)
|
||||
#define ISP176x_HW_SEL_CP_EXT_CLEAR BIT(19)
|
||||
#define ISP176x_HW_DM_PULLDOWN_CLEAR BIT(18)
|
||||
#define ISP176x_HW_DP_PULLDOWN_CLEAR BIT(17)
|
||||
#define ISP176x_HW_DP_PULLUP_CLEAR BIT(16)
|
||||
#define ISP176x_HW_OTG_DISABLE BIT(10)
|
||||
#define ISP176x_HW_SW_SEL_HC_DC BIT(7)
|
||||
#define ISP176x_HW_VBUS_DRV BIT(4)
|
||||
#define ISP176x_HW_SEL_CP_EXT BIT(3)
|
||||
#define ISP176x_HW_DM_PULLDOWN BIT(2)
|
||||
#define ISP176x_HW_DP_PULLDOWN BIT(1)
|
||||
#define ISP176x_HW_DP_PULLUP BIT(0)
|
||||
|
||||
#define ISP176x_DC_ENDPTYP_ISOC 0x01
|
||||
#define ISP176x_DC_ENDPTYP_BULK 0x02
|
||||
#define ISP176x_DC_ENDPTYP_INTERRUPT 0x03
|
||||
|
|
|
@ -1363,7 +1363,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev)
|
|||
|
||||
status = isp1760_udc_irq_get_status(udc);
|
||||
|
||||
if (status & DC_IEVBUS) {
|
||||
if (status & ISP176x_DC_IEVBUS) {
|
||||
dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__);
|
||||
/* The VBUS interrupt is only triggered when VBUS appears. */
|
||||
spin_lock(&udc->lock);
|
||||
|
@ -1371,7 +1371,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev)
|
|||
spin_unlock(&udc->lock);
|
||||
}
|
||||
|
||||
if (status & DC_IEBRST) {
|
||||
if (status & ISP176x_DC_IEBRST) {
|
||||
dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__);
|
||||
|
||||
isp1760_udc_reset(udc);
|
||||
|
@ -1391,18 +1391,18 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev)
|
|||
}
|
||||
}
|
||||
|
||||
if (status & DC_IEP0SETUP) {
|
||||
if (status & ISP176x_DC_IEP0SETUP) {
|
||||
dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__);
|
||||
|
||||
isp1760_ep0_setup(udc);
|
||||
}
|
||||
|
||||
if (status & DC_IERESM) {
|
||||
if (status & ISP176x_DC_IERESM) {
|
||||
dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__);
|
||||
isp1760_udc_resume(udc);
|
||||
}
|
||||
|
||||
if (status & DC_IESUSP) {
|
||||
if (status & ISP176x_DC_IESUSP) {
|
||||
dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__);
|
||||
|
||||
spin_lock(&udc->lock);
|
||||
|
@ -1413,7 +1413,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev)
|
|||
spin_unlock(&udc->lock);
|
||||
}
|
||||
|
||||
if (status & DC_IEHS_STA) {
|
||||
if (status & ISP176x_DC_IEHS_STA) {
|
||||
dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__);
|
||||
udc->gadget.speed = USB_SPEED_HIGH;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue