1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2014-08-04 20:11:28 -07:00
commit ae9b475ebe
267 changed files with 11206 additions and 5806 deletions

View File

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

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

View File

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

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

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

View File

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

View 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>;
...
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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