提交 ae9b475e 编写于 作者: L Linus Torvalds

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
  ...
...@@ -3,13 +3,13 @@ Date: May 2007 ...@@ -3,13 +3,13 @@ Date: May 2007
KernelVersion: 2.6.23 KernelVersion: 2.6.23
Contact: Alan Stern <stern@rowland.harvard.edu> Contact: Alan Stern <stern@rowland.harvard.edu>
Description: Description:
If CONFIG_USB_PERSIST is set, then each USB device directory USB device directories can contain a file named power/persist.
will contain a file named power/persist. The file holds a The file holds a boolean value (0 or 1) indicating whether or
boolean value (0 or 1) indicating whether or not the not the "USB-Persist" facility is enabled for the device. For
"USB-Persist" facility is enabled for the device. Since the hubs this facility is always enabled and their device
facility is inherently dangerous, it is disabled by default directories will not contain this file.
for all devices except hubs. For more information, see
Documentation/usb/persist.txt. For more information, see Documentation/usb/persist.txt.
What: /sys/bus/usb/devices/.../power/autosuspend What: /sys/bus/usb/devices/.../power/autosuspend
Date: March 2007 Date: March 2007
......
Link Layer Validation Device is a standard device for testing of Super
Speed Link Layer tests. These nodes are available in sysfs only when lvs
driver is bound with root hub device.
What: /sys/bus/usb/devices/.../get_dev_desc
Date: March 2014
Contact: Pratyush Anand <pratyush.anand@st.com>
Description:
Write to this node to issue "Get Device Descriptor"
for Link Layer Validation device. It is needed for TD.7.06.
What: /sys/bus/usb/devices/.../u1_timeout
Date: March 2014
Contact: Pratyush Anand <pratyush.anand@st.com>
Description:
Set "U1 timeout" for the downstream port where Link Layer
Validation device is connected. Timeout value must be between 0
and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21.
What: /sys/bus/usb/devices/.../u2_timeout
Date: March 2014
Contact: Pratyush Anand <pratyush.anand@st.com>
Description:
Set "U2 timeout" for the downstream port where Link Layer
Validation device is connected. Timeout value must be between 0
and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21.
What: /sys/bus/usb/devices/.../hot_reset
Date: March 2014
Contact: Pratyush Anand <pratyush.anand@st.com>
Description:
Write to this node to issue "Reset" for Link Layer Validation
device. It is needed for TD.7.29, TD.7.31, TD.7.34 and TD.7.35.
What: /sys/bus/usb/devices/.../u3_entry
Date: March 2014
Contact: Pratyush Anand <pratyush.anand@st.com>
Description:
Write to this node to issue "U3 entry" for Link Layer
Validation device. It is needed for TD.7.35 and TD.7.36.
What: /sys/bus/usb/devices/.../u3_exit
Date: March 2014
Contact: Pratyush Anand <pratyush.anand@st.com>
Description:
Write to this node to issue "U3 exit" for Link Layer
Validation device. It is needed for TD.7.36.
...@@ -556,11 +556,11 @@ been converted to this framework. ...@@ -556,11 +556,11 @@ been converted to this framework.
Near-term plans include converting all of them, except for "gadgetfs". Near-term plans include converting all of them, except for "gadgetfs".
</para> </para>
!Edrivers/usb/gadget/f_acm.c !Edrivers/usb/gadget/function/f_acm.c
!Edrivers/usb/gadget/f_ecm.c !Edrivers/usb/gadget/function/f_ecm.c
!Edrivers/usb/gadget/f_subset.c !Edrivers/usb/gadget/function/f_subset.c
!Edrivers/usb/gadget/f_obex.c !Edrivers/usb/gadget/function/f_obex.c
!Edrivers/usb/gadget/f_serial.c !Edrivers/usb/gadget/function/f_serial.c
</sect1> </sect1>
......
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>;
};
};
Hisilicon hix5hd2 SATA PHY
-----------------------
Required properties:
- compatible: should be "hisilicon,hix5hd2-sata-phy"
- reg: offset and length of the PHY registers
- #phy-cells: must be 0
Refer to phy/phy-bindings.txt for the generic PHY binding properties
Optional Properties:
- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
- hisilicon,power-reg: offset and bit number within peripheral-syscon,
register of controlling sata power supply.
Example:
sata_phy: phy@f9900000 {
compatible = "hisilicon,hix5hd2-sata-phy";
reg = <0xf9900000 0x10000>;
#phy-cells = <0>;
hisilicon,peripheral-syscon = <&peripheral_ctrl>;
hisilicon,power-reg = <0x8 10>;
};
...@@ -10,6 +10,10 @@ Required Properties: ...@@ -10,6 +10,10 @@ Required Properties:
provider can use the values in cells to find the appropriate provider can use the values in cells to find the appropriate
PHY. 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: For example:
phys: phy { phys: phy {
......
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>;
...
};
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>;
};
Qualcomm IPQ806x SATA PHY Controller
------------------------------------
SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
Each SATA PHY controller should have its own node.
Required properties:
- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
- reg: offset and length of the SATA PHY register set;
- #phy-cells: must be zero
- clocks: must be exactly one entry
- clock-names: must be "cfg"
Example:
sata_phy: sata-phy@1b400000 {
compatible = "qcom,ipq806x-sata-phy";
reg = <0x1b400000 0x200>;
clocks = <&gcc SATA_PHY_CFG_CLK>;
clock-names = "cfg";
#phy-cells = <0>;
};
...@@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY ...@@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY
Required properties: Required properties:
- compatible : should be one of the listed compatibles: - compatible : should be one of the listed compatibles:
- "samsung,exynos3250-usb2-phy"
- "samsung,exynos4210-usb2-phy" - "samsung,exynos4210-usb2-phy"
- "samsung,exynos4x12-usb2-phy" - "samsung,exynos4x12-usb2-phy"
- "samsung,exynos5250-usb2-phy" - "samsung,exynos5250-usb2-phy"
...@@ -46,6 +47,7 @@ and Exynos 4212) it is as follows: ...@@ -46,6 +47,7 @@ and Exynos 4212) it is as follows:
1 - USB host ("host"), 1 - USB host ("host"),
2 - HSIC0 ("hsic0"), 2 - HSIC0 ("hsic0"),
3 - HSIC1 ("hsic1"), 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 Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
register is supplied. register is supplied.
......
...@@ -9,15 +9,17 @@ Required properties: ...@@ -9,15 +9,17 @@ Required properties:
e.g. USB2_PHY on OMAP5. e.g. USB2_PHY on OMAP5.
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
e.g. USB3 PHY and SATA PHY on OMAP5. 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 "ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
DRA7 platform. DRA7 platform.
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on "ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
AM437 platform. AM437 platform.
- reg : Address and length of the register set for the device. It contains - reg : register ranges as listed in the reg-names property
the address of "otghs_control" for control-phy-otghs or "power" register - reg-names: "otghs_control" for control-phy-otghs
for other types. "power", "pcie_pcs" and "control_sma" for control-phy-pcie
- reg-names: should be "otghs_control" control-phy-otghs and "power" for "power" for all other types
other types.
omap_control_usb: omap-control-usb@4a002300 { omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,control-phy-otghs"; compatible = "ti,control-phy-otghs";
...@@ -56,8 +58,8 @@ usb2phy@4a0ad080 { ...@@ -56,8 +58,8 @@ usb2phy@4a0ad080 {
TI PIPE3 PHY TI PIPE3 PHY
Required properties: Required properties:
- compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata". - compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or
"ti,omap-usb3" is deprecated. "ti,phy-pipe3-pcie. "ti,omap-usb3" is deprecated.
- reg : Address and length of the register set for the device. - reg : Address and length of the register set for the device.
- reg-names: The names of the register addresses corresponding to the registers - reg-names: The names of the register addresses corresponding to the registers
filled in "reg". filled in "reg".
...@@ -69,10 +71,17 @@ Required properties: ...@@ -69,10 +71,17 @@ Required properties:
* "wkupclk" - wakeup clock. * "wkupclk" - wakeup clock.
* "sysclk" - system clock. * "sysclk" - system clock.
* "refclk" - reference 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: Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on - ctrl-module : phandle of the control module used by PHY driver to power on
the PHY. 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. This is usually a subnode of ocp2scp to which it is connected.
......
...@@ -4,6 +4,7 @@ Required properties: ...@@ -4,6 +4,7 @@ Required properties:
- compatible: Should be "fsl,imx27-usb" - compatible: Should be "fsl,imx27-usb"
- reg: Should contain registers location and length - reg: Should contain registers location and length
- interrupts: Should contain controller interrupt - interrupts: Should contain controller interrupt
- fsl,usbphy: phandle of usb phy that connects to the port
Recommended properies: Recommended properies:
- phy_type: the type of the phy connected to the core. Should be one - phy_type: the type of the phy connected to the core. Should be one
...@@ -12,7 +13,6 @@ Recommended properies: ...@@ -12,7 +13,6 @@ Recommended properies:
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" - dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
Optional properties: 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 - fsl,usbmisc: phandler of non-core register device, with one argument
that indicate usb controller index that indicate usb controller index
- vbus-supply: regulator for vbus - vbus-supply: regulator for vbus
......
...@@ -20,6 +20,12 @@ Required properties : ...@@ -20,6 +20,12 @@ Required properties :
Present if phy_type == utmi. Present if phy_type == utmi.
- ulpi-link: The clock Tegra provides to the ULPI PHY (cdev2). - ulpi-link: The clock Tegra provides to the ULPI PHY (cdev2).
Present if phy_type == ulpi, and ULPI link mode is in use. 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: Required properties for phy_type == ulpi:
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY. - nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
...@@ -56,6 +62,8 @@ Optional properties: ...@@ -56,6 +62,8 @@ Optional properties:
host means this is a host controller host means this is a host controller
peripheral means it is device controller peripheral means it is device controller
otg means it can operate as either ("on the go") 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 control (required for dr_mode == otg, optional for dr_mode == host):
- vbus-supply: regulator for VBUS - vbus-supply: regulator for VBUS
...@@ -9,8 +9,9 @@ Required properties: ...@@ -9,8 +9,9 @@ Required properties:
register set for the device. register set for the device.
- interrupts: one XHCI interrupt should be described here. - interrupts: one XHCI interrupt should be described here.
Optional property: Optional properties:
- clocks: reference to a clock - clocks: reference to a clock
- usb3-lpm-capable: determines if platform is USB3 LPM capable
Example: Example:
usb@f0931000 { usb@f0931000 {
......
...@@ -53,10 +53,12 @@ unregister the PHY. ...@@ -53,10 +53,12 @@ unregister the PHY.
The PHY driver should create the PHY in order for other peripheral controllers 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. 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 *phy_create(struct device *dev, struct device_node *node,
struct phy_init_data *init_data); const struct phy_ops *ops,
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, struct phy_init_data *init_data);
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 PHY drivers can use one of the above 2 APIs to create the PHY by passing
the device pointer, phy ops and init_data. the device pointer, phy ops and init_data.
......
...@@ -105,13 +105,13 @@ macros such as these, and use driver_info to store more information. ...@@ -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 A short example, for a driver that supports several specific USB devices
and their quirks, might have a MODULE_DEVICE_TABLE like this: 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 (0x9999, 0xaaaa), driver_info: QUIRK_X },
{ USB_DEVICE (0xbbbb, 0x8888), driver_info: QUIRK_Y|QUIRK_Z }, { USB_DEVICE (0xbbbb, 0x8888), driver_info: QUIRK_Y|QUIRK_Z },
... ...
{ } /* end with an all-zeroes entry */ { } /* 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 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 well as to the module management subsystem. Not all, though: some driver
...@@ -134,7 +134,7 @@ something like this: ...@@ -134,7 +134,7 @@ something like this:
if exposing any operations through usbdevfs: if exposing any operations through usbdevfs:
.ioctl = my_ioctl, .ioctl = my_ioctl,
*/ */
} };
When the USB subsystem knows about a driver's device ID table, it's used when 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 choosing drivers to probe(). The thread doing new device processing checks
......
...@@ -2,8 +2,27 @@ ...@@ -2,8 +2,27 @@
Alan Stern <stern@rowland.harvard.edu> 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? What is Power Management?
...@@ -516,3 +535,225 @@ relevant attribute files is usb2_hardware_lpm. ...@@ -516,3 +535,225 @@ relevant attribute files is usb2_hardware_lpm.
driver will enable hardware LPM for the device. You driver will enable hardware LPM for the device. You
can write y/Y/1 or n/N/0 to the file to enable/disable 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. USB2 hardware LPM manually. This is for test purpose mainly.
USB Port Power Control
----------------------
In addition to suspending endpoint devices and enabling hardware
controlled link power management, the USB subsystem also has the
capability to disable power to ports under some conditions. Power is
controlled through Set/ClearPortFeature(PORT_POWER) requests to a hub.
In the case of a root or platform-internal hub the host controller
driver translates PORT_POWER requests into platform firmware (ACPI)
method calls to set the port power state. For more background see the
Linux Plumbers Conference 2012 slides [1] and video [2]:
Upon receiving a ClearPortFeature(PORT_POWER) request a USB port is
logically off, and may trigger the actual loss of VBUS to the port [3].
VBUS may be maintained in the case where a hub gangs multiple ports into
a shared power well causing power to remain until all ports in the gang
are turned off. VBUS may also be maintained by hub ports configured for
a charging application. In any event a logically off port will lose
connection with its device, not respond to hotplug events, and not
respond to remote wakeup events*.
WARNING: turning off a port may result in the inability to hot add a device.
Please see "User Interface for Port Power Control" for details.
As far as the effect on the device itself it is similar to what a device
goes through during system suspend, i.e. the power session is lost. Any
USB device or driver that misbehaves with system suspend will be
similarly affected by a port power cycle event. For this reason the
implementation shares the same device recovery path (and honors the same
quirks) as the system resume path for the hub.
[1]: http://dl.dropbox.com/u/96820575/sarah-sharp-lpt-port-power-off2-mini.pdf
[2]: http://linuxplumbers.ubicast.tv/videos/usb-port-power-off-kerneluserspace-api/
[3]: USB 3.1 Section 10.12
* wakeup note: if a device is configured to send wakeup events the port
power control implementation will block poweroff attempts on that
port.
User Interface for Port Power Control
-------------------------------------
The port power control mechanism uses the PM runtime system. Poweroff is
requested by clearing the power/pm_qos_no_power_off flag of the port device
(defaults to 1). If the port is disconnected it will immediately receive a
ClearPortFeature(PORT_POWER) request. Otherwise, it will honor the pm runtime
rules and require the attached child device and all descendants to be suspended.
This mechanism is dependent on the hub advertising port power switching in its
hub descriptor (wHubCharacteristics logical power switching mode field).
Note, some interface devices/drivers do not support autosuspend. Userspace may
need to unbind the interface drivers before the usb_device will suspend. An
unbound interface device is suspended by default. When unbinding, be careful
to unbind interface drivers, not the driver of the parent usb device. Also,
leave hub interface drivers bound. If the driver for the usb device (not
interface) is unbound the kernel is no longer able to resume the device. If a
hub interface driver is unbound, control of its child ports is lost and all
attached child-devices will disconnect. A good rule of thumb is that if the
'driver/module' link for a device points to /sys/module/usbcore then unbinding
it will interfere with port power control.
Example of the relevant files for port power control. Note, in this example
these files are relative to a usb hub device (prefix).
prefix=/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1
attached child device +
hub port device + |
hub interface device + | |
v v v
$prefix/3-1:1.0/3-1-port1/device
$prefix/3-1:1.0/3-1-port1/power/pm_qos_no_power_off
$prefix/3-1:1.0/3-1-port1/device/power/control
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf0>/driver/unbind
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf1>/driver/unbind
...
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intfN>/driver/unbind
In addition to these files some ports may have a 'peer' link to a port on
another hub. The expectation is that all superspeed ports have a
hi-speed peer.
$prefix/3-1:1.0/3-1-port1/peer -> ../../../../usb2/2-1/2-1:1.0/2-1-port1
../../../../usb2/2-1/2-1:1.0/2-1-port1/peer -> ../../../../usb3/3-1/3-1:1.0/3-1-port1
Distinct from 'companion ports', or 'ehci/xhci shared switchover ports'
peer ports are simply the hi-speed and superspeed interface pins that
are combined into a single usb3 connector. Peer ports share the same
ancestor XHCI device.
While a superspeed port is powered off a device may downgrade its
connection and attempt to connect to the hi-speed pins. The
implementation takes steps to prevent this:
1/ Port suspend is sequenced to guarantee that hi-speed ports are powered-off
before their superspeed peer is permitted to power-off. The implication is
that the setting pm_qos_no_power_off to zero on a superspeed port may not cause
the port to power-off until its highspeed peer has gone to its runtime suspend
state. Userspace must take care to order the suspensions if it wants to
guarantee that a superspeed port will power-off.
2/ Port resume is sequenced to force a superspeed port to power-on prior to its
highspeed peer.
3/ Port resume always triggers an attached child device to resume. After a
power session is lost the device may have been removed, or need reset.
Resuming the child device when the parent port regains power resolves those
states and clamps the maximum port power cycle frequency at the rate the child
device can suspend (autosuspend-delay) and resume (reset-resume latency).
Sysfs files relevant for port power control:
<hubdev-portX>/power/pm_qos_no_power_off:
This writable flag controls the state of an idle port.
Once all children and descendants have suspended the
port may suspend/poweroff provided that
pm_qos_no_power_off is '0'. If pm_qos_no_power_off is
'1' the port will remain active/powered regardless of
the stats of descendants. Defaults to 1.
<hubdev-portX>/power/runtime_status:
This file reflects whether the port is 'active' (power is on)
or 'suspended' (logically off). There is no indication to
userspace whether VBUS is still supplied.
<hubdev-portX>/connect_type:
An advisory read-only flag to userspace indicating the
location and connection type of the port. It returns
one of four values 'hotplug', 'hardwired', 'not used',
and 'unknown'. All values, besides unknown, are set by
platform firmware.
"hotplug" indicates an externally connectable/visible
port on the platform. Typically userspace would choose
to keep such a port powered to handle new device
connection events.
"hardwired" refers to a port that is not visible but
connectable. Examples are internal ports for USB
bluetooth that can be disconnected via an external
switch or a port with a hardwired USB camera. It is
expected to be safe to allow these ports to suspend
provided pm_qos_no_power_off is coordinated with any
switch that gates connections. Userspace must arrange
for the device to be connected prior to the port
powering off, or to activate the port prior to enabling
connection via a switch.
"not used" refers to an internal port that is expected
to never have a device connected to it. These may be
empty internal ports, or ports that are not physically
exposed on a platform. Considered safe to be
powered-off at all times.
"unknown" means platform firmware does not provide
information for this port. Most commonly refers to
external hub ports which should be considered 'hotplug'
for policy decisions.
NOTE1: since we are relying on the BIOS to get this ACPI
information correct, the USB port descriptions may be
missing or wrong.
NOTE2: Take care in clearing pm_qos_no_power_off. Once
power is off this port will
not respond to new connect events.
Once a child device is attached additional constraints are
applied before the port is allowed to poweroff.
<child>/power/control:
Must be 'auto', and the port will not
power down until <child>/power/runtime_status
reflects the 'suspended' state. Default
value is controlled by child device driver.
<child>/power/persist:
This defaults to '1' for most devices and indicates if
kernel can persist the device's configuration across a
power session loss (suspend / port-power event). When
this value is '0' (quirky devices), port poweroff is
disabled.
<child>/driver/unbind:
Wakeup capable devices will block port poweroff. At
this time the only mechanism to clear the usb-internal
wakeup-capability for an interface device is to unbind
its driver.
Summary of poweroff pre-requisite settings relative to a port device:
echo 0 > power/pm_qos_no_power_off
echo 0 > peer/power/pm_qos_no_power_off # if it exists
echo auto > power/control # this is the default value
echo auto > <child>/power/control
echo 1 > <child>/power/persist # this is the default value
Suggested Userspace Port Power Policy
-------------------------------------
As noted above userspace needs to be careful and deliberate about what
ports are enabled for poweroff.
The default configuration is that all ports start with
power/pm_qos_no_power_off set to '1' causing ports to always remain
active.
Given confidence in the platform firmware's description of the ports
(ACPI _PLD record for a port populates 'connect_type') userspace can
clear pm_qos_no_power_off for all 'not used' ports. The same can be
done for 'hardwired' ports provided poweroff is coordinated with any
connection switch for the port.
A more aggressive userspace policy is to enable USB port power off for
all ports (set <hubdev-portX>/power/pm_qos_no_power_off to '0') when
some external factor indicates the user has stopped interacting with the
system. For example, a distro may want to enable power off all USB
ports when the screen blanks, and re-power them when the screen becomes
active. Smart phones and tablets may want to power off USB ports when
the user pushes the power button.
...@@ -657,6 +657,8 @@ ...@@ -657,6 +657,8 @@
<&tegra_car TEGRA114_CLK_PLL_U>, <&tegra_car TEGRA114_CLK_PLL_U>,
<&tegra_car TEGRA114_CLK_USBD>; <&tegra_car TEGRA114_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
...@@ -667,6 +669,7 @@ ...@@ -667,6 +669,7 @@
nvidia,hssquelch-level = <2>; nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>; nvidia,hsdiscon-level = <5>;
nvidia,xcvr-hsslew = <12>; nvidia,xcvr-hsslew = <12>;
nvidia,has-utmi-pad-registers;
status = "disabled"; status = "disabled";
}; };
...@@ -690,6 +693,8 @@ ...@@ -690,6 +693,8 @@
<&tegra_car TEGRA114_CLK_PLL_U>, <&tegra_car TEGRA114_CLK_PLL_U>,
<&tegra_car TEGRA114_CLK_USBD>; <&tegra_car TEGRA114_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
......
...@@ -613,6 +613,8 @@ ...@@ -613,6 +613,8 @@
<&tegra_car TEGRA124_CLK_PLL_U>, <&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>; <&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
...@@ -647,6 +649,8 @@ ...@@ -647,6 +649,8 @@
<&tegra_car TEGRA124_CLK_PLL_U>, <&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>; <&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
...@@ -657,6 +661,7 @@ ...@@ -657,6 +661,7 @@
nvidia,hssquelch-level = <2>; nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>; nvidia,hsdiscon-level = <5>;
nvidia,xcvr-hsslew = <12>; nvidia,xcvr-hsslew = <12>;
nvidia,has-utmi-pad-registers;
status = "disabled"; status = "disabled";
}; };
...@@ -681,6 +686,8 @@ ...@@ -681,6 +686,8 @@
<&tegra_car TEGRA124_CLK_PLL_U>, <&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>; <&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
......
...@@ -630,6 +630,8 @@ ...@@ -630,6 +630,8 @@
<&tegra_car TEGRA20_CLK_CLK_M>, <&tegra_car TEGRA20_CLK_CLK_M>,
<&tegra_car TEGRA20_CLK_USBD>; <&tegra_car TEGRA20_CLK_USBD>;
clock-names = "reg", "pll_u", "timer", "utmi-pads"; 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,has-legacy-mode;
nvidia,hssync-start-delay = <9>; nvidia,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
...@@ -638,6 +640,7 @@ ...@@ -638,6 +640,7 @@
nvidia,xcvr-setup = <9>; nvidia,xcvr-setup = <9>;
nvidia,xcvr-lsfslew = <1>; nvidia,xcvr-lsfslew = <1>;
nvidia,xcvr-lsrslew = <1>; nvidia,xcvr-lsrslew = <1>;
nvidia,has-utmi-pad-registers;
status = "disabled"; status = "disabled";
}; };
...@@ -661,6 +664,8 @@ ...@@ -661,6 +664,8 @@
<&tegra_car TEGRA20_CLK_PLL_U>, <&tegra_car TEGRA20_CLK_PLL_U>,
<&tegra_car TEGRA20_CLK_CDEV2>; <&tegra_car TEGRA20_CLK_CDEV2>;
clock-names = "reg", "pll_u", "ulpi-link"; clock-names = "reg", "pll_u", "ulpi-link";
resets = <&tegra_car 58>, <&tegra_car 22>;
reset-names = "usb", "utmi-pads";
status = "disabled"; status = "disabled";
}; };
...@@ -685,6 +690,8 @@ ...@@ -685,6 +690,8 @@
<&tegra_car TEGRA20_CLK_CLK_M>, <&tegra_car TEGRA20_CLK_CLK_M>,
<&tegra_car TEGRA20_CLK_USBD>; <&tegra_car TEGRA20_CLK_USBD>;
clock-names = "reg", "pll_u", "timer", "utmi-pads"; 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,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
......
...@@ -775,6 +775,8 @@ ...@@ -775,6 +775,8 @@
<&tegra_car TEGRA30_CLK_PLL_U>, <&tegra_car TEGRA30_CLK_PLL_U>,
<&tegra_car TEGRA30_CLK_USBD>; <&tegra_car TEGRA30_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
...@@ -786,6 +788,7 @@ ...@@ -786,6 +788,7 @@
nvidia,xcvr-hsslew = <32>; nvidia,xcvr-hsslew = <32>;
nvidia,hssquelch-level = <2>; nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>; nvidia,hsdiscon-level = <5>;
nvidia,has-utmi-pad-registers;
status = "disabled"; status = "disabled";
}; };
...@@ -809,6 +812,8 @@ ...@@ -809,6 +812,8 @@
<&tegra_car TEGRA30_CLK_PLL_U>, <&tegra_car TEGRA30_CLK_PLL_U>,
<&tegra_car TEGRA30_CLK_USBD>; <&tegra_car TEGRA30_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
...@@ -843,6 +848,8 @@ ...@@ -843,6 +848,8 @@
<&tegra_car TEGRA30_CLK_PLL_U>, <&tegra_car TEGRA30_CLK_PLL_U>,
<&tegra_car TEGRA30_CLK_USBD>; <&tegra_car TEGRA30_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads"; 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,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>; nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>; nvidia,elastic-limit = <16>;
......
...@@ -15,6 +15,13 @@ config GENERIC_PHY ...@@ -15,6 +15,13 @@ config GENERIC_PHY
phy users can obtain reference to the PHY. All the users of this phy users can obtain reference to the PHY. All the users of this
framework should select this config. 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 config PHY_EXYNOS_MIPI_VIDEO
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM depends on HAS_IOMEM
...@@ -27,10 +34,20 @@ config PHY_EXYNOS_MIPI_VIDEO ...@@ -27,10 +34,20 @@ config PHY_EXYNOS_MIPI_VIDEO
config PHY_MVEBU_SATA config PHY_MVEBU_SATA
def_bool y def_bool y
depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
depends on OF depends on OF
select GENERIC_PHY 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 config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver" tristate "OMAP CONTROL PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on ARCH_OMAP2PLUS || COMPILE_TEST
...@@ -109,6 +126,14 @@ config PHY_EXYNOS5250_SATA ...@@ -109,6 +126,14 @@ config PHY_EXYNOS5250_SATA
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
port to accept one SATA device. 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 config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver" tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF depends on ARCH_SUNXI && HAS_IOMEM && OF
...@@ -124,50 +149,39 @@ config PHY_SUN4I_USB ...@@ -124,50 +149,39 @@ config PHY_SUN4I_USB
config PHY_SAMSUNG_USB2 config PHY_SAMSUNG_USB2
tristate "Samsung USB 2.0 PHY driver" tristate "Samsung USB 2.0 PHY driver"
depends on HAS_IOMEM depends on HAS_IOMEM
depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2
select GENERIC_PHY select GENERIC_PHY
select MFD_SYSCON select MFD_SYSCON
default ARCH_EXYNOS
help help
Enable this to support the Samsung USB 2.0 PHY driver for Samsung 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 SoCs. This driver provides the interface for USB 2.0 PHY. Support
particular SoCs has to be enabled in addition to this driver. Number for particular PHYs will be enabled based on the SoC type in addition
and type of supported phys depends on the SoC. to this driver.
config PHY_EXYNOS4210_USB2 config PHY_EXYNOS4210_USB2
bool "Support for Exynos 4210" bool
depends on PHY_SAMSUNG_USB2 depends on PHY_SAMSUNG_USB2
depends on CPU_EXYNOS4210 default CPU_EXYNOS4210
help
Enable USB PHY support for Exynos 4210. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4210 four
phys are available - device, host, HSIC0 and HSIC1.
config PHY_EXYNOS4X12_USB2 config PHY_EXYNOS4X12_USB2
bool "Support for Exynos 4x12" bool
depends on PHY_SAMSUNG_USB2 depends on PHY_SAMSUNG_USB2
depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
help
Enable USB PHY support for Exynos 4x12. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4x12 four
phys are available - device, host, HSIC0 and HSIC1.
config PHY_EXYNOS5250_USB2 config PHY_EXYNOS5250_USB2
bool "Support for Exynos 5250" bool
depends on PHY_SAMSUNG_USB2 depends on PHY_SAMSUNG_USB2
depends on SOC_EXYNOS5250 default SOC_EXYNOS5250 || SOC_EXYNOS5420
help
Enable USB PHY support for Exynos 5250. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 5250 four
phys are available - device, host, HSIC0 and HSIC.
config PHY_EXYNOS5_USBDRD config PHY_EXYNOS5_USBDRD
tristate "Exynos5 SoC series USB DRD PHY driver" tristate "Exynos5 SoC series USB DRD PHY driver"
depends on ARCH_EXYNOS5 && OF depends on ARCH_EXYNOS5 && OF
depends on HAS_IOMEM depends on HAS_IOMEM
depends on USB_DWC3_EXYNOS
select GENERIC_PHY select GENERIC_PHY
select MFD_SYSCON select MFD_SYSCON
default y
help help
Enable USB DRD PHY support for Exynos 5 SoC series. Enable USB DRD PHY support for Exynos 5 SoC series.
This driver provides PHY interface for USB 3.0 DRD controller This driver provides PHY interface for USB 3.0 DRD controller
...@@ -180,4 +194,18 @@ config PHY_XGENE ...@@ -180,4 +194,18 @@ config PHY_XGENE
help help
This option enables support for APM X-Gene SoC multi-purpose PHY. 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 endmenu
...@@ -3,15 +3,18 @@ ...@@ -3,15 +3,18 @@
# #
obj-$(CONFIG_GENERIC_PHY) += phy-core.o 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_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.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_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.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_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.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_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
phy-exynos-usb2-y += phy-samsung-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 ...@@ -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 phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
...@@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) ...@@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy); platform_set_drvdata(pdev, phy);
gphy = devm_phy_create(dev, &ops, NULL); gphy = devm_phy_create(dev, NULL, &ops, NULL);
if (IS_ERR(gphy)) if (IS_ERR(gphy))
return PTR_ERR(gphy); return PTR_ERR(gphy);
......
/*
* Marvell Berlin SATA PHY driver
*
* Copyright (C) 2014 Marvell Technology Group Ltd.
*
* Antoine Ténart <antoine.tenart@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#define HOST_VSA_ADDR 0x0
#define HOST_VSA_DATA 0x4
#define PORT_SCR_CTL 0x2c
#define PORT_VSR_ADDR 0x78
#define PORT_VSR_DATA 0x7c
#define CONTROL_REGISTER 0x0
#define MBUS_SIZE_CONTROL 0x4
#define POWER_DOWN_PHY0 BIT(6)
#define POWER_DOWN_PHY1 BIT(14)
#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
#define PHY_BASE 0x200
/* register 0x01 */
#define REF_FREF_SEL_25 BIT(0)
#define PHY_MODE_SATA (0x0 << 5)
/* register 0x02 */
#define USE_MAX_PLL_RATE BIT(12)
/* register 0x23 */
#define DATA_BIT_WIDTH_10 (0x0 << 10)
#define DATA_BIT_WIDTH_20 (0x1 << 10)
#define DATA_BIT_WIDTH_40 (0x2 << 10)
/* register 0x25 */
#define PHY_GEN_MAX_1_5 (0x0 << 10)
#define PHY_GEN_MAX_3_0 (0x1 << 10)
#define PHY_GEN_MAX_6_0 (0x2 << 10)
struct phy_berlin_desc {
struct phy *phy;
u32 power_bit;
unsigned index;
};
struct phy_berlin_priv {
void __iomem *base;
spinlock_t lock;
struct clk *clk;
struct phy_berlin_desc **phys;
unsigned nphys;
};
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
u32 mask, u32 val)
{
u32 regval;
/* select register */
writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
/* set bits */
regval = readl(ctrl_reg + PORT_VSR_DATA);
regval &= ~mask;
regval |= val;
writel(regval, ctrl_reg + PORT_VSR_DATA);
}
static int phy_berlin_sata_power_on(struct phy *phy)
{
struct phy_berlin_desc *desc = phy_get_drvdata(phy);
struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80);
int ret = 0;
u32 regval;
clk_prepare_enable(priv->clk);
spin_lock(&priv->lock);
/* Power on PHY */
writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
regval = readl(priv->base + HOST_VSA_DATA);
regval &= ~desc->power_bit;
writel(regval, priv->base + HOST_VSA_DATA);
/* Configure MBus */
writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR);
regval = readl(priv->base + HOST_VSA_DATA);
regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128;
writel(regval, priv->base + HOST_VSA_DATA);
/* set PHY mode and ref freq to 25 MHz */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
REF_FREF_SEL_25 | PHY_MODE_SATA);
/* set PHY up to 6 Gbps */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
/* set 40 bits width */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
/* use max pll rate */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
/* set Gen3 controller speed */
regval = readl(ctrl_reg + PORT_SCR_CTL);
regval &= ~GENMASK(7, 4);
regval |= 0x30;
writel(regval, ctrl_reg + PORT_SCR_CTL);
spin_unlock(&priv->lock);
clk_disable_unprepare(priv->clk);
return ret;
}
static int phy_berlin_sata_power_off(struct phy *phy)
{
struct phy_berlin_desc *desc = phy_get_drvdata(phy);
struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
u32 regval;
clk_prepare_enable(priv->clk);
spin_lock(&priv->lock);
/* Power down PHY */
writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
regval = readl(priv->base + HOST_VSA_DATA);
regval |= desc->power_bit;
writel(regval, priv->base + HOST_VSA_DATA);
spin_unlock(&priv->lock);
clk_disable_unprepare(priv->clk);
return 0;
}
static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct phy_berlin_priv *priv = dev_get_drvdata(dev);
int i;
if (WARN_ON(args->args[0] >= priv->nphys))
return ERR_PTR(-ENODEV);
for (i = 0; i < priv->nphys; i++) {
if (priv->phys[i]->index == args->args[0])
break;
}
if (i == priv->nphys)
return ERR_PTR(-ENODEV);
return priv->phys[i]->phy;
}
static struct phy_ops phy_berlin_sata_ops = {
.power_on = phy_berlin_sata_power_on,
.power_off = phy_berlin_sata_power_off,
.owner = THIS_MODULE,
};
static u32 phy_berlin_power_down_bits[] = {
POWER_DOWN_PHY0,
POWER_DOWN_PHY1,
};
static int phy_berlin_sata_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child;
struct phy *phy;
struct phy_provider *phy_provider;
struct phy_berlin_priv *priv;
struct resource *res;
int i = 0;
u32 phy_id;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
priv->base = devm_ioremap(dev, res->start, resource_size(res));
if (!priv->base)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->nphys = of_get_child_count(dev->of_node);
if (priv->nphys == 0)
return -ENODEV;
priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
GFP_KERNEL);
if (!priv->phys)
return -ENOMEM;
dev_set_drvdata(dev, priv);
spin_lock_init(&priv->lock);
for_each_available_child_of_node(dev->of_node, child) {
struct phy_berlin_desc *phy_desc;
if (of_property_read_u32(child, "reg", &phy_id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
return -EINVAL;
}
if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
dev_err(dev, "invalid reg in node %s\n", child->name);
return -EINVAL;
}
phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
if (!phy_desc)
return -ENOMEM;
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", phy_id);
return PTR_ERR(phy);
}
phy_desc->phy = phy;
phy_desc->power_bit = phy_berlin_power_down_bits[phy_id];
phy_desc->index = phy_id;
phy_set_drvdata(phy, phy_desc);
priv->phys[i++] = phy_desc;
/* Make sure the PHY is off */
phy_berlin_sata_power_off(phy);
}
phy_provider =
devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
static const struct of_device_id phy_berlin_sata_of_match[] = {
{ .compatible = "marvell,berlin2q-sata-phy" },
{ },
};
static struct platform_driver phy_berlin_sata_driver = {
.probe = phy_berlin_sata_probe,
.driver = {
.name = "phy-berlin-sata",
.owner = THIS_MODULE,
.of_match_table = phy_berlin_sata_of_match,
},
};
module_platform_driver(phy_berlin_sata_driver);
MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver");
MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
MODULE_LICENSE("GPL v2");
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
static struct class *phy_class; static struct class *phy_class;
static DEFINE_MUTEX(phy_provider_mutex); static DEFINE_MUTEX(phy_provider_mutex);
...@@ -86,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port) ...@@ -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) static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{ {
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
struct device_node *child;
list_for_each_entry(phy_provider, &phy_provider_list, list) { list_for_each_entry(phy_provider, &phy_provider_list, list) {
if (phy_provider->dev->of_node == node) if (phy_provider->dev->of_node == node)
return phy_provider; 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); return ERR_PTR(-EPROBE_DEFER);
...@@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy) ...@@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy)
if (!phy) if (!phy)
return 0; return 0;
if (phy->pwr) {
ret = regulator_enable(phy->pwr);
if (ret)
return ret;
}
ret = phy_pm_runtime_get_sync(phy); ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP) if (ret < 0 && ret != -ENOTSUPP)
return ret; return ret;
...@@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy) ...@@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy)
out: out:
mutex_unlock(&phy->mutex); mutex_unlock(&phy->mutex);
phy_pm_runtime_put_sync(phy); phy_pm_runtime_put_sync(phy);
if (phy->pwr)
regulator_disable(phy->pwr);
return ret; return ret;
} }
...@@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy) ...@@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy)
mutex_unlock(&phy->mutex); mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy); phy_pm_runtime_put(phy);
if (phy->pwr)
regulator_disable(phy->pwr);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(phy_power_off); EXPORT_SYMBOL_GPL(phy_power_off);
...@@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args ...@@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
struct phy *phy; struct phy *phy;
struct class_dev_iter iter; struct class_dev_iter iter;
struct device_node *node = dev->of_node; struct device_node *node = dev->of_node;
struct device_node *child;
class_dev_iter_init(&iter, phy_class, NULL, NULL); class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) { while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev); 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; continue;
}
phy_found:
class_dev_iter_exit(&iter); class_dev_iter_exit(&iter);
return phy; return phy;
} }
...@@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get); ...@@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
/** /**
* phy_create() - create a new phy * phy_create() - create a new phy
* @dev: device that is creating the new phy * @dev: device that is creating the new phy
* @node: device node of the phy
* @ops: function pointers for performing phy operations * @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL * @init_data: contains the list of PHY consumers or NULL
* *
* Called to create a phy using phy framework. * Called to create a phy using phy framework.
*/ */
struct phy *phy_create(struct device *dev, const struct phy_ops *ops, struct phy *phy_create(struct device *dev, struct device_node *node,
struct phy_init_data *init_data) const struct phy_ops *ops,
struct phy_init_data *init_data)
{ {
int ret; int ret;
int id; int id;
...@@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, ...@@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
goto free_phy; 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); device_initialize(&phy->dev);
mutex_init(&phy->mutex); mutex_init(&phy->mutex);
phy->dev.class = phy_class; phy->dev.class = phy_class;
phy->dev.parent = dev; phy->dev.parent = dev;
phy->dev.of_node = dev->of_node; phy->dev.of_node = node ?: dev->of_node;
phy->id = id; phy->id = id;
phy->ops = ops; phy->ops = ops;
phy->init_data = init_data; phy->init_data = init_data;
...@@ -617,6 +653,9 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, ...@@ -617,6 +653,9 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
put_device(&phy->dev); /* calls phy_release() which frees resources */ put_device(&phy->dev); /* calls phy_release() which frees resources */
return ERR_PTR(ret); return ERR_PTR(ret);
free_ida:
ida_simple_remove(&phy_ida, phy->id);
free_phy: free_phy:
kfree(phy); kfree(phy);
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -626,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create); ...@@ -626,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create);
/** /**
* devm_phy_create() - create a new phy * devm_phy_create() - create a new phy
* @dev: device that is creating the new phy * @dev: device that is creating the new phy
* @node: device node of the phy
* @ops: function pointers for performing phy operations * @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL * @init_data: contains the list of PHY consumers or NULL
* *
...@@ -634,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create); ...@@ -634,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create);
* On driver detach, release function is invoked on the devres data, * On driver detach, release function is invoked on the devres data,
* then, devres data is freed. * then, devres data is freed.
*/ */
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, struct phy *devm_phy_create(struct device *dev, struct device_node *node,
struct phy_init_data *init_data) const struct phy_ops *ops,
struct phy_init_data *init_data)
{ {
struct phy **ptr, *phy; struct phy **ptr, *phy;
...@@ -643,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, ...@@ -643,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
if (!ptr) if (!ptr)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
phy = phy_create(dev, ops, init_data); phy = phy_create(dev, node, ops, init_data);
if (!IS_ERR(phy)) { if (!IS_ERR(phy)) {
*ptr = phy; *ptr = phy;
devres_add(dev, ptr); devres_add(dev, ptr);
...@@ -800,6 +841,7 @@ static void phy_release(struct device *dev) ...@@ -800,6 +841,7 @@ static void phy_release(struct device *dev)
phy = to_phy(dev); phy = to_phy(dev);
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
regulator_put(phy->pwr);
ida_simple_remove(&phy_ida, phy->id); ida_simple_remove(&phy_ida, phy->id);
kfree(phy); kfree(phy);
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -76,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) ...@@ -76,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
if (IS_ERR(state->regs)) if (IS_ERR(state->regs))
return PTR_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)) { if (IS_ERR(phy)) {
dev_err(dev, "failed to create Display Port PHY\n"); dev_err(dev, "failed to create Display Port PHY\n");
return PTR_ERR(phy); return PTR_ERR(phy);
...@@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) ...@@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
phy_set_drvdata(phy, state); phy_set_drvdata(phy, state);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 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[] = { static const struct of_device_id exynos_dp_video_phy_of_match[] = {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -135,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) ...@@ -135,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
spin_lock_init(&state->slock); spin_lock_init(&state->slock);
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { 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); &exynos_mipi_video_phy_ops, NULL);
if (IS_ERR(phy)) { if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", i); 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) ...@@ -149,10 +150,8 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev, phy_provider = devm_of_phy_provider_register(dev,
exynos_mipi_video_phy_xlate); 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[] = { static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
......
...@@ -67,6 +67,8 @@ ...@@ -67,6 +67,8 @@
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) #define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 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_ID_PULLUP BIT(3)
#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) #define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4)
#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) #define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7)
...@@ -86,13 +88,23 @@ ...@@ -86,13 +88,23 @@
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) #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_PHY1 BIT(4)
#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5) #define EXYNOS_4x12_URSTCON_HSIC0 BIT(6)
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6) #define EXYNOS_4x12_URSTCON_HSIC1 BIT(5)
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) #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_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 */ /* Isolation, configured in the power management unit */
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
...@@ -187,7 +199,12 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) ...@@ -187,7 +199,12 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; 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 |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON;
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); 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) ...@@ -198,27 +215,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
u32 phypwr = 0; u32 phypwr = 0;
u32 rst; u32 rst;
u32 pwr; u32 pwr;
u32 mode = 0;
u32 switch_mode = 0;
switch (inst->cfg->id) { switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE: case EXYNOS4x12_DEVICE:
phypwr = EXYNOS_4x12_UPHYPWR_PHY0; phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
rstbits = EXYNOS_4x12_URSTCON_PHY0; rstbits = EXYNOS_4x12_URSTCON_PHY0;
mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
switch_mode = 1;
break; break;
case EXYNOS4x12_HOST: case EXYNOS4x12_HOST:
phypwr = EXYNOS_4x12_UPHYPWR_PHY1; phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY; rstbits = EXYNOS_4x12_URSTCON_HOST_PHY |
mode = EXYNOS_4x12_MODE_SWITCH_HOST; EXYNOS_4x12_URSTCON_PHY1 |
switch_mode = 1; EXYNOS_4x12_URSTCON_HOST_LINK_P0;
break; break;
case EXYNOS4x12_HSIC0: case EXYNOS4x12_HSIC0:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
rstbits = EXYNOS_4x12_URSTCON_HSIC1 | rstbits = EXYNOS_4x12_URSTCON_HSIC0 |
EXYNOS_4x12_URSTCON_HOST_LINK_P0 | EXYNOS_4x12_URSTCON_HOST_LINK_P1;
EXYNOS_4x12_URSTCON_HOST_PHY;
break; break;
case EXYNOS4x12_HSIC1: case EXYNOS4x12_HSIC1:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
...@@ -228,11 +240,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) ...@@ -228,11 +240,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
}; };
if (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 = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
pwr &= ~phypwr; pwr &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); 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) ...@@ -253,41 +260,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
} }
} }
static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst)
{ {
struct samsung_usb2_phy_driver *drv = inst->drv; if (inst->int_cnt++ > 0)
return;
inst->enabled = 1;
exynos4x12_setup_clk(inst); exynos4x12_setup_clk(inst);
exynos4x12_phy_pwr(inst, 1);
exynos4x12_isol(inst, 0); 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;
if (inst->ext_cnt++ > 0)
return 0;
/* Power on the device, as it is necessary for HSIC to work */ if (inst->cfg->id == EXYNOS4x12_HOST) {
if (inst->cfg->id == EXYNOS4x12_HSIC0) { regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
struct samsung_usb2_phy_instance *device = EXYNOS_4x12_MODE_SWITCH_MASK,
&drv->instances[EXYNOS4x12_DEVICE]; EXYNOS_4x12_MODE_SWITCH_HOST);
exynos4x12_phy_pwr(device, 1); exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
exynos4x12_isol(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_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; return 0;
} }
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst)
{ {
struct samsung_usb2_phy_driver *drv = inst->drv; if (inst->int_cnt-- > 1)
struct samsung_usb2_phy_instance *device = return;
&drv->instances[EXYNOS4x12_DEVICE];
inst->enabled = 0;
exynos4x12_isol(inst, 1); exynos4x12_isol(inst, 1);
exynos4x12_phy_pwr(inst, 0); exynos4x12_phy_pwr(inst, 0);
}
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
if (inst->ext_cnt-- > 1)
return 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 && !device->enabled) { if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
exynos4x12_isol(device, 1); inst->cfg->id == EXYNOS4x12_HSIC1) {
exynos4x12_phy_pwr(device, 0); exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]);
} }
exynos4x12_power_off_int(inst);
return 0; return 0;
} }
...@@ -320,6 +364,13 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = { ...@@ -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 = { const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
.has_mode_switch = 1, .has_mode_switch = 1,
.num_phys = EXYNOS4x12_NUM_PHYS, .num_phys = EXYNOS4x12_NUM_PHYS,
......
...@@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = { ...@@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
.owner = THIS_MODULE, .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, .id = EXYNOS5_DRDPHY_UTMI,
.phy_isol = exynos5_usbdrd_phy_isol, .phy_isol = exynos5_usbdrd_phy_isol,
...@@ -521,13 +521,13 @@ const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { ...@@ -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, .phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
.pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_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, .phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
}; };
...@@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) ...@@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
dev_vdbg(dev, "Creating usbdrd_phy phy\n"); dev_vdbg(dev, "Creating usbdrd_phy phy\n");
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { 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); NULL);
if (IS_ERR(phy)) { if (IS_ERR(phy)) {
dev_err(dev, "Failed to create usbdrd_phy phy\n"); dev_err(dev, "Failed to create usbdrd_phy phy\n");
......
...@@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) ...@@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
return ret; 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)) { if (IS_ERR(sata_phy->phy)) {
clk_disable_unprepare(sata_phy->phyclk); clk_disable_unprepare(sata_phy->phyclk);
dev_err(dev, "failed to create PHY\n"); dev_err(dev, "failed to create PHY\n");
......
...@@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) ...@@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
break; break;
} }
inst->enabled = 1;
exynos5250_isol(inst, 0); exynos5250_isol(inst, 0);
return 0; return 0;
...@@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) ...@@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
u32 otg; u32 otg;
u32 hsic; u32 hsic;
inst->enabled = 0;
exynos5250_isol(inst, 1); exynos5250_isol(inst, 1);
switch (inst->cfg->id) { switch (inst->cfg->id) {
......
/*
* 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");
/*
* Copyright (C) 2014 STMicroelectronics – All Rights Reserved
*
* STMicroelectronics PHY driver MiPHY365 (for SoC STiH416).
*
* Authors: Alexandre Torgue <alexandre.torgue@st.com>
* Lee Jones <lee.jones@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
*/
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <dt-bindings/phy/phy-miphy365x.h>
#define HFC_TIMEOUT 100
#define SYSCFG_SELECT_SATA_MASK BIT(1)
#define SYSCFG_SELECT_SATA_POS 1
/* MiPHY365x register definitions */
#define RESET_REG 0x00
#define RST_PLL BIT(1)
#define RST_PLL_CAL BIT(2)
#define RST_RX BIT(4)
#define RST_MACRO BIT(7)
#define STATUS_REG 0x01
#define IDLL_RDY BIT(0)
#define PLL_RDY BIT(1)
#define DES_BIT_LOCK BIT(2)
#define DES_SYMBOL_LOCK BIT(3)
#define CTRL_REG 0x02
#define TERM_EN BIT(0)
#define PCI_EN BIT(2)
#define DES_BIT_LOCK_EN BIT(3)
#define TX_POL BIT(5)
#define INT_CTRL_REG 0x03
#define BOUNDARY1_REG 0x10
#define SPDSEL_SEL BIT(0)
#define BOUNDARY3_REG 0x12
#define TX_SPDSEL_GEN1_VAL 0
#define TX_SPDSEL_GEN2_VAL 0x01
#define TX_SPDSEL_GEN3_VAL 0x02
#define RX_SPDSEL_GEN1_VAL 0
#define RX_SPDSEL_GEN2_VAL (0x01 << 3)
#define RX_SPDSEL_GEN3_VAL (0x02 << 3)
#define PCIE_REG 0x16
#define BUF_SEL_REG 0x20
#define CONF_GEN_SEL_GEN3 0x02
#define CONF_GEN_SEL_GEN2 0x01
#define PD_VDDTFILTER BIT(4)
#define TXBUF1_REG 0x21
#define SWING_VAL 0x04
#define SWING_VAL_GEN1 0x03
#define PREEMPH_VAL (0x3 << 5)
#define TXBUF2_REG 0x22
#define TXSLEW_VAL 0x2
#define TXSLEW_VAL_GEN1 0x4
#define RXBUF_OFFSET_CTRL_REG 0x23
#define RXBUF_REG 0x25
#define SDTHRES_VAL 0x01
#define EQ_ON3 (0x03 << 4)
#define EQ_ON1 (0x01 << 4)
#define COMP_CTRL1_REG 0x40
#define START_COMSR BIT(0)
#define START_COMZC BIT(1)
#define COMSR_DONE BIT(2)
#define COMZC_DONE BIT(3)
#define COMP_AUTO_LOAD BIT(4)
#define COMP_CTRL2_REG 0x41
#define COMP_2MHZ_RAT_GEN1 0x1e
#define COMP_2MHZ_RAT 0xf
#define COMP_CTRL3_REG 0x42
#define COMSR_COMP_REF 0x33
#define COMP_IDLL_REG 0x47
#define COMZC_IDLL 0x2a
#define PLL_CTRL1_REG 0x50
#define PLL_START_CAL BIT(0)
#define BUF_EN BIT(2)
#define SYNCHRO_TX BIT(3)
#define SSC_EN BIT(6)
#define CONFIG_PLL BIT(7)
#define PLL_CTRL2_REG 0x51
#define BYPASS_PLL_CAL BIT(1)
#define PLL_RAT_REG 0x52
#define PLL_SSC_STEP_MSB_REG 0x56
#define PLL_SSC_STEP_MSB_VAL 0x03
#define PLL_SSC_STEP_LSB_REG 0x57
#define PLL_SSC_STEP_LSB_VAL 0x63
#define PLL_SSC_PER_MSB_REG 0x58
#define PLL_SSC_PER_MSB_VAL 0
#define PLL_SSC_PER_LSB_REG 0x59
#define PLL_SSC_PER_LSB_VAL 0xf1
#define IDLL_TEST_REG 0x72
#define START_CLK_HF BIT(6)
#define DES_BITLOCK_REG 0x86
#define BIT_LOCK_LEVEL 0x01
#define BIT_LOCK_CNT_512 (0x03 << 5)
struct miphy365x_phy {
struct phy *phy;
void __iomem *base;
bool pcie_tx_pol_inv;
bool sata_tx_pol_inv;
u32 sata_gen;
u64 ctrlreg;
u8 type;
};
struct miphy365x_dev {
struct device *dev;
struct regmap *regmap;
struct mutex miphy_mutex;
struct miphy365x_phy **phys;
};
/*
* These values are represented in Device tree. They are considered to be ABI
* and although they can be extended any existing values must not change.
*/
enum miphy_sata_gen {
SATA_GEN1 = 1,
SATA_GEN2,
SATA_GEN3
};
static u8 rx_tx_spd[] = {
TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL,
TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL,
TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL
};
/*
* This function selects the system configuration,
* either two SATA, one SATA and one PCIe, or two PCIe lanes.
*/
static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
return regmap_update_bits(miphy_dev->regmap,
(unsigned int)miphy_phy->ctrlreg,
SYSCFG_SELECT_SATA_MASK,
sata << SYSCFG_SELECT_SATA_POS);
}
static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
u8 val;
if (miphy_phy->pcie_tx_pol_inv) {
/* Invert Tx polarity and clear pci_txdetect_pol bit */
val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL;
writeb_relaxed(val, miphy_phy->base + CTRL_REG);
writeb_relaxed(0x00, miphy_phy->base + PCIE_REG);
}
return 0;
}
static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
u8 mask = IDLL_RDY | PLL_RDY;
u8 regval;
do {
regval = readb_relaxed(miphy_phy->base + STATUS_REG);
if (!(regval & mask))
return 0;
usleep_range(2000, 2500);
} while (time_before(jiffies, timeout));
dev_err(miphy_dev->dev, "HFC ready timeout!\n");
return -EBUSY;
}
static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
u8 mask = IDLL_RDY | PLL_RDY;
u8 regval;
do {
regval = readb_relaxed(miphy_phy->base + STATUS_REG);
if ((regval & mask) == mask)
return 0;
usleep_range(2000, 2500);
} while (time_before(jiffies, timeout));
dev_err(miphy_dev->dev, "PHY not ready timeout!\n");
return -EBUSY;
}
static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
u8 val, mask;
if (miphy_phy->sata_gen == SATA_GEN1)
writeb_relaxed(COMP_2MHZ_RAT_GEN1,
miphy_phy->base + COMP_CTRL2_REG);
else
writeb_relaxed(COMP_2MHZ_RAT,
miphy_phy->base + COMP_CTRL2_REG);
if (miphy_phy->sata_gen != SATA_GEN3) {
writeb_relaxed(COMSR_COMP_REF,
miphy_phy->base + COMP_CTRL3_REG);
/*
* Force VCO current to value defined by address 0x5A
* and disable PCIe100Mref bit
* Enable auto load compensation for pll_i_bias
*/
writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG);
writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG);
}
/*
* Force restart compensation and enable auto load
* for Comzc_Tx, Comzc_Rx and Comsr on macro
*/
val = START_COMSR | START_COMZC | COMP_AUTO_LOAD;
writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG);
mask = COMSR_DONE | COMZC_DONE;
while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask)
cpu_relax();
}
static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
u8 val;
/*
* SSC Settings. SSC will be enabled through Link
* SSC Ampl. = 0.4%
* SSC Freq = 31KHz
*/
writeb_relaxed(PLL_SSC_STEP_MSB_VAL,
miphy_phy->base + PLL_SSC_STEP_MSB_REG);
writeb_relaxed(PLL_SSC_STEP_LSB_VAL,
miphy_phy->base + PLL_SSC_STEP_LSB_REG);
writeb_relaxed(PLL_SSC_PER_MSB_VAL,
miphy_phy->base + PLL_SSC_PER_MSB_REG);
writeb_relaxed(PLL_SSC_PER_LSB_VAL,
miphy_phy->base + PLL_SSC_PER_LSB_REG);
/* SSC Settings complete */
if (miphy_phy->sata_gen == SATA_GEN1) {
val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
} else {
val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
}
}
static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev)
{
int ret;
u8 val;
/*
* Force PHY macro reset, PLL calibration reset, PLL reset
* and assert Deserializer Reset
*/
val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
writeb_relaxed(val, miphy_phy->base + RESET_REG);
if (miphy_phy->sata_tx_pol_inv)
writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
/*
* Force macro1 to use rx_lspd, tx_lspd
* Force Rx_Clock on first I-DLL phase
* Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3
*/
writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
val = rx_tx_spd[miphy_phy->sata_gen];
writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
/* Wait for HFC_READY = 0 */
ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev);
if (ret)
return ret;
/* Compensation Recalibration */
miphy365x_set_comp(miphy_phy, miphy_dev);
switch (miphy_phy->sata_gen) {
case SATA_GEN3:
/*
* TX Swing target 550-600mv peak to peak diff
* Tx Slew target 90-110ps rising/falling time
* Rx Eq ON3, Sigdet threshold SDTH1
*/
val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3;
writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG);
val = SWING_VAL | PREEMPH_VAL;
writeb_relaxed(val, miphy_phy->base + TXBUF1_REG);
writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG);
val = SDTHRES_VAL | EQ_ON3;
writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
break;
case SATA_GEN2:
/*
* conf gen sel=0x1 to program Gen2 banked registers
* VDDT filter ON
* Tx Swing target 550-600mV peak-to-peak diff
* Tx Slew target 90-110 ps rising/falling time
* RX Equalization ON1, Sigdet threshold SDTH1
*/
writeb_relaxed(CONF_GEN_SEL_GEN2,
miphy_phy->base + BUF_SEL_REG);
writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG);
writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
val = SDTHRES_VAL | EQ_ON1;
writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
break;
case SATA_GEN1:
/*
* conf gen sel = 00b to program Gen1 banked registers
* VDDT filter ON
* Tx Swing target 500-550mV peak-to-peak diff
* Tx Slew target120-140 ps rising/falling time
*/
writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG);
writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG);
writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG);
break;
default:
break;
}
/* Force Macro1 in partial mode & release pll cal reset */
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
usleep_range(100, 150);
miphy365x_set_ssc(miphy_phy, miphy_dev);
/* Wait for phy_ready */
ret = miphy365x_rdy(miphy_phy, miphy_dev);
if (ret)
return ret;
/*
* Enable macro1 to use rx_lspd & tx_lspd
* Release Rx_Clock on first I-DLL phase on macro1
* Assert deserializer reset
* des_bit_lock_en is set
* bit lock detection strength
* Deassert deserializer reset
*/
writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
val = miphy_phy->sata_tx_pol_inv ?
(TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
writeb_relaxed(val, miphy_phy->base + CTRL_REG);
val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL;
writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG);
writeb_relaxed(0x00, miphy_phy->base + RESET_REG);
return 0;
}
static int miphy365x_init(struct phy *phy)
{
struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy);
struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent);
int ret = 0;
mutex_lock(&miphy_dev->miphy_mutex);
ret = miphy365x_set_path(miphy_phy, miphy_dev);
if (ret) {
mutex_unlock(&miphy_dev->miphy_mutex);
return ret;
}
/* Initialise Miphy for PCIe or SATA */
if (miphy_phy->type == MIPHY_TYPE_PCIE)
ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
else
ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
mutex_unlock(&miphy_dev->miphy_mutex);
return ret;
}
int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
int index)
{
struct device_node *phynode = miphy_phy->phy->dev.of_node;
const char *name;
const __be32 *taddr;
int type = miphy_phy->type;
int ret;
ret = of_property_read_string_index(phynode, "reg-names", index, &name);
if (ret) {
dev_err(dev, "no reg-names property not found\n");
return ret;
}
if (!strncmp(name, "syscfg", 6)) {
taddr = of_get_address(phynode, index, NULL, NULL);
if (!taddr) {
dev_err(dev, "failed to fetch syscfg address\n");
return -EINVAL;
}
miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
dev_err(dev, "failed to translate syscfg address\n");
return -EINVAL;
}
return 0;
}
if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
(!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
return 0;
miphy_phy->base = of_iomap(phynode, index);
if (!miphy_phy->base) {
dev_err(dev, "Failed to map %s\n", phynode->full_name);
return -EINVAL;
}
return 0;
}
static struct phy *miphy365x_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev);
struct miphy365x_phy *miphy_phy = NULL;
struct device_node *phynode = args->np;
int ret, index;
if (!of_device_is_available(phynode)) {
dev_warn(dev, "Requested PHY is disabled\n");
return ERR_PTR(-ENODEV);
}
if (args->args_count != 1) {
dev_err(dev, "Invalid number of cells in 'phy' property\n");
return ERR_PTR(-EINVAL);
}
for (index = 0; index < of_get_child_count(dev->of_node); index++)
if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
miphy_phy = miphy_dev->phys[index];
break;
}
if (!miphy_phy) {
dev_err(dev, "Failed to find appropriate phy\n");
return ERR_PTR(-EINVAL);
}
miphy_phy->type = args->args[0];
if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
miphy_phy->type == MIPHY_TYPE_PCIE)) {
dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
return ERR_PTR(-EINVAL);
}
/* Each port handles SATA and PCIE - third entry is always sysconf. */
for (index = 0; index < 3; index++) {
ret = miphy365x_get_addr(dev, miphy_phy, index);
if (ret < 0)
return ERR_PTR(ret);
}
return miphy_phy->phy;
}
static struct phy_ops miphy365x_ops = {
.init = miphy365x_init,
.owner = THIS_MODULE,
};
static int miphy365x_of_probe(struct device_node *phynode,
struct miphy365x_phy *miphy_phy)
{
of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen);
if (!miphy_phy->sata_gen)
miphy_phy->sata_gen = SATA_GEN1;
miphy_phy->pcie_tx_pol_inv =
of_property_read_bool(phynode, "st,pcie-tx-pol-inv");
miphy_phy->sata_tx_pol_inv =
of_property_read_bool(phynode, "st,sata-tx-pol-inv");
return 0;
}
static int miphy365x_probe(struct platform_device *pdev)
{
struct device_node *child, *np = pdev->dev.of_node;
struct miphy365x_dev *miphy_dev;
struct phy_provider *provider;
struct phy *phy;
int chancount, port = 0;
int ret;
miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
if (!miphy_dev)
return -ENOMEM;
chancount = of_get_child_count(np);
miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
GFP_KERNEL);
if (!miphy_dev->phys)
return -ENOMEM;
miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(miphy_dev->regmap)) {
dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
return PTR_ERR(miphy_dev->regmap);
}
miphy_dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, miphy_dev);
mutex_init(&miphy_dev->miphy_mutex);
for_each_child_of_node(np, child) {
struct miphy365x_phy *miphy_phy;
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
GFP_KERNEL);
if (!miphy_phy)
return -ENOMEM;
miphy_dev->phys[port] = miphy_phy;
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(phy);
}
miphy_dev->phys[port]->phy = phy;
ret = miphy365x_of_probe(child, miphy_phy);
if (ret)
return ret;
phy_set_drvdata(phy, miphy_dev->phys[port]);
port++;
}
provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
if (IS_ERR(provider))
return PTR_ERR(provider);
return 0;
}
static const struct of_device_id miphy365x_of_match[] = {
{ .compatible = "st,miphy365x-phy", },
{ },
};
MODULE_DEVICE_TABLE(of, miphy365x_of_match);
static struct platform_driver miphy365x_driver = {
.probe = miphy365x_probe,
.driver = {
.name = "miphy365x-phy",
.owner = THIS_MODULE,
.of_match_table = miphy365x_of_match,
}
};
module_platform_driver(miphy365x_driver);
MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
MODULE_DESCRIPTION("STMicroelectronics miphy365x driver");
MODULE_LICENSE("GPL v2");
...@@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) ...@@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk)) if (IS_ERR(priv->clk))
return PTR_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)) if (IS_ERR(phy))
return PTR_ERR(phy); return PTR_ERR(phy);
......
...@@ -26,6 +26,41 @@ ...@@ -26,6 +26,41 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/phy/omap_control_phy.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 * omap_control_phy_power - power on/off the phy using control module reg
* @dev: the control module device * @dev: the control module device
...@@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on) ...@@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on)
val |= OMAP_CTRL_DEV_PHY_PD; val |= OMAP_CTRL_DEV_PHY_PD;
break; break;
case OMAP_CTRL_TYPE_PCIE:
case OMAP_CTRL_TYPE_PIPE3: case OMAP_CTRL_TYPE_PIPE3:
rate = clk_get_rate(control_phy->sys_clk); rate = clk_get_rate(control_phy->sys_clk);
rate = rate/1000000; rate = rate/1000000;
...@@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); ...@@ -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 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 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 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 dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; 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[] = { ...@@ -227,6 +264,10 @@ static const struct of_device_id omap_control_phy_id_table[] = {
.compatible = "ti,control-phy-pipe3", .compatible = "ti,control-phy-pipe3",
.data = &pipe3_data, .data = &pipe3_data,
}, },
{
.compatible = "ti,control-phy-pcie",
.data = &pcie_data,
},
{ {
.compatible = "ti,control-phy-usb2-dra7", .compatible = "ti,control-phy-usb2-dra7",
.data = &dra7usb2_data, .data = &dra7usb2_data,
...@@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev) ...@@ -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, control_phy->sys_clk = devm_clk_get(control_phy->dev,
"sys_clkin"); "sys_clkin");
if (IS_ERR(control_phy->sys_clk)) { if (IS_ERR(control_phy->sys_clk)) {
...@@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev) ...@@ -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); dev_set_drvdata(control_phy->dev, control_phy);
return 0; return 0;
......
...@@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev) ...@@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy); 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)) if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy); return PTR_ERR(generic_phy);
......
/*
* 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");
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
struct qcom_ipq806x_sata_phy {
void __iomem *mmio;
struct clk *cfg_clk;
struct device *dev;
};
#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
#define SATA_PHY_P0_PARAM0 0x200
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
#define SATA_PHY_P0_PARAM1 0x204
#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
#define SATA_PHY_P0_PARAM2 0x208
#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
#define SATA_PHY_P0_PARAM3 0x20C
#define SATA_PHY_SSC_EN 0x8
#define SATA_PHY_P0_PARAM4 0x210
#define SATA_PHY_REF_SSP_EN 0x2
#define SATA_PHY_RESET 0x1
static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
{
struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
u32 reg;
/* Setting SSC_EN to 1 */
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
reg = reg | SATA_PHY_SSC_EN;
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
/* Setting PHY_RESET to 1 */
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
reg = reg | SATA_PHY_RESET;
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
/* Setting REF_SSP_EN to 1 */
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
/* make sure all changes complete before we let the PHY out of reset */
mb();
/* sleep for max. 50us more to combine processor wakeups */
usleep_range(20, 20 + 50);
/* Clearing PHY_RESET to 0 */
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
reg = reg & ~SATA_PHY_RESET;
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
return 0;
}
static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
{
struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
u32 reg;
/* Setting PHY_RESET to 1 */
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
reg = reg | SATA_PHY_RESET;
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
return 0;
}
static struct phy_ops qcom_ipq806x_sata_phy_ops = {
.init = qcom_ipq806x_sata_phy_init,
.exit = qcom_ipq806x_sata_phy_exit,
.owner = THIS_MODULE,
};
static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
{
struct qcom_ipq806x_sata_phy *phy;
struct device *dev = &pdev->dev;
struct resource *res;
struct phy_provider *phy_provider;
struct phy *generic_phy;
int ret;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
phy->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(phy->mmio))
return PTR_ERR(phy->mmio);
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
NULL);
if (IS_ERR(generic_phy)) {
dev_err(dev, "%s: failed to create phy\n", __func__);
return PTR_ERR(generic_phy);
}
phy->dev = dev;
phy_set_drvdata(generic_phy, phy);
platform_set_drvdata(pdev, phy);
phy->cfg_clk = devm_clk_get(dev, "cfg");
if (IS_ERR(phy->cfg_clk)) {
dev_err(dev, "Failed to get sata cfg clock\n");
return PTR_ERR(phy->cfg_clk);
}
ret = clk_prepare_enable(phy->cfg_clk);
if (ret)
return ret;
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
clk_disable_unprepare(phy->cfg_clk);
dev_err(dev, "%s: failed to register phy\n", __func__);
return PTR_ERR(phy_provider);
}
return 0;
}
static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
{
struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
clk_disable_unprepare(phy->cfg_clk);
return 0;
}
static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
{ .compatible = "qcom,ipq806x-sata-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
static struct platform_driver qcom_ipq806x_sata_phy_driver = {
.probe = qcom_ipq806x_sata_phy_probe,
.remove = qcom_ipq806x_sata_phy_remove,
.driver = {
.name = "qcom-ipq806x-sata-phy",
.owner = THIS_MODULE,
.of_match_table = qcom_ipq806x_sata_phy_of_match,
}
};
module_platform_driver(qcom_ipq806x_sata_phy_driver);
MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
MODULE_LICENSE("GPL v2");
...@@ -87,6 +87,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev, ...@@ -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[] = { 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 #ifdef CONFIG_PHY_EXYNOS4210_USB2
{ {
.compatible = "samsung,exynos4210-usb2-phy", .compatible = "samsung,exynos4210-usb2-phy",
...@@ -190,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) ...@@ -190,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
struct samsung_usb2_phy_instance *p = &drv->instances[i]; struct samsung_usb2_phy_instance *p = &drv->instances[i];
dev_dbg(dev, "Creating phy \"%s\"\n", label); 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)) { if (IS_ERR(p->phy)) {
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
label); label);
......
...@@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance { ...@@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance {
const struct samsung_usb2_common_phy *cfg; const struct samsung_usb2_common_phy *cfg;
struct phy *phy; struct phy *phy;
struct samsung_usb2_phy_driver *drv; struct samsung_usb2_phy_driver *drv;
bool enabled; int int_cnt;
int ext_cnt;
}; };
struct samsung_usb2_phy_driver { struct samsung_usb2_phy_driver {
...@@ -59,8 +60,10 @@ struct samsung_usb2_phy_config { ...@@ -59,8 +60,10 @@ struct samsung_usb2_phy_config {
int (*rate_to_clk)(unsigned long, u32 *); int (*rate_to_clk)(unsigned long, u32 *);
unsigned int num_phys; unsigned int num_phys;
bool has_mode_switch; 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 exynos4210_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4x12_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; extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -294,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) ...@@ -294,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy->pmu); 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)) { if (IS_ERR(phy->phy)) {
dev_err(dev, "failed to create PHY %d\n", i); dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy->phy); return PTR_ERR(phy->phy);
...@@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) ...@@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, data); dev_set_drvdata(dev, data);
phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); 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[] = { static const struct of_device_id sun4i_usb_phy_of_match[] = {
......
...@@ -80,7 +80,9 @@ struct ti_pipe3 { ...@@ -80,7 +80,9 @@ struct ti_pipe3 {
struct clk *wkupclk; struct clk *wkupclk;
struct clk *sys_clk; struct clk *sys_clk;
struct clk *refclk; struct clk *refclk;
struct clk *div_clk;
struct pipe3_dpll_map *dpll_map; struct pipe3_dpll_map *dpll_map;
u8 id;
}; };
static struct pipe3_dpll_map dpll_map_usb[] = { static struct pipe3_dpll_map dpll_map_usb[] = {
...@@ -215,6 +217,11 @@ static int ti_pipe3_init(struct phy *x) ...@@ -215,6 +217,11 @@ static int ti_pipe3_init(struct phy *x)
u32 val; u32 val;
int ret = 0; 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 */ /* Bring it out of IDLE if it is IDLE */
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
if (val & PLL_IDLE) { if (val & PLL_IDLE) {
...@@ -238,8 +245,11 @@ static int ti_pipe3_exit(struct phy *x) ...@@ -238,8 +245,11 @@ static int ti_pipe3_exit(struct phy *x)
u32 val; u32 val;
unsigned long timeout; unsigned long timeout;
/* SATA DPLL can't be powered down due to Errata i783 */ /* SATA DPLL can't be powered down due to Errata i783 and PCIe
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) * 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; return 0;
/* Put DPLL in IDLE mode */ /* Put DPLL in IDLE mode */
...@@ -286,32 +296,41 @@ static int ti_pipe3_probe(struct platform_device *pdev) ...@@ -286,32 +296,41 @@ static int ti_pipe3_probe(struct platform_device *pdev)
struct device_node *control_node; struct device_node *control_node;
struct platform_device *control_pdev; struct platform_device *control_pdev;
const struct of_device_id *match; const struct of_device_id *match;
struct clk *clk;
match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
if (!match)
return -EINVAL;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) { if (!phy) {
dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n"); dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
return -ENOMEM; return -ENOMEM;
} }
phy->dev = &pdev->dev;
phy->dpll_map = (struct pipe3_dpll_map *)match->data; if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
if (!phy->dpll_map) { match = of_match_device(of_match_ptr(ti_pipe3_id_table),
dev_err(&pdev->dev, "no DPLL data\n"); &pdev->dev);
return -EINVAL; if (!match)
} return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); phy->dpll_map = (struct pipe3_dpll_map *)match->data;
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); if (!phy->dpll_map) {
if (IS_ERR(phy->pll_ctrl_base)) dev_err(&pdev->dev, "no DPLL data\n");
return PTR_ERR(phy->pll_ctrl_base); return -EINVAL;
}
phy->dev = &pdev->dev; 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);
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { 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"); phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) { if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get wkupclk\n"); dev_err(&pdev->dev, "unable to get wkupclk\n");
...@@ -328,10 +347,38 @@ static int ti_pipe3_probe(struct platform_device *pdev) ...@@ -328,10 +347,38 @@ static int ti_pipe3_probe(struct platform_device *pdev)
phy->refclk = ERR_PTR(-ENODEV); phy->refclk = ERR_PTR(-ENODEV);
} }
phy->sys_clk = devm_clk_get(phy->dev, "sysclk"); if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
if (IS_ERR(phy->sys_clk)) { if (of_property_read_u8(node, "id", &phy->id) < 0)
dev_err(&pdev->dev, "unable to get sysclk\n"); phy->id = 1;
return -EINVAL;
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); control_node = of_parse_phandle(node, "ctrl-module", 0);
...@@ -353,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) ...@@ -353,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy); platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev); 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)) if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy); return PTR_ERR(generic_phy);
...@@ -387,6 +434,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev) ...@@ -387,6 +434,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev)
clk_disable_unprepare(phy->wkupclk); clk_disable_unprepare(phy->wkupclk);
if (!IS_ERR(phy->refclk)) if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk); clk_disable_unprepare(phy->refclk);
if (!IS_ERR(phy->div_clk))
clk_disable_unprepare(phy->div_clk);
return 0; return 0;
} }
...@@ -412,8 +461,19 @@ static int ti_pipe3_runtime_resume(struct device *dev) ...@@ -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; return 0;
err3:
if (!IS_ERR(phy->wkupclk))
clk_disable_unprepare(phy->wkupclk);
err2: err2:
if (!IS_ERR(phy->refclk)) if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk); clk_disable_unprepare(phy->refclk);
...@@ -446,6 +506,9 @@ static const struct of_device_id ti_pipe3_id_table[] = { ...@@ -446,6 +506,9 @@ static const struct of_device_id ti_pipe3_id_table[] = {
.compatible = "ti,phy-pipe3-sata", .compatible = "ti,phy-pipe3-sata",
.data = dpll_map_sata, .data = dpll_map_sata,
}, },
{
.compatible = "ti,phy-pipe3-pcie",
},
{} {}
}; };
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
......
...@@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) ...@@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
otg->set_host = twl4030_set_host; otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral; 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)) { if (IS_ERR(phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n"); dev_dbg(&pdev->dev, "Failed to create PHY\n");
return PTR_ERR(phy); return PTR_ERR(phy);
......
...@@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ...@@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
ctx->dev = &pdev->dev; ctx->dev = &pdev->dev;
platform_set_drvdata(pdev, ctx); 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)) { if (IS_ERR(ctx->phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n"); dev_dbg(&pdev->dev, "Failed to create PHY\n");
rc = PTR_ERR(ctx->phy); rc = PTR_ERR(ctx->phy);
......
...@@ -133,6 +133,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -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); data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
if (IS_ERR(data->phy)) { if (IS_ERR(data->phy)) {
ret = PTR_ERR(data->phy); ret = PTR_ERR(data->phy);
/* Return -EINVAL if no usbphy is available */
if (ret == -ENODEV)
ret = -EINVAL;
goto err_clk; goto err_clk;
} }
......
...@@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = { ...@@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = {
.release = single_release, .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 ci_hdrc *ci = s->private;
struct otg_fsm *fsm; struct otg_fsm *fsm;
...@@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = { ...@@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = {
.release = single_release, .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; struct ci_hdrc *ci = s->private;
u32 tmp_reg; u32 tmp_reg;
......
...@@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) ...@@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
u8 *buffer; u8 *buffer;
int rv; int rv;
int n; int n;
int actual; int actual = 0;
int max_size; int max_size;
dev = &data->intf->dev; dev = &data->intf->dev;
......
...@@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, ...@@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
if (n == 0) if (n == 0)
n = 9; /* 32 ms = 2^(9-1) uframes */ n = 9; /* 32 ms = 2^(9-1) uframes */
j = 16; 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; break;
default: /* USB_SPEED_FULL or _LOW */ default: /* USB_SPEED_FULL or _LOW */
/* For low-speed, 10 ms is the official minimum. /* For low-speed, 10 ms is the official minimum.
......
...@@ -1509,7 +1509,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb ...@@ -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); u = (is_in ? URB_DIR_IN : URB_DIR_OUT);
if (uurb->flags & USBDEVFS_URB_ISO_ASAP) if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
u |= 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; u |= URB_SHORT_NOT_OK;
if (uurb->flags & USBDEVFS_URB_NO_FSBR) if (uurb->flags & USBDEVFS_URB_NO_FSBR)
u |= URB_NO_FSBR; u |= URB_NO_FSBR;
......
...@@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev) ...@@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev)
*/ */
lpm_disable_error = usb_unlocked_disable_lpm(udev); 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); usb_disable_interface(udev, intf, false);
driver->disconnect(intf); driver->disconnect(intf);
......
...@@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) ...@@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
hcd->driver->shutdown) { hcd->driver->shutdown) {
hcd->driver->shutdown(hcd); hcd->driver->shutdown(hcd);
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
free_irq(hcd->irq, hcd);
pci_disable_device(dev); pci_disable_device(dev);
} }
} }
......
...@@ -855,8 +855,6 @@ static ssize_t authorized_default_show(struct device *dev, ...@@ -855,8 +855,6 @@ static ssize_t authorized_default_show(struct device *dev,
struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_bus *usb_bus = rh_usb_dev->bus;
struct usb_hcd *usb_hcd; 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); usb_hcd = bus_to_hcd(usb_bus);
return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default); return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default);
} }
...@@ -871,8 +869,6 @@ static ssize_t authorized_default_store(struct device *dev, ...@@ -871,8 +869,6 @@ static ssize_t authorized_default_store(struct device *dev,
struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_bus *usb_bus = rh_usb_dev->bus;
struct usb_hcd *usb_hcd; 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); usb_hcd = bus_to_hcd(usb_bus);
result = sscanf(buf, "%u\n", &val); result = sscanf(buf, "%u\n", &val);
if (result == 1) { if (result == 1) {
......
...@@ -2606,13 +2606,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1, ...@@ -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? /* Is a USB 3.0 port in the Inactive or Compliance Mode state?
* Port worm reset is required to recover * 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) && u16 link_state;
(((portstatus & USB_PORT_STAT_LINK_STATE) ==
USB_SS_PORT_LS_SS_INACTIVE) || if (!hub_is_superspeed(hub->hdev))
((portstatus & USB_PORT_STAT_LINK_STATE) == return false;
USB_SS_PORT_LS_COMP_MOD)) ;
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, 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, ...@@ -2649,7 +2656,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if ((portstatus & USB_PORT_STAT_RESET)) if ((portstatus & USB_PORT_STAT_RESET))
return -EBUSY; return -EBUSY;
if (hub_port_warm_reset_required(hub, portstatus)) if (hub_port_warm_reset_required(hub, port1, portstatus))
return -ENOTCONN; return -ENOTCONN;
/* Device went away? */ /* Device went away? */
...@@ -2749,9 +2756,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, ...@@ -2749,9 +2756,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
if (status < 0) if (status < 0)
goto done; goto done;
if (hub_port_warm_reset_required(hub, portstatus)) if (hub_port_warm_reset_required(hub, port1, portstatus))
warm = true; warm = true;
} }
clear_bit(port1, hub->warm_reset_bits);
/* Reset the port */ /* Reset the port */
for (i = 0; i < PORT_RESET_TRIES; i++) { for (i = 0; i < PORT_RESET_TRIES; i++) {
...@@ -2788,7 +2796,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, ...@@ -2788,7 +2796,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
&portstatus, &portchange) < 0) &portstatus, &portchange) < 0)
goto done; goto done;
if (!hub_port_warm_reset_required(hub, portstatus)) if (!hub_port_warm_reset_required(hub, port1,
portstatus))
goto done; goto done;
/* /*
...@@ -2875,8 +2884,13 @@ static int check_port_resume_type(struct usb_device *udev, ...@@ -2875,8 +2884,13 @@ static int check_port_resume_type(struct usb_device *udev,
{ {
struct usb_port *port_dev = hub->ports[port1 - 1]; 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? */ /* 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) || !port_is_power_on(hub, portstatus) ||
!(portstatus & USB_PORT_STAT_CONNECTION)) { !(portstatus & USB_PORT_STAT_CONNECTION)) {
if (status >= 0) if (status >= 0)
...@@ -3263,6 +3277,43 @@ static int finish_port_resume(struct usb_device *udev) ...@@ -3263,6 +3277,43 @@ static int finish_port_resume(struct usb_device *udev)
return status; 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 * usb_port_resume - re-activate a suspended usb device's upstream port
* @udev: device to re-activate, not a root hub * @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) ...@@ -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, status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus); hub, port1, status, portchange, portstatus);
if (status == 0) if (status == 0)
...@@ -3879,7 +3934,8 @@ int usb_disable_lpm(struct usb_device *udev) ...@@ -3879,7 +3934,8 @@ int usb_disable_lpm(struct usb_device *udev)
if (!udev || !udev->parent || if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER || udev->speed != USB_SPEED_SUPER ||
!udev->lpm_capable) !udev->lpm_capable ||
udev->state < USB_STATE_DEFAULT)
return 0; return 0;
hcd = bus_to_hcd(udev->bus); hcd = bus_to_hcd(udev->bus);
...@@ -3935,7 +3991,8 @@ void usb_enable_lpm(struct usb_device *udev) ...@@ -3935,7 +3991,8 @@ void usb_enable_lpm(struct usb_device *udev)
if (!udev || !udev->parent || if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER || udev->speed != USB_SPEED_SUPER ||
!udev->lpm_capable) !udev->lpm_capable ||
udev->state < USB_STATE_DEFAULT)
return; return;
udev->lpm_disable_count--; udev->lpm_disable_count--;
...@@ -4550,6 +4607,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, ...@@ -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_hcd *hcd = bus_to_hcd(hdev->bus);
struct usb_port *port_dev = hub->ports[port1 - 1]; struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child; struct usb_device *udev = port_dev->child;
static int unreliable_port = -1;
/* Disconnect any existing devices under this port */ /* Disconnect any existing devices under this port */
if (udev) { if (udev) {
...@@ -4570,10 +4628,14 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, ...@@ -4570,10 +4628,14 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
USB_PORT_STAT_C_ENABLE)) { USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce_be_stable(hub, port1); status = hub_port_debounce_be_stable(hub, port1);
if (status < 0) { if (status < 0) {
if (status != -ENODEV && printk_ratelimit()) if (status != -ENODEV &&
dev_err(&port_dev->dev, port1 != unreliable_port &&
"connect-debounce failed\n"); printk_ratelimit())
dev_err(&udev->dev, "connect-debounce failed, port %d disabled\n",
port1);
portstatus &= ~USB_PORT_STAT_CONNECTION; portstatus &= ~USB_PORT_STAT_CONNECTION;
unreliable_port = port1;
} else { } else {
portstatus = status; portstatus = status;
} }
...@@ -4889,7 +4951,7 @@ static void port_event(struct usb_hub *hub, int port1) ...@@ -4889,7 +4951,7 @@ static void port_event(struct usb_hub *hub, int port1)
* Warm reset a USB3 protocol port if it's in * Warm reset a USB3 protocol port if it's in
* SS.Inactive state. * 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"); dev_dbg(&port_dev->dev, "do warm reset\n");
if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
|| udev->state == USB_STATE_NOTATTACHED) { || udev->state == USB_STATE_NOTATTACHED) {
......
...@@ -52,6 +52,8 @@ struct usb_hub { ...@@ -52,6 +52,8 @@ struct usb_hub {
unsigned long power_bits[1]; /* ports that are powered */ unsigned long power_bits[1]; /* ports that are powered */
unsigned long child_usage_bits[1]; /* ports powered on for unsigned long child_usage_bits[1]; /* ports powered on for
children */ children */
unsigned long warm_reset_bits[1]; /* ports requesting warm
reset recovery */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short! #error event_bits[] is too short!
#endif #endif
......
...@@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev) ...@@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev)
msleep(hub_power_on_good_delay(hub)); msleep(hub_power_on_good_delay(hub));
if (udev && !retval) { if (udev && !retval) {
/* /*
* Attempt to wait for usb hub port to be reconnected in order * Our preference is to simply wait for the port to reconnect,
* to make the resume procedure successful. The device may have * as that is the lowest latency method to restart the port.
* disconnected while the port was powered off, so ignore the * However, there are cases where toggling port power results in
* return status. * 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 (hub_port_debounce_be_connected(hub, port1) < 0) {
if (retval < 0) dev_dbg(&port_dev->dev, "reconnect timeout\n");
dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", if (hub_is_superspeed(hdev))
retval); set_bit(port1, hub->warm_reset_bits);
retval = 0; }
/* Force the child awake to revalidate after the power loss. */ /* Force the child awake to revalidate after the power loss. */
if (!test_and_set_bit(port1, hub->child_usage_bits)) { if (!test_and_set_bit(port1, hub->child_usage_bits)) {
......
...@@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = { ...@@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* SKYMEDI USB_DRIVE */ /* SKYMEDI USB_DRIVE */
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME }, { 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 */ /* BUILDWIN Photo Frame */
{ USB_DEVICE(0x1908, 0x1315), .driver_info = { USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES }, USB_QUIRK_HONOR_BNUMINTERFACES },
...@@ -152,6 +156,9 @@ static const struct usb_device_id usb_quirk_list[] = { ...@@ -152,6 +156,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* INTEL VALUE SSD */ /* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, { 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 */ { } /* terminating entry must be last */
}; };
......
...@@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) ...@@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
URB_FREE_BUFFER); URB_FREE_BUFFER);
switch (xfertype) { switch (xfertype) {
case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
if (is_out) if (is_out)
allowed |= URB_ZERO_PACKET; allowed |= URB_ZERO_PACKET;
/* FALLTHROUGH */ /* FALLTHROUGH */
......
...@@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, ...@@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
} }
return dev; return dev;
} }
EXPORT_SYMBOL_GPL(usb_alloc_dev);
/** /**
* usb_get_dev - increments the reference count of the usb device structure * usb_get_dev - increments the reference count of the usb device structure
......
/** /**
* linux/drivers/usb/gadget/s3c-hsotg.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd. * Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
...@@ -1022,7 +1020,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); ...@@ -1022,7 +1020,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
* *
* Set stall for ep0 as response for setup request. * 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]; struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
u32 reg; u32 reg;
u32 ctrl; u32 ctrl;
...@@ -1994,7 +1993,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, ...@@ -1994,7 +1993,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
s3c_hsotg_complete_request(hsotg, ep, req, s3c_hsotg_complete_request(hsotg, ep, req,
result); result);
} }
if(hsotg->dedicated_fifos) if (hsotg->dedicated_fifos)
if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072)
s3c_hsotg_txfifo_flush(hsotg, ep->index); s3c_hsotg_txfifo_flush(hsotg, ep->index);
} }
...@@ -3390,10 +3389,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev) ...@@ -3390,10 +3389,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
int i; int i;
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
if (!hsotg) { if (!hsotg)
dev_err(dev, "cannot get memory\n");
return -ENOMEM; return -ENOMEM;
}
/* /*
* Attempt to find a generic PHY, then look for an old style * 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) ...@@ -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), eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep),
GFP_KERNEL); GFP_KERNEL);
if (!eps) { if (!eps) {
dev_err(dev, "cannot get memory\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_supplies; goto err_supplies;
} }
......
...@@ -93,4 +93,11 @@ config USB_DWC3_VERBOSE ...@@ -93,4 +93,11 @@ config USB_DWC3_VERBOSE
help help
Say Y here to enable verbose debugging messages on DWC3 Driver. 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 endif
...@@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc) ...@@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
} }
dwc->revision = reg; 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 */ /* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500); timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
...@@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev)
return -ENODEV; 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) { if (node) {
dwc->maximum_speed = of_usb_get_maximum_speed(node); dwc->maximum_speed = of_usb_get_maximum_speed(node);
...@@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret) if (ret)
return 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); spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc); platform_set_drvdata(pdev, dwc);
dwc->regs = regs;
dwc->regs_size = resource_size(res);
dev->dma_mask = dev->parent->dma_mask; dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms; dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
......
...@@ -191,6 +191,19 @@ ...@@ -191,6 +191,19 @@
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) #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 */ /* Global HWPARAMS4 Register */
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15 #define DWC3_MAX_HIBER_SCRATCHBUFS 15
......
...@@ -77,10 +77,6 @@ ...@@ -77,10 +77,6 @@
#define USBOTGSS_DEV_EBC_EN 0x0110 #define USBOTGSS_DEV_EBC_EN 0x0110
#define USBOTGSS_DEBUG_OFFSET 0x0600 #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 */ /* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) #define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
...@@ -129,7 +125,6 @@ struct dwc3_omap { ...@@ -129,7 +125,6 @@ struct dwc3_omap {
u32 irq_eoi_offset; u32 irq_eoi_offset;
u32 debug_offset; u32 debug_offset;
u32 irq0_offset; u32 irq0_offset;
u32 revision;
u32 dma_status:1; u32 dma_status:1;
...@@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb, ...@@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb,
return NOTIFY_DONE; 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) static int dwc3_omap_probe(struct platform_device *pdev)
{ {
struct device_node *node = pdev->dev.of_node; struct device_node *node = pdev->dev.of_node;
...@@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) ...@@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap; struct dwc3_omap *omap;
struct resource *res; struct resource *res;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct extcon_dev *edev;
struct regulator *vbus_reg = NULL; struct regulator *vbus_reg = NULL;
int ret; int ret;
int irq; int irq;
int utmi_mode = 0;
int x_major;
u32 reg; u32 reg;
void __iomem *base; void __iomem *base;
...@@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) ...@@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
goto err0; goto err0;
} }
reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION); dwc3_omap_map_offset(omap);
omap->revision = reg; dwc3_omap_set_utmi_mode(omap);
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);
/* check the DMA Status */ /* check the DMA Status */
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
...@@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) ...@@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev)
dwc3_omap_enable_irqs(omap); dwc3_omap_enable_irqs(omap);
if (of_property_read_bool(node, "extcon")) { ret = dwc3_omap_extcon_register(omap);
edev = extcon_get_edev_by_phandle(dev, 0); if (ret < 0)
if (IS_ERR(edev)) { goto err2;
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 = of_platform_populate(node, NULL, NULL, dev); ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) { if (ret) {
......
...@@ -1971,8 +1971,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -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, static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
struct dwc3_ep *dep, const struct dwc3_event_depevt *event, struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
int start_new)
{ {
unsigned status = 0; unsigned status = 0;
int clean_busy; int clean_busy;
...@@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, ...@@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return; return;
} }
dwc3_endpoint_transfer_complete(dwc, dep, event, 1); dwc3_endpoint_transfer_complete(dwc, dep, event);
break; break;
case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_XFERINPROGRESS:
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
...@@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, ...@@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return; return;
} }
dwc3_endpoint_transfer_complete(dwc, dep, event, 0); dwc3_endpoint_transfer_complete(dwc, dep, event);
break; break;
case DWC3_DEPEVT_XFERNOTREADY: case DWC3_DEPEVT_XFERNOTREADY:
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
......
...@@ -16,12 +16,14 @@ ...@@ -16,12 +16,14 @@
*/ */
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/usb/xhci_pdriver.h>
#include "core.h" #include "core.h"
int dwc3_host_init(struct dwc3 *dwc) int dwc3_host_init(struct dwc3 *dwc)
{ {
struct platform_device *xhci; struct platform_device *xhci;
struct usb_xhci_pdata pdata;
int ret; int ret;
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
...@@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc) ...@@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc)
goto err1; 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); ret = platform_device_add(xhci);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n"); dev_err(dwc->dev, "failed to register xHCI device\n");
......
此差异已折叠。
此差异已折叠。
...@@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) ...@@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
} }
if (cdev->req) { if (cdev->req) {
kfree(cdev->req->buf); kfree(cdev->req->buf);
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
usb_ep_free_request(cdev->gadget->ep0, cdev->req); usb_ep_free_request(cdev->gadget->ep0, cdev->req);
} }
cdev->next_string_id = 0; cdev->next_string_id = 0;
......
...@@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop, ...@@ -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') if (page[len - 1] == '\n' || page[len - 1] == '\0')
--len; --len;
new_data = kzalloc(len, GFP_KERNEL); new_data = kmemdup(page, len, GFP_KERNEL);
if (!new_data) if (!new_data)
return -ENOMEM; return -ENOMEM;
memcpy(new_data, page, len);
if (desc->opts_mutex) if (desc->opts_mutex)
mutex_lock(desc->opts_mutex); mutex_lock(desc->opts_mutex);
kfree(ext_prop->data); kfree(ext_prop->data);
......
此差异已折叠。
...@@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_control_intf.bInterfaceNumber = status; rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = 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); status = usb_interface_id(c, f);
if (status < 0) if (status < 0)
goto fail; goto fail;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册