1
0
Fork 0

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
  ...
wifi-calibration
Linus Torvalds 2014-04-01 17:06:09 -07:00
commit 3e75c6de1a
156 changed files with 9166 additions and 2201 deletions

View File

@ -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";
};

View File

@ -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.

View File

@ -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";
};

View File

@ -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";
};

View File

@ -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";
};

View File

@ -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>;
};

View File

@ -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.

View File

@ -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>;
};

View File

@ -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";
};

View File

@ -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";
};

View File

@ -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";
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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.

View File

@ -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*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -265,7 +265,6 @@ config 440EP
select PPC_FPU
select IBM440EP_ERR42
select IBM_EMAC_ZMII
select USB_ARCH_HAS_OHCI
config 440EPX
bool

View File

@ -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

View File

@ -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

View File

@ -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) },
{ }
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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[] = {

View File

@ -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

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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>");

View File

@ -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,
};

View File

@ -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");

View File

@ -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,

View File

@ -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");

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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),)

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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");

View File

@ -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;
}

View File

@ -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)

View File

@ -10,7 +10,6 @@
#define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXENDPOINTS 30 /* Hard limit */
#define USB_MAXCONFIG 8 /* Arbitrary limit */

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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",

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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 = {

View File

@ -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 */

View File

@ -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);

View File

@ -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, &params);
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, &params);
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(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
@ -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;

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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();
}

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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[] = {

View File

@ -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))

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 },

View File

@ -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

View File

@ -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), },
{},

View File

@ -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,
}
};

View File

@ -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", },
{}
};

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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" },
{ },
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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 {

View File

@ -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.

View File

@ -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