mirror of
https://github.com/torvalds/linux.git
synced 2024-12-05 18:41:23 +00:00
for_3.17
Adds regulator support in PHY core. PHY core is modified to support representation of multi-phy PHY providers with each individual PHY as sub-node OF PHY provider node. New PHY drivers adapted to PHY framework (hix5hd2 SATA PHY, QCOM APQ8064 SATA PHY, QCOM IPQ806x SATA PHY, Berlin SATA PHY and MiPHY356x). Existing TI PIPE3 PHY can now be used for PCIe too. Includes misc fixes and cleanups. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTzhPPAAoJEA5ceFyATYLZYH0QAJTrXObYG3pSWonyqn3fUUkZ G9GexVDajtZw3SQytVJseDsiwmqZAib/ePBI/HGNGa40Iq59VuvtnabMJWF48gQk GPufh3cXxe/MiC0U7wHncRhoq7Al7JAvbpihVA4fXD6W4ji9JrbogaxB6+tweAgW 4i8+t9SkamLMz7OwNME7/lTOx+kUtEakoHmwfgf0wRYmGktyAG8HJ+mOoJgEeZa/ weC5J1ay8nt2ISi4gyckaW83sl5UvfWG1XOzqypZXhTARGvDIZ/4fPyUsrhjCv6L V5SGuq0HdUgbc7FS5Gvnn56PSHO8QFG0li2VSaJKlf1vYv/hGDxMFcpbuFOAhl99 +edueAAFr/NY2/lpAhf6qse2SRQqJGqy33Rn8mp4x3yIX18bLKvg+i/MXhN7xUFn 5lRitH4v23kKT6iv5iyZP5YW5ljYMSKmG122Ri3YwHB9cFvZKG8aEzr2Js1gSVTI 9K8AcQ3VpCi+9IS1WTVCU/nPYOUTm+wb5i4TgC19yjUw97+i9e2DtosHAwx3Xj93 HRSxWmJOHRwI0mBPyrIiNj40Hya9HU+7sf2Z6W3O9BKGWYxYVZ8aarFm/ChQDUBS Smmj8eGdW7yRQ+opb1usYcePoxsrp2B86fNsCRqc/oI4JGyLh7OcDKIaQhGtcEep pZjQ+ClSzUv65XVVZVqi =uUhi -----END PGP SIGNATURE----- Merge tag 'for_3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next Kishon writes: for_3.17 Adds regulator support in PHY core. PHY core is modified to support representation of multi-phy PHY providers with each individual PHY as sub-node OF PHY provider node. New PHY drivers adapted to PHY framework (hix5hd2 SATA PHY, QCOM APQ8064 SATA PHY, QCOM IPQ806x SATA PHY, Berlin SATA PHY and MiPHY356x). Existing TI PIPE3 PHY can now be used for PCIe too. Includes misc fixes and cleanups.
This commit is contained in:
commit
d508d99202
34
Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
Normal file
34
Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Berlin SATA PHY
|
||||
---------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "marvell,berlin2q-sata-phy"
|
||||
- address-cells: should be 1
|
||||
- size-cells: should be 0
|
||||
- phy-cells: from the generic PHY bindings, must be 1
|
||||
- reg: address and length of the register
|
||||
- clocks: reference to the clock entry
|
||||
|
||||
Sub-nodes:
|
||||
Each PHY should be represented as a sub-node.
|
||||
|
||||
Sub-nodes required properties:
|
||||
- reg: the PHY number
|
||||
|
||||
Example:
|
||||
sata_phy: phy@f7e900a0 {
|
||||
compatible = "marvell,berlin2q-sata-phy";
|
||||
reg = <0xf7e900a0 0x200>;
|
||||
clocks = <&chip CLKID_SATA>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#phy-cells = <1>;
|
||||
|
||||
sata-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
sata-phy@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
22
Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
Normal file
22
Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
Normal file
@ -0,0 +1,22 @@
|
||||
Hisilicon hix5hd2 SATA PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "hisilicon,hix5hd2-sata-phy"
|
||||
- reg: offset and length of the PHY registers
|
||||
- #phy-cells: must be 0
|
||||
Refer to phy/phy-bindings.txt for the generic PHY binding properties
|
||||
|
||||
Optional Properties:
|
||||
- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
|
||||
- hisilicon,power-reg: offset and bit number within peripheral-syscon,
|
||||
register of controlling sata power supply.
|
||||
|
||||
Example:
|
||||
sata_phy: phy@f9900000 {
|
||||
compatible = "hisilicon,hix5hd2-sata-phy";
|
||||
reg = <0xf9900000 0x10000>;
|
||||
#phy-cells = <0>;
|
||||
hisilicon,peripheral-syscon = <&peripheral_ctrl>;
|
||||
hisilicon,power-reg = <0x8 10>;
|
||||
};
|
@ -10,6 +10,10 @@ Required Properties:
|
||||
provider can use the values in cells to find the appropriate
|
||||
PHY.
|
||||
|
||||
Optional Properties:
|
||||
phy-supply: Phandle to a regulator that provides power to the PHY. This
|
||||
regulator will be managed during the PHY power on/off sequence.
|
||||
|
||||
For example:
|
||||
|
||||
phys: phy {
|
||||
|
76
Documentation/devicetree/bindings/phy/phy-miphy365x.txt
Normal file
76
Documentation/devicetree/bindings/phy/phy-miphy365x.txt
Normal file
@ -0,0 +1,76 @@
|
||||
STMicroelectronics STi MIPHY365x PHY binding
|
||||
============================================
|
||||
|
||||
This binding describes a miphy device that is used to control PHY hardware
|
||||
for SATA and PCIe.
|
||||
|
||||
Required properties (controller (parent) node):
|
||||
- compatible : Should be "st,miphy365x-phy"
|
||||
- st,syscfg : Should be a phandle of the system configuration register group
|
||||
which contain the SATA, PCIe mode setting bits
|
||||
|
||||
Required nodes : A sub-node is required for each channel the controller
|
||||
provides. Address range information including the usual
|
||||
'reg' and 'reg-names' properties are used inside these
|
||||
nodes to describe the controller's topology. These nodes
|
||||
are translated by the driver's .xlate() function.
|
||||
|
||||
Required properties (port (child) node):
|
||||
- #phy-cells : Should be 1 (See second example)
|
||||
Cell after port phandle is device type from:
|
||||
- MIPHY_TYPE_SATA
|
||||
- MIPHY_TYPE_PCI
|
||||
- reg : Address and length of register sets for each device in
|
||||
"reg-names"
|
||||
- reg-names : The names of the register addresses corresponding to the
|
||||
registers filled in "reg":
|
||||
- sata: For SATA devices
|
||||
- pcie: For PCIe devices
|
||||
- syscfg: To specify the syscfg based config register
|
||||
|
||||
Optional properties (port (child) node):
|
||||
- st,sata-gen : Generation of locally attached SATA IP. Expected values
|
||||
are {1,2,3). If not supplied generation 1 hardware will
|
||||
be expected
|
||||
- st,pcie-tx-pol-inv : Bool property to invert the polarity PCIe Tx (Txn/Txp)
|
||||
- st,sata-tx-pol-inv : Bool property to invert the polarity SATA Tx (Txn/Txp)
|
||||
|
||||
Example:
|
||||
|
||||
miphy365x_phy: miphy365x@fe382000 {
|
||||
compatible = "st,miphy365x-phy";
|
||||
st,syscfg = <&syscfg_rear>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
phy_port0: port@fe382000 {
|
||||
reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>;
|
||||
reg-names = "sata", "pcie", "syscfg";
|
||||
#phy-cells = <1>;
|
||||
st,sata-gen = <3>;
|
||||
};
|
||||
|
||||
phy_port1: port@fe38a000 {
|
||||
reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;;
|
||||
reg-names = "sata", "pcie", "syscfg";
|
||||
#phy-cells = <1>;
|
||||
st,pcie-tx-pol-inv;
|
||||
};
|
||||
};
|
||||
|
||||
Specifying phy control of devices
|
||||
=================================
|
||||
|
||||
Device nodes should specify the configuration required in their "phys"
|
||||
property, containing a phandle to the phy port node and a device type.
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
|
||||
sata0: sata@fe380000 {
|
||||
...
|
||||
phys = <&phy_port0 MIPHY_TYPE_SATA>;
|
||||
...
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
Qualcomm APQ8064 SATA PHY Controller
|
||||
------------------------------------
|
||||
|
||||
SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
|
||||
Each SATA PHY controller should have its own node.
|
||||
|
||||
Required properties:
|
||||
- compatible: compatible list, contains "qcom,apq8064-sata-phy".
|
||||
- reg: offset and length of the SATA PHY register set;
|
||||
- #phy-cells: must be zero
|
||||
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
- clock-names: must be "cfg" for phy config clock.
|
||||
|
||||
Example:
|
||||
sata_phy: sata-phy@1b400000 {
|
||||
compatible = "qcom,apq8064-sata-phy";
|
||||
reg = <0x1b400000 0x200>;
|
||||
|
||||
clocks = <&gcc SATA_PHY_CFG_CLK>;
|
||||
clock-names = "cfg";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
Qualcomm IPQ806x SATA PHY Controller
|
||||
------------------------------------
|
||||
|
||||
SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
|
||||
Each SATA PHY controller should have its own node.
|
||||
|
||||
Required properties:
|
||||
- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
|
||||
- reg: offset and length of the SATA PHY register set;
|
||||
- #phy-cells: must be zero
|
||||
- clocks: must be exactly one entry
|
||||
- clock-names: must be "cfg"
|
||||
|
||||
Example:
|
||||
sata_phy: sata-phy@1b400000 {
|
||||
compatible = "qcom,ipq806x-sata-phy";
|
||||
reg = <0x1b400000 0x200>;
|
||||
|
||||
clocks = <&gcc SATA_PHY_CFG_CLK>;
|
||||
clock-names = "cfg";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of the listed compatibles:
|
||||
- "samsung,exynos3250-usb2-phy"
|
||||
- "samsung,exynos4210-usb2-phy"
|
||||
- "samsung,exynos4x12-usb2-phy"
|
||||
- "samsung,exynos5250-usb2-phy"
|
||||
@ -46,6 +47,7 @@ and Exynos 4212) it is as follows:
|
||||
1 - USB host ("host"),
|
||||
2 - HSIC0 ("hsic0"),
|
||||
3 - HSIC1 ("hsic1"),
|
||||
Exynos3250 has only USB device phy available as phy 0.
|
||||
|
||||
Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
|
||||
register is supplied.
|
||||
|
@ -9,15 +9,17 @@ Required properties:
|
||||
e.g. USB2_PHY on OMAP5.
|
||||
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
|
||||
e.g. USB3 PHY and SATA PHY on OMAP5.
|
||||
"ti,control-phy-pcie" - for pcie to support external clock for pcie and to
|
||||
set PCS delay value.
|
||||
e.g. PCIE PHY in DRA7x
|
||||
"ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
|
||||
DRA7 platform.
|
||||
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
|
||||
AM437 platform.
|
||||
- reg : Address and length of the register set for the device. It contains
|
||||
the address of "otghs_control" for control-phy-otghs or "power" register
|
||||
for other types.
|
||||
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
|
||||
other types.
|
||||
- reg : register ranges as listed in the reg-names property
|
||||
- reg-names: "otghs_control" for control-phy-otghs
|
||||
"power", "pcie_pcs" and "control_sma" for control-phy-pcie
|
||||
"power" for all other types
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,control-phy-otghs";
|
||||
@ -56,8 +58,8 @@ usb2phy@4a0ad080 {
|
||||
TI PIPE3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
|
||||
"ti,omap-usb3" is deprecated.
|
||||
- compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or
|
||||
"ti,phy-pipe3-pcie. "ti,omap-usb3" is deprecated.
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
@ -69,10 +71,17 @@ Required properties:
|
||||
* "wkupclk" - wakeup clock.
|
||||
* "sysclk" - system clock.
|
||||
* "refclk" - reference clock.
|
||||
* "dpll_ref" - external dpll ref clk
|
||||
* "dpll_ref_m2" - external dpll ref clk
|
||||
* "phy-div" - divider for apll
|
||||
* "div-clk" - apll clock
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
- id: If there are multiple instance of the same type, in order to
|
||||
differentiate between each instance "id" can be used (e.g., multi-lane PCIe
|
||||
PHY). If "id" is not provided, it is set to default value of '1'.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
|
@ -53,10 +53,12 @@ unregister the PHY.
|
||||
The PHY driver should create the PHY in order for other peripheral controllers
|
||||
to make use of it. The PHY framework provides 2 APIs to create the PHY.
|
||||
|
||||
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
|
||||
The PHY drivers can use one of the above 2 APIs to create the PHY by passing
|
||||
the device pointer, phy ops and init_data.
|
||||
|
@ -15,6 +15,13 @@ config GENERIC_PHY
|
||||
phy users can obtain reference to the PHY. All the users of this
|
||||
framework should select this config.
|
||||
|
||||
config PHY_BERLIN_SATA
|
||||
tristate "Marvell Berlin SATA PHY driver"
|
||||
depends on ARCH_BERLIN && HAS_IOMEM && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the SATA PHY on Marvell Berlin SoCs.
|
||||
|
||||
config PHY_EXYNOS_MIPI_VIDEO
|
||||
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
@ -27,10 +34,20 @@ config PHY_EXYNOS_MIPI_VIDEO
|
||||
|
||||
config PHY_MVEBU_SATA
|
||||
def_bool y
|
||||
depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
|
||||
depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_MIPHY365X
|
||||
tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
|
||||
depends on ARCH_STI
|
||||
depends on GENERIC_PHY
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
help
|
||||
Enable this to support the miphy transceiver (for SATA/PCIE)
|
||||
that is part of STMicroelectronics STiH41x SoC series.
|
||||
|
||||
config OMAP_CONTROL_PHY
|
||||
tristate "OMAP CONTROL PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
@ -109,6 +126,14 @@ config PHY_EXYNOS5250_SATA
|
||||
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
|
||||
port to accept one SATA device.
|
||||
|
||||
config PHY_HIX5HD2_SATA
|
||||
tristate "HIX5HD2 SATA PHY Driver"
|
||||
depends on ARCH_HIX5HD2 && OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for SATA PHY on Hisilicon hix5hd2 Soc.
|
||||
|
||||
config PHY_SUN4I_USB
|
||||
tristate "Allwinner sunxi SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
@ -124,50 +149,39 @@ config PHY_SUN4I_USB
|
||||
config PHY_SAMSUNG_USB2
|
||||
tristate "Samsung USB 2.0 PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
default ARCH_EXYNOS
|
||||
help
|
||||
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
|
||||
SoCs. This driver provides the interface for USB 2.0 PHY. Support for
|
||||
particular SoCs has to be enabled in addition to this driver. Number
|
||||
and type of supported phys depends on the SoC.
|
||||
SoCs. This driver provides the interface for USB 2.0 PHY. Support
|
||||
for particular PHYs will be enabled based on the SoC type in addition
|
||||
to this driver.
|
||||
|
||||
config PHY_EXYNOS4210_USB2
|
||||
bool "Support for Exynos 4210"
|
||||
bool
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on CPU_EXYNOS4210
|
||||
help
|
||||
Enable USB PHY support for Exynos 4210. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 4210 four
|
||||
phys are available - device, host, HSIC0 and HSIC1.
|
||||
default CPU_EXYNOS4210
|
||||
|
||||
config PHY_EXYNOS4X12_USB2
|
||||
bool "Support for Exynos 4x12"
|
||||
bool
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
|
||||
help
|
||||
Enable USB PHY support for Exynos 4x12. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 4x12 four
|
||||
phys are available - device, host, HSIC0 and HSIC1.
|
||||
default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
|
||||
|
||||
config PHY_EXYNOS5250_USB2
|
||||
bool "Support for Exynos 5250"
|
||||
bool
|
||||
depends on PHY_SAMSUNG_USB2
|
||||
depends on SOC_EXYNOS5250
|
||||
help
|
||||
Enable USB PHY support for Exynos 5250. This option requires that
|
||||
Samsung USB 2.0 PHY driver is enabled and means that support for this
|
||||
particular SoC is compiled in the driver. In case of Exynos 5250 four
|
||||
phys are available - device, host, HSIC0 and HSIC.
|
||||
default SOC_EXYNOS5250 || SOC_EXYNOS5420
|
||||
|
||||
config PHY_EXYNOS5_USBDRD
|
||||
tristate "Exynos5 SoC series USB DRD PHY driver"
|
||||
depends on ARCH_EXYNOS5 && OF
|
||||
depends on HAS_IOMEM
|
||||
depends on USB_DWC3_EXYNOS
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
default y
|
||||
help
|
||||
Enable USB DRD PHY support for Exynos 5 SoC series.
|
||||
This driver provides PHY interface for USB 3.0 DRD controller
|
||||
@ -180,4 +194,18 @@ config PHY_XGENE
|
||||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
config PHY_QCOM_APQ8064_SATA
|
||||
tristate "Qualcomm APQ8064 SATA SerDes/PHY driver"
|
||||
depends on ARCH_QCOM
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_QCOM_IPQ806X_SATA
|
||||
tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
|
||||
depends on ARCH_QCOM
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
endmenu
|
||||
|
@ -3,15 +3,18 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
|
||||
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
|
||||
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
|
||||
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
|
||||
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
|
||||
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
|
||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
|
||||
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
|
||||
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
||||
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
|
||||
phy-exynos-usb2-y += phy-samsung-usb2.o
|
||||
@ -20,3 +23,5 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
|
||||
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
|
||||
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
|
||||
|
@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
gphy = devm_phy_create(dev, &ops, NULL);
|
||||
gphy = devm_phy_create(dev, NULL, &ops, NULL);
|
||||
if (IS_ERR(gphy))
|
||||
return PTR_ERR(gphy);
|
||||
|
||||
|
284
drivers/phy/phy-berlin-sata.c
Normal file
284
drivers/phy/phy-berlin-sata.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Marvell Berlin SATA PHY driver
|
||||
*
|
||||
* Copyright (C) 2014 Marvell Technology Group Ltd.
|
||||
*
|
||||
* Antoine Ténart <antoine.tenart@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define HOST_VSA_ADDR 0x0
|
||||
#define HOST_VSA_DATA 0x4
|
||||
#define PORT_SCR_CTL 0x2c
|
||||
#define PORT_VSR_ADDR 0x78
|
||||
#define PORT_VSR_DATA 0x7c
|
||||
|
||||
#define CONTROL_REGISTER 0x0
|
||||
#define MBUS_SIZE_CONTROL 0x4
|
||||
|
||||
#define POWER_DOWN_PHY0 BIT(6)
|
||||
#define POWER_DOWN_PHY1 BIT(14)
|
||||
#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
|
||||
#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
|
||||
|
||||
#define PHY_BASE 0x200
|
||||
|
||||
/* register 0x01 */
|
||||
#define REF_FREF_SEL_25 BIT(0)
|
||||
#define PHY_MODE_SATA (0x0 << 5)
|
||||
|
||||
/* register 0x02 */
|
||||
#define USE_MAX_PLL_RATE BIT(12)
|
||||
|
||||
/* register 0x23 */
|
||||
#define DATA_BIT_WIDTH_10 (0x0 << 10)
|
||||
#define DATA_BIT_WIDTH_20 (0x1 << 10)
|
||||
#define DATA_BIT_WIDTH_40 (0x2 << 10)
|
||||
|
||||
/* register 0x25 */
|
||||
#define PHY_GEN_MAX_1_5 (0x0 << 10)
|
||||
#define PHY_GEN_MAX_3_0 (0x1 << 10)
|
||||
#define PHY_GEN_MAX_6_0 (0x2 << 10)
|
||||
|
||||
struct phy_berlin_desc {
|
||||
struct phy *phy;
|
||||
u32 power_bit;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
struct phy_berlin_priv {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
struct phy_berlin_desc **phys;
|
||||
unsigned nphys;
|
||||
};
|
||||
|
||||
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
/* select register */
|
||||
writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
|
||||
|
||||
/* set bits */
|
||||
regval = readl(ctrl_reg + PORT_VSR_DATA);
|
||||
regval &= ~mask;
|
||||
regval |= val;
|
||||
writel(regval, ctrl_reg + PORT_VSR_DATA);
|
||||
}
|
||||
|
||||
static int phy_berlin_sata_power_on(struct phy *phy)
|
||||
{
|
||||
struct phy_berlin_desc *desc = phy_get_drvdata(phy);
|
||||
struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80);
|
||||
int ret = 0;
|
||||
u32 regval;
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Power on PHY */
|
||||
writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
|
||||
regval = readl(priv->base + HOST_VSA_DATA);
|
||||
regval &= ~desc->power_bit;
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
/* Configure MBus */
|
||||
writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR);
|
||||
regval = readl(priv->base + HOST_VSA_DATA);
|
||||
regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128;
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
/* set PHY mode and ref freq to 25 MHz */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
|
||||
REF_FREF_SEL_25 | PHY_MODE_SATA);
|
||||
|
||||
/* set PHY up to 6 Gbps */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
|
||||
|
||||
/* set 40 bits width */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
|
||||
|
||||
/* use max pll rate */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
|
||||
|
||||
/* set Gen3 controller speed */
|
||||
regval = readl(ctrl_reg + PORT_SCR_CTL);
|
||||
regval &= ~GENMASK(7, 4);
|
||||
regval |= 0x30;
|
||||
writel(regval, ctrl_reg + PORT_SCR_CTL);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int phy_berlin_sata_power_off(struct phy *phy)
|
||||
{
|
||||
struct phy_berlin_desc *desc = phy_get_drvdata(phy);
|
||||
struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
u32 regval;
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Power down PHY */
|
||||
writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
|
||||
regval = readl(priv->base + HOST_VSA_DATA);
|
||||
regval |= desc->power_bit;
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct phy_berlin_priv *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
if (WARN_ON(args->args[0] >= priv->nphys))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
for (i = 0; i < priv->nphys; i++) {
|
||||
if (priv->phys[i]->index == args->args[0])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == priv->nphys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return priv->phys[i]->phy;
|
||||
}
|
||||
|
||||
static struct phy_ops phy_berlin_sata_ops = {
|
||||
.power_on = phy_berlin_sata_power_on,
|
||||
.power_off = phy_berlin_sata_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static u32 phy_berlin_power_down_bits[] = {
|
||||
POWER_DOWN_PHY0,
|
||||
POWER_DOWN_PHY1,
|
||||
};
|
||||
|
||||
static int phy_berlin_sata_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child;
|
||||
struct phy *phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy_berlin_priv *priv;
|
||||
struct resource *res;
|
||||
int i = 0;
|
||||
u32 phy_id;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
priv->base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
priv->nphys = of_get_child_count(dev->of_node);
|
||||
if (priv->nphys == 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
|
||||
GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
struct phy_berlin_desc *phy_desc;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &phy_id)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
|
||||
dev_err(dev, "invalid reg in node %s\n", child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
|
||||
if (!phy_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", phy_id);
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
phy_desc->phy = phy;
|
||||
phy_desc->power_bit = phy_berlin_power_down_bits[phy_id];
|
||||
phy_desc->index = phy_id;
|
||||
phy_set_drvdata(phy, phy_desc);
|
||||
|
||||
priv->phys[i++] = phy_desc;
|
||||
|
||||
/* Make sure the PHY is off */
|
||||
phy_berlin_sata_power_off(phy);
|
||||
}
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id phy_berlin_sata_of_match[] = {
|
||||
{ .compatible = "marvell,berlin2q-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver phy_berlin_sata_driver = {
|
||||
.probe = phy_berlin_sata_probe,
|
||||
.driver = {
|
||||
.name = "phy-berlin-sata",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = phy_berlin_sata_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_berlin_sata_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver");
|
||||
MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -21,6 +21,7 @@
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
static struct class *phy_class;
|
||||
static DEFINE_MUTEX(phy_provider_mutex);
|
||||
@ -86,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port)
|
||||
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *child;
|
||||
|
||||
list_for_each_entry(phy_provider, &phy_provider_list, list) {
|
||||
if (phy_provider->dev->of_node == node)
|
||||
return phy_provider;
|
||||
|
||||
for_each_child_of_node(phy_provider->dev->of_node, child)
|
||||
if (child == node)
|
||||
return phy_provider;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy)
|
||||
if (!phy)
|
||||
return 0;
|
||||
|
||||
if (phy->pwr) {
|
||||
ret = regulator_enable(phy->pwr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_pm_runtime_get_sync(phy);
|
||||
if (ret < 0 && ret != -ENOTSUPP)
|
||||
return ret;
|
||||
@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy)
|
||||
out:
|
||||
mutex_unlock(&phy->mutex);
|
||||
phy_pm_runtime_put_sync(phy);
|
||||
if (phy->pwr)
|
||||
regulator_disable(phy->pwr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy)
|
||||
mutex_unlock(&phy->mutex);
|
||||
phy_pm_runtime_put(phy);
|
||||
|
||||
if (phy->pwr)
|
||||
regulator_disable(phy->pwr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_power_off);
|
||||
@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
|
||||
struct phy *phy;
|
||||
struct class_dev_iter iter;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *child;
|
||||
|
||||
class_dev_iter_init(&iter, phy_class, NULL, NULL);
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
phy = to_phy(dev);
|
||||
if (node != phy->dev.of_node)
|
||||
if (node != phy->dev.of_node) {
|
||||
for_each_child_of_node(node, child) {
|
||||
if (child == phy->dev.of_node)
|
||||
goto phy_found;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
phy_found:
|
||||
class_dev_iter_exit(&iter);
|
||||
return phy;
|
||||
}
|
||||
@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
|
||||
/**
|
||||
* phy_create() - create a new phy
|
||||
* @dev: device that is creating the new phy
|
||||
* @node: device node of the phy
|
||||
* @ops: function pointers for performing phy operations
|
||||
* @init_data: contains the list of PHY consumers or NULL
|
||||
*
|
||||
* Called to create a phy using phy framework.
|
||||
*/
|
||||
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
{
|
||||
int ret;
|
||||
int id;
|
||||
@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
goto free_phy;
|
||||
}
|
||||
|
||||
/* phy-supply */
|
||||
phy->pwr = regulator_get_optional(dev, "phy");
|
||||
if (IS_ERR(phy->pwr)) {
|
||||
if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto free_ida;
|
||||
}
|
||||
phy->pwr = NULL;
|
||||
}
|
||||
|
||||
device_initialize(&phy->dev);
|
||||
mutex_init(&phy->mutex);
|
||||
|
||||
phy->dev.class = phy_class;
|
||||
phy->dev.parent = dev;
|
||||
phy->dev.of_node = dev->of_node;
|
||||
phy->dev.of_node = node ?: dev->of_node;
|
||||
phy->id = id;
|
||||
phy->ops = ops;
|
||||
phy->init_data = init_data;
|
||||
@ -617,6 +653,9 @@ put_dev:
|
||||
put_device(&phy->dev); /* calls phy_release() which frees resources */
|
||||
return ERR_PTR(ret);
|
||||
|
||||
free_ida:
|
||||
ida_simple_remove(&phy_ida, phy->id);
|
||||
|
||||
free_phy:
|
||||
kfree(phy);
|
||||
return ERR_PTR(ret);
|
||||
@ -626,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create);
|
||||
/**
|
||||
* devm_phy_create() - create a new phy
|
||||
* @dev: device that is creating the new phy
|
||||
* @node: device node of the phy
|
||||
* @ops: function pointers for performing phy operations
|
||||
* @init_data: contains the list of PHY consumers or NULL
|
||||
*
|
||||
@ -634,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create);
|
||||
* On driver detach, release function is invoked on the devres data,
|
||||
* then, devres data is freed.
|
||||
*/
|
||||
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
|
||||
@ -643,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy = phy_create(dev, ops, init_data);
|
||||
phy = phy_create(dev, node, ops, init_data);
|
||||
if (!IS_ERR(phy)) {
|
||||
*ptr = phy;
|
||||
devres_add(dev, ptr);
|
||||
@ -800,6 +841,7 @@ static void phy_release(struct device *dev)
|
||||
|
||||
phy = to_phy(dev);
|
||||
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
|
||||
regulator_put(phy->pwr);
|
||||
ida_simple_remove(&phy_ida, phy->id);
|
||||
kfree(phy);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -76,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(state->regs))
|
||||
return PTR_ERR(state->regs);
|
||||
|
||||
phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create Display Port PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
|
||||
phy_set_drvdata(phy, state);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id exynos_dp_video_phy_of_match[] = {
|
||||
|
@ -9,6 +9,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -135,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&state->slock);
|
||||
|
||||
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
|
||||
struct phy *phy = devm_phy_create(dev,
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos_mipi_video_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
@ -149,10 +150,8 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev,
|
||||
exynos_mipi_video_phy_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
|
||||
|
@ -67,6 +67,8 @@
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0)
|
||||
|
||||
#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8)
|
||||
|
||||
#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4)
|
||||
#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7)
|
||||
@ -86,13 +88,23 @@
|
||||
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
|
||||
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
|
||||
/* The following bit defines are presented in the
|
||||
* order taken from the Exynos4412 reference manual.
|
||||
*
|
||||
* During experiments with the hardware and debugging
|
||||
* it was determined that the hardware behaves contrary
|
||||
* to the manual.
|
||||
*
|
||||
* The following bit values were chaned accordingly to the
|
||||
* results of real hardware experiments.
|
||||
*/
|
||||
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6)
|
||||
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
|
||||
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8)
|
||||
|
||||
/* Isolation, configured in the power management unit */
|
||||
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
|
||||
@ -187,7 +199,12 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
|
||||
|
||||
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
||||
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
|
||||
|
||||
if (drv->cfg->has_refclk_sel)
|
||||
clk = EXYNOS_3250_UPHYCLK_REFCLKSEL;
|
||||
|
||||
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
|
||||
clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON;
|
||||
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
||||
}
|
||||
|
||||
@ -198,27 +215,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
u32 phypwr = 0;
|
||||
u32 rst;
|
||||
u32 pwr;
|
||||
u32 mode = 0;
|
||||
u32 switch_mode = 0;
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
case EXYNOS4x12_DEVICE:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
|
||||
rstbits = EXYNOS_4x12_URSTCON_PHY0;
|
||||
mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
|
||||
switch_mode = 1;
|
||||
break;
|
||||
case EXYNOS4x12_HOST:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY;
|
||||
mode = EXYNOS_4x12_MODE_SWITCH_HOST;
|
||||
switch_mode = 1;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY |
|
||||
EXYNOS_4x12_URSTCON_PHY1 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P0;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC0:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
|
||||
EXYNOS_4x12_URSTCON_HOST_PHY;
|
||||
rstbits = EXYNOS_4x12_URSTCON_HSIC0 |
|
||||
EXYNOS_4x12_URSTCON_HOST_LINK_P1;
|
||||
break;
|
||||
case EXYNOS4x12_HSIC1:
|
||||
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
|
||||
@ -228,11 +240,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
};
|
||||
|
||||
if (on) {
|
||||
if (switch_mode)
|
||||
regmap_update_bits(drv->reg_sys,
|
||||
EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK, mode);
|
||||
|
||||
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
||||
pwr &= ~phypwr;
|
||||
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
||||
@ -253,41 +260,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
||||
}
|
||||
}
|
||||
|
||||
static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
if (inst->int_cnt++ > 0)
|
||||
return;
|
||||
|
||||
exynos4x12_setup_clk(inst);
|
||||
exynos4x12_isol(inst, 0);
|
||||
exynos4x12_phy_pwr(inst, 1);
|
||||
}
|
||||
|
||||
static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
|
||||
inst->enabled = 1;
|
||||
exynos4x12_setup_clk(inst);
|
||||
exynos4x12_phy_pwr(inst, 1);
|
||||
exynos4x12_isol(inst, 0);
|
||||
if (inst->ext_cnt++ > 0)
|
||||
return 0;
|
||||
|
||||
/* Power on the device, as it is necessary for HSIC to work */
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0) {
|
||||
struct samsung_usb2_phy_instance *device =
|
||||
&drv->instances[EXYNOS4x12_DEVICE];
|
||||
exynos4x12_phy_pwr(device, 1);
|
||||
exynos4x12_isol(device, 0);
|
||||
if (inst->cfg->id == EXYNOS4x12_HOST) {
|
||||
regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK,
|
||||
EXYNOS_4x12_MODE_SWITCH_HOST);
|
||||
exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
}
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
|
||||
regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK,
|
||||
EXYNOS_4x12_MODE_SWITCH_DEVICE);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
|
||||
inst->cfg->id == EXYNOS4x12_HSIC1) {
|
||||
exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]);
|
||||
}
|
||||
|
||||
exynos4x12_power_on_int(inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
if (inst->int_cnt-- > 1)
|
||||
return;
|
||||
|
||||
exynos4x12_isol(inst, 1);
|
||||
exynos4x12_phy_pwr(inst, 0);
|
||||
}
|
||||
|
||||
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
|
||||
{
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
struct samsung_usb2_phy_instance *device =
|
||||
&drv->instances[EXYNOS4x12_DEVICE];
|
||||
|
||||
inst->enabled = 0;
|
||||
exynos4x12_isol(inst, 1);
|
||||
exynos4x12_phy_pwr(inst, 0);
|
||||
if (inst->ext_cnt-- > 1)
|
||||
return 0;
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
|
||||
exynos4x12_isol(device, 1);
|
||||
exynos4x12_phy_pwr(device, 0);
|
||||
if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
|
||||
regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
||||
EXYNOS_4x12_MODE_SWITCH_MASK,
|
||||
EXYNOS_4x12_MODE_SWITCH_HOST);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HOST)
|
||||
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
|
||||
if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
|
||||
inst->cfg->id == EXYNOS4x12_HSIC1) {
|
||||
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
||||
exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]);
|
||||
}
|
||||
|
||||
exynos4x12_power_off_int(inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -320,6 +364,13 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = {
|
||||
.has_refclk_sel = 1,
|
||||
.num_phys = 1,
|
||||
.phys = exynos4x12_phys,
|
||||
.rate_to_clk = exynos4x12_rate_to_clk,
|
||||
};
|
||||
|
||||
const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
|
||||
.has_mode_switch = 1,
|
||||
.num_phys = EXYNOS4x12_NUM_PHYS,
|
||||
|
@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
{
|
||||
.id = EXYNOS5_DRDPHY_UTMI,
|
||||
.phy_isol = exynos5_usbdrd_phy_isol,
|
||||
@ -521,13 +521,13 @@ const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
.pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
|
||||
};
|
||||
|
||||
const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
};
|
||||
@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
|
||||
dev_vdbg(dev, "Creating usbdrd_phy phy\n");
|
||||
|
||||
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
|
||||
struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops,
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos5_usbdrd_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "Failed to create usbdrd_phy phy\n");
|
||||
|
@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
|
||||
sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL);
|
||||
if (IS_ERR(sata_phy->phy)) {
|
||||
clk_disable_unprepare(sata_phy->phyclk);
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
|
||||
|
||||
break;
|
||||
}
|
||||
inst->enabled = 1;
|
||||
exynos5250_isol(inst, 0);
|
||||
|
||||
return 0;
|
||||
@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
|
||||
u32 otg;
|
||||
u32 hsic;
|
||||
|
||||
inst->enabled = 0;
|
||||
exynos5250_isol(inst, 1);
|
||||
|
||||
switch (inst->cfg->id) {
|
||||
|
192
drivers/phy/phy-hix5hd2-sata.c
Normal file
192
drivers/phy/phy-hix5hd2-sata.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Linaro Ltd.
|
||||
* Copyright (c) 2014 Hisilicon Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define SATA_PHY0_CTLL 0xa0
|
||||
#define MPLL_MULTIPLIER_SHIFT 1
|
||||
#define MPLL_MULTIPLIER_MASK 0xfe
|
||||
#define MPLL_MULTIPLIER_50M 0x3c
|
||||
#define MPLL_MULTIPLIER_100M 0x1e
|
||||
#define PHY_RESET BIT(0)
|
||||
#define REF_SSP_EN BIT(9)
|
||||
#define SSC_EN BIT(10)
|
||||
#define REF_USE_PAD BIT(23)
|
||||
|
||||
#define SATA_PORT_PHYCTL 0x174
|
||||
#define SPEED_MODE_MASK 0x6f0000
|
||||
#define HALF_RATE_SHIFT 16
|
||||
#define PHY_CONFIG_SHIFT 18
|
||||
#define GEN2_EN_SHIFT 21
|
||||
#define SPEED_CTRL BIT(20)
|
||||
|
||||
#define SATA_PORT_PHYCTL1 0x148
|
||||
#define AMPLITUDE_MASK 0x3ffffe
|
||||
#define AMPLITUDE_GEN3 0x68
|
||||
#define AMPLITUDE_GEN3_SHIFT 15
|
||||
#define AMPLITUDE_GEN2 0x56
|
||||
#define AMPLITUDE_GEN2_SHIFT 8
|
||||
#define AMPLITUDE_GEN1 0x56
|
||||
#define AMPLITUDE_GEN1_SHIFT 1
|
||||
|
||||
#define SATA_PORT_PHYCTL2 0x14c
|
||||
#define PREEMPH_MASK 0x3ffff
|
||||
#define PREEMPH_GEN3 0x20
|
||||
#define PREEMPH_GEN3_SHIFT 12
|
||||
#define PREEMPH_GEN2 0x15
|
||||
#define PREEMPH_GEN2_SHIFT 6
|
||||
#define PREEMPH_GEN1 0x5
|
||||
#define PREEMPH_GEN1_SHIFT 0
|
||||
|
||||
struct hix5hd2_priv {
|
||||
void __iomem *base;
|
||||
struct regmap *peri_ctrl;
|
||||
};
|
||||
|
||||
enum phy_speed_mode {
|
||||
SPEED_MODE_GEN1 = 0,
|
||||
SPEED_MODE_GEN2 = 1,
|
||||
SPEED_MODE_GEN3 = 2,
|
||||
};
|
||||
|
||||
static int hix5hd2_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
struct hix5hd2_priv *priv = phy_get_drvdata(phy);
|
||||
u32 val, data[2];
|
||||
int ret;
|
||||
|
||||
if (priv->peri_ctrl) {
|
||||
ret = of_property_read_u32_array(phy->dev.of_node,
|
||||
"hisilicon,power-reg",
|
||||
&data[0], 2);
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "Fail read hisilicon,power-reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->peri_ctrl, data[0],
|
||||
BIT(data[1]), BIT(data[1]));
|
||||
}
|
||||
|
||||
/* reset phy */
|
||||
val = readl_relaxed(priv->base + SATA_PHY0_CTLL);
|
||||
val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD);
|
||||
val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT |
|
||||
REF_SSP_EN | PHY_RESET;
|
||||
writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
|
||||
msleep(20);
|
||||
val &= ~PHY_RESET;
|
||||
writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
|
||||
|
||||
val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1);
|
||||
val &= ~AMPLITUDE_MASK;
|
||||
val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT |
|
||||
AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT |
|
||||
AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1);
|
||||
|
||||
val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2);
|
||||
val &= ~PREEMPH_MASK;
|
||||
val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT |
|
||||
PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT |
|
||||
PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2);
|
||||
|
||||
/* ensure PHYCTRL setting takes effect */
|
||||
val = readl_relaxed(priv->base + SATA_PORT_PHYCTL);
|
||||
val &= ~SPEED_MODE_MASK;
|
||||
val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT |
|
||||
SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT |
|
||||
SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
|
||||
|
||||
msleep(20);
|
||||
val &= ~SPEED_MODE_MASK;
|
||||
val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT |
|
||||
SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT |
|
||||
SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
|
||||
|
||||
val &= ~(SPEED_MODE_MASK | SPEED_CTRL);
|
||||
val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT |
|
||||
SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT |
|
||||
SPEED_MODE_GEN2 << GEN2_EN_SHIFT;
|
||||
writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops hix5hd2_sata_phy_ops = {
|
||||
.init = hix5hd2_sata_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy *phy;
|
||||
struct hix5hd2_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"hisilicon,peripheral-syscon");
|
||||
if (IS_ERR(priv->peri_ctrl))
|
||||
priv->peri_ctrl = NULL;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
|
||||
{.compatible = "hisilicon,hix5hd2-sata-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver hix5hd2_sata_phy_driver = {
|
||||
.probe = hix5hd2_sata_phy_probe,
|
||||
.driver = {
|
||||
.name = "hix5hd2-sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = hix5hd2_sata_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(hix5hd2_sata_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Jiancheng Xue <xuejiancheng@huawei.com>");
|
||||
MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver");
|
||||
MODULE_ALIAS("platform:hix5hd2-sata-phy");
|
||||
MODULE_LICENSE("GPL v2");
|
636
drivers/phy/phy-miphy365x.c
Normal file
636
drivers/phy/phy-miphy365x.c
Normal file
@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics – All Rights Reserved
|
||||
*
|
||||
* STMicroelectronics PHY driver MiPHY365 (for SoC STiH416).
|
||||
*
|
||||
* Authors: Alexandre Torgue <alexandre.torgue@st.com>
|
||||
* Lee Jones <lee.jones@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
|
||||
#define HFC_TIMEOUT 100
|
||||
|
||||
#define SYSCFG_SELECT_SATA_MASK BIT(1)
|
||||
#define SYSCFG_SELECT_SATA_POS 1
|
||||
|
||||
/* MiPHY365x register definitions */
|
||||
#define RESET_REG 0x00
|
||||
#define RST_PLL BIT(1)
|
||||
#define RST_PLL_CAL BIT(2)
|
||||
#define RST_RX BIT(4)
|
||||
#define RST_MACRO BIT(7)
|
||||
|
||||
#define STATUS_REG 0x01
|
||||
#define IDLL_RDY BIT(0)
|
||||
#define PLL_RDY BIT(1)
|
||||
#define DES_BIT_LOCK BIT(2)
|
||||
#define DES_SYMBOL_LOCK BIT(3)
|
||||
|
||||
#define CTRL_REG 0x02
|
||||
#define TERM_EN BIT(0)
|
||||
#define PCI_EN BIT(2)
|
||||
#define DES_BIT_LOCK_EN BIT(3)
|
||||
#define TX_POL BIT(5)
|
||||
|
||||
#define INT_CTRL_REG 0x03
|
||||
|
||||
#define BOUNDARY1_REG 0x10
|
||||
#define SPDSEL_SEL BIT(0)
|
||||
|
||||
#define BOUNDARY3_REG 0x12
|
||||
#define TX_SPDSEL_GEN1_VAL 0
|
||||
#define TX_SPDSEL_GEN2_VAL 0x01
|
||||
#define TX_SPDSEL_GEN3_VAL 0x02
|
||||
#define RX_SPDSEL_GEN1_VAL 0
|
||||
#define RX_SPDSEL_GEN2_VAL (0x01 << 3)
|
||||
#define RX_SPDSEL_GEN3_VAL (0x02 << 3)
|
||||
|
||||
#define PCIE_REG 0x16
|
||||
|
||||
#define BUF_SEL_REG 0x20
|
||||
#define CONF_GEN_SEL_GEN3 0x02
|
||||
#define CONF_GEN_SEL_GEN2 0x01
|
||||
#define PD_VDDTFILTER BIT(4)
|
||||
|
||||
#define TXBUF1_REG 0x21
|
||||
#define SWING_VAL 0x04
|
||||
#define SWING_VAL_GEN1 0x03
|
||||
#define PREEMPH_VAL (0x3 << 5)
|
||||
|
||||
#define TXBUF2_REG 0x22
|
||||
#define TXSLEW_VAL 0x2
|
||||
#define TXSLEW_VAL_GEN1 0x4
|
||||
|
||||
#define RXBUF_OFFSET_CTRL_REG 0x23
|
||||
|
||||
#define RXBUF_REG 0x25
|
||||
#define SDTHRES_VAL 0x01
|
||||
#define EQ_ON3 (0x03 << 4)
|
||||
#define EQ_ON1 (0x01 << 4)
|
||||
|
||||
#define COMP_CTRL1_REG 0x40
|
||||
#define START_COMSR BIT(0)
|
||||
#define START_COMZC BIT(1)
|
||||
#define COMSR_DONE BIT(2)
|
||||
#define COMZC_DONE BIT(3)
|
||||
#define COMP_AUTO_LOAD BIT(4)
|
||||
|
||||
#define COMP_CTRL2_REG 0x41
|
||||
#define COMP_2MHZ_RAT_GEN1 0x1e
|
||||
#define COMP_2MHZ_RAT 0xf
|
||||
|
||||
#define COMP_CTRL3_REG 0x42
|
||||
#define COMSR_COMP_REF 0x33
|
||||
|
||||
#define COMP_IDLL_REG 0x47
|
||||
#define COMZC_IDLL 0x2a
|
||||
|
||||
#define PLL_CTRL1_REG 0x50
|
||||
#define PLL_START_CAL BIT(0)
|
||||
#define BUF_EN BIT(2)
|
||||
#define SYNCHRO_TX BIT(3)
|
||||
#define SSC_EN BIT(6)
|
||||
#define CONFIG_PLL BIT(7)
|
||||
|
||||
#define PLL_CTRL2_REG 0x51
|
||||
#define BYPASS_PLL_CAL BIT(1)
|
||||
|
||||
#define PLL_RAT_REG 0x52
|
||||
|
||||
#define PLL_SSC_STEP_MSB_REG 0x56
|
||||
#define PLL_SSC_STEP_MSB_VAL 0x03
|
||||
|
||||
#define PLL_SSC_STEP_LSB_REG 0x57
|
||||
#define PLL_SSC_STEP_LSB_VAL 0x63
|
||||
|
||||
#define PLL_SSC_PER_MSB_REG 0x58
|
||||
#define PLL_SSC_PER_MSB_VAL 0
|
||||
|
||||
#define PLL_SSC_PER_LSB_REG 0x59
|
||||
#define PLL_SSC_PER_LSB_VAL 0xf1
|
||||
|
||||
#define IDLL_TEST_REG 0x72
|
||||
#define START_CLK_HF BIT(6)
|
||||
|
||||
#define DES_BITLOCK_REG 0x86
|
||||
#define BIT_LOCK_LEVEL 0x01
|
||||
#define BIT_LOCK_CNT_512 (0x03 << 5)
|
||||
|
||||
struct miphy365x_phy {
|
||||
struct phy *phy;
|
||||
void __iomem *base;
|
||||
bool pcie_tx_pol_inv;
|
||||
bool sata_tx_pol_inv;
|
||||
u32 sata_gen;
|
||||
u64 ctrlreg;
|
||||
u8 type;
|
||||
};
|
||||
|
||||
struct miphy365x_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex miphy_mutex;
|
||||
struct miphy365x_phy **phys;
|
||||
};
|
||||
|
||||
/*
|
||||
* These values are represented in Device tree. They are considered to be ABI
|
||||
* and although they can be extended any existing values must not change.
|
||||
*/
|
||||
enum miphy_sata_gen {
|
||||
SATA_GEN1 = 1,
|
||||
SATA_GEN2,
|
||||
SATA_GEN3
|
||||
};
|
||||
|
||||
static u8 rx_tx_spd[] = {
|
||||
TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL,
|
||||
TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL,
|
||||
TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL
|
||||
};
|
||||
|
||||
/*
|
||||
* This function selects the system configuration,
|
||||
* either two SATA, one SATA and one PCIe, or two PCIe lanes.
|
||||
*/
|
||||
static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
|
||||
|
||||
return regmap_update_bits(miphy_dev->regmap,
|
||||
(unsigned int)miphy_phy->ctrlreg,
|
||||
SYSCFG_SELECT_SATA_MASK,
|
||||
sata << SYSCFG_SELECT_SATA_POS);
|
||||
}
|
||||
|
||||
static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (miphy_phy->pcie_tx_pol_inv) {
|
||||
/* Invert Tx polarity and clear pci_txdetect_pol bit */
|
||||
val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL;
|
||||
writeb_relaxed(val, miphy_phy->base + CTRL_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + PCIE_REG);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
|
||||
u8 mask = IDLL_RDY | PLL_RDY;
|
||||
u8 regval;
|
||||
|
||||
do {
|
||||
regval = readb_relaxed(miphy_phy->base + STATUS_REG);
|
||||
if (!(regval & mask))
|
||||
return 0;
|
||||
|
||||
usleep_range(2000, 2500);
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
dev_err(miphy_dev->dev, "HFC ready timeout!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
|
||||
u8 mask = IDLL_RDY | PLL_RDY;
|
||||
u8 regval;
|
||||
|
||||
do {
|
||||
regval = readb_relaxed(miphy_phy->base + STATUS_REG);
|
||||
if ((regval & mask) == mask)
|
||||
return 0;
|
||||
|
||||
usleep_range(2000, 2500);
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
dev_err(miphy_dev->dev, "PHY not ready timeout!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
u8 val, mask;
|
||||
|
||||
if (miphy_phy->sata_gen == SATA_GEN1)
|
||||
writeb_relaxed(COMP_2MHZ_RAT_GEN1,
|
||||
miphy_phy->base + COMP_CTRL2_REG);
|
||||
else
|
||||
writeb_relaxed(COMP_2MHZ_RAT,
|
||||
miphy_phy->base + COMP_CTRL2_REG);
|
||||
|
||||
if (miphy_phy->sata_gen != SATA_GEN3) {
|
||||
writeb_relaxed(COMSR_COMP_REF,
|
||||
miphy_phy->base + COMP_CTRL3_REG);
|
||||
/*
|
||||
* Force VCO current to value defined by address 0x5A
|
||||
* and disable PCIe100Mref bit
|
||||
* Enable auto load compensation for pll_i_bias
|
||||
*/
|
||||
writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG);
|
||||
writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force restart compensation and enable auto load
|
||||
* for Comzc_Tx, Comzc_Rx and Comsr on macro
|
||||
*/
|
||||
val = START_COMSR | START_COMZC | COMP_AUTO_LOAD;
|
||||
writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG);
|
||||
|
||||
mask = COMSR_DONE | COMZC_DONE;
|
||||
while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
/*
|
||||
* SSC Settings. SSC will be enabled through Link
|
||||
* SSC Ampl. = 0.4%
|
||||
* SSC Freq = 31KHz
|
||||
*/
|
||||
writeb_relaxed(PLL_SSC_STEP_MSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_STEP_MSB_REG);
|
||||
writeb_relaxed(PLL_SSC_STEP_LSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_STEP_LSB_REG);
|
||||
writeb_relaxed(PLL_SSC_PER_MSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_PER_MSB_REG);
|
||||
writeb_relaxed(PLL_SSC_PER_LSB_VAL,
|
||||
miphy_phy->base + PLL_SSC_PER_LSB_REG);
|
||||
|
||||
/* SSC Settings complete */
|
||||
if (miphy_phy->sata_gen == SATA_GEN1) {
|
||||
val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
|
||||
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
|
||||
} else {
|
||||
val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
|
||||
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
/*
|
||||
* Force PHY macro reset, PLL calibration reset, PLL reset
|
||||
* and assert Deserializer Reset
|
||||
*/
|
||||
val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
|
||||
writeb_relaxed(val, miphy_phy->base + RESET_REG);
|
||||
|
||||
if (miphy_phy->sata_tx_pol_inv)
|
||||
writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
|
||||
|
||||
/*
|
||||
* Force macro1 to use rx_lspd, tx_lspd
|
||||
* Force Rx_Clock on first I-DLL phase
|
||||
* Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3
|
||||
*/
|
||||
writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
|
||||
writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
|
||||
val = rx_tx_spd[miphy_phy->sata_gen];
|
||||
writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
|
||||
|
||||
/* Wait for HFC_READY = 0 */
|
||||
ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Compensation Recalibration */
|
||||
miphy365x_set_comp(miphy_phy, miphy_dev);
|
||||
|
||||
switch (miphy_phy->sata_gen) {
|
||||
case SATA_GEN3:
|
||||
/*
|
||||
* TX Swing target 550-600mv peak to peak diff
|
||||
* Tx Slew target 90-110ps rising/falling time
|
||||
* Rx Eq ON3, Sigdet threshold SDTH1
|
||||
*/
|
||||
val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3;
|
||||
writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG);
|
||||
val = SWING_VAL | PREEMPH_VAL;
|
||||
writeb_relaxed(val, miphy_phy->base + TXBUF1_REG);
|
||||
writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG);
|
||||
val = SDTHRES_VAL | EQ_ON3;
|
||||
writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
|
||||
break;
|
||||
case SATA_GEN2:
|
||||
/*
|
||||
* conf gen sel=0x1 to program Gen2 banked registers
|
||||
* VDDT filter ON
|
||||
* Tx Swing target 550-600mV peak-to-peak diff
|
||||
* Tx Slew target 90-110 ps rising/falling time
|
||||
* RX Equalization ON1, Sigdet threshold SDTH1
|
||||
*/
|
||||
writeb_relaxed(CONF_GEN_SEL_GEN2,
|
||||
miphy_phy->base + BUF_SEL_REG);
|
||||
writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG);
|
||||
writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
|
||||
val = SDTHRES_VAL | EQ_ON1;
|
||||
writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
|
||||
break;
|
||||
case SATA_GEN1:
|
||||
/*
|
||||
* conf gen sel = 00b to program Gen1 banked registers
|
||||
* VDDT filter ON
|
||||
* Tx Swing target 500-550mV peak-to-peak diff
|
||||
* Tx Slew target120-140 ps rising/falling time
|
||||
*/
|
||||
writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG);
|
||||
writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG);
|
||||
writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Force Macro1 in partial mode & release pll cal reset */
|
||||
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
|
||||
usleep_range(100, 150);
|
||||
|
||||
miphy365x_set_ssc(miphy_phy, miphy_dev);
|
||||
|
||||
/* Wait for phy_ready */
|
||||
ret = miphy365x_rdy(miphy_phy, miphy_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable macro1 to use rx_lspd & tx_lspd
|
||||
* Release Rx_Clock on first I-DLL phase on macro1
|
||||
* Assert deserializer reset
|
||||
* des_bit_lock_en is set
|
||||
* bit lock detection strength
|
||||
* Deassert deserializer reset
|
||||
*/
|
||||
writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
|
||||
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
|
||||
val = miphy_phy->sata_tx_pol_inv ?
|
||||
(TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
|
||||
writeb_relaxed(val, miphy_phy->base + CTRL_REG);
|
||||
|
||||
val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL;
|
||||
writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG);
|
||||
writeb_relaxed(0x00, miphy_phy->base + RESET_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int miphy365x_init(struct phy *phy)
|
||||
{
|
||||
struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy);
|
||||
struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&miphy_dev->miphy_mutex);
|
||||
|
||||
ret = miphy365x_set_path(miphy_phy, miphy_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&miphy_dev->miphy_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialise Miphy for PCIe or SATA */
|
||||
if (miphy_phy->type == MIPHY_TYPE_PCIE)
|
||||
ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
|
||||
else
|
||||
ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
|
||||
|
||||
mutex_unlock(&miphy_dev->miphy_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
|
||||
int index)
|
||||
{
|
||||
struct device_node *phynode = miphy_phy->phy->dev.of_node;
|
||||
const char *name;
|
||||
const __be32 *taddr;
|
||||
int type = miphy_phy->type;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_string_index(phynode, "reg-names", index, &name);
|
||||
if (ret) {
|
||||
dev_err(dev, "no reg-names property not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!strncmp(name, "syscfg", 6)) {
|
||||
taddr = of_get_address(phynode, index, NULL, NULL);
|
||||
if (!taddr) {
|
||||
dev_err(dev, "failed to fetch syscfg address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
|
||||
if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
|
||||
dev_err(dev, "failed to translate syscfg address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
|
||||
(!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
|
||||
return 0;
|
||||
|
||||
miphy_phy->base = of_iomap(phynode, index);
|
||||
if (!miphy_phy->base) {
|
||||
dev_err(dev, "Failed to map %s\n", phynode->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy *miphy365x_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev);
|
||||
struct miphy365x_phy *miphy_phy = NULL;
|
||||
struct device_node *phynode = args->np;
|
||||
int ret, index;
|
||||
|
||||
if (!of_device_is_available(phynode)) {
|
||||
dev_warn(dev, "Requested PHY is disabled\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (args->args_count != 1) {
|
||||
dev_err(dev, "Invalid number of cells in 'phy' property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
for (index = 0; index < of_get_child_count(dev->of_node); index++)
|
||||
if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
|
||||
miphy_phy = miphy_dev->phys[index];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!miphy_phy) {
|
||||
dev_err(dev, "Failed to find appropriate phy\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
miphy_phy->type = args->args[0];
|
||||
|
||||
if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
|
||||
miphy_phy->type == MIPHY_TYPE_PCIE)) {
|
||||
dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* Each port handles SATA and PCIE - third entry is always sysconf. */
|
||||
for (index = 0; index < 3; index++) {
|
||||
ret = miphy365x_get_addr(dev, miphy_phy, index);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return miphy_phy->phy;
|
||||
}
|
||||
|
||||
static struct phy_ops miphy365x_ops = {
|
||||
.init = miphy365x_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int miphy365x_of_probe(struct device_node *phynode,
|
||||
struct miphy365x_phy *miphy_phy)
|
||||
{
|
||||
of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen);
|
||||
if (!miphy_phy->sata_gen)
|
||||
miphy_phy->sata_gen = SATA_GEN1;
|
||||
|
||||
miphy_phy->pcie_tx_pol_inv =
|
||||
of_property_read_bool(phynode, "st,pcie-tx-pol-inv");
|
||||
|
||||
miphy_phy->sata_tx_pol_inv =
|
||||
of_property_read_bool(phynode, "st,sata-tx-pol-inv");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int miphy365x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *child, *np = pdev->dev.of_node;
|
||||
struct miphy365x_dev *miphy_dev;
|
||||
struct phy_provider *provider;
|
||||
struct phy *phy;
|
||||
int chancount, port = 0;
|
||||
int ret;
|
||||
|
||||
miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
|
||||
if (!miphy_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
chancount = of_get_child_count(np);
|
||||
miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
|
||||
GFP_KERNEL);
|
||||
if (!miphy_dev->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
||||
if (IS_ERR(miphy_dev->regmap)) {
|
||||
dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
|
||||
return PTR_ERR(miphy_dev->regmap);
|
||||
}
|
||||
|
||||
miphy_dev->dev = &pdev->dev;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, miphy_dev);
|
||||
|
||||
mutex_init(&miphy_dev->miphy_mutex);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct miphy365x_phy *miphy_phy;
|
||||
|
||||
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
|
||||
GFP_KERNEL);
|
||||
if (!miphy_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
miphy_dev->phys[port] = miphy_phy;
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(&pdev->dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
miphy_dev->phys[port]->phy = phy;
|
||||
|
||||
ret = miphy365x_of_probe(child, miphy_phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_set_drvdata(phy, miphy_dev->phys[port]);
|
||||
port++;
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
|
||||
if (IS_ERR(provider))
|
||||
return PTR_ERR(provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id miphy365x_of_match[] = {
|
||||
{ .compatible = "st,miphy365x-phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, miphy365x_of_match);
|
||||
|
||||
static struct platform_driver miphy365x_driver = {
|
||||
.probe = miphy365x_probe,
|
||||
.driver = {
|
||||
.name = "miphy365x-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = miphy365x_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(miphy365x_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics miphy365x driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
|
@ -26,6 +26,41 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
|
||||
/**
|
||||
* omap_control_pcie_pcs - set the PCS delay count
|
||||
* @dev: the control module device
|
||||
* @id: index of the pcie PHY (should be 1 or 2)
|
||||
* @delay: 8 bit delay value
|
||||
*/
|
||||
void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
|
||||
{
|
||||
u32 val;
|
||||
struct omap_control_phy *control_phy;
|
||||
|
||||
if (IS_ERR(dev) || !dev) {
|
||||
pr_err("%s: invalid device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
control_phy = dev_get_drvdata(dev);
|
||||
if (!control_phy) {
|
||||
dev_err(dev, "%s: invalid control phy device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (control_phy->type != OMAP_CTRL_TYPE_PCIE) {
|
||||
dev_err(dev, "%s: unsupported operation\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
val = readl(control_phy->pcie_pcs);
|
||||
val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
|
||||
(id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT));
|
||||
val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
|
||||
writel(val, control_phy->pcie_pcs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
|
||||
|
||||
/**
|
||||
* omap_control_phy_power - power on/off the phy using control module reg
|
||||
* @dev: the control module device
|
||||
@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on)
|
||||
val |= OMAP_CTRL_DEV_PHY_PD;
|
||||
break;
|
||||
|
||||
case OMAP_CTRL_TYPE_PCIE:
|
||||
case OMAP_CTRL_TYPE_PIPE3:
|
||||
rate = clk_get_rate(control_phy->sys_clk);
|
||||
rate = rate/1000000;
|
||||
@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
|
||||
static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
|
||||
static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
|
||||
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
|
||||
static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE;
|
||||
static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
|
||||
static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
|
||||
|
||||
@ -227,6 +264,10 @@ static const struct of_device_id omap_control_phy_id_table[] = {
|
||||
.compatible = "ti,control-phy-pipe3",
|
||||
.data = &pipe3_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,control-phy-pcie",
|
||||
.data = &pcie_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,control-phy-usb2-dra7",
|
||||
.data = &dra7usb2_data,
|
||||
@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 ||
|
||||
control_phy->type == OMAP_CTRL_TYPE_PCIE) {
|
||||
control_phy->sys_clk = devm_clk_get(control_phy->dev,
|
||||
"sys_clkin");
|
||||
if (IS_ERR(control_phy->sys_clk)) {
|
||||
@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (control_phy->type == OMAP_CTRL_TYPE_PCIE) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pcie_pcs");
|
||||
control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(control_phy->pcie_pcs))
|
||||
return PTR_ERR(control_phy->pcie_pcs);
|
||||
}
|
||||
|
||||
dev_set_drvdata(control_phy->dev, control_phy);
|
||||
|
||||
return 0;
|
||||
|
@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
|
289
drivers/phy/phy-qcom-apq8064-sata.c
Normal file
289
drivers/phy/phy-qcom-apq8064-sata.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
/* PHY registers */
|
||||
#define UNIPHY_PLL_REFCLK_CFG 0x000
|
||||
#define UNIPHY_PLL_PWRGEN_CFG 0x014
|
||||
#define UNIPHY_PLL_GLB_CFG 0x020
|
||||
#define UNIPHY_PLL_SDM_CFG0 0x038
|
||||
#define UNIPHY_PLL_SDM_CFG1 0x03C
|
||||
#define UNIPHY_PLL_SDM_CFG2 0x040
|
||||
#define UNIPHY_PLL_SDM_CFG3 0x044
|
||||
#define UNIPHY_PLL_SDM_CFG4 0x048
|
||||
#define UNIPHY_PLL_SSC_CFG0 0x04C
|
||||
#define UNIPHY_PLL_SSC_CFG1 0x050
|
||||
#define UNIPHY_PLL_SSC_CFG2 0x054
|
||||
#define UNIPHY_PLL_SSC_CFG3 0x058
|
||||
#define UNIPHY_PLL_LKDET_CFG0 0x05C
|
||||
#define UNIPHY_PLL_LKDET_CFG1 0x060
|
||||
#define UNIPHY_PLL_LKDET_CFG2 0x064
|
||||
#define UNIPHY_PLL_CAL_CFG0 0x06C
|
||||
#define UNIPHY_PLL_CAL_CFG8 0x08C
|
||||
#define UNIPHY_PLL_CAL_CFG9 0x090
|
||||
#define UNIPHY_PLL_CAL_CFG10 0x094
|
||||
#define UNIPHY_PLL_CAL_CFG11 0x098
|
||||
#define UNIPHY_PLL_STATUS 0x0C0
|
||||
|
||||
#define SATA_PHY_SER_CTRL 0x100
|
||||
#define SATA_PHY_TX_DRIV_CTRL0 0x104
|
||||
#define SATA_PHY_TX_DRIV_CTRL1 0x108
|
||||
#define SATA_PHY_TX_IMCAL0 0x11C
|
||||
#define SATA_PHY_TX_IMCAL2 0x124
|
||||
#define SATA_PHY_RX_IMCAL0 0x128
|
||||
#define SATA_PHY_EQUAL 0x13C
|
||||
#define SATA_PHY_OOB_TERM 0x144
|
||||
#define SATA_PHY_CDR_CTRL0 0x148
|
||||
#define SATA_PHY_CDR_CTRL1 0x14C
|
||||
#define SATA_PHY_CDR_CTRL2 0x150
|
||||
#define SATA_PHY_CDR_CTRL3 0x154
|
||||
#define SATA_PHY_PI_CTRL0 0x168
|
||||
#define SATA_PHY_POW_DWN_CTRL0 0x180
|
||||
#define SATA_PHY_POW_DWN_CTRL1 0x184
|
||||
#define SATA_PHY_TX_DATA_CTRL 0x188
|
||||
#define SATA_PHY_ALIGNP 0x1A4
|
||||
#define SATA_PHY_TX_IMCAL_STAT 0x1E4
|
||||
#define SATA_PHY_RX_IMCAL_STAT 0x1E8
|
||||
|
||||
#define UNIPHY_PLL_LOCK BIT(0)
|
||||
#define SATA_PHY_TX_CAL BIT(0)
|
||||
#define SATA_PHY_RX_CAL BIT(0)
|
||||
|
||||
/* default timeout set to 1 sec */
|
||||
#define TIMEOUT_MS 10000
|
||||
#define DELAY_INTERVAL_US 100
|
||||
|
||||
struct qcom_apq8064_sata_phy {
|
||||
void __iomem *mmio;
|
||||
struct clk *cfg_clk;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/* Helper function to do poll and timeout */
|
||||
static int read_poll_timeout(void __iomem *addr, u32 mask)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
|
||||
|
||||
do {
|
||||
if (readl_relaxed(addr) & mask)
|
||||
return 0;
|
||||
|
||||
usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
void __iomem *base = phy->mmio;
|
||||
int ret = 0;
|
||||
|
||||
/* SATA phy initialization */
|
||||
writel_relaxed(0x01, base + SATA_PHY_SER_CTRL);
|
||||
writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
/* Make sure the power down happens before power up */
|
||||
mb();
|
||||
usleep_range(10, 60);
|
||||
|
||||
writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
|
||||
writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
|
||||
writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2);
|
||||
|
||||
/* Write UNIPHYPLL registers to configure PLL */
|
||||
writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG);
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG);
|
||||
|
||||
writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0);
|
||||
writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8);
|
||||
writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9);
|
||||
writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10);
|
||||
writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11);
|
||||
|
||||
writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0);
|
||||
writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1);
|
||||
writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2);
|
||||
writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3);
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4);
|
||||
|
||||
writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0);
|
||||
writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1);
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2);
|
||||
writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3);
|
||||
|
||||
writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0);
|
||||
writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1);
|
||||
|
||||
writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG);
|
||||
/* make sure global config LDO power down happens before power up */
|
||||
mb();
|
||||
|
||||
writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG);
|
||||
writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
|
||||
|
||||
/* PLL Lock wait */
|
||||
ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TX Calibration */
|
||||
ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* RX Calibration */
|
||||
ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* SATA phy calibrated succesfully, power up to functional mode */
|
||||
writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
|
||||
writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
|
||||
|
||||
writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0);
|
||||
writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1);
|
||||
writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2);
|
||||
writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0);
|
||||
writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3);
|
||||
writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
|
||||
writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL);
|
||||
writel_relaxed(0x43, base + SATA_PHY_ALIGNP);
|
||||
writel_relaxed(0x04, base + SATA_PHY_OOB_TERM);
|
||||
|
||||
writel_relaxed(0x01, base + SATA_PHY_EQUAL);
|
||||
writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0);
|
||||
writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
void __iomem *base = phy->mmio;
|
||||
|
||||
/* Power down PHY */
|
||||
writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0);
|
||||
writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1);
|
||||
|
||||
/* Power down PLL block */
|
||||
writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops qcom_apq8064_sata_phy_ops = {
|
||||
.init = qcom_apq8064_sata_phy_init,
|
||||
.exit = qcom_apq8064_sata_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy *generic_phy;
|
||||
int ret;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "%s: failed to create phy\n", __func__);
|
||||
return PTR_ERR(generic_phy);
|
||||
}
|
||||
|
||||
phy->dev = dev;
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
phy->cfg_clk = devm_clk_get(dev, "cfg");
|
||||
if (IS_ERR(phy->cfg_clk)) {
|
||||
dev_err(dev, "Failed to get sata cfg clock\n");
|
||||
return PTR_ERR(phy->cfg_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(phy->cfg_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
dev_err(dev, "%s: failed to register phy\n", __func__);
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = {
|
||||
{ .compatible = "qcom,apq8064-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver qcom_apq8064_sata_phy_driver = {
|
||||
.probe = qcom_apq8064_sata_phy_probe,
|
||||
.remove = qcom_apq8064_sata_phy_remove,
|
||||
.driver = {
|
||||
.name = "qcom-apq8064-sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = qcom_apq8064_sata_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(qcom_apq8064_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
211
drivers/phy/phy-qcom-ipq806x-sata.c
Normal file
211
drivers/phy/phy-qcom-ipq806x-sata.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
struct qcom_ipq806x_sata_phy {
|
||||
void __iomem *mmio;
|
||||
struct clk *cfg_clk;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
|
||||
|
||||
#define SATA_PHY_P0_PARAM0 0x200
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
|
||||
#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
|
||||
|
||||
#define SATA_PHY_P0_PARAM1 0x204
|
||||
#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
|
||||
#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
|
||||
|
||||
#define SATA_PHY_P0_PARAM2 0x208
|
||||
#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
|
||||
#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
|
||||
|
||||
#define SATA_PHY_P0_PARAM3 0x20C
|
||||
#define SATA_PHY_SSC_EN 0x8
|
||||
#define SATA_PHY_P0_PARAM4 0x210
|
||||
#define SATA_PHY_REF_SSP_EN 0x2
|
||||
#define SATA_PHY_RESET 0x1
|
||||
|
||||
static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
u32 reg;
|
||||
|
||||
/* Setting SSC_EN to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
|
||||
reg = reg | SATA_PHY_SSC_EN;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
|
||||
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
|
||||
~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
|
||||
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
|
||||
SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
|
||||
reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
|
||||
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
|
||||
~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
|
||||
reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
|
||||
SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
|
||||
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
|
||||
~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
|
||||
reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
|
||||
|
||||
/* Setting PHY_RESET to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg | SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
/* Setting REF_SSP_EN to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
/* make sure all changes complete before we let the PHY out of reset */
|
||||
mb();
|
||||
|
||||
/* sleep for max. 50us more to combine processor wakeups */
|
||||
usleep_range(20, 20 + 50);
|
||||
|
||||
/* Clearing PHY_RESET to 0 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg & ~SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
|
||||
u32 reg;
|
||||
|
||||
/* Setting PHY_RESET to 1 */
|
||||
reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
reg = reg | SATA_PHY_RESET;
|
||||
writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops qcom_ipq806x_sata_phy_ops = {
|
||||
.init = qcom_ipq806x_sata_phy_init,
|
||||
.exit = qcom_ipq806x_sata_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy *generic_phy;
|
||||
int ret;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "%s: failed to create phy\n", __func__);
|
||||
return PTR_ERR(generic_phy);
|
||||
}
|
||||
|
||||
phy->dev = dev;
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
phy->cfg_clk = devm_clk_get(dev, "cfg");
|
||||
if (IS_ERR(phy->cfg_clk)) {
|
||||
dev_err(dev, "Failed to get sata cfg clock\n");
|
||||
return PTR_ERR(phy->cfg_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(phy->cfg_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
dev_err(dev, "%s: failed to register phy\n", __func__);
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(phy->cfg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
|
||||
{ .compatible = "qcom,ipq806x-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
|
||||
|
||||
static struct platform_driver qcom_ipq806x_sata_phy_driver = {
|
||||
.probe = qcom_ipq806x_sata_phy_probe,
|
||||
.remove = qcom_ipq806x_sata_phy_remove,
|
||||
.driver = {
|
||||
.name = "qcom-ipq806x-sata-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = qcom_ipq806x_sata_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(qcom_ipq806x_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -87,6 +87,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev,
|
||||
}
|
||||
|
||||
static const struct of_device_id samsung_usb2_phy_of_match[] = {
|
||||
#ifdef CONFIG_PHY_EXYNOS4X12_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos3250-usb2-phy",
|
||||
.data = &exynos3250_usb2_phy_config,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_PHY_EXYNOS4210_USB2
|
||||
{
|
||||
.compatible = "samsung,exynos4210-usb2-phy",
|
||||
@ -190,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
|
||||
struct samsung_usb2_phy_instance *p = &drv->instances[i];
|
||||
|
||||
dev_dbg(dev, "Creating phy \"%s\"\n", label);
|
||||
p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
|
||||
p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
|
||||
NULL);
|
||||
if (IS_ERR(p->phy)) {
|
||||
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
|
||||
label);
|
||||
|
@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance {
|
||||
const struct samsung_usb2_common_phy *cfg;
|
||||
struct phy *phy;
|
||||
struct samsung_usb2_phy_driver *drv;
|
||||
bool enabled;
|
||||
int int_cnt;
|
||||
int ext_cnt;
|
||||
};
|
||||
|
||||
struct samsung_usb2_phy_driver {
|
||||
@ -59,8 +60,10 @@ struct samsung_usb2_phy_config {
|
||||
int (*rate_to_clk)(unsigned long, u32 *);
|
||||
unsigned int num_phys;
|
||||
bool has_mode_switch;
|
||||
bool has_refclk_sel;
|
||||
};
|
||||
|
||||
extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config;
|
||||
extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
|
||||
extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
|
||||
extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -294,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(phy->pmu);
|
||||
}
|
||||
|
||||
phy->phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
|
||||
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
|
||||
if (IS_ERR(phy->phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
return PTR_ERR(phy->phy);
|
||||
@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
|
@ -80,7 +80,9 @@ struct ti_pipe3 {
|
||||
struct clk *wkupclk;
|
||||
struct clk *sys_clk;
|
||||
struct clk *refclk;
|
||||
struct clk *div_clk;
|
||||
struct pipe3_dpll_map *dpll_map;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static struct pipe3_dpll_map dpll_map_usb[] = {
|
||||
@ -215,6 +217,11 @@ static int ti_pipe3_init(struct phy *x)
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
|
||||
omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bring it out of IDLE if it is IDLE */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
if (val & PLL_IDLE) {
|
||||
@ -238,8 +245,11 @@ static int ti_pipe3_exit(struct phy *x)
|
||||
u32 val;
|
||||
unsigned long timeout;
|
||||
|
||||
/* SATA DPLL can't be powered down due to Errata i783 */
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
|
||||
/* SATA DPLL can't be powered down due to Errata i783 and PCIe
|
||||
* does not have internal DPLL
|
||||
*/
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") ||
|
||||
of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie"))
|
||||
return 0;
|
||||
|
||||
/* Put DPLL in IDLE mode */
|
||||
@ -286,32 +296,41 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
struct device_node *control_node;
|
||||
struct platform_device *control_pdev;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
struct clk *clk;
|
||||
|
||||
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
|
||||
if (!phy->dpll_map) {
|
||||
dev_err(&pdev->dev, "no DPLL data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
|
||||
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(phy->pll_ctrl_base))
|
||||
return PTR_ERR(phy->pll_ctrl_base);
|
||||
|
||||
phy->dev = &pdev->dev;
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
match = of_match_device(of_match_ptr(ti_pipe3_id_table),
|
||||
&pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
|
||||
if (!phy->dpll_map) {
|
||||
dev_err(&pdev->dev, "no DPLL data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pll_ctrl");
|
||||
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(phy->pll_ctrl_base))
|
||||
return PTR_ERR(phy->pll_ctrl_base);
|
||||
|
||||
phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
|
||||
if (IS_ERR(phy->sys_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get sysclk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
dev_err(&pdev->dev, "unable to get wkupclk\n");
|
||||
@ -328,10 +347,38 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
phy->refclk = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
|
||||
if (IS_ERR(phy->sys_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get sysclk\n");
|
||||
return -EINVAL;
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
if (of_property_read_u8(node, "id", &phy->id) < 0)
|
||||
phy->id = 1;
|
||||
|
||||
clk = devm_clk_get(phy->dev, "dpll_ref");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get dpll ref clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 1500000000);
|
||||
|
||||
clk = devm_clk_get(phy->dev, "dpll_ref_m2");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 100000000);
|
||||
|
||||
clk = devm_clk_get(phy->dev, "phy-div");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get phy-div clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 100000000);
|
||||
|
||||
phy->div_clk = devm_clk_get(phy->dev, "div-clk");
|
||||
if (IS_ERR(phy->div_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get div-clk\n");
|
||||
return PTR_ERR(phy->div_clk);
|
||||
}
|
||||
} else {
|
||||
phy->div_clk = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
@ -353,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
@ -387,6 +434,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev)
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
if (!IS_ERR(phy->div_clk))
|
||||
clk_disable_unprepare(phy->div_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -412,8 +461,19 @@ static int ti_pipe3_runtime_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(phy->div_clk)) {
|
||||
ret = clk_prepare_enable(phy->div_clk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "Failed to enable div_clk %d\n", ret);
|
||||
goto err3;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
if (!IS_ERR(phy->wkupclk))
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
|
||||
err2:
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
@ -446,6 +506,9 @@ static const struct of_device_id ti_pipe3_id_table[] = {
|
||||
.compatible = "ti,phy-pipe3-sata",
|
||||
.data = dpll_map_sata,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,phy-pipe3-pcie",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
|
||||
|
@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
otg->set_host = twl4030_set_host;
|
||||
otg->set_peripheral = twl4030_set_peripheral;
|
||||
|
||||
phy = devm_phy_create(twl->dev, &ops, init_data);
|
||||
phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
|
@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
|
||||
ctx->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL);
|
||||
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
|
||||
if (IS_ERR(ctx->phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
rc = PTR_ERR(ctx->phy);
|
||||
|
14
include/dt-bindings/phy/phy-miphy365x.h
Normal file
14
include/dt-bindings/phy/phy-miphy365x.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This header provides constants for the phy framework
|
||||
* based on the STMicroelectronics MiPHY365x.
|
||||
*
|
||||
* Author: Lee Jones <lee.jones@linaro.org>
|
||||
*/
|
||||
#ifndef _DT_BINDINGS_PHY_MIPHY
|
||||
#define _DT_BINDINGS_PHY_MIPHY
|
||||
|
||||
#define MIPHY_TYPE_SATA 1
|
||||
#define MIPHY_TYPE_PCIE 2
|
||||
#define MIPHY_TYPE_USB 3
|
||||
|
||||
#endif /* _DT_BINDINGS_PHY_MIPHY */
|
@ -23,6 +23,7 @@ enum omap_control_phy_type {
|
||||
OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */
|
||||
OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */
|
||||
OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
|
||||
OMAP_CTRL_TYPE_PCIE, /* RX TX control of ACSPCIE */
|
||||
OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
|
||||
OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
|
||||
};
|
||||
@ -33,6 +34,7 @@ struct omap_control_phy {
|
||||
u32 __iomem *otghs_control;
|
||||
u32 __iomem *power;
|
||||
u32 __iomem *power_aux;
|
||||
u32 __iomem *pcie_pcs;
|
||||
|
||||
struct clk *sys_clk;
|
||||
|
||||
@ -63,6 +65,9 @@ enum omap_control_usb_mode {
|
||||
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3
|
||||
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0
|
||||
|
||||
#define OMAP_CTRL_PCIE_PCS_MASK 0xff
|
||||
#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 0x8
|
||||
|
||||
#define OMAP_CTRL_USB2_PHY_PD BIT(28)
|
||||
|
||||
#define AM437X_CTRL_USB2_PHY_PD BIT(0)
|
||||
@ -74,6 +79,7 @@ enum omap_control_usb_mode {
|
||||
void omap_control_phy_power(struct device *dev, int on);
|
||||
void omap_control_usb_set_mode(struct device *dev,
|
||||
enum omap_control_usb_mode mode);
|
||||
void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay);
|
||||
#else
|
||||
|
||||
static inline void omap_control_phy_power(struct device *dev, int on)
|
||||
@ -84,6 +90,10 @@ static inline void omap_control_usb_set_mode(struct device *dev,
|
||||
enum omap_control_usb_mode mode)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __OMAP_CONTROL_PHY_H__ */
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct phy;
|
||||
|
||||
@ -65,6 +66,7 @@ struct phy {
|
||||
int init_count;
|
||||
int power_count;
|
||||
struct phy_attrs attrs;
|
||||
struct regulator *pwr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -156,9 +158,10 @@ void devm_phy_put(struct device *dev, struct phy *phy);
|
||||
struct phy *of_phy_get(struct device_node *np, const char *con_id);
|
||||
struct phy *of_phy_simple_xlate(struct device *dev,
|
||||
struct of_phandle_args *args);
|
||||
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *devm_phy_create(struct device *dev,
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops, struct phy_init_data *init_data);
|
||||
void phy_destroy(struct phy *phy);
|
||||
void devm_phy_destroy(struct device *dev, struct phy *phy);
|
||||
@ -297,13 +300,17 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev,
|
||||
}
|
||||
|
||||
static inline struct phy *phy_create(struct device *dev,
|
||||
const struct phy_ops *ops, struct phy_init_data *init_data)
|
||||
struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
|
||||
static inline struct phy *devm_phy_create(struct device *dev,
|
||||
const struct phy_ops *ops, struct phy_init_data *init_data)
|
||||
struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user