USB patches for 3.15-rc1
Here's the big USB pull request for 3.15-rc1. The normal set of patches, lots of controller driver updates, and a smattering of individual USB driver updates as well. All have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlM7AbcACgkQMUfUDdst+ymbZACgncdbZyPsVZ7ZUpBFNbO/vBVT T9UAmwciojEzjh7b+x4ylsWH+O3LWVN3 =RpAF -----END PGP SIGNATURE----- Merge tag 'usb-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB patches from Greg KH: "Here's the big USB pull request for 3.15-rc1. The normal set of patches, lots of controller driver updates, and a smattering of individual USB driver updates as well. All have been in linux-next for a while" * tag 'usb-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (249 commits) xhci: Transition maintainership to Mathias Nyman. USB: disable reset-resume when USB_QUIRK_RESET is set USB: unbind all interfaces before rebinding any usb: phy: Add ulpi IDs for SMSC USB3320 and TI TUSB1210 usb: gadget: tcm_usb_gadget: stop format strings usb: gadget: f_fs: add missing spinlock and mutex unlock usb: gadget: composite: switch over to ERR_CAST() usb: gadget: inode: switch over to memdup_user() usb: gadget: f_subset: switch over to PTR_RET usb: gadget: lpc32xx_udc: fix wrong clk_put() sequence USB: keyspan: remove dead debugging code USB: serial: add missing newlines to dev_<level> messages. USB: serial: add missing braces USB: serial: continue to write on errors USB: serial: continue to read on errors USB: serial: make bulk_out_size a lower limit USB: cypress_m8: fix potential scheduling while atomic devicetree: bindings: document lsi,zevio-usb usb: chipidea: add support for USB OTG controller on LSI Zevio SoCs usb: chipidea: imx: Use dev_name() for ci_hdrc name to distinguish USBs ...
This commit is contained in:
commit
3e75c6de1a
|
@ -0,0 +1,79 @@
|
|||
* APM X-Gene 15Gbps Multi-purpose PHY nodes
|
||||
|
||||
PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each
|
||||
PHY (pair of lanes) has its own node.
|
||||
|
||||
Required properties:
|
||||
- compatible : Shall be "apm,xgene-phy".
|
||||
- reg : PHY memory resource is the SDS PHY access resource.
|
||||
- #phy-cells : Shall be 1 as it expects one argument for setting
|
||||
the mode of the PHY. Possible values are 0 (SATA),
|
||||
1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI).
|
||||
|
||||
Optional properties:
|
||||
- status : Shall be "ok" if enabled or "disabled" if disabled.
|
||||
Default is "ok".
|
||||
- clocks : Reference to the clock entry.
|
||||
- apm,tx-eye-tuning : Manual control to fine tune the capture of the serial
|
||||
bit lines from the automatic calibrated position.
|
||||
Two set of 3-tuple setting for each (up to 3)
|
||||
supported link speed on the host. Range from 0 to
|
||||
127 in unit of one bit period. Default is 10.
|
||||
- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample
|
||||
data earlier than the nominal sampling point. 1 means
|
||||
sample data later than the nominal sampling point.
|
||||
Two set of 3-tuple setting for each (up to 3)
|
||||
supported link speed on the host. Default is 0.
|
||||
- apm,tx-boost-gain : Frequency boost AC (LSB 3-bit) and DC (2-bit)
|
||||
gain control. Two set of 3-tuple setting for each
|
||||
(up to 3) supported link speed on the host. Range is
|
||||
between 0 to 31 in unit of dB. Default is 3.
|
||||
- apm,tx-amplitude : Amplitude control. Two set of 3-tuple setting for
|
||||
each (up to 3) supported link speed on the host.
|
||||
Range is between 0 to 199500 in unit of uV.
|
||||
Default is 199500 uV.
|
||||
- apm,tx-pre-cursor1 : 1st pre-cursor emphasis taps control. Two set of
|
||||
3-tuple setting for each (up to 3) supported link
|
||||
speed on the host. Range is 0 to 273000 in unit of
|
||||
uV. Default is 0.
|
||||
- apm,tx-pre-cursor2 : 2st pre-cursor emphasis taps control. Two set of
|
||||
3-tuple setting for each (up to 3) supported link
|
||||
speed on the host. Range is 0 to 127400 in unit uV.
|
||||
Default is 0x0.
|
||||
- apm,tx-post-cursor : Post-cursor emphasis taps control. Two set of
|
||||
3-tuple setting for Gen1, Gen2, and Gen3. Range is
|
||||
between 0 to 0x1f in unit of 18.2mV. Default is 0xf.
|
||||
- apm,tx-speed : Tx operating speed. One set of 3-tuple for each
|
||||
supported link speed on the host.
|
||||
0 = 1-2Gbps
|
||||
1 = 2-4Gbps (1st tuple default)
|
||||
2 = 4-8Gbps
|
||||
3 = 8-15Gbps (2nd tuple default)
|
||||
4 = 2.5-4Gbps
|
||||
5 = 4-5Gbps
|
||||
6 = 5-6Gbps
|
||||
7 = 6-16Gbps (3rd tuple default)
|
||||
|
||||
NOTE: PHY override parameters are board specific setting.
|
||||
|
||||
Example:
|
||||
phy1: phy@1f21a000 {
|
||||
compatible = "apm,xgene-phy";
|
||||
reg = <0x0 0x1f21a000 0x0 0x100>;
|
||||
#phy-cells = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
phy2: phy@1f22a000 {
|
||||
compatible = "apm,xgene-phy";
|
||||
reg = <0x0 0x1f22a000 0x0 0x100>;
|
||||
#phy-cells = <1>;
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
phy3: phy@1f23a000 {
|
||||
compatible = "apm,xgene-phy";
|
||||
reg = <0x0 0x1f23a000 0x0 0x100>;
|
||||
#phy-cells = <1>;
|
||||
status = "ok";
|
||||
};
|
|
@ -20,3 +20,57 @@ Required properties:
|
|||
- compatible : should be "samsung,exynos5250-dp-video-phy";
|
||||
- reg : offset and length of the Display Port PHY register set;
|
||||
- #phy-cells : from the generic PHY bindings, must be 0;
|
||||
|
||||
Samsung S5P/EXYNOS SoC series USB PHY
|
||||
-------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of the listed compatibles:
|
||||
- "samsung,exynos4210-usb2-phy"
|
||||
- "samsung,exynos4x12-usb2-phy"
|
||||
- "samsung,exynos5250-usb2-phy"
|
||||
- reg : a list of registers used by phy driver
|
||||
- first and obligatory is the location of phy modules registers
|
||||
- samsung,sysreg-phandle - handle to syscon used to control the system registers
|
||||
- samsung,pmureg-phandle - handle to syscon used to control PMU registers
|
||||
- #phy-cells : from the generic phy bindings, must be 1;
|
||||
- clocks and clock-names:
|
||||
- the "phy" clock is required by the phy module, used as a gate
|
||||
- the "ref" clock is used to get the rate of the clock provided to the
|
||||
PHY module
|
||||
|
||||
The first phandle argument in the PHY specifier identifies the PHY, its
|
||||
meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
|
||||
and Exynos 4212) it is as follows:
|
||||
0 - USB device ("device"),
|
||||
1 - USB host ("host"),
|
||||
2 - HSIC0 ("hsic0"),
|
||||
3 - HSIC1 ("hsic1"),
|
||||
|
||||
Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
|
||||
register is supplied.
|
||||
|
||||
Example:
|
||||
|
||||
For Exynos 4412 (compatible with Exynos 4212):
|
||||
|
||||
usbphy: phy@125b0000 {
|
||||
compatible = "samsung,exynos4x12-usb2-phy";
|
||||
reg = <0x125b0000 0x100>;
|
||||
clocks = <&clock 305>, <&clock 2>;
|
||||
clock-names = "phy", "ref";
|
||||
status = "okay";
|
||||
#phy-cells = <1>;
|
||||
samsung,sysreg-phandle = <&sys_reg>;
|
||||
samsung,pmureg-phandle = <&pmu_reg>;
|
||||
};
|
||||
|
||||
Then the PHY can be used in other nodes such as:
|
||||
|
||||
phy-consumer@12340000 {
|
||||
phys = <&usbphy 2>;
|
||||
phy-names = "phy";
|
||||
};
|
||||
|
||||
Refer to DT bindings documentation of particular PHY consumer devices for more
|
||||
information about required PHYs and the way of specification.
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
Allwinner sun4i USB PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
|
||||
"allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
|
||||
- reg : a list of offset + length pairs
|
||||
- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2"
|
||||
- #phy-cells : from the generic phy bindings, must be 1
|
||||
- clocks : phandle + clock specifier for the phy clock
|
||||
- clock-names : "usb_phy"
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset"
|
||||
|
||||
Example:
|
||||
usbphy: phy@0x01c13400 {
|
||||
#phy-cells = <1>;
|
||||
compatible = "allwinner,sun4i-a10-usb-phy";
|
||||
/* phy base regs, phy1 pmu reg, phy2 pmu reg */
|
||||
reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
|
||||
reg-names = "phy_ctrl", "pmu1", "pmu2";
|
||||
clocks = <&usb_clk 8>;
|
||||
clock-names = "usb_phy";
|
||||
resets = <&usb_clk 1>, <&usb_clk 2>;
|
||||
reset-names = "usb1_reset", "usb2_reset";
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
TI PHY: DT DOCUMENTATION FOR PHYs in TI PLATFORMs
|
||||
|
||||
OMAP CONTROL PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
|
||||
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
|
||||
e.g. USB2_PHY on OMAP5.
|
||||
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
|
||||
e.g. USB3 PHY and SATA PHY on OMAP5.
|
||||
"ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
|
||||
DRA7 platform.
|
||||
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
|
||||
AM437 platform.
|
||||
- reg : Address and length of the register set for the device. It contains
|
||||
the address of "otghs_control" for control-phy-otghs or "power" register
|
||||
for other types.
|
||||
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
|
||||
other types.
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,control-phy-otghs";
|
||||
reg = <0x4a00233c 0x4>;
|
||||
reg-names = "otghs_control";
|
||||
};
|
||||
|
||||
OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
TI PIPE3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
|
||||
"ti,omap-usb3" is deprecated.
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
- clock-names: should include:
|
||||
* "wkupclk" - wakeup clock.
|
||||
* "sysclk" - system clock.
|
||||
* "refclk" - reference clock.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb3phy@4a084400 {
|
||||
compatible = "ti,phy-usb3";
|
||||
reg = <0x4a084400 0x80>,
|
||||
<0x4a084800 0x64>,
|
||||
<0x4a084c00 0x40>;
|
||||
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
clocks = <&usb_phy_cm_clk32k>,
|
||||
<&sys_clkin>,
|
||||
<&usb_otg_ss_refclk960m>;
|
||||
clock-names = "wkupclk",
|
||||
"sysclk",
|
||||
"refclk";
|
||||
};
|
|
@ -18,6 +18,7 @@ Optional properties:
|
|||
- vbus-supply: regulator for vbus
|
||||
- disable-over-current: disable over current detect
|
||||
- external-vbus-divider: enables off-chip resistor divider for Vbus
|
||||
- maximum-speed: limit the maximum connection speed to "full-speed".
|
||||
|
||||
Examples:
|
||||
usb@02184000 { /* USB OTG */
|
||||
|
@ -28,4 +29,5 @@ usb@02184000 { /* USB OTG */
|
|||
fsl,usbmisc = <&usbmisc 0>;
|
||||
disable-over-current;
|
||||
external-vbus-divider;
|
||||
maximum-speed = "full-speed";
|
||||
};
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
* LSI Zevio USB OTG Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "lsi,zevio-usb"
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain controller interrupt
|
||||
|
||||
Optional properties:
|
||||
- vbus-supply: regulator for vbus
|
||||
|
||||
Examples:
|
||||
usb0: usb@b0000000 {
|
||||
reg = <0xb0000000 0x1000>;
|
||||
compatible = "lsi,zevio-usb";
|
||||
interrupts = <8>;
|
||||
vbus-supply = <&vbus_reg>;
|
||||
};
|
|
@ -6,11 +6,13 @@ Required properties:
|
|||
- compatible: must be "snps,dwc3"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts: Interrupts used by the dwc3 controller.
|
||||
|
||||
Optional properties:
|
||||
- usb-phy : array of phandle for the PHY device. The first element
|
||||
in the array is expected to be a handle to the USB2/HS PHY and
|
||||
the second element is expected to be a handle to the USB3/SS PHY
|
||||
|
||||
Optional properties:
|
||||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings
|
||||
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
|
||||
This is usually a subnode to DWC3 glue to which it is connected.
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
* Freescale MXS USB Phy Device
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx23-usbphy"
|
||||
- compatible: should contain:
|
||||
* "fsl,imx23-usbphy" for imx23 and imx28
|
||||
* "fsl,imx6q-usbphy" for imx6dq and imx6dl
|
||||
* "fsl,imx6sl-usbphy" for imx6sl
|
||||
"fsl,imx23-usbphy" is still a fallback for other strings
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain phy interrupt
|
||||
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
|
||||
|
||||
Example:
|
||||
usbphy1: usbphy@020c9000 {
|
||||
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
|
||||
reg = <0x020c9000 0x1000>;
|
||||
interrupts = <0 44 0x04>;
|
||||
fsl,anatop = <&anatop>;
|
||||
};
|
||||
|
|
|
@ -76,27 +76,3 @@ omap_dwc3 {
|
|||
ranges;
|
||||
};
|
||||
|
||||
OMAP CONTROL USB
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
|
||||
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
|
||||
e.g. USB2_PHY on OMAP5.
|
||||
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
|
||||
e.g. USB3 PHY and SATA PHY on OMAP5.
|
||||
"ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on
|
||||
DRA7 platform.
|
||||
"ti,control-phy-am437usb2" - if it has power down register like USB2 PHY on
|
||||
AM437 platform.
|
||||
- reg : Address and length of the register set for the device. It contains
|
||||
the address of "otghs_control" for control-phy-otghs or "power" register
|
||||
for other types.
|
||||
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
|
||||
other types.
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,control-phy-otghs";
|
||||
reg = <0x4a00233c 0x4>;
|
||||
reg-names = "otghs_control";
|
||||
};
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
USB EHCI controllers
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "usb-ehci".
|
||||
- compatible : should be "generic-ehci".
|
||||
- reg : should contain at least address and length of the standard EHCI
|
||||
register set for the device. Optional platform-dependent registers
|
||||
(debug-port or other) can be also specified here, but only after
|
||||
definition of standard EHCI registers.
|
||||
- interrupts : one EHCI interrupt should be described here.
|
||||
If device registers are implemented in big endian mode, the device
|
||||
node should have "big-endian-regs" property.
|
||||
If controller implementation operates with big endian descriptors,
|
||||
"big-endian-desc" property should be specified.
|
||||
If both big endian registers and descriptors are used by the controller
|
||||
implementation, "big-endian" property can be specified instead of having
|
||||
both "big-endian-regs" and "big-endian-desc".
|
||||
|
||||
Optional properties:
|
||||
- big-endian-regs : boolean, set this for hcds with big-endian registers
|
||||
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
|
||||
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
|
||||
- clocks : a list of phandle + clock specifier pairs
|
||||
- phys : phandle + phy specifier pair
|
||||
- phy-names : "usb"
|
||||
|
||||
Example (Sequoia 440EPx):
|
||||
ehci@e0000300 {
|
||||
|
@ -23,3 +24,13 @@ Example (Sequoia 440EPx):
|
|||
reg = <0 e0000300 90 0 e0000390 70>;
|
||||
big-endian;
|
||||
};
|
||||
|
||||
Example (Allwinner sun4i A10 SoC):
|
||||
ehci0: usb@01c14000 {
|
||||
compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
|
||||
reg = <0x01c14000 0x100>;
|
||||
interrupts = <39>;
|
||||
clocks = <&ahb_gates 1>;
|
||||
phys = <&usbphy 1>;
|
||||
phy-names = "usb";
|
||||
};
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
USB OHCI controllers
|
||||
|
||||
Required properties:
|
||||
- compatible : "generic-ohci"
|
||||
- reg : ohci controller register range (address and length)
|
||||
- interrupts : ohci controller interrupt
|
||||
|
||||
Optional properties:
|
||||
- big-endian-regs : boolean, set this for hcds with big-endian registers
|
||||
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
|
||||
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
|
||||
- clocks : a list of phandle + clock specifier pairs
|
||||
- phys : phandle + phy specifier pair
|
||||
- phy-names : "usb"
|
||||
|
||||
Example:
|
||||
|
||||
ohci0: usb@01c14400 {
|
||||
compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
|
||||
reg = <0x01c14400 0x100>;
|
||||
interrupts = <64>;
|
||||
clocks = <&usb_clk 6>, <&ahb_gates 2>;
|
||||
phys = <&usbphy 1>;
|
||||
phy-names = "usb";
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
USB PHY
|
||||
|
||||
OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
OMAP USB3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb3"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb3phy@4a084400 {
|
||||
compatible = "ti,omap-usb3";
|
||||
reg = <0x4a084400 0x80>,
|
||||
<0x4a084800 0x64>,
|
||||
<0x4a084c00 0x40>;
|
||||
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
};
|
|
@ -2,14 +2,14 @@ Generic Platform UHCI Controller
|
|||
-----------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : "platform-uhci"
|
||||
- compatible : "generic-uhci" (deprecated: "platform-uhci")
|
||||
- reg : Should contain 1 register ranges(address and length)
|
||||
- interrupts : UHCI controller interrupt
|
||||
|
||||
Example:
|
||||
|
||||
uhci@d8007b00 {
|
||||
compatible = "platform-uhci";
|
||||
compatible = "generic-uhci";
|
||||
reg = <0xd8007b00 0x200>;
|
||||
interrupts = <43>;
|
||||
};
|
|
@ -1,14 +1,14 @@
|
|||
USB xHCI controllers
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "xhci-platform".
|
||||
- compatible: should be "generic-xhci" (deprecated: "xhci-platform").
|
||||
- reg: should contain address and length of the standard XHCI
|
||||
register set for the device.
|
||||
- interrupts: one XHCI interrupt should be described here.
|
||||
|
||||
Example:
|
||||
usb@f0931000 {
|
||||
compatible = "xhci-platform";
|
||||
compatible = "generic-xhci";
|
||||
reg = <0xf0931000 0x8c8>;
|
||||
interrupts = <0x0 0x4e 0x0>;
|
||||
};
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
VIA/Wondermedia VT8500 EHCI Controller
|
||||
-----------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : "via,vt8500-ehci"
|
||||
- reg : Should contain 1 register ranges(address and length)
|
||||
- interrupts : ehci controller interrupt
|
||||
|
||||
Example:
|
||||
|
||||
ehci@d8007900 {
|
||||
compatible = "via,vt8500-ehci";
|
||||
reg = <0xd8007900 0x200>;
|
||||
interrupts = <43>;
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
|
||||
- reg: Address range of the ehci registers. size should be 0x200
|
||||
- interrupts: Should contain the ehci interrupt.
|
||||
|
||||
usb: ehci@D8007100 {
|
||||
compatible = "wm,prizm-ehci", "usb-ehci";
|
||||
reg = <0xD8007100 0x200>;
|
||||
interrupts = <1>;
|
||||
};
|
|
@ -0,0 +1,135 @@
|
|||
.------------------------------------------------------------------------------+
|
||||
| Samsung USB 2.0 PHY adaptation layer |
|
||||
+-----------------------------------------------------------------------------+'
|
||||
|
||||
| 1. Description
|
||||
+----------------
|
||||
|
||||
The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
|
||||
among many SoCs. In spite of the similarities it proved difficult to
|
||||
create a one driver that would fit all these PHY controllers. Often
|
||||
the differences were minor and were found in particular bits of the
|
||||
registers of the PHY. In some rare cases the order of register writes or
|
||||
the PHY powering up process had to be altered. This adaptation layer is
|
||||
a compromise between having separate drivers and having a single driver
|
||||
with added support for many special cases.
|
||||
|
||||
| 2. Files description
|
||||
+----------------------
|
||||
|
||||
- phy-samsung-usb2.c
|
||||
This is the main file of the adaptation layer. This file contains
|
||||
the probe function and provides two callbacks to the Generic PHY
|
||||
Framework. This two callbacks are used to power on and power off the
|
||||
phy. They carry out the common work that has to be done on all version
|
||||
of the PHY module. Depending on which SoC was chosen they execute SoC
|
||||
specific callbacks. The specific SoC version is selected by choosing
|
||||
the appropriate compatible string. In addition, this file contains
|
||||
struct of_device_id definitions for particular SoCs.
|
||||
|
||||
- phy-samsung-usb2.h
|
||||
This is the include file. It declares the structures used by this
|
||||
driver. In addition it should contain extern declarations for
|
||||
structures that describe particular SoCs.
|
||||
|
||||
| 3. Supporting SoCs
|
||||
+--------------------
|
||||
|
||||
To support a new SoC a new file should be added to the drivers/phy
|
||||
directory. Each SoC's configuration is stored in an instance of the
|
||||
struct samsung_usb2_phy_config.
|
||||
|
||||
struct samsung_usb2_phy_config {
|
||||
const struct samsung_usb2_common_phy *phys;
|
||||
int (*rate_to_clk)(unsigned long, u32 *);
|
||||
unsigned int num_phys;
|
||||
bool has_mode_switch;
|
||||
};
|
||||
|
||||
The num_phys is the number of phys handled by the driver. *phys is an
|
||||
array that contains the configuration for each phy. The has_mode_switch
|
||||
property is a boolean flag that determines whether the SoC has USB host
|
||||
and device on a single pair of pins. If so, a special register has to
|
||||
be modified to change the internal routing of these pins between a USB
|
||||
device or host module.
|
||||
|
||||
For example the configuration for Exynos 4210 is following:
|
||||
|
||||
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
|
||||
.has_mode_switch = 0,
|
||||
.num_phys = EXYNOS4210_NUM_PHYS,
|
||||
.phys = exynos4210_phys,
|
||||
.rate_to_clk = exynos4210_rate_to_clk,
|
||||
}
|
||||
|
||||
- int (*rate_to_clk)(unsigned long, u32 *)
|
||||
The rate_to_clk callback is to convert the rate of the clock
|
||||
used as the reference clock for the PHY module to the value
|
||||
that should be written in the hardware register.
|
||||
|
||||
The exynos4210_phys configuration array is as follows:
|
||||
|
||||
static const struct samsung_usb2_common_phy exynos4210_phys[] = {
|
||||
{
|
||||
.label = "device",
|
||||
.id = EXYNOS4210_DEVICE,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{
|
||||
.label = "host",
|
||||
.id = EXYNOS4210_HOST,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic0",
|
||||
.id = EXYNOS4210_HSIC0,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic1",
|
||||
.id = EXYNOS4210_HSIC1,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
- int (*power_on)(struct samsung_usb2_phy_instance *);
|
||||
- int (*power_off)(struct samsung_usb2_phy_instance *);
|
||||
These two callbacks are used to power on and power off the phy
|
||||
by modifying appropriate registers.
|
||||
|
||||
Final change to the driver is adding appropriate compatible value to the
|
||||
phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
|
||||
added to the struct of_device_id samsung_usb2_phy_of_match[] array:
|
||||
|
||||
#ifdef CONFIG_PHY_EXYNOS4210_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos4210-usb2-phy",
|
||||
.data = &exynos4210_usb2_phy_config,
|
||||
},
|
||||
#endif
|
||||
|
||||
To add further flexibility to the driver the Kconfig file enables to
|
||||
include support for selected SoCs in the compiled driver. The Kconfig
|
||||
entry for Exynos 4210 is following:
|
||||
|
||||
config PHY_EXYNOS4210_USB2
|
||||
bool "Support for Exynos 4210"
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on CPU_EXYNOS4210
|
||||
help
|
||||
Enable USB PHY support for Exynos 4210. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 4210 four
|
||||
phys are available - device, host, HSCI0 and HSCI1.
|
||||
|
||||
The newly created file that supports the new SoC has to be also added to the
|
||||
Makefile. In case of Exynos 4210 the added line is following:
|
||||
|
||||
obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
|
||||
|
||||
After completing these steps the support for the new SoC should be ready.
|
|
@ -9130,8 +9130,7 @@ S: Maintained
|
|||
F: drivers/net/wireless/ath/ar5523/
|
||||
|
||||
USB ATTACHED SCSI
|
||||
M: Matthew Wilcox <willy@linux.intel.com>
|
||||
M: Sarah Sharp <sarah.a.sharp@linux.intel.com>
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
L: linux-scsi@vger.kernel.org
|
||||
|
@ -9357,7 +9356,7 @@ S: Maintained
|
|||
F: drivers/net/wireless/rndis_wlan.c
|
||||
|
||||
USB XHCI DRIVER
|
||||
M: Sarah Sharp <sarah.a.sharp@linux.intel.com>
|
||||
M: Mathias Nyman <mathias.nyman@intel.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/usb/host/xhci*
|
||||
|
|
|
@ -534,7 +534,6 @@ config ARCH_DOVE
|
|||
select PINCTRL
|
||||
select PINCTRL_DOVE
|
||||
select PLAT_ORION_LEGACY
|
||||
select USB_ARCH_HAS_EHCI
|
||||
help
|
||||
Support for the Marvell Dove SoC 88AP510
|
||||
|
||||
|
@ -633,7 +632,6 @@ config ARCH_LPC32XX
|
|||
select GENERIC_CLOCKEVENTS
|
||||
select HAVE_IDE
|
||||
select HAVE_PWM
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USE_OF
|
||||
help
|
||||
Support for the NXP LPC32XX family of processors
|
||||
|
@ -770,7 +768,6 @@ config ARCH_S3C64XX
|
|||
select SAMSUNG_ATAGS
|
||||
select SAMSUNG_WAKEMASK
|
||||
select SAMSUNG_WDT_RESET
|
||||
select USB_ARCH_HAS_OHCI
|
||||
help
|
||||
Samsung S3C64XX series based systems
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ config ARCH_EXYNOS5
|
|||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_SMP
|
||||
select PINCTRL
|
||||
select USB_ARCH_HAS_XHCI
|
||||
help
|
||||
Samsung EXYNOS5 (Cortex-A15) SoC based systems
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ config ARCH_OMAP3
|
|||
select PM_OPP if PM
|
||||
select PM_RUNTIME if CPU_IDLE
|
||||
select SOC_HAS_OMAP2_SDRC
|
||||
select USB_ARCH_HAS_EHCI if USB_SUPPORT
|
||||
|
||||
config ARCH_OMAP4
|
||||
bool "TI OMAP4"
|
||||
|
@ -42,7 +41,6 @@ config ARCH_OMAP4
|
|||
select PL310_ERRATA_727915
|
||||
select PM_OPP if PM
|
||||
select PM_RUNTIME if CPU_IDLE
|
||||
select USB_ARCH_HAS_EHCI if USB_SUPPORT
|
||||
select ARM_ERRATA_754322
|
||||
select ARM_ERRATA_775420
|
||||
|
||||
|
|
|
@ -114,8 +114,6 @@ config ARCH_R8A7778
|
|||
select CPU_V7
|
||||
select SH_CLK_CPG
|
||||
select ARM_GIC
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select SYS_SUPPORTS_SH_TMU
|
||||
|
||||
config ARCH_R8A7779
|
||||
|
@ -124,8 +122,6 @@ config ARCH_R8A7779
|
|||
select ARM_GIC
|
||||
select CPU_V7
|
||||
select SH_CLK_CPG
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select RENESAS_INTC_IRQPIN
|
||||
select SYS_SUPPORTS_SH_TMU
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ config ARCH_TEGRA
|
|||
select RESET_CONTROLLER
|
||||
select SOC_BUS
|
||||
select SPARSE_IRQ
|
||||
select USB_ARCH_HAS_EHCI if USB_SUPPORT
|
||||
select USB_ULPI if USB_PHY
|
||||
select USB_ULPI_VIEWPORT if USB_PHY
|
||||
select USE_OF
|
||||
|
|
|
@ -67,8 +67,6 @@ config MIPS_ALCHEMY
|
|||
select SYS_SUPPORTS_APM_EMULATION
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select SYS_SUPPORTS_ZBOOT
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
|
||||
config AR7
|
||||
bool "Texas Instruments AR7"
|
||||
|
@ -360,7 +358,6 @@ config MIPS_SEAD3
|
|||
select SYS_SUPPORTS_LITTLE_ENDIAN
|
||||
select SYS_SUPPORTS_SMARTMIPS
|
||||
select SYS_SUPPORTS_MICROMIPS
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select USE_OF
|
||||
|
@ -718,8 +715,6 @@ config CAVIUM_OCTEON_SOC
|
|||
select SWAP_IO_SPACE
|
||||
select HW_HAS_PCI
|
||||
select ZONE_DMA32
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select HOLES_IN_ZONE
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
help
|
||||
|
@ -756,8 +751,6 @@ config NLM_XLR_BOARD
|
|||
select ZONE_DMA32 if 64BIT
|
||||
select SYNC_R4K
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
select USB_ARCH_HAS_OHCI if USB_SUPPORT
|
||||
select USB_ARCH_HAS_EHCI if USB_SUPPORT
|
||||
select SYS_SUPPORTS_ZBOOT
|
||||
select SYS_SUPPORTS_ZBOOT_UART16550
|
||||
help
|
||||
|
|
|
@ -74,34 +74,26 @@ config ATH79_MACH_UBNT_XM
|
|||
endmenu
|
||||
|
||||
config SOC_AR71XX
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select HW_HAS_PCI
|
||||
def_bool n
|
||||
|
||||
config SOC_AR724X
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select HW_HAS_PCI
|
||||
select PCI_AR724X if PCI
|
||||
def_bool n
|
||||
|
||||
config SOC_AR913X
|
||||
select USB_ARCH_HAS_EHCI
|
||||
def_bool n
|
||||
|
||||
config SOC_AR933X
|
||||
select USB_ARCH_HAS_EHCI
|
||||
def_bool n
|
||||
|
||||
config SOC_AR934X
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select HW_HAS_PCI
|
||||
select PCI_AR724X if PCI
|
||||
def_bool n
|
||||
|
||||
config SOC_QCA955X
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select HW_HAS_PCI
|
||||
select PCI_AR724X if PCI
|
||||
def_bool n
|
||||
|
|
|
@ -20,19 +20,13 @@ choice
|
|||
config SOC_RT305X
|
||||
bool "RT305x"
|
||||
select USB_ARCH_HAS_HCD
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
|
||||
config SOC_RT3883
|
||||
bool "RT3883"
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select HW_HAS_PCI
|
||||
|
||||
config SOC_MT7620
|
||||
bool "MT7620"
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
|
||||
endchoice
|
||||
|
||||
|
|
|
@ -265,7 +265,6 @@ config 440EP
|
|||
select PPC_FPU
|
||||
select IBM440EP_ERR42
|
||||
select IBM_EMAC_ZMII
|
||||
select USB_ARCH_HAS_OHCI
|
||||
|
||||
config 440EPX
|
||||
bool
|
||||
|
|
|
@ -2,10 +2,8 @@ config PPC_PS3
|
|||
bool "Sony PS3"
|
||||
depends on PPC64 && PPC_BOOK3S
|
||||
select PPC_CELL
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_OHCI_LITTLE_ENDIAN
|
||||
select USB_OHCI_BIG_ENDIAN_MMIO
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select PPC_PCI_CHOICE
|
||||
help
|
||||
|
|
|
@ -347,7 +347,6 @@ config CPU_SUBTYPE_SH7720
|
|||
select CPU_HAS_DSP
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_OHCI_SH if USB_OHCI_HCD
|
||||
select PINCTRL
|
||||
help
|
||||
|
@ -358,7 +357,6 @@ config CPU_SUBTYPE_SH7721
|
|||
select CPU_SH3
|
||||
select CPU_HAS_DSP
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_OHCI_SH if USB_OHCI_HCD
|
||||
help
|
||||
Select SH7721 if you have a SH3-DSP SH7721 CPU.
|
||||
|
@ -436,8 +434,6 @@ config CPU_SUBTYPE_SH7734
|
|||
select CPU_SH4A
|
||||
select CPU_SHX2
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select PINCTRL
|
||||
help
|
||||
Select SH7734 if you have a SH4A SH7734 CPU.
|
||||
|
@ -447,8 +443,6 @@ config CPU_SUBTYPE_SH7757
|
|||
select CPU_SH4A
|
||||
select CPU_SHX2
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select PINCTRL
|
||||
help
|
||||
Select SH7757 if you have a SH4A SH7757 CPU.
|
||||
|
@ -456,7 +450,6 @@ config CPU_SUBTYPE_SH7757
|
|||
config CPU_SUBTYPE_SH7763
|
||||
bool "Support SH7763 processor"
|
||||
select CPU_SH4A
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_OHCI_SH if USB_OHCI_HCD
|
||||
help
|
||||
Select SH7763 if you have a SH4A SH7763(R5S77631) CPU.
|
||||
|
@ -485,9 +478,7 @@ config CPU_SUBTYPE_SH7786
|
|||
select CPU_HAS_PTEAEX
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_OHCI_SH if USB_OHCI_HCD
|
||||
select USB_ARCH_HAS_EHCI
|
||||
select USB_EHCI_SH if USB_EHCI_HCD
|
||||
select PINCTRL
|
||||
|
||||
|
|
|
@ -2278,6 +2278,7 @@ static const struct hid_device_id hid_ignore_list[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -961,4 +961,7 @@
|
|||
#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05
|
||||
|
||||
|
||||
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */
|
||||
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,30 +16,56 @@ config GENERIC_PHY
|
|||
framework should select this config.
|
||||
|
||||
config PHY_EXYNOS_MIPI_VIDEO
|
||||
depends on HAS_IOMEM
|
||||
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
default y if ARCH_S5PV210 || ARCH_EXYNOS
|
||||
help
|
||||
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
|
||||
and EXYNOS SoCs.
|
||||
|
||||
config PHY_MVEBU_SATA
|
||||
def_bool y
|
||||
depends on ARCH_KIRKWOOD || ARCH_DOVE
|
||||
depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config OMAP_CONTROL_PHY
|
||||
tristate "OMAP CONTROL PHY Driver"
|
||||
help
|
||||
Enable this to add support for the PHY part present in the control
|
||||
module. This driver has API to power on the USB2 PHY and to write to
|
||||
the mailbox. The mailbox is present only in omap4 and the register to
|
||||
power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
|
||||
additional register to power on USB3 PHY/SATA PHY/PCIE PHY
|
||||
(PIPE3 PHY).
|
||||
|
||||
config OMAP_USB2
|
||||
tristate "OMAP USB2 PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
depends on USB_PHY
|
||||
select GENERIC_PHY
|
||||
select OMAP_CONTROL_USB
|
||||
select OMAP_CONTROL_PHY
|
||||
depends on OMAP_OCP2SCP
|
||||
help
|
||||
Enable this to support the transceiver that is part of SOC. This
|
||||
driver takes care of all the PHY functionality apart from comparator.
|
||||
The USB OTG controller communicates with the comparator using this
|
||||
driver.
|
||||
|
||||
config TI_PIPE3
|
||||
tristate "TI PIPE3 PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
select OMAP_CONTROL_PHY
|
||||
depends on OMAP_OCP2SCP
|
||||
help
|
||||
Enable this to support the PIPE3 PHY that is part of TI SOCs. This
|
||||
driver takes care of all the PHY functionality apart from comparator.
|
||||
This driver interacts with the "OMAP Control PHY Driver" to power
|
||||
on/off the PHY.
|
||||
|
||||
config TWL4030_USB
|
||||
tristate "TWL4030 USB Transceiver Driver"
|
||||
depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
|
||||
|
@ -54,6 +80,8 @@ config TWL4030_USB
|
|||
config PHY_EXYNOS_DP_VIDEO
|
||||
tristate "EXYNOS SoC series Display Port PHY driver"
|
||||
depends on OF
|
||||
depends on ARCH_EXYNOS || COMPILE_TEST
|
||||
default ARCH_EXYNOS
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for Display Port PHY found on Samsung EXYNOS SoCs.
|
||||
|
@ -65,4 +93,77 @@ config BCM_KONA_USB2_PHY
|
|||
help
|
||||
Enable this to support the Broadcom Kona USB 2.0 PHY.
|
||||
|
||||
config PHY_EXYNOS5250_SATA
|
||||
tristate "Exynos5250 Sata SerDes/PHY driver"
|
||||
depends on SOC_EXYNOS5250
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
select I2C
|
||||
select I2C_S3C2410
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enable this to support SATA SerDes/Phy found on Samsung's
|
||||
Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s,
|
||||
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
|
||||
port to accept one SATA device.
|
||||
|
||||
config PHY_SUN4I_USB
|
||||
tristate "Allwinner sunxi SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the transceiver that is part of Allwinner
|
||||
sunxi SoCs.
|
||||
|
||||
This driver controls the entire USB PHY block, both the USB OTG
|
||||
parts, as well as the 2 regular USB 2 host PHYs.
|
||||
|
||||
config PHY_SAMSUNG_USB2
|
||||
tristate "Samsung USB 2.0 PHY driver"
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
|
||||
SoCs. This driver provides the interface for USB 2.0 PHY. Support for
|
||||
particular SoCs has to be enabled in addition to this driver. Number
|
||||
and type of supported phys depends on the SoC.
|
||||
|
||||
config PHY_EXYNOS4210_USB2
|
||||
bool "Support for Exynos 4210"
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on CPU_EXYNOS4210
|
||||
help
|
||||
Enable USB PHY support for Exynos 4210. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 4210 four
|
||||
phys are available - device, host, HSIC0 and HSIC1.
|
||||
|
||||
config PHY_EXYNOS4X12_USB2
|
||||
bool "Support for Exynos 4x12"
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
|
||||
help
|
||||
Enable USB PHY support for Exynos 4x12. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 4x12 four
|
||||
phys are available - device, host, HSIC0 and HSIC1.
|
||||
|
||||
config PHY_EXYNOS5250_USB2
|
||||
bool "Support for Exynos 5250"
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on SOC_EXYNOS5250
|
||||
help
|
||||
Enable USB PHY support for Exynos 5250. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 5250 four
|
||||
phys are available - device, host, HSIC0 and HSIC.
|
||||
|
||||
config PHY_XGENE
|
||||
tristate "APM X-Gene 15Gbps PHY support"
|
||||
depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -7,5 +7,14 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
|
|||
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
|
||||
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
|
||||
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
|
||||
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
|
||||
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
|
||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
|
||||
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
||||
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-samsung-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
|
|
|
@ -128,10 +128,8 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
|
|||
|
||||
phy_provider = devm_of_phy_provider_register(dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm_kona_usb2_dt_ids[] = {
|
||||
|
|
|
@ -274,8 +274,8 @@ int phy_power_off(struct phy *phy)
|
|||
EXPORT_SYMBOL_GPL(phy_power_off);
|
||||
|
||||
/**
|
||||
* of_phy_get() - lookup and obtain a reference to a phy by phandle
|
||||
* @dev: device that requests this phy
|
||||
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
|
||||
* @np: device_node for which to get the phy
|
||||
* @index: the index of the phy
|
||||
*
|
||||
* Returns the phy associated with the given phandle value,
|
||||
|
@ -284,20 +284,17 @@ EXPORT_SYMBOL_GPL(phy_power_off);
|
|||
* not yet loaded. This function uses of_xlate call back function provided
|
||||
* while registering the phy_provider to find the phy instance.
|
||||
*/
|
||||
static struct phy *of_phy_get(struct device *dev, int index)
|
||||
static struct phy *_of_phy_get(struct device_node *np, int index)
|
||||
{
|
||||
int ret;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy *phy = NULL;
|
||||
struct of_phandle_args args;
|
||||
|
||||
ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
|
||||
ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
|
||||
index, &args);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "failed to get phy in %s node\n",
|
||||
dev->of_node->full_name);
|
||||
if (ret)
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
mutex_lock(&phy_provider_mutex);
|
||||
phy_provider = of_phy_provider_lookup(args.np);
|
||||
|
@ -316,6 +313,36 @@ err0:
|
|||
return phy;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_phy_get() - lookup and obtain a reference to a phy using a device_node.
|
||||
* @np: device_node for which to get the phy
|
||||
* @con_id: name of the phy from device's point of view
|
||||
*
|
||||
* Returns the phy driver, after getting a refcount to it; or
|
||||
* -ENODEV if there is no such phy. The caller is responsible for
|
||||
* calling phy_put() to release that count.
|
||||
*/
|
||||
struct phy *of_phy_get(struct device_node *np, const char *con_id)
|
||||
{
|
||||
struct phy *phy = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (con_id)
|
||||
index = of_property_match_string(np, "phy-names", con_id);
|
||||
|
||||
phy = _of_phy_get(np, index);
|
||||
if (IS_ERR(phy))
|
||||
return phy;
|
||||
|
||||
if (!try_module_get(phy->ops->owner))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
get_device(&phy->dev);
|
||||
|
||||
return phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_phy_get);
|
||||
|
||||
/**
|
||||
* phy_put() - release the PHY
|
||||
* @phy: the phy returned by phy_get()
|
||||
|
@ -407,7 +434,7 @@ struct phy *phy_get(struct device *dev, const char *string)
|
|||
if (dev->of_node) {
|
||||
index = of_property_match_string(dev->of_node, "phy-names",
|
||||
string);
|
||||
phy = of_phy_get(dev, index);
|
||||
phy = _of_phy_get(dev->of_node, index);
|
||||
} else {
|
||||
phy = phy_lookup(dev, string);
|
||||
}
|
||||
|
@ -498,6 +525,37 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_phy_optional_get);
|
||||
|
||||
/**
|
||||
* devm_of_phy_get() - lookup and obtain a reference to a phy.
|
||||
* @dev: device that requests this phy
|
||||
* @np: node containing the phy
|
||||
* @con_id: name of the phy from device's point of view
|
||||
*
|
||||
* Gets the phy using of_phy_get(), and associates a device with it using
|
||||
* devres. On driver detach, release function is invoked on the devres data,
|
||||
* then, devres data is freed.
|
||||
*/
|
||||
struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
|
||||
const char *con_id)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
|
||||
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy = of_phy_get(np, con_id);
|
||||
if (!IS_ERR(phy)) {
|
||||
*ptr = phy;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
}
|
||||
|
||||
return phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_phy_get);
|
||||
|
||||
/**
|
||||
* phy_create() - create a new phy
|
||||
* @dev: device that is creating the new phy
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Kamil Debski <k.debski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "phy-samsung-usb2.h"
|
||||
|
||||
/* Exynos USB PHY registers */
|
||||
|
||||
/* PHY power control */
|
||||
#define EXYNOS_4210_UPHYPWR 0x0
|
||||
|
||||
#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY0 ( \
|
||||
EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
|
||||
EXYNOS_4210_UPHYPWR_PHY0_PWR | \
|
||||
EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
|
||||
EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
|
||||
|
||||
#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8)
|
||||
#define EXYNOS_4210_UPHYPWR_PHY1 ( \
|
||||
EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
|
||||
EXYNOS_4210_UPHYPWR_PHY1_PWR | \
|
||||
EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
|
||||
|
||||
#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9)
|
||||
#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10)
|
||||
#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
|
||||
EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
|
||||
EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
|
||||
|
||||
#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11)
|
||||
#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12)
|
||||
#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
|
||||
EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
|
||||
EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
|
||||
|
||||
/* PHY clock control */
|
||||
#define EXYNOS_4210_UPHYCLK 0x4
|
||||
|
||||
#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
|
||||
#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0
|
||||
#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
|
||||
#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
|
||||
#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
|
||||
|
||||
#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
|
||||
#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4)
|
||||
#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7)
|
||||
|
||||
/* PHY reset control */
|
||||
#define EXYNOS_4210_UPHYRST 0x8
|
||||
|
||||
#define EXYNOS_4210_URSTCON_PHY0 BIT(0)
|
||||
#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1)
|
||||
#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2)
|
||||
#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3)
|
||||
#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4)
|
||||
#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5)
|
||||
#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6)
|
||||
#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7)
|
||||
#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8)
|
||||
#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9)
|
||||
|
||||
/* Isolation, configured in the power management unit */
|
||||
#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704
|
||||
#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)
|
||||
#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708
|
||||
#define EXYNOS_4210_USB_ISOL_HOST BIT(0)
|
||||
|
||||
/* USBYPHY1 Floating prevention */
|
||||
#define EXYNOS_4210_UPHY1CON 0x34
|
||||
#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1
|
||||
|
||||
/* Mode switching SUB Device <-> Host */
|
||||
#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c
|
||||
#define EXYNOS_4210_MODE_SWITCH_MASK 1
|
||||
#define EXYNOS_4210_MODE_SWITCH_DEVICE 0
|
||||
#define EXYNOS_4210_MODE_SWITCH_HOST 1
|
||||
|
||||
enum exynos4210_phy_id {
|
||||
EXYNOS4210_DEVICE,
|
||||
EXYNOS4210_HOST,
|
||||
EXYNOS4210_HSIC0,
|
||||
EXYNOS4210_HSIC1,
|
||||
EXYNOS4210_NUM_PHYS,
|
||||
};
|
||||
|
||||
/*
|
||||
* exynos4210_rate_to_clk() converts the supplied clock rate to the value that
|
||||
* can be written to the phy register.
|
||||
*/
|
||||
static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
|
||||
{
|
||||
switch (rate) {
|
||||
case 12 * MHZ:
|
||||
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
|
||||
break;
|
||||
case 48 * MHZ:
|
||||
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS4210_DEVICE:
|
||||
offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
|
||||
mask = EXYNOS_4210_USB_ISOL_DEVICE;
|
||||
break;
|
||||
case EXYNOS4210_HOST:
|
||||
offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
|
||||
mask = EXYNOS_4210_USB_ISOL_HOST;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
||||
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
|
||||
}
|
||||
|
||||
static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 rstbits = 0;
|
||||
u32 phypwr = 0;
|
||||
u32 rst;
|
||||
u32 pwr;
|
||||
u32 clk;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS4210_DEVICE:
|
||||
phypwr = EXYNOS_4210_UPHYPWR_PHY0;
|
||||
rstbits = EXYNOS_4210_URSTCON_PHY0;
|
||||
break;
|
||||
case EXYNOS4210_HOST:
|
||||
phypwr = EXYNOS_4210_UPHYPWR_PHY1;
|
||||
rstbits = EXYNOS_4210_URSTCON_PHY1_ALL |
|
||||
EXYNOS_4210_URSTCON_PHY1_P0 |
|
||||
EXYNOS_4210_URSTCON_PHY1_P1P2 |
|
||||
EXYNOS_4210_URSTCON_HOST_LINK_ALL |
|
||||
EXYNOS_4210_URSTCON_HOST_LINK_P0;
|
||||
writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
|
||||
break;
|
||||
case EXYNOS4210_HSIC0:
|
||||
phypwr = EXYNOS_4210_UPHYPWR_HSIC0;
|
||||
rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
|
||||
EXYNOS_4210_URSTCON_HOST_LINK_P1;
|
||||
break;
|
||||
case EXYNOS4210_HSIC1:
|
||||
phypwr = EXYNOS_4210_UPHYPWR_HSIC1;
|
||||
rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
|
||||
EXYNOS_4210_URSTCON_HOST_LINK_P2;
|
||||
break;
|
||||
};
|
||||
|
||||
if (on) {
|
||||
clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
|
||||
clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
|
||||
clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
|
||||
writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
|
||||
|
||||
pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
|
||||
pwr &= ~phypwr;
|
||||
writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
|
||||
|
||||
rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
|
||||
rst |= rstbits;
|
||||
writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
|
||||
udelay(10);
|
||||
rst &= ~rstbits;
|
||||
writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
|
||||
/* The following delay is necessary for the reset sequence to be
|
||||
* completed */
|
||||
udelay(80);
|
||||
} else {
|
||||
pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
|
||||
pwr |= phypwr;
|
||||
writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
|
||||
}
|
||||
}
|
||||
|
||||
static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
/* Order of initialisation is important - first power then isolation */
|
||||
exynos4210_phy_pwr(inst, 1);
|
||||
exynos4210_isol(inst, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
exynos4210_isol(inst, 1);
|
||||
exynos4210_phy_pwr(inst, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct samsung_usb2_common_phy exynos4210_phys[] = {
|
||||
{
|
||||
.label = "device",
|
||||
.id = EXYNOS4210_DEVICE,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{
|
||||
.label = "host",
|
||||
.id = EXYNOS4210_HOST,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic0",
|
||||
.id = EXYNOS4210_HSIC0,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic1",
|
||||
.id = EXYNOS4210_HSIC1,
|
||||
.power_on = exynos4210_power_on,
|
||||
.power_off = exynos4210_power_off,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
|
||||
.has_mode_switch = 0,
|
||||
.num_phys = EXYNOS4210_NUM_PHYS,
|
||||
.phys = exynos4210_phys,
|
||||
.rate_to_clk = exynos4210_rate_to_clk,
|
||||
};
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Kamil Debski <k.debski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "phy-samsung-usb2.h"
|
||||
|
||||
/* Exynos USB PHY registers */
|
||||
|
||||
/* PHY power control */
|
||||
#define EXYNOS_4x12_UPHYPWR 0x0
|
||||
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
|
||||
EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
|
||||
EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
|
||||
EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
|
||||
EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
|
||||
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8)
|
||||
#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
|
||||
EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
|
||||
EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
|
||||
EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
|
||||
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9)
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10)
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11)
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
|
||||
EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
|
||||
EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
|
||||
EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
|
||||
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12)
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13)
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14)
|
||||
#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
|
||||
EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
|
||||
EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
|
||||
EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
|
||||
|
||||
/* PHY clock control */
|
||||
#define EXYNOS_4x12_UPHYCLK 0x4
|
||||
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0)
|
||||
|
||||
#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7)
|
||||
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10)
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10)
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10)
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10)
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10)
|
||||
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10)
|
||||
|
||||
/* PHY reset control */
|
||||
#define EXYNOS_4x12_UPHYRST 0x8
|
||||
|
||||
#define EXYNOS_4x12_URSTCON_PHY0 BIT(0)
|
||||
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
|
||||
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
|
||||
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
|
||||
|
||||
/* Isolation, configured in the power management unit */
|
||||
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
|
||||
#define EXYNOS_4x12_USB_ISOL_OTG BIT(0)
|
||||
#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708
|
||||
#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0)
|
||||
#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c
|
||||
#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0)
|
||||
|
||||
/* Mode switching SUB Device <-> Host */
|
||||
#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c
|
||||
#define EXYNOS_4x12_MODE_SWITCH_MASK 1
|
||||
#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0
|
||||
#define EXYNOS_4x12_MODE_SWITCH_HOST 1
|
||||
|
||||
enum exynos4x12_phy_id {
|
||||
EXYNOS4x12_DEVICE,
|
||||
EXYNOS4x12_HOST,
|
||||
EXYNOS4x12_HSIC0,
|
||||
EXYNOS4x12_HSIC1,
|
||||
EXYNOS4x12_NUM_PHYS,
|
||||
};
|
||||
|
||||
/*
|
||||
* exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
|
||||
* can be written to the phy register.
|
||||
*/
|
||||
static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
|
||||
{
|
||||
/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
|
||||
|
||||
switch (rate) {
|
||||
case 9600 * KHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
|
||||
break;
|
||||
case 10 * MHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
|
||||
break;
|
||||
case 12 * MHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
|
||||
break;
|
||||
case 19200 * KHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
|
||||
break;
|
||||
case 20 * MHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
|
||||
break;
|
||||
case 50 * MHZ:
|
||||
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS4x12_DEVICE:
|
||||
case EXYNOS4x12_HOST:
|
||||
offset = EXYNOS_4x12_USB_ISOL_OFFSET;
|
||||
mask = EXYNOS_4x12_USB_ISOL_OTG;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC0:
|
||||
offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
|
||||
mask = EXYNOS_4x12_USB_ISOL_HSIC0;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC1:
|
||||
offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
|
||||
mask = EXYNOS_4x12_USB_ISOL_HSIC1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
||||
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
|
||||
}
|
||||
|
||||
static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 clk;
|
||||
|
||||
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
||||
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
|
||||
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
|
||||
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
||||
}
|
||||
|
||||
static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 rstbits = 0;
|
||||
u32 phypwr = 0;
|
||||
u32 rst;
|
||||
u32 pwr;
|
||||
u32 mode = 0;
|
||||
u32 switch_mode = 0;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS4x12_DEVICE:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
|
||||
rstbits = EXYNOS_4x12_URSTCON_PHY0;
|
||||
mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
|
||||
switch_mode = 1;
|
||||
break;
|
||||
case EXYNOS4x12_HOST:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY;
|
||||
mode = EXYNOS_4x12_MODE_SWITCH_HOST;
|
||||
switch_mode = 1;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC0:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
|
||||
EXYNOS_4x12_URSTCON_HOST_PHY;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC1:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P1;
|
||||
break;
|
||||
};
|
||||
|
||||
if (on) {
|
||||
if (switch_mode)
|
||||
regmap_update_bits(drv->reg_sys,
|
||||
EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK, mode);
|
||||
|
||||
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
||||
pwr &= ~phypwr;
|
||||
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
||||
|
||||
rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
|
||||
rst |= rstbits;
|
||||
writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
|
||||
udelay(10);
|
||||
rst &= ~rstbits;
|
||||
writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
|
||||
/* The following delay is necessary for the reset sequence to be
|
||||
* completed */
|
||||
udelay(80);
|
||||
} else {
|
||||
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
||||
pwr |= phypwr;
|
||||
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
||||
}
|
||||
}
|
||||
|
||||
static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
|
||||
inst->enabled = 1;
|
||||
exynos4x12_setup_clk(inst);
|
||||
exynos4x12_phy_pwr(inst, 1);
|
||||
exynos4x12_isol(inst, 0);
|
||||
|
||||
/* Power on the device, as it is necessary for HSIC to work */
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0) {
|
||||
struct samsung_usb2_phy_instance *device =
|
||||
&drv->instances[EXYNOS4x12_DEVICE];
|
||||
exynos4x12_phy_pwr(device, 1);
|
||||
exynos4x12_isol(device, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
struct samsung_usb2_phy_instance *device =
|
||||
&drv->instances[EXYNOS4x12_DEVICE];
|
||||
|
||||
inst->enabled = 0;
|
||||
exynos4x12_isol(inst, 1);
|
||||
exynos4x12_phy_pwr(inst, 0);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
|
||||
exynos4x12_isol(device, 1);
|
||||
exynos4x12_phy_pwr(device, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
|
||||
{
|
||||
.label = "device",
|
||||
.id = EXYNOS4x12_DEVICE,
|
||||
.power_on = exynos4x12_power_on,
|
||||
.power_off = exynos4x12_power_off,
|
||||
},
|
||||
{
|
||||
.label = "host",
|
||||
.id = EXYNOS4x12_HOST,
|
||||
.power_on = exynos4x12_power_on,
|
||||
.power_off = exynos4x12_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic0",
|
||||
.id = EXYNOS4x12_HSIC0,
|
||||
.power_on = exynos4x12_power_on,
|
||||
.power_off = exynos4x12_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic1",
|
||||
.id = EXYNOS4x12_HSIC1,
|
||||
.power_on = exynos4x12_power_on,
|
||||
.power_off = exynos4x12_power_off,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
|
||||
.has_mode_switch = 1,
|
||||
.num_phys = EXYNOS4x12_NUM_PHYS,
|
||||
.phys = exynos4x12_phys,
|
||||
.rate_to_clk = exynos4x12_rate_to_clk,
|
||||
};
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Samsung SATA SerDes(PHY) driver
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Authors: Girish K S <ks.giri@samsung.com>
|
||||
* Yuvaraj Kumar C D <yuvaraj.cd@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#define SATAPHY_CONTROL_OFFSET 0x0724
|
||||
#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0)
|
||||
#define EXYNOS5_SATA_RESET 0x4
|
||||
#define RESET_GLOBAL_RST_N BIT(0)
|
||||
#define RESET_CMN_RST_N BIT(1)
|
||||
#define RESET_CMN_BLOCK_RST_N BIT(2)
|
||||
#define RESET_CMN_I2C_RST_N BIT(3)
|
||||
#define RESET_TX_RX_PIPE_RST_N BIT(4)
|
||||
#define RESET_TX_RX_BLOCK_RST_N BIT(5)
|
||||
#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7))
|
||||
#define LINK_RESET 0xf0000
|
||||
#define EXYNOS5_SATA_MODE0 0x10
|
||||
#define SATA_SPD_GEN3 BIT(1)
|
||||
#define EXYNOS5_SATA_CTRL0 0x14
|
||||
#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9)
|
||||
#define CTRL0_P0_PHY_CALIBRATED BIT(8)
|
||||
#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0
|
||||
#define PHCTRLM_REF_RATE BIT(1)
|
||||
#define PHCTRLM_HIGH_SPEED BIT(0)
|
||||
#define EXYNOS5_SATA_PHSATA_STATM 0xf0
|
||||
#define PHSTATM_PLL_LOCKED BIT(0)
|
||||
|
||||
#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
|
||||
|
||||
struct exynos_sata_phy {
|
||||
struct phy *phy;
|
||||
struct clk *phyclk;
|
||||
void __iomem *regs;
|
||||
struct regmap *pmureg;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
|
||||
u32 status)
|
||||
{
|
||||
unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
if ((readl(base + reg) & checkbit) == status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static int exynos_sata_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
|
||||
|
||||
return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
|
||||
EXYNOS5_SATAPHY_PMU_ENABLE, true);
|
||||
|
||||
}
|
||||
|
||||
static int exynos_sata_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
|
||||
|
||||
return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
|
||||
EXYNOS5_SATAPHY_PMU_ENABLE, false);
|
||||
|
||||
}
|
||||
|
||||
static int exynos_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
u32 val = 0;
|
||||
int ret = 0;
|
||||
u8 buf[] = { 0x3a, 0x0b };
|
||||
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
|
||||
|
||||
ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
|
||||
EXYNOS5_SATAPHY_PMU_ENABLE, true);
|
||||
if (ret != 0)
|
||||
dev_err(&sata_phy->phy->dev, "phy init failed\n");
|
||||
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
|
||||
| RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
|
||||
| RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
val |= LINK_RESET;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
val |= RESET_CMN_RST_N;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
|
||||
val &= ~PHCTRLM_REF_RATE;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
|
||||
|
||||
/* High speed enable for Gen3 */
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
|
||||
val |= PHCTRLM_HIGH_SPEED;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
|
||||
val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
|
||||
val |= SATA_SPD_GEN3;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
|
||||
|
||||
ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* release cmu reset */
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
val &= ~RESET_CMN_RST_N;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
|
||||
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
val |= RESET_CMN_RST_N;
|
||||
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
|
||||
|
||||
ret = wait_for_reg_status(sata_phy->regs,
|
||||
EXYNOS5_SATA_PHSATA_STATM,
|
||||
PHSTATM_PLL_LOCKED, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&sata_phy->phy->dev,
|
||||
"PHY PLL locking failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops exynos_sata_phy_ops = {
|
||||
.init = exynos_sata_phy_init,
|
||||
.power_on = exynos_sata_phy_power_on,
|
||||
.power_off = exynos_sata_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int exynos_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_sata_phy *sata_phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *node;
|
||||
int ret = 0;
|
||||
|
||||
sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
|
||||
if (!sata_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
sata_phy->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(sata_phy->regs))
|
||||
return PTR_ERR(sata_phy->regs);
|
||||
|
||||
sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
if (IS_ERR(sata_phy->pmureg)) {
|
||||
dev_err(dev, "syscon regmap lookup failed.\n");
|
||||
return PTR_ERR(sata_phy->pmureg);
|
||||
}
|
||||
|
||||
node = of_parse_phandle(dev->of_node,
|
||||
"samsung,exynos-sataphy-i2c-phandle", 0);
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
sata_phy->client = of_find_i2c_device_by_node(node);
|
||||
if (!sata_phy->client)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_set_drvdata(dev, sata_phy);
|
||||
|
||||
sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
|
||||
if (IS_ERR(sata_phy->phyclk)) {
|
||||
dev_err(dev, "failed to get clk for PHY\n");
|
||||
return PTR_ERR(sata_phy->phyclk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sata_phy->phyclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable source clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
|
||||
if (IS_ERR(sata_phy->phy)) {
|
||||
clk_disable_unprepare(sata_phy->phyclk);
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(sata_phy->phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(sata_phy->phy, sata_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
clk_disable_unprepare(sata_phy->phyclk);
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id exynos_sata_phy_of_match[] = {
|
||||
{ .compatible = "samsung,exynos5250-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver exynos_sata_phy_driver = {
|
||||
.probe = exynos_sata_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = exynos_sata_phy_of_match,
|
||||
.name = "samsung,sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
module_platform_driver(exynos_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung SerDes PHY driver");
|
||||
MODULE_LICENSE("GPL V2");
|
||||
MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
|
||||
MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
|
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Kamil Debski <k.debski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "phy-samsung-usb2.h"
|
||||
|
||||
/* Exynos USB PHY registers */
|
||||
#define EXYNOS_5250_REFCLKSEL_CRYSTAL 0x0
|
||||
#define EXYNOS_5250_REFCLKSEL_XO 0x1
|
||||
#define EXYNOS_5250_REFCLKSEL_CLKCORE 0x2
|
||||
|
||||
#define EXYNOS_5250_FSEL_9MHZ6 0x0
|
||||
#define EXYNOS_5250_FSEL_10MHZ 0x1
|
||||
#define EXYNOS_5250_FSEL_12MHZ 0x2
|
||||
#define EXYNOS_5250_FSEL_19MHZ2 0x3
|
||||
#define EXYNOS_5250_FSEL_20MHZ 0x4
|
||||
#define EXYNOS_5250_FSEL_24MHZ 0x5
|
||||
#define EXYNOS_5250_FSEL_50MHZ 0x7
|
||||
|
||||
/* Normal host */
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0 0x0
|
||||
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL BIT(31)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT 19
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK \
|
||||
(0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT 16
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \
|
||||
(0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN BIT(11)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE BIT(10)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N BIT(9)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK (0x3 << 7)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL (0x0 << 7)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0 (0x1 << 7)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST (0x2 << 7)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ BIT(6)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP BIT(5)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND BIT(4)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE BIT(3)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST BIT(2)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST BIT(1)
|
||||
#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST BIT(0)
|
||||
|
||||
/* HSIC0 & HSIC1 */
|
||||
#define EXYNOS_5250_HSICPHYCTRL1 0x10
|
||||
#define EXYNOS_5250_HSICPHYCTRL2 0x20
|
||||
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK (0x3 << 23)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT (0x2 << 23)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK (0x7f << 16)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 (0x24 << 16)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15 (0x1c << 16)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16 (0x1a << 16)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2 (0x15 << 16)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20 (0x14 << 16)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ BIT(6)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP BIT(5)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND BIT(4)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE BIT(3)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST BIT(2)
|
||||
#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST BIT(0)
|
||||
|
||||
/* EHCI control */
|
||||
#define EXYNOS_5250_HOSTEHCICTRL 0x30
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN BIT(29)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 BIT(28)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 BIT(27)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16 BIT(26)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN BIT(25)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT 19
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \
|
||||
(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT 13
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK \
|
||||
(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT 7
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \
|
||||
(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT 1
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \
|
||||
(0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT)
|
||||
#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE BIT(0)
|
||||
|
||||
/* OHCI control */
|
||||
#define EXYNOS_5250_HOSTOHCICTRL 0x34
|
||||
#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT 1
|
||||
#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \
|
||||
(0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT)
|
||||
#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN BIT(0)
|
||||
|
||||
/* USBOTG */
|
||||
#define EXYNOS_5250_USBOTGSYS 0x38
|
||||
#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET BIT(14)
|
||||
#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG BIT(13)
|
||||
#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST BIT(12)
|
||||
#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT 9
|
||||
#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \
|
||||
(0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT)
|
||||
#define EXYNOS_5250_USBOTGSYS_ID_PULLUP BIT(8)
|
||||
#define EXYNOS_5250_USBOTGSYS_COMMON_ON BIT(7)
|
||||
#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT 4
|
||||
#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \
|
||||
(0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT)
|
||||
#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP BIT(3)
|
||||
#define EXYNOS_5250_USBOTGSYS_OTGDISABLE BIT(2)
|
||||
#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG BIT(1)
|
||||
#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND BIT(0)
|
||||
|
||||
/* Isolation, configured in the power management unit */
|
||||
#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704
|
||||
#define EXYNOS_5250_USB_ISOL_OTG BIT(0)
|
||||
#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708
|
||||
#define EXYNOS_5250_USB_ISOL_HOST BIT(0)
|
||||
|
||||
/* Mode swtich register */
|
||||
#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230
|
||||
#define EXYNOS_5250_MODE_SWITCH_MASK 1
|
||||
#define EXYNOS_5250_MODE_SWITCH_DEVICE 0
|
||||
#define EXYNOS_5250_MODE_SWITCH_HOST 1
|
||||
|
||||
enum exynos4x12_phy_id {
|
||||
EXYNOS5250_DEVICE,
|
||||
EXYNOS5250_HOST,
|
||||
EXYNOS5250_HSIC0,
|
||||
EXYNOS5250_HSIC1,
|
||||
EXYNOS5250_NUM_PHYS,
|
||||
};
|
||||
|
||||
/*
|
||||
* exynos5250_rate_to_clk() converts the supplied clock rate to the value that
|
||||
* can be written to the phy register.
|
||||
*/
|
||||
static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg)
|
||||
{
|
||||
/* EXYNOS_5250_FSEL_MASK */
|
||||
|
||||
switch (rate) {
|
||||
case 9600 * KHZ:
|
||||
*reg = EXYNOS_5250_FSEL_9MHZ6;
|
||||
break;
|
||||
case 10 * MHZ:
|
||||
*reg = EXYNOS_5250_FSEL_10MHZ;
|
||||
break;
|
||||
case 12 * MHZ:
|
||||
*reg = EXYNOS_5250_FSEL_12MHZ;
|
||||
break;
|
||||
case 19200 * KHZ:
|
||||
*reg = EXYNOS_5250_FSEL_19MHZ2;
|
||||
break;
|
||||
case 20 * MHZ:
|
||||
*reg = EXYNOS_5250_FSEL_20MHZ;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
*reg = EXYNOS_5250_FSEL_24MHZ;
|
||||
break;
|
||||
case 50 * MHZ:
|
||||
*reg = EXYNOS_5250_FSEL_50MHZ;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS5250_DEVICE:
|
||||
offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
|
||||
mask = EXYNOS_5250_USB_ISOL_OTG;
|
||||
break;
|
||||
case EXYNOS5250_HOST:
|
||||
offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
|
||||
mask = EXYNOS_5250_USB_ISOL_HOST;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
||||
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
|
||||
}
|
||||
|
||||
static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 ctrl0;
|
||||
u32 otg;
|
||||
u32 ehci;
|
||||
u32 ohci;
|
||||
u32 hsic;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS5250_DEVICE:
|
||||
regmap_update_bits(drv->reg_sys,
|
||||
EXYNOS_5250_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_5250_MODE_SWITCH_MASK,
|
||||
EXYNOS_5250_MODE_SWITCH_DEVICE);
|
||||
|
||||
/* OTG configuration */
|
||||
otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
/* The clock */
|
||||
otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
|
||||
otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
|
||||
/* Reset */
|
||||
otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
|
||||
EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
|
||||
EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
|
||||
otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
|
||||
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
|
||||
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
|
||||
EXYNOS_5250_USBOTGSYS_OTGDISABLE;
|
||||
/* Ref clock */
|
||||
otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
|
||||
otg |= EXYNOS_5250_REFCLKSEL_CLKCORE <<
|
||||
EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
|
||||
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
udelay(100);
|
||||
otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
|
||||
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
|
||||
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
|
||||
EXYNOS_5250_USBOTGSYS_OTGDISABLE);
|
||||
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
|
||||
|
||||
break;
|
||||
case EXYNOS5250_HOST:
|
||||
case EXYNOS5250_HSIC0:
|
||||
case EXYNOS5250_HSIC1:
|
||||
/* Host registers configuration */
|
||||
ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
|
||||
/* The clock */
|
||||
ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
|
||||
ctrl0 |= drv->ref_reg_val <<
|
||||
EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT;
|
||||
|
||||
/* Reset */
|
||||
ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP);
|
||||
ctrl0 |= EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N;
|
||||
writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
|
||||
udelay(10);
|
||||
ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST);
|
||||
writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
|
||||
|
||||
/* OTG configuration */
|
||||
otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
/* The clock */
|
||||
otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
|
||||
otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
|
||||
/* Reset */
|
||||
otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
|
||||
EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
|
||||
EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
|
||||
otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
|
||||
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
|
||||
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
|
||||
EXYNOS_5250_USBOTGSYS_OTGDISABLE;
|
||||
/* Ref clock */
|
||||
otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
|
||||
otg |= EXYNOS_5250_REFCLKSEL_CLKCORE <<
|
||||
EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
|
||||
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
udelay(10);
|
||||
otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
|
||||
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
|
||||
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET);
|
||||
|
||||
/* HSIC phy configuration */
|
||||
hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
|
||||
EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
|
||||
EXYNOS_5250_HSICPHYCTRLX_PHYSWRST);
|
||||
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
|
||||
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
|
||||
udelay(10);
|
||||
hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST;
|
||||
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
|
||||
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
|
||||
/* The following delay is necessary for the reset sequence to be
|
||||
* completed */
|
||||
udelay(80);
|
||||
|
||||
/* Enable EHCI DMA burst */
|
||||
ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
|
||||
ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN |
|
||||
EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 |
|
||||
EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 |
|
||||
EXYNOS_5250_HOSTEHCICTRL_ENAINCR16;
|
||||
writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
|
||||
|
||||
/* OHCI settings */
|
||||
ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
|
||||
/* Following code is based on the old driver */
|
||||
ohci |= 0x1 << 3;
|
||||
writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
|
||||
|
||||
break;
|
||||
}
|
||||
inst->enabled = 1;
|
||||
exynos5250_isol(inst, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
u32 ctrl0;
|
||||
u32 otg;
|
||||
u32 hsic;
|
||||
|
||||
inst->enabled = 0;
|
||||
exynos5250_isol(inst, 1);
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS5250_DEVICE:
|
||||
otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
|
||||
EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG |
|
||||
EXYNOS_5250_USBOTGSYS_FORCE_SLEEP);
|
||||
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
|
||||
break;
|
||||
case EXYNOS5250_HOST:
|
||||
ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
|
||||
ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
|
||||
EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL);
|
||||
writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
|
||||
break;
|
||||
case EXYNOS5250_HSIC0:
|
||||
case EXYNOS5250_HSIC1:
|
||||
hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
|
||||
EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
|
||||
EXYNOS_5250_HSICPHYCTRLX_SIDDQ |
|
||||
EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP |
|
||||
EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND
|
||||
);
|
||||
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
|
||||
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct samsung_usb2_common_phy exynos5250_phys[] = {
|
||||
{
|
||||
.label = "device",
|
||||
.id = EXYNOS5250_DEVICE,
|
||||
.power_on = exynos5250_power_on,
|
||||
.power_off = exynos5250_power_off,
|
||||
},
|
||||
{
|
||||
.label = "host",
|
||||
.id = EXYNOS5250_HOST,
|
||||
.power_on = exynos5250_power_on,
|
||||
.power_off = exynos5250_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic0",
|
||||
.id = EXYNOS5250_HSIC0,
|
||||
.power_on = exynos5250_power_on,
|
||||
.power_off = exynos5250_power_off,
|
||||
},
|
||||
{
|
||||
.label = "hsic1",
|
||||
.id = EXYNOS5250_HSIC1,
|
||||
.power_on = exynos5250_power_on,
|
||||
.power_off = exynos5250_power_off,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
|
||||
.has_mode_switch = 1,
|
||||
.num_phys = EXYNOS5250_NUM_PHYS,
|
||||
.phys = exynos5250_phys,
|
||||
.rate_to_clk = exynos5250_rate_to_clk,
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* omap-control-usb.c - The USB part of control module.
|
||||
* omap-control-phy.c - The PHY part of control module.
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -24,36 +24,36 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/omap_control_usb.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
|
||||
/**
|
||||
* omap_control_usb_phy_power - power on/off the phy using control module reg
|
||||
* omap_control_phy_power - power on/off the phy using control module reg
|
||||
* @dev: the control module device
|
||||
* @on: 0 or 1, based on powering on or off the PHY
|
||||
*/
|
||||
void omap_control_usb_phy_power(struct device *dev, int on)
|
||||
void omap_control_phy_power(struct device *dev, int on)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long rate;
|
||||
struct omap_control_usb *control_usb;
|
||||
struct omap_control_phy *control_phy;
|
||||
|
||||
if (IS_ERR(dev) || !dev) {
|
||||
pr_err("%s: invalid device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
control_usb = dev_get_drvdata(dev);
|
||||
if (!control_usb) {
|
||||
dev_err(dev, "%s: invalid control usb device\n", __func__);
|
||||
control_phy = dev_get_drvdata(dev);
|
||||
if (!control_phy) {
|
||||
dev_err(dev, "%s: invalid control phy device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)
|
||||
return;
|
||||
|
||||
val = readl(control_usb->power);
|
||||
val = readl(control_phy->power);
|
||||
|
||||
switch (control_usb->type) {
|
||||
switch (control_phy->type) {
|
||||
case OMAP_CTRL_TYPE_USB2:
|
||||
if (on)
|
||||
val &= ~OMAP_CTRL_DEV_PHY_PD;
|
||||
|
@ -62,19 +62,20 @@ void omap_control_usb_phy_power(struct device *dev, int on)
|
|||
break;
|
||||
|
||||
case OMAP_CTRL_TYPE_PIPE3:
|
||||
rate = clk_get_rate(control_usb->sys_clk);
|
||||
rate = clk_get_rate(control_phy->sys_clk);
|
||||
rate = rate/1000000;
|
||||
|
||||
if (on) {
|
||||
val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
|
||||
OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
|
||||
val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
|
||||
OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
|
||||
val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
|
||||
val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
|
||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
|
||||
val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
|
||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
val |= rate <<
|
||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
||||
} else {
|
||||
val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
|
||||
val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
|
||||
OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
|
||||
val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
|
||||
val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
|
||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -100,66 +101,66 @@ void omap_control_usb_phy_power(struct device *dev, int on)
|
|||
break;
|
||||
default:
|
||||
dev_err(dev, "%s: type %d not recognized\n",
|
||||
__func__, control_usb->type);
|
||||
__func__, control_phy->type);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(val, control_usb->power);
|
||||
writel(val, control_phy->power);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
|
||||
EXPORT_SYMBOL_GPL(omap_control_phy_power);
|
||||
|
||||
/**
|
||||
* omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
|
||||
* @ctrl_usb: struct omap_control_usb *
|
||||
* @ctrl_phy: struct omap_control_phy *
|
||||
*
|
||||
* Writes to the mailbox register to notify the usb core that a usb
|
||||
* device has been connected.
|
||||
*/
|
||||
static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb)
|
||||
static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(ctrl_usb->otghs_control);
|
||||
val = readl(ctrl_phy->otghs_control);
|
||||
val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
|
||||
val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
|
||||
writel(val, ctrl_usb->otghs_control);
|
||||
writel(val, ctrl_phy->otghs_control);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
|
||||
* impedance
|
||||
* @ctrl_usb: struct omap_control_usb *
|
||||
* @ctrl_phy: struct omap_control_phy *
|
||||
*
|
||||
* Writes to the mailbox register to notify the usb core that it has been
|
||||
* connected to a usb host.
|
||||
*/
|
||||
static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb)
|
||||
static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(ctrl_usb->otghs_control);
|
||||
val = readl(ctrl_phy->otghs_control);
|
||||
val &= ~OMAP_CTRL_DEV_SESSEND;
|
||||
val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
|
||||
OMAP_CTRL_DEV_VBUSVALID;
|
||||
writel(val, ctrl_usb->otghs_control);
|
||||
writel(val, ctrl_phy->otghs_control);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
|
||||
* impedance
|
||||
* @ctrl_usb: struct omap_control_usb *
|
||||
* @ctrl_phy: struct omap_control_phy *
|
||||
*
|
||||
* Writes to the mailbox register to notify the usb core it's now in
|
||||
* disconnected state.
|
||||
*/
|
||||
static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
|
||||
static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(ctrl_usb->otghs_control);
|
||||
val = readl(ctrl_phy->otghs_control);
|
||||
val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
|
||||
val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
|
||||
writel(val, ctrl_usb->otghs_control);
|
||||
writel(val, ctrl_phy->otghs_control);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,30 +175,30 @@ static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
|
|||
void omap_control_usb_set_mode(struct device *dev,
|
||||
enum omap_control_usb_mode mode)
|
||||
{
|
||||
struct omap_control_usb *ctrl_usb;
|
||||
struct omap_control_phy *ctrl_phy;
|
||||
|
||||
if (IS_ERR(dev) || !dev)
|
||||
return;
|
||||
|
||||
ctrl_usb = dev_get_drvdata(dev);
|
||||
ctrl_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (!ctrl_usb) {
|
||||
dev_err(dev, "Invalid control usb device\n");
|
||||
if (!ctrl_phy) {
|
||||
dev_err(dev, "Invalid control phy device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
|
||||
if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)
|
||||
return;
|
||||
|
||||
switch (mode) {
|
||||
case USB_MODE_HOST:
|
||||
omap_control_usb_host_mode(ctrl_usb);
|
||||
omap_control_usb_host_mode(ctrl_phy);
|
||||
break;
|
||||
case USB_MODE_DEVICE:
|
||||
omap_control_usb_device_mode(ctrl_usb);
|
||||
omap_control_usb_device_mode(ctrl_phy);
|
||||
break;
|
||||
case USB_MODE_DISCONNECT:
|
||||
omap_control_usb_set_sessionend(ctrl_usb);
|
||||
omap_control_usb_set_sessionend(ctrl_phy);
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(dev, "invalid omap control usb mode\n");
|
||||
|
@ -207,13 +208,13 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
|
||||
static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
|
||||
static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
|
||||
static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
|
||||
static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
|
||||
static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
|
||||
static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
|
||||
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
|
||||
static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
|
||||
static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
|
||||
|
||||
static const struct of_device_id omap_control_usb_id_table[] = {
|
||||
static const struct of_device_id omap_control_phy_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,control-phy-otghs",
|
||||
.data = &otghs_data,
|
||||
|
@ -227,93 +228,93 @@ static const struct of_device_id omap_control_usb_id_table[] = {
|
|||
.data = &pipe3_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,control-phy-dra7usb2",
|
||||
.compatible = "ti,control-phy-usb2-dra7",
|
||||
.data = &dra7usb2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,control-phy-am437usb2",
|
||||
.compatible = "ti,control-phy-usb2-am437",
|
||||
.data = &am437usb2_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
|
||||
MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
|
||||
#endif
|
||||
|
||||
|
||||
static int omap_control_usb_probe(struct platform_device *pdev)
|
||||
static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
struct omap_control_usb *control_usb;
|
||||
struct omap_control_phy *control_phy;
|
||||
|
||||
of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
|
||||
&pdev->dev);
|
||||
of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
|
||||
&pdev->dev);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
|
||||
control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),
|
||||
GFP_KERNEL);
|
||||
if (!control_usb) {
|
||||
dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
|
||||
if (!control_phy) {
|
||||
dev_err(&pdev->dev, "unable to alloc memory for control phy\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
control_usb->dev = &pdev->dev;
|
||||
control_usb->type = *(enum omap_control_usb_type *)of_id->data;
|
||||
control_phy->dev = &pdev->dev;
|
||||
control_phy->type = *(enum omap_control_phy_type *)of_id->data;
|
||||
|
||||
if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"otghs_control");
|
||||
control_usb->otghs_control = devm_ioremap_resource(
|
||||
control_phy->otghs_control = devm_ioremap_resource(
|
||||
&pdev->dev, res);
|
||||
if (IS_ERR(control_usb->otghs_control))
|
||||
return PTR_ERR(control_usb->otghs_control);
|
||||
if (IS_ERR(control_phy->otghs_control))
|
||||
return PTR_ERR(control_phy->otghs_control);
|
||||
} else {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"power");
|
||||
control_usb->power = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(control_usb->power)) {
|
||||
control_phy->power = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(control_phy->power)) {
|
||||
dev_err(&pdev->dev, "Couldn't get power register\n");
|
||||
return PTR_ERR(control_usb->power);
|
||||
return PTR_ERR(control_phy->power);
|
||||
}
|
||||
}
|
||||
|
||||
if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
|
||||
control_usb->sys_clk = devm_clk_get(control_usb->dev,
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
|
||||
control_phy->sys_clk = devm_clk_get(control_phy->dev,
|
||||
"sys_clkin");
|
||||
if (IS_ERR(control_usb->sys_clk)) {
|
||||
if (IS_ERR(control_phy->sys_clk)) {
|
||||
pr_err("%s: unable to get sys_clkin\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
dev_set_drvdata(control_usb->dev, control_usb);
|
||||
dev_set_drvdata(control_phy->dev, control_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_control_usb_driver = {
|
||||
.probe = omap_control_usb_probe,
|
||||
static struct platform_driver omap_control_phy_driver = {
|
||||
.probe = omap_control_phy_probe,
|
||||
.driver = {
|
||||
.name = "omap-control-usb",
|
||||
.name = "omap-control-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(omap_control_usb_id_table),
|
||||
.of_match_table = of_match_ptr(omap_control_phy_id_table),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap_control_usb_init(void)
|
||||
static int __init omap_control_phy_init(void)
|
||||
{
|
||||
return platform_driver_register(&omap_control_usb_driver);
|
||||
return platform_driver_register(&omap_control_phy_driver);
|
||||
}
|
||||
subsys_initcall(omap_control_usb_init);
|
||||
subsys_initcall(omap_control_phy_init);
|
||||
|
||||
static void __exit omap_control_usb_exit(void)
|
||||
static void __exit omap_control_phy_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap_control_usb_driver);
|
||||
platform_driver_unregister(&omap_control_phy_driver);
|
||||
}
|
||||
module_exit(omap_control_usb_exit);
|
||||
module_exit(omap_control_phy_exit);
|
||||
|
||||
MODULE_ALIAS("platform: omap_control_usb");
|
||||
MODULE_ALIAS("platform: omap_control_phy");
|
||||
MODULE_AUTHOR("Texas Instruments Inc.");
|
||||
MODULE_DESCRIPTION("OMAP Control Module USB Driver");
|
||||
MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -21,16 +21,19 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb/omap_usb.h>
|
||||
#include <linux/phy/omap_usb.h>
|
||||
#include <linux/usb/phy_companion.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/omap_control_usb.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
|
||||
#define USB2PHY_ANA_CONFIG1 0x4c
|
||||
|
||||
/**
|
||||
* omap_usb2_set_comparator - links the comparator present in the sytem with
|
||||
* this phy
|
||||
|
@ -98,33 +101,11 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int omap_usb2_suspend(struct usb_phy *x, int suspend)
|
||||
{
|
||||
struct omap_usb *phy = phy_to_omapusb(x);
|
||||
int ret;
|
||||
|
||||
if (suspend && !phy->is_suspended) {
|
||||
omap_control_usb_phy_power(phy->control_dev, 0);
|
||||
pm_runtime_put_sync(phy->dev);
|
||||
phy->is_suspended = 1;
|
||||
} else if (!suspend && phy->is_suspended) {
|
||||
ret = pm_runtime_get_sync(phy->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->dev, "get_sync failed with err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
omap_control_usb_phy_power(phy->control_dev, 1);
|
||||
phy->is_suspended = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_usb_power_off(struct phy *x)
|
||||
{
|
||||
struct omap_usb *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_usb_phy_power(phy->control_dev, 0);
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -133,30 +114,103 @@ static int omap_usb_power_on(struct phy *x)
|
|||
{
|
||||
struct omap_usb *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_usb_phy_power(phy->control_dev, 1);
|
||||
omap_control_phy_power(phy->control_dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_usb_init(struct phy *x)
|
||||
{
|
||||
struct omap_usb *phy = phy_get_drvdata(x);
|
||||
u32 val;
|
||||
|
||||
if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
|
||||
/*
|
||||
*
|
||||
* Reduce the sensitivity of internal PHY by enabling the
|
||||
* DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
|
||||
* resolves issues with certain devices which can otherwise
|
||||
* be prone to false disconnects.
|
||||
*
|
||||
*/
|
||||
val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
|
||||
val |= USB2PHY_DISCON_BYP_LATCH;
|
||||
omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ops = {
|
||||
.init = omap_usb_init,
|
||||
.power_on = omap_usb_power_on,
|
||||
.power_off = omap_usb_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct usb_phy_data omap_usb2_data = {
|
||||
.label = "omap_usb2",
|
||||
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data omap5_usb2_data = {
|
||||
.label = "omap5_usb2",
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data dra7x_usb2_data = {
|
||||
.label = "dra7x_usb2",
|
||||
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data am437x_usb2_data = {
|
||||
.label = "am437x_usb2",
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct of_device_id omap_usb2_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,omap-usb2",
|
||||
.data = &omap_usb2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,omap5-usb2",
|
||||
.data = &omap5_usb2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra7x-usb2",
|
||||
.data = &dra7x_usb2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am437x-usb2",
|
||||
.data = &am437x_usb2_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
|
||||
#endif
|
||||
|
||||
static int omap_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_usb *phy;
|
||||
struct phy *generic_phy;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
struct usb_otg *otg;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *control_node;
|
||||
struct platform_device *control_pdev;
|
||||
const struct of_device_id *of_id;
|
||||
struct usb_phy_data *phy_data;
|
||||
|
||||
if (!node)
|
||||
of_id = of_match_device(of_match_ptr(omap_usb2_id_table), &pdev->dev);
|
||||
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
phy_data = (struct usb_phy_data *)of_id->data;
|
||||
|
||||
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
|
||||
|
@ -172,11 +226,18 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
|||
phy->dev = &pdev->dev;
|
||||
|
||||
phy->phy.dev = phy->dev;
|
||||
phy->phy.label = "omap-usb2";
|
||||
phy->phy.set_suspend = omap_usb2_suspend;
|
||||
phy->phy.label = phy_data->label;
|
||||
phy->phy.otg = otg;
|
||||
phy->phy.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!phy->phy_base)
|
||||
return -ENOMEM;
|
||||
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
|
||||
}
|
||||
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
if (!control_node) {
|
||||
dev_err(&pdev->dev, "Failed to get control device phandle\n");
|
||||
|
@ -190,14 +251,14 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
|
||||
phy->is_suspended = 1;
|
||||
omap_control_usb_phy_power(phy->control_dev, 0);
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
|
||||
otg->set_host = omap_usb_set_host;
|
||||
otg->set_peripheral = omap_usb_set_peripheral;
|
||||
otg->set_vbus = omap_usb_set_vbus;
|
||||
otg->start_srp = omap_usb_start_srp;
|
||||
if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
|
||||
otg->set_vbus = omap_usb_set_vbus;
|
||||
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
|
||||
otg->start_srp = omap_usb_start_srp;
|
||||
otg->phy = &phy->phy;
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
@ -297,14 +358,6 @@ static const struct dev_pm_ops omap_usb2_pm_ops = {
|
|||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id omap_usb2_id_table[] = {
|
||||
{ .compatible = "ti,omap-usb2" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
|
||||
#endif
|
||||
|
||||
static struct platform_driver omap_usb2_driver = {
|
||||
.probe = omap_usb2_probe,
|
||||
.remove = omap_usb2_remove,
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Samsung SoC USB 1.1/2.0 PHY driver
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Kamil Debski <k.debski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "phy-samsung-usb2.h"
|
||||
|
||||
static int samsung_usb2_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
int ret;
|
||||
|
||||
dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
|
||||
inst->cfg->label);
|
||||
ret = clk_prepare_enable(drv->clk);
|
||||
if (ret)
|
||||
goto err_main_clk;
|
||||
ret = clk_prepare_enable(drv->ref_clk);
|
||||
if (ret)
|
||||
goto err_instance_clk;
|
||||
if (inst->cfg->power_on) {
|
||||
spin_lock(&drv->lock);
|
||||
ret = inst->cfg->power_on(inst);
|
||||
spin_unlock(&drv->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_instance_clk:
|
||||
clk_disable_unprepare(drv->clk);
|
||||
err_main_clk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int samsung_usb2_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
|
||||
inst->cfg->label);
|
||||
if (inst->cfg->power_off) {
|
||||
spin_lock(&drv->lock);
|
||||
ret = inst->cfg->power_off(inst);
|
||||
spin_unlock(&drv->lock);
|
||||
}
|
||||
clk_disable_unprepare(drv->ref_clk);
|
||||
clk_disable_unprepare(drv->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops samsung_usb2_phy_ops = {
|
||||
.power_on = samsung_usb2_phy_power_on,
|
||||
.power_off = samsung_usb2_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *samsung_usb2_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv;
|
||||
|
||||
drv = dev_get_drvdata(dev);
|
||||
if (!drv)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return drv->instances[args->args[0]].phy;
|
||||
}
|
||||
|
||||
static const struct of_device_id samsung_usb2_phy_of_match[] = {
|
||||
#ifdef CONFIG_PHY_EXYNOS4210_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos4210-usb2-phy",
|
||||
.data = &exynos4210_usb2_phy_config,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_PHY_EXYNOS4X12_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos4x12-usb2-phy",
|
||||
.data = &exynos4x12_usb2_phy_config,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_PHY_EXYNOS5250_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos5250-usb2-phy",
|
||||
.data = &exynos5250_usb2_phy_config,
|
||||
},
|
||||
#endif
|
||||
{ },
|
||||
};
|
||||
|
||||
static int samsung_usb2_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct samsung_usb2_phy_config *cfg;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy_provider *phy_provider;
|
||||
struct resource *mem;
|
||||
struct samsung_usb2_phy_driver *drv;
|
||||
int i, ret;
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
dev_err(dev, "This driver is required to be instantiated from device tree\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
|
||||
if (!match) {
|
||||
dev_err(dev, "of_match_node() failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
cfg = match->data;
|
||||
|
||||
drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
|
||||
cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
|
||||
GFP_KERNEL);
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, drv);
|
||||
spin_lock_init(&drv->lock);
|
||||
|
||||
drv->cfg = cfg;
|
||||
drv->dev = dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
drv->reg_phy = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(drv->reg_phy)) {
|
||||
dev_err(dev, "Failed to map register memory (phy)\n");
|
||||
return PTR_ERR(drv->reg_phy);
|
||||
}
|
||||
|
||||
drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"samsung,pmureg-phandle");
|
||||
if (IS_ERR(drv->reg_pmu)) {
|
||||
dev_err(dev, "Failed to map PMU registers (via syscon)\n");
|
||||
return PTR_ERR(drv->reg_pmu);
|
||||
}
|
||||
|
||||
if (drv->cfg->has_mode_switch) {
|
||||
drv->reg_sys = syscon_regmap_lookup_by_phandle(
|
||||
pdev->dev.of_node, "samsung,sysreg-phandle");
|
||||
if (IS_ERR(drv->reg_sys)) {
|
||||
dev_err(dev, "Failed to map system registers (via syscon)\n");
|
||||
return PTR_ERR(drv->reg_sys);
|
||||
}
|
||||
}
|
||||
|
||||
drv->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(drv->clk)) {
|
||||
dev_err(dev, "Failed to get clock of phy controller\n");
|
||||
return PTR_ERR(drv->clk);
|
||||
}
|
||||
|
||||
drv->ref_clk = devm_clk_get(dev, "ref");
|
||||
if (IS_ERR(drv->ref_clk)) {
|
||||
dev_err(dev, "Failed to get reference clock for the phy controller\n");
|
||||
return PTR_ERR(drv->ref_clk);
|
||||
}
|
||||
|
||||
drv->ref_rate = clk_get_rate(drv->ref_clk);
|
||||
if (drv->cfg->rate_to_clk) {
|
||||
ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < drv->cfg->num_phys; i++) {
|
||||
char *label = drv->cfg->phys[i].label;
|
||||
struct samsung_usb2_phy_instance *p = &drv->instances[i];
|
||||
|
||||
dev_dbg(dev, "Creating phy \"%s\"\n", label);
|
||||
p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
|
||||
if (IS_ERR(p->phy)) {
|
||||
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
|
||||
label);
|
||||
return PTR_ERR(p->phy);
|
||||
}
|
||||
|
||||
p->cfg = &drv->cfg->phys[i];
|
||||
p->drv = drv;
|
||||
phy_set_bus_width(p->phy, 8);
|
||||
phy_set_drvdata(p->phy, p);
|
||||
}
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev,
|
||||
samsung_usb2_phy_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
dev_err(drv->dev, "Failed to register phy provider\n");
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver samsung_usb2_phy_driver = {
|
||||
.probe = samsung_usb2_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = samsung_usb2_phy_of_match,
|
||||
.name = "samsung-usb2-phy",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(samsung_usb2_phy_driver);
|
||||
MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
|
||||
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:samsung-usb2-phy");
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Samsung SoC USB 1.1/2.0 PHY driver
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Kamil Debski <k.debski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _PHY_EXYNOS_USB2_H
|
||||
#define _PHY_EXYNOS_USB2_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define KHZ 1000
|
||||
#define MHZ (KHZ * KHZ)
|
||||
|
||||
struct samsung_usb2_phy_driver;
|
||||
struct samsung_usb2_phy_instance;
|
||||
struct samsung_usb2_phy_config;
|
||||
|
||||
struct samsung_usb2_phy_instance {
|
||||
const struct samsung_usb2_common_phy *cfg;
|
||||
struct phy *phy;
|
||||
struct samsung_usb2_phy_driver *drv;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct samsung_usb2_phy_driver {
|
||||
const struct samsung_usb2_phy_config *cfg;
|
||||
struct clk *clk;
|
||||
struct clk *ref_clk;
|
||||
unsigned long ref_rate;
|
||||
u32 ref_reg_val;
|
||||
struct device *dev;
|
||||
void __iomem *reg_phy;
|
||||
struct regmap *reg_pmu;
|
||||
struct regmap *reg_sys;
|
||||
spinlock_t lock;
|
||||
struct samsung_usb2_phy_instance instances[0];
|
||||
};
|
||||
|
||||
struct samsung_usb2_common_phy {
|
||||
int (*power_on)(struct samsung_usb2_phy_instance *);
|
||||
int (*power_off)(struct samsung_usb2_phy_instance *);
|
||||
unsigned int id;
|
||||
char *label;
|
||||
};
|
||||
|
||||
|
||||
struct samsung_usb2_phy_config {
|
||||
const struct samsung_usb2_common_phy *phys;
|
||||
int (*rate_to_clk)(unsigned long, u32 *);
|
||||
unsigned int num_phys;
|
||||
bool has_mode_switch;
|
||||
};
|
||||
|
||||
extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
|
||||
extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
|
||||
extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
|
||||
#endif
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Allwinner sun4i USB phy driver
|
||||
*
|
||||
* Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Based on code from
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
*
|
||||
* Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define REG_ISCR 0x00
|
||||
#define REG_PHYCTL 0x04
|
||||
#define REG_PHYBIST 0x08
|
||||
#define REG_PHYTUNE 0x0c
|
||||
|
||||
#define PHYCTL_DATA BIT(7)
|
||||
|
||||
#define SUNXI_AHB_ICHR8_EN BIT(10)
|
||||
#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
|
||||
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
|
||||
#define SUNXI_ULPI_BYPASS_EN BIT(0)
|
||||
|
||||
/* Common Control Bits for Both PHYs */
|
||||
#define PHY_PLL_BW 0x03
|
||||
#define PHY_RES45_CAL_EN 0x0c
|
||||
|
||||
/* Private Control Bits for Each PHY */
|
||||
#define PHY_TX_AMPLITUDE_TUNE 0x20
|
||||
#define PHY_TX_SLEWRATE_TUNE 0x22
|
||||
#define PHY_VBUSVALID_TH_SEL 0x25
|
||||
#define PHY_PULLUP_RES_SEL 0x27
|
||||
#define PHY_OTG_FUNC_EN 0x28
|
||||
#define PHY_VBUS_DET_EN 0x29
|
||||
#define PHY_DISCON_TH_SEL 0x2a
|
||||
|
||||
#define MAX_PHYS 3
|
||||
|
||||
struct sun4i_usb_phy_data {
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
struct mutex mutex;
|
||||
int num_phys;
|
||||
u32 disc_thresh;
|
||||
struct sun4i_usb_phy {
|
||||
struct phy *phy;
|
||||
void __iomem *pmu;
|
||||
struct regulator *vbus;
|
||||
struct reset_control *reset;
|
||||
int index;
|
||||
} phys[MAX_PHYS];
|
||||
};
|
||||
|
||||
#define to_sun4i_usb_phy_data(phy) \
|
||||
container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
|
||||
|
||||
static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
|
||||
int len)
|
||||
{
|
||||
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
|
||||
u32 temp, usbc_bit = BIT(phy->index * 2);
|
||||
int i;
|
||||
|
||||
mutex_lock(&phy_data->mutex);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
temp = readl(phy_data->base + REG_PHYCTL);
|
||||
|
||||
/* clear the address portion */
|
||||
temp &= ~(0xff << 8);
|
||||
|
||||
/* set the address */
|
||||
temp |= ((addr + i) << 8);
|
||||
writel(temp, phy_data->base + REG_PHYCTL);
|
||||
|
||||
/* set the data bit and clear usbc bit*/
|
||||
temp = readb(phy_data->base + REG_PHYCTL);
|
||||
if (data & 0x1)
|
||||
temp |= PHYCTL_DATA;
|
||||
else
|
||||
temp &= ~PHYCTL_DATA;
|
||||
temp &= ~usbc_bit;
|
||||
writeb(temp, phy_data->base + REG_PHYCTL);
|
||||
|
||||
/* pulse usbc_bit */
|
||||
temp = readb(phy_data->base + REG_PHYCTL);
|
||||
temp |= usbc_bit;
|
||||
writeb(temp, phy_data->base + REG_PHYCTL);
|
||||
|
||||
temp = readb(phy_data->base + REG_PHYCTL);
|
||||
temp &= ~usbc_bit;
|
||||
writeb(temp, phy_data->base + REG_PHYCTL);
|
||||
|
||||
data >>= 1;
|
||||
}
|
||||
mutex_unlock(&phy_data->mutex);
|
||||
}
|
||||
|
||||
static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
|
||||
{
|
||||
u32 bits, reg_value;
|
||||
|
||||
if (!phy->pmu)
|
||||
return;
|
||||
|
||||
bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
|
||||
SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
|
||||
|
||||
reg_value = readl(phy->pmu);
|
||||
|
||||
if (enable)
|
||||
reg_value |= bits;
|
||||
else
|
||||
reg_value &= ~bits;
|
||||
|
||||
writel(reg_value, phy->pmu);
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_init(struct phy *_phy)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(phy->reset);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(data->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Adjust PHY's magnitude and rate */
|
||||
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
|
||||
|
||||
/* Disconnect threshold adjustment */
|
||||
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
|
||||
|
||||
sun4i_usb_phy_passby(phy, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_exit(struct phy *_phy)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
|
||||
sun4i_usb_phy_passby(phy, 0);
|
||||
reset_control_assert(phy->reset);
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_power_on(struct phy *_phy)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
int ret = 0;
|
||||
|
||||
if (phy->vbus)
|
||||
ret = regulator_enable(phy->vbus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_power_off(struct phy *_phy)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
|
||||
if (phy->vbus)
|
||||
regulator_disable(phy->vbus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops sun4i_usb_phy_ops = {
|
||||
.init = sun4i_usb_phy_init,
|
||||
.exit = sun4i_usb_phy_exit,
|
||||
.power_on = sun4i_usb_phy_power_on,
|
||||
.power_off = sun4i_usb_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *sun4i_usb_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return data->phys[args->args[0]].phy;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sun4i_usb_phy_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
void __iomem *pmu = NULL;
|
||||
struct phy_provider *phy_provider;
|
||||
struct reset_control *reset;
|
||||
struct regulator *vbus;
|
||||
struct resource *res;
|
||||
struct phy *phy;
|
||||
char name[16];
|
||||
int i;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
|
||||
data->num_phys = 2;
|
||||
else
|
||||
data->num_phys = 3;
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
|
||||
data->disc_thresh = 3;
|
||||
else
|
||||
data->disc_thresh = 2;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
|
||||
data->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
data->clk = devm_clk_get(dev, "usb_phy");
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(dev, "could not get usb_phy clock\n");
|
||||
return PTR_ERR(data->clk);
|
||||
}
|
||||
|
||||
/* Skip 0, 0 is the phy for otg which is not yet supported. */
|
||||
for (i = 1; i < data->num_phys; i++) {
|
||||
snprintf(name, sizeof(name), "usb%d_vbus", i);
|
||||
vbus = devm_regulator_get_optional(dev, name);
|
||||
if (IS_ERR(vbus)) {
|
||||
if (PTR_ERR(vbus) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
vbus = NULL;
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "usb%d_reset", i);
|
||||
reset = devm_reset_control_get(dev, name);
|
||||
if (IS_ERR(reset)) {
|
||||
dev_err(dev, "failed to get reset %s\n", name);
|
||||
return PTR_ERR(reset);
|
||||
}
|
||||
|
||||
if (i) { /* No pmu for usbc0 */
|
||||
snprintf(name, sizeof(name), "pmu%d", i);
|
||||
res = platform_get_resource_byname(pdev,
|
||||
IORESOURCE_MEM, name);
|
||||
pmu = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pmu))
|
||||
return PTR_ERR(pmu);
|
||||
}
|
||||
|
||||
phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
data->phys[i].phy = phy;
|
||||
data->phys[i].pmu = pmu;
|
||||
data->phys[i].vbus = vbus;
|
||||
data->phys[i].reset = reset;
|
||||
data->phys[i].index = i;
|
||||
phy_set_drvdata(phy, &data->phys[i]);
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy" },
|
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy" },
|
||||
{ .compatible = "allwinner,sun7i-a20-usb-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
|
||||
|
||||
static struct platform_driver sun4i_usb_phy_driver = {
|
||||
.probe = sun4i_usb_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = sun4i_usb_phy_of_match,
|
||||
.name = "sun4i-usb-phy",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
module_platform_driver(sun4i_usb_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* phy-ti-pipe3 - PIPE3 PHY driver.
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||
* 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.
|
||||
*
|
||||
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define PLL_STATUS 0x00000004
|
||||
#define PLL_GO 0x00000008
|
||||
#define PLL_CONFIGURATION1 0x0000000C
|
||||
#define PLL_CONFIGURATION2 0x00000010
|
||||
#define PLL_CONFIGURATION3 0x00000014
|
||||
#define PLL_CONFIGURATION4 0x00000020
|
||||
|
||||
#define PLL_REGM_MASK 0x001FFE00
|
||||
#define PLL_REGM_SHIFT 0x9
|
||||
#define PLL_REGM_F_MASK 0x0003FFFF
|
||||
#define PLL_REGM_F_SHIFT 0x0
|
||||
#define PLL_REGN_MASK 0x000001FE
|
||||
#define PLL_REGN_SHIFT 0x1
|
||||
#define PLL_SELFREQDCO_MASK 0x0000000E
|
||||
#define PLL_SELFREQDCO_SHIFT 0x1
|
||||
#define PLL_SD_MASK 0x0003FC00
|
||||
#define PLL_SD_SHIFT 10
|
||||
#define SET_PLL_GO 0x1
|
||||
#define PLL_LDOPWDN BIT(15)
|
||||
#define PLL_TICOPWDN BIT(16)
|
||||
#define PLL_LOCK 0x2
|
||||
#define PLL_IDLE 0x1
|
||||
|
||||
/*
|
||||
* This is an Empirical value that works, need to confirm the actual
|
||||
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
|
||||
* to be correctly reflected in the PIPE3PHY_PLL_STATUS register.
|
||||
*/
|
||||
#define PLL_IDLE_TIME 100 /* in milliseconds */
|
||||
#define PLL_LOCK_TIME 100 /* in milliseconds */
|
||||
|
||||
struct pipe3_dpll_params {
|
||||
u16 m;
|
||||
u8 n;
|
||||
u8 freq:3;
|
||||
u8 sd;
|
||||
u32 mf;
|
||||
};
|
||||
|
||||
struct pipe3_dpll_map {
|
||||
unsigned long rate;
|
||||
struct pipe3_dpll_params params;
|
||||
};
|
||||
|
||||
struct ti_pipe3 {
|
||||
void __iomem *pll_ctrl_base;
|
||||
struct device *dev;
|
||||
struct device *control_dev;
|
||||
struct clk *wkupclk;
|
||||
struct clk *sys_clk;
|
||||
struct clk *refclk;
|
||||
struct pipe3_dpll_map *dpll_map;
|
||||
};
|
||||
|
||||
static struct pipe3_dpll_map dpll_map_usb[] = {
|
||||
{12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */
|
||||
{16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */
|
||||
{19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */
|
||||
{20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */
|
||||
{26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */
|
||||
{38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */
|
||||
{ }, /* Terminator */
|
||||
};
|
||||
|
||||
static struct pipe3_dpll_map dpll_map_sata[] = {
|
||||
{12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */
|
||||
{16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */
|
||||
{19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */
|
||||
{20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */
|
||||
{26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */
|
||||
{38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */
|
||||
{ }, /* Terminator */
|
||||
};
|
||||
|
||||
static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readl(addr + offset);
|
||||
}
|
||||
|
||||
static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset,
|
||||
u32 data)
|
||||
{
|
||||
__raw_writel(data, addr + offset);
|
||||
}
|
||||
|
||||
static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy)
|
||||
{
|
||||
unsigned long rate;
|
||||
struct pipe3_dpll_map *dpll_map = phy->dpll_map;
|
||||
|
||||
rate = clk_get_rate(phy->sys_clk);
|
||||
|
||||
for (; dpll_map->rate; dpll_map++) {
|
||||
if (rate == dpll_map->rate)
|
||||
return &dpll_map->params;
|
||||
}
|
||||
|
||||
dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ti_pipe3_power_off(struct phy *x)
|
||||
{
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_power_on(struct phy *x)
|
||||
{
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME);
|
||||
do {
|
||||
cpu_relax();
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
||||
if (val & PLL_LOCK)
|
||||
break;
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
if (!(val & PLL_LOCK)) {
|
||||
dev_err(phy->dev, "DPLL failed to lock\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
|
||||
{
|
||||
u32 val;
|
||||
struct pipe3_dpll_params *dpll_params;
|
||||
|
||||
dpll_params = ti_pipe3_get_dpll_params(phy);
|
||||
if (!dpll_params)
|
||||
return -EINVAL;
|
||||
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
|
||||
val &= ~PLL_REGN_MASK;
|
||||
val |= dpll_params->n << PLL_REGN_SHIFT;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
val &= ~PLL_SELFREQDCO_MASK;
|
||||
val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
|
||||
val &= ~PLL_REGM_MASK;
|
||||
val |= dpll_params->m << PLL_REGM_SHIFT;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
|
||||
val &= ~PLL_REGM_F_MASK;
|
||||
val |= dpll_params->mf << PLL_REGM_F_SHIFT;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
|
||||
val &= ~PLL_SD_MASK;
|
||||
val |= dpll_params->sd << PLL_SD_SHIFT;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
|
||||
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
|
||||
|
||||
return ti_pipe3_dpll_wait_lock(phy);
|
||||
}
|
||||
|
||||
static int ti_pipe3_init(struct phy *x)
|
||||
{
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
/* Bring it out of IDLE if it is IDLE */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
if (val & PLL_IDLE) {
|
||||
val &= ~PLL_IDLE;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
|
||||
ret = ti_pipe3_dpll_wait_lock(phy);
|
||||
}
|
||||
|
||||
/* Program the DPLL only if not locked */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
||||
if (!(val & PLL_LOCK))
|
||||
if (ti_pipe3_dpll_program(phy))
|
||||
return -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_pipe3_exit(struct phy *x)
|
||||
{
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
u32 val;
|
||||
unsigned long timeout;
|
||||
|
||||
/* SATA DPLL can't be powered down due to Errata i783 */
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
|
||||
return 0;
|
||||
|
||||
/* Put DPLL in IDLE mode */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
val |= PLL_IDLE;
|
||||
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
|
||||
|
||||
/* wait for LDO and Oscillator to power down */
|
||||
timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME);
|
||||
do {
|
||||
cpu_relax();
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
||||
if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
|
||||
break;
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
|
||||
dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n",
|
||||
val);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static struct phy_ops ops = {
|
||||
.init = ti_pipe3_init,
|
||||
.exit = ti_pipe3_exit,
|
||||
.power_on = ti_pipe3_power_on,
|
||||
.power_off = ti_pipe3_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ti_pipe3_id_table[];
|
||||
#endif
|
||||
|
||||
static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_pipe3 *phy;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct resource *res;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *control_node;
|
||||
struct platform_device *control_pdev;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
|
||||
if (!phy->dpll_map) {
|
||||
dev_err(&pdev->dev, "no DPLL data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
|
||||
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(phy->pll_ctrl_base))
|
||||
return PTR_ERR(phy->pll_ctrl_base);
|
||||
|
||||
phy->dev = &pdev->dev;
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
dev_err(&pdev->dev, "unable to get wkupclk\n");
|
||||
return PTR_ERR(phy->wkupclk);
|
||||
}
|
||||
|
||||
phy->refclk = devm_clk_get(phy->dev, "refclk");
|
||||
if (IS_ERR(phy->refclk)) {
|
||||
dev_err(&pdev->dev, "unable to get refclk\n");
|
||||
return PTR_ERR(phy->refclk);
|
||||
}
|
||||
} else {
|
||||
phy->wkupclk = ERR_PTR(-ENODEV);
|
||||
phy->refclk = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
|
||||
if (IS_ERR(phy->sys_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get sysclk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
if (!control_node) {
|
||||
dev_err(&pdev->dev, "Failed to get control device phandle\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control_pdev = of_find_device_by_node(control_node);
|
||||
if (!control_pdev) {
|
||||
dev_err(&pdev->dev, "Failed to get control device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
phy_provider = devm_of_phy_provider_register(phy->dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
pm_runtime_get(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (!pm_runtime_suspended(&pdev->dev))
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int ti_pipe3_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ti_pipe3 *phy = dev_get_drvdata(dev);
|
||||
|
||||
if (!IS_ERR(phy->wkupclk))
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_runtime_resume(struct device *dev)
|
||||
{
|
||||
u32 ret = 0;
|
||||
struct ti_pipe3 *phy = dev_get_drvdata(dev);
|
||||
|
||||
if (!IS_ERR(phy->refclk)) {
|
||||
ret = clk_prepare_enable(phy->refclk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(phy->wkupclk)) {
|
||||
ret = clk_prepare_enable(phy->wkupclk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
|
||||
err1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ti_pipe3_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
|
||||
ti_pipe3_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&ti_pipe3_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ti_pipe3_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,phy-usb3",
|
||||
.data = dpll_map_usb,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,omap-usb3",
|
||||
.data = dpll_map_usb,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,phy-pipe3-sata",
|
||||
.data = dpll_map_sata,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ti_pipe3_driver = {
|
||||
.probe = ti_pipe3_probe,
|
||||
.remove = ti_pipe3_remove,
|
||||
.driver = {
|
||||
.name = "ti-pipe3",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = DEV_PM_OPS,
|
||||
.of_match_table = of_match_ptr(ti_pipe3_id_table),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ti_pipe3_driver);
|
||||
|
||||
MODULE_ALIAS("platform: ti_pipe3");
|
||||
MODULE_AUTHOR("Texas Instruments Inc.");
|
||||
MODULE_DESCRIPTION("TI PIPE3 phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -338,7 +338,7 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
|
|||
dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
|
||||
mode);
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
|
||||
|
@ -661,7 +661,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
|||
struct phy_provider *phy_provider;
|
||||
struct phy_init_data *init_data = NULL;
|
||||
|
||||
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
|
||||
twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
|
||||
if (!twl)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
|
||||
otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
|
||||
if (!otg)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,10 +2,6 @@
|
|||
# USB device configuration
|
||||
#
|
||||
|
||||
# These are unused now, remove them once they are no longer selected
|
||||
config USB_ARCH_HAS_OHCI
|
||||
bool
|
||||
|
||||
config USB_OHCI_BIG_ENDIAN_DESC
|
||||
bool
|
||||
|
||||
|
@ -17,18 +13,12 @@ config USB_OHCI_LITTLE_ENDIAN
|
|||
default n if STB03xxx || PPC_MPC52xx
|
||||
default y
|
||||
|
||||
config USB_ARCH_HAS_EHCI
|
||||
bool
|
||||
|
||||
config USB_EHCI_BIG_ENDIAN_MMIO
|
||||
bool
|
||||
|
||||
config USB_EHCI_BIG_ENDIAN_DESC
|
||||
bool
|
||||
|
||||
config USB_ARCH_HAS_XHCI
|
||||
bool
|
||||
|
||||
menuconfig USB_SUPPORT
|
||||
bool "USB support"
|
||||
depends on HAS_IOMEM
|
||||
|
|
|
@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
|
|||
# Glue/Bridge layers go here
|
||||
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
|
||||
|
||||
# PCI doesn't provide stubs, need to check
|
||||
ifneq ($(CONFIG_PCI),)
|
||||
|
|
|
@ -50,12 +50,14 @@
|
|||
#define PORTSC_PTC (0x0FUL << 16)
|
||||
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
|
||||
/* PTS and PTW for non lpm version only */
|
||||
#define PORTSC_PFSC BIT(24)
|
||||
#define PORTSC_PTS(d) \
|
||||
(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
|
||||
#define PORTSC_PTW BIT(28)
|
||||
#define PORTSC_STS BIT(29)
|
||||
|
||||
/* DEVLC */
|
||||
#define DEVLC_PFSC BIT(23)
|
||||
#define DEVLC_PSPD (0x03UL << 25)
|
||||
#define DEVLC_PSPD_HS (0x02UL << 25)
|
||||
#define DEVLC_PTW BIT(27)
|
||||
|
|
|
@ -196,8 +196,6 @@ struct ci_hdrc {
|
|||
|
||||
struct ci_hdrc_platform_data *platdata;
|
||||
int vbus_active;
|
||||
/* FIXME: some day, we'll not use global phy */
|
||||
bool global_phy;
|
||||
struct usb_phy *transceiver;
|
||||
struct usb_hcd *hcd;
|
||||
struct dentry *debugfs;
|
||||
|
|
|
@ -96,7 +96,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct ci_hdrc_imx_data *data;
|
||||
struct ci_hdrc_platform_data pdata = {
|
||||
.name = "ci_hdrc_imx",
|
||||
.name = dev_name(&pdev->dev),
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_REQUIRE_TRANSCEIVER |
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Based off drivers/usb/chipidea/ci_hdrc_msm.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#include "ci.h"
|
||||
|
||||
static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
|
||||
.name = "ci_hdrc_zevio",
|
||||
.flags = CI_HDRC_REGS_SHARED,
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
};
|
||||
|
||||
static int ci_hdrc_zevio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *ci_pdev;
|
||||
|
||||
dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
|
||||
|
||||
ci_pdev = ci_hdrc_add_device(&pdev->dev,
|
||||
pdev->resource, pdev->num_resources,
|
||||
&ci_hdrc_zevio_platdata);
|
||||
|
||||
if (IS_ERR(ci_pdev)) {
|
||||
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
|
||||
return PTR_ERR(ci_pdev);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ci_pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_hdrc_zevio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *ci_pdev = platform_get_drvdata(pdev);
|
||||
|
||||
ci_hdrc_remove_device(ci_pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
|
||||
{ .compatible = "lsi,zevio-usb", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver ci_hdrc_zevio_driver = {
|
||||
.probe = ci_hdrc_zevio_probe,
|
||||
.remove = ci_hdrc_zevio_remove,
|
||||
.driver = {
|
||||
.name = "zevio_usb",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ci_hdrc_zevio_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
|
||||
module_platform_driver(ci_hdrc_zevio_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -64,6 +64,7 @@
|
|||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
|
@ -298,6 +299,13 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode)
|
|||
if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
|
||||
if (ci->hw_bank.lpm)
|
||||
hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC);
|
||||
else
|
||||
hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
|
||||
}
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
|
||||
|
@ -412,6 +420,9 @@ static int ci_get_platdata(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL)
|
||||
platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -496,33 +507,6 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
|
|||
}
|
||||
}
|
||||
|
||||
static int ci_usb_phy_init(struct ci_hdrc *ci)
|
||||
{
|
||||
if (ci->platdata->phy) {
|
||||
ci->transceiver = ci->platdata->phy;
|
||||
return usb_phy_init(ci->transceiver);
|
||||
} else {
|
||||
ci->global_phy = true;
|
||||
ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(ci->transceiver))
|
||||
ci->transceiver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ci_usb_phy_destroy(struct ci_hdrc *ci)
|
||||
{
|
||||
if (!ci->transceiver)
|
||||
return;
|
||||
|
||||
otg_set_peripheral(ci->transceiver->otg, NULL);
|
||||
if (ci->global_phy)
|
||||
usb_put_phy(ci->transceiver);
|
||||
else
|
||||
usb_phy_shutdown(ci->transceiver);
|
||||
}
|
||||
|
||||
static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -532,7 +516,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
enum usb_dr_mode dr_mode;
|
||||
|
||||
if (!dev->platform_data) {
|
||||
if (!dev_get_platdata(dev)) {
|
||||
dev_err(dev, "platform data missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -549,7 +533,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
ci->dev = dev;
|
||||
ci->platdata = dev->platform_data;
|
||||
ci->platdata = dev_get_platdata(dev);
|
||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||
CI_HDRC_IMX28_WRITE_FIX);
|
||||
|
||||
|
@ -561,7 +545,26 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
|
||||
hw_phymode_configure(ci);
|
||||
|
||||
ret = ci_usb_phy_init(ci);
|
||||
if (ci->platdata->phy)
|
||||
ci->transceiver = ci->platdata->phy;
|
||||
else
|
||||
ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
|
||||
if (IS_ERR(ci->transceiver)) {
|
||||
ret = PTR_ERR(ci->transceiver);
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
return ret;
|
||||
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = usb_phy_init(ci->transceiver);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to init phy: %d\n", ret);
|
||||
return ret;
|
||||
|
@ -572,8 +575,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
ci->irq = platform_get_irq(pdev, 0);
|
||||
if (ci->irq < 0) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
ret = -ENODEV;
|
||||
goto destroy_phy;
|
||||
ret = ci->irq;
|
||||
goto deinit_phy;
|
||||
}
|
||||
|
||||
ci_get_otg_capable(ci);
|
||||
|
@ -590,23 +593,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
ret = ci_hdrc_gadget_init(ci);
|
||||
if (ret)
|
||||
dev_info(dev, "doesn't support gadget\n");
|
||||
if (!ret && ci->transceiver) {
|
||||
ret = otg_set_peripheral(ci->transceiver->otg,
|
||||
&ci->gadget);
|
||||
/*
|
||||
* If we implement all USB functions using chipidea drivers,
|
||||
* it doesn't need to call above API, meanwhile, if we only
|
||||
* use gadget function, calling above API is useless.
|
||||
*/
|
||||
if (ret && ret != -ENOTSUPP)
|
||||
goto destroy_phy;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
|
||||
dev_err(dev, "no supported roles\n");
|
||||
ret = -ENODEV;
|
||||
goto destroy_phy;
|
||||
goto deinit_phy;
|
||||
}
|
||||
|
||||
if (ci->is_otg) {
|
||||
|
@ -663,8 +655,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
free_irq(ci->irq, ci);
|
||||
stop:
|
||||
ci_role_destroy(ci);
|
||||
destroy_phy:
|
||||
ci_usb_phy_destroy(ci);
|
||||
deinit_phy:
|
||||
usb_phy_shutdown(ci->transceiver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -677,7 +669,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
|
|||
free_irq(ci->irq, ci);
|
||||
ci_role_destroy(ci);
|
||||
ci_hdrc_enter_lpm(ci, true);
|
||||
ci_usb_phy_destroy(ci);
|
||||
usb_phy_shutdown(ci->transceiver);
|
||||
kfree(ci->hw_bank.regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -177,19 +177,6 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir)
|
|||
return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_test_and_clear_setup_status: test & clear setup status (execute without
|
||||
* interruption)
|
||||
* @n: endpoint number
|
||||
*
|
||||
* This function returns setup status
|
||||
*/
|
||||
static int hw_test_and_clear_setup_status(struct ci_hdrc *ci, int n)
|
||||
{
|
||||
n = ep_to_bit(ci, n);
|
||||
return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_ep_prime: primes endpoint (execute without interruption)
|
||||
* @num: endpoint number
|
||||
|
@ -961,6 +948,156 @@ __acquires(hwep->lock)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* isr_setup_packet_handler: setup packet handler
|
||||
* @ci: UDC descriptor
|
||||
*
|
||||
* This function handles setup packet
|
||||
*/
|
||||
static void isr_setup_packet_handler(struct ci_hdrc *ci)
|
||||
__releases(ci->lock)
|
||||
__acquires(ci->lock)
|
||||
{
|
||||
struct ci_hw_ep *hwep = &ci->ci_hw_ep[0];
|
||||
struct usb_ctrlrequest req;
|
||||
int type, num, dir, err = -EINVAL;
|
||||
u8 tmode = 0;
|
||||
|
||||
/*
|
||||
* Flush data and handshake transactions of previous
|
||||
* setup packet.
|
||||
*/
|
||||
_ep_nuke(ci->ep0out);
|
||||
_ep_nuke(ci->ep0in);
|
||||
|
||||
/* read_setup_packet */
|
||||
do {
|
||||
hw_test_and_set_setup_guard(ci);
|
||||
memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
|
||||
} while (!hw_test_and_clear_setup_guard(ci));
|
||||
|
||||
type = req.bRequestType;
|
||||
|
||||
ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
|
||||
|
||||
switch (req.bRequest) {
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
||||
le16_to_cpu(req.wValue) ==
|
||||
USB_ENDPOINT_HALT) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
num = le16_to_cpu(req.wIndex);
|
||||
dir = num & USB_ENDPOINT_DIR_MASK;
|
||||
num &= USB_ENDPOINT_NUMBER_MASK;
|
||||
if (dir) /* TX */
|
||||
num += ci->hw_ep_max / 2;
|
||||
if (!ci->ci_hw_ep[num].wedge) {
|
||||
spin_unlock(&ci->lock);
|
||||
err = usb_ep_clear_halt(
|
||||
&ci->ci_hw_ep[num].ep);
|
||||
spin_lock(&ci->lock);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
err = isr_setup_status_phase(ci);
|
||||
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
|
||||
le16_to_cpu(req.wValue) ==
|
||||
USB_DEVICE_REMOTE_WAKEUP) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
ci->remote_wakeup = 0;
|
||||
err = isr_setup_status_phase(ci);
|
||||
} else {
|
||||
goto delegate;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_INTERFACE))
|
||||
goto delegate;
|
||||
if (le16_to_cpu(req.wLength) != 2 ||
|
||||
le16_to_cpu(req.wValue) != 0)
|
||||
break;
|
||||
err = isr_get_status_response(ci, &req);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
|
||||
goto delegate;
|
||||
if (le16_to_cpu(req.wLength) != 0 ||
|
||||
le16_to_cpu(req.wIndex) != 0)
|
||||
break;
|
||||
ci->address = (u8)le16_to_cpu(req.wValue);
|
||||
ci->setaddr = true;
|
||||
err = isr_setup_status_phase(ci);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
||||
le16_to_cpu(req.wValue) ==
|
||||
USB_ENDPOINT_HALT) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
num = le16_to_cpu(req.wIndex);
|
||||
dir = num & USB_ENDPOINT_DIR_MASK;
|
||||
num &= USB_ENDPOINT_NUMBER_MASK;
|
||||
if (dir) /* TX */
|
||||
num += ci->hw_ep_max / 2;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
|
||||
spin_lock(&ci->lock);
|
||||
if (!err)
|
||||
isr_setup_status_phase(ci);
|
||||
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
switch (le16_to_cpu(req.wValue)) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
ci->remote_wakeup = 1;
|
||||
err = isr_setup_status_phase(ci);
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
tmode = le16_to_cpu(req.wIndex) >> 8;
|
||||
switch (tmode) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
ci->test_mode = tmode;
|
||||
err = isr_setup_status_phase(
|
||||
ci);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto delegate;
|
||||
}
|
||||
} else {
|
||||
goto delegate;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
delegate:
|
||||
if (req.wLength == 0) /* no data phase */
|
||||
ci->ep0_dir = TX;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
err = ci->driver->setup(&ci->gadget, &req);
|
||||
spin_lock(&ci->lock);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
spin_unlock(&ci->lock);
|
||||
if (usb_ep_set_halt(&hwep->ep))
|
||||
dev_err(ci->dev, "error: ep_set_halt\n");
|
||||
spin_lock(&ci->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* isr_tr_complete_handler: transaction complete interrupt handler
|
||||
* @ci: UDC descriptor
|
||||
|
@ -972,12 +1109,10 @@ __releases(ci->lock)
|
|||
__acquires(ci->lock)
|
||||
{
|
||||
unsigned i;
|
||||
u8 tmode = 0;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ci->hw_ep_max; i++) {
|
||||
struct ci_hw_ep *hwep = &ci->ci_hw_ep[i];
|
||||
int type, num, dir, err = -EINVAL;
|
||||
struct usb_ctrlrequest req;
|
||||
|
||||
if (hwep->ep.desc == NULL)
|
||||
continue; /* not configured */
|
||||
|
@ -997,148 +1132,10 @@ __acquires(ci->lock)
|
|||
}
|
||||
}
|
||||
|
||||
if (hwep->type != USB_ENDPOINT_XFER_CONTROL ||
|
||||
!hw_test_and_clear_setup_status(ci, i))
|
||||
continue;
|
||||
|
||||
if (i != 0) {
|
||||
dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush data and handshake transactions of previous
|
||||
* setup packet.
|
||||
*/
|
||||
_ep_nuke(ci->ep0out);
|
||||
_ep_nuke(ci->ep0in);
|
||||
|
||||
/* read_setup_packet */
|
||||
do {
|
||||
hw_test_and_set_setup_guard(ci);
|
||||
memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
|
||||
} while (!hw_test_and_clear_setup_guard(ci));
|
||||
|
||||
type = req.bRequestType;
|
||||
|
||||
ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
|
||||
|
||||
switch (req.bRequest) {
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
||||
le16_to_cpu(req.wValue) ==
|
||||
USB_ENDPOINT_HALT) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
num = le16_to_cpu(req.wIndex);
|
||||
dir = num & USB_ENDPOINT_DIR_MASK;
|
||||
num &= USB_ENDPOINT_NUMBER_MASK;
|
||||
if (dir) /* TX */
|
||||
num += ci->hw_ep_max/2;
|
||||
if (!ci->ci_hw_ep[num].wedge) {
|
||||
spin_unlock(&ci->lock);
|
||||
err = usb_ep_clear_halt(
|
||||
&ci->ci_hw_ep[num].ep);
|
||||
spin_lock(&ci->lock);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
err = isr_setup_status_phase(ci);
|
||||
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
|
||||
le16_to_cpu(req.wValue) ==
|
||||
USB_DEVICE_REMOTE_WAKEUP) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
ci->remote_wakeup = 0;
|
||||
err = isr_setup_status_phase(ci);
|
||||
} else {
|
||||
goto delegate;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_INTERFACE))
|
||||
goto delegate;
|
||||
if (le16_to_cpu(req.wLength) != 2 ||
|
||||
le16_to_cpu(req.wValue) != 0)
|
||||
break;
|
||||
err = isr_get_status_response(ci, &req);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
|
||||
goto delegate;
|
||||
if (le16_to_cpu(req.wLength) != 0 ||
|
||||
le16_to_cpu(req.wIndex) != 0)
|
||||
break;
|
||||
ci->address = (u8)le16_to_cpu(req.wValue);
|
||||
ci->setaddr = true;
|
||||
err = isr_setup_status_phase(ci);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
||||
le16_to_cpu(req.wValue) ==
|
||||
USB_ENDPOINT_HALT) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
num = le16_to_cpu(req.wIndex);
|
||||
dir = num & USB_ENDPOINT_DIR_MASK;
|
||||
num &= USB_ENDPOINT_NUMBER_MASK;
|
||||
if (dir) /* TX */
|
||||
num += ci->hw_ep_max/2;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
|
||||
spin_lock(&ci->lock);
|
||||
if (!err)
|
||||
isr_setup_status_phase(ci);
|
||||
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
|
||||
if (req.wLength != 0)
|
||||
break;
|
||||
switch (le16_to_cpu(req.wValue)) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
ci->remote_wakeup = 1;
|
||||
err = isr_setup_status_phase(ci);
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
tmode = le16_to_cpu(req.wIndex) >> 8;
|
||||
switch (tmode) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
ci->test_mode = tmode;
|
||||
err = isr_setup_status_phase(
|
||||
ci);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto delegate;
|
||||
}
|
||||
} else {
|
||||
goto delegate;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
delegate:
|
||||
if (req.wLength == 0) /* no data phase */
|
||||
ci->ep0_dir = TX;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
err = ci->driver->setup(&ci->gadget, &req);
|
||||
spin_lock(&ci->lock);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
spin_unlock(&ci->lock);
|
||||
if (usb_ep_set_halt(&hwep->ep))
|
||||
dev_err(ci->dev, "error: ep_set_halt\n");
|
||||
spin_lock(&ci->lock);
|
||||
}
|
||||
/* Only handle setup packet below */
|
||||
if (i == 0 &&
|
||||
hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
|
||||
isr_setup_packet_handler(ci);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1193,6 +1190,11 @@ static int ep_enable(struct usb_ep *ep,
|
|||
|
||||
hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */
|
||||
|
||||
if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) {
|
||||
dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n");
|
||||
retval = -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable endpoints in the HW other than ep0 as ep0
|
||||
* is always enabled
|
||||
|
@ -1837,12 +1839,6 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
|
|||
|
||||
dma_pool_destroy(ci->td_pool);
|
||||
dma_pool_destroy(ci->qh_pool);
|
||||
|
||||
if (ci->transceiver) {
|
||||
otg_set_peripheral(ci->transceiver->otg, NULL);
|
||||
if (ci->global_phy)
|
||||
usb_put_phy(ci->transceiver);
|
||||
}
|
||||
}
|
||||
|
||||
static int udc_id_switch_for_device(struct ci_hdrc *ci)
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
|
||||
#define USB_MAXALTSETTING 128 /* Hard limit */
|
||||
#define USB_MAXENDPOINTS 30 /* Hard limit */
|
||||
|
||||
#define USB_MAXCONFIG 8 /* Arbitrary limit */
|
||||
|
||||
|
|
|
@ -769,6 +769,88 @@ static int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev,
|
||||
unsigned char ep)
|
||||
{
|
||||
if (ep & USB_ENDPOINT_DIR_MASK)
|
||||
return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK];
|
||||
else
|
||||
return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK];
|
||||
}
|
||||
|
||||
static int parse_usbdevfs_streams(struct usb_dev_state *ps,
|
||||
struct usbdevfs_streams __user *streams,
|
||||
unsigned int *num_streams_ret,
|
||||
unsigned int *num_eps_ret,
|
||||
struct usb_host_endpoint ***eps_ret,
|
||||
struct usb_interface **intf_ret)
|
||||
{
|
||||
unsigned int i, num_streams, num_eps;
|
||||
struct usb_host_endpoint **eps;
|
||||
struct usb_interface *intf = NULL;
|
||||
unsigned char ep;
|
||||
int ifnum, ret;
|
||||
|
||||
if (get_user(num_streams, &streams->num_streams) ||
|
||||
get_user(num_eps, &streams->num_eps))
|
||||
return -EFAULT;
|
||||
|
||||
if (num_eps < 1 || num_eps > USB_MAXENDPOINTS)
|
||||
return -EINVAL;
|
||||
|
||||
/* The XHCI controller allows max 2 ^ 16 streams */
|
||||
if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
|
||||
return -EINVAL;
|
||||
|
||||
eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL);
|
||||
if (!eps)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
if (get_user(ep, &streams->eps[i])) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
eps[i] = ep_to_host_endpoint(ps->dev, ep);
|
||||
if (!eps[i]) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* usb_alloc/free_streams operate on an usb_interface */
|
||||
ifnum = findintfep(ps->dev, ep);
|
||||
if (ifnum < 0) {
|
||||
ret = ifnum;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
ret = checkintf(ps, ifnum);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
intf = usb_ifnum_to_if(ps->dev, ifnum);
|
||||
} else {
|
||||
/* Verify all eps belong to the same interface */
|
||||
if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (num_streams_ret)
|
||||
*num_streams_ret = num_streams;
|
||||
*num_eps_ret = num_eps;
|
||||
*eps_ret = eps;
|
||||
*intf_ret = intf;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(eps);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int match_devt(struct device *dev, void *data)
|
||||
{
|
||||
return dev->devt == (dev_t) (unsigned long) data;
|
||||
|
@ -1043,6 +1125,20 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void check_reset_of_active_ep(struct usb_device *udev,
|
||||
unsigned int epnum, char *ioctl_name)
|
||||
{
|
||||
struct usb_host_endpoint **eps;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out;
|
||||
ep = eps[epnum & 0x0f];
|
||||
if (ep && !list_empty(&ep->urb_list))
|
||||
dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n",
|
||||
task_pid_nr(current), current->comm,
|
||||
ioctl_name, epnum);
|
||||
}
|
||||
|
||||
static int proc_resetep(struct usb_dev_state *ps, void __user *arg)
|
||||
{
|
||||
unsigned int ep;
|
||||
|
@ -1056,6 +1152,7 @@ static int proc_resetep(struct usb_dev_state *ps, void __user *arg)
|
|||
ret = checkintf(ps, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
check_reset_of_active_ep(ps->dev, ep, "RESETEP");
|
||||
usb_reset_endpoint(ps->dev, ep);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1074,6 +1171,7 @@ static int proc_clearhalt(struct usb_dev_state *ps, void __user *arg)
|
|||
ret = checkintf(ps, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT");
|
||||
if (ep & USB_DIR_IN)
|
||||
pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
|
||||
else
|
||||
|
@ -1127,6 +1225,9 @@ static int proc_setintf(struct usb_dev_state *ps, void __user *arg)
|
|||
return -EFAULT;
|
||||
if ((ret = checkintf(ps, setintf.interface)))
|
||||
return ret;
|
||||
|
||||
destroy_async_on_interface(ps, setintf.interface);
|
||||
|
||||
return usb_set_interface(ps->dev, setintf.interface,
|
||||
setintf.altsetting);
|
||||
}
|
||||
|
@ -1189,6 +1290,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
struct usb_ctrlrequest *dr = NULL;
|
||||
unsigned int u, totlen, isofrmlen;
|
||||
int i, ret, is_in, num_sgs = 0, ifnum = -1;
|
||||
int number_of_packets = 0;
|
||||
unsigned int stream_id = 0;
|
||||
void *buf;
|
||||
|
||||
if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
|
||||
|
@ -1209,15 +1312,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) {
|
||||
is_in = 1;
|
||||
ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
|
||||
} else {
|
||||
is_in = 0;
|
||||
ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
|
||||
}
|
||||
ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);
|
||||
if (!ep)
|
||||
return -ENOENT;
|
||||
is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;
|
||||
|
||||
u = 0;
|
||||
switch(uurb->type) {
|
||||
|
@ -1242,7 +1340,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
le16_to_cpup(&dr->wIndex));
|
||||
if (ret)
|
||||
goto error;
|
||||
uurb->number_of_packets = 0;
|
||||
uurb->buffer_length = le16_to_cpup(&dr->wLength);
|
||||
uurb->buffer += 8;
|
||||
if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
|
||||
|
@ -1272,17 +1369,17 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
|
||||
goto interrupt_urb;
|
||||
}
|
||||
uurb->number_of_packets = 0;
|
||||
num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
|
||||
if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
|
||||
num_sgs = 0;
|
||||
if (ep->streams)
|
||||
stream_id = uurb->stream_id;
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_INTERRUPT:
|
||||
if (!usb_endpoint_xfer_int(&ep->desc))
|
||||
return -EINVAL;
|
||||
interrupt_urb:
|
||||
uurb->number_of_packets = 0;
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_ISO:
|
||||
|
@ -1292,15 +1389,16 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
return -EINVAL;
|
||||
if (!usb_endpoint_xfer_isoc(&ep->desc))
|
||||
return -EINVAL;
|
||||
number_of_packets = uurb->number_of_packets;
|
||||
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
|
||||
uurb->number_of_packets;
|
||||
number_of_packets;
|
||||
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
for (totlen = u = 0; u < uurb->number_of_packets; u++) {
|
||||
for (totlen = u = 0; u < number_of_packets; u++) {
|
||||
/*
|
||||
* arbitrary limit need for USB 3.0
|
||||
* bMaxBurst (0~15 allowed, 1~16 packets)
|
||||
|
@ -1331,7 +1429,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
as = alloc_async(uurb->number_of_packets);
|
||||
as = alloc_async(number_of_packets);
|
||||
if (!as) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
|
@ -1425,7 +1523,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
as->urb->setup_packet = (unsigned char *)dr;
|
||||
dr = NULL;
|
||||
as->urb->start_frame = uurb->start_frame;
|
||||
as->urb->number_of_packets = uurb->number_of_packets;
|
||||
as->urb->number_of_packets = number_of_packets;
|
||||
as->urb->stream_id = stream_id;
|
||||
if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
|
||||
ps->dev->speed == USB_SPEED_HIGH)
|
||||
as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
|
||||
|
@ -1433,7 +1532,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
as->urb->interval = ep->desc.bInterval;
|
||||
as->urb->context = as;
|
||||
as->urb->complete = async_completed;
|
||||
for (totlen = u = 0; u < uurb->number_of_packets; u++) {
|
||||
for (totlen = u = 0; u < number_of_packets; u++) {
|
||||
as->urb->iso_frame_desc[u].offset = totlen;
|
||||
as->urb->iso_frame_desc[u].length = isopkt[u].length;
|
||||
totlen += isopkt[u].length;
|
||||
|
@ -1983,6 +2082,45 @@ static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
|
|||
return claimintf(ps, dc.interface);
|
||||
}
|
||||
|
||||
static int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg)
|
||||
{
|
||||
unsigned num_streams, num_eps;
|
||||
struct usb_host_endpoint **eps;
|
||||
struct usb_interface *intf;
|
||||
int r;
|
||||
|
||||
r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps,
|
||||
&eps, &intf);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
destroy_async_on_interface(ps,
|
||||
intf->altsetting[0].desc.bInterfaceNumber);
|
||||
|
||||
r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL);
|
||||
kfree(eps);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
|
||||
{
|
||||
unsigned num_eps;
|
||||
struct usb_host_endpoint **eps;
|
||||
struct usb_interface *intf;
|
||||
int r;
|
||||
|
||||
r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
destroy_async_on_interface(ps,
|
||||
intf->altsetting[0].desc.bInterfaceNumber);
|
||||
|
||||
r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
|
||||
kfree(eps);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: All requests here that have interface numbers as parameters
|
||||
* are assuming that somehow the configuration has been prevented from
|
||||
|
@ -2159,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
case USBDEVFS_DISCONNECT_CLAIM:
|
||||
ret = proc_disconnect_claim(ps, p);
|
||||
break;
|
||||
case USBDEVFS_ALLOC_STREAMS:
|
||||
ret = proc_alloc_streams(ps, p);
|
||||
break;
|
||||
case USBDEVFS_FREE_STREAMS:
|
||||
ret = proc_free_streams(ps, p);
|
||||
break;
|
||||
}
|
||||
usb_unlock_device(dev);
|
||||
if (ret >= 0)
|
||||
|
|
|
@ -312,9 +312,9 @@ static int usb_probe_interface(struct device *dev)
|
|||
return error;
|
||||
}
|
||||
|
||||
id = usb_match_id(intf, driver->id_table);
|
||||
id = usb_match_dynamic_id(intf, driver);
|
||||
if (!id)
|
||||
id = usb_match_dynamic_id(intf, driver);
|
||||
id = usb_match_id(intf, driver->id_table);
|
||||
if (!id)
|
||||
return error;
|
||||
|
||||
|
@ -400,8 +400,9 @@ static int usb_unbind_interface(struct device *dev)
|
|||
{
|
||||
struct usb_driver *driver = to_usb_driver(dev->driver);
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_host_endpoint *ep, **eps = NULL;
|
||||
struct usb_device *udev;
|
||||
int error, r, lpm_disable_error;
|
||||
int i, j, error, r, lpm_disable_error;
|
||||
|
||||
intf->condition = USB_INTERFACE_UNBINDING;
|
||||
|
||||
|
@ -425,6 +426,26 @@ static int usb_unbind_interface(struct device *dev)
|
|||
driver->disconnect(intf);
|
||||
usb_cancel_queued_reset(intf);
|
||||
|
||||
/* Free streams */
|
||||
for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
|
||||
ep = &intf->cur_altsetting->endpoint[i];
|
||||
if (ep->streams == 0)
|
||||
continue;
|
||||
if (j == 0) {
|
||||
eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!eps) {
|
||||
dev_warn(dev, "oom, leaking streams\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
eps[j++] = ep;
|
||||
}
|
||||
if (j) {
|
||||
usb_free_streams(intf, eps, j, GFP_KERNEL);
|
||||
kfree(eps);
|
||||
}
|
||||
|
||||
/* Reset other interface state.
|
||||
* We cannot do a Set-Interface if the device is suspended or
|
||||
* if it is prepared for a system sleep (since installing a new
|
||||
|
@ -990,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister);
|
|||
* it doesn't support pre_reset/post_reset/reset_resume or
|
||||
* because it doesn't support suspend/resume.
|
||||
*
|
||||
* The caller must hold @intf's device's lock, but not its pm_mutex
|
||||
* and not @intf->dev.sem.
|
||||
* The caller must hold @intf's device's lock, but not @intf's lock.
|
||||
*/
|
||||
void usb_forced_unbind_intf(struct usb_interface *intf)
|
||||
{
|
||||
|
@ -1004,16 +1024,37 @@ void usb_forced_unbind_intf(struct usb_interface *intf)
|
|||
intf->needs_binding = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unbind drivers for @udev's marked interfaces. These interfaces have
|
||||
* the needs_binding flag set, for example by usb_resume_interface().
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
static void unbind_marked_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
if (intf->dev.driver && intf->needs_binding)
|
||||
usb_forced_unbind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delayed forced unbinding of a USB interface driver and scan
|
||||
* for rebinding.
|
||||
*
|
||||
* The caller must hold @intf's device's lock, but not its pm_mutex
|
||||
* and not @intf->dev.sem.
|
||||
* The caller must hold @intf's device's lock, but not @intf's lock.
|
||||
*
|
||||
* Note: Rebinds will be skipped if a system sleep transition is in
|
||||
* progress and the PM "complete" callback hasn't occurred yet.
|
||||
*/
|
||||
void usb_rebind_intf(struct usb_interface *intf)
|
||||
static void usb_rebind_intf(struct usb_interface *intf)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
@ -1030,6 +1071,41 @@ void usb_rebind_intf(struct usb_interface *intf)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rebind drivers to @udev's marked interfaces. These interfaces have
|
||||
* the needs_binding flag set.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
static void rebind_marked_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
if (intf->needs_binding)
|
||||
usb_rebind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unbind all of @udev's marked interfaces and then rebind all of them.
|
||||
* This ordering is necessary because some drivers claim several interfaces
|
||||
* when they are first probed.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
|
||||
{
|
||||
unbind_marked_interfaces(udev);
|
||||
rebind_marked_interfaces(udev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* Unbind drivers for @udev's interfaces that don't support suspend/resume
|
||||
|
@ -1059,43 +1135,6 @@ static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
|
|||
}
|
||||
}
|
||||
|
||||
/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
|
||||
* These interfaces have the needs_binding flag set by usb_resume_interface().
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
if (intf->dev.driver && intf->needs_binding)
|
||||
usb_forced_unbind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void do_rebind_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
if (intf->needs_binding)
|
||||
usb_rebind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device_driver *udriver;
|
||||
|
@ -1420,7 +1459,7 @@ int usb_resume_complete(struct device *dev)
|
|||
* whose needs_binding flag is set
|
||||
*/
|
||||
if (udev->state != USB_STATE_NOTATTACHED)
|
||||
do_rebind_interfaces(udev);
|
||||
rebind_marked_interfaces(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1442,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
|||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
unbind_no_reset_resume_drivers_interfaces(udev);
|
||||
unbind_marked_interfaces(udev);
|
||||
}
|
||||
|
||||
/* Avoid PM error messages for devices disconnected while suspended
|
||||
|
|
|
@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface,
|
|||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_device *dev;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
dev = interface_to_usbdev(interface);
|
||||
hcd = bus_to_hcd(dev->bus);
|
||||
|
@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface,
|
|||
if (dev->speed != USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
/* Streams only apply to bulk endpoints. */
|
||||
for (i = 0; i < num_eps; i++)
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
/* Streams only apply to bulk endpoints. */
|
||||
if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
|
||||
return -EINVAL;
|
||||
/* Re-alloc is not allowed */
|
||||
if (eps[i]->streams)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
|
||||
ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
|
||||
num_streams, mem_flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num_eps; i++)
|
||||
eps[i]->streams = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_alloc_streams);
|
||||
|
||||
|
@ -2078,8 +2089,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
|
|||
* Reverts a group of bulk endpoints back to not using stream IDs.
|
||||
* Can fail if we are given bad arguments, or HCD is broken.
|
||||
*
|
||||
* Return: On success, the number of allocated streams. On failure, a negative
|
||||
* error code.
|
||||
* Return: 0 on success. On failure, a negative error code.
|
||||
*/
|
||||
int usb_free_streams(struct usb_interface *interface,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
|
@ -2087,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface,
|
|||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_device *dev;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
dev = interface_to_usbdev(interface);
|
||||
hcd = bus_to_hcd(dev->bus);
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
/* Streams only apply to bulk endpoints. */
|
||||
/* Double-free is not allowed */
|
||||
for (i = 0; i < num_eps; i++)
|
||||
if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
|
||||
if (!eps[i] || !eps[i]->streams)
|
||||
return -EINVAL;
|
||||
|
||||
return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
|
||||
ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num_eps; i++)
|
||||
eps[i]->streams = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_free_streams);
|
||||
|
||||
|
|
|
@ -141,19 +141,27 @@ static int usb_device_supports_lpm(struct usb_device *udev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* All USB 3.0 must support LPM, but we need their max exit latency
|
||||
* information from the SuperSpeed Extended Capabilities BOS descriptor.
|
||||
/*
|
||||
* According to the USB 3.0 spec, all USB 3.0 devices must support LPM.
|
||||
* However, there are some that don't, and they set the U1/U2 exit
|
||||
* latencies to zero.
|
||||
*/
|
||||
if (!udev->bos->ss_cap) {
|
||||
dev_warn(&udev->dev, "No LPM exit latency info found. "
|
||||
"Power management will be impacted.\n");
|
||||
dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
|
||||
return 0;
|
||||
}
|
||||
if (udev->parent->lpm_capable)
|
||||
return 1;
|
||||
|
||||
dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. "
|
||||
"Power management will be impacted.\n");
|
||||
if (udev->bos->ss_cap->bU1devExitLat == 0 &&
|
||||
udev->bos->ss_cap->bU2DevExitLat == 0) {
|
||||
if (udev->parent)
|
||||
dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n");
|
||||
else
|
||||
dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!udev->parent || udev->parent->lpm_capable)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -499,7 +507,8 @@ static void led_work (struct work_struct *work)
|
|||
changed++;
|
||||
}
|
||||
if (changed)
|
||||
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&hub->leds, LED_CYCLE_PERIOD);
|
||||
}
|
||||
|
||||
/* use a short timeout for hub/port status fetches */
|
||||
|
@ -1041,7 +1050,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
if (type == HUB_INIT) {
|
||||
delay = hub_power_on(hub, false);
|
||||
INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
|
||||
schedule_delayed_work(&hub->init_work,
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&hub->init_work,
|
||||
msecs_to_jiffies(delay));
|
||||
|
||||
/* Suppress autosuspend until init is done */
|
||||
|
@ -1195,7 +1205,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
/* Don't do a long sleep inside a workqueue routine */
|
||||
if (type == HUB_INIT2) {
|
||||
INIT_DELAYED_WORK(&hub->init_work, hub_init_func3);
|
||||
schedule_delayed_work(&hub->init_work,
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&hub->init_work,
|
||||
msecs_to_jiffies(delay));
|
||||
return; /* Continues at init3: below */
|
||||
} else {
|
||||
|
@ -1209,7 +1220,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
if (status < 0)
|
||||
dev_err(hub->intfdev, "activate --> %d\n", status);
|
||||
if (hub->has_indicators && blinkenlights)
|
||||
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&hub->leds, LED_CYCLE_PERIOD);
|
||||
|
||||
/* Scan all ports that need attention */
|
||||
kick_khubd(hub);
|
||||
|
@ -3095,9 +3107,19 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
* operation is carried out here, after the port has been
|
||||
* resumed.
|
||||
*/
|
||||
if (udev->reset_resume)
|
||||
if (udev->reset_resume) {
|
||||
/*
|
||||
* If the device morphs or switches modes when it is reset,
|
||||
* we don't want to perform a reset-resume. We'll fail the
|
||||
* resume, which will cause a logical disconnect, and then
|
||||
* the device will be rediscovered.
|
||||
*/
|
||||
retry_reset_resume:
|
||||
status = usb_reset_and_verify_device(udev);
|
||||
if (udev->quirks & USB_QUIRK_RESET)
|
||||
status = -ENODEV;
|
||||
else
|
||||
status = usb_reset_and_verify_device(udev);
|
||||
}
|
||||
|
||||
/* 10.5.4.5 says be sure devices in the tree are still there.
|
||||
* For now let's assume the device didn't go crazy on resume,
|
||||
|
@ -3960,7 +3982,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
|
|||
connect_type = usb_get_hub_port_connect_type(udev->parent,
|
||||
udev->portnum);
|
||||
|
||||
if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
|
||||
if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) ||
|
||||
connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
|
||||
udev->usb2_hw_lpm_allowed = 1;
|
||||
usb_set_usb2_hardware_lpm(udev, 1);
|
||||
|
@ -4109,8 +4131,12 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
did_new_scheme = true;
|
||||
retval = hub_enable_device(udev);
|
||||
if (retval < 0)
|
||||
if (retval < 0) {
|
||||
dev_err(&udev->dev,
|
||||
"hub failed to enable device, error %d\n",
|
||||
retval);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#define GET_DESCRIPTOR_BUFSIZE 64
|
||||
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
|
||||
|
@ -4313,7 +4339,8 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
|
|||
/* hub LEDs are probably harder to miss than syslog */
|
||||
if (hub->has_indicators) {
|
||||
hub->indicator[port1-1] = INDICATOR_GREEN_BLINK;
|
||||
schedule_delayed_work (&hub->leds, 0);
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&hub->leds, 0);
|
||||
}
|
||||
}
|
||||
kfree(qual);
|
||||
|
@ -4542,7 +4569,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
if (hub->has_indicators) {
|
||||
hub->indicator[port1-1] =
|
||||
INDICATOR_AMBER_BLINK;
|
||||
schedule_delayed_work (&hub->leds, 0);
|
||||
queue_delayed_work(
|
||||
system_power_efficient_wq,
|
||||
&hub->leds, 0);
|
||||
}
|
||||
status = -ENOTCONN; /* Don't retry */
|
||||
goto loop_disable;
|
||||
|
@ -4741,6 +4770,8 @@ static void hub_events(void)
|
|||
|
||||
/* deal with port status changes */
|
||||
for (i = 1; i <= hdev->maxchild; i++) {
|
||||
struct usb_device *udev = hub->ports[i - 1]->child;
|
||||
|
||||
if (test_bit(i, hub->busy_bits))
|
||||
continue;
|
||||
connect_change = test_bit(i, hub->change_bits);
|
||||
|
@ -4839,8 +4870,6 @@ static void hub_events(void)
|
|||
*/
|
||||
if (hub_port_warm_reset_required(hub, portstatus)) {
|
||||
int status;
|
||||
struct usb_device *udev =
|
||||
hub->ports[i - 1]->child;
|
||||
|
||||
dev_dbg(hub_dev, "warm reset port %d\n", i);
|
||||
if (!udev ||
|
||||
|
@ -4857,6 +4886,24 @@ static void hub_events(void)
|
|||
usb_unlock_device(udev);
|
||||
connect_change = 0;
|
||||
}
|
||||
/*
|
||||
* On disconnect USB3 protocol ports transit from U0 to
|
||||
* SS.Inactive to Rx.Detect. If this happens a warm-
|
||||
* reset is not needed, but a (re)connect may happen
|
||||
* before khubd runs and sees the disconnect, and the
|
||||
* device may be an unknown state.
|
||||
*
|
||||
* If the port went through SS.Inactive without khubd
|
||||
* seeing it the C_LINK_STATE change flag will be set,
|
||||
* and we reset the dev to put it in a known state.
|
||||
*/
|
||||
} else if (udev && hub_is_superspeed(hub->hdev) &&
|
||||
(portchange & USB_PORT_STAT_C_LINK_STATE) &&
|
||||
(portstatus & USB_PORT_STAT_CONNECTION)) {
|
||||
usb_lock_device(udev);
|
||||
usb_reset_device(udev);
|
||||
usb_unlock_device(udev);
|
||||
connect_change = 0;
|
||||
}
|
||||
|
||||
if (connect_change)
|
||||
|
@ -5114,7 +5161,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
struct usb_device_descriptor descriptor = udev->descriptor;
|
||||
struct usb_host_bos *bos;
|
||||
int i, ret = 0;
|
||||
int i, j, ret = 0;
|
||||
int port1 = udev->portnum;
|
||||
|
||||
if (udev->state == USB_STATE_NOTATTACHED ||
|
||||
|
@ -5240,6 +5287,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
ret);
|
||||
goto re_enumerate;
|
||||
}
|
||||
/* Resetting also frees any allocated streams */
|
||||
for (j = 0; j < intf->cur_altsetting->desc.bNumEndpoints; j++)
|
||||
intf->cur_altsetting->endpoint[j].streams = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -5342,10 +5392,11 @@ int usb_reset_device(struct usb_device *udev)
|
|||
else if (cintf->condition ==
|
||||
USB_INTERFACE_BOUND)
|
||||
rebind = 1;
|
||||
if (rebind)
|
||||
cintf->needs_binding = 1;
|
||||
}
|
||||
if (ret == 0 && rebind)
|
||||
usb_rebind_intf(cintf);
|
||||
}
|
||||
usb_unbind_and_rebind_marked_interfaces(udev);
|
||||
}
|
||||
|
||||
usb_autosuspend_device(udev);
|
||||
|
|
|
@ -1293,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
struct usb_interface *iface;
|
||||
struct usb_host_interface *alt;
|
||||
struct usb_hcd *hcd = bus_to_hcd(dev->bus);
|
||||
int ret;
|
||||
int manual = 0;
|
||||
int i, ret, manual = 0;
|
||||
unsigned int epaddr;
|
||||
unsigned int pipe;
|
||||
|
||||
|
@ -1329,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Changing alt-setting also frees any allocated streams */
|
||||
for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++)
|
||||
iface->cur_altsetting->endpoint[i].streams = 0;
|
||||
|
||||
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
|
||||
if (ret < 0) {
|
||||
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
|
||||
|
|
|
@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct usb_device *dev,
|
|||
extern int usb_match_device(struct usb_device *dev,
|
||||
const struct usb_device_id *id);
|
||||
extern void usb_forced_unbind_intf(struct usb_interface *intf);
|
||||
extern void usb_rebind_intf(struct usb_interface *intf);
|
||||
extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
|
||||
|
||||
extern void usb_hub_release_all_ports(struct usb_device *hdev,
|
||||
struct usb_dev_state *owner);
|
||||
|
|
|
@ -71,6 +71,26 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
|
||||
* When the PRTINT interrupt fires, there are certain status bits in the Host
|
||||
* Port that needs to get cleared.
|
||||
*
|
||||
* @hsotg: Programming view of DWC_otg controller
|
||||
*/
|
||||
static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 hprt0 = readl(hsotg->regs + HPRT0);
|
||||
|
||||
if (hprt0 & HPRT0_ENACHG) {
|
||||
hprt0 &= ~HPRT0_ENA;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
|
||||
*
|
||||
|
@ -479,9 +499,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
|
|||
if (dwc2_is_device_mode(hsotg)) {
|
||||
dev_dbg(hsotg->dev,
|
||||
" --Port interrupt received in Device mode--\n");
|
||||
gintsts = GINTSTS_PRTINT;
|
||||
writel(gintsts, hsotg->regs + GINTSTS);
|
||||
retval = 1;
|
||||
dwc2_handle_usb_port_intr(hsotg);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -975,8 +975,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
|
|||
struct dwc2_qtd *qtd)
|
||||
{
|
||||
struct dwc2_hcd_urb *urb = qtd->urb;
|
||||
int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
|
||||
enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE;
|
||||
int pipe_type;
|
||||
int urb_xfer_done;
|
||||
|
||||
if (dbg_hc(chan))
|
||||
|
@ -984,6 +984,11 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
|
|||
"--Host Channel %d Interrupt: Transfer Complete--\n",
|
||||
chnum);
|
||||
|
||||
if (!urb)
|
||||
goto handle_xfercomp_done;
|
||||
|
||||
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
|
||||
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status);
|
||||
if (pipe_type == USB_ENDPOINT_XFER_ISOC)
|
||||
|
@ -1005,9 +1010,6 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
|
|||
}
|
||||
}
|
||||
|
||||
if (!urb)
|
||||
goto handle_xfercomp_done;
|
||||
|
||||
/* Update the QTD and URB states */
|
||||
switch (pipe_type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
|
@ -1105,7 +1107,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
|
|||
struct dwc2_qtd *qtd)
|
||||
{
|
||||
struct dwc2_hcd_urb *urb = qtd->urb;
|
||||
int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
|
||||
int pipe_type;
|
||||
|
||||
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n",
|
||||
chnum);
|
||||
|
@ -1119,6 +1121,8 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
|
|||
if (!urb)
|
||||
goto handle_stall_halt;
|
||||
|
||||
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
|
||||
|
||||
if (pipe_type == USB_ENDPOINT_XFER_CONTROL)
|
||||
dwc2_host_complete(hsotg, qtd, -EPIPE);
|
||||
|
||||
|
|
|
@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
|||
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
|
||||
* @dwc: pointer to our context structure
|
||||
*/
|
||||
static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* Before Resetting PHY, put Core in Reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
|
@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0) {
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
return ret;
|
||||
}
|
||||
mdelay(100);
|
||||
|
||||
/* Clear USB3 PHY reset */
|
||||
|
@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_CORESOFTRESET;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,6 +254,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
if (!dwc->has_hibernation)
|
||||
return 0;
|
||||
|
||||
if (!dwc->nr_scratch)
|
||||
return 0;
|
||||
|
||||
dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
|
||||
DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
|
||||
if (!dwc->scratchbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
dma_addr_t scratch_addr;
|
||||
u32 param;
|
||||
int ret;
|
||||
|
||||
if (!dwc->has_hibernation)
|
||||
return 0;
|
||||
|
||||
if (!dwc->nr_scratch)
|
||||
return 0;
|
||||
|
||||
/* should never fall here */
|
||||
if (!WARN_ON(dwc->scratchbuf))
|
||||
return 0;
|
||||
|
||||
scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
|
||||
dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (dma_mapping_error(dwc->dev, scratch_addr)) {
|
||||
dev_err(dwc->dev, "failed to map scratch buffer\n");
|
||||
ret = -EFAULT;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->scratch_addr = scratch_addr;
|
||||
|
||||
param = lower_32_bits(scratch_addr);
|
||||
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
param = upper_32_bits(scratch_addr);
|
||||
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
|
||||
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
if (!dwc->has_hibernation)
|
||||
return;
|
||||
|
||||
if (!dwc->nr_scratch)
|
||||
return;
|
||||
|
||||
/* should never fall here */
|
||||
if (!WARN_ON(dwc->scratchbuf))
|
||||
return;
|
||||
|
||||
dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
|
||||
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
|
||||
kfree(dwc->scratchbuf);
|
||||
}
|
||||
|
||||
static void dwc3_core_num_eps(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_hwparams *parms = &dwc->hwparams;
|
||||
|
@ -277,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
|||
static int dwc3_core_init(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 hwparams4 = dwc->hwparams.hwparams4;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
|
@ -306,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
cpu_relax();
|
||||
} while (true);
|
||||
|
||||
dwc3_core_soft_reset(dwc);
|
||||
ret = dwc3_core_soft_reset(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
|
@ -314,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
/**
|
||||
* WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
|
||||
* issue which would cause xHCI compliance tests to fail.
|
||||
*
|
||||
* Because of that we cannot enable clock gating on such
|
||||
* configurations.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
|
||||
* SOF/ITP Mode Used
|
||||
*/
|
||||
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
|
||||
dwc->dr_mode == USB_DR_MODE_OTG) &&
|
||||
(dwc->revision >= DWC3_REVISION_210A &&
|
||||
dwc->revision <= DWC3_REVISION_250A))
|
||||
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
|
||||
else
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
break;
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
|
||||
/* enable hibernation here */
|
||||
dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
|
@ -333,16 +454,36 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
ret = dwc3_setup_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
err1:
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
}
|
||||
|
||||
#define DWC3_ALIGN_MASK (16 - 1)
|
||||
|
@ -411,32 +552,52 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
if (IS_ERR(dwc->usb2_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_phy);
|
||||
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
if (ret == -ENXIO || ret == -ENODEV) {
|
||||
dwc->usb2_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
} else {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR(dwc->usb3_phy)) {
|
||||
ret = PTR_ERR(dwc->usb3_phy);
|
||||
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
if (ret == -ENXIO || ret == -ENODEV) {
|
||||
dwc->usb3_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
|
||||
if (IS_ERR(dwc->usb2_generic_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_generic_phy);
|
||||
if (ret == -ENOSYS || ret == -ENODEV) {
|
||||
dwc->usb2_generic_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
|
||||
if (IS_ERR(dwc->usb3_generic_phy)) {
|
||||
ret = PTR_ERR(dwc->usb3_generic_phy);
|
||||
if (ret == -ENOSYS || ret == -ENODEV) {
|
||||
dwc->usb3_generic_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dwc->xhci_resources[0].start = res->start;
|
||||
|
@ -479,6 +640,14 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
goto err0;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
dwc->dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
dwc->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
|
@ -487,21 +656,20 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_power;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err1;
|
||||
goto err_usb3phy_power;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
dwc->dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
dwc->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
|
@ -568,6 +736,12 @@ err3:
|
|||
err2:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err_usb3phy_power:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err_usb2phy_power:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err1:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
|
@ -585,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev)
|
|||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -682,6 +858,8 @@ static int dwc3_suspend(struct device *dev)
|
|||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -690,9 +868,17 @@ static int dwc3_resume(struct device *dev)
|
|||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_init;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
|
@ -716,6 +902,11 @@ static int dwc3_resume(struct device *dev)
|
|||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_usb2phy_init:
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
||||
|
|
|
@ -31,11 +31,14 @@
|
|||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
|
||||
#define DWC3_EVENT_SIZE 4 /* bytes */
|
||||
#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
|
||||
#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
|
||||
|
@ -157,6 +160,7 @@
|
|||
#define DWC3_GCTL_PRTCAP_OTG 3
|
||||
|
||||
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
|
||||
#define DWC3_GCTL_SOFITPSYNC (1 << 10)
|
||||
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
|
||||
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
|
||||
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
|
||||
|
@ -318,7 +322,7 @@
|
|||
/* Device Endpoint Command Register */
|
||||
#define DWC3_DEPCMD_PARAM_SHIFT 16
|
||||
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
|
||||
#define DWC3_DEPCMD_CMDACT (1 << 10)
|
||||
|
@ -393,6 +397,7 @@ struct dwc3_event_buffer {
|
|||
* @busy_slot: first slot which is owned by HW
|
||||
* @desc: usb_endpoint_descriptor pointer
|
||||
* @dwc: pointer to DWC controller
|
||||
* @saved_state: ep state saved during hibernation
|
||||
* @flags: endpoint flags (wedged, stalled, ...)
|
||||
* @current_trb: index of current used trb
|
||||
* @number: endpoint number (1 - 15)
|
||||
|
@ -415,6 +420,7 @@ struct dwc3_ep {
|
|||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
u32 saved_state;
|
||||
unsigned flags;
|
||||
#define DWC3_EP_ENABLED (1 << 0)
|
||||
#define DWC3_EP_STALL (1 << 1)
|
||||
|
@ -598,6 +604,7 @@ struct dwc3_scratchpad_array {
|
|||
* @ep0_trb: dma address of ep0_trb
|
||||
* @ep0_usb_req: dummy req used while handling STD USB requests
|
||||
* @ep0_bounce_addr: dma address of ep0_bounce
|
||||
* @scratch_addr: dma address of scratchbuf
|
||||
* @lock: for synchronizing
|
||||
* @dev: pointer to our struct device
|
||||
* @xhci: pointer to our xHCI child
|
||||
|
@ -606,6 +613,7 @@ struct dwc3_scratchpad_array {
|
|||
* @gadget_driver: pointer to the gadget driver
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @nr_scratch: number of scratch buffers
|
||||
* @num_event_buffers: calculated number of event buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
|
@ -613,16 +621,10 @@ struct dwc3_scratchpad_array {
|
|||
* @dr_mode: requested mode of operation
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
* @usb3_generic_phy: pointer to USB3 PHY
|
||||
* @dcfg: saved contents of DCFG register
|
||||
* @gctl: saved contents of GCTL register
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
* @u2sel: parameter from Set SEL request.
|
||||
* @u2pel: parameter from Set SEL request.
|
||||
|
@ -637,15 +639,31 @@ struct dwc3_scratchpad_array {
|
|||
* @mem: points to start of memory which is used for this struct.
|
||||
* @hwparams: copy of hwparams registers
|
||||
* @root: debugfs root folder pointer
|
||||
* @regset: debugfs pointer to regdump file
|
||||
* @test_mode: true when we're entering a USB test mode
|
||||
* @test_mode_nr: test feature selector
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @has_hibernation: true when dwc3 was configured with Hibernation
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *ep0_bounce;
|
||||
void *scratchbuf;
|
||||
u8 *setup_buf;
|
||||
dma_addr_t ctrl_req_addr;
|
||||
dma_addr_t ep0_trb_addr;
|
||||
dma_addr_t ep0_bounce_addr;
|
||||
dma_addr_t scratch_addr;
|
||||
struct dwc3_request ep0_usb_req;
|
||||
|
||||
/* device lock */
|
||||
|
@ -665,6 +683,9 @@ struct dwc3 {
|
|||
struct usb_phy *usb2_phy;
|
||||
struct usb_phy *usb3_phy;
|
||||
|
||||
struct phy *usb2_generic_phy;
|
||||
struct phy *usb3_generic_phy;
|
||||
|
||||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
|
@ -674,6 +695,7 @@ struct dwc3 {
|
|||
u32 dcfg;
|
||||
u32 gctl;
|
||||
|
||||
u32 nr_scratch;
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
|
@ -695,17 +717,9 @@ struct dwc3 {
|
|||
#define DWC3_REVISION_230A 0x5533230a
|
||||
#define DWC3_REVISION_240A 0x5533240a
|
||||
#define DWC3_REVISION_250A 0x5533250a
|
||||
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned pullups_connected:1;
|
||||
#define DWC3_REVISION_260A 0x5533260a
|
||||
#define DWC3_REVISION_270A 0x5533270a
|
||||
#define DWC3_REVISION_280A 0x5533280a
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
|
@ -730,6 +744,18 @@ struct dwc3 {
|
|||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
unsigned has_hibernation:1;
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned three_stage_setup:1;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -815,15 +841,15 @@ struct dwc3_event_depevt {
|
|||
* 12 - VndrDevTstRcved
|
||||
* @reserved15_12: Reserved, not used
|
||||
* @event_info: Information about this event
|
||||
* @reserved31_24: Reserved, not used
|
||||
* @reserved31_25: Reserved, not used
|
||||
*/
|
||||
struct dwc3_event_devt {
|
||||
u32 one_bit:1;
|
||||
u32 device_event:7;
|
||||
u32 type:4;
|
||||
u32 reserved15_12:4;
|
||||
u32 event_info:8;
|
||||
u32 reserved31_24:8;
|
||||
u32 event_info:9;
|
||||
u32 reserved31_25:7;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
|
@ -856,6 +882,19 @@ union dwc3_event {
|
|||
struct dwc3_event_gevt gevt;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc3_gadget_ep_cmd_params - representation of endpoint command
|
||||
* parameters
|
||||
* @param2: third parameter
|
||||
* @param1: second parameter
|
||||
* @param0: first parameter
|
||||
*/
|
||||
struct dwc3_gadget_ep_cmd_params {
|
||||
u32 param2;
|
||||
u32 param1;
|
||||
u32 param0;
|
||||
};
|
||||
|
||||
/*
|
||||
* DWC3 Features to be used as Driver Data
|
||||
*/
|
||||
|
@ -881,11 +920,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc)
|
|||
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_gadget_init(struct dwc3 *dwc);
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
||||
{ return 0; }
|
||||
static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
|
||||
enum dwc3_link_state state)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
{ return 0; }
|
||||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
int cmd, u32 param)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
/* power management interface */
|
||||
|
|
|
@ -423,11 +423,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory base resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
|
|
@ -67,6 +67,22 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_get_link_state - Gets current state of USB Link
|
||||
* @dwc: pointer to our context structure
|
||||
*
|
||||
* Caller should take care of locking. This function will
|
||||
* return the link state on success (>= 0) or -ETIMEDOUT.
|
||||
*/
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
return DWC3_DSTS_USBLNKST(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_set_link_state - Sets USB Link to a particular State
|
||||
* @dwc: pointer to our context structure
|
||||
|
@ -417,7 +433,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
||||
bool ignore)
|
||||
bool ignore, bool restore)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
||||
|
@ -436,6 +452,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
if (ignore)
|
||||
params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
|
||||
|
||||
if (restore) {
|
||||
params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
|
||||
params.param2 |= dep->saved_state;
|
||||
}
|
||||
|
||||
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
||||
| DWC3_DEPCFG_XFER_NOT_READY_EN;
|
||||
|
||||
|
@ -494,7 +515,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
||||
bool ignore)
|
||||
bool ignore, bool restore)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
|
@ -508,7 +529,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
|
||||
restore);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -548,13 +570,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
|
||||
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
|
||||
if (!list_empty(&dep->req_queued)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
|
||||
/* - giveback all requests to gadget driver */
|
||||
while (!list_empty(&dep->req_queued)) {
|
||||
|
@ -659,7 +681,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -771,9 +793,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
else
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
|
||||
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
|
@ -788,6 +807,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
BUG();
|
||||
}
|
||||
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
|
@ -1077,7 +1099,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
if (list_empty(&dep->req_queued)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1107,6 +1129,23 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4. Stream Capable Bulk Endpoints. We need to start the transfer
|
||||
* right away, otherwise host will not know we have streams to be
|
||||
* handled.
|
||||
*/
|
||||
if (dep->stream_capable) {
|
||||
int ret;
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1163,7 +1202,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
}
|
||||
if (r == req) {
|
||||
/* wait until it is processed */
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
goto out1;
|
||||
}
|
||||
dev_err(dwc->dev, "request %p was not queued to %s\n",
|
||||
|
@ -1194,8 +1233,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
|||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_SETSTALL, ¶ms);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to %s STALL on %s\n",
|
||||
value ? "set" : "clear",
|
||||
dev_err(dwc->dev, "failed to set STALL on %s\n",
|
||||
dep->name);
|
||||
else
|
||||
dep->flags |= DWC3_EP_STALL;
|
||||
|
@ -1203,8 +1241,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
|||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_CLEARSTALL, ¶ms);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to %s STALL on %s\n",
|
||||
value ? "set" : "clear",
|
||||
dev_err(dwc->dev, "failed to clear STALL on %s\n",
|
||||
dep->name);
|
||||
else
|
||||
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
||||
|
@ -1387,7 +1424,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
||||
{
|
||||
u32 reg;
|
||||
u32 timeout = 500;
|
||||
|
@ -1402,9 +1439,17 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
|||
if (dwc->revision >= DWC3_REVISION_194A)
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
reg |= DWC3_DCTL_RUN_STOP;
|
||||
|
||||
if (dwc->has_hibernation)
|
||||
reg |= DWC3_DCTL_KEEP_CONNECT;
|
||||
|
||||
dwc->pullups_connected = true;
|
||||
} else {
|
||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||
|
||||
if (dwc->has_hibernation && !suspend)
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
|
||||
dwc->pullups_connected = false;
|
||||
}
|
||||
|
||||
|
@ -1442,7 +1487,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
is_on = !!is_on;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = dwc3_gadget_run_stop(dwc, is_on);
|
||||
ret = dwc3_gadget_run_stop(dwc, is_on, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -1549,14 +1594,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err3;
|
||||
|
@ -1849,15 +1896,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
*/
|
||||
dep->flags = DWC3_EP_PENDING_REQUEST;
|
||||
} else {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1999,7 +2043,25 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
||||
static void dwc3_suspend_gadget(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dwc->gadget_driver->suspend(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_resume_gadget(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dwc->gadget_driver->resume(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
@ -2031,7 +2093,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
|||
*/
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
|
||||
cmd |= DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
|
@ -2259,18 +2322,24 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
*/
|
||||
reg |= DWC3_DCTL_HIRD_THRES(12);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
} else {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
}
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
|
@ -2378,9 +2447,50 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
|
||||
dwc->link_state = next;
|
||||
|
||||
switch (next) {
|
||||
case DWC3_LINK_STATE_U1:
|
||||
if (dwc->speed == USB_SPEED_SUPER)
|
||||
dwc3_suspend_gadget(dwc);
|
||||
break;
|
||||
case DWC3_LINK_STATE_U2:
|
||||
case DWC3_LINK_STATE_U3:
|
||||
dwc3_suspend_gadget(dwc);
|
||||
break;
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
dwc3_resume_gadget(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
|
||||
unsigned int evtinfo)
|
||||
{
|
||||
unsigned int is_ss = evtinfo & BIT(4);
|
||||
|
||||
/**
|
||||
* WORKAROUND: DWC3 revison 2.20a with hibernation support
|
||||
* have a known issue which can cause USB CV TD.9.23 to fail
|
||||
* randomly.
|
||||
*
|
||||
* Because of this issue, core could generate bogus hibernation
|
||||
* events which SW needs to ignore.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
|
||||
* Device Fallback from SuperSpeed
|
||||
*/
|
||||
if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
|
||||
return;
|
||||
|
||||
/* enter hibernation here */
|
||||
}
|
||||
|
||||
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_devt *event)
|
||||
{
|
||||
|
@ -2397,6 +2507,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
dwc3_gadget_wakeup_interrupt(dwc);
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_HIBER_REQ:
|
||||
if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
|
||||
"unexpected hibernation event\n"))
|
||||
break;
|
||||
|
||||
dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
|
||||
break;
|
||||
|
@ -2661,8 +2778,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
|
||||
int dwc3_gadget_prepare(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected)
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2671,7 +2790,7 @@ void dwc3_gadget_complete(struct dwc3 *dwc)
|
|||
{
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true);
|
||||
dwc3_gadget_run_stop(dwc, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2694,12 +2813,14 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
|
|||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
|
|
|
@ -56,12 +56,6 @@ struct dwc3;
|
|||
/* DEPXFERCFG parameter 0 */
|
||||
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
|
||||
|
||||
struct dwc3_gadget_ep_cmd_params {
|
||||
u32 param2;
|
||||
u32 param1;
|
||||
u32 param0;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
|
||||
|
@ -85,9 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
|
|||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event);
|
||||
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
||||
|
@ -95,9 +86,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
|||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
|
|
@ -226,7 +226,7 @@ config USB_GR_UDC
|
|||
config USB_OMAP
|
||||
tristate "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP1
|
||||
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG
|
||||
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
|
||||
help
|
||||
Many Texas Instruments OMAP processors have flexible full
|
||||
speed USB device controllers, with support for up to 30
|
||||
|
@ -301,7 +301,6 @@ config USB_PXA27X
|
|||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_S3C_HSOTG
|
||||
depends on ARM
|
||||
tristate "Designware/S3C HS/OtG USB Device controller"
|
||||
help
|
||||
The Designware USB2.0 high-speed gadget controller
|
||||
|
|
|
@ -1758,15 +1758,15 @@ static int at91udc_probe(struct platform_device *pdev)
|
|||
|
||||
/* newer chips have more FIFO memory than rm9200 */
|
||||
if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) {
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512);
|
||||
udc->ep[0].maxpacket = 64;
|
||||
udc->ep[3].maxpacket = 64;
|
||||
udc->ep[4].maxpacket = 512;
|
||||
udc->ep[5].maxpacket = 512;
|
||||
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
|
||||
udc->ep[3].maxpacket = 64;
|
||||
} else if (cpu_is_at91sam9263()) {
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
|
||||
udc->ep[0].maxpacket = 64;
|
||||
udc->ep[3].maxpacket = 64;
|
||||
}
|
||||
|
||||
udc->udp_baseaddr = ioremap(res->start, resource_size(res));
|
||||
|
|
|
@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
if (dma_status) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < USBA_NR_ENDPOINTS; i++)
|
||||
for (i = 1; i < USBA_NR_DMAS; i++)
|
||||
if (dma_status & (1 << i))
|
||||
usba_dma_irq(udc, &udc->usba_ep[i]);
|
||||
}
|
||||
|
@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
if (ep_status) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < USBA_NR_ENDPOINTS; i++)
|
||||
for (i = 0; i < udc->num_ep; i++)
|
||||
if (ep_status & (1 << i)) {
|
||||
if (ep_is_control(&udc->usba_ep[i]))
|
||||
usba_control_irq(udc, &udc->usba_ep[i]);
|
||||
|
@ -1827,12 +1827,12 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
|
|||
toggle_bias(0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
|
||||
DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name);
|
||||
DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1914,6 +1914,12 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
|||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
dev_err(&pdev->dev, "of_probe: no endpoint specified\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return eps;
|
||||
err:
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
@ -210,7 +210,7 @@
|
|||
#define USBA_FIFO_BASE(x) ((x) << 16)
|
||||
|
||||
/* Synth parameters */
|
||||
#define USBA_NR_ENDPOINTS 7
|
||||
#define USBA_NR_DMAS 7
|
||||
|
||||
#define EP0_FIFO_SIZE 64
|
||||
#define EP0_EPT_SIZE USBA_EPT_SIZE_64
|
||||
|
|
|
@ -1139,7 +1139,7 @@ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
|
|||
|
||||
uc = copy_gadget_strings(sp, n_gstrings, n_strings);
|
||||
if (IS_ERR(uc))
|
||||
return ERR_PTR(PTR_ERR(uc));
|
||||
return ERR_CAST(uc);
|
||||
|
||||
n_gs = get_containers_gs(uc);
|
||||
ret = usb_string_ids_tab(cdev, n_gs[0]->strings);
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
|
||||
#include <linux/aio.h>
|
||||
#include <linux/mmu_context.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include "u_fs.h"
|
||||
#include "configfs.h"
|
||||
|
||||
|
@ -99,6 +103,14 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
|
|||
}
|
||||
|
||||
|
||||
static inline enum ffs_setup_state
|
||||
ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
|
||||
{
|
||||
return (enum ffs_setup_state)
|
||||
cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
|
||||
}
|
||||
|
||||
|
||||
static void ffs_func_eps_disable(struct ffs_function *func);
|
||||
static int __must_check ffs_func_eps_enable(struct ffs_function *func);
|
||||
|
||||
|
@ -122,8 +134,8 @@ struct ffs_ep {
|
|||
struct usb_ep *ep; /* P: ffs->eps_lock */
|
||||
struct usb_request *req; /* P: epfile->mutex */
|
||||
|
||||
/* [0]: full speed, [1]: high speed */
|
||||
struct usb_endpoint_descriptor *descs[2];
|
||||
/* [0]: full speed, [1]: high speed, [2]: super speed */
|
||||
struct usb_endpoint_descriptor *descs[3];
|
||||
|
||||
u8 num;
|
||||
|
||||
|
@ -148,6 +160,25 @@ struct ffs_epfile {
|
|||
unsigned char _pad;
|
||||
};
|
||||
|
||||
/* ffs_io_data structure ***************************************************/
|
||||
|
||||
struct ffs_io_data {
|
||||
bool aio;
|
||||
bool read;
|
||||
|
||||
struct kiocb *kiocb;
|
||||
const struct iovec *iovec;
|
||||
unsigned long nr_segs;
|
||||
char __user *buf;
|
||||
size_t len;
|
||||
|
||||
struct mm_struct *mm;
|
||||
struct work_struct work;
|
||||
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
};
|
||||
|
||||
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
|
||||
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
|
||||
|
||||
|
@ -161,8 +192,10 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
|
|||
DEFINE_MUTEX(ffs_lock);
|
||||
EXPORT_SYMBOL(ffs_lock);
|
||||
|
||||
static struct ffs_dev *ffs_find_dev(const char *name);
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name);
|
||||
static struct ffs_dev *_ffs_alloc_dev(void);
|
||||
static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
|
||||
static void _ffs_free_dev(struct ffs_dev *dev);
|
||||
static void *ffs_acquire_dev(const char *dev_name);
|
||||
static void ffs_release_dev(struct ffs_data *ffs_data);
|
||||
static int ffs_ready(struct ffs_data *ffs);
|
||||
|
@ -218,7 +251,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
|
|||
}
|
||||
|
||||
ffs->setup_state = FFS_NO_SETUP;
|
||||
return ffs->ep0req_status;
|
||||
return req->status ? req->status : req->actual;
|
||||
}
|
||||
|
||||
static int __ffs_ep0_stall(struct ffs_data *ffs)
|
||||
|
@ -244,7 +277,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
|||
ENTER();
|
||||
|
||||
/* Fast check if setup was canceled */
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
|
||||
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
|
||||
return -EIDRM;
|
||||
|
||||
/* Acquire mutex */
|
||||
|
@ -310,8 +343,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
|||
* rather then _irqsave
|
||||
*/
|
||||
spin_lock_irq(&ffs->ev.waitq.lock);
|
||||
switch (FFS_SETUP_STATE(ffs)) {
|
||||
case FFS_SETUP_CANCELED:
|
||||
switch (ffs_setup_state_clear_cancelled(ffs)) {
|
||||
case FFS_SETUP_CANCELLED:
|
||||
ret = -EIDRM;
|
||||
goto done_spin;
|
||||
|
||||
|
@ -346,7 +379,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
|||
/*
|
||||
* We are guaranteed to be still in FFS_ACTIVE state
|
||||
* but the state of setup could have changed from
|
||||
* FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
|
||||
* FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
|
||||
* to check for that. If that happened we copied data
|
||||
* from user space in vain but it's unlikely.
|
||||
*
|
||||
|
@ -355,7 +388,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
|||
* transition can be performed and it's protected by
|
||||
* mutex.
|
||||
*/
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
|
||||
if (ffs_setup_state_clear_cancelled(ffs) ==
|
||||
FFS_SETUP_CANCELLED) {
|
||||
ret = -EIDRM;
|
||||
done_spin:
|
||||
spin_unlock_irq(&ffs->ev.waitq.lock);
|
||||
|
@ -421,7 +455,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
|||
ENTER();
|
||||
|
||||
/* Fast check if setup was canceled */
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
|
||||
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
|
||||
return -EIDRM;
|
||||
|
||||
/* Acquire mutex */
|
||||
|
@ -441,8 +475,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
|||
*/
|
||||
spin_lock_irq(&ffs->ev.waitq.lock);
|
||||
|
||||
switch (FFS_SETUP_STATE(ffs)) {
|
||||
case FFS_SETUP_CANCELED:
|
||||
switch (ffs_setup_state_clear_cancelled(ffs)) {
|
||||
case FFS_SETUP_CANCELLED:
|
||||
ret = -EIDRM;
|
||||
break;
|
||||
|
||||
|
@ -489,7 +523,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
|||
spin_lock_irq(&ffs->ev.waitq.lock);
|
||||
|
||||
/* See ffs_ep0_write() */
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
|
||||
if (ffs_setup_state_clear_cancelled(ffs) ==
|
||||
FFS_SETUP_CANCELLED) {
|
||||
ret = -EIDRM;
|
||||
break;
|
||||
}
|
||||
|
@ -558,6 +593,45 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
unsigned int mask = POLLWRNORM;
|
||||
int ret;
|
||||
|
||||
poll_wait(file, &ffs->ev.waitq, wait);
|
||||
|
||||
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
|
||||
if (unlikely(ret < 0))
|
||||
return mask;
|
||||
|
||||
switch (ffs->state) {
|
||||
case FFS_READ_DESCRIPTORS:
|
||||
case FFS_READ_STRINGS:
|
||||
mask |= POLLOUT;
|
||||
break;
|
||||
|
||||
case FFS_ACTIVE:
|
||||
switch (ffs->setup_state) {
|
||||
case FFS_NO_SETUP:
|
||||
if (ffs->ev.count)
|
||||
mask |= POLLIN;
|
||||
break;
|
||||
|
||||
case FFS_SETUP_PENDING:
|
||||
case FFS_SETUP_CANCELLED:
|
||||
mask |= (POLLIN | POLLOUT);
|
||||
break;
|
||||
}
|
||||
case FFS_CLOSING:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&ffs->mutex);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations ffs_ep0_operations = {
|
||||
.llseek = no_llseek,
|
||||
|
||||
|
@ -566,6 +640,7 @@ static const struct file_operations ffs_ep0_operations = {
|
|||
.read = ffs_ep0_read,
|
||||
.release = ffs_ep0_release,
|
||||
.unlocked_ioctl = ffs_ep0_ioctl,
|
||||
.poll = ffs_ep0_poll,
|
||||
};
|
||||
|
||||
|
||||
|
@ -581,8 +656,52 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_io(struct file *file,
|
||||
char __user *buf, size_t len, int read)
|
||||
static void ffs_user_copy_worker(struct work_struct *work)
|
||||
{
|
||||
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
|
||||
work);
|
||||
int ret = io_data->req->status ? io_data->req->status :
|
||||
io_data->req->actual;
|
||||
|
||||
if (io_data->read && ret > 0) {
|
||||
int i;
|
||||
size_t pos = 0;
|
||||
use_mm(io_data->mm);
|
||||
for (i = 0; i < io_data->nr_segs; i++) {
|
||||
if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
|
||||
&io_data->buf[pos],
|
||||
io_data->iovec[i].iov_len))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
pos += io_data->iovec[i].iov_len;
|
||||
}
|
||||
unuse_mm(io_data->mm);
|
||||
}
|
||||
|
||||
aio_complete(io_data->kiocb, ret, ret);
|
||||
|
||||
usb_ep_free_request(io_data->ep, io_data->req);
|
||||
|
||||
io_data->kiocb->private = NULL;
|
||||
if (io_data->read)
|
||||
kfree(io_data->iovec);
|
||||
kfree(io_data->buf);
|
||||
kfree(io_data);
|
||||
}
|
||||
|
||||
static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct ffs_io_data *io_data = req->context;
|
||||
|
||||
ENTER();
|
||||
|
||||
INIT_WORK(&io_data->work, ffs_user_copy_worker);
|
||||
schedule_work(&io_data->work);
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
{
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
struct ffs_ep *ep;
|
||||
|
@ -612,7 +731,7 @@ static ssize_t ffs_epfile_io(struct file *file,
|
|||
}
|
||||
|
||||
/* Do we halt? */
|
||||
halt = !read == !epfile->in;
|
||||
halt = (!io_data->read == !epfile->in);
|
||||
if (halt && epfile->isoc) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
|
@ -630,15 +749,32 @@ static ssize_t ffs_epfile_io(struct file *file,
|
|||
* Controller may require buffer size to be aligned to
|
||||
* maxpacketsize of an out endpoint.
|
||||
*/
|
||||
data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len;
|
||||
data_len = io_data->read ?
|
||||
usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
|
||||
io_data->len;
|
||||
|
||||
data = kmalloc(data_len, GFP_KERNEL);
|
||||
if (unlikely(!data))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!read && unlikely(copy_from_user(data, buf, len))) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
if (io_data->aio && !io_data->read) {
|
||||
int i;
|
||||
size_t pos = 0;
|
||||
for (i = 0; i < io_data->nr_segs; i++) {
|
||||
if (unlikely(copy_from_user(&data[pos],
|
||||
io_data->iovec[i].iov_base,
|
||||
io_data->iovec[i].iov_len))) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
pos += io_data->iovec[i].iov_len;
|
||||
}
|
||||
} else {
|
||||
if (!io_data->read &&
|
||||
unlikely(__copy_from_user(data, io_data->buf,
|
||||
io_data->len))) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,40 +797,78 @@ static ssize_t ffs_epfile_io(struct file *file,
|
|||
ret = -EBADMSG;
|
||||
} else {
|
||||
/* Fire the request */
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
struct usb_request *req;
|
||||
|
||||
struct usb_request *req = ep->req;
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
if (io_data->aio) {
|
||||
req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
|
||||
if (unlikely(!req))
|
||||
goto error_lock;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
req->buf = data;
|
||||
req->length = io_data->len;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
io_data->buf = data;
|
||||
io_data->ep = ep->ep;
|
||||
io_data->req = req;
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
/* nop */
|
||||
} else if (unlikely(wait_for_completion_interruptible(&done))) {
|
||||
ret = -EINTR;
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
req->context = io_data;
|
||||
req->complete = ffs_epfile_async_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret)) {
|
||||
usb_ep_free_request(ep->ep, req);
|
||||
goto error_lock;
|
||||
}
|
||||
ret = -EIOCBQUEUED;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
} else {
|
||||
/*
|
||||
* XXX We may end up silently droping data here.
|
||||
* Since data_len (i.e. req->length) may be bigger
|
||||
* than len (after being rounded up to maxpacketsize),
|
||||
* we may end up with more data then user space has
|
||||
* space for.
|
||||
*/
|
||||
ret = ep->status;
|
||||
if (read && ret > 0 &&
|
||||
unlikely(copy_to_user(buf, data,
|
||||
min_t(size_t, ret, len))))
|
||||
ret = -EFAULT;
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = io_data->len;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
/* nop */
|
||||
} else if (unlikely(
|
||||
wait_for_completion_interruptible(&done))) {
|
||||
ret = -EINTR;
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
} else {
|
||||
/*
|
||||
* XXX We may end up silently droping data
|
||||
* here. Since data_len (i.e. req->length) may
|
||||
* be bigger than len (after being rounded up
|
||||
* to maxpacketsize), we may end up with more
|
||||
* data then user space has space for.
|
||||
*/
|
||||
ret = ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = min_t(size_t, ret, io_data->len);
|
||||
|
||||
if (unlikely(copy_to_user(io_data->buf,
|
||||
data, ret)))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
}
|
||||
kfree(data);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&epfile->mutex);
|
||||
return ret;
|
||||
|
||||
error_lock:
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
mutex_unlock(&epfile->mutex);
|
||||
error:
|
||||
kfree(data);
|
||||
return ret;
|
||||
|
@ -704,17 +878,31 @@ static ssize_t
|
|||
ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
|
||||
loff_t *ptr)
|
||||
{
|
||||
struct ffs_io_data io_data;
|
||||
|
||||
ENTER();
|
||||
|
||||
return ffs_epfile_io(file, (char __user *)buf, len, 0);
|
||||
io_data.aio = false;
|
||||
io_data.read = false;
|
||||
io_data.buf = (char * __user)buf;
|
||||
io_data.len = len;
|
||||
|
||||
return ffs_epfile_io(file, &io_data);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
|
||||
{
|
||||
struct ffs_io_data io_data;
|
||||
|
||||
ENTER();
|
||||
|
||||
return ffs_epfile_io(file, buf, len, 1);
|
||||
io_data.aio = false;
|
||||
io_data.read = true;
|
||||
io_data.buf = buf;
|
||||
io_data.len = len;
|
||||
|
||||
return ffs_epfile_io(file, &io_data);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -733,6 +921,89 @@ ffs_epfile_open(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ffs_aio_cancel(struct kiocb *kiocb)
|
||||
{
|
||||
struct ffs_io_data *io_data = kiocb->private;
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
int value;
|
||||
|
||||
ENTER();
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (likely(io_data && io_data->ep && io_data->req))
|
||||
value = usb_ep_dequeue(io_data->ep, io_data->req);
|
||||
else
|
||||
value = -EINVAL;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
|
||||
const struct iovec *iovec,
|
||||
unsigned long nr_segs, loff_t loff)
|
||||
{
|
||||
struct ffs_io_data *io_data;
|
||||
|
||||
ENTER();
|
||||
|
||||
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
|
||||
if (unlikely(!io_data))
|
||||
return -ENOMEM;
|
||||
|
||||
io_data->aio = true;
|
||||
io_data->read = false;
|
||||
io_data->kiocb = kiocb;
|
||||
io_data->iovec = iovec;
|
||||
io_data->nr_segs = nr_segs;
|
||||
io_data->len = kiocb->ki_nbytes;
|
||||
io_data->mm = current->mm;
|
||||
|
||||
kiocb->private = io_data;
|
||||
|
||||
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
|
||||
|
||||
return ffs_epfile_io(kiocb->ki_filp, io_data);
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
|
||||
const struct iovec *iovec,
|
||||
unsigned long nr_segs, loff_t loff)
|
||||
{
|
||||
struct ffs_io_data *io_data;
|
||||
struct iovec *iovec_copy;
|
||||
|
||||
ENTER();
|
||||
|
||||
iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
|
||||
if (unlikely(!iovec_copy))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
|
||||
|
||||
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
|
||||
if (unlikely(!io_data)) {
|
||||
kfree(iovec_copy);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
io_data->aio = true;
|
||||
io_data->read = true;
|
||||
io_data->kiocb = kiocb;
|
||||
io_data->iovec = iovec_copy;
|
||||
io_data->nr_segs = nr_segs;
|
||||
io_data->len = kiocb->ki_nbytes;
|
||||
io_data->mm = current->mm;
|
||||
|
||||
kiocb->private = io_data;
|
||||
|
||||
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
|
||||
|
||||
return ffs_epfile_io(kiocb->ki_filp, io_data);
|
||||
}
|
||||
|
||||
static int
|
||||
ffs_epfile_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
|
@ -789,6 +1060,8 @@ static const struct file_operations ffs_epfile_operations = {
|
|||
.open = ffs_epfile_open,
|
||||
.write = ffs_epfile_write,
|
||||
.read = ffs_epfile_read,
|
||||
.aio_write = ffs_epfile_aio_write,
|
||||
.aio_read = ffs_epfile_aio_read,
|
||||
.release = ffs_epfile_release,
|
||||
.unlocked_ioctl = ffs_epfile_ioctl,
|
||||
};
|
||||
|
@ -1172,7 +1445,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
|
|||
if (ffs->epfiles)
|
||||
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
|
||||
|
||||
kfree(ffs->raw_descs);
|
||||
kfree(ffs->raw_descs_data);
|
||||
kfree(ffs->raw_strings);
|
||||
kfree(ffs->stringtabs);
|
||||
}
|
||||
|
@ -1184,14 +1457,15 @@ static void ffs_data_reset(struct ffs_data *ffs)
|
|||
ffs_data_clear(ffs);
|
||||
|
||||
ffs->epfiles = NULL;
|
||||
ffs->raw_descs_data = NULL;
|
||||
ffs->raw_descs = NULL;
|
||||
ffs->raw_strings = NULL;
|
||||
ffs->stringtabs = NULL;
|
||||
|
||||
ffs->raw_descs_length = 0;
|
||||
ffs->raw_fs_descs_length = 0;
|
||||
ffs->fs_descs_count = 0;
|
||||
ffs->hs_descs_count = 0;
|
||||
ffs->ss_descs_count = 0;
|
||||
|
||||
ffs->strings_count = 0;
|
||||
ffs->interfaces_count = 0;
|
||||
|
@ -1334,7 +1608,24 @@ static int ffs_func_eps_enable(struct ffs_function *func)
|
|||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||
do {
|
||||
struct usb_endpoint_descriptor *ds;
|
||||
ds = ep->descs[ep->descs[1] ? 1 : 0];
|
||||
int desc_idx;
|
||||
|
||||
if (ffs->gadget->speed == USB_SPEED_SUPER)
|
||||
desc_idx = 2;
|
||||
else if (ffs->gadget->speed == USB_SPEED_HIGH)
|
||||
desc_idx = 1;
|
||||
else
|
||||
desc_idx = 0;
|
||||
|
||||
/* fall-back to lower speed if desc missing for current speed */
|
||||
do {
|
||||
ds = ep->descs[desc_idx];
|
||||
} while (!ds && --desc_idx >= 0);
|
||||
|
||||
if (!ds) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ep->ep->driver_data = ep;
|
||||
ep->ep->desc = ds;
|
||||
|
@ -1469,6 +1760,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
|
|||
}
|
||||
break;
|
||||
|
||||
case USB_DT_SS_ENDPOINT_COMP:
|
||||
pr_vdebug("EP SS companion descriptor\n");
|
||||
if (length != sizeof(struct usb_ss_ep_comp_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
case USB_DT_INTERFACE_POWER:
|
||||
case USB_DT_DEBUG:
|
||||
|
@ -1579,60 +1876,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
|
|||
static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
char *const _data, size_t len)
|
||||
{
|
||||
unsigned fs_count, hs_count;
|
||||
int fs_len, ret = -EINVAL;
|
||||
char *data = _data;
|
||||
char *data = _data, *raw_descs;
|
||||
unsigned counts[3], flags;
|
||||
int ret = -EINVAL, i;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
|
||||
get_unaligned_le32(data + 4) != len))
|
||||
if (get_unaligned_le32(data + 4) != len)
|
||||
goto error;
|
||||
fs_count = get_unaligned_le32(data + 8);
|
||||
hs_count = get_unaligned_le32(data + 12);
|
||||
|
||||
if (!fs_count && !hs_count)
|
||||
goto einval;
|
||||
|
||||
data += 16;
|
||||
len -= 16;
|
||||
|
||||
if (likely(fs_count)) {
|
||||
fs_len = ffs_do_descs(fs_count, data, len,
|
||||
__ffs_data_do_entity, ffs);
|
||||
if (unlikely(fs_len < 0)) {
|
||||
ret = fs_len;
|
||||
switch (get_unaligned_le32(data)) {
|
||||
case FUNCTIONFS_DESCRIPTORS_MAGIC:
|
||||
flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
|
||||
data += 8;
|
||||
len -= 8;
|
||||
break;
|
||||
case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
|
||||
flags = get_unaligned_le32(data + 8);
|
||||
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
|
||||
FUNCTIONFS_HAS_HS_DESC |
|
||||
FUNCTIONFS_HAS_SS_DESC)) {
|
||||
ret = -ENOSYS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
data += fs_len;
|
||||
len -= fs_len;
|
||||
} else {
|
||||
fs_len = 0;
|
||||
data += 12;
|
||||
len -= 12;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (likely(hs_count)) {
|
||||
ret = ffs_do_descs(hs_count, data, len,
|
||||
__ffs_data_do_entity, ffs);
|
||||
if (unlikely(ret < 0))
|
||||
/* Read fs_count, hs_count and ss_count (if present) */
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (!(flags & (1 << i))) {
|
||||
counts[i] = 0;
|
||||
} else if (len < 4) {
|
||||
goto error;
|
||||
} else {
|
||||
ret = 0;
|
||||
} else {
|
||||
counts[i] = get_unaligned_le32(data);
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(len != ret))
|
||||
goto einval;
|
||||
/* Read descriptors */
|
||||
raw_descs = data;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (!counts[i])
|
||||
continue;
|
||||
ret = ffs_do_descs(counts[i], data, len,
|
||||
__ffs_data_do_entity, ffs);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
data += ret;
|
||||
len -= ret;
|
||||
}
|
||||
|
||||
ffs->raw_fs_descs_length = fs_len;
|
||||
ffs->raw_descs_length = fs_len + ret;
|
||||
ffs->raw_descs = _data;
|
||||
ffs->fs_descs_count = fs_count;
|
||||
ffs->hs_descs_count = hs_count;
|
||||
if (raw_descs == data || len) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ffs->raw_descs_data = _data;
|
||||
ffs->raw_descs = raw_descs;
|
||||
ffs->raw_descs_length = data - raw_descs;
|
||||
ffs->fs_descs_count = counts[0];
|
||||
ffs->hs_descs_count = counts[1];
|
||||
ffs->ss_descs_count = counts[2];
|
||||
|
||||
return 0;
|
||||
|
||||
einval:
|
||||
ret = -EINVAL;
|
||||
error:
|
||||
kfree(_data);
|
||||
return ret;
|
||||
|
@ -1789,7 +2102,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
|
|||
* the source does nothing.
|
||||
*/
|
||||
if (ffs->setup_state == FFS_SETUP_PENDING)
|
||||
ffs->setup_state = FFS_SETUP_CANCELED;
|
||||
ffs->setup_state = FFS_SETUP_CANCELLED;
|
||||
|
||||
switch (type) {
|
||||
case FUNCTIONFS_RESUME:
|
||||
|
@ -1850,21 +2163,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
|||
struct usb_endpoint_descriptor *ds = (void *)desc;
|
||||
struct ffs_function *func = priv;
|
||||
struct ffs_ep *ffs_ep;
|
||||
|
||||
/*
|
||||
* If hs_descriptors is not NULL then we are reading hs
|
||||
* descriptors now
|
||||
*/
|
||||
const int isHS = func->function.hs_descriptors != NULL;
|
||||
unsigned idx;
|
||||
unsigned ep_desc_id, idx;
|
||||
static const char *speed_names[] = { "full", "high", "super" };
|
||||
|
||||
if (type != FFS_DESCRIPTOR)
|
||||
return 0;
|
||||
|
||||
if (isHS)
|
||||
/*
|
||||
* If ss_descriptors is not NULL, we are reading super speed
|
||||
* descriptors; if hs_descriptors is not NULL, we are reading high
|
||||
* speed descriptors; otherwise, we are reading full speed
|
||||
* descriptors.
|
||||
*/
|
||||
if (func->function.ss_descriptors) {
|
||||
ep_desc_id = 2;
|
||||
func->function.ss_descriptors[(long)valuep] = desc;
|
||||
} else if (func->function.hs_descriptors) {
|
||||
ep_desc_id = 1;
|
||||
func->function.hs_descriptors[(long)valuep] = desc;
|
||||
else
|
||||
} else {
|
||||
ep_desc_id = 0;
|
||||
func->function.fs_descriptors[(long)valuep] = desc;
|
||||
}
|
||||
|
||||
if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
return 0;
|
||||
|
@ -1872,13 +2192,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
|||
idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
|
||||
ffs_ep = func->eps + idx;
|
||||
|
||||
if (unlikely(ffs_ep->descs[isHS])) {
|
||||
pr_vdebug("two %sspeed descriptors for EP %d\n",
|
||||
isHS ? "high" : "full",
|
||||
if (unlikely(ffs_ep->descs[ep_desc_id])) {
|
||||
pr_err("two %sspeed descriptors for EP %d\n",
|
||||
speed_names[ep_desc_id],
|
||||
ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
ffs_ep->descs[isHS] = ds;
|
||||
ffs_ep->descs[ep_desc_id] = ds;
|
||||
|
||||
ffs_dump_mem(": Original ep desc", ds, ds->bLength);
|
||||
if (ffs_ep->ep) {
|
||||
|
@ -2022,8 +2342,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
|||
const int full = !!func->ffs->fs_descs_count;
|
||||
const int high = gadget_is_dualspeed(func->gadget) &&
|
||||
func->ffs->hs_descs_count;
|
||||
const int super = gadget_is_superspeed(func->gadget) &&
|
||||
func->ffs->ss_descs_count;
|
||||
|
||||
int ret;
|
||||
int fs_len, hs_len, ret;
|
||||
|
||||
/* Make it a single chunk, less management later on */
|
||||
vla_group(d);
|
||||
|
@ -2032,15 +2354,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
|||
full ? ffs->fs_descs_count + 1 : 0);
|
||||
vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
|
||||
high ? ffs->hs_descs_count + 1 : 0);
|
||||
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
|
||||
super ? ffs->ss_descs_count + 1 : 0);
|
||||
vla_item_with_sz(d, short, inums, ffs->interfaces_count);
|
||||
vla_item_with_sz(d, char, raw_descs,
|
||||
high ? ffs->raw_descs_length : ffs->raw_fs_descs_length);
|
||||
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
|
||||
char *vlabuf;
|
||||
|
||||
ENTER();
|
||||
|
||||
/* Only high speed but not supported by gadget? */
|
||||
if (unlikely(!(full | high)))
|
||||
/* Has descriptors only for speeds gadget does not support */
|
||||
if (unlikely(!(full | high | super)))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Allocate a single chunk, less management later on */
|
||||
|
@ -2050,8 +2373,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
|||
|
||||
/* Zero */
|
||||
memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
|
||||
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16,
|
||||
d_raw_descs__sz);
|
||||
/* Copy descriptors */
|
||||
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
|
||||
ffs->raw_descs_length);
|
||||
|
||||
memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
|
||||
for (ret = ffs->eps_count; ret; --ret) {
|
||||
struct ffs_ep *ptr;
|
||||
|
@ -2073,22 +2398,38 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
|||
*/
|
||||
if (likely(full)) {
|
||||
func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
|
||||
ret = ffs_do_descs(ffs->fs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs),
|
||||
d_raw_descs__sz,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(ret < 0))
|
||||
fs_len = ffs_do_descs(ffs->fs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs),
|
||||
d_raw_descs__sz,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(fs_len < 0)) {
|
||||
ret = fs_len;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
ret = 0;
|
||||
fs_len = 0;
|
||||
}
|
||||
|
||||
if (likely(high)) {
|
||||
func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
|
||||
ret = ffs_do_descs(ffs->hs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) + ret,
|
||||
d_raw_descs__sz - ret,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
hs_len = ffs_do_descs(ffs->hs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) + fs_len,
|
||||
d_raw_descs__sz - fs_len,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(hs_len < 0)) {
|
||||
ret = hs_len;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
hs_len = 0;
|
||||
}
|
||||
|
||||
if (likely(super)) {
|
||||
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
|
||||
ret = ffs_do_descs(ffs->ss_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
|
||||
d_raw_descs__sz - fs_len - hs_len,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
}
|
||||
|
@ -2099,7 +2440,8 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
|||
* now.
|
||||
*/
|
||||
ret = ffs_do_descs(ffs->fs_descs_count +
|
||||
(high ? ffs->hs_descs_count : 0),
|
||||
(high ? ffs->hs_descs_count : 0) +
|
||||
(super ? ffs->ss_descs_count : 0),
|
||||
vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
|
||||
__ffs_func_bind_do_nums, func);
|
||||
if (unlikely(ret < 0))
|
||||
|
@ -2258,7 +2600,7 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
|
|||
|
||||
static LIST_HEAD(ffs_devices);
|
||||
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name)
|
||||
static struct ffs_dev *_ffs_do_find_dev(const char *name)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
|
@ -2275,7 +2617,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name)
|
|||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
static struct ffs_dev *ffs_get_single_dev(void)
|
||||
static struct ffs_dev *_ffs_get_single_dev(void)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
|
@ -2291,15 +2633,15 @@ static struct ffs_dev *ffs_get_single_dev(void)
|
|||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
static struct ffs_dev *ffs_find_dev(const char *name)
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
dev = ffs_get_single_dev();
|
||||
dev = _ffs_get_single_dev();
|
||||
if (dev)
|
||||
return dev;
|
||||
|
||||
return _ffs_find_dev(name);
|
||||
return _ffs_do_find_dev(name);
|
||||
}
|
||||
|
||||
/* Configfs support *********************************************************/
|
||||
|
@ -2335,7 +2677,7 @@ static void ffs_free_inst(struct usb_function_instance *f)
|
|||
|
||||
opts = to_f_fs_opts(f);
|
||||
ffs_dev_lock();
|
||||
ffs_free_dev(opts->dev);
|
||||
_ffs_free_dev(opts->dev);
|
||||
ffs_dev_unlock();
|
||||
kfree(opts);
|
||||
}
|
||||
|
@ -2390,7 +2732,7 @@ static struct usb_function_instance *ffs_alloc_inst(void)
|
|||
opts->func_inst.set_inst_name = ffs_set_inst_name;
|
||||
opts->func_inst.free_func_inst = ffs_free_inst;
|
||||
ffs_dev_lock();
|
||||
dev = ffs_alloc_dev();
|
||||
dev = _ffs_alloc_dev();
|
||||
ffs_dev_unlock();
|
||||
if (IS_ERR(dev)) {
|
||||
kfree(opts);
|
||||
|
@ -2446,6 +2788,7 @@ static void ffs_func_unbind(struct usb_configuration *c,
|
|||
*/
|
||||
func->function.fs_descriptors = NULL;
|
||||
func->function.hs_descriptors = NULL;
|
||||
func->function.ss_descriptors = NULL;
|
||||
func->interfaces_nums = NULL;
|
||||
|
||||
ffs_event_add(ffs, FUNCTIONFS_UNBIND);
|
||||
|
@ -2478,12 +2821,12 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
|
|||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
struct ffs_dev *ffs_alloc_dev(void)
|
||||
static struct ffs_dev *_ffs_alloc_dev(void)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
int ret;
|
||||
|
||||
if (ffs_get_single_dev())
|
||||
if (_ffs_get_single_dev())
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
|
@ -2511,10 +2854,10 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
|
|||
{
|
||||
struct ffs_dev *existing;
|
||||
|
||||
existing = _ffs_find_dev(name);
|
||||
existing = _ffs_do_find_dev(name);
|
||||
if (existing)
|
||||
return -EBUSY;
|
||||
|
||||
|
||||
dev->name = name;
|
||||
|
||||
return 0;
|
||||
|
@ -2555,7 +2898,7 @@ EXPORT_SYMBOL(ffs_single_dev);
|
|||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
void ffs_free_dev(struct ffs_dev *dev)
|
||||
static void _ffs_free_dev(struct ffs_dev *dev)
|
||||
{
|
||||
list_del(&dev->entry);
|
||||
if (dev->name_allocated)
|
||||
|
@ -2572,7 +2915,7 @@ static void *ffs_acquire_dev(const char *dev_name)
|
|||
ENTER();
|
||||
ffs_dev_lock();
|
||||
|
||||
ffs_dev = ffs_find_dev(dev_name);
|
||||
ffs_dev = _ffs_find_dev(dev_name);
|
||||
if (!ffs_dev)
|
||||
ffs_dev = ERR_PTR(-ENODEV);
|
||||
else if (ffs_dev->mounted)
|
||||
|
@ -2595,11 +2938,12 @@ static void ffs_release_dev(struct ffs_data *ffs_data)
|
|||
ffs_dev_lock();
|
||||
|
||||
ffs_dev = ffs_data->private_data;
|
||||
if (ffs_dev)
|
||||
if (ffs_dev) {
|
||||
ffs_dev->mounted = false;
|
||||
|
||||
if (ffs_dev->ffs_release_dev_callback)
|
||||
ffs_dev->ffs_release_dev_callback(ffs_dev);
|
||||
|
||||
if (ffs_dev->ffs_release_dev_callback)
|
||||
ffs_dev->ffs_release_dev_callback(ffs_dev);
|
||||
}
|
||||
|
||||
ffs_dev_unlock();
|
||||
}
|
||||
|
|
|
@ -276,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
}
|
||||
|
||||
net = gether_connect(&geth->port);
|
||||
return IS_ERR(net) ? PTR_ERR(net) : 0;
|
||||
return PTR_RET(net);
|
||||
}
|
||||
|
||||
static void geth_disable(struct usb_function *f)
|
||||
|
|
|
@ -225,14 +225,8 @@ static void gr_dfs_create(struct gr_udc *dev)
|
|||
const char *name = "gr_udc_state";
|
||||
|
||||
dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
|
||||
if (IS_ERR(dev->dfs_root)) {
|
||||
dev_err(dev->dev, "Failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root,
|
||||
dev, &gr_dfs_fops);
|
||||
if (IS_ERR(dev->dfs_state))
|
||||
dev_err(dev->dev, "Failed to create debugfs file %s\n", name);
|
||||
dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev,
|
||||
&gr_dfs_fops);
|
||||
}
|
||||
|
||||
static void gr_dfs_delete(struct gr_udc *dev)
|
||||
|
|
|
@ -439,11 +439,9 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
|||
/* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
|
||||
|
||||
value = -ENOMEM;
|
||||
kbuf = kmalloc (len, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
goto free1;
|
||||
if (copy_from_user (kbuf, buf, len)) {
|
||||
value = -EFAULT;
|
||||
kbuf = memdup_user(buf, len);
|
||||
if (!kbuf) {
|
||||
value = PTR_ERR(kbuf);
|
||||
goto free1;
|
||||
}
|
||||
|
||||
|
@ -452,7 +450,6 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
|||
data->name, len, (int) value);
|
||||
free1:
|
||||
mutex_unlock(&data->lock);
|
||||
kfree (kbuf);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -3295,9 +3295,9 @@ usb_clk_enable_fail:
|
|||
pll_set_fail:
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
pll_enable_fail:
|
||||
clk_put(udc->usb_slv_clk);
|
||||
usb_otg_clk_get_fail:
|
||||
clk_put(udc->usb_otg_clk);
|
||||
usb_otg_clk_get_fail:
|
||||
clk_put(udc->usb_slv_clk);
|
||||
usb_clk_get_fail:
|
||||
clk_put(udc->usb_pll_clk);
|
||||
pll_get_fail:
|
||||
|
|
|
@ -427,12 +427,17 @@ setup_rx_reqs(struct printer_dev *dev)
|
|||
req->length = USB_BUFSIZE;
|
||||
req->complete = rx_complete;
|
||||
|
||||
/* here, we unlock, and only unlock, to avoid deadlock. */
|
||||
spin_unlock(&dev->lock);
|
||||
error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
|
||||
spin_lock(&dev->lock);
|
||||
if (error) {
|
||||
DBG(dev, "rx submit --> %d\n", error);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
/* if the req is empty, then add it into dev->rx_reqs_active. */
|
||||
else if (list_empty(&req->list)) {
|
||||
list_add(&req->list, &dev->rx_reqs_active);
|
||||
}
|
||||
}
|
||||
|
@ -1133,6 +1138,7 @@ static int __init printer_bind_config(struct usb_configuration *c)
|
|||
NULL, "g_printer");
|
||||
if (IS_ERR(dev->pdev)) {
|
||||
ERROR(dev, "Failed to create device: g_printer\n");
|
||||
status = PTR_ERR(dev->pdev);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -617,7 +617,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
|||
to_write = DIV_ROUND_UP(to_write, 4);
|
||||
data = hs_req->req.buf + buf_pos;
|
||||
|
||||
writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
|
||||
iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
|
||||
|
||||
return (to_write >= can_write) ? -ENOSPC : 0;
|
||||
}
|
||||
|
@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
|||
ureq->length, ureq->actual);
|
||||
if (0)
|
||||
dev_dbg(hsotg->dev,
|
||||
"REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n",
|
||||
ureq->buf, length, ureq->dma,
|
||||
"REQ buf %p len %d dma 0x%pad noi=%d zp=%d snok=%d\n",
|
||||
ureq->buf, length, &ureq->dma,
|
||||
ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
|
||||
|
||||
maxreq = get_ep_limit(hs_ep);
|
||||
|
@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
|||
dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
|
||||
writel(ureq->dma, hsotg->regs + dma_reg);
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n",
|
||||
__func__, ureq->dma, dma_reg);
|
||||
dev_dbg(hsotg->dev, "%s: 0x%pad => 0x%08x\n",
|
||||
__func__, &ureq->dma, dma_reg);
|
||||
}
|
||||
|
||||
ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */
|
||||
|
@ -1185,6 +1185,41 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
|||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_stall_ep0 - stall ep0
|
||||
* @hsotg: The device state
|
||||
*
|
||||
* Set stall for ep0 as response for setup request.
|
||||
*/
|
||||
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) {
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
u32 reg;
|
||||
u32 ctrl;
|
||||
|
||||
dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
|
||||
reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
|
||||
|
||||
/*
|
||||
* DxEPCTL_Stall will be cleared by EP once it has
|
||||
* taken effect, so no need to clear later.
|
||||
*/
|
||||
|
||||
ctrl = readl(hsotg->regs + reg);
|
||||
ctrl |= DxEPCTL_Stall;
|
||||
ctrl |= DxEPCTL_CNAK;
|
||||
writel(ctrl, hsotg->regs + reg);
|
||||
|
||||
dev_dbg(hsotg->dev,
|
||||
"written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
|
||||
ctrl, reg, readl(hsotg->regs + reg));
|
||||
|
||||
/*
|
||||
* complete won't be called, so we enqueue
|
||||
* setup request here
|
||||
*/
|
||||
s3c_hsotg_enqueue_setup(hsotg);
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c_hsotg_process_control - process a control request
|
||||
* @hsotg: The device state
|
||||
|
@ -1262,38 +1297,8 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
|
|||
* so respond with a STALL for the status stage to indicate failure.
|
||||
*/
|
||||
|
||||
if (ret < 0) {
|
||||
u32 reg;
|
||||
u32 ctrl;
|
||||
|
||||
dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
|
||||
reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
|
||||
|
||||
/*
|
||||
* DxEPCTL_Stall will be cleared by EP once it has
|
||||
* taken effect, so no need to clear later.
|
||||
*/
|
||||
|
||||
ctrl = readl(hsotg->regs + reg);
|
||||
ctrl |= DxEPCTL_Stall;
|
||||
ctrl |= DxEPCTL_CNAK;
|
||||
writel(ctrl, hsotg->regs + reg);
|
||||
|
||||
dev_dbg(hsotg->dev,
|
||||
"written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
|
||||
ctrl, reg, readl(hsotg->regs + reg));
|
||||
|
||||
/*
|
||||
* don't believe we need to anything more to get the EP
|
||||
* to reply with a STALL packet
|
||||
*/
|
||||
|
||||
/*
|
||||
* complete won't be called, so we enqueue
|
||||
* setup request here
|
||||
*/
|
||||
s3c_hsotg_enqueue_setup(hsotg);
|
||||
}
|
||||
if (ret < 0)
|
||||
s3c_hsotg_stall_ep0(hsotg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1488,7 +1493,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
|||
* note, we might over-write the buffer end by 3 bytes depending on
|
||||
* alignment of the data.
|
||||
*/
|
||||
readsl(fifo, hs_req->req.buf + read_ptr, to_read);
|
||||
ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2832,6 +2837,15 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
|||
|
||||
dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
|
||||
|
||||
if (index == 0) {
|
||||
if (value)
|
||||
s3c_hsotg_stall_ep0(hs);
|
||||
else
|
||||
dev_warn(hs->dev,
|
||||
"%s: can't clear halt on ep0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write both IN and OUT control registers */
|
||||
|
||||
epreg = DIEPCTL(index);
|
||||
|
@ -3760,10 +3774,55 @@ static int s3c_hsotg_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 1
|
||||
#define s3c_hsotg_suspend NULL
|
||||
#define s3c_hsotg_resume NULL
|
||||
#endif
|
||||
static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->driver)
|
||||
dev_info(hsotg->dev, "suspending usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
if (hsotg->driver) {
|
||||
int ep;
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c_hsotg_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->driver) {
|
||||
dev_info(hsotg->dev, "resuming usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id s3c_hsotg_of_ids[] = {
|
||||
|
|
|
@ -1344,7 +1344,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
err_add_udc:
|
||||
err_add_device:
|
||||
clk_disable(hsudc->uclk);
|
||||
err_res:
|
||||
if (!IS_ERR_OR_NULL(hsudc->transceiver))
|
||||
|
|
|
@ -1613,7 +1613,7 @@ static struct se_wwn *usbg_make_tport(
|
|||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
tport->tport_wwpn = wwpn;
|
||||
snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name);
|
||||
snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
|
||||
return &tport->tport_wwn;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
|
||||
#define UETH__VERSION "29-May-2008"
|
||||
|
||||
#define GETHER_NAPI_WEIGHT 32
|
||||
|
||||
struct eth_dev {
|
||||
/* lock is held while accessing port_usb
|
||||
*/
|
||||
|
@ -72,6 +74,7 @@ struct eth_dev {
|
|||
struct sk_buff_head *list);
|
||||
|
||||
struct work_struct work;
|
||||
struct napi_struct rx_napi;
|
||||
|
||||
unsigned long todo;
|
||||
#define WORK_RX_MEMORY 0
|
||||
|
@ -253,18 +256,16 @@ enomem:
|
|||
DBG(dev, "rx submit --> %d\n", retval);
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct sk_buff *skb = req->context, *skb2;
|
||||
struct sk_buff *skb = req->context;
|
||||
struct eth_dev *dev = ep->driver_data;
|
||||
int status = req->status;
|
||||
bool rx_queue = 0;
|
||||
|
||||
switch (status) {
|
||||
|
||||
|
@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
} else {
|
||||
skb_queue_tail(&dev->rx_frames, skb);
|
||||
}
|
||||
skb = NULL;
|
||||
|
||||
skb2 = skb_dequeue(&dev->rx_frames);
|
||||
while (skb2) {
|
||||
if (status < 0
|
||||
|| ETH_HLEN > skb2->len
|
||||
|| skb2->len > VLAN_ETH_FRAME_LEN) {
|
||||
dev->net->stats.rx_errors++;
|
||||
dev->net->stats.rx_length_errors++;
|
||||
DBG(dev, "rx length %d\n", skb2->len);
|
||||
dev_kfree_skb_any(skb2);
|
||||
goto next_frame;
|
||||
}
|
||||
skb2->protocol = eth_type_trans(skb2, dev->net);
|
||||
dev->net->stats.rx_packets++;
|
||||
dev->net->stats.rx_bytes += skb2->len;
|
||||
|
||||
/* no buffer copies needed, unless hardware can't
|
||||
* use skb buffers.
|
||||
*/
|
||||
status = netif_rx(skb2);
|
||||
next_frame:
|
||||
skb2 = skb_dequeue(&dev->rx_frames);
|
||||
}
|
||||
if (!status)
|
||||
rx_queue = 1;
|
||||
break;
|
||||
|
||||
/* software-driven interface shutdown */
|
||||
|
@ -334,22 +313,20 @@ quiesce:
|
|||
/* FALLTHROUGH */
|
||||
|
||||
default:
|
||||
rx_queue = 1;
|
||||
dev_kfree_skb_any(skb);
|
||||
dev->net->stats.rx_errors++;
|
||||
DBG(dev, "rx status %d\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!netif_running(dev->net)) {
|
||||
clean:
|
||||
spin_lock(&dev->req_lock);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock(&dev->req_lock);
|
||||
req = NULL;
|
||||
}
|
||||
if (req)
|
||||
rx_submit(dev, req, GFP_ATOMIC);
|
||||
|
||||
if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi)))
|
||||
__napi_schedule(&dev->rx_napi);
|
||||
}
|
||||
|
||||
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
|
||||
|
@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
|
|||
{
|
||||
struct usb_request *req;
|
||||
unsigned long flags;
|
||||
int rx_counts = 0;
|
||||
|
||||
/* fill unused rxq slots with some skb */
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
while (!list_empty(&dev->rx_reqs)) {
|
||||
|
||||
if (++rx_counts > qlen(dev->gadget, dev->qmult))
|
||||
break;
|
||||
|
||||
req = container_of(dev->rx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_del_init(&req->list);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
|
||||
if (rx_submit(dev, req, gfp_flags) < 0) {
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
defer_kevent(dev, WORK_RX_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
|
|||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
|
||||
static int gether_poll(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi);
|
||||
struct sk_buff *skb;
|
||||
unsigned int work_done = 0;
|
||||
int status = 0;
|
||||
|
||||
while ((skb = skb_dequeue(&dev->rx_frames))) {
|
||||
if (status < 0
|
||||
|| ETH_HLEN > skb->len
|
||||
|| skb->len > VLAN_ETH_FRAME_LEN) {
|
||||
dev->net->stats.rx_errors++;
|
||||
dev->net->stats.rx_length_errors++;
|
||||
DBG(dev, "rx length %d\n", skb->len);
|
||||
dev_kfree_skb_any(skb);
|
||||
continue;
|
||||
}
|
||||
skb->protocol = eth_type_trans(skb, dev->net);
|
||||
dev->net->stats.rx_packets++;
|
||||
dev->net->stats.rx_bytes += skb->len;
|
||||
|
||||
status = netif_rx_ni(skb);
|
||||
}
|
||||
|
||||
if (netif_running(dev->net)) {
|
||||
rx_fill(dev, GFP_KERNEL);
|
||||
work_done++;
|
||||
}
|
||||
|
||||
if (work_done < budget)
|
||||
napi_complete(&dev->rx_napi);
|
||||
|
||||
return work_done;
|
||||
}
|
||||
|
||||
static void eth_work(struct work_struct *work)
|
||||
{
|
||||
struct eth_dev *dev = container_of(work, struct eth_dev, work);
|
||||
|
@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
|
|||
/* and open the tx floodgates */
|
||||
atomic_set(&dev->tx_qlen, 0);
|
||||
netif_wake_queue(dev->net);
|
||||
napi_enable(&dev->rx_napi);
|
||||
}
|
||||
|
||||
static int eth_open(struct net_device *net)
|
||||
|
@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net)
|
|||
unsigned long flags;
|
||||
|
||||
VDBG(dev, "%s\n", __func__);
|
||||
napi_disable(&dev->rx_napi);
|
||||
netif_stop_queue(net);
|
||||
|
||||
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
|
||||
|
@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = netdev_priv(net);
|
||||
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->req_lock);
|
||||
INIT_WORK(&dev->work, eth_work);
|
||||
|
@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = netdev_priv(net);
|
||||
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->req_lock);
|
||||
INIT_WORK(&dev->work, eth_work);
|
||||
|
@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link)
|
|||
{
|
||||
struct eth_dev *dev = link->ioport;
|
||||
struct usb_request *req;
|
||||
struct sk_buff *skb;
|
||||
|
||||
WARN_ON(!dev);
|
||||
if (!dev)
|
||||
|
@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link)
|
|||
spin_lock(&dev->req_lock);
|
||||
}
|
||||
spin_unlock(&dev->req_lock);
|
||||
|
||||
spin_lock(&dev->rx_frames.lock);
|
||||
while ((skb = __skb_dequeue(&dev->rx_frames)))
|
||||
dev_kfree_skb_any(skb);
|
||||
spin_unlock(&dev->rx_frames.lock);
|
||||
|
||||
link->in_ep->driver_data = NULL;
|
||||
link->in_ep->desc = NULL;
|
||||
|
||||
|
|
|
@ -65,10 +65,8 @@ static inline void ffs_dev_unlock(void)
|
|||
mutex_unlock(&ffs_lock);
|
||||
}
|
||||
|
||||
struct ffs_dev *ffs_alloc_dev(void);
|
||||
int ffs_name_dev(struct ffs_dev *dev, const char *name);
|
||||
int ffs_single_dev(struct ffs_dev *dev);
|
||||
void ffs_free_dev(struct ffs_dev *dev);
|
||||
|
||||
struct ffs_epfile;
|
||||
struct ffs_function;
|
||||
|
@ -125,7 +123,7 @@ enum ffs_setup_state {
|
|||
* setup. If this state is set read/write on ep0 return
|
||||
* -EIDRM. This state is only set when adding event.
|
||||
*/
|
||||
FFS_SETUP_CANCELED
|
||||
FFS_SETUP_CANCELLED
|
||||
};
|
||||
|
||||
struct ffs_data {
|
||||
|
@ -156,7 +154,6 @@ struct ffs_data {
|
|||
*/
|
||||
struct usb_request *ep0req; /* P: mutex */
|
||||
struct completion ep0req_completion; /* P: mutex */
|
||||
int ep0req_status; /* P: mutex */
|
||||
|
||||
/* reference counter */
|
||||
atomic_t ref;
|
||||
|
@ -168,19 +165,18 @@ struct ffs_data {
|
|||
|
||||
/*
|
||||
* Possible transitions:
|
||||
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
|
||||
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
|
||||
* happens only in ep0 read which is P: mutex
|
||||
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
|
||||
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
|
||||
* happens only in ep0 i/o which is P: mutex
|
||||
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
|
||||
* + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
|
||||
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock
|
||||
* + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg
|
||||
*
|
||||
* This field should never be accessed directly and instead
|
||||
* ffs_setup_state_clear_cancelled function should be used.
|
||||
*/
|
||||
enum ffs_setup_state setup_state;
|
||||
|
||||
#define FFS_SETUP_STATE(ffs) \
|
||||
((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
|
||||
FFS_SETUP_CANCELED, FFS_NO_SETUP))
|
||||
|
||||
/* Events & such. */
|
||||
struct {
|
||||
u8 types[4];
|
||||
|
@ -210,16 +206,16 @@ struct ffs_data {
|
|||
|
||||
/* filled by __ffs_data_got_descs() */
|
||||
/*
|
||||
* Real descriptors are 16 bytes after raw_descs (so you need
|
||||
* to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
|
||||
* first full speed descriptor). raw_descs_length and
|
||||
* raw_fs_descs_length do not have those 16 bytes added.
|
||||
* raw_descs is what you kfree, real_descs points inside of raw_descs,
|
||||
* where full speed, high speed and super speed descriptors start.
|
||||
* real_descs_length is the length of all those descriptors.
|
||||
*/
|
||||
const void *raw_descs_data;
|
||||
const void *raw_descs;
|
||||
unsigned raw_descs_length;
|
||||
unsigned raw_fs_descs_length;
|
||||
unsigned fs_descs_count;
|
||||
unsigned hs_descs_count;
|
||||
unsigned ss_descs_count;
|
||||
|
||||
unsigned short strings_count;
|
||||
unsigned short interfaces_count;
|
||||
|
|
|
@ -584,7 +584,6 @@ config FHCI_DEBUG
|
|||
config USB_U132_HCD
|
||||
tristate "Elan U132 Adapter Host Controller"
|
||||
depends on USB_FTDI_ELAN
|
||||
default M
|
||||
help
|
||||
The U132 adapter is a USB to CardBus adapter specifically designed
|
||||
for PC cards that contain an OHCI host controller. Typical PC cards
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright 2007 Steven Brown <sbrown@cortland.com>
|
||||
* Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
|
||||
* Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Derived from the ohci-ssb driver
|
||||
* Copyright 2007 Michael Buesch <m@bues.ch>
|
||||
|
@ -18,6 +19,7 @@
|
|||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -25,6 +27,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
@ -33,6 +36,13 @@
|
|||
#include "ehci.h"
|
||||
|
||||
#define DRIVER_DESC "EHCI generic platform driver"
|
||||
#define EHCI_MAX_CLKS 3
|
||||
#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
|
||||
|
||||
struct ehci_platform_priv {
|
||||
struct clk *clks[EHCI_MAX_CLKS];
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static const char hcd_name[] = "ehci-platform";
|
||||
|
||||
|
@ -45,8 +55,6 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
|
|||
|
||||
hcd->has_tt = pdata->has_tt;
|
||||
ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
|
||||
ehci->big_endian_desc = pdata->big_endian_desc;
|
||||
ehci->big_endian_mmio = pdata->big_endian_mmio;
|
||||
|
||||
if (pdata->pre_setup) {
|
||||
retval = pdata->pre_setup(hcd);
|
||||
|
@ -64,38 +72,91 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_platform_power_on(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
int clk, ret;
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
|
||||
ret = clk_prepare_enable(priv->clks[clk]);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
}
|
||||
|
||||
if (priv->phy) {
|
||||
ret = phy_init(priv->phy);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
|
||||
ret = phy_power_on(priv->phy);
|
||||
if (ret)
|
||||
goto err_exit_phy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit_phy:
|
||||
phy_exit(priv->phy);
|
||||
err_disable_clks:
|
||||
while (--clk >= 0)
|
||||
clk_disable_unprepare(priv->clks[clk]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ehci_platform_power_off(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
int clk;
|
||||
|
||||
if (priv->phy) {
|
||||
phy_power_off(priv->phy);
|
||||
phy_exit(priv->phy);
|
||||
}
|
||||
|
||||
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
if (priv->clks[clk])
|
||||
clk_disable_unprepare(priv->clks[clk]);
|
||||
}
|
||||
|
||||
static struct hc_driver __read_mostly ehci_platform_hc_driver;
|
||||
|
||||
static const struct ehci_driver_overrides platform_overrides __initconst = {
|
||||
.reset = ehci_platform_reset,
|
||||
.reset = ehci_platform_reset,
|
||||
.extra_priv_size = sizeof(struct ehci_platform_priv),
|
||||
};
|
||||
|
||||
static struct usb_ehci_pdata ehci_platform_defaults;
|
||||
static struct usb_ehci_pdata ehci_platform_defaults = {
|
||||
.power_on = ehci_platform_power_on,
|
||||
.power_suspend = ehci_platform_power_off,
|
||||
.power_off = ehci_platform_power_off,
|
||||
};
|
||||
|
||||
static int ehci_platform_probe(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res_mem;
|
||||
struct usb_ehci_pdata *pdata;
|
||||
int irq;
|
||||
int err;
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ehci_platform_priv *priv;
|
||||
struct ehci_hcd *ehci;
|
||||
int err, irq, clk = 0;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* use reasonable defaults so platforms don't have to provide these.
|
||||
* with DT probing on ARM, none of these are set.
|
||||
* Use reasonable defaults so platforms don't have to provide these
|
||||
* with DT probing on ARM.
|
||||
*/
|
||||
if (!dev_get_platdata(&dev->dev))
|
||||
dev->dev.platform_data = &ehci_platform_defaults;
|
||||
if (!pdata)
|
||||
pdata = &ehci_platform_defaults;
|
||||
|
||||
err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pdata = dev_get_platdata(&dev->dev);
|
||||
|
||||
irq = platform_get_irq(dev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&dev->dev, "no irq provided");
|
||||
|
@ -107,17 +168,72 @@ static int ehci_platform_probe(struct platform_device *dev)
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
|
||||
dev_name(&dev->dev));
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(dev, hcd);
|
||||
dev->dev.platform_data = pdata;
|
||||
priv = hcd_to_ehci_priv(hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
|
||||
if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
|
||||
ehci->big_endian_mmio = 1;
|
||||
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
|
||||
ehci->big_endian_desc = 1;
|
||||
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian"))
|
||||
ehci->big_endian_mmio = ehci->big_endian_desc = 1;
|
||||
|
||||
priv->phy = devm_phy_get(&dev->dev, "usb");
|
||||
if (IS_ERR(priv->phy)) {
|
||||
err = PTR_ERR(priv->phy);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_hcd;
|
||||
priv->phy = NULL;
|
||||
}
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
|
||||
priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
|
||||
if (IS_ERR(priv->clks[clk])) {
|
||||
err = PTR_ERR(priv->clks[clk]);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_clks;
|
||||
priv->clks[clk] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
ehci->big_endian_desc = 1;
|
||||
if (pdata->big_endian_mmio)
|
||||
ehci->big_endian_mmio = 1;
|
||||
|
||||
#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
|
||||
if (ehci->big_endian_mmio) {
|
||||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
}
|
||||
#endif
|
||||
#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
|
||||
if (ehci->big_endian_desc) {
|
||||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pdata->power_on) {
|
||||
err = pdata->power_on(dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
|
||||
dev_name(&dev->dev));
|
||||
if (!hcd) {
|
||||
err = -ENOMEM;
|
||||
goto err_power;
|
||||
goto err_put_clks;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res_mem->start;
|
||||
|
@ -126,22 +242,28 @@ static int ehci_platform_probe(struct platform_device *dev)
|
|||
hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
goto err_put_hcd;
|
||||
goto err_power;
|
||||
}
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err)
|
||||
goto err_put_hcd;
|
||||
goto err_power;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
platform_set_drvdata(dev, hcd);
|
||||
|
||||
return err;
|
||||
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
err_put_hcd:
|
||||
if (pdata == &ehci_platform_defaults)
|
||||
dev->dev.platform_data = NULL;
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -150,13 +272,19 @@ static int ehci_platform_remove(struct platform_device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
int clk;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (pdata == &ehci_platform_defaults)
|
||||
dev->dev.platform_data = NULL;
|
||||
|
||||
|
@ -207,8 +335,10 @@ static int ehci_platform_resume(struct device *dev)
|
|||
static const struct of_device_id vt8500_ehci_ids[] = {
|
||||
{ .compatible = "via,vt8500-ehci", },
|
||||
{ .compatible = "wm,prizm-ehci", },
|
||||
{ .compatible = "generic-ehci", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
|
||||
|
||||
static const struct platform_device_id ehci_platform_table[] = {
|
||||
{ "ehci-platform", 0 },
|
||||
|
|
|
@ -38,10 +38,6 @@
|
|||
|
||||
#include "ehci.h"
|
||||
|
||||
#define TEGRA_USB_BASE 0xC5000000
|
||||
#define TEGRA_USB2_BASE 0xC5004000
|
||||
#define TEGRA_USB3_BASE 0xC5008000
|
||||
|
||||
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
|
||||
|
||||
#define TEGRA_USB_DMA_ALIGN 32
|
||||
|
|
|
@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
|
|||
dev_err(dev, "cannot listen to notifications: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
/*
|
||||
* If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set,
|
||||
* disable transfer notifications.
|
||||
*/
|
||||
if (hwahc->wa.quirks &
|
||||
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) {
|
||||
struct usb_host_interface *cur_altsetting =
|
||||
hwahc->wa.usb_iface->cur_altsetting;
|
||||
|
||||
result = usb_control_msg(hwahc->wa.usb_dev,
|
||||
usb_sndctrlpipe(hwahc->wa.usb_dev, 0),
|
||||
WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR |
|
||||
USB_RECIP_INTERFACE,
|
||||
WA_REQ_ALEREON_FEATURE_SET,
|
||||
cur_altsetting->desc.bInterfaceNumber,
|
||||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
/*
|
||||
* If we successfully sent the control message, start DTI here
|
||||
* because no transfer notifications will be received which is
|
||||
* where DTI is normally started.
|
||||
*/
|
||||
if (result == 0)
|
||||
result = wa_dti_start(&hwahc->wa);
|
||||
else
|
||||
result = 0; /* OK. Continue normally. */
|
||||
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot start DTI: %d\n", result);
|
||||
goto error_dti_start;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
error_dti_start:
|
||||
wa_nep_disarm(&hwahc->wa);
|
||||
error_stop:
|
||||
__wa_clear_feature(&hwahc->wa, WA_ENABLE);
|
||||
return result;
|
||||
|
@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface)
|
|||
static struct usb_device_id hwahc_id_table[] = {
|
||||
/* Alereon 5310 */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
|
||||
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
|
||||
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
|
||||
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
|
||||
/* Alereon 5611 */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
|
||||
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
|
||||
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
|
||||
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
|
||||
/* FIXME: use class labels for this */
|
||||
{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
|
||||
{},
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright 2007 Michael Buesch <m@bues.ch>
|
||||
* Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
|
||||
* Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Derived from the OCHI-SSB driver
|
||||
* Derived from the OHCI-PCI driver
|
||||
|
@ -14,11 +15,14 @@
|
|||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/ohci_pdriver.h>
|
||||
#include <linux/usb.h>
|
||||
|
@ -27,6 +31,13 @@
|
|||
#include "ohci.h"
|
||||
|
||||
#define DRIVER_DESC "OHCI generic platform driver"
|
||||
#define OHCI_MAX_CLKS 3
|
||||
#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv)
|
||||
|
||||
struct ohci_platform_priv {
|
||||
struct clk *clks[OHCI_MAX_CLKS];
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static const char hcd_name[] = "ohci-platform";
|
||||
|
||||
|
@ -36,10 +47,6 @@ static int ohci_platform_reset(struct usb_hcd *hcd)
|
|||
struct usb_ohci_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
ohci->flags |= OHCI_QUIRK_BE_DESC;
|
||||
if (pdata->big_endian_mmio)
|
||||
ohci->flags |= OHCI_QUIRK_BE_MMIO;
|
||||
if (pdata->no_big_frame_no)
|
||||
ohci->flags |= OHCI_QUIRK_FRAME_NO;
|
||||
if (pdata->num_ports)
|
||||
|
@ -48,11 +55,67 @@ static int ohci_platform_reset(struct usb_hcd *hcd)
|
|||
return ohci_setup(hcd);
|
||||
}
|
||||
|
||||
static int ohci_platform_power_on(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
|
||||
int clk, ret;
|
||||
|
||||
for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) {
|
||||
ret = clk_prepare_enable(priv->clks[clk]);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
}
|
||||
|
||||
if (priv->phy) {
|
||||
ret = phy_init(priv->phy);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
|
||||
ret = phy_power_on(priv->phy);
|
||||
if (ret)
|
||||
goto err_exit_phy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit_phy:
|
||||
phy_exit(priv->phy);
|
||||
err_disable_clks:
|
||||
while (--clk >= 0)
|
||||
clk_disable_unprepare(priv->clks[clk]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ohci_platform_power_off(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
|
||||
int clk;
|
||||
|
||||
if (priv->phy) {
|
||||
phy_power_off(priv->phy);
|
||||
phy_exit(priv->phy);
|
||||
}
|
||||
|
||||
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
if (priv->clks[clk])
|
||||
clk_disable_unprepare(priv->clks[clk]);
|
||||
}
|
||||
|
||||
static struct hc_driver __read_mostly ohci_platform_hc_driver;
|
||||
|
||||
static const struct ohci_driver_overrides platform_overrides __initconst = {
|
||||
.product_desc = "Generic Platform OHCI controller",
|
||||
.reset = ohci_platform_reset,
|
||||
.product_desc = "Generic Platform OHCI controller",
|
||||
.reset = ohci_platform_reset,
|
||||
.extra_priv_size = sizeof(struct ohci_platform_priv),
|
||||
};
|
||||
|
||||
static struct usb_ohci_pdata ohci_platform_defaults = {
|
||||
.power_on = ohci_platform_power_on,
|
||||
.power_suspend = ohci_platform_power_off,
|
||||
.power_off = ohci_platform_power_off,
|
||||
};
|
||||
|
||||
static int ohci_platform_probe(struct platform_device *dev)
|
||||
|
@ -60,17 +123,24 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
struct usb_hcd *hcd;
|
||||
struct resource *res_mem;
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
int irq;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (!pdata) {
|
||||
WARN_ON(1);
|
||||
return -ENODEV;
|
||||
}
|
||||
struct ohci_platform_priv *priv;
|
||||
struct ohci_hcd *ohci;
|
||||
int err, irq, clk = 0;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Use reasonable defaults so platforms don't have to provide these
|
||||
* with DT probing on ARM.
|
||||
*/
|
||||
if (!pdata)
|
||||
pdata = &ohci_platform_defaults;
|
||||
|
||||
err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq = platform_get_irq(dev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&dev->dev, "no irq provided");
|
||||
|
@ -83,17 +153,72 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
|
||||
dev_name(&dev->dev));
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(dev, hcd);
|
||||
dev->dev.platform_data = pdata;
|
||||
priv = hcd_to_ohci_priv(hcd);
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
|
||||
if (pdata == &ohci_platform_defaults && dev->dev.of_node) {
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
|
||||
ohci->flags |= OHCI_QUIRK_BE_MMIO;
|
||||
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
|
||||
ohci->flags |= OHCI_QUIRK_BE_DESC;
|
||||
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian"))
|
||||
ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
|
||||
|
||||
priv->phy = devm_phy_get(&dev->dev, "usb");
|
||||
if (IS_ERR(priv->phy)) {
|
||||
err = PTR_ERR(priv->phy);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_hcd;
|
||||
priv->phy = NULL;
|
||||
}
|
||||
|
||||
for (clk = 0; clk < OHCI_MAX_CLKS; clk++) {
|
||||
priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
|
||||
if (IS_ERR(priv->clks[clk])) {
|
||||
err = PTR_ERR(priv->clks[clk]);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_clks;
|
||||
priv->clks[clk] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
ohci->flags |= OHCI_QUIRK_BE_DESC;
|
||||
if (pdata->big_endian_mmio)
|
||||
ohci->flags |= OHCI_QUIRK_BE_MMIO;
|
||||
|
||||
#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
|
||||
if (ohci->flags & OHCI_QUIRK_BE_MMIO) {
|
||||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
}
|
||||
#endif
|
||||
#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
|
||||
if (ohci->flags & OHCI_QUIRK_BE_DESC) {
|
||||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pdata->power_on) {
|
||||
err = pdata->power_on(dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
|
||||
dev_name(&dev->dev));
|
||||
if (!hcd) {
|
||||
err = -ENOMEM;
|
||||
goto err_power;
|
||||
goto err_put_clks;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res_mem->start;
|
||||
|
@ -102,11 +227,11 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
goto err_put_hcd;
|
||||
goto err_power;
|
||||
}
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err)
|
||||
goto err_put_hcd;
|
||||
goto err_power;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
|
||||
|
@ -114,11 +239,17 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
|
||||
return err;
|
||||
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
err_put_hcd:
|
||||
if (pdata == &ohci_platform_defaults)
|
||||
dev->dev.platform_data = NULL;
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -127,13 +258,22 @@ static int ohci_platform_remove(struct platform_device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
|
||||
int clk;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (pdata == &ohci_platform_defaults)
|
||||
dev->dev.platform_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -180,6 +320,12 @@ static int ohci_platform_resume(struct device *dev)
|
|||
#define ohci_platform_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct of_device_id ohci_platform_ids[] = {
|
||||
{ .compatible = "generic-ohci", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ohci_platform_ids);
|
||||
|
||||
static const struct platform_device_id ohci_platform_table[] = {
|
||||
{ "ohci-platform", 0 },
|
||||
{ }
|
||||
|
@ -200,6 +346,7 @@ static struct platform_driver ohci_platform_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
.name = "ohci-platform",
|
||||
.pm = &ohci_platform_pm_ops,
|
||||
.of_match_table = ohci_platform_ids,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op)
|
|||
}
|
||||
|
||||
static const struct of_device_id platform_uhci_ids[] = {
|
||||
{ .compatible = "generic-uhci", },
|
||||
{ .compatible = "platform-uhci", },
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -732,9 +732,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
/* Set the U1 and U2 exit latencies. */
|
||||
memcpy(buf, &usb_bos_descriptor,
|
||||
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
|
||||
temp = readl(&xhci->cap_regs->hcs_params3);
|
||||
buf[12] = HCS_U1_LATENCY(temp);
|
||||
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
|
||||
if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
|
||||
temp = readl(&xhci->cap_regs->hcs_params3);
|
||||
buf[12] = HCS_U1_LATENCY(temp);
|
||||
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
|
||||
}
|
||||
|
||||
/* Indicate whether the host has LTM support. */
|
||||
temp = readl(&xhci->cap_regs->hcc_params);
|
||||
|
|
|
@ -149,14 +149,140 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We need a radix tree for mapping physical addresses of TRBs to which stream
|
||||
* ID they belong to. We need to do this because the host controller won't tell
|
||||
* us which stream ring the TRB came from. We could store the stream ID in an
|
||||
* event data TRB, but that doesn't help us for the cancellation case, since the
|
||||
* endpoint may stop before it reaches that event data TRB.
|
||||
*
|
||||
* The radix tree maps the upper portion of the TRB DMA address to a ring
|
||||
* segment that has the same upper portion of DMA addresses. For example, say I
|
||||
* have segments of size 1KB, that are always 1KB aligned. A segment may
|
||||
* start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
|
||||
* key to the stream ID is 0x43244. I can use the DMA address of the TRB to
|
||||
* pass the radix tree a key to get the right stream ID:
|
||||
*
|
||||
* 0x10c90fff >> 10 = 0x43243
|
||||
* 0x10c912c0 >> 10 = 0x43244
|
||||
* 0x10c91400 >> 10 = 0x43245
|
||||
*
|
||||
* Obviously, only those TRBs with DMA addresses that are within the segment
|
||||
* will make the radix tree return the stream ID for that ring.
|
||||
*
|
||||
* Caveats for the radix tree:
|
||||
*
|
||||
* The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
|
||||
* unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
|
||||
* 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
|
||||
* key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
|
||||
* PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
|
||||
* extended systems (where the DMA address can be bigger than 32-bits),
|
||||
* if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
|
||||
*/
|
||||
static int xhci_insert_segment_mapping(struct radix_tree_root *trb_address_map,
|
||||
struct xhci_ring *ring,
|
||||
struct xhci_segment *seg,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
unsigned long key;
|
||||
int ret;
|
||||
|
||||
key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
|
||||
/* Skip any segments that were already added. */
|
||||
if (radix_tree_lookup(trb_address_map, key))
|
||||
return 0;
|
||||
|
||||
ret = radix_tree_maybe_preload(mem_flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = radix_tree_insert(trb_address_map,
|
||||
key, ring);
|
||||
radix_tree_preload_end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_remove_segment_mapping(struct radix_tree_root *trb_address_map,
|
||||
struct xhci_segment *seg)
|
||||
{
|
||||
unsigned long key;
|
||||
|
||||
key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
|
||||
if (radix_tree_lookup(trb_address_map, key))
|
||||
radix_tree_delete(trb_address_map, key);
|
||||
}
|
||||
|
||||
static int xhci_update_stream_segment_mapping(
|
||||
struct radix_tree_root *trb_address_map,
|
||||
struct xhci_ring *ring,
|
||||
struct xhci_segment *first_seg,
|
||||
struct xhci_segment *last_seg,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
struct xhci_segment *failed_seg;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(trb_address_map == NULL))
|
||||
return 0;
|
||||
|
||||
seg = first_seg;
|
||||
do {
|
||||
ret = xhci_insert_segment_mapping(trb_address_map,
|
||||
ring, seg, mem_flags);
|
||||
if (ret)
|
||||
goto remove_streams;
|
||||
if (seg == last_seg)
|
||||
return 0;
|
||||
seg = seg->next;
|
||||
} while (seg != first_seg);
|
||||
|
||||
return 0;
|
||||
|
||||
remove_streams:
|
||||
failed_seg = seg;
|
||||
seg = first_seg;
|
||||
do {
|
||||
xhci_remove_segment_mapping(trb_address_map, seg);
|
||||
if (seg == failed_seg)
|
||||
return ret;
|
||||
seg = seg->next;
|
||||
} while (seg != first_seg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_remove_stream_mapping(struct xhci_ring *ring)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
|
||||
if (WARN_ON_ONCE(ring->trb_address_map == NULL))
|
||||
return;
|
||||
|
||||
seg = ring->first_seg;
|
||||
do {
|
||||
xhci_remove_segment_mapping(ring->trb_address_map, seg);
|
||||
seg = seg->next;
|
||||
} while (seg != ring->first_seg);
|
||||
}
|
||||
|
||||
static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags)
|
||||
{
|
||||
return xhci_update_stream_segment_mapping(ring->trb_address_map, ring,
|
||||
ring->first_seg, ring->last_seg, mem_flags);
|
||||
}
|
||||
|
||||
/* XXX: Do we need the hcd structure in all these functions? */
|
||||
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
if (!ring)
|
||||
return;
|
||||
|
||||
if (ring->first_seg)
|
||||
if (ring->first_seg) {
|
||||
if (ring->type == TYPE_STREAM)
|
||||
xhci_remove_stream_mapping(ring);
|
||||
xhci_free_segments_for_ring(xhci, ring->first_seg);
|
||||
}
|
||||
|
||||
kfree(ring);
|
||||
}
|
||||
|
@ -349,6 +475,21 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
|||
if (ret)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ring->type == TYPE_STREAM)
|
||||
ret = xhci_update_stream_segment_mapping(ring->trb_address_map,
|
||||
ring, first, last, flags);
|
||||
if (ret) {
|
||||
struct xhci_segment *next;
|
||||
do {
|
||||
next = first->next;
|
||||
xhci_segment_free(xhci, first);
|
||||
if (first == last)
|
||||
break;
|
||||
first = next;
|
||||
} while (true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
xhci_link_rings(xhci, ring, first, last, num_segs);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
|
||||
"ring expansion succeed, now has %d segments",
|
||||
|
@ -434,12 +575,12 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
|
|||
struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
|
||||
|
||||
if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
dma_free_coherent(dev,
|
||||
sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
|
||||
if (size > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
dma_free_coherent(dev, size,
|
||||
stream_ctx, dma);
|
||||
else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
|
||||
else if (size <= SMALL_STREAM_ARRAY_SIZE)
|
||||
return dma_pool_free(xhci->small_streams_pool,
|
||||
stream_ctx, dma);
|
||||
else
|
||||
|
@ -462,12 +603,12 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
|
|||
gfp_t mem_flags)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
|
||||
|
||||
if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
return dma_alloc_coherent(dev,
|
||||
sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
|
||||
if (size > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
return dma_alloc_coherent(dev, size,
|
||||
dma, mem_flags);
|
||||
else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
|
||||
else if (size <= SMALL_STREAM_ARRAY_SIZE)
|
||||
return dma_pool_alloc(xhci->small_streams_pool,
|
||||
mem_flags, dma);
|
||||
else
|
||||
|
@ -510,36 +651,6 @@ struct xhci_ring *xhci_stream_id_to_ring(
|
|||
* The number of stream contexts in the stream context array may be bigger than
|
||||
* the number of streams the driver wants to use. This is because the number of
|
||||
* stream context array entries must be a power of two.
|
||||
*
|
||||
* We need a radix tree for mapping physical addresses of TRBs to which stream
|
||||
* ID they belong to. We need to do this because the host controller won't tell
|
||||
* us which stream ring the TRB came from. We could store the stream ID in an
|
||||
* event data TRB, but that doesn't help us for the cancellation case, since the
|
||||
* endpoint may stop before it reaches that event data TRB.
|
||||
*
|
||||
* The radix tree maps the upper portion of the TRB DMA address to a ring
|
||||
* segment that has the same upper portion of DMA addresses. For example, say I
|
||||
* have segments of size 1KB, that are always 64-byte aligned. A segment may
|
||||
* start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
|
||||
* key to the stream ID is 0x43244. I can use the DMA address of the TRB to
|
||||
* pass the radix tree a key to get the right stream ID:
|
||||
*
|
||||
* 0x10c90fff >> 10 = 0x43243
|
||||
* 0x10c912c0 >> 10 = 0x43244
|
||||
* 0x10c91400 >> 10 = 0x43245
|
||||
*
|
||||
* Obviously, only those TRBs with DMA addresses that are within the segment
|
||||
* will make the radix tree return the stream ID for that ring.
|
||||
*
|
||||
* Caveats for the radix tree:
|
||||
*
|
||||
* The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
|
||||
* unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
|
||||
* 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
|
||||
* key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
|
||||
* PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
|
||||
* extended systems (where the DMA address can be bigger than 32-bits),
|
||||
* if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
|
||||
*/
|
||||
struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs,
|
||||
|
@ -548,7 +659,6 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
|||
struct xhci_stream_info *stream_info;
|
||||
u32 cur_stream;
|
||||
struct xhci_ring *cur_ring;
|
||||
unsigned long key;
|
||||
u64 addr;
|
||||
int ret;
|
||||
|
||||
|
@ -603,6 +713,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
|||
if (!cur_ring)
|
||||
goto cleanup_rings;
|
||||
cur_ring->stream_id = cur_stream;
|
||||
cur_ring->trb_address_map = &stream_info->trb_address_map;
|
||||
/* Set deq ptr, cycle bit, and stream context type */
|
||||
addr = cur_ring->first_seg->dma |
|
||||
SCT_FOR_CTX(SCT_PRI_TR) |
|
||||
|
@ -612,10 +723,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
|||
xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
|
||||
cur_stream, (unsigned long long) addr);
|
||||
|
||||
key = (unsigned long)
|
||||
(cur_ring->first_seg->dma >> TRB_SEGMENT_SHIFT);
|
||||
ret = radix_tree_insert(&stream_info->trb_address_map,
|
||||
key, cur_ring);
|
||||
ret = xhci_update_stream_mapping(cur_ring, mem_flags);
|
||||
if (ret) {
|
||||
xhci_ring_free(xhci, cur_ring);
|
||||
stream_info->stream_rings[cur_stream] = NULL;
|
||||
|
@ -635,9 +743,6 @@ cleanup_rings:
|
|||
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
if (cur_ring) {
|
||||
addr = cur_ring->first_seg->dma;
|
||||
radix_tree_delete(&stream_info->trb_address_map,
|
||||
addr >> TRB_SEGMENT_SHIFT);
|
||||
xhci_ring_free(xhci, cur_ring);
|
||||
stream_info->stream_rings[cur_stream] = NULL;
|
||||
}
|
||||
|
@ -698,7 +803,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
|
|||
{
|
||||
int cur_stream;
|
||||
struct xhci_ring *cur_ring;
|
||||
dma_addr_t addr;
|
||||
|
||||
if (!stream_info)
|
||||
return;
|
||||
|
@ -707,9 +811,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
|
|||
cur_stream++) {
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
if (cur_ring) {
|
||||
addr = cur_ring->first_seg->dma;
|
||||
radix_tree_delete(&stream_info->trb_address_map,
|
||||
addr >> TRB_SEGMENT_SHIFT);
|
||||
xhci_ring_free(xhci, cur_ring);
|
||||
stream_info->stream_rings[cur_stream] = NULL;
|
||||
}
|
||||
|
@ -1711,7 +1812,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
|
||||
if (xhci->lpm_command)
|
||||
xhci_free_command(xhci, xhci->lpm_command);
|
||||
xhci->cmd_ring_reserved_trbs = 0;
|
||||
if (xhci->cmd_ring)
|
||||
xhci_ring_free(xhci, xhci->cmd_ring);
|
||||
xhci->cmd_ring = NULL;
|
||||
|
@ -1776,6 +1876,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
}
|
||||
|
||||
no_bw:
|
||||
xhci->cmd_ring_reserved_trbs = 0;
|
||||
xhci->num_usb2_ports = 0;
|
||||
xhci->num_usb3_ports = 0;
|
||||
xhci->num_active_eps = 0;
|
||||
|
@ -2274,11 +2375,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
/*
|
||||
* Initialize the ring segment pool. The ring must be a contiguous
|
||||
* structure comprised of TRBs. The TRBs must be 16 byte aligned,
|
||||
* however, the command ring segment needs 64-byte aligned segments,
|
||||
* so we pick the greater alignment need.
|
||||
* however, the command ring segment needs 64-byte aligned segments
|
||||
* and our use of dma addresses in the trb_address_map radix tree needs
|
||||
* TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
|
||||
*/
|
||||
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
|
||||
TRB_SEGMENT_SIZE, 64, xhci->page_size);
|
||||
TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
|
||||
|
||||
/* See Table 46 and Note on Figure 55 */
|
||||
xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
|
||||
|
|
|
@ -190,6 +190,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
struct usb_hcd *hcd;
|
||||
|
||||
driver = (struct hc_driver *)id->driver_data;
|
||||
|
||||
/* Prevent runtime suspending between USB-2 and USB-3 initialization */
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
|
||||
/* Register the USB 2.0 roothub.
|
||||
* FIXME: USB core must know to register the USB 2.0 roothub first.
|
||||
* This is sort of silly, because we could just set the HCD driver flags
|
||||
|
@ -199,7 +203,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
retval = usb_hcd_pci_probe(dev, id);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
goto put_runtime_pm;
|
||||
|
||||
/* USB 2.0 roothub is stored in the PCI device now. */
|
||||
hcd = dev_get_drvdata(&dev->dev);
|
||||
|
@ -222,11 +226,11 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
goto put_usb3_hcd;
|
||||
/* Roothub already marked as USB 3.0 speed */
|
||||
|
||||
/* We know the LPM timeout algorithms for this host, let the USB core
|
||||
* enable and disable LPM for devices under the USB 3.0 roothub.
|
||||
*/
|
||||
if (xhci->quirks & XHCI_LPM_SUPPORT)
|
||||
hcd_to_bus(xhci->shared_hcd)->root_hub->lpm_capable = 1;
|
||||
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
||||
xhci->shared_hcd->can_do_streams = 1;
|
||||
|
||||
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -234,6 +238,8 @@ put_usb3_hcd:
|
|||
usb_put_hcd(xhci->shared_hcd);
|
||||
dealloc_usb2_hcd:
|
||||
usb_hcd_pci_remove(dev);
|
||||
put_runtime_pm:
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -158,6 +158,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
*/
|
||||
*((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
|
||||
|
||||
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
||||
xhci->shared_hcd->can_do_streams = 1;
|
||||
|
||||
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto put_usb3_hcd;
|
||||
|
@ -226,6 +229,7 @@ static const struct dev_pm_ops xhci_plat_pm_ops = {
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id usb_xhci_of_match[] = {
|
||||
{ .compatible = "generic-xhci" },
|
||||
{ .compatible = "xhci-platform" },
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -546,9 +546,9 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
|||
struct xhci_dequeue_state *state)
|
||||
{
|
||||
struct xhci_virt_device *dev = xhci->devs[slot_id];
|
||||
struct xhci_virt_ep *ep = &dev->eps[ep_index];
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_generic_trb *trb;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
dma_addr_t addr;
|
||||
|
||||
ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
|
||||
|
@ -573,8 +573,16 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
|||
/* Dig out the cycle state saved by the xHC during the stop ep cmd */
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Finding endpoint context");
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
|
||||
state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq);
|
||||
/* 4.6.9 the css flag is written to the stream context for streams */
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
struct xhci_stream_ctx *ctx =
|
||||
&ep->stream_info->stream_ctx_array[stream_id];
|
||||
state->new_cycle_state = 0x1 & le64_to_cpu(ctx->stream_ring);
|
||||
} else {
|
||||
struct xhci_ep_ctx *ep_ctx
|
||||
= xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
|
||||
state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq);
|
||||
}
|
||||
|
||||
state->new_deq_ptr = cur_td->last_trb;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
|
@ -892,6 +900,57 @@ remove_finished_td:
|
|||
/* Return to the event handler with xhci->lock re-acquired */
|
||||
}
|
||||
|
||||
static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
struct xhci_td *cur_td;
|
||||
|
||||
while (!list_empty(&ring->td_list)) {
|
||||
cur_td = list_first_entry(&ring->td_list,
|
||||
struct xhci_td, td_list);
|
||||
list_del_init(&cur_td->td_list);
|
||||
if (!list_empty(&cur_td->cancelled_td_list))
|
||||
list_del_init(&cur_td->cancelled_td_list);
|
||||
xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
|
||||
int slot_id, int ep_index)
|
||||
{
|
||||
struct xhci_td *cur_td;
|
||||
struct xhci_virt_ep *ep;
|
||||
struct xhci_ring *ring;
|
||||
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
if ((ep->ep_state & EP_HAS_STREAMS) ||
|
||||
(ep->ep_state & EP_GETTING_NO_STREAMS)) {
|
||||
int stream_id;
|
||||
|
||||
for (stream_id = 0; stream_id < ep->stream_info->num_streams;
|
||||
stream_id++) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Killing URBs for slot ID %u, ep index %u, stream %u",
|
||||
slot_id, ep_index, stream_id + 1);
|
||||
xhci_kill_ring_urbs(xhci,
|
||||
ep->stream_info->stream_rings[stream_id]);
|
||||
}
|
||||
} else {
|
||||
ring = ep->ring;
|
||||
if (!ring)
|
||||
return;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Killing URBs for slot ID %u, ep index %u",
|
||||
slot_id, ep_index);
|
||||
xhci_kill_ring_urbs(xhci, ring);
|
||||
}
|
||||
while (!list_empty(&ep->cancelled_td_list)) {
|
||||
cur_td = list_first_entry(&ep->cancelled_td_list,
|
||||
struct xhci_td, cancelled_td_list);
|
||||
list_del_init(&cur_td->cancelled_td_list);
|
||||
xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
/* Watchdog timer function for when a stop endpoint command fails to complete.
|
||||
* In this case, we assume the host controller is broken or dying or dead. The
|
||||
* host may still be completing some other events, so we have to be careful to
|
||||
|
@ -915,9 +974,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
|
|||
{
|
||||
struct xhci_hcd *xhci;
|
||||
struct xhci_virt_ep *ep;
|
||||
struct xhci_virt_ep *temp_ep;
|
||||
struct xhci_ring *ring;
|
||||
struct xhci_td *cur_td;
|
||||
int ret, i, j;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -974,34 +1030,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
|
|||
for (i = 0; i < MAX_HC_SLOTS; i++) {
|
||||
if (!xhci->devs[i])
|
||||
continue;
|
||||
for (j = 0; j < 31; j++) {
|
||||
temp_ep = &xhci->devs[i]->eps[j];
|
||||
ring = temp_ep->ring;
|
||||
if (!ring)
|
||||
continue;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Killing URBs for slot ID %u, "
|
||||
"ep index %u", i, j);
|
||||
while (!list_empty(&ring->td_list)) {
|
||||
cur_td = list_first_entry(&ring->td_list,
|
||||
struct xhci_td,
|
||||
td_list);
|
||||
list_del_init(&cur_td->td_list);
|
||||
if (!list_empty(&cur_td->cancelled_td_list))
|
||||
list_del_init(&cur_td->cancelled_td_list);
|
||||
xhci_giveback_urb_in_irq(xhci, cur_td,
|
||||
-ESHUTDOWN);
|
||||
}
|
||||
while (!list_empty(&temp_ep->cancelled_td_list)) {
|
||||
cur_td = list_first_entry(
|
||||
&temp_ep->cancelled_td_list,
|
||||
struct xhci_td,
|
||||
cancelled_td_list);
|
||||
list_del_init(&cur_td->cancelled_td_list);
|
||||
xhci_giveback_urb_in_irq(xhci, cur_td,
|
||||
-ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 31; j++)
|
||||
xhci_kill_endpoint_urbs(xhci, i, j);
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
|
@ -1073,17 +1103,18 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
unsigned int stream_id;
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_virt_device *dev;
|
||||
struct xhci_virt_ep *ep;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct xhci_slot_ctx *slot_ctx;
|
||||
|
||||
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
|
||||
stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
|
||||
dev = xhci->devs[slot_id];
|
||||
ep = &dev->eps[ep_index];
|
||||
|
||||
ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
|
||||
if (!ep_ring) {
|
||||
xhci_warn(xhci, "WARN Set TR deq ptr command for "
|
||||
"freed stream ID %u\n",
|
||||
xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n",
|
||||
stream_id);
|
||||
/* XXX: Harmless??? */
|
||||
dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
|
||||
|
@ -1099,12 +1130,10 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
|
||||
switch (cmd_comp_code) {
|
||||
case COMP_TRB_ERR:
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
|
||||
"of stream ID configuration\n");
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because of stream ID configuration\n");
|
||||
break;
|
||||
case COMP_CTX_STATE:
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due "
|
||||
"to incorrect slot or ep state.\n");
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.\n");
|
||||
ep_state = le32_to_cpu(ep_ctx->ep_info);
|
||||
ep_state &= EP_STATE_MASK;
|
||||
slot_state = le32_to_cpu(slot_ctx->dev_state);
|
||||
|
@ -1114,13 +1143,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
slot_state, ep_state);
|
||||
break;
|
||||
case COMP_EBADSLT:
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because "
|
||||
"slot %u was not enabled.\n", slot_id);
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because slot %u was not enabled.\n",
|
||||
slot_id);
|
||||
break;
|
||||
default:
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
|
||||
"completion code of %u.\n",
|
||||
cmd_comp_code);
|
||||
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown completion code of %u.\n",
|
||||
cmd_comp_code);
|
||||
break;
|
||||
}
|
||||
/* OK what do we do now? The endpoint state is hosed, and we
|
||||
|
@ -1130,23 +1158,28 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
* cancelling URBs, which might not be an error...
|
||||
*/
|
||||
} else {
|
||||
u64 deq;
|
||||
/* 4.6.10 deq ptr is written to the stream ctx for streams */
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
struct xhci_stream_ctx *ctx =
|
||||
&ep->stream_info->stream_ctx_array[stream_id];
|
||||
deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
|
||||
} else {
|
||||
deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
|
||||
}
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Successful Set TR Deq Ptr cmd, deq = @%08llx",
|
||||
le64_to_cpu(ep_ctx->deq));
|
||||
if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg,
|
||||
dev->eps[ep_index].queued_deq_ptr) ==
|
||||
(le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) {
|
||||
"Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
|
||||
if (xhci_trb_virt_to_dma(ep->queued_deq_seg,
|
||||
ep->queued_deq_ptr) == deq) {
|
||||
/* Update the ring's dequeue segment and dequeue pointer
|
||||
* to reflect the new position.
|
||||
*/
|
||||
update_ring_for_set_deq_completion(xhci, dev,
|
||||
ep_ring, ep_index);
|
||||
} else {
|
||||
xhci_warn(xhci, "Mismatch between completed Set TR Deq "
|
||||
"Ptr command & xHCI internal state.\n");
|
||||
xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
|
||||
xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
|
||||
dev->eps[ep_index].queued_deq_seg,
|
||||
dev->eps[ep_index].queued_deq_ptr);
|
||||
ep->queued_deq_seg, ep->queued_deq_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4070,6 +4103,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
|
||||
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
|
||||
u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id);
|
||||
u32 trb_sct = 0;
|
||||
u32 type = TRB_TYPE(TRB_SET_DEQ);
|
||||
struct xhci_virt_ep *ep;
|
||||
|
||||
|
@ -4088,7 +4122,9 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
}
|
||||
ep->queued_deq_seg = deq_seg;
|
||||
ep->queued_deq_ptr = deq_ptr;
|
||||
return queue_command(xhci, lower_32_bits(addr) | cycle_state,
|
||||
if (stream_id)
|
||||
trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
|
||||
return queue_command(xhci, lower_32_bits(addr) | trb_sct | cycle_state,
|
||||
upper_32_bits(addr), trb_stream_id,
|
||||
trb_slot_id | trb_ep_index | type, false);
|
||||
}
|
||||
|
|
|
@ -390,6 +390,10 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
|
|||
}
|
||||
|
||||
legacy_irq:
|
||||
if (!strlen(hcd->irq_descr))
|
||||
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
|
||||
hcd->driver->description, hcd->self.busnum);
|
||||
|
||||
/* fall back to legacy interrupt*/
|
||||
ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
|
||||
hcd->irq_descr, hcd);
|
||||
|
@ -2678,6 +2682,20 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *vdev, int i)
|
||||
{
|
||||
struct xhci_virt_ep *ep = &vdev->eps[i];
|
||||
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n",
|
||||
xhci_get_endpoint_address(i));
|
||||
xhci_free_stream_info(xhci, ep->stream_info);
|
||||
ep->stream_info = NULL;
|
||||
ep->ep_state &= ~EP_HAS_STREAMS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called after one or more calls to xhci_add_endpoint() or
|
||||
* xhci_drop_endpoint(). If this call fails, the USB core is expected
|
||||
* to call xhci_reset_bandwidth().
|
||||
|
@ -2742,8 +2760,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
/* Free any rings that were dropped, but not changed. */
|
||||
for (i = 1; i < 31; ++i) {
|
||||
if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
|
||||
!(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))))
|
||||
!(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) {
|
||||
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
|
||||
xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
|
||||
}
|
||||
}
|
||||
xhci_zero_in_ctx(xhci, virt_dev);
|
||||
/*
|
||||
|
@ -2759,6 +2779,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
if (virt_dev->eps[i].ring) {
|
||||
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
|
||||
}
|
||||
xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
|
||||
virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
|
||||
virt_dev->eps[i].new_ring = NULL;
|
||||
}
|
||||
|
@ -2954,7 +2975,7 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
|
|||
ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__);
|
||||
if (ret <= 0)
|
||||
return -EINVAL;
|
||||
if (ep->ss_ep_comp.bmAttributes == 0) {
|
||||
if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) {
|
||||
xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion"
|
||||
" descriptor for ep 0x%x does not support streams\n",
|
||||
ep->desc.bEndpointAddress);
|
||||
|
@ -3121,6 +3142,12 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n",
|
||||
num_streams);
|
||||
|
||||
/* MaxPSASize value 0 (2 streams) means streams are not supported */
|
||||
if (HCC_MAX_PSA(xhci->hcc_params) < 4) {
|
||||
xhci_dbg(xhci, "xHCI controller does not support streams.\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
|
||||
if (!config_cmd) {
|
||||
xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
|
||||
|
@ -3519,6 +3546,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
struct xhci_virt_ep *ep = &virt_dev->eps[i];
|
||||
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on device reset, freeing streams.\n",
|
||||
xhci_get_endpoint_address(i));
|
||||
xhci_free_stream_info(xhci, ep->stream_info);
|
||||
ep->stream_info = NULL;
|
||||
ep->ep_state &= ~EP_HAS_STREAMS;
|
||||
|
|
|
@ -703,6 +703,7 @@ struct xhci_ep_ctx {
|
|||
|
||||
/* deq bitmasks */
|
||||
#define EP_CTX_CYCLE_MASK (1 << 0)
|
||||
#define SCTX_DEQ_MASK (~0xfL)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1118,9 +1119,10 @@ enum xhci_setup_dev {
|
|||
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
|
||||
#define LAST_EP_INDEX 30
|
||||
|
||||
/* Set TR Dequeue Pointer command TRB fields */
|
||||
/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */
|
||||
#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16))
|
||||
#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
|
||||
#define SCT_FOR_TRB(p) (((p) << 1) & 0x7)
|
||||
|
||||
|
||||
/* Port Status Change Event TRB fields */
|
||||
|
@ -1341,6 +1343,7 @@ struct xhci_ring {
|
|||
unsigned int num_trbs_free_temp;
|
||||
enum xhci_ring_type type;
|
||||
bool last_td_was_short;
|
||||
struct radix_tree_root *trb_address_map;
|
||||
};
|
||||
|
||||
struct xhci_erst_entry {
|
||||
|
|
|
@ -128,7 +128,6 @@ config USB_IDMOUSE
|
|||
|
||||
config USB_FTDI_ELAN
|
||||
tristate "Elan PCMCIA CardBus Adapter USB Client"
|
||||
default M
|
||||
help
|
||||
ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
|
||||
Currently only the U132 adapter is available.
|
||||
|
|
|
@ -2123,8 +2123,8 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
|
|||
u8 tmp8, tmp82, ramtype;
|
||||
int bw = 0;
|
||||
char *ramtypetext1 = NULL;
|
||||
const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM",
|
||||
"DDR SDRAM", "DDR SGRAM" };
|
||||
static const char ram_datarate[4] = {'S', 'S', 'D', 'D'};
|
||||
static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'};
|
||||
static const int busSDR[4] = {64, 64, 128, 128};
|
||||
static const int busDDR[4] = {32, 32, 64, 64};
|
||||
static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
|
||||
|
@ -2156,8 +2156,10 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
|
|||
break;
|
||||
}
|
||||
|
||||
dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1,
|
||||
ramtypetext2[ramtype], bw);
|
||||
|
||||
dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %cDR S%cRAM, bus width %d\n",
|
||||
sisusb->vramsize >> 20, ramtypetext1,
|
||||
ram_datarate[ramtype], ram_dynamictype[ramtype], bw);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue