mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
USB patches for 3.17-rc1
Here is the big USB driver update for 3.17-rc1. Loads of gadget driver changes in here, including some big file movements to make things easier to manage over time. There's also the usual xhci and uas driver updates, and a handful of other changes in here. The changelog has the full details. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlPf2ZIACgkQMUfUDdst+ylvQwCfQKPKcwhtq4vJ2imUzJROEZwN ygYAnRpFpH/19X59uGSdE7DAdhbitqKg =uPh1 -----END PGP SIGNATURE----- Merge tag 'usb-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here is the big USB driver update for 3.17-rc1. Loads of gadget driver changes in here, including some big file movements to make things easier to manage over time. There's also the usual xhci and uas driver updates, and a handful of other changes in here. The changelog has the full details. All of these have been in linux-next for a while" * tag 'usb-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (211 commits) USB: devio: fix issue with log flooding uas: Log a warning when we cannot use uas because the hcd lacks streams uas: Only complain about missing sg if all other checks succeed xhci: Add missing checks for xhci_alloc_command failure xhci: Rename Asrock P67 pci product-id to EJ168 xhci: Blacklist using streams on the Etron EJ168 controller uas: Limit qdepth to 32 when connected over usb-2 uwb/whci: use correct structure type name in sizeof usb-core bInterval quirk USB: serial: ftdi_sio: Add support for new Xsens devices USB: serial: ftdi_sio: Annotate the current Xsens PID assignments usb: chipidea: debug: fix sparse non static symbol warnings usb: ci_hdrc_imx doc: fsl,usbphy is required usb: ci_hdrc_imx: Return -EINVAL for missing USB PHY usb: core: allow zero packet flag for interrupt urbs usb: lvstest: Fix sparse warnings generated by kbuild test bot USB: core: hcd-pci: free IRQ before disabling PCI device when shutting down phy: miphy365x: Represent each PHY channel as a DT subnode phy: miphy365x: Provide support for the MiPHY356x Generic PHY phy: miphy365x: Add Device Tree bindings for the MiPHY365x ...
This commit is contained in:
commit
ae9b475ebe
@ -3,13 +3,13 @@ Date: May 2007
|
||||
KernelVersion: 2.6.23
|
||||
Contact: Alan Stern <stern@rowland.harvard.edu>
|
||||
Description:
|
||||
If CONFIG_USB_PERSIST is set, then each USB device directory
|
||||
will contain a file named power/persist. The file holds a
|
||||
boolean value (0 or 1) indicating whether or not the
|
||||
"USB-Persist" facility is enabled for the device. Since the
|
||||
facility is inherently dangerous, it is disabled by default
|
||||
for all devices except hubs. For more information, see
|
||||
Documentation/usb/persist.txt.
|
||||
USB device directories can contain a file named power/persist.
|
||||
The file holds a boolean value (0 or 1) indicating whether or
|
||||
not the "USB-Persist" facility is enabled for the device. For
|
||||
hubs this facility is always enabled and their device
|
||||
directories will not contain this file.
|
||||
|
||||
For more information, see Documentation/usb/persist.txt.
|
||||
|
||||
What: /sys/bus/usb/devices/.../power/autosuspend
|
||||
Date: March 2007
|
||||
|
47
Documentation/ABI/testing/sysfs-bus-usb-lvstest
Normal file
47
Documentation/ABI/testing/sysfs-bus-usb-lvstest
Normal file
@ -0,0 +1,47 @@
|
||||
Link Layer Validation Device is a standard device for testing of Super
|
||||
Speed Link Layer tests. These nodes are available in sysfs only when lvs
|
||||
driver is bound with root hub device.
|
||||
|
||||
What: /sys/bus/usb/devices/.../get_dev_desc
|
||||
Date: March 2014
|
||||
Contact: Pratyush Anand <pratyush.anand@st.com>
|
||||
Description:
|
||||
Write to this node to issue "Get Device Descriptor"
|
||||
for Link Layer Validation device. It is needed for TD.7.06.
|
||||
|
||||
What: /sys/bus/usb/devices/.../u1_timeout
|
||||
Date: March 2014
|
||||
Contact: Pratyush Anand <pratyush.anand@st.com>
|
||||
Description:
|
||||
Set "U1 timeout" for the downstream port where Link Layer
|
||||
Validation device is connected. Timeout value must be between 0
|
||||
and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21.
|
||||
|
||||
What: /sys/bus/usb/devices/.../u2_timeout
|
||||
Date: March 2014
|
||||
Contact: Pratyush Anand <pratyush.anand@st.com>
|
||||
Description:
|
||||
Set "U2 timeout" for the downstream port where Link Layer
|
||||
Validation device is connected. Timeout value must be between 0
|
||||
and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21.
|
||||
|
||||
What: /sys/bus/usb/devices/.../hot_reset
|
||||
Date: March 2014
|
||||
Contact: Pratyush Anand <pratyush.anand@st.com>
|
||||
Description:
|
||||
Write to this node to issue "Reset" for Link Layer Validation
|
||||
device. It is needed for TD.7.29, TD.7.31, TD.7.34 and TD.7.35.
|
||||
|
||||
What: /sys/bus/usb/devices/.../u3_entry
|
||||
Date: March 2014
|
||||
Contact: Pratyush Anand <pratyush.anand@st.com>
|
||||
Description:
|
||||
Write to this node to issue "U3 entry" for Link Layer
|
||||
Validation device. It is needed for TD.7.35 and TD.7.36.
|
||||
|
||||
What: /sys/bus/usb/devices/.../u3_exit
|
||||
Date: March 2014
|
||||
Contact: Pratyush Anand <pratyush.anand@st.com>
|
||||
Description:
|
||||
Write to this node to issue "U3 exit" for Link Layer
|
||||
Validation device. It is needed for TD.7.36.
|
@ -556,11 +556,11 @@ been converted to this framework.
|
||||
Near-term plans include converting all of them, except for "gadgetfs".
|
||||
</para>
|
||||
|
||||
!Edrivers/usb/gadget/f_acm.c
|
||||
!Edrivers/usb/gadget/f_ecm.c
|
||||
!Edrivers/usb/gadget/f_subset.c
|
||||
!Edrivers/usb/gadget/f_obex.c
|
||||
!Edrivers/usb/gadget/f_serial.c
|
||||
!Edrivers/usb/gadget/function/f_acm.c
|
||||
!Edrivers/usb/gadget/function/f_ecm.c
|
||||
!Edrivers/usb/gadget/function/f_subset.c
|
||||
!Edrivers/usb/gadget/function/f_obex.c
|
||||
!Edrivers/usb/gadget/function/f_serial.c
|
||||
|
||||
</sect1>
|
||||
|
||||
|
34
Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
Normal file
34
Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Berlin SATA PHY
|
||||
---------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "marvell,berlin2q-sata-phy"
|
||||
- address-cells: should be 1
|
||||
- size-cells: should be 0
|
||||
- phy-cells: from the generic PHY bindings, must be 1
|
||||
- reg: address and length of the register
|
||||
- clocks: reference to the clock entry
|
||||
|
||||
Sub-nodes:
|
||||
Each PHY should be represented as a sub-node.
|
||||
|
||||
Sub-nodes required properties:
|
||||
- reg: the PHY number
|
||||
|
||||
Example:
|
||||
sata_phy: phy@f7e900a0 {
|
||||
compatible = "marvell,berlin2q-sata-phy";
|
||||
reg = <0xf7e900a0 0x200>;
|
||||
clocks = <&chip CLKID_SATA>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#phy-cells = <1>;
|
||||
|
||||
sata-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
sata-phy@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
22
Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
Normal file
22
Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
Normal file
@ -0,0 +1,22 @@
|
||||
Hisilicon hix5hd2 SATA PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "hisilicon,hix5hd2-sata-phy"
|
||||
- reg: offset and length of the PHY registers
|
||||
- #phy-cells: must be 0
|
||||
Refer to phy/phy-bindings.txt for the generic PHY binding properties
|
||||
|
||||
Optional Properties:
|
||||
- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
|
||||
- hisilicon,power-reg: offset and bit number within peripheral-syscon,
|
||||
register of controlling sata power supply.
|
||||
|
||||
Example:
|
||||
sata_phy: phy@f9900000 {
|
||||
compatible = "hisilicon,hix5hd2-sata-phy";
|
||||
reg = <0xf9900000 0x10000>;
|
||||
#phy-cells = <0>;
|
||||
hisilicon,peripheral-syscon = <&peripheral_ctrl>;
|
||||
hisilicon,power-reg = <0x8 10>;
|
||||
};
|
@ -10,6 +10,10 @@ Required Properties:
|
||||
provider can use the values in cells to find the appropriate
|
||||
PHY.
|
||||
|
||||
Optional Properties:
|
||||
phy-supply: Phandle to a regulator that provides power to the PHY. This
|
||||
regulator will be managed during the PHY power on/off sequence.
|
||||
|
||||
For example:
|
||||
|
||||
phys: phy {
|
||||
|
76
Documentation/devicetree/bindings/phy/phy-miphy365x.txt
Normal file
76
Documentation/devicetree/bindings/phy/phy-miphy365x.txt
Normal file
@ -0,0 +1,76 @@
|
||||
STMicroelectronics STi MIPHY365x PHY binding
|
||||
============================================
|
||||
|
||||
This binding describes a miphy device that is used to control PHY hardware
|
||||
for SATA and PCIe.
|
||||
|
||||
Required properties (controller (parent) node):
|
||||
- compatible : Should be "st,miphy365x-phy"
|
||||
- st,syscfg : Should be a phandle of the system configuration register group
|
||||
which contain the SATA, PCIe mode setting bits
|
||||
|
||||
Required nodes : A sub-node is required for each channel the controller
|
||||
provides. Address range information including the usual
|
||||
'reg' and 'reg-names' properties are used inside these
|
||||
nodes to describe the controller's topology. These nodes
|
||||
are translated by the driver's .xlate() function.
|
||||
|
||||
Required properties (port (child) node):
|
||||
- #phy-cells : Should be 1 (See second example)
|
||||
Cell after port phandle is device type from:
|
||||
- MIPHY_TYPE_SATA
|
||||
- MIPHY_TYPE_PCI
|
||||
- reg : Address and length of register sets for each device in
|
||||
"reg-names"
|
||||
- reg-names : The names of the register addresses corresponding to the
|
||||
registers filled in "reg":
|
||||
- sata: For SATA devices
|
||||
- pcie: For PCIe devices
|
||||
- syscfg: To specify the syscfg based config register
|
||||
|
||||
Optional properties (port (child) node):
|
||||
- st,sata-gen : Generation of locally attached SATA IP. Expected values
|
||||
are {1,2,3). If not supplied generation 1 hardware will
|
||||
be expected
|
||||
- st,pcie-tx-pol-inv : Bool property to invert the polarity PCIe Tx (Txn/Txp)
|
||||
- st,sata-tx-pol-inv : Bool property to invert the polarity SATA Tx (Txn/Txp)
|
||||
|
||||
Example:
|
||||
|
||||
miphy365x_phy: miphy365x@fe382000 {
|
||||
compatible = "st,miphy365x-phy";
|
||||
st,syscfg = <&syscfg_rear>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
phy_port0: port@fe382000 {
|
||||
reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>;
|
||||
reg-names = "sata", "pcie", "syscfg";
|
||||
#phy-cells = <1>;
|
||||
st,sata-gen = <3>;
|
||||
};
|
||||
|
||||
phy_port1: port@fe38a000 {
|
||||
reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;;
|
||||
reg-names = "sata", "pcie", "syscfg";
|
||||
#phy-cells = <1>;
|
||||
st,pcie-tx-pol-inv;
|
||||
};
|
||||
};
|
||||
|
||||
Specifying phy control of devices
|
||||
=================================
|
||||
|
||||
Device nodes should specify the configuration required in their "phys"
|
||||
property, containing a phandle to the phy port node and a device type.
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
|
||||
sata0: sata@fe380000 {
|
||||
...
|
||||
phys = <&phy_port0 MIPHY_TYPE_SATA>;
|
||||
...
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
Qualcomm APQ8064 SATA PHY Controller
|
||||
------------------------------------
|
||||
|
||||
SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
|
||||
Each SATA PHY controller should have its own node.
|
||||
|
||||
Required properties:
|
||||
- compatible: compatible list, contains "qcom,apq8064-sata-phy".
|
||||
- reg: offset and length of the SATA PHY register set;
|
||||
- #phy-cells: must be zero
|
||||
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
- clock-names: must be "cfg" for phy config clock.
|
||||
|
||||
Example:
|
||||
sata_phy: sata-phy@1b400000 {
|
||||
compatible = "qcom,apq8064-sata-phy";
|
||||
reg = <0x1b400000 0x200>;
|
||||
|
||||
clocks = <&gcc SATA_PHY_CFG_CLK>;
|
||||
clock-names = "cfg";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
Qualcomm IPQ806x SATA PHY Controller
|
||||
------------------------------------
|
||||
|
||||
SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
|
||||
Each SATA PHY controller should have its own node.
|
||||
|
||||
Required properties:
|
||||
- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
|
||||
- reg: offset and length of the SATA PHY register set;
|
||||
- #phy-cells: must be zero
|
||||
- clocks: must be exactly one entry
|
||||
- clock-names: must be "cfg"
|
||||
|
||||
Example:
|
||||
sata_phy: sata-phy@1b400000 {
|
||||
compatible = "qcom,ipq806x-sata-phy";
|
||||
reg = <0x1b400000 0x200>;
|
||||
|
||||
clocks = <&gcc SATA_PHY_CFG_CLK>;
|
||||
clock-names = "cfg";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of the listed compatibles:
|
||||
- "samsung,exynos3250-usb2-phy"
|
||||
- "samsung,exynos4210-usb2-phy"
|
||||
- "samsung,exynos4x12-usb2-phy"
|
||||
- "samsung,exynos5250-usb2-phy"
|
||||
@ -46,6 +47,7 @@ and Exynos 4212) it is as follows:
|
||||
1 - USB host ("host"),
|
||||
2 - HSIC0 ("hsic0"),
|
||||
3 - HSIC1 ("hsic1"),
|
||||
Exynos3250 has only USB device phy available as phy 0.
|
||||
|
||||
Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
|
||||
register is supplied.
|
||||
|
@ -9,15 +9,17 @@ Required properties:
|
||||
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-pcie" - for pcie to support external clock for pcie and to
|
||||
set PCS delay value.
|
||||
e.g. PCIE PHY in DRA7x
|
||||
"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.
|
||||
- reg : register ranges as listed in the reg-names property
|
||||
- reg-names: "otghs_control" for control-phy-otghs
|
||||
"power", "pcie_pcs" and "control_sma" for control-phy-pcie
|
||||
"power" for all other types
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,control-phy-otghs";
|
||||
@ -56,8 +58,8 @@ usb2phy@4a0ad080 {
|
||||
TI PIPE3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
|
||||
"ti,omap-usb3" is deprecated.
|
||||
- compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or
|
||||
"ti,phy-pipe3-pcie. "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".
|
||||
@ -69,10 +71,17 @@ Required properties:
|
||||
* "wkupclk" - wakeup clock.
|
||||
* "sysclk" - system clock.
|
||||
* "refclk" - reference clock.
|
||||
* "dpll_ref" - external dpll ref clk
|
||||
* "dpll_ref_m2" - external dpll ref clk
|
||||
* "phy-div" - divider for apll
|
||||
* "div-clk" - apll clock
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
- id: If there are multiple instance of the same type, in order to
|
||||
differentiate between each instance "id" can be used (e.g., multi-lane PCIe
|
||||
PHY). If "id" is not provided, it is set to default value of '1'.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
|
@ -4,6 +4,7 @@ Required properties:
|
||||
- compatible: Should be "fsl,imx27-usb"
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain controller interrupt
|
||||
- fsl,usbphy: phandle of usb phy that connects to the port
|
||||
|
||||
Recommended properies:
|
||||
- phy_type: the type of the phy connected to the core. Should be one
|
||||
@ -12,7 +13,6 @@ Recommended properies:
|
||||
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
|
||||
|
||||
Optional properties:
|
||||
- fsl,usbphy: phandler of usb phy that connects to the only one port
|
||||
- fsl,usbmisc: phandler of non-core register device, with one argument
|
||||
that indicate usb controller index
|
||||
- vbus-supply: regulator for vbus
|
||||
|
@ -20,6 +20,12 @@ Required properties :
|
||||
Present if phy_type == utmi.
|
||||
- ulpi-link: The clock Tegra provides to the ULPI PHY (cdev2).
|
||||
Present if phy_type == ulpi, and ULPI link mode is in use.
|
||||
- resets : Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names : Must include the following entries:
|
||||
- usb: The PHY's own reset signal.
|
||||
- utmi-pads: The reset of the PHY containing the chip-wide UTMI pad control
|
||||
registers. Required even if phy_type == ulpi.
|
||||
|
||||
Required properties for phy_type == ulpi:
|
||||
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
|
||||
@ -56,6 +62,8 @@ Optional properties:
|
||||
host means this is a host controller
|
||||
peripheral means it is device controller
|
||||
otg means it can operate as either ("on the go")
|
||||
- nvidia,has-utmi-pad-registers : boolean indicates whether this controller
|
||||
contains the UTMI pad control registers common to all USB controllers.
|
||||
|
||||
VBUS control (required for dr_mode == otg, optional for dr_mode == host):
|
||||
- vbus-supply: regulator for VBUS
|
||||
|
@ -9,8 +9,9 @@ Required properties:
|
||||
register set for the device.
|
||||
- interrupts: one XHCI interrupt should be described here.
|
||||
|
||||
Optional property:
|
||||
Optional properties:
|
||||
- clocks: reference to a clock
|
||||
- usb3-lpm-capable: determines if platform is USB3 LPM capable
|
||||
|
||||
Example:
|
||||
usb@f0931000 {
|
||||
|
@ -53,10 +53,12 @@ unregister the PHY.
|
||||
The PHY driver should create the PHY in order for other peripheral controllers
|
||||
to make use of it. The PHY framework provides 2 APIs to create the PHY.
|
||||
|
||||
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
|
||||
The PHY drivers can use one of the above 2 APIs to create the PHY by passing
|
||||
the device pointer, phy ops and init_data.
|
||||
|
@ -105,13 +105,13 @@ macros such as these, and use driver_info to store more information.
|
||||
A short example, for a driver that supports several specific USB devices
|
||||
and their quirks, might have a MODULE_DEVICE_TABLE like this:
|
||||
|
||||
static const struct usb_device_id mydriver_id_table = {
|
||||
static const struct usb_device_id mydriver_id_table[] = {
|
||||
{ USB_DEVICE (0x9999, 0xaaaa), driver_info: QUIRK_X },
|
||||
{ USB_DEVICE (0xbbbb, 0x8888), driver_info: QUIRK_Y|QUIRK_Z },
|
||||
...
|
||||
{ } /* end with an all-zeroes entry */
|
||||
}
|
||||
MODULE_DEVICE_TABLE (usb, mydriver_id_table);
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, mydriver_id_table);
|
||||
|
||||
Most USB device drivers should pass these tables to the USB subsystem as
|
||||
well as to the module management subsystem. Not all, though: some driver
|
||||
@ -134,7 +134,7 @@ something like this:
|
||||
if exposing any operations through usbdevfs:
|
||||
.ioctl = my_ioctl,
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
When the USB subsystem knows about a driver's device ID table, it's used when
|
||||
choosing drivers to probe(). The thread doing new device processing checks
|
||||
|
@ -2,9 +2,28 @@
|
||||
|
||||
Alan Stern <stern@rowland.harvard.edu>
|
||||
|
||||
October 28, 2010
|
||||
Last-updated: February 2014
|
||||
|
||||
|
||||
Contents:
|
||||
---------
|
||||
* What is Power Management?
|
||||
* What is Remote Wakeup?
|
||||
* When is a USB device idle?
|
||||
* Forms of dynamic PM
|
||||
* The user interface for dynamic PM
|
||||
* Changing the default idle-delay time
|
||||
* Warnings
|
||||
* The driver interface for Power Management
|
||||
* The driver interface for autosuspend and autoresume
|
||||
* Other parts of the driver interface
|
||||
* Mutual exclusion
|
||||
* Interaction between dynamic PM and system PM
|
||||
* xHCI hardware link PM
|
||||
* USB Port Power Control
|
||||
* User Interface for Port Power Control
|
||||
* Suggested Userspace Port Power Policy
|
||||
|
||||
|
||||
What is Power Management?
|
||||
-------------------------
|
||||
@ -516,3 +535,225 @@ relevant attribute files is usb2_hardware_lpm.
|
||||
driver will enable hardware LPM for the device. You
|
||||
can write y/Y/1 or n/N/0 to the file to enable/disable
|
||||
USB2 hardware LPM manually. This is for test purpose mainly.
|
||||
|
||||
|
||||
USB Port Power Control
|
||||
----------------------
|
||||
|
||||
In addition to suspending endpoint devices and enabling hardware
|
||||
controlled link power management, the USB subsystem also has the
|
||||
capability to disable power to ports under some conditions. Power is
|
||||
controlled through Set/ClearPortFeature(PORT_POWER) requests to a hub.
|
||||
In the case of a root or platform-internal hub the host controller
|
||||
driver translates PORT_POWER requests into platform firmware (ACPI)
|
||||
method calls to set the port power state. For more background see the
|
||||
Linux Plumbers Conference 2012 slides [1] and video [2]:
|
||||
|
||||
Upon receiving a ClearPortFeature(PORT_POWER) request a USB port is
|
||||
logically off, and may trigger the actual loss of VBUS to the port [3].
|
||||
VBUS may be maintained in the case where a hub gangs multiple ports into
|
||||
a shared power well causing power to remain until all ports in the gang
|
||||
are turned off. VBUS may also be maintained by hub ports configured for
|
||||
a charging application. In any event a logically off port will lose
|
||||
connection with its device, not respond to hotplug events, and not
|
||||
respond to remote wakeup events*.
|
||||
|
||||
WARNING: turning off a port may result in the inability to hot add a device.
|
||||
Please see "User Interface for Port Power Control" for details.
|
||||
|
||||
As far as the effect on the device itself it is similar to what a device
|
||||
goes through during system suspend, i.e. the power session is lost. Any
|
||||
USB device or driver that misbehaves with system suspend will be
|
||||
similarly affected by a port power cycle event. For this reason the
|
||||
implementation shares the same device recovery path (and honors the same
|
||||
quirks) as the system resume path for the hub.
|
||||
|
||||
[1]: http://dl.dropbox.com/u/96820575/sarah-sharp-lpt-port-power-off2-mini.pdf
|
||||
[2]: http://linuxplumbers.ubicast.tv/videos/usb-port-power-off-kerneluserspace-api/
|
||||
[3]: USB 3.1 Section 10.12
|
||||
* wakeup note: if a device is configured to send wakeup events the port
|
||||
power control implementation will block poweroff attempts on that
|
||||
port.
|
||||
|
||||
|
||||
User Interface for Port Power Control
|
||||
-------------------------------------
|
||||
|
||||
The port power control mechanism uses the PM runtime system. Poweroff is
|
||||
requested by clearing the power/pm_qos_no_power_off flag of the port device
|
||||
(defaults to 1). If the port is disconnected it will immediately receive a
|
||||
ClearPortFeature(PORT_POWER) request. Otherwise, it will honor the pm runtime
|
||||
rules and require the attached child device and all descendants to be suspended.
|
||||
This mechanism is dependent on the hub advertising port power switching in its
|
||||
hub descriptor (wHubCharacteristics logical power switching mode field).
|
||||
|
||||
Note, some interface devices/drivers do not support autosuspend. Userspace may
|
||||
need to unbind the interface drivers before the usb_device will suspend. An
|
||||
unbound interface device is suspended by default. When unbinding, be careful
|
||||
to unbind interface drivers, not the driver of the parent usb device. Also,
|
||||
leave hub interface drivers bound. If the driver for the usb device (not
|
||||
interface) is unbound the kernel is no longer able to resume the device. If a
|
||||
hub interface driver is unbound, control of its child ports is lost and all
|
||||
attached child-devices will disconnect. A good rule of thumb is that if the
|
||||
'driver/module' link for a device points to /sys/module/usbcore then unbinding
|
||||
it will interfere with port power control.
|
||||
|
||||
Example of the relevant files for port power control. Note, in this example
|
||||
these files are relative to a usb hub device (prefix).
|
||||
|
||||
prefix=/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1
|
||||
|
||||
attached child device +
|
||||
hub port device + |
|
||||
hub interface device + | |
|
||||
v v v
|
||||
$prefix/3-1:1.0/3-1-port1/device
|
||||
|
||||
$prefix/3-1:1.0/3-1-port1/power/pm_qos_no_power_off
|
||||
$prefix/3-1:1.0/3-1-port1/device/power/control
|
||||
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf0>/driver/unbind
|
||||
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf1>/driver/unbind
|
||||
...
|
||||
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intfN>/driver/unbind
|
||||
|
||||
In addition to these files some ports may have a 'peer' link to a port on
|
||||
another hub. The expectation is that all superspeed ports have a
|
||||
hi-speed peer.
|
||||
|
||||
$prefix/3-1:1.0/3-1-port1/peer -> ../../../../usb2/2-1/2-1:1.0/2-1-port1
|
||||
../../../../usb2/2-1/2-1:1.0/2-1-port1/peer -> ../../../../usb3/3-1/3-1:1.0/3-1-port1
|
||||
|
||||
Distinct from 'companion ports', or 'ehci/xhci shared switchover ports'
|
||||
peer ports are simply the hi-speed and superspeed interface pins that
|
||||
are combined into a single usb3 connector. Peer ports share the same
|
||||
ancestor XHCI device.
|
||||
|
||||
While a superspeed port is powered off a device may downgrade its
|
||||
connection and attempt to connect to the hi-speed pins. The
|
||||
implementation takes steps to prevent this:
|
||||
|
||||
1/ Port suspend is sequenced to guarantee that hi-speed ports are powered-off
|
||||
before their superspeed peer is permitted to power-off. The implication is
|
||||
that the setting pm_qos_no_power_off to zero on a superspeed port may not cause
|
||||
the port to power-off until its highspeed peer has gone to its runtime suspend
|
||||
state. Userspace must take care to order the suspensions if it wants to
|
||||
guarantee that a superspeed port will power-off.
|
||||
|
||||
2/ Port resume is sequenced to force a superspeed port to power-on prior to its
|
||||
highspeed peer.
|
||||
|
||||
3/ Port resume always triggers an attached child device to resume. After a
|
||||
power session is lost the device may have been removed, or need reset.
|
||||
Resuming the child device when the parent port regains power resolves those
|
||||
states and clamps the maximum port power cycle frequency at the rate the child
|
||||
device can suspend (autosuspend-delay) and resume (reset-resume latency).
|
||||
|
||||
Sysfs files relevant for port power control:
|
||||
<hubdev-portX>/power/pm_qos_no_power_off:
|
||||
This writable flag controls the state of an idle port.
|
||||
Once all children and descendants have suspended the
|
||||
port may suspend/poweroff provided that
|
||||
pm_qos_no_power_off is '0'. If pm_qos_no_power_off is
|
||||
'1' the port will remain active/powered regardless of
|
||||
the stats of descendants. Defaults to 1.
|
||||
|
||||
<hubdev-portX>/power/runtime_status:
|
||||
This file reflects whether the port is 'active' (power is on)
|
||||
or 'suspended' (logically off). There is no indication to
|
||||
userspace whether VBUS is still supplied.
|
||||
|
||||
<hubdev-portX>/connect_type:
|
||||
An advisory read-only flag to userspace indicating the
|
||||
location and connection type of the port. It returns
|
||||
one of four values 'hotplug', 'hardwired', 'not used',
|
||||
and 'unknown'. All values, besides unknown, are set by
|
||||
platform firmware.
|
||||
|
||||
"hotplug" indicates an externally connectable/visible
|
||||
port on the platform. Typically userspace would choose
|
||||
to keep such a port powered to handle new device
|
||||
connection events.
|
||||
|
||||
"hardwired" refers to a port that is not visible but
|
||||
connectable. Examples are internal ports for USB
|
||||
bluetooth that can be disconnected via an external
|
||||
switch or a port with a hardwired USB camera. It is
|
||||
expected to be safe to allow these ports to suspend
|
||||
provided pm_qos_no_power_off is coordinated with any
|
||||
switch that gates connections. Userspace must arrange
|
||||
for the device to be connected prior to the port
|
||||
powering off, or to activate the port prior to enabling
|
||||
connection via a switch.
|
||||
|
||||
"not used" refers to an internal port that is expected
|
||||
to never have a device connected to it. These may be
|
||||
empty internal ports, or ports that are not physically
|
||||
exposed on a platform. Considered safe to be
|
||||
powered-off at all times.
|
||||
|
||||
"unknown" means platform firmware does not provide
|
||||
information for this port. Most commonly refers to
|
||||
external hub ports which should be considered 'hotplug'
|
||||
for policy decisions.
|
||||
|
||||
NOTE1: since we are relying on the BIOS to get this ACPI
|
||||
information correct, the USB port descriptions may be
|
||||
missing or wrong.
|
||||
|
||||
NOTE2: Take care in clearing pm_qos_no_power_off. Once
|
||||
power is off this port will
|
||||
not respond to new connect events.
|
||||
|
||||
Once a child device is attached additional constraints are
|
||||
applied before the port is allowed to poweroff.
|
||||
|
||||
<child>/power/control:
|
||||
Must be 'auto', and the port will not
|
||||
power down until <child>/power/runtime_status
|
||||
reflects the 'suspended' state. Default
|
||||
value is controlled by child device driver.
|
||||
|
||||
<child>/power/persist:
|
||||
This defaults to '1' for most devices and indicates if
|
||||
kernel can persist the device's configuration across a
|
||||
power session loss (suspend / port-power event). When
|
||||
this value is '0' (quirky devices), port poweroff is
|
||||
disabled.
|
||||
|
||||
<child>/driver/unbind:
|
||||
Wakeup capable devices will block port poweroff. At
|
||||
this time the only mechanism to clear the usb-internal
|
||||
wakeup-capability for an interface device is to unbind
|
||||
its driver.
|
||||
|
||||
Summary of poweroff pre-requisite settings relative to a port device:
|
||||
|
||||
echo 0 > power/pm_qos_no_power_off
|
||||
echo 0 > peer/power/pm_qos_no_power_off # if it exists
|
||||
echo auto > power/control # this is the default value
|
||||
echo auto > <child>/power/control
|
||||
echo 1 > <child>/power/persist # this is the default value
|
||||
|
||||
Suggested Userspace Port Power Policy
|
||||
-------------------------------------
|
||||
|
||||
As noted above userspace needs to be careful and deliberate about what
|
||||
ports are enabled for poweroff.
|
||||
|
||||
The default configuration is that all ports start with
|
||||
power/pm_qos_no_power_off set to '1' causing ports to always remain
|
||||
active.
|
||||
|
||||
Given confidence in the platform firmware's description of the ports
|
||||
(ACPI _PLD record for a port populates 'connect_type') userspace can
|
||||
clear pm_qos_no_power_off for all 'not used' ports. The same can be
|
||||
done for 'hardwired' ports provided poweroff is coordinated with any
|
||||
connection switch for the port.
|
||||
|
||||
A more aggressive userspace policy is to enable USB port power off for
|
||||
all ports (set <hubdev-portX>/power/pm_qos_no_power_off to '0') when
|
||||
some external factor indicates the user has stopped interacting with the
|
||||
system. For example, a distro may want to enable power off all USB
|
||||
ports when the screen blanks, and re-power them when the screen becomes
|
||||
active. Smart phones and tablets may want to power off USB ports when
|
||||
the user pushes the power button.
|
||||
|
@ -657,6 +657,8 @@
|
||||
<&tegra_car TEGRA114_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA114_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 22>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <0>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
@ -667,6 +669,7 @@
|
||||
nvidia,hssquelch-level = <2>;
|
||||
nvidia,hsdiscon-level = <5>;
|
||||
nvidia,xcvr-hsslew = <12>;
|
||||
nvidia,has-utmi-pad-registers;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -690,6 +693,8 @@
|
||||
<&tegra_car TEGRA114_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA114_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 59>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <0>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
|
@ -613,6 +613,8 @@
|
||||
<&tegra_car TEGRA124_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA124_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 59>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <0>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
@ -647,6 +649,8 @@
|
||||
<&tegra_car TEGRA124_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA124_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 22>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <0>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
@ -657,6 +661,7 @@
|
||||
nvidia,hssquelch-level = <2>;
|
||||
nvidia,hsdiscon-level = <5>;
|
||||
nvidia,xcvr-hsslew = <12>;
|
||||
nvidia,has-utmi-pad-registers;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -681,6 +686,8 @@
|
||||
<&tegra_car TEGRA124_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA124_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 58>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <0>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
|
@ -630,6 +630,8 @@
|
||||
<&tegra_car TEGRA20_CLK_CLK_M>,
|
||||
<&tegra_car TEGRA20_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "timer", "utmi-pads";
|
||||
resets = <&tegra_car 22>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,has-legacy-mode;
|
||||
nvidia,hssync-start-delay = <9>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
@ -638,6 +640,7 @@
|
||||
nvidia,xcvr-setup = <9>;
|
||||
nvidia,xcvr-lsfslew = <1>;
|
||||
nvidia,xcvr-lsrslew = <1>;
|
||||
nvidia,has-utmi-pad-registers;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -661,6 +664,8 @@
|
||||
<&tegra_car TEGRA20_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA20_CLK_CDEV2>;
|
||||
clock-names = "reg", "pll_u", "ulpi-link";
|
||||
resets = <&tegra_car 58>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -685,6 +690,8 @@
|
||||
<&tegra_car TEGRA20_CLK_CLK_M>,
|
||||
<&tegra_car TEGRA20_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "timer", "utmi-pads";
|
||||
resets = <&tegra_car 59>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <9>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
|
@ -775,6 +775,8 @@
|
||||
<&tegra_car TEGRA30_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA30_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 22>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <9>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
@ -786,6 +788,7 @@
|
||||
nvidia,xcvr-hsslew = <32>;
|
||||
nvidia,hssquelch-level = <2>;
|
||||
nvidia,hsdiscon-level = <5>;
|
||||
nvidia,has-utmi-pad-registers;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -809,6 +812,8 @@
|
||||
<&tegra_car TEGRA30_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA30_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 58>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <9>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
@ -843,6 +848,8 @@
|
||||
<&tegra_car TEGRA30_CLK_PLL_U>,
|
||||
<&tegra_car TEGRA30_CLK_USBD>;
|
||||
clock-names = "reg", "pll_u", "utmi-pads";
|
||||
resets = <&tegra_car 59>, <&tegra_car 22>;
|
||||
reset-names = "usb", "utmi-pads";
|
||||
nvidia,hssync-start-delay = <0>;
|
||||
nvidia,idle-wait-delay = <17>;
|
||||
nvidia,elastic-limit = <16>;
|
||||
|
@ -15,6 +15,13 @@ config GENERIC_PHY
|
||||
phy users can obtain reference to the PHY. All the users of this
|
||||
framework should select this config.
|
||||
|
||||
config PHY_BERLIN_SATA
|
||||
tristate "Marvell Berlin SATA PHY driver"
|
||||
depends on ARCH_BERLIN && HAS_IOMEM && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the SATA PHY on Marvell Berlin SoCs.
|
||||
|
||||
config PHY_EXYNOS_MIPI_VIDEO
|
||||
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
@ -27,10 +34,20 @@ config PHY_EXYNOS_MIPI_VIDEO
|
||||
|
||||
config PHY_MVEBU_SATA
|
||||
def_bool y
|
||||
depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
|
||||
depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_MIPHY365X
|
||||
tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
|
||||
depends on ARCH_STI
|
||||
depends on GENERIC_PHY
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
help
|
||||
Enable this to support the miphy transceiver (for SATA/PCIE)
|
||||
that is part of STMicroelectronics STiH41x SoC series.
|
||||
|
||||
config OMAP_CONTROL_PHY
|
||||
tristate "OMAP CONTROL PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
@ -109,6 +126,14 @@ config PHY_EXYNOS5250_SATA
|
||||
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
|
||||
port to accept one SATA device.
|
||||
|
||||
config PHY_HIX5HD2_SATA
|
||||
tristate "HIX5HD2 SATA PHY Driver"
|
||||
depends on ARCH_HIX5HD2 && OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for SATA PHY on Hisilicon hix5hd2 Soc.
|
||||
|
||||
config PHY_SUN4I_USB
|
||||
tristate "Allwinner sunxi SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
@ -124,50 +149,39 @@ config PHY_SUN4I_USB
|
||||
config PHY_SAMSUNG_USB2
|
||||
tristate "Samsung USB 2.0 PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
default ARCH_EXYNOS
|
||||
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.
|
||||
SoCs. This driver provides the interface for USB 2.0 PHY. Support
|
||||
for particular PHYs will be enabled based on the SoC type in addition
|
||||
to this driver.
|
||||
|
||||
config PHY_EXYNOS4210_USB2
|
||||
bool "Support for Exynos 4210"
|
||||
bool
|
||||
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.
|
||||
default CPU_EXYNOS4210
|
||||
|
||||
config PHY_EXYNOS4X12_USB2
|
||||
bool "Support for Exynos 4x12"
|
||||
bool
|
||||
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.
|
||||
default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
|
||||
|
||||
config PHY_EXYNOS5250_USB2
|
||||
bool "Support for Exynos 5250"
|
||||
bool
|
||||
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.
|
||||
default SOC_EXYNOS5250 || SOC_EXYNOS5420
|
||||
|
||||
config PHY_EXYNOS5_USBDRD
|
||||
tristate "Exynos5 SoC series USB DRD PHY driver"
|
||||
depends on ARCH_EXYNOS5 && OF
|
||||
depends on HAS_IOMEM
|
||||
depends on USB_DWC3_EXYNOS
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
default y
|
||||
help
|
||||
Enable USB DRD PHY support for Exynos 5 SoC series.
|
||||
This driver provides PHY interface for USB 3.0 DRD controller
|
||||
@ -180,4 +194,18 @@ config PHY_XGENE
|
||||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
config PHY_QCOM_APQ8064_SATA
|
||||
tristate "Qualcomm APQ8064 SATA SerDes/PHY driver"
|
||||
depends on ARCH_QCOM
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_QCOM_IPQ806X_SATA
|
||||
tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
|
||||
depends on ARCH_QCOM
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
endmenu
|
||||
|
@ -3,15 +3,18 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
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_PHY_MIPHY365X) += phy-miphy365x.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_HIX5HD2_SATA) += phy-hix5hd2-sata.o
|
||||
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
||||
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
|
||||
phy-exynos-usb2-y += phy-samsung-usb2.o
|
||||
@ -20,3 +23,5 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
|
||||
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
|
||||
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
|
||||
|
@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
gphy = devm_phy_create(dev, &ops, NULL);
|
||||
gphy = devm_phy_create(dev, NULL, &ops, NULL);
|
||||
if (IS_ERR(gphy))
|
||||
return PTR_ERR(gphy);
|
||||
|
||||
|
284
drivers/phy/phy-berlin-sata.c
Normal file
284
drivers/phy/phy-berlin-sata.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Marvell Berlin SATA PHY driver
|
||||
*
|
||||
* Copyright (C) 2014 Marvell Technology Group Ltd.
|
||||
*
|
||||
* Antoine Ténart <antoine.tenart@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define HOST_VSA_ADDR 0x0
|
||||
#define HOST_VSA_DATA 0x4
|
||||
#define PORT_SCR_CTL 0x2c
|
||||
#define PORT_VSR_ADDR 0x78
|
||||
#define PORT_VSR_DATA 0x7c
|
||||
|
||||
#define CONTROL_REGISTER 0x0
|
||||
#define MBUS_SIZE_CONTROL 0x4
|
||||
|
||||
#define POWER_DOWN_PHY0 BIT(6)
|
||||
#define POWER_DOWN_PHY1 BIT(14)
|
||||
#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
|
||||
#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
|
||||
|
||||
#define PHY_BASE 0x200
|
||||
|
||||
/* register 0x01 */
|
||||
#define REF_FREF_SEL_25 BIT(0)
|
||||
#define PHY_MODE_SATA (0x0 << 5)
|
||||
|
||||
/* register 0x02 */
|
||||
#define USE_MAX_PLL_RATE BIT(12)
|
||||
|
||||
/* register 0x23 */
|
||||
#define DATA_BIT_WIDTH_10 (0x0 << 10)
|
||||
#define DATA_BIT_WIDTH_20 (0x1 << 10)
|
||||
#define DATA_BIT_WIDTH_40 (0x2 << 10)
|
||||
|
||||
/* register 0x25 */
|
||||
#define PHY_GEN_MAX_1_5 (0x0 << 10)
|
||||
#define PHY_GEN_MAX_3_0 (0x1 << 10)
|
||||
#define PHY_GEN_MAX_6_0 (0x2 << 10)
|
||||
|
||||
struct phy_berlin_desc {
|
||||
struct phy *phy;
|
||||
u32 power_bit;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
struct phy_berlin_priv {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
struct phy_berlin_desc **phys;
|
||||
unsigned nphys;
|
||||
};
|
||||
|
||||
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
/* select register */
|
||||
writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
|
||||
|
||||
/* set bits */
|
||||
regval = readl(ctrl_reg + PORT_VSR_DATA);
|
||||
regval &= ~mask;
|
||||
regval |= val;
|
||||
writel(regval, ctrl_reg + PORT_VSR_DATA);
|
||||
}
|
||||
|
||||
static int phy_berlin_sata_power_on(struct phy *phy)
|
||||
{
|
||||
struct phy_berlin_desc *desc = phy_get_drvdata(phy);
|
||||
struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80);
|
||||
int ret = 0;
|
||||
u32 regval;
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Power on PHY */
|
||||
writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
|
||||
regval = readl(priv->base + HOST_VSA_DATA);
|
||||
regval &= ~desc->power_bit;
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
/* Configure MBus */
|
||||
writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR);
|
||||
regval = readl(priv->base + HOST_VSA_DATA);
|
||||
regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128;
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
/* set PHY mode and ref freq to 25 MHz */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
|
||||
REF_FREF_SEL_25 | PHY_MODE_SATA);
|
||||
|
||||
/* set PHY up to 6 Gbps */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
|
||||
|
||||
/* set 40 bits width */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
|
||||
|
||||
/* use max pll rate */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
|
||||
|
||||
/* set Gen3 controller speed */
|
||||
regval = readl(ctrl_reg + PORT_SCR_CTL);
|
||||
regval &= ~GENMASK(7, 4);
|
||||
regval |= 0x30;
|
||||
writel(regval, ctrl_reg + PORT_SCR_CTL);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int phy_berlin_sata_power_off(struct phy *phy)
|
||||
{
|
||||
struct phy_berlin_desc *desc = phy_get_drvdata(phy);
|
||||
struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
u32 regval;
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Power down PHY */
|
||||
writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
|
||||
regval = readl(priv->base + HOST_VSA_DATA);
|
||||
regval |= desc->power_bit;
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct phy_berlin_priv *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
if (WARN_ON(args->args[0] >= priv->nphys))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
for (i = 0; i < priv->nphys; i++) {
|
||||
if (priv->phys[i]->index == args->args[0])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == priv->nphys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return priv->phys[i]->phy;
|
||||
}
|
||||
|
||||
static struct phy_ops phy_berlin_sata_ops = {
|
||||
.power_on = phy_berlin_sata_power_on,
|
||||
.power_off = phy_berlin_sata_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static u32 phy_berlin_power_down_bits[] = {
|
||||
POWER_DOWN_PHY0,
|
||||
POWER_DOWN_PHY1,
|
||||
};
|
||||
|
||||
static int phy_berlin_sata_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child;
|
||||
struct phy *phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy_berlin_priv *priv;
|
||||
struct resource *res;
|
||||
int i = 0;
|
||||
u32 phy_id;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
priv->base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
priv->nphys = of_get_child_count(dev->of_node);
|
||||
if (priv->nphys == 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
|
||||
GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
struct phy_berlin_desc *phy_desc;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &phy_id)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
|
||||
dev_err(dev, "invalid reg in node %s\n", child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
|
||||
if (!phy_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", phy_id);
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
phy_desc->phy = phy;
|
||||
phy_desc->power_bit = phy_berlin_power_down_bits[phy_id];
|
||||
phy_desc->index = phy_id;
|
||||
phy_set_drvdata(phy, phy_desc);
|
||||
|
||||
priv->phys[i++] = phy_desc;
|
||||
|
||||
/* Make sure the PHY is off */
|
||||
phy_berlin_sata_power_off(phy);
|
||||
}
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id phy_berlin_sata_of_match[] = {
|
||||
{ .compatible = "marvell,berlin2q-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver phy_berlin_sata_driver = {
|
||||
.probe = phy_berlin_sata_probe,
|
||||
.driver = {
|
||||
.name = "phy-berlin-sata",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = phy_berlin_sata_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_berlin_sata_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver");
|
||||
MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -21,6 +21,7 @@
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
static struct class *phy_class;
|
||||
static DEFINE_MUTEX(phy_provider_mutex);
|
||||
@ -86,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port)
|
||||
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *child;
|
||||
|
||||
list_for_each_entry(phy_provider, &phy_provider_list, list) {
|
||||
if (phy_provider->dev->of_node == node)
|
||||
return phy_provider;
|
||||
|
||||
for_each_child_of_node(phy_provider->dev->of_node, child)
|
||||
if (child == node)
|
||||
return phy_provider;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy)
|
||||
if (!phy)
|
||||
return 0;
|
||||
|
||||
if (phy->pwr) {
|
||||
ret = regulator_enable(phy->pwr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_pm_runtime_get_sync(phy);
|
||||
if (ret < 0 && ret != -ENOTSUPP)
|
||||
return ret;
|
||||
@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy)
|
||||
out:
|
||||
mutex_unlock(&phy->mutex);
|
||||
phy_pm_runtime_put_sync(phy);
|
||||
if (phy->pwr)
|
||||
regulator_disable(phy->pwr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy)
|
||||
mutex_unlock(&phy->mutex);
|
||||
phy_pm_runtime_put(phy);
|
||||
|
||||
if (phy->pwr)
|
||||
regulator_disable(phy->pwr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_power_off);
|
||||
@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
|
||||
struct phy *phy;
|
||||
struct class_dev_iter iter;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *child;
|
||||
|
||||
class_dev_iter_init(&iter, phy_class, NULL, NULL);
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
phy = to_phy(dev);
|
||||
if (node != phy->dev.of_node)
|
||||
if (node != phy->dev.of_node) {
|
||||
for_each_child_of_node(node, child) {
|
||||
if (child == phy->dev.of_node)
|
||||
goto phy_found;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
phy_found:
|
||||
class_dev_iter_exit(&iter);
|
||||
return phy;
|
||||
}
|
||||
@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
|
||||
/**
|
||||
* phy_create() - create a new phy
|
||||
* @dev: device that is creating the new phy
|
||||
* @node: device node of the phy
|
||||
* @ops: function pointers for performing phy operations
|
||||
* @init_data: contains the list of PHY consumers or NULL
|
||||
*
|
||||
* Called to create a phy using phy framework.
|
||||
*/
|
||||
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
{
|
||||
int ret;
|
||||
int id;
|
||||
@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
goto free_phy;
|
||||
}
|
||||
|
||||
/* phy-supply */
|
||||
phy->pwr = regulator_get_optional(dev, "phy");
|
||||
if (IS_ERR(phy->pwr)) {
|
||||
if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto free_ida;
|
||||
}
|
||||
phy->pwr = NULL;
|
||||
}
|
||||
|
||||
device_initialize(&phy->dev);
|
||||
mutex_init(&phy->mutex);
|
||||
|
||||
phy->dev.class = phy_class;
|
||||
phy->dev.parent = dev;
|
||||
phy->dev.of_node = dev->of_node;
|
||||
phy->dev.of_node = node ?: dev->of_node;
|
||||
phy->id = id;
|
||||
phy->ops = ops;
|
||||
phy->init_data = init_data;
|
||||
@ -617,6 +653,9 @@ put_dev:
|
||||
put_device(&phy->dev); /* calls phy_release() which frees resources */
|
||||
return ERR_PTR(ret);
|
||||
|
||||
free_ida:
|
||||
ida_simple_remove(&phy_ida, phy->id);
|
||||
|
||||
free_phy:
|
||||
kfree(phy);
|
||||
return ERR_PTR(ret);
|
||||
@ -626,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create);
|
||||
/**
|
||||
* devm_phy_create() - create a new phy
|
||||
* @dev: device that is creating the new phy
|
||||
* @node: device node of the phy
|
||||
* @ops: function pointers for performing phy operations
|
||||
* @init_data: contains the list of PHY consumers or NULL
|
||||
*
|
||||
@ -634,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create);
|
||||
* On driver detach, release function is invoked on the devres data,
|
||||
* then, devres data is freed.
|
||||
*/
|
||||
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
|
||||
@ -643,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy = phy_create(dev, ops, init_data);
|
||||
phy = phy_create(dev, node, ops, init_data);
|
||||
if (!IS_ERR(phy)) {
|
||||
*ptr = phy;
|
||||
devres_add(dev, ptr);
|
||||
@ -800,6 +841,7 @@ static void phy_release(struct device *dev)
|
||||
|
||||
phy = to_phy(dev);
|
||||
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
|
||||
regulator_put(phy->pwr);
|
||||
ida_simple_remove(&phy_ida, phy->id);
|
||||
kfree(phy);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -76,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(state->regs))
|
||||
return PTR_ERR(state->regs);
|
||||
|
||||
phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create Display Port PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
|
||||
phy_set_drvdata(phy, state);
|
||||
|
||||
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 exynos_dp_video_phy_of_match[] = {
|
||||
|
@ -9,6 +9,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -135,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&state->slock);
|
||||
|
||||
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
|
||||
struct phy *phy = devm_phy_create(dev,
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos_mipi_video_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
@ -149,10 +150,8 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev,
|
||||
exynos_mipi_video_phy_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 exynos_mipi_video_phy_of_match[] = {
|
||||
|
@ -67,6 +67,8 @@
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0)
|
||||
|
||||
#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8)
|
||||
|
||||
#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)
|
||||
@ -86,13 +88,23 @@
|
||||
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
|
||||
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
|
||||
/* The following bit defines are presented in the
|
||||
* order taken from the Exynos4412 reference manual.
|
||||
*
|
||||
* During experiments with the hardware and debugging
|
||||
* it was determined that the hardware behaves contrary
|
||||
* to the manual.
|
||||
*
|
||||
* The following bit values were chaned accordingly to the
|
||||
* results of real hardware experiments.
|
||||
*/
|
||||
#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_HSIC0 BIT(6)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8)
|
||||
|
||||
/* Isolation, configured in the power management unit */
|
||||
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
|
||||
@ -187,7 +199,12 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
|
||||
|
||||
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
||||
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
|
||||
|
||||
if (drv->cfg->has_refclk_sel)
|
||||
clk = EXYNOS_3250_UPHYCLK_REFCLKSEL;
|
||||
|
||||
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
|
||||
clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON;
|
||||
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
||||
}
|
||||
|
||||
@ -198,27 +215,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
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;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY |
|
||||
EXYNOS_4x12_URSTCON_PHY1 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P0;
|
||||
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;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HSIC0 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P1;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC1:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
|
||||
@ -228,11 +240,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
};
|
||||
|
||||
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);
|
||||
@ -253,41 +260,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
}
|
||||
}
|
||||
|
||||
static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
if (inst->int_cnt++ > 0)
|
||||
return;
|
||||
|
||||
exynos4x12_setup_clk(inst);
|
||||
exynos4x12_isol(inst, 0);
|
||||
exynos4x12_phy_pwr(inst, 1);
|
||||
}
|
||||
|
||||
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);
|
||||
if (inst->ext_cnt++ > 0)
|
||||
return 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);
|
||||
if (inst->cfg->id == EXYNOS4x12_HOST) {
|
||||
regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK,
|
||||
EXYNOS_4x12_MODE_SWITCH_HOST);
|
||||
exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
}
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
|
||||
regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK,
|
||||
EXYNOS_4x12_MODE_SWITCH_DEVICE);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
|
||||
inst->cfg->id == EXYNOS4x12_HSIC1) {
|
||||
exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]);
|
||||
}
|
||||
|
||||
exynos4x12_power_on_int(inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
if (inst->int_cnt-- > 1)
|
||||
return;
|
||||
|
||||
exynos4x12_isol(inst, 1);
|
||||
exynos4x12_phy_pwr(inst, 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->ext_cnt-- > 1)
|
||||
return 0;
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
|
||||
exynos4x12_isol(device, 1);
|
||||
exynos4x12_phy_pwr(device, 0);
|
||||
if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
|
||||
regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK,
|
||||
EXYNOS_4x12_MODE_SWITCH_HOST);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HOST)
|
||||
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
|
||||
inst->cfg->id == EXYNOS4x12_HSIC1) {
|
||||
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]);
|
||||
}
|
||||
|
||||
exynos4x12_power_off_int(inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -320,6 +364,13 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = {
|
||||
.has_refclk_sel = 1,
|
||||
.num_phys = 1,
|
||||
.phys = exynos4x12_phys,
|
||||
.rate_to_clk = exynos4x12_rate_to_clk,
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
|
||||
.has_mode_switch = 1,
|
||||
.num_phys = EXYNOS4x12_NUM_PHYS,
|
||||
|
@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
{
|
||||
.id = EXYNOS5_DRDPHY_UTMI,
|
||||
.phy_isol = exynos5_usbdrd_phy_isol,
|
||||
@ -521,13 +521,13 @@ const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
.pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
|
||||
};
|
||||
|
||||
const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
};
|
||||
@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
|
||||
dev_vdbg(dev, "Creating usbdrd_phy phy\n");
|
||||
|
||||
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
|
||||
struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops,
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos5_usbdrd_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "Failed to create usbdrd_phy phy\n");
|
||||
|
@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
|
||||
sata_phy->phy = devm_phy_create(dev, NULL, &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");
|
||||
|
@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
|
||||
|
||||
break;
|
||||
}
|
||||
inst->enabled = 1;
|
||||
exynos5250_isol(inst, 0);
|
||||
|
||||
return 0;
|
||||
@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
|
||||
u32 otg;
|
||||
u32 hsic;
|
||||
|
||||
inst->enabled = 0;
|
||||
exynos5250_isol(inst, 1);
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
|
192
drivers/phy/phy-hix5hd2-sata.c
Normal file
192
drivers/phy/phy-hix5hd2-sata.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Linaro Ltd.
|
||||
* Copyright (c) 2014 Hisilicon Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define SATA_PHY0_CTLL 0xa0
|
||||
#define MPLL_MULTIPLIER_SHIFT 1
|
||||
#define MPLL_MULTIPLIER_MASK 0xfe
|
||||
#define MPLL_MULTIPLIER_50M 0x3c
|
||||
#define MPLL_MULTIPLIER_100M 0x1e
|
||||
#define PHY_RESET BIT(0)
|
||||
#define REF_SSP_EN BIT(9)
|
||||
#define SSC_EN BIT(10)
|
||||
#define REF_USE_PAD BIT(23)
|
||||
|
||||
#define SATA_PORT_PHYCTL 0x174
|
||||
#define SPEED_MODE_MASK 0x6f0000
|
||||
#define HALF_RATE_SHIFT 16
|
||||
#define PHY_CONFIG_SHIFT 18
|
||||
#define GEN2_EN_SHIFT 21
|
||||
#define SPEED_CTRL BIT(20)
|
||||
|
||||
#define SATA_PORT_PHYCTL1 0x148
|
||||
#define AMPLITUDE_MASK 0x3ffffe
|
||||
#define AMPLITUDE_GEN3 0x68
|
||||
#define AMPLITUDE_GEN3_SHIFT 15
|
||||
#define AMPLITUDE_GEN2 0x56
|
||||
#define AMPLITUDE_GEN2_SHIFT 8
|
||||
#define AMPLITUDE_GEN1 0x56
|
||||
#define AMPLITUDE_GEN1_SHIFT 1
|
||||
|
||||
#define SATA_PORT_PHYCTL2 0x14c
|
||||
#define PREEMPH_MASK 0x3ffff
|
||||
#define PREEMPH_GEN3 0x20
|
||||
#define PREEMPH_GEN3_SHIFT 12
|
||||
#define PREEMPH_GEN2 0x15
|
||||
#define PREEMPH_GEN2_SHIFT 6
|
||||
#define PREEMPH_GEN1 0x5
|
||||
#define PREEMPH_GEN1_SHIFT 0
|
||||
|
||||
struct hix5hd2_priv {
|
||||
void __iomem *base;
|
||||
struct regmap *peri_ctrl;
|
||||
};
|
||||
|
||||
enum phy_speed_mode {
|
||||
SPEED_MODE_GEN1 = 0,
|
||||
SPEED_MODE_GEN2 = 1,
|
||||
SPEED_MODE_GEN3 = 2,
|
||||
};
|
||||
|
||||
static int hix5hd2_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
struct hix5hd2_priv *priv = phy_get_drvdata(phy);
|
||||
u32 val, data[2];
|
||||
int ret;
|
||||
|
||||
if (priv->peri_ctrl) {
|
||||
ret = of_property_read_u32_array(phy->dev.of_node,
|
||||
"hisilicon,power-reg",
|
||||
&data[0], 2);
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "Fail read hisilicon,power-reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->peri_ctrl, data[0],
|
||||
BIT(data[1]), BIT(data[1]));
|
||||
}
|
||||
|
||||
/* reset phy */
|
||||
val = readl_relaxed(priv->base + SATA_PHY0_CTLL);
|
||||
val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD);
|
||||
val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT |
|
||||
REF_SSP_EN | PHY_RESET;
|
||||
writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
|
||||
msleep(20);
|
||||
val &= ~PHY_RESET;
|
||||
writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
|
||||
|
||||
val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1);
|
||||
val &= ~AMPLITUDE_MASK;
|
||||
val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT |
|
||||
AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT |
|
||||
AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1);
|
||||
|
||||
val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2);
|
||||
val &= ~PREEMPH_MASK;
|
||||
val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT |
|
||||
PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT |
|
||||
PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2);
|
||||
|
||||
/* ensure PHYCTRL setting takes effect */
|
||||
val = readl_relaxed(priv->base + SATA_PORT_PHYCTL);
|
||||
val &= ~SPEED_MODE_MASK;
|
||||
val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT |
|
||||
SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT |
|
||||
SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
|
||||
|
||||
msleep(20);
|
||||
val &= ~SPEED_MODE_MASK;
|
||||
val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT |
|
||||
SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT |
|
||||
SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
|
||||
|
||||
val &= ~(SPEED_MODE_MASK | SPEED_CTRL);
|
||||
val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT |
|
||||
SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT |
|
||||
SPEED_MODE_GEN2 << GEN2_EN_SHIFT;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops hix5hd2_sata_phy_ops = {
|
||||
.init = hix5hd2_sata_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy *phy;
|
||||
struct hix5hd2_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"hisilicon,peripheral-syscon");
|
||||
if (IS_ERR(priv->peri_ctrl))
|
||||
priv->peri_ctrl = NULL;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
|
||||
{.compatible = "hisilicon,hix5hd2-sata-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver hix5hd2_sata_phy_driver = {
|
||||
.probe = hix5hd2_sata_phy_probe,
|
||||
.driver = {
|
||||
.name = "hix5hd2-sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = hix5hd2_sata_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(hix5hd2_sata_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Jiancheng Xue <xuejiancheng@huawei.com>");
|
||||
MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver");
|
||||
MODULE_ALIAS("platform:hix5hd2-sata-phy");
|
||||
MODULE_LICENSE("GPL v2");
|
636
drivers/phy/phy-miphy365x.c
Normal file
636
drivers/phy/phy-miphy365x.c
Normal file
@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics – All Rights Reserved
|
||||
*
|
||||
* STMicroelectronics PHY driver MiPHY365 (for SoC STiH416).
|
||||
*
|
||||
* Authors: Alexandre Torgue <alexandre.torgue@st.com>
|
||||
* Lee Jones <lee.jones@linaro.org>
|
||||
*
|
||||
* 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/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
|
||||
#define HFC_TIMEOUT 100
|
||||
|
||||
#define SYSCFG_SELECT_SATA_MASK BIT(1)
|
||||
#define SYSCFG_SELECT_SATA_POS 1
|
||||
|
||||
/* MiPHY365x register definitions */
|
||||
#define RESET_REG 0x00
|
||||
#define RST_PLL BIT(1)
|
||||
#define RST_PLL_CAL BIT(2)
|
||||
#define RST_RX BIT(4)
|
||||
#define RST_MACRO BIT(7)
|
||||
|
||||
#define STATUS_REG 0x01
|
||||
#define IDLL_RDY BIT(0)
|
||||
#define PLL_RDY BIT(1)
|
||||
#define DES_BIT_LOCK BIT(2)
|
||||
#define DES_SYMBOL_LOCK BIT(3)
|
||||
|
||||
#define CTRL_REG 0x02
|
||||
#define TERM_EN BIT(0)
|
||||
#define PCI_EN BIT(2)
|
||||
#define DES_BIT_LOCK_EN BIT(3)
|
||||
#define TX_POL BIT(5)
|
||||
|
||||
#define INT_CTRL_REG 0x03
|
||||
|
||||
#define BOUNDARY1_REG 0x10
|
||||
#define SPDSEL_SEL BIT(0)
|
||||
|
||||
#define BOUNDARY3_REG 0x12
|
||||
#define TX_SPDSEL_GEN1_VAL 0
|
||||
#define TX_SPDSEL_GEN2_VAL 0x01
|
||||
#define TX_SPDSEL_GEN3_VAL 0x02
|
||||
#define RX_SPDSEL_GEN1_VAL 0
|
||||
#define RX_SPDSEL_GEN2_VAL (0x01 << 3)
|
||||
#define RX_SPDSEL_GEN3_VAL (0x02 << 3)
|
||||
|
||||
#define PCIE_REG 0x16
|
||||
|
||||
#define BUF_SEL_REG 0x20
|
||||
#define CONF_GEN_SEL_GEN3 0x02
|
||||
#define CONF_GEN_SEL_GEN2 0x01
|
||||
#define PD_VDDTFILTER BIT(4)
|
||||
|
||||
#define TXBUF1_REG 0x21
|
||||
#define SWING_VAL 0x04
|
||||
#define SWING_VAL_GEN1 0x03
|
||||
#define PREEMPH_VAL (0x3 << 5)
|
||||
|
||||
#define TXBUF2_REG 0x22
|
||||
#define TXSLEW_VAL 0x2
|
||||
#define TXSLEW_VAL_GEN1 0x4
|
||||
|
||||
#define RXBUF_OFFSET_CTRL_REG 0x23
|
||||
|
||||
#define RXBUF_REG 0x25
|
||||
#define SDTHRES_VAL 0x01
|
||||
#define EQ_ON3 (0x03 << 4)
|
||||
#define EQ_ON1 (0x01 << 4)
|
||||
|
||||
#define COMP_CTRL1_REG 0x40
|
||||
#define START_COMSR BIT(0)
|
||||
#define START_COMZC BIT(1)
|
||||
#define COMSR_DONE BIT(2)
|
||||
#define COMZC_DONE BIT(3)
|
||||
#define COMP_AUTO_LOAD BIT(4)
|
||||
|
||||
#define COMP_CTRL2_REG 0x41
|
||||
#define COMP_2MHZ_RAT_GEN1 0x1e
|
||||
#define COMP_2MHZ_RAT 0xf
|
||||
|
||||
#define COMP_CTRL3_REG 0x42
|
||||
#define COMSR_COMP_REF 0x33
|
||||
|
||||
#define COMP_IDLL_REG 0x47
|
||||
#define COMZC_IDLL 0x2a
|
||||
|
||||
#define PLL_CTRL1_REG 0x50
|
||||
#define PLL_START_CAL BIT(0)
|
||||
#define BUF_EN BIT(2)
|
||||
#define SYNCHRO_TX BIT(3)
|
||||
#define SSC_EN BIT(6)
|
||||
#define CONFIG_PLL BIT(7)
|
||||
|
||||
#define PLL_CTRL2_REG 0x51
|
||||
#define BYPASS_PLL_CAL BIT(1)
|
||||
|
||||
#define PLL_RAT_REG 0x52
|
||||
|
||||
#define PLL_SSC_STEP_MSB_REG 0x56
|
||||
#define PLL_SSC_STEP_MSB_VAL 0x03
|
||||
|
||||
#define PLL_SSC_STEP_LSB_REG 0x57
|
||||
#define PLL_SSC_STEP_LSB_VAL 0x63
|
||||
|
||||
#define PLL_SSC_PER_MSB_REG 0x58
|
||||
#define PLL_SSC_PER_MSB_VAL 0
|
||||
|
||||
#define PLL_SSC_PER_LSB_REG 0x59
|
||||
#define PLL_SSC_PER_LSB_VAL 0xf1
|
||||
|
||||
#define IDLL_TEST_REG 0x72
|
||||
#define START_CLK_HF BIT(6)
|
||||
|
||||
#define DES_BITLOCK_REG 0x86
|
||||
#define BIT_LOCK_LEVEL 0x01
|
||||
#define BIT_LOCK_CNT_512 (0x03 << 5)
|
||||
|
||||
struct miphy365x_phy {
|
||||
struct phy *phy;
|
||||
void __iomem *base;
|
||||
bool pcie_tx_pol_inv;
|
||||
bool sata_tx_pol_inv;
|
||||
u32 sata_gen;
|
||||
u64 ctrlreg;
|
||||
u8 type;
|
||||
};
|
||||
|
||||
struct miphy365x_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex miphy_mutex;
|
||||
struct miphy365x_phy **phys;
|
||||
};
|
||||
|
||||
/*
|
||||
* These values are represented in Device tree. They are considered to be ABI
|
||||
* and although they can be extended any existing values must not change.
|
||||
*/
|
||||
enum miphy_sata_gen {
|
||||
SATA_GEN1 = 1,
|
||||
SATA_GEN2,
|
||||
SATA_GEN3
|
||||
};
|
||||
|
||||
static u8 rx_tx_spd[] = {
|
||||
TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL,
|
||||
TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL,
|
||||
TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL
|
||||
};
|
||||
|
||||
/*
|
||||
* This function selects the system configuration,
|
||||
* either two SATA, one SATA and one PCIe, or two PCIe lanes.
|
||||
*/
|
||||
static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
|
||||
|
||||
return regmap_update_bits(miphy_dev->regmap,
|
||||
(unsigned int)miphy_phy->ctrlreg,
|
||||
SYSCFG_SELECT_SATA_MASK,
|
||||
sata << SYSCFG_SELECT_SATA_POS);
|
||||
}
|
||||
|
||||
static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (miphy_phy->pcie_tx_pol_inv) {
|
||||
/* Invert Tx polarity and clear pci_txdetect_pol bit */
|
||||
val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL;
|
||||
writeb_relaxed(val, miphy_phy->base + CTRL_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + PCIE_REG);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
|
||||
u8 mask = IDLL_RDY | PLL_RDY;
|
||||
u8 regval;
|
||||
|
||||
do {
|
||||
regval = readb_relaxed(miphy_phy->base + STATUS_REG);
|
||||
if (!(regval & mask))
|
||||
return 0;
|
||||
|
||||
usleep_range(2000, 2500);
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
dev_err(miphy_dev->dev, "HFC ready timeout!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
|
||||
u8 mask = IDLL_RDY | PLL_RDY;
|
||||
u8 regval;
|
||||
|
||||
do {
|
||||
regval = readb_relaxed(miphy_phy->base + STATUS_REG);
|
||||
if ((regval & mask) == mask)
|
||||
return 0;
|
||||
|
||||
usleep_range(2000, 2500);
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
dev_err(miphy_dev->dev, "PHY not ready timeout!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
u8 val, mask;
|
||||
|
||||
if (miphy_phy->sata_gen == SATA_GEN1)
|
||||
writeb_relaxed(COMP_2MHZ_RAT_GEN1,
|
||||
miphy_phy->base + COMP_CTRL2_REG);
|
||||
else
|
||||
writeb_relaxed(COMP_2MHZ_RAT,
|
||||
miphy_phy->base + COMP_CTRL2_REG);
|
||||
|
||||
if (miphy_phy->sata_gen != SATA_GEN3) {
|
||||
writeb_relaxed(COMSR_COMP_REF,
|
||||
miphy_phy->base + COMP_CTRL3_REG);
|
||||
/*
|
||||
* Force VCO current to value defined by address 0x5A
|
||||
* and disable PCIe100Mref bit
|
||||
* Enable auto load compensation for pll_i_bias
|
||||
*/
|
||||
writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG);
|
||||
writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force restart compensation and enable auto load
|
||||
* for Comzc_Tx, Comzc_Rx and Comsr on macro
|
||||
*/
|
||||
val = START_COMSR | START_COMZC | COMP_AUTO_LOAD;
|
||||
writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG);
|
||||
|
||||
mask = COMSR_DONE | COMZC_DONE;
|
||||
while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
/*
|
||||
* SSC Settings. SSC will be enabled through Link
|
||||
* SSC Ampl. = 0.4%
|
||||
* SSC Freq = 31KHz
|
||||
*/
|
||||
writeb_relaxed(PLL_SSC_STEP_MSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_STEP_MSB_REG);
|
||||
writeb_relaxed(PLL_SSC_STEP_LSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_STEP_LSB_REG);
|
||||
writeb_relaxed(PLL_SSC_PER_MSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_PER_MSB_REG);
|
||||
writeb_relaxed(PLL_SSC_PER_LSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_PER_LSB_REG);
|
||||
|
||||
/* SSC Settings complete */
|
||||
if (miphy_phy->sata_gen == SATA_GEN1) {
|
||||
val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
|
||||
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
|
||||
} else {
|
||||
val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
|
||||
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
/*
|
||||
* Force PHY macro reset, PLL calibration reset, PLL reset
|
||||
* and assert Deserializer Reset
|
||||
*/
|
||||
val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
|
||||
writeb_relaxed(val, miphy_phy->base + RESET_REG);
|
||||
|
||||
if (miphy_phy->sata_tx_pol_inv)
|
||||
writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
|
||||
|
||||
/*
|
||||
* Force macro1 to use rx_lspd, tx_lspd
|
||||
* Force Rx_Clock on first I-DLL phase
|
||||
* Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3
|
||||
*/
|
||||
writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
|
||||
writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
|
||||
val = rx_tx_spd[miphy_phy->sata_gen];
|
||||
writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
|
||||
|
||||
/* Wait for HFC_READY = 0 */
|
||||
ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Compensation Recalibration */
|
||||
miphy365x_set_comp(miphy_phy, miphy_dev);
|
||||
|
||||
switch (miphy_phy->sata_gen) {
|
||||
case SATA_GEN3:
|
||||
/*
|
||||
* TX Swing target 550-600mv peak to peak diff
|
||||
* Tx Slew target 90-110ps rising/falling time
|
||||
* Rx Eq ON3, Sigdet threshold SDTH1
|
||||
*/
|
||||
val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3;
|
||||
writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG);
|
||||
val = SWING_VAL | PREEMPH_VAL;
|
||||
writeb_relaxed(val, miphy_phy->base + TXBUF1_REG);
|
||||
writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG);
|
||||
val = SDTHRES_VAL | EQ_ON3;
|
||||
writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
|
||||
break;
|
||||
case SATA_GEN2:
|
||||
/*
|
||||
* conf gen sel=0x1 to program Gen2 banked registers
|
||||
* VDDT filter ON
|
||||
* Tx Swing target 550-600mV peak-to-peak diff
|
||||
* Tx Slew target 90-110 ps rising/falling time
|
||||
* RX Equalization ON1, Sigdet threshold SDTH1
|
||||
*/
|
||||
writeb_relaxed(CONF_GEN_SEL_GEN2,
|
||||
miphy_phy->base + BUF_SEL_REG);
|
||||
writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG);
|
||||
writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
|
||||
val = SDTHRES_VAL | EQ_ON1;
|
||||
writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
|
||||
break;
|
||||
case SATA_GEN1:
|
||||
/*
|
||||
* conf gen sel = 00b to program Gen1 banked registers
|
||||
* VDDT filter ON
|
||||
* Tx Swing target 500-550mV peak-to-peak diff
|
||||
* Tx Slew target120-140 ps rising/falling time
|
||||
*/
|
||||
writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG);
|
||||
writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG);
|
||||
writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Force Macro1 in partial mode & release pll cal reset */
|
||||
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
|
||||
usleep_range(100, 150);
|
||||
|
||||
miphy365x_set_ssc(miphy_phy, miphy_dev);
|
||||
|
||||
/* Wait for phy_ready */
|
||||
ret = miphy365x_rdy(miphy_phy, miphy_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable macro1 to use rx_lspd & tx_lspd
|
||||
* Release Rx_Clock on first I-DLL phase on macro1
|
||||
* Assert deserializer reset
|
||||
* des_bit_lock_en is set
|
||||
* bit lock detection strength
|
||||
* Deassert deserializer reset
|
||||
*/
|
||||
writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
|
||||
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
|
||||
val = miphy_phy->sata_tx_pol_inv ?
|
||||
(TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
|
||||
writeb_relaxed(val, miphy_phy->base + CTRL_REG);
|
||||
|
||||
val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL;
|
||||
writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + RESET_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int miphy365x_init(struct phy *phy)
|
||||
{
|
||||
struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy);
|
||||
struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&miphy_dev->miphy_mutex);
|
||||
|
||||
ret = miphy365x_set_path(miphy_phy, miphy_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&miphy_dev->miphy_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialise Miphy for PCIe or SATA */
|
||||
if (miphy_phy->type == MIPHY_TYPE_PCIE)
|
||||
ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
|
||||
else
|
||||
ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
|
||||
|
||||
mutex_unlock(&miphy_dev->miphy_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
|
||||
int index)
|
||||
{
|
||||
struct device_node *phynode = miphy_phy->phy->dev.of_node;
|
||||
const char *name;
|
||||
const __be32 *taddr;
|
||||
int type = miphy_phy->type;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_string_index(phynode, "reg-names", index, &name);
|
||||
if (ret) {
|
||||
dev_err(dev, "no reg-names property not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!strncmp(name, "syscfg", 6)) {
|
||||
taddr = of_get_address(phynode, index, NULL, NULL);
|
||||
if (!taddr) {
|
||||
dev_err(dev, "failed to fetch syscfg address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
|
||||
if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
|
||||
dev_err(dev, "failed to translate syscfg address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
|
||||
(!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
|
||||
return 0;
|
||||
|
||||
miphy_phy->base = of_iomap(phynode, index);
|
||||
if (!miphy_phy->base) {
|
||||
dev_err(dev, "Failed to map %s\n", phynode->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy *miphy365x_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev);
|
||||
struct miphy365x_phy *miphy_phy = NULL;
|
||||
struct device_node *phynode = args->np;
|
||||
int ret, index;
|
||||
|
||||
if (!of_device_is_available(phynode)) {
|
||||
dev_warn(dev, "Requested PHY is disabled\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (args->args_count != 1) {
|
||||
dev_err(dev, "Invalid number of cells in 'phy' property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
for (index = 0; index < of_get_child_count(dev->of_node); index++)
|
||||
if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
|
||||
miphy_phy = miphy_dev->phys[index];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!miphy_phy) {
|
||||
dev_err(dev, "Failed to find appropriate phy\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
miphy_phy->type = args->args[0];
|
||||
|
||||
if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
|
||||
miphy_phy->type == MIPHY_TYPE_PCIE)) {
|
||||
dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* Each port handles SATA and PCIE - third entry is always sysconf. */
|
||||
for (index = 0; index < 3; index++) {
|
||||
ret = miphy365x_get_addr(dev, miphy_phy, index);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return miphy_phy->phy;
|
||||
}
|
||||
|
||||
static struct phy_ops miphy365x_ops = {
|
||||
.init = miphy365x_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int miphy365x_of_probe(struct device_node *phynode,
|
||||
struct miphy365x_phy *miphy_phy)
|
||||
{
|
||||
of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen);
|
||||
if (!miphy_phy->sata_gen)
|
||||
miphy_phy->sata_gen = SATA_GEN1;
|
||||
|
||||
miphy_phy->pcie_tx_pol_inv =
|
||||
of_property_read_bool(phynode, "st,pcie-tx-pol-inv");
|
||||
|
||||
miphy_phy->sata_tx_pol_inv =
|
||||
of_property_read_bool(phynode, "st,sata-tx-pol-inv");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int miphy365x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *child, *np = pdev->dev.of_node;
|
||||
struct miphy365x_dev *miphy_dev;
|
||||
struct phy_provider *provider;
|
||||
struct phy *phy;
|
||||
int chancount, port = 0;
|
||||
int ret;
|
||||
|
||||
miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
|
||||
if (!miphy_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
chancount = of_get_child_count(np);
|
||||
miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
|
||||
GFP_KERNEL);
|
||||
if (!miphy_dev->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
||||
if (IS_ERR(miphy_dev->regmap)) {
|
||||
dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
|
||||
return PTR_ERR(miphy_dev->regmap);
|
||||
}
|
||||
|
||||
miphy_dev->dev = &pdev->dev;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, miphy_dev);
|
||||
|
||||
mutex_init(&miphy_dev->miphy_mutex);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct miphy365x_phy *miphy_phy;
|
||||
|
||||
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
|
||||
GFP_KERNEL);
|
||||
if (!miphy_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
miphy_dev->phys[port] = miphy_phy;
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(&pdev->dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
miphy_dev->phys[port]->phy = phy;
|
||||
|
||||
ret = miphy365x_of_probe(child, miphy_phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_set_drvdata(phy, miphy_dev->phys[port]);
|
||||
port++;
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
|
||||
if (IS_ERR(provider))
|
||||
return PTR_ERR(provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id miphy365x_of_match[] = {
|
||||
{ .compatible = "st,miphy365x-phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, miphy365x_of_match);
|
||||
|
||||
static struct platform_driver miphy365x_driver = {
|
||||
.probe = miphy365x_probe,
|
||||
.driver = {
|
||||
.name = "miphy365x-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = miphy365x_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(miphy365x_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics miphy365x driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
|
@ -26,6 +26,41 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
|
||||
/**
|
||||
* omap_control_pcie_pcs - set the PCS delay count
|
||||
* @dev: the control module device
|
||||
* @id: index of the pcie PHY (should be 1 or 2)
|
||||
* @delay: 8 bit delay value
|
||||
*/
|
||||
void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
|
||||
{
|
||||
u32 val;
|
||||
struct omap_control_phy *control_phy;
|
||||
|
||||
if (IS_ERR(dev) || !dev) {
|
||||
pr_err("%s: invalid device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
control_phy = dev_get_drvdata(dev);
|
||||
if (!control_phy) {
|
||||
dev_err(dev, "%s: invalid control phy device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (control_phy->type != OMAP_CTRL_TYPE_PCIE) {
|
||||
dev_err(dev, "%s: unsupported operation\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
val = readl(control_phy->pcie_pcs);
|
||||
val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
|
||||
(id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT));
|
||||
val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
|
||||
writel(val, control_phy->pcie_pcs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
|
||||
|
||||
/**
|
||||
* omap_control_phy_power - power on/off the phy using control module reg
|
||||
* @dev: the control module device
|
||||
@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on)
|
||||
val |= OMAP_CTRL_DEV_PHY_PD;
|
||||
break;
|
||||
|
||||
case OMAP_CTRL_TYPE_PCIE:
|
||||
case OMAP_CTRL_TYPE_PIPE3:
|
||||
rate = clk_get_rate(control_phy->sys_clk);
|
||||
rate = rate/1000000;
|
||||
@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
|
||||
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 pcie_data = OMAP_CTRL_TYPE_PCIE;
|
||||
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;
|
||||
|
||||
@ -227,6 +264,10 @@ static const struct of_device_id omap_control_phy_id_table[] = {
|
||||
.compatible = "ti,control-phy-pipe3",
|
||||
.data = &pipe3_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,control-phy-pcie",
|
||||
.data = &pcie_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,control-phy-usb2-dra7",
|
||||
.data = &dra7usb2_data,
|
||||
@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 ||
|
||||
control_phy->type == OMAP_CTRL_TYPE_PCIE) {
|
||||
control_phy->sys_clk = devm_clk_get(control_phy->dev,
|
||||
"sys_clkin");
|
||||
if (IS_ERR(control_phy->sys_clk)) {
|
||||
@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PCIE) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pcie_pcs");
|
||||
control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(control_phy->pcie_pcs))
|
||||
return PTR_ERR(control_phy->pcie_pcs);
|
||||
}
|
||||
|
||||
dev_set_drvdata(control_phy->dev, control_phy);
|
||||
|
||||
return 0;
|
||||
|
@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
|
289
drivers/phy/phy-qcom-apq8064-sata.c
Normal file
289
drivers/phy/phy-qcom-apq8064-sata.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
/* PHY registers */
|
||||
#define UNIPHY_PLL_REFCLK_CFG 0x000
|
||||
#define UNIPHY_PLL_PWRGEN_CFG 0x014
|
||||
#define UNIPHY_PLL_GLB_CFG 0x020
|
||||
#define UNIPHY_PLL_SDM_CFG0 0x038
|
||||
#define UNIPHY_PLL_SDM_CFG1 0x03C
|
||||
#define UNIPHY_PLL_SDM_CFG2 0x040
|
||||
#define UNIPHY_PLL_SDM_CFG3 0x044
|
||||
#define UNIPHY_PLL_SDM_CFG4 0x048
|
||||
#define UNIPHY_PLL_SSC_CFG0 0x04C
|
||||
#define UNIPHY_PLL_SSC_CFG1 0x050
|
||||
#define UNIPHY_PLL_SSC_CFG2 0x054
|
||||
#define UNIPHY_PLL_SSC_CFG3 0x058
|
||||
#define UNIPHY_PLL_LKDET_CFG0 0x05C
|
||||
#define UNIPHY_PLL_LKDET_CFG1 0x060
|
||||
#define UNIPHY_PLL_LKDET_CFG2 0x064
|
||||
#define UNIPHY_PLL_CAL_CFG0 0x06C
|
||||
#define UNIPHY_PLL_CAL_CFG8 0x08C
|
||||
#define UNIPHY_PLL_CAL_CFG9 0x090
|
||||
#define UNIPHY_PLL_CAL_CFG10 0x094
|
||||
#define UNIPHY_PLL_CAL_CFG11 0x098
|
||||
#define UNIPHY_PLL_STATUS 0x0C0
|
||||
|
||||
#define SATA_PHY_SER_CTRL 0x100
|
||||
#define SATA_PHY_TX_DRIV_CTRL0 0x104
|
||||
#define SATA_PHY_TX_DRIV_CTRL1 0x108
|
||||
#define SATA_PHY_TX_IMCAL0 0x11C
|
||||
#define SATA_PHY_TX_IMCAL2 0x124
|
||||
#define SATA_PHY_RX_IMCAL0 0x128
|
||||
#define SATA_PHY_EQUAL 0x13C
|
||||
#define SATA_PHY_OOB_TERM 0x144
|
||||
#define SATA_PHY_CDR_CTRL0 0x148
|
||||
#define SATA_PHY_CDR_CTRL1 0x14C
|
||||
#define SATA_PHY_CDR_CTRL2 0x150
|
||||
#define SATA_PHY_CDR_CTRL3 0x154
|
||||
#define SATA_PHY_PI_CTRL0 0x168
|
||||
#define SATA_PHY_POW_DWN_CTRL0 0x180
|
||||
#define SATA_PHY_POW_DWN_CTRL1 0x184
|
||||
#define SATA_PHY_TX_DATA_CTRL 0x188
|
||||
#define SATA_PHY_ALIGNP 0x1A4
|
||||
#define SATA_PHY_TX_IMCAL_STAT 0x1E4
|
||||
#define SATA_PHY_RX_IMCAL_STAT 0x1E8
|
||||
|
||||
#define UNIPHY_PLL_LOCK BIT(0)
|
||||
#define SATA_PHY_TX_CAL BIT(0)
|
||||
#define SATA_PHY_RX_CAL BIT(0)
|
||||
|
||||
/* default timeout set to 1 sec */
|
||||
#define TIMEOUT_MS 10000
|
||||
#define DELAY_INTERVAL_US 100
|
||||
|
||||
struct qcom_apq8064_sata_phy {
|
||||
void __iomem *mmio;
|
||||
struct clk *cfg_clk;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/* Helper function to do poll and timeout */
|
||||
static int read_poll_timeout(void __iomem *addr, u32 mask)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
|
||||
|
||||
do {
|
||||
if (readl_relaxed(addr) & mask)
|
||||
return 0;
|
||||
|
||||
usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
void __iomem *base = phy->mmio;
|
||||
int ret = 0;
|
||||
|
||||
/* SATA phy initialization */
|
||||
writel_relaxed(0x01, base + SATA_PHY_SER_CTRL);
|
||||
writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
/* Make sure the power down happens before power up */
|
||||
mb();
|
||||
usleep_range(10, 60);
|
||||
|
||||
writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
|
||||
writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
|
||||
writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2);
|
||||
|
||||
/* Write UNIPHYPLL registers to configure PLL */
|
||||
writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG);
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG);
|
||||
|
||||
writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0);
|
||||
writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8);
|
||||
writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9);
|
||||
writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10);
|
||||
writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11);
|
||||
|
||||
writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0);
|
||||
writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1);
|
||||
writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2);
|
||||
writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3);
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4);
|
||||
|
||||
writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0);
|
||||
writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1);
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2);
|
||||
writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3);
|
||||
|
||||
writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0);
|
||||
writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1);
|
||||
|
||||
writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG);
|
||||
/* make sure global config LDO power down happens before power up */
|
||||
mb();
|
||||
|
||||
writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG);
|
||||
writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
|
||||
|
||||
/* PLL Lock wait */
|
||||
ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TX Calibration */
|
||||
ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* RX Calibration */
|
||||
ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* SATA phy calibrated succesfully, power up to functional mode */
|
||||
writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
|
||||
writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
|
||||
|
||||
writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0);
|
||||
writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1);
|
||||
writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2);
|
||||
writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0);
|
||||
writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3);
|
||||
writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
|
||||
writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL);
|
||||
writel_relaxed(0x43, base + SATA_PHY_ALIGNP);
|
||||
writel_relaxed(0x04, base + SATA_PHY_OOB_TERM);
|
||||
|
||||
writel_relaxed(0x01, base + SATA_PHY_EQUAL);
|
||||
writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0);
|
||||
writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
void __iomem *base = phy->mmio;
|
||||
|
||||
/* Power down PHY */
|
||||
writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
|
||||
/* Power down PLL block */
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops qcom_apq8064_sata_phy_ops = {
|
||||
.init = qcom_apq8064_sata_phy_init,
|
||||
.exit = qcom_apq8064_sata_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy *generic_phy;
|
||||
int ret;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "%s: failed to create phy\n", __func__);
|
||||
return PTR_ERR(generic_phy);
|
||||
}
|
||||
|
||||
phy->dev = dev;
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
phy->cfg_clk = devm_clk_get(dev, "cfg");
|
||||
if (IS_ERR(phy->cfg_clk)) {
|
||||
dev_err(dev, "Failed to get sata cfg clock\n");
|
||||
return PTR_ERR(phy->cfg_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(phy->cfg_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
dev_err(dev, "%s: failed to register phy\n", __func__);
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = {
|
||||
{ .compatible = "qcom,apq8064-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver qcom_apq8064_sata_phy_driver = {
|
||||
.probe = qcom_apq8064_sata_phy_probe,
|
||||
.remove = qcom_apq8064_sata_phy_remove,
|
||||
.driver = {
|
||||
.name = "qcom-apq8064-sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = qcom_apq8064_sata_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(qcom_apq8064_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
211
drivers/phy/phy-qcom-ipq806x-sata.c
Normal file
211
drivers/phy/phy-qcom-ipq806x-sata.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
struct qcom_ipq806x_sata_phy {
|
||||
void __iomem *mmio;
|
||||
struct clk *cfg_clk;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
|
||||
|
||||
#define SATA_PHY_P0_PARAM0 0x200
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
|
||||
|
||||
#define SATA_PHY_P0_PARAM1 0x204
|
||||
#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
|
||||
|
||||
#define SATA_PHY_P0_PARAM2 0x208
|
||||
#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
|
||||
#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
|
||||
|
||||
#define SATA_PHY_P0_PARAM3 0x20C
|
||||
#define SATA_PHY_SSC_EN 0x8
|
||||
#define SATA_PHY_P0_PARAM4 0x210
|
||||
#define SATA_PHY_REF_SSP_EN 0x2
|
||||
#define SATA_PHY_RESET 0x1
|
||||
|
||||
static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
u32 reg;
|
||||
|
||||
/* Setting SSC_EN to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
|
||||
reg = reg | SATA_PHY_SSC_EN;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
|
||||
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
|
||||
~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
|
||||
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
|
||||
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
|
||||
reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
|
||||
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
|
||||
~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
|
||||
reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
|
||||
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
|
||||
~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
|
||||
reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
|
||||
|
||||
/* Setting PHY_RESET to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg | SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
/* Setting REF_SSP_EN to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
/* make sure all changes complete before we let the PHY out of reset */
|
||||
mb();
|
||||
|
||||
/* sleep for max. 50us more to combine processor wakeups */
|
||||
usleep_range(20, 20 + 50);
|
||||
|
||||
/* Clearing PHY_RESET to 0 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg & ~SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
u32 reg;
|
||||
|
||||
/* Setting PHY_RESET to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg | SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops qcom_ipq806x_sata_phy_ops = {
|
||||
.init = qcom_ipq806x_sata_phy_init,
|
||||
.exit = qcom_ipq806x_sata_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy *generic_phy;
|
||||
int ret;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "%s: failed to create phy\n", __func__);
|
||||
return PTR_ERR(generic_phy);
|
||||
}
|
||||
|
||||
phy->dev = dev;
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
phy->cfg_clk = devm_clk_get(dev, "cfg");
|
||||
if (IS_ERR(phy->cfg_clk)) {
|
||||
dev_err(dev, "Failed to get sata cfg clock\n");
|
||||
return PTR_ERR(phy->cfg_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(phy->cfg_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
dev_err(dev, "%s: failed to register phy\n", __func__);
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
|
||||
{ .compatible = "qcom,ipq806x-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver qcom_ipq806x_sata_phy_driver = {
|
||||
.probe = qcom_ipq806x_sata_phy_probe,
|
||||
.remove = qcom_ipq806x_sata_phy_remove,
|
||||
.driver = {
|
||||
.name = "qcom-ipq806x-sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = qcom_ipq806x_sata_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(qcom_ipq806x_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -87,6 +87,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev,
|
||||
}
|
||||
|
||||
static const struct of_device_id samsung_usb2_phy_of_match[] = {
|
||||
#ifdef CONFIG_PHY_EXYNOS4X12_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos3250-usb2-phy",
|
||||
.data = &exynos3250_usb2_phy_config,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_PHY_EXYNOS4210_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos4210-usb2-phy",
|
||||
@ -190,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
|
||||
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);
|
||||
p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(p->phy)) {
|
||||
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
|
||||
label);
|
||||
|
@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance {
|
||||
const struct samsung_usb2_common_phy *cfg;
|
||||
struct phy *phy;
|
||||
struct samsung_usb2_phy_driver *drv;
|
||||
bool enabled;
|
||||
int int_cnt;
|
||||
int ext_cnt;
|
||||
};
|
||||
|
||||
struct samsung_usb2_phy_driver {
|
||||
@ -59,8 +60,10 @@ struct samsung_usb2_phy_config {
|
||||
int (*rate_to_clk)(unsigned long, u32 *);
|
||||
unsigned int num_phys;
|
||||
bool has_mode_switch;
|
||||
bool has_refclk_sel;
|
||||
};
|
||||
|
||||
extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config;
|
||||
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;
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -294,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(phy->pmu);
|
||||
}
|
||||
|
||||
phy->phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
|
||||
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
|
||||
if (IS_ERR(phy->phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
return PTR_ERR(phy->phy);
|
||||
@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
|
||||
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;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
|
@ -80,7 +80,9 @@ struct ti_pipe3 {
|
||||
struct clk *wkupclk;
|
||||
struct clk *sys_clk;
|
||||
struct clk *refclk;
|
||||
struct clk *div_clk;
|
||||
struct pipe3_dpll_map *dpll_map;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static struct pipe3_dpll_map dpll_map_usb[] = {
|
||||
@ -215,6 +217,11 @@ static int ti_pipe3_init(struct phy *x)
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
|
||||
omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bring it out of IDLE if it is IDLE */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
if (val & PLL_IDLE) {
|
||||
@ -238,8 +245,11 @@ static int ti_pipe3_exit(struct phy *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"))
|
||||
/* SATA DPLL can't be powered down due to Errata i783 and PCIe
|
||||
* does not have internal DPLL
|
||||
*/
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") ||
|
||||
of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie"))
|
||||
return 0;
|
||||
|
||||
/* Put DPLL in IDLE mode */
|
||||
@ -286,32 +296,41 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
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;
|
||||
struct clk *clk;
|
||||
|
||||
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")) {
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
match = of_match_device(of_match_ptr(ti_pipe3_id_table),
|
||||
&pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
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->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;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
@ -328,10 +347,38 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
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;
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
if (of_property_read_u8(node, "id", &phy->id) < 0)
|
||||
phy->id = 1;
|
||||
|
||||
clk = devm_clk_get(phy->dev, "dpll_ref");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get dpll ref clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 1500000000);
|
||||
|
||||
clk = devm_clk_get(phy->dev, "dpll_ref_m2");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 100000000);
|
||||
|
||||
clk = devm_clk_get(phy->dev, "phy-div");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get phy-div clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 100000000);
|
||||
|
||||
phy->div_clk = devm_clk_get(phy->dev, "div-clk");
|
||||
if (IS_ERR(phy->div_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get div-clk\n");
|
||||
return PTR_ERR(phy->div_clk);
|
||||
}
|
||||
} else {
|
||||
phy->div_clk = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
@ -353,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
@ -387,6 +434,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev)
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
if (!IS_ERR(phy->div_clk))
|
||||
clk_disable_unprepare(phy->div_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -412,8 +461,19 @@ static int ti_pipe3_runtime_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(phy->div_clk)) {
|
||||
ret = clk_prepare_enable(phy->div_clk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "Failed to enable div_clk %d\n", ret);
|
||||
goto err3;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
if (!IS_ERR(phy->wkupclk))
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
|
||||
err2:
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
@ -446,6 +506,9 @@ static const struct of_device_id ti_pipe3_id_table[] = {
|
||||
.compatible = "ti,phy-pipe3-sata",
|
||||
.data = dpll_map_sata,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,phy-pipe3-pcie",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
|
||||
|
@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
otg->set_host = twl4030_set_host;
|
||||
otg->set_peripheral = twl4030_set_peripheral;
|
||||
|
||||
phy = devm_phy_create(twl->dev, &ops, init_data);
|
||||
phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
|
@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
|
||||
ctx->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL);
|
||||
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
|
||||
if (IS_ERR(ctx->phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
rc = PTR_ERR(ctx->phy);
|
||||
|
@ -133,6 +133,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
|
||||
if (IS_ERR(data->phy)) {
|
||||
ret = PTR_ERR(data->phy);
|
||||
/* Return -EINVAL if no usbphy is available */
|
||||
if (ret == -ENODEV)
|
||||
ret = -EINVAL;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int ci_otg_show(struct seq_file *s, void *unused)
|
||||
static int ci_otg_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
struct otg_fsm *fsm;
|
||||
@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int ci_registers_show(struct seq_file *s, void *unused)
|
||||
static int ci_registers_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
u32 tmp_reg;
|
||||
|
@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
|
||||
u8 *buffer;
|
||||
int rv;
|
||||
int n;
|
||||
int actual;
|
||||
int actual = 0;
|
||||
int max_size;
|
||||
|
||||
dev = &data->intf->dev;
|
||||
|
@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
if (n == 0)
|
||||
n = 9; /* 32 ms = 2^(9-1) uframes */
|
||||
j = 16;
|
||||
|
||||
/*
|
||||
* Adjust bInterval for quirked devices.
|
||||
* This quirk fixes bIntervals reported in
|
||||
* linear microframes.
|
||||
*/
|
||||
if (to_usb_device(ddev)->quirks &
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) {
|
||||
n = clamp(fls(d->bInterval), i, j);
|
||||
i = j = n;
|
||||
}
|
||||
break;
|
||||
default: /* USB_SPEED_FULL or _LOW */
|
||||
/* For low-speed, 10 ms is the official minimum.
|
||||
|
@ -1509,7 +1509,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
||||
u = (is_in ? URB_DIR_IN : URB_DIR_OUT);
|
||||
if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
|
||||
u |= URB_ISO_ASAP;
|
||||
if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
|
||||
if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in)
|
||||
u |= URB_SHORT_NOT_OK;
|
||||
if (uurb->flags & USBDEVFS_URB_NO_FSBR)
|
||||
u |= URB_NO_FSBR;
|
||||
|
@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev)
|
||||
*/
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
|
||||
/* Terminate all URBs for this interface unless the driver
|
||||
* supports "soft" unbinding.
|
||||
/*
|
||||
* Terminate all URBs for this interface unless the driver
|
||||
* supports "soft" unbinding and the device is still present.
|
||||
*/
|
||||
if (!driver->soft_unbind)
|
||||
if (!driver->soft_unbind || udev->state == USB_STATE_NOTATTACHED)
|
||||
usb_disable_interface(udev, intf, false);
|
||||
|
||||
driver->disconnect(intf);
|
||||
|
@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
|
||||
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
|
||||
hcd->driver->shutdown) {
|
||||
hcd->driver->shutdown(hcd);
|
||||
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
|
||||
free_irq(hcd->irq, hcd);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
}
|
||||
|
@ -855,8 +855,6 @@ static ssize_t authorized_default_show(struct device *dev,
|
||||
struct usb_bus *usb_bus = rh_usb_dev->bus;
|
||||
struct usb_hcd *usb_hcd;
|
||||
|
||||
if (usb_bus == NULL) /* FIXME: not sure if this case is possible */
|
||||
return -ENODEV;
|
||||
usb_hcd = bus_to_hcd(usb_bus);
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default);
|
||||
}
|
||||
@ -871,8 +869,6 @@ static ssize_t authorized_default_store(struct device *dev,
|
||||
struct usb_bus *usb_bus = rh_usb_dev->bus;
|
||||
struct usb_hcd *usb_hcd;
|
||||
|
||||
if (usb_bus == NULL) /* FIXME: not sure if this case is possible */
|
||||
return -ENODEV;
|
||||
usb_hcd = bus_to_hcd(usb_bus);
|
||||
result = sscanf(buf, "%u\n", &val);
|
||||
if (result == 1) {
|
||||
|
@ -2606,13 +2606,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
|
||||
* Port worm reset is required to recover
|
||||
*/
|
||||
static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
|
||||
static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
|
||||
u16 portstatus)
|
||||
{
|
||||
return hub_is_superspeed(hub->hdev) &&
|
||||
(((portstatus & USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_SS_INACTIVE) ||
|
||||
((portstatus & USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_COMP_MOD)) ;
|
||||
u16 link_state;
|
||||
|
||||
if (!hub_is_superspeed(hub->hdev))
|
||||
return false;
|
||||
|
||||
if (test_bit(port1, hub->warm_reset_bits))
|
||||
return true;
|
||||
|
||||
link_state = portstatus & USB_PORT_STAT_LINK_STATE;
|
||||
return link_state == USB_SS_PORT_LS_SS_INACTIVE
|
||||
|| link_state == USB_SS_PORT_LS_COMP_MOD;
|
||||
}
|
||||
|
||||
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
||||
@ -2649,7 +2656,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
||||
if ((portstatus & USB_PORT_STAT_RESET))
|
||||
return -EBUSY;
|
||||
|
||||
if (hub_port_warm_reset_required(hub, portstatus))
|
||||
if (hub_port_warm_reset_required(hub, port1, portstatus))
|
||||
return -ENOTCONN;
|
||||
|
||||
/* Device went away? */
|
||||
@ -2749,9 +2756,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
if (status < 0)
|
||||
goto done;
|
||||
|
||||
if (hub_port_warm_reset_required(hub, portstatus))
|
||||
if (hub_port_warm_reset_required(hub, port1, portstatus))
|
||||
warm = true;
|
||||
}
|
||||
clear_bit(port1, hub->warm_reset_bits);
|
||||
|
||||
/* Reset the port */
|
||||
for (i = 0; i < PORT_RESET_TRIES; i++) {
|
||||
@ -2788,7 +2796,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
&portstatus, &portchange) < 0)
|
||||
goto done;
|
||||
|
||||
if (!hub_port_warm_reset_required(hub, portstatus))
|
||||
if (!hub_port_warm_reset_required(hub, port1,
|
||||
portstatus))
|
||||
goto done;
|
||||
|
||||
/*
|
||||
@ -2875,8 +2884,13 @@ static int check_port_resume_type(struct usb_device *udev,
|
||||
{
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1];
|
||||
|
||||
/* Is a warm reset needed to recover the connection? */
|
||||
if (status == 0 && udev->reset_resume
|
||||
&& hub_port_warm_reset_required(hub, port1, portstatus)) {
|
||||
/* pass */;
|
||||
}
|
||||
/* Is the device still present? */
|
||||
if (status || port_is_suspended(hub, portstatus) ||
|
||||
else if (status || port_is_suspended(hub, portstatus) ||
|
||||
!port_is_power_on(hub, portstatus) ||
|
||||
!(portstatus & USB_PORT_STAT_CONNECTION)) {
|
||||
if (status >= 0)
|
||||
@ -3263,6 +3277,43 @@ static int finish_port_resume(struct usb_device *udev)
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are some SS USB devices which take longer time for link training.
|
||||
* XHCI specs 4.19.4 says that when Link training is successful, port
|
||||
* sets CSC bit to 1. So if SW reads port status before successful link
|
||||
* training, then it will not find device to be present.
|
||||
* USB Analyzer log with such buggy devices show that in some cases
|
||||
* device switch on the RX termination after long delay of host enabling
|
||||
* the VBUS. In few other cases it has been seen that device fails to
|
||||
* negotiate link training in first attempt. It has been
|
||||
* reported till now that few devices take as long as 2000 ms to train
|
||||
* the link after host enabling its VBUS and termination. Following
|
||||
* routine implements a 2000 ms timeout for link training. If in a case
|
||||
* link trains before timeout, loop will exit earlier.
|
||||
*
|
||||
* FIXME: If a device was connected before suspend, but was removed
|
||||
* while system was asleep, then the loop in the following routine will
|
||||
* only exit at timeout.
|
||||
*
|
||||
* This routine should only be called when persist is enabled for a SS
|
||||
* device.
|
||||
*/
|
||||
static int wait_for_ss_port_enable(struct usb_device *udev,
|
||||
struct usb_hub *hub, int *port1,
|
||||
u16 *portchange, u16 *portstatus)
|
||||
{
|
||||
int status = 0, delay_ms = 0;
|
||||
|
||||
while (delay_ms < 2000) {
|
||||
if (status || *portstatus & USB_PORT_STAT_CONNECTION)
|
||||
break;
|
||||
msleep(20);
|
||||
delay_ms += 20;
|
||||
status = hub_port_status(hub, *port1, portstatus, portchange);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_port_resume - re-activate a suspended usb device's upstream port
|
||||
* @udev: device to re-activate, not a root hub
|
||||
@ -3359,6 +3410,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
}
|
||||
}
|
||||
|
||||
if (udev->persist_enabled && hub_is_superspeed(hub->hdev))
|
||||
status = wait_for_ss_port_enable(udev, hub, &port1, &portchange,
|
||||
&portstatus);
|
||||
|
||||
status = check_port_resume_type(udev,
|
||||
hub, port1, status, portchange, portstatus);
|
||||
if (status == 0)
|
||||
@ -3879,7 +3934,8 @@ int usb_disable_lpm(struct usb_device *udev)
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable)
|
||||
!udev->lpm_capable ||
|
||||
udev->state < USB_STATE_DEFAULT)
|
||||
return 0;
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
@ -3935,7 +3991,8 @@ void usb_enable_lpm(struct usb_device *udev)
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable)
|
||||
!udev->lpm_capable ||
|
||||
udev->state < USB_STATE_DEFAULT)
|
||||
return;
|
||||
|
||||
udev->lpm_disable_count--;
|
||||
@ -4550,6 +4607,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1];
|
||||
struct usb_device *udev = port_dev->child;
|
||||
static int unreliable_port = -1;
|
||||
|
||||
/* Disconnect any existing devices under this port */
|
||||
if (udev) {
|
||||
@ -4570,10 +4628,14 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
||||
USB_PORT_STAT_C_ENABLE)) {
|
||||
status = hub_port_debounce_be_stable(hub, port1);
|
||||
if (status < 0) {
|
||||
if (status != -ENODEV && printk_ratelimit())
|
||||
dev_err(&port_dev->dev,
|
||||
"connect-debounce failed\n");
|
||||
if (status != -ENODEV &&
|
||||
port1 != unreliable_port &&
|
||||
printk_ratelimit())
|
||||
dev_err(&udev->dev, "connect-debounce failed, port %d disabled\n",
|
||||
port1);
|
||||
|
||||
portstatus &= ~USB_PORT_STAT_CONNECTION;
|
||||
unreliable_port = port1;
|
||||
} else {
|
||||
portstatus = status;
|
||||
}
|
||||
@ -4889,7 +4951,7 @@ static void port_event(struct usb_hub *hub, int port1)
|
||||
* Warm reset a USB3 protocol port if it's in
|
||||
* SS.Inactive state.
|
||||
*/
|
||||
if (hub_port_warm_reset_required(hub, portstatus)) {
|
||||
if (hub_port_warm_reset_required(hub, port1, portstatus)) {
|
||||
dev_dbg(&port_dev->dev, "do warm reset\n");
|
||||
if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
|
||||
|| udev->state == USB_STATE_NOTATTACHED) {
|
||||
|
@ -52,6 +52,8 @@ struct usb_hub {
|
||||
unsigned long power_bits[1]; /* ports that are powered */
|
||||
unsigned long child_usage_bits[1]; /* ports powered on for
|
||||
children */
|
||||
unsigned long warm_reset_bits[1]; /* ports requesting warm
|
||||
reset recovery */
|
||||
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
||||
#error event_bits[] is too short!
|
||||
#endif
|
||||
|
@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev)
|
||||
msleep(hub_power_on_good_delay(hub));
|
||||
if (udev && !retval) {
|
||||
/*
|
||||
* Attempt to wait for usb hub port to be reconnected in order
|
||||
* to make the resume procedure successful. The device may have
|
||||
* disconnected while the port was powered off, so ignore the
|
||||
* return status.
|
||||
* Our preference is to simply wait for the port to reconnect,
|
||||
* as that is the lowest latency method to restart the port.
|
||||
* However, there are cases where toggling port power results in
|
||||
* the host port and the device port getting out of sync causing
|
||||
* a link training live lock. Upon timeout, flag the port as
|
||||
* needing warm reset recovery (to be performed later by
|
||||
* usb_port_resume() as requested via usb_wakeup_notification())
|
||||
*/
|
||||
retval = hub_port_debounce_be_connected(hub, port1);
|
||||
if (retval < 0)
|
||||
dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
|
||||
retval);
|
||||
retval = 0;
|
||||
if (hub_port_debounce_be_connected(hub, port1) < 0) {
|
||||
dev_dbg(&port_dev->dev, "reconnect timeout\n");
|
||||
if (hub_is_superspeed(hdev))
|
||||
set_bit(port1, hub->warm_reset_bits);
|
||||
}
|
||||
|
||||
/* Force the child awake to revalidate after the power loss. */
|
||||
if (!test_and_set_bit(port1, hub->child_usage_bits)) {
|
||||
|
@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* SKYMEDI USB_DRIVE */
|
||||
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Razer - Razer Blade Keyboard */
|
||||
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
|
||||
|
||||
/* BUILDWIN Photo Frame */
|
||||
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
|
||||
USB_QUIRK_HONOR_BNUMINTERFACES },
|
||||
@ -152,6 +156,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* INTEL VALUE SSD */
|
||||
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* USB3503 */
|
||||
{ USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
|
@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
URB_FREE_BUFFER);
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (is_out)
|
||||
allowed |= URB_ZERO_PACKET;
|
||||
/* FALLTHROUGH */
|
||||
|
@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_alloc_dev);
|
||||
|
||||
/**
|
||||
* usb_get_dev - increments the reference count of the usb device structure
|
||||
|
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* linux/drivers/usb/gadget/s3c-hsotg.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
@ -1022,7 +1020,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
*
|
||||
* Set stall for ep0 as response for setup request.
|
||||
*/
|
||||
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) {
|
||||
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
u32 reg;
|
||||
u32 ctrl;
|
||||
@ -1994,7 +1993,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||
s3c_hsotg_complete_request(hsotg, ep, req,
|
||||
result);
|
||||
}
|
||||
if(hsotg->dedicated_fifos)
|
||||
if (hsotg->dedicated_fifos)
|
||||
if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072)
|
||||
s3c_hsotg_txfifo_flush(hsotg, ep->index);
|
||||
}
|
||||
@ -3390,10 +3389,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
int i;
|
||||
|
||||
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
|
||||
if (!hsotg) {
|
||||
dev_err(dev, "cannot get memory\n");
|
||||
if (!hsotg)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to find a generic PHY, then look for an old style
|
||||
@ -3512,7 +3509,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep),
|
||||
GFP_KERNEL);
|
||||
if (!eps) {
|
||||
dev_err(dev, "cannot get memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_supplies;
|
||||
}
|
||||
|
@ -93,4 +93,11 @@ config USB_DWC3_VERBOSE
|
||||
help
|
||||
Say Y here to enable verbose debugging messages on DWC3 Driver.
|
||||
|
||||
config DWC3_HOST_USB3_LPM_ENABLE
|
||||
bool "Enable USB3 LPM Capability"
|
||||
depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
|
||||
default n
|
||||
help
|
||||
Select this when you want to enable USB3 LPM with dwc3 xhci host.
|
||||
|
||||
endif
|
||||
|
@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
}
|
||||
dwc->revision = reg;
|
||||
|
||||
/* Handle USB2.0-only core configuration */
|
||||
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
|
||||
if (dwc->maximum_speed == USB_SPEED_SUPER)
|
||||
dwc->maximum_speed = USB_SPEED_HIGH;
|
||||
}
|
||||
|
||||
/* issue device SoftReset too */
|
||||
timeout = jiffies + msecs_to_jiffies(500);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
|
||||
@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc->xhci_resources[0].start = res->start;
|
||||
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
|
||||
DWC3_XHCI_REGS_END;
|
||||
dwc->xhci_resources[0].flags = res->flags;
|
||||
dwc->xhci_resources[0].name = res->name;
|
||||
|
||||
res->start += DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/*
|
||||
* Request memory region but exclude xHCI regs,
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
*/
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
/*
|
||||
* restore res->start back to its original value so that,
|
||||
* in case the probe is deferred, we don't end up getting error in
|
||||
* request the memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
if (node) {
|
||||
dwc->maximum_speed = of_usb_get_maximum_speed(node);
|
||||
|
||||
@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dwc->xhci_resources[0].start = res->start;
|
||||
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
|
||||
DWC3_XHCI_REGS_END;
|
||||
dwc->xhci_resources[0].flags = res->flags;
|
||||
dwc->xhci_resources[0].name = res->name;
|
||||
|
||||
res->start += DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/*
|
||||
* Request memory region but exclude xHCI regs,
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
*/
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
|
||||
dev->dma_mask = dev->parent->dma_mask;
|
||||
dev->dma_parms = dev->parent->dma_parms;
|
||||
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
|
||||
|
@ -191,6 +191,19 @@
|
||||
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
|
||||
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
|
||||
|
||||
/* Global HWPARAMS3 Register */
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3
|
||||
#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4)
|
||||
#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1
|
||||
|
||||
/* Global HWPARAMS4 Register */
|
||||
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
|
||||
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
|
||||
|
@ -77,10 +77,6 @@
|
||||
#define USBOTGSS_DEV_EBC_EN 0x0110
|
||||
#define USBOTGSS_DEBUG_OFFSET 0x0600
|
||||
|
||||
/* REVISION REGISTER */
|
||||
#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7)
|
||||
#define USBOTGSS_REVISION_XMAJOR1 1
|
||||
#define USBOTGSS_REVISION_XMAJOR2 2
|
||||
/* SYSCONFIG REGISTER */
|
||||
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
|
||||
|
||||
@ -129,7 +125,6 @@ struct dwc3_omap {
|
||||
u32 irq_eoi_offset;
|
||||
u32 debug_offset;
|
||||
u32 irq0_offset;
|
||||
u32 revision;
|
||||
|
||||
u32 dma_status:1;
|
||||
|
||||
@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void dwc3_omap_map_offset(struct dwc3_omap *omap)
|
||||
{
|
||||
struct device_node *node = omap->dev->of_node;
|
||||
|
||||
/*
|
||||
* Differentiate between OMAP5 and AM437x.
|
||||
*
|
||||
* For OMAP5(ES2.0) and AM437x wrapper revision is same, even
|
||||
* though there are changes in wrapper register offsets.
|
||||
*
|
||||
* Using dt compatible to differentiate AM437x.
|
||||
*/
|
||||
if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
|
||||
omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
|
||||
omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
|
||||
omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
|
||||
omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
|
||||
omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap)
|
||||
{
|
||||
u32 reg;
|
||||
struct device_node *node = omap->dev->of_node;
|
||||
int utmi_mode = 0;
|
||||
|
||||
reg = dwc3_omap_read_utmi_status(omap);
|
||||
|
||||
of_property_read_u32(node, "utmi-mode", &utmi_mode);
|
||||
|
||||
switch (utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
|
||||
}
|
||||
|
||||
dwc3_omap_write_utmi_status(omap, reg);
|
||||
}
|
||||
|
||||
static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
|
||||
{
|
||||
u32 ret;
|
||||
struct device_node *node = omap->dev->of_node;
|
||||
struct extcon_dev *edev;
|
||||
|
||||
if (of_property_read_bool(node, "extcon")) {
|
||||
edev = extcon_get_edev_by_phandle(omap->dev, 0);
|
||||
if (IS_ERR(edev)) {
|
||||
dev_vdbg(omap->dev, "couldn't get extcon device\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
|
||||
ret = extcon_register_interest(&omap->extcon_vbus_dev,
|
||||
edev->name, "USB",
|
||||
&omap->vbus_nb);
|
||||
if (ret < 0)
|
||||
dev_vdbg(omap->dev, "failed to register notifier for USB\n");
|
||||
|
||||
omap->id_nb.notifier_call = dwc3_omap_id_notifier;
|
||||
ret = extcon_register_interest(&omap->extcon_id_dev,
|
||||
edev->name, "USB-HOST",
|
||||
&omap->id_nb);
|
||||
if (ret < 0)
|
||||
dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n");
|
||||
|
||||
if (extcon_get_cable_state(edev, "USB") == true)
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
|
||||
if (extcon_get_cable_state(edev, "USB-HOST") == true)
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct extcon_dev *edev;
|
||||
struct regulator *vbus_reg = NULL;
|
||||
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
int utmi_mode = 0;
|
||||
int x_major;
|
||||
|
||||
u32 reg;
|
||||
|
||||
void __iomem *base;
|
||||
@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
goto err0;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION);
|
||||
omap->revision = reg;
|
||||
x_major = USBOTGSS_REVISION_XMAJOR(reg);
|
||||
|
||||
/* Differentiate between OMAP5 and AM437x */
|
||||
switch (x_major) {
|
||||
case USBOTGSS_REVISION_XMAJOR1:
|
||||
case USBOTGSS_REVISION_XMAJOR2:
|
||||
omap->irq_eoi_offset = 0;
|
||||
omap->irq0_offset = 0;
|
||||
omap->irqmisc_offset = 0;
|
||||
omap->utmi_otg_offset = 0;
|
||||
omap->debug_offset = 0;
|
||||
break;
|
||||
default:
|
||||
/* Default to the latest revision */
|
||||
omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
|
||||
omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
|
||||
omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
|
||||
omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
|
||||
omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
|
||||
break;
|
||||
}
|
||||
|
||||
/* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are
|
||||
* changes in wrapper registers, Using dt compatible for aegis
|
||||
*/
|
||||
|
||||
if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
|
||||
omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
|
||||
omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
|
||||
omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
|
||||
omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
|
||||
omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_read_utmi_status(omap);
|
||||
|
||||
of_property_read_u32(node, "utmi-mode", &utmi_mode);
|
||||
|
||||
switch (utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
|
||||
}
|
||||
|
||||
dwc3_omap_write_utmi_status(omap, reg);
|
||||
dwc3_omap_map_offset(omap);
|
||||
dwc3_omap_set_utmi_mode(omap);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
if (of_property_read_bool(node, "extcon")) {
|
||||
edev = extcon_get_edev_by_phandle(dev, 0);
|
||||
if (IS_ERR(edev)) {
|
||||
dev_vdbg(dev, "couldn't get extcon device\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
|
||||
ret = extcon_register_interest(&omap->extcon_vbus_dev,
|
||||
edev->name, "USB", &omap->vbus_nb);
|
||||
if (ret < 0)
|
||||
dev_vdbg(dev, "failed to register notifier for USB\n");
|
||||
omap->id_nb.notifier_call = dwc3_omap_id_notifier;
|
||||
ret = extcon_register_interest(&omap->extcon_id_dev, edev->name,
|
||||
"USB-HOST", &omap->id_nb);
|
||||
if (ret < 0)
|
||||
dev_vdbg(dev,
|
||||
"failed to register notifier for USB-HOST\n");
|
||||
|
||||
if (extcon_get_cable_state(edev, "USB") == true)
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
|
||||
if (extcon_get_cable_state(edev, "USB-HOST") == true)
|
||||
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
|
||||
}
|
||||
ret = dwc3_omap_extcon_register(omap);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
|
@ -1971,8 +1971,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
}
|
||||
|
||||
static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
||||
struct dwc3_ep *dep, const struct dwc3_event_depevt *event,
|
||||
int start_new)
|
||||
struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
|
||||
{
|
||||
unsigned status = 0;
|
||||
int clean_busy;
|
||||
@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
return;
|
||||
}
|
||||
|
||||
dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
|
||||
dwc3_endpoint_transfer_complete(dwc, dep, event);
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
return;
|
||||
}
|
||||
|
||||
dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
|
||||
dwc3_endpoint_transfer_complete(dwc, dep, event);
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
|
@ -16,12 +16,14 @@
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/xhci_pdriver.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
struct usb_xhci_pdata pdata;
|
||||
int ret;
|
||||
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
|
||||
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
|
||||
pdata.usb3_lpm_capable = 1;
|
||||
#endif
|
||||
|
||||
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "couldn't add platform data to xHCI device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
ret = platform_device_add(xhci);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register xHCI device\n");
|
||||
|
@ -127,368 +127,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
|
||||
a module parameter as well.
|
||||
If unsure, say 2.
|
||||
|
||||
#
|
||||
# USB Peripheral Controller Support
|
||||
#
|
||||
# The order here is alphabetical, except that integrated controllers go
|
||||
# before discrete ones so they will be the initial/default value:
|
||||
# - integrated/SOC controllers first
|
||||
# - licensed IP used in both SOC and discrete versions
|
||||
# - discrete ones (including all PCI-only controllers)
|
||||
# - debug/dummy gadget+hcd is last.
|
||||
#
|
||||
menu "USB Peripheral Controller"
|
||||
|
||||
#
|
||||
# Integrated controllers
|
||||
#
|
||||
|
||||
config USB_AT91
|
||||
tristate "Atmel AT91 USB Device Port"
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
Many Atmel AT91 processors (such as the AT91RM2000) have a
|
||||
full speed USB Device Port with support for five configurable
|
||||
endpoints (plus endpoint zero).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "at91_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_LPC32XX
|
||||
tristate "LPC32XX USB Peripheral Controller"
|
||||
depends on ARCH_LPC32XX && I2C
|
||||
select USB_ISP1301
|
||||
help
|
||||
This option selects the USB device controller in the LPC32xx SoC.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "lpc32xx_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_ATMEL_USBA
|
||||
tristate "Atmel USBA"
|
||||
depends on AVR32 || ARCH_AT91
|
||||
help
|
||||
USBA is the integrated high-speed USB Device controller on
|
||||
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
|
||||
|
||||
config USB_BCM63XX_UDC
|
||||
tristate "Broadcom BCM63xx Peripheral Controller"
|
||||
depends on BCM63XX
|
||||
help
|
||||
Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
|
||||
high speed USB Device Port with support for four fixed endpoints
|
||||
(plus endpoint zero).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "bcm63xx_udc".
|
||||
|
||||
config USB_FSL_USB2
|
||||
tristate "Freescale Highspeed USB DR Peripheral Controller"
|
||||
depends on FSL_SOC || ARCH_MXC
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
help
|
||||
Some of Freescale PowerPC and i.MX processors have a High Speed
|
||||
Dual-Role(DR) USB controller, which supports device mode.
|
||||
|
||||
The number of programmable endpoints is different through
|
||||
SOC revisions.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "fsl_usb2_udc" and force
|
||||
all gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_FUSB300
|
||||
tristate "Faraday FUSB300 USB Peripheral Controller"
|
||||
depends on !PHYS_ADDR_T_64BIT && HAS_DMA
|
||||
help
|
||||
Faraday usb device controller FUSB300 driver
|
||||
|
||||
config USB_FOTG210_UDC
|
||||
depends on HAS_DMA
|
||||
tristate "Faraday FOTG210 USB Peripheral Controller"
|
||||
help
|
||||
Faraday USB2.0 OTG controller which can be configured as
|
||||
high speed or full speed USB device. This driver supppors
|
||||
Bulk Transfer so far.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "fotg210_udc".
|
||||
|
||||
config USB_GR_UDC
|
||||
tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
|
||||
VHDL IP core library.
|
||||
|
||||
config USB_OMAP
|
||||
tristate "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP1
|
||||
depends on ISP1301_OMAP || !(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
|
||||
endpoints (plus endpoint zero). This driver supports the
|
||||
controller in the OMAP 1611, and should work with controllers
|
||||
in other OMAP processors too, given minor tweaks.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "omap_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_PXA25X
|
||||
tristate "PXA 25x or IXP 4xx"
|
||||
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
|
||||
help
|
||||
Intel's PXA 25x series XScale ARM-5TE processors include
|
||||
an integrated full speed USB 1.1 device controller. The
|
||||
controller in the IXP 4xx series is register-compatible.
|
||||
|
||||
It has fifteen fixed-function endpoints, as well as endpoint
|
||||
zero (for control transfers).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "pxa25x_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
# if there's only one gadget driver, using only two bulk endpoints,
|
||||
# don't waste memory for the other endpoints
|
||||
config USB_PXA25X_SMALL
|
||||
depends on USB_PXA25X
|
||||
bool
|
||||
default n if USB_ETH_RNDIS
|
||||
default y if USB_ZERO
|
||||
default y if USB_ETH
|
||||
default y if USB_G_SERIAL
|
||||
|
||||
config USB_R8A66597
|
||||
tristate "Renesas R8A66597 USB Peripheral Controller"
|
||||
depends on HAS_DMA
|
||||
help
|
||||
R8A66597 is a discrete USB host and peripheral controller chip that
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
It has nine configurable endpoints, and endpoint zero.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "r8a66597_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_RENESAS_USBHS_UDC
|
||||
tristate 'Renesas USBHS controller'
|
||||
depends on USB_RENESAS_USBHS
|
||||
help
|
||||
Renesas USBHS is a discrete USB host and peripheral controller chip
|
||||
that supports both full and high speed USB 2.0 data transfers.
|
||||
It has nine or more configurable endpoints, and endpoint zero.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "renesas_usbhs" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_PXA27X
|
||||
tristate "PXA 27x"
|
||||
help
|
||||
Intel's PXA 27x series XScale ARM v5TE processors include
|
||||
an integrated full speed USB 1.1 device controller.
|
||||
|
||||
It has up to 23 endpoints, as well as endpoint zero (for
|
||||
control transfers).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "pxa27x_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_S3C2410
|
||||
tristate "S3C2410 USB Device Controller"
|
||||
depends on ARCH_S3C24XX
|
||||
help
|
||||
Samsung's S3C2410 is an ARM-4 processor with an integrated
|
||||
full speed USB 1.1 device controller. It has 4 configurable
|
||||
endpoints, as well as endpoint zero (for control transfers).
|
||||
|
||||
This driver has been tested on the S3C2410, S3C2412, and
|
||||
S3C2440 processors.
|
||||
|
||||
config USB_S3C2410_DEBUG
|
||||
boolean "S3C2410 udc debug messages"
|
||||
depends on USB_S3C2410
|
||||
|
||||
config USB_S3C_HSUDC
|
||||
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
|
||||
depends on ARCH_S3C24XX
|
||||
help
|
||||
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
|
||||
integrated with dual speed USB 2.0 device controller. It has
|
||||
8 endpoints, as well as endpoint zero.
|
||||
|
||||
This driver has been tested on S3C2416 and S3C2450 processors.
|
||||
|
||||
config USB_MV_UDC
|
||||
tristate "Marvell USB2.0 Device Controller"
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Marvell Socs (including PXA and MMP series) include a high speed
|
||||
USB2.0 OTG controller, which can be configured as high speed or
|
||||
full speed USB peripheral.
|
||||
|
||||
config USB_MV_U3D
|
||||
depends on HAS_DMA
|
||||
tristate "MARVELL PXA2128 USB 3.0 controller"
|
||||
help
|
||||
MARVELL PXA2128 Processor series include a super speed USB3.0 device
|
||||
controller, which support super speed USB peripheral.
|
||||
|
||||
#
|
||||
# Controllers available in both integrated and discrete versions
|
||||
#
|
||||
|
||||
config USB_M66592
|
||||
tristate "Renesas M66592 USB Peripheral Controller"
|
||||
help
|
||||
M66592 is a discrete USB peripheral controller chip that
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
It has seven configurable endpoints, and endpoint zero.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "m66592_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
#
|
||||
# Controllers available only in discrete form (and all PCI controllers)
|
||||
#
|
||||
|
||||
config USB_AMD5536UDC
|
||||
tristate "AMD5536 UDC"
|
||||
depends on PCI
|
||||
help
|
||||
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
|
||||
It is a USB Highspeed DMA capable USB device controller. Beside ep0
|
||||
it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
|
||||
The UDC port supports OTG operation, and may be used as a host port
|
||||
if it's not being used to implement peripheral or OTG roles.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "amd5536udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_FSL_QE
|
||||
tristate "Freescale QE/CPM USB Device Controller"
|
||||
depends on FSL_SOC && (QUICC_ENGINE || CPM)
|
||||
help
|
||||
Some of Freescale PowerPC processors have a Full Speed
|
||||
QE/CPM2 USB controller, which support device mode with 4
|
||||
programmable endpoints. This driver supports the
|
||||
controller in the MPC8360 and MPC8272, and should work with
|
||||
controllers having QE or CPM2, given minor tweaks.
|
||||
|
||||
Set CONFIG_USB_GADGET to "m" to build this driver as a
|
||||
dynamically linked module called "fsl_qe_udc".
|
||||
|
||||
config USB_NET2272
|
||||
tristate "PLX NET2272"
|
||||
help
|
||||
PLX NET2272 is a USB peripheral controller which supports
|
||||
both full and high speed USB 2.0 data transfers.
|
||||
|
||||
It has three configurable endpoints, as well as endpoint zero
|
||||
(for control transfer).
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "net2272" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_NET2272_DMA
|
||||
boolean "Support external DMA controller"
|
||||
depends on USB_NET2272 && HAS_DMA
|
||||
help
|
||||
The NET2272 part can optionally support an external DMA
|
||||
controller, but your board has to have support in the
|
||||
driver itself.
|
||||
|
||||
If unsure, say "N" here. The driver works fine in PIO mode.
|
||||
|
||||
config USB_NET2280
|
||||
tristate "NetChip 228x"
|
||||
depends on PCI
|
||||
help
|
||||
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
|
||||
It has six configurable endpoints, as well as endpoint zero
|
||||
(for control transfers) and several endpoints with dedicated
|
||||
functions.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "net2280" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_GOKU
|
||||
tristate "Toshiba TC86C001 'Goku-S'"
|
||||
depends on PCI
|
||||
help
|
||||
The Toshiba TC86C001 is a PCI device which includes controllers
|
||||
for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
|
||||
|
||||
The device controller has three configurable (bulk or interrupt)
|
||||
endpoints, plus endpoint zero (for control transfers).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "goku_udc" and to force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_EG20T
|
||||
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
|
||||
depends on PCI
|
||||
help
|
||||
This is a USB device driver for EG20T PCH.
|
||||
EG20T PCH is the platform controller hub that is used in Intel's
|
||||
general embedded platform. EG20T PCH has USB device interface.
|
||||
Using this interface, it is able to access system devices connected
|
||||
to USB device.
|
||||
This driver enables USB device function.
|
||||
USB device is a USB peripheral controller which
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
This driver supports both control transfer and bulk transfer modes.
|
||||
This driver dose not support interrupt transfer or isochronous
|
||||
transfer modes.
|
||||
|
||||
This driver also can be used for LAPIS Semiconductor's ML7213 which is
|
||||
for IVI(In-Vehicle Infotainment) use.
|
||||
ML7831 is for general purpose use.
|
||||
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
|
||||
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
|
||||
|
||||
#
|
||||
# LAST -- dummy/emulated controller
|
||||
#
|
||||
|
||||
config USB_DUMMY_HCD
|
||||
tristate "Dummy HCD (DEVELOPMENT)"
|
||||
depends on USB=y || (USB=m && USB_GADGET=m)
|
||||
help
|
||||
This host controller driver emulates USB, looping all data transfer
|
||||
requests back to a USB "gadget driver" in the same host. The host
|
||||
side is the master; the gadget side is the slave. Gadget drivers
|
||||
can be high, full, or low speed; and they have access to endpoints
|
||||
like those from NET2280, PXA2xx, or SA1100 hardware.
|
||||
|
||||
This may help in some stages of creating a driver to embed in a
|
||||
Linux device, since it lets you debug several parts of the gadget
|
||||
driver without its hardware or drivers being involved.
|
||||
|
||||
Since such a gadget side driver needs to interoperate with a host
|
||||
side Linux-USB device driver, this may help to debug both sides
|
||||
of a USB protocol stack.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "dummy_hcd" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
|
||||
# first and will be selected by default.
|
||||
|
||||
endmenu
|
||||
source "drivers/usb/gadget/udc/Kconfig"
|
||||
|
||||
#
|
||||
# USB Gadget Drivers
|
||||
@ -714,466 +353,7 @@ config USB_CONFIGFS_F_FS
|
||||
implemented in kernel space (for instance Ethernet, serial or
|
||||
mass storage) and other are implemented in user space.
|
||||
|
||||
config USB_ZERO
|
||||
tristate "Gadget Zero (DEVELOPMENT)"
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_SS_LB
|
||||
help
|
||||
Gadget Zero is a two-configuration device. It either sinks and
|
||||
sources bulk data; or it loops back a configurable number of
|
||||
transfers. It also implements control requests, for "chapter 9"
|
||||
conformance. The driver needs only two bulk-capable endpoints, so
|
||||
it can work on top of most device-side usb controllers. It's
|
||||
useful for testing, and is also a working example showing how
|
||||
USB "gadget drivers" can be written.
|
||||
|
||||
Make this be the first driver you try using on top of any new
|
||||
USB peripheral controller driver. Then you can use host-side
|
||||
test software, like the "usbtest" driver, to put your hardware
|
||||
and its driver through a basic set of functional tests.
|
||||
|
||||
Gadget Zero also works with the host-side "usb-skeleton" driver,
|
||||
and with many kinds of host-side test software. You may need
|
||||
to tweak product and vendor IDs before host software knows about
|
||||
this device, and arrange to select an appropriate configuration.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_zero".
|
||||
|
||||
config USB_ZERO_HNPTEST
|
||||
boolean "HNP Test Device"
|
||||
depends on USB_ZERO && USB_OTG
|
||||
help
|
||||
You can configure this device to enumerate using the device
|
||||
identifiers of the USB-OTG test device. That means that when
|
||||
this gadget connects to another OTG device, with this one using
|
||||
the "B-Peripheral" role, that device will use HNP to let this
|
||||
one serve as the USB host instead (in the "B-Host" role).
|
||||
|
||||
config USB_AUDIO
|
||||
tristate "Audio Gadget"
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
help
|
||||
This Gadget Audio driver is compatible with USB Audio Class
|
||||
specification 2.0. It implements 1 AudioControl interface,
|
||||
1 AudioStreaming Interface each for USB-OUT and USB-IN.
|
||||
Number of channels, sample rate and sample size can be
|
||||
specified as module parameters.
|
||||
This driver doesn't expect any real Audio codec to be present
|
||||
on the device - the audio streams are simply sinked to and
|
||||
sourced from a virtual ALSA sound card created. The user-space
|
||||
application may choose to do whatever it wants with the data
|
||||
received from the USB Host and choose to provide whatever it
|
||||
wants as audio data to the USB Host.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_audio".
|
||||
|
||||
config GADGET_UAC1
|
||||
bool "UAC 1.0 (Legacy)"
|
||||
depends on USB_AUDIO
|
||||
help
|
||||
If you instead want older UAC Spec-1.0 driver that also has audio
|
||||
paths hardwired to the Audio codec chip on-board and doesn't work
|
||||
without one.
|
||||
|
||||
config USB_ETH
|
||||
tristate "Ethernet Gadget (with CDC Ethernet support)"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_ETHER
|
||||
select USB_F_ECM
|
||||
select USB_F_SUBSET
|
||||
select CRC32
|
||||
help
|
||||
This driver implements Ethernet style communication, in one of
|
||||
several ways:
|
||||
|
||||
- The "Communication Device Class" (CDC) Ethernet Control Model.
|
||||
That protocol is often avoided with pure Ethernet adapters, in
|
||||
favor of simpler vendor-specific hardware, but is widely
|
||||
supported by firmware for smart network devices.
|
||||
|
||||
- On hardware can't implement that protocol, a simple CDC subset
|
||||
is used, placing fewer demands on USB.
|
||||
|
||||
- CDC Ethernet Emulation Model (EEM) is a newer standard that has
|
||||
a simpler interface that can be used by more USB hardware.
|
||||
|
||||
RNDIS support is an additional option, more demanding than than
|
||||
subset.
|
||||
|
||||
Within the USB device, this gadget driver exposes a network device
|
||||
"usbX", where X depends on what other networking devices you have.
|
||||
Treat it like a two-node Ethernet link: host, and gadget.
|
||||
|
||||
The Linux-USB host-side "usbnet" driver interoperates with this
|
||||
driver, so that deep I/O queues can be supported. On 2.4 kernels,
|
||||
use "CDCEther" instead, if you're using the CDC option. That CDC
|
||||
mode should also interoperate with standard CDC Ethernet class
|
||||
drivers on other host operating systems.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_ether".
|
||||
|
||||
config USB_ETH_RNDIS
|
||||
bool "RNDIS support"
|
||||
depends on USB_ETH
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_RNDIS
|
||||
default y
|
||||
help
|
||||
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
|
||||
and Microsoft provides redistributable binary RNDIS drivers for
|
||||
older versions of Windows.
|
||||
|
||||
If you say "y" here, the Ethernet gadget driver will try to provide
|
||||
a second device configuration, supporting RNDIS to talk to such
|
||||
Microsoft USB hosts.
|
||||
|
||||
To make MS-Windows work with this, use Documentation/usb/linux.inf
|
||||
as the "driver info file". For versions of MS-Windows older than
|
||||
XP, you'll need to download drivers from Microsoft's website; a URL
|
||||
is given in comments found in that info file.
|
||||
|
||||
config USB_ETH_EEM
|
||||
bool "Ethernet Emulation Model (EEM) support"
|
||||
depends on USB_ETH
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_EEM
|
||||
default n
|
||||
help
|
||||
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
|
||||
and therefore can be supported by more hardware. Technically ECM and
|
||||
EEM are designed for different applications. The ECM model extends
|
||||
the network interface to the target (e.g. a USB cable modem), and the
|
||||
EEM model is for mobile devices to communicate with hosts using
|
||||
ethernet over USB. For Linux gadgets, however, the interface with
|
||||
the host is the same (a usbX device), so the differences are minimal.
|
||||
|
||||
If you say "y" here, the Ethernet gadget driver will use the EEM
|
||||
protocol rather than ECM. If unsure, say "n".
|
||||
|
||||
config USB_G_NCM
|
||||
tristate "Network Control Model (NCM) support"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_ETHER
|
||||
select USB_F_NCM
|
||||
select CRC32
|
||||
help
|
||||
This driver implements USB CDC NCM subclass standard. NCM is
|
||||
an advanced protocol for Ethernet encapsulation, allows grouping
|
||||
of several ethernet frames into one USB transfer and different
|
||||
alignment possibilities.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_ncm".
|
||||
|
||||
config USB_GADGETFS
|
||||
tristate "Gadget Filesystem"
|
||||
help
|
||||
This driver provides a filesystem based API that lets user mode
|
||||
programs implement a single-configuration USB device, including
|
||||
endpoint I/O and control requests that don't relate to enumeration.
|
||||
All endpoints, transfer speeds, and transfer types supported by
|
||||
the hardware are available, through read() and write() calls.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "gadgetfs".
|
||||
|
||||
config USB_FUNCTIONFS
|
||||
tristate "Function Filesystem"
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_FS
|
||||
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
|
||||
help
|
||||
The Function Filesystem (FunctionFS) lets one create USB
|
||||
composite functions in user space in the same way GadgetFS
|
||||
lets one create USB gadgets in user space. This allows creation
|
||||
of composite gadgets such that some of the functions are
|
||||
implemented in kernel space (for instance Ethernet, serial or
|
||||
mass storage) and other are implemented in user space.
|
||||
|
||||
If you say "y" or "m" here you will be able what kind of
|
||||
configurations the gadget will provide.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build
|
||||
a dynamically linked module called "g_ffs".
|
||||
|
||||
config USB_FUNCTIONFS_ETH
|
||||
bool "Include configuration with CDC ECM (Ethernet)"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
select USB_U_ETHER
|
||||
select USB_F_ECM
|
||||
select USB_F_SUBSET
|
||||
help
|
||||
Include a configuration with CDC ECM function (Ethernet) and the
|
||||
Function Filesystem.
|
||||
|
||||
config USB_FUNCTIONFS_RNDIS
|
||||
bool "Include configuration with RNDIS (Ethernet)"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
select USB_U_ETHER
|
||||
select USB_F_RNDIS
|
||||
help
|
||||
Include a configuration with RNDIS function (Ethernet) and the Filesystem.
|
||||
|
||||
config USB_FUNCTIONFS_GENERIC
|
||||
bool "Include 'pure' configuration"
|
||||
depends on USB_FUNCTIONFS
|
||||
help
|
||||
Include a configuration with the Function Filesystem alone with
|
||||
no Ethernet interface.
|
||||
|
||||
config USB_MASS_STORAGE
|
||||
tristate "Mass Storage Gadget"
|
||||
depends on BLOCK
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_MASS_STORAGE
|
||||
help
|
||||
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
|
||||
As its storage repository it can use a regular file or a block
|
||||
device (in much the same way as the "loop" device driver),
|
||||
specified as a module parameter or sysfs option.
|
||||
|
||||
This driver is a replacement for now removed File-backed
|
||||
Storage Gadget (g_file_storage).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build
|
||||
a dynamically linked module called "g_mass_storage".
|
||||
|
||||
config USB_GADGET_TARGET
|
||||
tristate "USB Gadget Target Fabric Module"
|
||||
depends on TARGET_CORE
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This fabric is an USB gadget. Two USB protocols are supported that is
|
||||
BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
|
||||
advertised on alternative interface 0 (primary) and UAS is on
|
||||
alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
|
||||
UAS utilizes the USB 3.0 feature called streams support.
|
||||
|
||||
config USB_G_SERIAL
|
||||
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
|
||||
depends on TTY
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
select USB_F_SERIAL
|
||||
select USB_F_OBEX
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Serial Gadget talks to the Linux-USB generic serial driver.
|
||||
This driver supports a CDC-ACM module option, which can be used
|
||||
to interoperate with MS-Windows hosts or with the Linux-USB
|
||||
"cdc-acm" driver.
|
||||
|
||||
This driver also supports a CDC-OBEX option. You will need a
|
||||
user space OBEX server talking to /dev/ttyGS*, since the kernel
|
||||
itself doesn't implement the OBEX protocol.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_serial".
|
||||
|
||||
For more information, see Documentation/usb/gadget_serial.txt
|
||||
which includes instructions and a "driver info file" needed to
|
||||
make MS-Windows work with CDC ACM.
|
||||
|
||||
config USB_MIDI_GADGET
|
||||
tristate "MIDI Gadget"
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_RAWMIDI
|
||||
help
|
||||
The MIDI Gadget acts as a USB Audio device, with one MIDI
|
||||
input and one MIDI output. These MIDI jacks appear as
|
||||
a sound "card" in the ALSA sound system. Other MIDI
|
||||
connections can then be made on the gadget system, using
|
||||
ALSA's aconnect utility etc.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_midi".
|
||||
|
||||
config USB_G_PRINTER
|
||||
tristate "Printer Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Printer Gadget channels data between the USB host and a
|
||||
userspace program driving the print engine. The user space
|
||||
program reads and writes the device file /dev/g_printer to
|
||||
receive or send printer data. It can use ioctl calls to
|
||||
the device file to get or set printer status.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_printer".
|
||||
|
||||
For more information, see Documentation/usb/gadget_printer.txt
|
||||
which includes sample code for accessing the device file.
|
||||
|
||||
if TTY
|
||||
|
||||
config USB_CDC_COMPOSITE
|
||||
tristate "CDC Composite Device (Ethernet and ACM)"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_F_ACM
|
||||
select USB_F_ECM
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
|
||||
|
||||
This driver requires four bulk and two interrupt endpoints,
|
||||
plus the ability to handle altsettings. Not all peripheral
|
||||
controllers are that capable.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module.
|
||||
|
||||
config USB_G_NOKIA
|
||||
tristate "Nokia composite gadget"
|
||||
depends on PHONET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_F_ACM
|
||||
select USB_F_OBEX
|
||||
select USB_F_PHONET
|
||||
select USB_F_ECM
|
||||
help
|
||||
The Nokia composite gadget provides support for acm, obex
|
||||
and phonet in only one composite gadget driver.
|
||||
|
||||
It's only really useful for N900 hardware. If you're building
|
||||
a kernel for N900, say Y or M here. If unsure, say N.
|
||||
|
||||
config USB_G_ACM_MS
|
||||
tristate "CDC Composite Device (ACM and mass storage)"
|
||||
depends on BLOCK
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
select USB_F_MASS_STORAGE
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a mass storage, and a CDC ACM (serial port) link.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_acm_ms".
|
||||
|
||||
config USB_G_MULTI
|
||||
tristate "Multifunction Composite Gadget"
|
||||
depends on BLOCK && NET
|
||||
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_F_ACM
|
||||
select USB_F_MASS_STORAGE
|
||||
help
|
||||
The Multifunction Composite Gadget provides Ethernet (RNDIS
|
||||
and/or CDC Ethernet), mass storage and ACM serial link
|
||||
interfaces.
|
||||
|
||||
You will be asked to choose which of the two configurations is
|
||||
to be available in the gadget. At least one configuration must
|
||||
be chosen to make the gadget usable. Selecting more than one
|
||||
configuration will prevent Windows from automatically detecting
|
||||
the gadget as a composite gadget, so an INF file will be needed to
|
||||
use the gadget.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_multi".
|
||||
|
||||
config USB_G_MULTI_RNDIS
|
||||
bool "RNDIS + CDC Serial + Storage configuration"
|
||||
depends on USB_G_MULTI
|
||||
select USB_F_RNDIS
|
||||
default y
|
||||
help
|
||||
This option enables a configuration with RNDIS, CDC Serial and
|
||||
Mass Storage functions available in the Multifunction Composite
|
||||
Gadget. This is the configuration dedicated for Windows since RNDIS
|
||||
is Microsoft's protocol.
|
||||
|
||||
If unsure, say "y".
|
||||
|
||||
config USB_G_MULTI_CDC
|
||||
bool "CDC Ethernet + CDC Serial + Storage configuration"
|
||||
depends on USB_G_MULTI
|
||||
default n
|
||||
select USB_F_ECM
|
||||
help
|
||||
This option enables a configuration with CDC Ethernet (ECM), CDC
|
||||
Serial and Mass Storage functions available in the Multifunction
|
||||
Composite Gadget.
|
||||
|
||||
If unsure, say "y".
|
||||
|
||||
endif # TTY
|
||||
|
||||
config USB_G_HID
|
||||
tristate "HID Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The HID gadget driver provides generic emulation of USB
|
||||
Human Interface Devices (HID).
|
||||
|
||||
For more information, see Documentation/usb/gadget_hid.txt which
|
||||
includes sample code for accessing the device files.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_hid".
|
||||
|
||||
# Standalone / single function gadgets
|
||||
config USB_G_DBGP
|
||||
tristate "EHCI Debug Device Gadget"
|
||||
depends on TTY
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This gadget emulates an EHCI Debug device. This is useful when you want
|
||||
to interact with an EHCI Debug Port.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_dbgp".
|
||||
|
||||
if USB_G_DBGP
|
||||
choice
|
||||
prompt "EHCI Debug Device mode"
|
||||
default USB_G_DBGP_SERIAL
|
||||
|
||||
config USB_G_DBGP_PRINTK
|
||||
depends on USB_G_DBGP
|
||||
bool "printk"
|
||||
help
|
||||
Directly printk() received data. No interaction.
|
||||
|
||||
config USB_G_DBGP_SERIAL
|
||||
depends on USB_G_DBGP
|
||||
select USB_U_SERIAL
|
||||
bool "serial"
|
||||
help
|
||||
Userland can interact using /dev/ttyGSxxx.
|
||||
endchoice
|
||||
endif
|
||||
|
||||
# put drivers that need isochronous transfer support (for audio
|
||||
# or video class gadget drivers), or specific hardware, here.
|
||||
config USB_G_WEBCAM
|
||||
tristate "USB Webcam Gadget"
|
||||
depends on VIDEO_DEV
|
||||
select USB_LIBCOMPOSITE
|
||||
select VIDEOBUF2_VMALLOC
|
||||
help
|
||||
The Webcam Gadget acts as a composite USB Audio and Video Class
|
||||
device. It provides a userspace API to process UVC control requests
|
||||
and stream video data to the host.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_webcam".
|
||||
source "drivers/usb/gadget/legacy/Kconfig"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -1,105 +1,12 @@
|
||||
#
|
||||
# USB peripheral controller drivers
|
||||
#
|
||||
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
||||
ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
|
||||
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
||||
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
|
||||
ccflags-y += -I$(PWD)/drivers/usb/gadget/udc
|
||||
|
||||
obj-$(CONFIG_USB_GADGET) += udc-core.o
|
||||
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
||||
libcomposite-y := usbstring.o config.o epautoconf.o
|
||||
libcomposite-y += composite.o functions.o configfs.o u_f.o
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
|
||||
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
|
||||
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
|
||||
obj-$(CONFIG_USB_GOKU) += goku_udc.o
|
||||
obj-$(CONFIG_USB_OMAP) += omap_udc.o
|
||||
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
|
||||
obj-$(CONFIG_USB_AT91) += at91_udc.o
|
||||
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
||||
obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
|
||||
fsl_usb2_udc-y := fsl_udc_core.o
|
||||
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
|
||||
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o
|
||||
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
||||
obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
|
||||
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
|
||||
|
||||
# USB Functions
|
||||
usb_f_acm-y := f_acm.o
|
||||
obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
|
||||
usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
|
||||
obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
|
||||
obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
|
||||
usb_f_serial-y := f_serial.o
|
||||
obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
|
||||
usb_f_obex-y := f_obex.o
|
||||
obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
|
||||
obj-$(CONFIG_USB_U_ETHER) += u_ether.o
|
||||
usb_f_ncm-y := f_ncm.o
|
||||
obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
|
||||
usb_f_ecm-y := f_ecm.o
|
||||
obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
|
||||
usb_f_phonet-y := f_phonet.o
|
||||
obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
|
||||
usb_f_eem-y := f_eem.o
|
||||
obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
|
||||
usb_f_ecm_subset-y := f_subset.o
|
||||
obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
|
||||
usb_f_rndis-y := f_rndis.o rndis.o
|
||||
obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
|
||||
usb_f_mass_storage-y := f_mass_storage.o storage_common.o
|
||||
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
|
||||
usb_f_fs-y := f_fs.o
|
||||
obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
#
|
||||
g_zero-y := zero.o
|
||||
g_audio-y := audio.o
|
||||
g_ether-y := ether.o
|
||||
g_serial-y := serial.o
|
||||
g_midi-y := gmidi.o
|
||||
gadgetfs-y := inode.o
|
||||
g_mass_storage-y := mass_storage.o
|
||||
g_printer-y := printer.o
|
||||
g_cdc-y := cdc2.o
|
||||
g_multi-y := multi.o
|
||||
g_hid-y := hid.o
|
||||
g_dbgp-y := dbgp.o
|
||||
g_nokia-y := nokia.o
|
||||
g_webcam-y := webcam.o
|
||||
g_ncm-y := ncm.o
|
||||
g_acm_ms-y := acm_ms.o
|
||||
g_tcm_usb_gadget-y := tcm_usb_gadget.o
|
||||
|
||||
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||||
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
obj-$(CONFIG_USB_ETH) += g_ether.o
|
||||
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
|
||||
obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
|
||||
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
|
||||
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
|
||||
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
|
||||
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
|
||||
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
|
||||
obj-$(CONFIG_USB_G_HID) += g_hid.o
|
||||
obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
|
||||
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
|
||||
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
|
||||
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
||||
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
|
||||
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
|
||||
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
|
||||
obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
|
||||
|
@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
||||
}
|
||||
if (cdev->req) {
|
||||
kfree(cdev->req->buf);
|
||||
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
|
||||
}
|
||||
cdev->next_string_id = 0;
|
||||
|
@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop,
|
||||
|
||||
if (page[len - 1] == '\n' || page[len - 1] == '\0')
|
||||
--len;
|
||||
new_data = kzalloc(len, GFP_KERNEL);
|
||||
new_data = kmemdup(page, len, GFP_KERNEL);
|
||||
if (!new_data)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(new_data, page, len);
|
||||
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
kfree(ext_prop->data);
|
||||
|
34
drivers/usb/gadget/function/Makefile
Normal file
34
drivers/usb/gadget/function/Makefile
Normal file
@ -0,0 +1,34 @@
|
||||
#
|
||||
# USB peripheral controller drivers
|
||||
#
|
||||
|
||||
ccflags-y := -I$(PWD)/drivers/usb/gadget/
|
||||
ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
|
||||
|
||||
# USB Functions
|
||||
usb_f_acm-y := f_acm.o
|
||||
obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
|
||||
usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
|
||||
obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
|
||||
obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
|
||||
usb_f_serial-y := f_serial.o
|
||||
obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
|
||||
usb_f_obex-y := f_obex.o
|
||||
obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
|
||||
obj-$(CONFIG_USB_U_ETHER) += u_ether.o
|
||||
usb_f_ncm-y := f_ncm.o
|
||||
obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
|
||||
usb_f_ecm-y := f_ecm.o
|
||||
obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
|
||||
usb_f_phonet-y := f_phonet.o
|
||||
obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
|
||||
usb_f_eem-y := f_eem.o
|
||||
obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
|
||||
usb_f_ecm_subset-y := f_subset.o
|
||||
obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
|
||||
usb_f_rndis-y := f_rndis.o rndis.o
|
||||
obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
|
||||
usb_f_mass_storage-y := f_mass_storage.o storage_common.o
|
||||
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
|
||||
usb_f_fs-y := f_fs.o
|
||||
obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
|
@ -355,20 +355,18 @@ static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
|
||||
int padlen = 0;
|
||||
u16 len = skb->len;
|
||||
|
||||
if (!skb_cloned(skb)) {
|
||||
int headroom = skb_headroom(skb);
|
||||
int tailroom = skb_tailroom(skb);
|
||||
int headroom = skb_headroom(skb);
|
||||
int tailroom = skb_tailroom(skb);
|
||||
|
||||
/* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
|
||||
* stick two bytes of zero-length EEM packet on the end.
|
||||
*/
|
||||
if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
|
||||
padlen += 2;
|
||||
/* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
|
||||
* stick two bytes of zero-length EEM packet on the end.
|
||||
*/
|
||||
if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
|
||||
padlen += 2;
|
||||
|
||||
if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
|
||||
(headroom >= EEM_HLEN))
|
||||
goto done;
|
||||
}
|
||||
if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
|
||||
(headroom >= EEM_HLEN) && !skb_cloned(skb))
|
||||
goto done;
|
||||
|
||||
skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
|
||||
dev_kfree_skb_any(skb);
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include "u_fs.h"
|
||||
#include "u_f.h"
|
||||
#include "u_os_desc.h"
|
||||
#include "configfs.h"
|
||||
|
||||
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
|
||||
@ -1646,13 +1647,22 @@ enum ffs_entity_type {
|
||||
FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
|
||||
};
|
||||
|
||||
enum ffs_os_desc_type {
|
||||
FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
|
||||
};
|
||||
|
||||
typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
|
||||
u8 *valuep,
|
||||
struct usb_descriptor_header *desc,
|
||||
void *priv);
|
||||
|
||||
static int __must_check ffs_do_desc(char *data, unsigned len,
|
||||
ffs_entity_callback entity, void *priv)
|
||||
typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
|
||||
struct usb_os_desc_header *h, void *data,
|
||||
unsigned len, void *priv);
|
||||
|
||||
static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
ffs_entity_callback entity,
|
||||
void *priv)
|
||||
{
|
||||
struct usb_descriptor_header *_ds = (void *)data;
|
||||
u8 length;
|
||||
@ -1804,7 +1814,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
||||
if (!data)
|
||||
return _len - len;
|
||||
|
||||
ret = ffs_do_desc(data, len, entity, priv);
|
||||
ret = ffs_do_single_desc(data, len, entity, priv);
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_debug("%s returns %d\n", __func__, ret);
|
||||
return ret;
|
||||
@ -1857,11 +1867,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
|
||||
struct usb_os_desc_header *desc)
|
||||
{
|
||||
u16 bcd_version = le16_to_cpu(desc->bcdVersion);
|
||||
u16 w_index = le16_to_cpu(desc->wIndex);
|
||||
|
||||
if (bcd_version != 1) {
|
||||
pr_vdebug("unsupported os descriptors version: %d",
|
||||
bcd_version);
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (w_index) {
|
||||
case 0x4:
|
||||
*next_type = FFS_OS_DESC_EXT_COMPAT;
|
||||
break;
|
||||
case 0x5:
|
||||
*next_type = FFS_OS_DESC_EXT_PROP;
|
||||
break;
|
||||
default:
|
||||
pr_vdebug("unsupported os descriptor type: %d", w_index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return sizeof(*desc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process all extended compatibility/extended property descriptors
|
||||
* of a feature descriptor
|
||||
*/
|
||||
static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
|
||||
enum ffs_os_desc_type type,
|
||||
u16 feature_count,
|
||||
ffs_os_desc_callback entity,
|
||||
void *priv,
|
||||
struct usb_os_desc_header *h)
|
||||
{
|
||||
int ret;
|
||||
const unsigned _len = len;
|
||||
|
||||
ENTER();
|
||||
|
||||
/* loop over all ext compat/ext prop descriptors */
|
||||
while (feature_count--) {
|
||||
ret = entity(type, h, data, len, priv);
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_debug("bad OS descriptor, type: %d\n", type);
|
||||
return ret;
|
||||
}
|
||||
data += ret;
|
||||
len -= ret;
|
||||
}
|
||||
return _len - len;
|
||||
}
|
||||
|
||||
/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
|
||||
static int __must_check ffs_do_os_descs(unsigned count,
|
||||
char *data, unsigned len,
|
||||
ffs_os_desc_callback entity, void *priv)
|
||||
{
|
||||
const unsigned _len = len;
|
||||
unsigned long num = 0;
|
||||
|
||||
ENTER();
|
||||
|
||||
for (num = 0; num < count; ++num) {
|
||||
int ret;
|
||||
enum ffs_os_desc_type type;
|
||||
u16 feature_count;
|
||||
struct usb_os_desc_header *desc = (void *)data;
|
||||
|
||||
if (len < sizeof(*desc))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Record "descriptor" entity.
|
||||
* Process dwLength, bcdVersion, wIndex, get b/wCount.
|
||||
* Move the data pointer to the beginning of extended
|
||||
* compatibilities proper or extended properties proper
|
||||
* portions of the data
|
||||
*/
|
||||
if (le32_to_cpu(desc->dwLength) > len)
|
||||
return -EINVAL;
|
||||
|
||||
ret = __ffs_do_os_desc_header(&type, desc);
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
|
||||
num, ret);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
|
||||
*/
|
||||
feature_count = le16_to_cpu(desc->wCount);
|
||||
if (type == FFS_OS_DESC_EXT_COMPAT &&
|
||||
(feature_count > 255 || desc->Reserved))
|
||||
return -EINVAL;
|
||||
len -= ret;
|
||||
data += ret;
|
||||
|
||||
/*
|
||||
* Process all function/property descriptors
|
||||
* of this Feature Descriptor
|
||||
*/
|
||||
ret = ffs_do_single_os_desc(data, len, type,
|
||||
feature_count, entity, priv, desc);
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_debug("%s returns %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
len -= ret;
|
||||
data += ret;
|
||||
}
|
||||
return _len - len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate contents of the buffer from userspace related to OS descriptors.
|
||||
*/
|
||||
static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
|
||||
struct usb_os_desc_header *h, void *data,
|
||||
unsigned len, void *priv)
|
||||
{
|
||||
struct ffs_data *ffs = priv;
|
||||
u8 length;
|
||||
|
||||
ENTER();
|
||||
|
||||
switch (type) {
|
||||
case FFS_OS_DESC_EXT_COMPAT: {
|
||||
struct usb_ext_compat_desc *d = data;
|
||||
int i;
|
||||
|
||||
if (len < sizeof(*d) ||
|
||||
d->bFirstInterfaceNumber >= ffs->interfaces_count ||
|
||||
d->Reserved1)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
|
||||
if (d->Reserved2[i])
|
||||
return -EINVAL;
|
||||
|
||||
length = sizeof(struct usb_ext_compat_desc);
|
||||
}
|
||||
break;
|
||||
case FFS_OS_DESC_EXT_PROP: {
|
||||
struct usb_ext_prop_desc *d = data;
|
||||
u32 type, pdl;
|
||||
u16 pnl;
|
||||
|
||||
if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
|
||||
return -EINVAL;
|
||||
length = le32_to_cpu(d->dwSize);
|
||||
type = le32_to_cpu(d->dwPropertyDataType);
|
||||
if (type < USB_EXT_PROP_UNICODE ||
|
||||
type > USB_EXT_PROP_UNICODE_MULTI) {
|
||||
pr_vdebug("unsupported os descriptor property type: %d",
|
||||
type);
|
||||
return -EINVAL;
|
||||
}
|
||||
pnl = le16_to_cpu(d->wPropertyNameLength);
|
||||
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
|
||||
if (length != 14 + pnl + pdl) {
|
||||
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
|
||||
length, pnl, pdl, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
++ffs->ms_os_descs_ext_prop_count;
|
||||
/* property name reported to the host as "WCHAR"s */
|
||||
ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
|
||||
ffs->ms_os_descs_ext_prop_data_len += pdl;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_vdebug("unknown descriptor: %d\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
char *const _data, size_t len)
|
||||
{
|
||||
char *data = _data, *raw_descs;
|
||||
unsigned counts[3], flags;
|
||||
unsigned os_descs_count = 0, counts[3], flags;
|
||||
int ret = -EINVAL, i;
|
||||
|
||||
ENTER();
|
||||
@ -1879,7 +2069,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
flags = get_unaligned_le32(data + 8);
|
||||
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
|
||||
FUNCTIONFS_HAS_HS_DESC |
|
||||
FUNCTIONFS_HAS_SS_DESC)) {
|
||||
FUNCTIONFS_HAS_SS_DESC |
|
||||
FUNCTIONFS_HAS_MS_OS_DESC)) {
|
||||
ret = -ENOSYS;
|
||||
goto error;
|
||||
}
|
||||
@ -1902,6 +2093,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
len -= 4;
|
||||
}
|
||||
}
|
||||
if (flags & (1 << i)) {
|
||||
os_descs_count = get_unaligned_le32(data);
|
||||
data += 4;
|
||||
len -= 4;
|
||||
};
|
||||
|
||||
/* Read descriptors */
|
||||
raw_descs = data;
|
||||
@ -1915,6 +2111,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
data += ret;
|
||||
len -= ret;
|
||||
}
|
||||
if (os_descs_count) {
|
||||
ret = ffs_do_os_descs(os_descs_count, data, len,
|
||||
__ffs_data_do_os_desc, ffs);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
data += ret;
|
||||
len -= ret;
|
||||
}
|
||||
|
||||
if (raw_descs == data || len) {
|
||||
ret = -EINVAL;
|
||||
@ -1927,6 +2131,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
ffs->fs_descs_count = counts[0];
|
||||
ffs->hs_descs_count = counts[1];
|
||||
ffs->ss_descs_count = counts[2];
|
||||
ffs->ms_os_descs_count = os_descs_count;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2268,6 +2473,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
|
||||
struct usb_os_desc_header *h, void *data,
|
||||
unsigned len, void *priv)
|
||||
{
|
||||
struct ffs_function *func = priv;
|
||||
u8 length = 0;
|
||||
|
||||
switch (type) {
|
||||
case FFS_OS_DESC_EXT_COMPAT: {
|
||||
struct usb_ext_compat_desc *desc = data;
|
||||
struct usb_os_desc_table *t;
|
||||
|
||||
t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
|
||||
t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
|
||||
memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
|
||||
ARRAY_SIZE(desc->CompatibleID) +
|
||||
ARRAY_SIZE(desc->SubCompatibleID));
|
||||
length = sizeof(*desc);
|
||||
}
|
||||
break;
|
||||
case FFS_OS_DESC_EXT_PROP: {
|
||||
struct usb_ext_prop_desc *desc = data;
|
||||
struct usb_os_desc_table *t;
|
||||
struct usb_os_desc_ext_prop *ext_prop;
|
||||
char *ext_prop_name;
|
||||
char *ext_prop_data;
|
||||
|
||||
t = &func->function.os_desc_table[h->interface];
|
||||
t->if_id = func->interfaces_nums[h->interface];
|
||||
|
||||
ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
|
||||
func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
|
||||
|
||||
ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
|
||||
ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
|
||||
ext_prop->data_len = le32_to_cpu(*(u32 *)
|
||||
usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
|
||||
length = ext_prop->name_len + ext_prop->data_len + 14;
|
||||
|
||||
ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
|
||||
func->ffs->ms_os_descs_ext_prop_name_avail +=
|
||||
ext_prop->name_len;
|
||||
|
||||
ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
|
||||
func->ffs->ms_os_descs_ext_prop_data_avail +=
|
||||
ext_prop->data_len;
|
||||
memcpy(ext_prop_data,
|
||||
usb_ext_prop_data_ptr(data, ext_prop->name_len),
|
||||
ext_prop->data_len);
|
||||
/* unicode data reported to the host as "WCHAR"s */
|
||||
switch (ext_prop->type) {
|
||||
case USB_EXT_PROP_UNICODE:
|
||||
case USB_EXT_PROP_UNICODE_ENV:
|
||||
case USB_EXT_PROP_UNICODE_LINK:
|
||||
case USB_EXT_PROP_UNICODE_MULTI:
|
||||
ext_prop->data_len *= 2;
|
||||
break;
|
||||
}
|
||||
ext_prop->data = ext_prop_data;
|
||||
|
||||
memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
|
||||
ext_prop->name_len);
|
||||
/* property name reported to the host as "WCHAR"s */
|
||||
ext_prop->name_len *= 2;
|
||||
ext_prop->name = ext_prop_name;
|
||||
|
||||
t->os_desc->ext_prop_len +=
|
||||
ext_prop->name_len + ext_prop->data_len + 14;
|
||||
++t->os_desc->ext_prop_count;
|
||||
list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_vdebug("unknown descriptor: %d\n", type);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
|
||||
struct usb_configuration *c)
|
||||
{
|
||||
@ -2329,7 +2613,7 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
const int super = gadget_is_superspeed(func->gadget) &&
|
||||
func->ffs->ss_descs_count;
|
||||
|
||||
int fs_len, hs_len, ret;
|
||||
int fs_len, hs_len, ss_len, ret, i;
|
||||
|
||||
/* Make it a single chunk, less management later on */
|
||||
vla_group(d);
|
||||
@ -2341,6 +2625,18 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
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, struct usb_os_desc_table, os_desc_table,
|
||||
c->cdev->use_os_string ? ffs->interfaces_count : 0);
|
||||
vla_item_with_sz(d, char[16], ext_compat,
|
||||
c->cdev->use_os_string ? ffs->interfaces_count : 0);
|
||||
vla_item_with_sz(d, struct usb_os_desc, os_desc,
|
||||
c->cdev->use_os_string ? ffs->interfaces_count : 0);
|
||||
vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
|
||||
ffs->ms_os_descs_ext_prop_count);
|
||||
vla_item_with_sz(d, char, ext_prop_name,
|
||||
ffs->ms_os_descs_ext_prop_name_len);
|
||||
vla_item_with_sz(d, char, ext_prop_data,
|
||||
ffs->ms_os_descs_ext_prop_data_len);
|
||||
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
|
||||
char *vlabuf;
|
||||
|
||||
@ -2351,12 +2647,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Allocate a single chunk, less management later on */
|
||||
vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
|
||||
vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
|
||||
if (unlikely(!vlabuf))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Zero */
|
||||
memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
|
||||
ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
|
||||
ffs->ms_os_descs_ext_prop_name_avail =
|
||||
vla_ptr(vlabuf, d, ext_prop_name);
|
||||
ffs->ms_os_descs_ext_prop_data_avail =
|
||||
vla_ptr(vlabuf, d, ext_prop_data);
|
||||
|
||||
/* Copy descriptors */
|
||||
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
|
||||
ffs->raw_descs_length);
|
||||
@ -2410,12 +2710,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
|
||||
if (likely(super)) {
|
||||
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
|
||||
ret = ffs_do_descs(ffs->ss_descs_count,
|
||||
ss_len = 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))
|
||||
if (unlikely(ss_len < 0)) {
|
||||
ret = ss_len;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
ss_len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2431,6 +2735,28 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
|
||||
func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
|
||||
if (c->cdev->use_os_string)
|
||||
for (i = 0; i < ffs->interfaces_count; ++i) {
|
||||
struct usb_os_desc *desc;
|
||||
|
||||
desc = func->function.os_desc_table[i].os_desc =
|
||||
vla_ptr(vlabuf, d, os_desc) +
|
||||
i * sizeof(struct usb_os_desc);
|
||||
desc->ext_compat_id =
|
||||
vla_ptr(vlabuf, d, ext_compat) + i * 16;
|
||||
INIT_LIST_HEAD(&desc->ext_prop);
|
||||
}
|
||||
ret = ffs_do_os_descs(ffs->ms_os_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) +
|
||||
fs_len + hs_len + ss_len,
|
||||
d_raw_descs__sz - fs_len - hs_len - ss_len,
|
||||
__ffs_func_bind_do_os_desc, func);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
func->function.os_desc_n =
|
||||
c->cdev->use_os_string ? ffs->interfaces_count : 0;
|
||||
|
||||
/* And we're done */
|
||||
ffs_event_add(ffs, FUNCTIONFS_BIND);
|
||||
return 0;
|
||||
@ -2901,12 +3227,12 @@ static void *ffs_acquire_dev(const char *dev_name)
|
||||
|
||||
ffs_dev = _ffs_find_dev(dev_name);
|
||||
if (!ffs_dev)
|
||||
ffs_dev = ERR_PTR(-ENODEV);
|
||||
ffs_dev = ERR_PTR(-ENOENT);
|
||||
else if (ffs_dev->mounted)
|
||||
ffs_dev = ERR_PTR(-EBUSY);
|
||||
else if (ffs_dev->ffs_acquire_dev_callback &&
|
||||
ffs_dev->ffs_acquire_dev_callback(ffs_dev))
|
||||
ffs_dev = ERR_PTR(-ENODEV);
|
||||
ffs_dev = ERR_PTR(-ENOENT);
|
||||
else
|
||||
ffs_dev->mounted = true;
|
||||
|
@ -68,6 +68,18 @@ struct f_ncm {
|
||||
* callback and ethernet open/close
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
struct net_device *netdev;
|
||||
|
||||
/* For multi-frame NDP TX */
|
||||
struct sk_buff *skb_tx_data;
|
||||
struct sk_buff *skb_tx_ndp;
|
||||
u16 ndp_dgram_count;
|
||||
bool timer_force_tx;
|
||||
struct tasklet_struct tx_tasklet;
|
||||
struct hrtimer task_timer;
|
||||
|
||||
bool timer_stopping;
|
||||
};
|
||||
|
||||
static inline struct f_ncm *func_to_ncm(struct usb_function *f)
|
||||
@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g)
|
||||
* If the host can group frames, allow it to do that, 16K is selected,
|
||||
* because it's used by default by the current linux host driver
|
||||
*/
|
||||
#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE
|
||||
#define NTB_DEFAULT_IN_SIZE 16384
|
||||
#define NTB_OUT_SIZE 16384
|
||||
|
||||
/*
|
||||
* skbs of size less than that will not be aligned
|
||||
* to NCM's dwNtbInMaxSize to save bus bandwidth
|
||||
/* Allocation for storing the NDP, 32 should suffice for a
|
||||
* 16k packet. This allows a maximum of 32 * 507 Byte packets to
|
||||
* be transmitted in a single 16kB skb, though when sending full size
|
||||
* packets this limit will be plenty.
|
||||
* Smaller packets are not likely to be trying to maximize the
|
||||
* throughput and will be mstly sending smaller infrequent frames.
|
||||
*/
|
||||
#define TX_MAX_NUM_DPE 32
|
||||
|
||||
#define MAX_TX_NONFIXED (512 * 3)
|
||||
/* Delay for the transmit to wait before sending an unfilled NTB frame. */
|
||||
#define TX_TIMEOUT_NSECS 300000
|
||||
|
||||
#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
|
||||
USB_CDC_NCM_NTB32_SUPPORTED)
|
||||
@ -355,14 +372,15 @@ struct ndp_parser_opts {
|
||||
u32 ndp_sign;
|
||||
unsigned nth_size;
|
||||
unsigned ndp_size;
|
||||
unsigned dpe_size;
|
||||
unsigned ndplen_align;
|
||||
/* sizes in u16 units */
|
||||
unsigned dgram_item_len; /* index or length */
|
||||
unsigned block_length;
|
||||
unsigned fp_index;
|
||||
unsigned ndp_index;
|
||||
unsigned reserved1;
|
||||
unsigned reserved2;
|
||||
unsigned next_fp_index;
|
||||
unsigned next_ndp_index;
|
||||
};
|
||||
|
||||
#define INIT_NDP16_OPTS { \
|
||||
@ -370,13 +388,14 @@ struct ndp_parser_opts {
|
||||
.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \
|
||||
.nth_size = sizeof(struct usb_cdc_ncm_nth16), \
|
||||
.ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \
|
||||
.dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \
|
||||
.ndplen_align = 4, \
|
||||
.dgram_item_len = 1, \
|
||||
.block_length = 1, \
|
||||
.fp_index = 1, \
|
||||
.ndp_index = 1, \
|
||||
.reserved1 = 0, \
|
||||
.reserved2 = 0, \
|
||||
.next_fp_index = 1, \
|
||||
.next_ndp_index = 1, \
|
||||
}
|
||||
|
||||
|
||||
@ -385,13 +404,14 @@ struct ndp_parser_opts {
|
||||
.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \
|
||||
.nth_size = sizeof(struct usb_cdc_ncm_nth32), \
|
||||
.ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \
|
||||
.dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \
|
||||
.ndplen_align = 8, \
|
||||
.dgram_item_len = 2, \
|
||||
.block_length = 2, \
|
||||
.fp_index = 2, \
|
||||
.ndp_index = 2, \
|
||||
.reserved1 = 1, \
|
||||
.reserved2 = 2, \
|
||||
.next_fp_index = 2, \
|
||||
.next_ndp_index = 2, \
|
||||
}
|
||||
|
||||
static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
|
||||
@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
|
||||
if (ncm->port.in_ep->driver_data) {
|
||||
DBG(cdev, "reset ncm\n");
|
||||
ncm->timer_stopping = true;
|
||||
ncm->netdev = NULL;
|
||||
gether_disconnect(&ncm->port);
|
||||
ncm_reset_values(ncm);
|
||||
}
|
||||
@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
net = gether_connect(&ncm->port);
|
||||
if (IS_ERR(net))
|
||||
return PTR_ERR(net);
|
||||
ncm->netdev = net;
|
||||
ncm->timer_stopping = false;
|
||||
}
|
||||
|
||||
spin_lock(&ncm->lock);
|
||||
@ -865,95 +889,232 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf)
|
||||
return ncm->port.in_ep->driver_data ? 1 : 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *package_for_tx(struct f_ncm *ncm)
|
||||
{
|
||||
__le16 *ntb_iter;
|
||||
struct sk_buff *skb2 = NULL;
|
||||
unsigned ndp_pad;
|
||||
unsigned ndp_index;
|
||||
unsigned new_len;
|
||||
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
|
||||
const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
|
||||
|
||||
/* Stop the timer */
|
||||
hrtimer_try_to_cancel(&ncm->task_timer);
|
||||
|
||||
ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
|
||||
ncm->skb_tx_data->len;
|
||||
ndp_index = ncm->skb_tx_data->len + ndp_pad;
|
||||
new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
|
||||
|
||||
/* Set the final BlockLength and wNdpIndex */
|
||||
ntb_iter = (void *) ncm->skb_tx_data->data;
|
||||
/* Increment pointer to BlockLength */
|
||||
ntb_iter += 2 + 1 + 1;
|
||||
put_ncm(&ntb_iter, opts->block_length, new_len);
|
||||
put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
|
||||
|
||||
/* Set the final NDP wLength */
|
||||
new_len = opts->ndp_size +
|
||||
(ncm->ndp_dgram_count * dgram_idx_len);
|
||||
ncm->ndp_dgram_count = 0;
|
||||
/* Increment from start to wLength */
|
||||
ntb_iter = (void *) ncm->skb_tx_ndp->data;
|
||||
ntb_iter += 2;
|
||||
put_unaligned_le16(new_len, ntb_iter);
|
||||
|
||||
/* Merge the skbs */
|
||||
swap(skb2, ncm->skb_tx_data);
|
||||
if (ncm->skb_tx_data) {
|
||||
dev_kfree_skb_any(ncm->skb_tx_data);
|
||||
ncm->skb_tx_data = NULL;
|
||||
}
|
||||
|
||||
/* Insert NDP alignment. */
|
||||
ntb_iter = (void *) skb_put(skb2, ndp_pad);
|
||||
memset(ntb_iter, 0, ndp_pad);
|
||||
|
||||
/* Copy NTB across. */
|
||||
ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
|
||||
memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
|
||||
dev_kfree_skb_any(ncm->skb_tx_ndp);
|
||||
ncm->skb_tx_ndp = NULL;
|
||||
|
||||
/* Insert zero'd datagram. */
|
||||
ntb_iter = (void *) skb_put(skb2, dgram_idx_len);
|
||||
memset(ntb_iter, 0, dgram_idx_len);
|
||||
|
||||
return skb2;
|
||||
}
|
||||
|
||||
static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct f_ncm *ncm = func_to_ncm(&port->func);
|
||||
struct sk_buff *skb2;
|
||||
struct sk_buff *skb2 = NULL;
|
||||
int ncb_len = 0;
|
||||
__le16 *tmp;
|
||||
int div;
|
||||
int rem;
|
||||
int pad;
|
||||
int ndp_align;
|
||||
int ndp_pad;
|
||||
__le16 *ntb_data;
|
||||
__le16 *ntb_ndp;
|
||||
int dgram_pad;
|
||||
|
||||
unsigned max_size = ncm->port.fixed_in_len;
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
|
||||
const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
|
||||
const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
|
||||
const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
|
||||
const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
|
||||
|
||||
div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
|
||||
rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
|
||||
ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
|
||||
if (!skb && !ncm->skb_tx_data)
|
||||
return NULL;
|
||||
|
||||
ncb_len += opts->nth_size;
|
||||
ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len;
|
||||
ncb_len += ndp_pad;
|
||||
ncb_len += opts->ndp_size;
|
||||
ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */
|
||||
ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */
|
||||
pad = ALIGN(ncb_len, div) + rem - ncb_len;
|
||||
ncb_len += pad;
|
||||
if (skb) {
|
||||
/* Add the CRC if required up front */
|
||||
if (ncm->is_crc) {
|
||||
uint32_t crc;
|
||||
__le16 *crc_pos;
|
||||
|
||||
if (ncb_len + skb->len + crc_len > max_size) {
|
||||
crc = ~crc32_le(~0,
|
||||
skb->data,
|
||||
skb->len);
|
||||
crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
|
||||
put_unaligned_le32(crc, crc_pos);
|
||||
}
|
||||
|
||||
/* If the new skb is too big for the current NCM NTB then
|
||||
* set the current stored skb to be sent now and clear it
|
||||
* ready for new data.
|
||||
* NOTE: Assume maximum align for speed of calculation.
|
||||
*/
|
||||
if (ncm->skb_tx_data
|
||||
&& (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
|
||||
|| (ncm->skb_tx_data->len +
|
||||
div + rem + skb->len +
|
||||
ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
|
||||
> max_size)) {
|
||||
skb2 = package_for_tx(ncm);
|
||||
if (!skb2)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!ncm->skb_tx_data) {
|
||||
ncb_len = opts->nth_size;
|
||||
dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
|
||||
ncb_len += dgram_pad;
|
||||
|
||||
/* Create a new skb for the NTH and datagrams. */
|
||||
ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
|
||||
if (!ncm->skb_tx_data)
|
||||
goto err;
|
||||
|
||||
ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
|
||||
memset(ntb_data, 0, ncb_len);
|
||||
/* dwSignature */
|
||||
put_unaligned_le32(opts->nth_sign, ntb_data);
|
||||
ntb_data += 2;
|
||||
/* wHeaderLength */
|
||||
put_unaligned_le16(opts->nth_size, ntb_data++);
|
||||
|
||||
/* Allocate an skb for storing the NDP,
|
||||
* TX_MAX_NUM_DPE should easily suffice for a
|
||||
* 16k packet.
|
||||
*/
|
||||
ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
|
||||
+ opts->dpe_size
|
||||
* TX_MAX_NUM_DPE),
|
||||
GFP_ATOMIC);
|
||||
if (!ncm->skb_tx_ndp)
|
||||
goto err;
|
||||
ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
|
||||
opts->ndp_size);
|
||||
memset(ntb_ndp, 0, ncb_len);
|
||||
/* dwSignature */
|
||||
put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
|
||||
ntb_ndp += 2;
|
||||
|
||||
/* There is always a zeroed entry */
|
||||
ncm->ndp_dgram_count = 1;
|
||||
|
||||
/* Note: we skip opts->next_ndp_index */
|
||||
}
|
||||
|
||||
/* Delay the timer. */
|
||||
hrtimer_start(&ncm->task_timer,
|
||||
ktime_set(0, TX_TIMEOUT_NSECS),
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
/* Add the datagram position entries */
|
||||
ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len);
|
||||
memset(ntb_ndp, 0, dgram_idx_len);
|
||||
|
||||
ncb_len = ncm->skb_tx_data->len;
|
||||
dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
|
||||
ncb_len += dgram_pad;
|
||||
|
||||
/* (d)wDatagramIndex */
|
||||
put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
|
||||
/* (d)wDatagramLength */
|
||||
put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
|
||||
ncm->ndp_dgram_count++;
|
||||
|
||||
/* Add the new data to the skb */
|
||||
ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
|
||||
memset(ntb_data, 0, dgram_pad);
|
||||
ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
|
||||
memcpy(ntb_data, skb->data, skb->len);
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
skb = NULL;
|
||||
|
||||
} else if (ncm->skb_tx_data && ncm->timer_force_tx) {
|
||||
/* If the tx was requested because of a timeout then send */
|
||||
skb2 = package_for_tx(ncm);
|
||||
if (!skb2)
|
||||
goto err;
|
||||
}
|
||||
|
||||
skb2 = skb_copy_expand(skb, ncb_len,
|
||||
max_size - skb->len - ncb_len - crc_len,
|
||||
GFP_ATOMIC);
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!skb2)
|
||||
return NULL;
|
||||
return skb2;
|
||||
|
||||
skb = skb2;
|
||||
err:
|
||||
ncm->netdev->stats.tx_dropped++;
|
||||
|
||||
tmp = (void *) skb_push(skb, ncb_len);
|
||||
memset(tmp, 0, ncb_len);
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
if (ncm->skb_tx_data)
|
||||
dev_kfree_skb_any(ncm->skb_tx_data);
|
||||
if (ncm->skb_tx_ndp)
|
||||
dev_kfree_skb_any(ncm->skb_tx_ndp);
|
||||
|
||||
put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */
|
||||
tmp += 2;
|
||||
/* wHeaderLength */
|
||||
put_unaligned_le16(opts->nth_size, tmp++);
|
||||
tmp++; /* skip wSequence */
|
||||
put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */
|
||||
/* (d)wFpIndex */
|
||||
/* the first pointer is right after the NTH + align */
|
||||
put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp = (void *)tmp + ndp_pad;
|
||||
/*
|
||||
* This transmits the NTB if there are frames waiting.
|
||||
*/
|
||||
static void ncm_tx_tasklet(unsigned long data)
|
||||
{
|
||||
struct f_ncm *ncm = (void *)data;
|
||||
|
||||
/* NDP */
|
||||
put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */
|
||||
tmp += 2;
|
||||
/* wLength */
|
||||
put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);
|
||||
if (ncm->timer_stopping)
|
||||
return;
|
||||
|
||||
tmp += opts->reserved1;
|
||||
tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
|
||||
tmp += opts->reserved2;
|
||||
|
||||
if (ncm->is_crc) {
|
||||
uint32_t crc;
|
||||
|
||||
crc = ~crc32_le(~0,
|
||||
skb->data + ncb_len,
|
||||
skb->len - ncb_len);
|
||||
put_unaligned_le32(crc, skb->data + skb->len);
|
||||
skb_put(skb, crc_len);
|
||||
/* Only send if data is available. */
|
||||
if (ncm->skb_tx_data) {
|
||||
ncm->timer_force_tx = true;
|
||||
ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
|
||||
ncm->timer_force_tx = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* (d)wDatagramIndex[0] */
|
||||
put_ncm(&tmp, opts->dgram_item_len, ncb_len);
|
||||
/* (d)wDatagramLength[0] */
|
||||
put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len);
|
||||
/* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */
|
||||
|
||||
if (skb->len > MAX_TX_NONFIXED)
|
||||
memset(skb_put(skb, max_size - skb->len),
|
||||
0, max_size - skb->len);
|
||||
|
||||
return skb;
|
||||
/*
|
||||
* The transmit should only be run if no skb data has been sent
|
||||
* for a certain duration.
|
||||
*/
|
||||
static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
|
||||
{
|
||||
struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
|
||||
tasklet_schedule(&ncm->tx_tasklet);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int ncm_unwrap_ntb(struct gether *port,
|
||||
@ -963,6 +1124,7 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
struct f_ncm *ncm = func_to_ncm(&port->func);
|
||||
__le16 *tmp = (void *) skb->data;
|
||||
unsigned index, index2;
|
||||
int ndp_index;
|
||||
unsigned dg_len, dg_len2;
|
||||
unsigned ndp_len;
|
||||
struct sk_buff *skb2;
|
||||
@ -995,91 +1157,101 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
goto err;
|
||||
}
|
||||
|
||||
index = get_ncm(&tmp, opts->fp_index);
|
||||
/* NCM 3.2 */
|
||||
if (((index % 4) != 0) && (index < opts->nth_size)) {
|
||||
INFO(port->func.config->cdev, "Bad index: %x\n",
|
||||
index);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* walk through NDP */
|
||||
tmp = ((void *)skb->data) + index;
|
||||
if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
|
||||
INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
|
||||
goto err;
|
||||
}
|
||||
tmp += 2;
|
||||
|
||||
ndp_len = get_unaligned_le16(tmp++);
|
||||
/*
|
||||
* NCM 3.3.1
|
||||
* entry is 2 items
|
||||
* item size is 16/32 bits, opts->dgram_item_len * 2 bytes
|
||||
* minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
|
||||
*/
|
||||
if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2))
|
||||
|| (ndp_len % opts->ndplen_align != 0)) {
|
||||
INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len);
|
||||
goto err;
|
||||
}
|
||||
tmp += opts->reserved1;
|
||||
tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
|
||||
tmp += opts->reserved2;
|
||||
|
||||
ndp_len -= opts->ndp_size;
|
||||
index2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
dgram_counter = 0;
|
||||
ndp_index = get_ncm(&tmp, opts->ndp_index);
|
||||
|
||||
/* Run through all the NDP's in the NTB */
|
||||
do {
|
||||
index = index2;
|
||||
dg_len = dg_len2;
|
||||
if (dg_len < 14 + crc_len) { /* ethernet header + crc */
|
||||
INFO(port->func.config->cdev, "Bad dgram length: %x\n",
|
||||
dg_len);
|
||||
/* NCM 3.2 */
|
||||
if (((ndp_index % 4) != 0) &&
|
||||
(ndp_index < opts->nth_size)) {
|
||||
INFO(port->func.config->cdev, "Bad index: %#X\n",
|
||||
ndp_index);
|
||||
goto err;
|
||||
}
|
||||
if (ncm->is_crc) {
|
||||
uint32_t crc, crc2;
|
||||
|
||||
crc = get_unaligned_le32(skb->data +
|
||||
index + dg_len - crc_len);
|
||||
crc2 = ~crc32_le(~0,
|
||||
skb->data + index,
|
||||
dg_len - crc_len);
|
||||
if (crc != crc2) {
|
||||
INFO(port->func.config->cdev, "Bad CRC\n");
|
||||
goto err;
|
||||
}
|
||||
/* walk through NDP */
|
||||
tmp = (void *)(skb->data + ndp_index);
|
||||
if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
|
||||
INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
|
||||
goto err;
|
||||
}
|
||||
tmp += 2;
|
||||
|
||||
ndp_len = get_unaligned_le16(tmp++);
|
||||
/*
|
||||
* NCM 3.3.1
|
||||
* entry is 2 items
|
||||
* item size is 16/32 bits, opts->dgram_item_len * 2 bytes
|
||||
* minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
|
||||
* Each entry is a dgram index and a dgram length.
|
||||
*/
|
||||
if ((ndp_len < opts->ndp_size
|
||||
+ 2 * 2 * (opts->dgram_item_len * 2))
|
||||
|| (ndp_len % opts->ndplen_align != 0)) {
|
||||
INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
|
||||
ndp_len);
|
||||
goto err;
|
||||
}
|
||||
tmp += opts->reserved1;
|
||||
/* Check for another NDP (d)wNextNdpIndex */
|
||||
ndp_index = get_ncm(&tmp, opts->next_ndp_index);
|
||||
tmp += opts->reserved2;
|
||||
|
||||
ndp_len -= opts->ndp_size;
|
||||
index2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
dgram_counter = 0;
|
||||
|
||||
if (index2 == 0 || dg_len2 == 0) {
|
||||
skb2 = skb;
|
||||
} else {
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
do {
|
||||
index = index2;
|
||||
dg_len = dg_len2;
|
||||
if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad dgram length: %#X\n", dg_len);
|
||||
goto err;
|
||||
}
|
||||
if (ncm->is_crc) {
|
||||
uint32_t crc, crc2;
|
||||
|
||||
crc = get_unaligned_le32(skb->data +
|
||||
index + dg_len -
|
||||
crc_len);
|
||||
crc2 = ~crc32_le(~0,
|
||||
skb->data + index,
|
||||
dg_len - crc_len);
|
||||
if (crc != crc2) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad CRC\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
index2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
|
||||
/*
|
||||
* Copy the data into a new skb.
|
||||
* This ensures the truesize is correct
|
||||
*/
|
||||
skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
|
||||
dg_len - crc_len);
|
||||
if (skb2 == NULL)
|
||||
goto err;
|
||||
}
|
||||
memcpy(skb_put(skb2, dg_len - crc_len),
|
||||
skb->data + index, dg_len - crc_len);
|
||||
|
||||
if (!skb_pull(skb2, index)) {
|
||||
ret = -EOVERFLOW;
|
||||
goto err;
|
||||
}
|
||||
skb_queue_tail(list, skb2);
|
||||
|
||||
skb_trim(skb2, dg_len - crc_len);
|
||||
skb_queue_tail(list, skb2);
|
||||
ndp_len -= 2 * (opts->dgram_item_len * 2);
|
||||
|
||||
ndp_len -= 2 * (opts->dgram_item_len * 2);
|
||||
dgram_counter++;
|
||||
|
||||
dgram_counter++;
|
||||
if (index2 == 0 || dg_len2 == 0)
|
||||
break;
|
||||
} while (ndp_len > 2 * (opts->dgram_item_len * 2));
|
||||
} while (ndp_index);
|
||||
|
||||
if (index2 == 0 || dg_len2 == 0)
|
||||
break;
|
||||
} while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
VDBG(port->func.config->cdev,
|
||||
"Parsed NTB with %d frames\n", dgram_counter);
|
||||
@ -1097,8 +1269,11 @@ static void ncm_disable(struct usb_function *f)
|
||||
|
||||
DBG(cdev, "ncm deactivated\n");
|
||||
|
||||
if (ncm->port.in_ep->driver_data)
|
||||
if (ncm->port.in_ep->driver_data) {
|
||||
ncm->timer_stopping = true;
|
||||
ncm->netdev = NULL;
|
||||
gether_disconnect(&ncm->port);
|
||||
}
|
||||
|
||||
if (ncm->notify->driver_data) {
|
||||
usb_ep_disable(ncm->notify);
|
||||
@ -1267,6 +1442,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
ncm->port.open = ncm_open;
|
||||
ncm->port.close = ncm_close;
|
||||
|
||||
tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
|
||||
hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
ncm->task_timer.function = ncm_tx_timeout;
|
||||
|
||||
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
ncm->port.in_ep->name, ncm->port.out_ep->name,
|
||||
@ -1380,6 +1559,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
DBG(c->cdev, "ncm unbind\n");
|
||||
|
||||
hrtimer_cancel(&ncm->task_timer);
|
||||
tasklet_kill(&ncm->tx_tasklet);
|
||||
|
||||
ncm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(ncm->notify_req->buf);
|
||||
@ -1416,6 +1599,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
|
||||
ncm->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
ncm->port.is_fixed = true;
|
||||
ncm->port.supports_multi_frame = true;
|
||||
|
||||
ncm->port.func.name = "cdc_network";
|
||||
/* descriptors are per-instance copies */
|
@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
rndis_control_intf.bInterfaceNumber = status;
|
||||
rndis_union_desc.bMasterInterface0 = status;
|
||||
|
||||
if (cdev->use_os_string)
|
||||
f->os_desc_table[0].if_id =
|
||||
rndis_iad_descriptor.bFirstInterface;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
@ -348,14 +348,34 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
spin_lock_init(&uac2->p_prm.lock);
|
||||
runtime->hw.rate_min = p_srate;
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */
|
||||
switch (p_ssize) {
|
||||
case 3:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
|
||||
break;
|
||||
case 4:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
break;
|
||||
default:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
break;
|
||||
}
|
||||
runtime->hw.channels_min = num_channels(p_chmask);
|
||||
runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize
|
||||
/ runtime->hw.periods_min;
|
||||
} else {
|
||||
spin_lock_init(&uac2->c_prm.lock);
|
||||
runtime->hw.rate_min = c_srate;
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */
|
||||
switch (c_ssize) {
|
||||
case 3:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
|
||||
break;
|
||||
case 4:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
break;
|
||||
default:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
break;
|
||||
}
|
||||
runtime->hw.channels_min = num_channels(c_chmask);
|
||||
runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize
|
||||
/ runtime->hw.periods_min;
|
@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *net)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
int length = skb->len;
|
||||
int length = 0;
|
||||
int retval;
|
||||
struct usb_request *req = NULL;
|
||||
unsigned long flags;
|
||||
@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (!in) {
|
||||
if (skb && !in) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* apply outgoing CDC or RNDIS filters */
|
||||
if (!is_promisc(cdc_filter)) {
|
||||
if (skb && !is_promisc(cdc_filter)) {
|
||||
u8 *dest = skb->data;
|
||||
|
||||
if (is_multicast_ether_addr(dest)) {
|
||||
@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
if (dev->port_usb)
|
||||
skb = dev->wrap(dev->port_usb, skb);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
if (!skb)
|
||||
if (!skb) {
|
||||
/* Multi frame CDC protocols may store the frame for
|
||||
* later which is not a dropped frame.
|
||||
*/
|
||||
if (dev->port_usb->supports_multi_frame)
|
||||
goto multiframe;
|
||||
goto drop;
|
||||
|
||||
length = skb->len;
|
||||
}
|
||||
}
|
||||
|
||||
length = skb->len;
|
||||
req->buf = skb->data;
|
||||
req->context = skb;
|
||||
req->complete = tx_complete;
|
||||
@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
dev_kfree_skb_any(skb);
|
||||
drop:
|
||||
dev->net->stats.tx_dropped++;
|
||||
multiframe:
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
if (list_empty(&dev->tx_reqs))
|
||||
netif_start_queue(net);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user