phy for 5.9
- Core: - New PHY attribute for max_link_rate - New phy drivers: - Rockchip dphy driver moved from staging - Socionext UniPhier AHCI PHY driver - Intel LGM SoC USB phy - Intel Keem Bay eMMC PHY driver - Updates: - Support for imx8mp usb phy - Support for DP Phy and USB3+DP combo phy in QMP driver - Support for Qualcomm sc7180 DP phy - Support for cadence torrent PCIe and USB single linke and multilink configurations along with USB, SGMII/QSGMII configurations -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAl929+AACgkQfBQHDyUj g0cyqQ/+OGgTU1pbJdUAztYWnIy1q3g4Y39Kc9iAwuHrl3g9DB65JakJrnBvPjwL cSwdKHZbWHVAyDGIRjnroUqJ1EzOFsFubSmZ444oQdRAIB+wz1MBhD+ntiw172nX LFe1hQYCuLM2Vzkzs3OTdCLBDuoZd8JHeZzV/RVzQAHshnRAoim2Wfh+/k/YWjwB g1YB5ylsCH9MmsJBsvf5QEOwKyfDhmyEUdeFI1uQt7LipQ8uhjg0/Mg8Jc5D5aRi kqUN8y7UrJ7Tyc1tradt700zg4gnI240VLPdb0pyxcvm5H6lTVrzZGgJuVHeWYG/ JMdrB9+ZMC5yfiAtiKFjeUikYQr+RqDfR3iNk683k8k9G/0VjFJtC53pTHSVdMO5 SfijP2iIRsnAU+iVJ576/eXPlOeUqacjWQRndbkXTHH5zfp0e2cmEKAczTxC7ACA 3FpakddNy54wRDjAQW2/YL76gf2HD05ud6OOi40Ohc1Wd0EErLjcMhdPf59KPSMQ 9kOLrDXbYIwVB+s2ecDjxpacqugfmlFQztFuPYapukRvQHqwYhjqfqcSWJHL2cZe WRwJ9Qc6V7OklFU1MmiVhZn3H5Xr6xitK4+XKKP6FtU42TaJ4f9e71JECo5eGo9d b+4nRrm7AirIJ9R2dXiKOvzY0iYNrGPmHccVUfJLhlJVWU6szbE= =U0WQ -----END PGP SIGNATURE----- Merge tag 'phy-for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy into usb-next Vinod writes: phy for 5.9 - Core: - New PHY attribute for max_link_rate - New phy drivers: - Rockchip dphy driver moved from staging - Socionext UniPhier AHCI PHY driver - Intel LGM SoC USB phy - Intel Keem Bay eMMC PHY driver - Updates: - Support for imx8mp usb phy - Support for DP Phy and USB3+DP combo phy in QMP driver - Support for Qualcomm sc7180 DP phy - Support for cadence torrent PCIe and USB single linke and multilink configurations along with USB, SGMII/QSGMII configurations * tag 'phy-for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (72 commits) phy: qcom-qmp: initialize the pointer to NULL phy: qcom-qmp: Add support for sc7180 DP phy phy: qcom-qmp: Add support for DP in USB3+DP combo phy phy: qcom-qmp: Use devm_platform_ioremap_resource() to simplify phy: qcom-qmp: Get dp_com I/O resource by index phy: qcom-qmp: Move 'serdes' and 'cfg' into 'struct qcom_phy' phy: qcom-qmp: Remove 'initialized' in favor of 'init_count' phy: qcom-qmp: Move phy mode into struct qmp_phy dt-bindings: phy: qcom,qmp-usb3-dp: Add DP phy information dt-bindings: phy: ti,phy-j721e-wiz: fix bindings for torrent phy dt-bindings: phy: cdns,torrent-phy: add reset-names phy: rockchip-dphy-rx0: Include linux/delay.h phy: fix USB_LGM_PHY warning & build errors phy: cadence-torrent: Add USB + SGMII/QSGMII multilink configuration phy: cadence-torrent: Add PCIe + USB multilink configuration phy: cadence-torrent: Add single link USB register sequences phy: cadence-torrent: Add single link SGMII/QSGMII register sequences phy: cadence-torrent: Configure PHY_PLL_CFG as part of link_cmn_vals phy: cadence-torrent: Add PHY link configuration sequences for single link phy: cadence-torrent: Add clk changes for multilink configuration ...
This commit is contained in:
commit
9f76e198dd
|
@ -1,7 +1,7 @@
|
|||
* Freescale i.MX8MQ USB3 PHY binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx8mq-usb-phy"
|
||||
- compatible: Should be "fsl,imx8mq-usb-phy" or "fsl,imx8mp-usb-phy"
|
||||
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
|
||||
- reg: The base address and length of the registers
|
||||
- clocks: phandles to the clocks for each clock listed in clock-names
|
||||
|
|
|
@ -23,7 +23,9 @@ description: |+
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,lgm-emmc-phy
|
||||
oneOf:
|
||||
- const: intel,lgm-emmc-phy
|
||||
- const: intel,keembay-emmc-phy
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
@ -34,6 +36,10 @@ properties:
|
|||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: emmcclk
|
||||
|
||||
required:
|
||||
- "#phy-cells"
|
||||
- compatible
|
||||
|
@ -57,4 +63,13 @@ examples:
|
|||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
phy@20290000 {
|
||||
compatible = "intel,keembay-emmc-phy";
|
||||
reg = <0x20290000 0x54>;
|
||||
clocks = <&emmc>;
|
||||
clock-names = "emmcclk";
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
...
|
||||
|
|
58
Documentation/devicetree/bindings/phy/intel,lgm-usb-phy.yaml
Normal file
58
Documentation/devicetree/bindings/phy/intel,lgm-usb-phy.yaml
Normal file
|
@ -0,0 +1,58 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/intel,lgm-usb-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel LGM USB PHY Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Vadivel Murugan Ramuthevar <vadivel.muruganx.ramuthevar@linux.intel.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,lgm-usb-phy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: USB PHY and Host controller reset
|
||||
- description: APB BUS reset
|
||||
- description: General Hardware reset
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: phy
|
||||
- const: apb
|
||||
- const: phy31
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- reg
|
||||
- resets
|
||||
- reset-names
|
||||
- "#phy-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb-phy@e7e00000 {
|
||||
compatible = "intel,lgm-usb-phy";
|
||||
reg = <0xe7e00000 0x10000>;
|
||||
clocks = <&cgu0 153>;
|
||||
resets = <&rcu 0x70 0x24>,
|
||||
<&rcu 0x70 0x26>,
|
||||
<&rcu 0x70 0x28>;
|
||||
reset-names = "phy", "apb", "phy31";
|
||||
#phy-cells = <0>;
|
||||
};
|
|
@ -4,11 +4,13 @@
|
|||
$id: "http://devicetree.org/schemas/phy/phy-cadence-torrent.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Cadence Torrent SD0801 PHY binding for DisplayPort
|
||||
title: Cadence Torrent SD0801 PHY binding
|
||||
|
||||
description:
|
||||
This binding describes the Cadence SD0801 PHY (also known as Torrent PHY)
|
||||
hardware included with the Cadence MHDP DisplayPort controller.
|
||||
hardware included with the Cadence MHDP DisplayPort controller. Torrent
|
||||
PHY also supports multilink multiprotocol combinations including protocols
|
||||
such as PCIe, USB, SGMII, QSGMII etc.
|
||||
|
||||
maintainers:
|
||||
- Swapnil Jakhade <sjakhade@cadence.com>
|
||||
|
@ -49,13 +51,21 @@ properties:
|
|||
- const: dptx_phy
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
description:
|
||||
Torrent PHY reset.
|
||||
See Documentation/devicetree/bindings/reset/reset.txt
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: Torrent PHY reset.
|
||||
- description: Torrent APB reset. This is optional.
|
||||
|
||||
reset-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: torrent_reset
|
||||
- const: torrent_apb
|
||||
|
||||
patternProperties:
|
||||
'^phy@[0-7]+$':
|
||||
'^phy@[0-3]$':
|
||||
type: object
|
||||
description:
|
||||
Each group of PHY lanes with a single master lane should be represented as a sub-node.
|
||||
|
@ -63,6 +73,8 @@ patternProperties:
|
|||
reg:
|
||||
description:
|
||||
The master lane number. This is the lowest numbered lane in the lane group.
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
|
@ -78,15 +90,25 @@ patternProperties:
|
|||
Specifies the type of PHY for which the group of PHY lanes is used.
|
||||
Refer include/dt-bindings/phy/phy.h. Constants from the header should be used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [1, 2, 3, 4, 5, 6]
|
||||
minimum: 1
|
||||
maximum: 9
|
||||
|
||||
cdns,num-lanes:
|
||||
description:
|
||||
Number of DisplayPort lanes.
|
||||
Number of lanes.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [1, 2, 4]
|
||||
enum: [1, 2, 3, 4]
|
||||
default: 4
|
||||
|
||||
cdns,ssc-mode:
|
||||
description:
|
||||
Specifies the Spread Spectrum Clocking mode used. It can be NO_SSC,
|
||||
EXTERNAL_SSC or INTERNAL_SSC.
|
||||
Refer include/dt-bindings/phy/phy-cadence-torrent.h for the constants to be used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1, 2]
|
||||
default: 0
|
||||
|
||||
cdns,max-bit-rate:
|
||||
description:
|
||||
Maximum DisplayPort link bit rate to use, in Mbps
|
||||
|
@ -99,6 +121,7 @@ patternProperties:
|
|||
- resets
|
||||
- "#phy-cells"
|
||||
- cdns,phy-type
|
||||
- cdns,num-lanes
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -111,6 +134,7 @@ required:
|
|||
- reg
|
||||
- reg-names
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -128,18 +152,56 @@ examples:
|
|||
<0xf0 0xfb030a00 0x0 0x00000040>;
|
||||
reg-names = "torrent_phy", "dptx_phy";
|
||||
resets = <&phyrst 0>;
|
||||
reset-names = "torrent_reset";
|
||||
clocks = <&ref_clk>;
|
||||
clock-names = "refclk";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy@0 {
|
||||
reg = <0>;
|
||||
resets = <&phyrst 1>, <&phyrst 2>,
|
||||
<&phyrst 3>, <&phyrst 4>;
|
||||
#phy-cells = <0>;
|
||||
cdns,phy-type = <PHY_TYPE_DP>;
|
||||
cdns,num-lanes = <4>;
|
||||
cdns,max-bit-rate = <8100>;
|
||||
reg = <0>;
|
||||
resets = <&phyrst 1>, <&phyrst 2>,
|
||||
<&phyrst 3>, <&phyrst 4>;
|
||||
#phy-cells = <0>;
|
||||
cdns,phy-type = <PHY_TYPE_DP>;
|
||||
cdns,num-lanes = <4>;
|
||||
cdns,max-bit-rate = <8100>;
|
||||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <dt-bindings/phy/phy-cadence-torrent.h>
|
||||
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
torrent-phy@f0fb500000 {
|
||||
compatible = "cdns,torrent-phy";
|
||||
reg = <0xf0 0xfb500000 0x0 0x00100000>;
|
||||
reg-names = "torrent_phy";
|
||||
resets = <&phyrst 0>, <&phyrst 1>;
|
||||
reset-names = "torrent_reset", "torrent_apb";
|
||||
clocks = <&ref_clk>;
|
||||
clock-names = "refclk";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy@0 {
|
||||
reg = <0>;
|
||||
resets = <&phyrst 2>, <&phyrst 3>;
|
||||
#phy-cells = <0>;
|
||||
cdns,phy-type = <PHY_TYPE_PCIE>;
|
||||
cdns,num-lanes = <2>;
|
||||
cdns,ssc-mode = <TORRENT_SERDES_NO_SSC>;
|
||||
};
|
||||
|
||||
phy@2 {
|
||||
reg = <2>;
|
||||
resets = <&phyrst 4>;
|
||||
#phy-cells = <0>;
|
||||
cdns,phy-type = <PHY_TYPE_SGMII>;
|
||||
cdns,num-lanes = <1>;
|
||||
cdns,ssc-mode = <TORRENT_SERDES_NO_SSC>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -13,17 +13,21 @@ maintainers:
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sc7180-qmp-usb3-dp-phy
|
||||
- qcom,sc7180-qmp-usb3-phy
|
||||
- qcom,sdm845-qmp-usb3-dp-phy
|
||||
- qcom,sdm845-qmp-usb3-phy
|
||||
reg:
|
||||
items:
|
||||
- description: Address and length of PHY's common serdes block.
|
||||
- description: Address and length of PHY's USB serdes block.
|
||||
- description: Address and length of the DP_COM control block.
|
||||
- description: Address and length of PHY's DP serdes block.
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: reg-base
|
||||
- const: usb
|
||||
- const: dp_com
|
||||
- const: dp
|
||||
|
||||
"#clock-cells":
|
||||
enum: [ 1, 2 ]
|
||||
|
@ -74,16 +78,74 @@ properties:
|
|||
|
||||
#Required nodes:
|
||||
patternProperties:
|
||||
"^phy@[0-9a-f]+$":
|
||||
"^usb3-phy@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
Each device node of QMP phy is required to have as many child nodes as
|
||||
the number of lanes the PHY has.
|
||||
The USB3 PHY.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Address and length of TX.
|
||||
- description: Address and length of RX.
|
||||
- description: Address and length of PCS.
|
||||
- description: Address and length of TX2.
|
||||
- description: Address and length of RX2.
|
||||
- description: Address and length of pcs_misc.
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: pipe clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pipe0
|
||||
|
||||
clock-output-names:
|
||||
items:
|
||||
- const: usb3_phy_pipe_clk_src
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
'#phy-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#clock-cells'
|
||||
- '#phy-cells'
|
||||
|
||||
"^dp-phy@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
The DP PHY.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Address and length of TX.
|
||||
- description: Address and length of RX.
|
||||
- description: Address and length of PCS.
|
||||
- description: Address and length of TX2.
|
||||
- description: Address and length of RX2.
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
'#phy-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- reg
|
||||
- '#clock-cells'
|
||||
- '#phy-cells'
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- "#clock-cells"
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
@ -101,14 +163,15 @@ examples:
|
|||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
|
||||
usb_1_qmpphy: phy-wrapper@88e9000 {
|
||||
compatible = "qcom,sdm845-qmp-usb3-phy";
|
||||
compatible = "qcom,sdm845-qmp-usb3-dp-phy";
|
||||
reg = <0x088e9000 0x18c>,
|
||||
<0x088e8000 0x10>;
|
||||
reg-names = "reg-base", "dp_com";
|
||||
<0x088e8000 0x10>,
|
||||
<0x088ea000 0x40>;
|
||||
reg-names = "usb", "dp_com", "dp";
|
||||
#clock-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x088e9000 0x1000>;
|
||||
ranges = <0x0 0x088e9000 0x2000>;
|
||||
|
||||
clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
|
||||
<&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
|
||||
|
@ -123,7 +186,7 @@ examples:
|
|||
vdda-phy-supply = <&vdda_usb2_ss_1p2>;
|
||||
vdda-pll-supply = <&vdda_usb2_ss_core>;
|
||||
|
||||
phy@200 {
|
||||
usb3-phy@200 {
|
||||
reg = <0x200 0x128>,
|
||||
<0x400 0x200>,
|
||||
<0xc00 0x218>,
|
||||
|
@ -136,4 +199,14 @@ examples:
|
|||
clock-names = "pipe0";
|
||||
clock-output-names = "usb3_phy_pipe_clk_src";
|
||||
};
|
||||
|
||||
dp-phy@88ea200 {
|
||||
reg = <0xa200 0x200>,
|
||||
<0xa400 0x200>,
|
||||
<0xaa00 0x200>,
|
||||
<0xa600 0x200>,
|
||||
<0xa800 0x200>;
|
||||
#clock-cells = <1>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/socionext,uniphier-ahci-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Socionext UniPhier AHCI PHY
|
||||
|
||||
description: |
|
||||
This describes the deivcetree bindings for PHY interfaces built into
|
||||
AHCI controller implemented on Socionext UniPhier SoCs.
|
||||
|
||||
maintainers:
|
||||
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- socionext,uniphier-pxs2-ahci-phy
|
||||
- socionext,uniphier-pxs3-ahci-phy
|
||||
|
||||
reg:
|
||||
description: PHY register region (offset and length)
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items: # for PXs2
|
||||
- const: link
|
||||
- items: # for others
|
||||
- const: link
|
||||
- const: phy
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: link
|
||||
- const: phy
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#phy-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
ahci-glue@65700000 {
|
||||
compatible = "socionext,uniphier-pxs3-ahci-glue",
|
||||
"simple-mfd";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0x65700000 0x100>;
|
||||
|
||||
ahci_phy: phy@10 {
|
||||
compatible = "socionext,uniphier-pxs3-ahci-phy";
|
||||
reg = <0x10 0x10>;
|
||||
#phy-cells = <0>;
|
||||
clock-names = "link", "phy";
|
||||
clocks = <&sys_clk 28>, <&sys_clk 30>;
|
||||
reset-names = "link", "phy";
|
||||
resets = <&sys_rst 28>, <&sys_rst 30>;
|
||||
};
|
||||
};
|
74
Documentation/devicetree/bindings/phy/ti,omap-usb2.yaml
Normal file
74
Documentation/devicetree/bindings/phy/ti,omap-usb2.yaml
Normal file
|
@ -0,0 +1,74 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/ti,omap-usb2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: OMAP USB2 PHY
|
||||
|
||||
maintainers:
|
||||
- Kishon Vijay Abraham I <kishon@ti.com>
|
||||
- Roger Quadros <rogerq@ti.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- ti,dra7x-usb2
|
||||
- ti,dra7x-usb2-phy2
|
||||
- ti,am654-usb2
|
||||
- enum:
|
||||
- ti,omap-usb2
|
||||
- items:
|
||||
- const: ti,am437x-usb2
|
||||
- items:
|
||||
- const: ti,omap-usb2
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: wakeup clock
|
||||
- description: reference clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: wkupclk
|
||||
- const: refclk
|
||||
|
||||
syscon-phy-power:
|
||||
$ref: /schemas/types.yaml#definitions/phandle-array
|
||||
description:
|
||||
phandle/offset pair. Phandle to the system control module and
|
||||
register offset to power on/off the PHY.
|
||||
|
||||
ctrl-module:
|
||||
$ref: /schemas/types.yaml#definitions/phandle
|
||||
description:
|
||||
(deprecated) phandle of the control module used by PHY driver
|
||||
to power on the PHY. Use syscon-phy-power instead.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#phy-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb0_phy: phy@4100000 {
|
||||
compatible = "ti,am654-usb2", "ti,omap-usb2";
|
||||
reg = <0x4100000 0x54>;
|
||||
syscon-phy-power = <&scm_conf 0x4000>;
|
||||
clocks = <&k3_clks 151 0>, <&k3_clks 151 1>;
|
||||
clock-names = "wkupclk", "refclk";
|
||||
#phy-cells = <0>;
|
||||
};
|
|
@ -45,9 +45,15 @@ properties:
|
|||
ranges: true
|
||||
|
||||
assigned-clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
assigned-clock-parents:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
assigned-clock-rates:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
typec-dir-gpios:
|
||||
|
@ -119,9 +125,10 @@ patternProperties:
|
|||
logic.
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 4
|
||||
description: Phandle to four clock nodes representing the inputs to
|
||||
refclk_dig
|
||||
description: Phandle to two (Torrent) or four (Sierra) clock nodes representing
|
||||
the inputs to refclk_dig
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
@ -203,7 +210,7 @@ examples:
|
|||
};
|
||||
|
||||
refclk-dig {
|
||||
clocks = <&k3_clks 292 11>, <&k3_clks 292 0>,
|
||||
clocks = <&k3_clks 292 11>, <&k3_clks 292 0>,
|
||||
<&dummy_cmn_refclk>, <&dummy_cmn_refclk1>;
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&wiz0_refclk_dig>;
|
||||
|
|
|
@ -27,43 +27,6 @@ omap_control_usb: omap-control-usb@4a002300 {
|
|||
reg-names = "otghs_control";
|
||||
};
|
||||
|
||||
OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
Should be "ti,dra7x-usb2" for the 1st instance of USB2 PHY on
|
||||
DRA7x
|
||||
Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
|
||||
in DRA7x
|
||||
Should be "ti,am654-usb2" for the USB2 PHYs on AM654.
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
- clock-names: should include:
|
||||
* "wkupclk" - wakeup clock.
|
||||
* "refclk" - reference clock (optional).
|
||||
|
||||
Deprecated properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
Recommended properies:
|
||||
- syscon-phy-power : phandle/offset pair. Phandle to the system control
|
||||
module and the register offset to power on/off the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
clocks = <&usb_phy_cm_clk32k>, <&usb_otg_ss_refclk960m>;
|
||||
clock-names = "wkupclk", "refclk";
|
||||
};
|
||||
|
||||
TI PIPE3 PHY
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -49,6 +49,17 @@ config PHY_XGENE
|
|||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
config USB_LGM_PHY
|
||||
tristate "INTEL Lightning Mountain USB PHY Driver"
|
||||
depends on USB_SUPPORT
|
||||
select USB_PHY
|
||||
select REGULATOR
|
||||
select REGULATOR_FIXED_VOLTAGE
|
||||
help
|
||||
Enable this to support Intel DWC3 PHY USB phy. This driver provides
|
||||
interface to interact with USB GEN-II and USB 3.x PHY that is part
|
||||
of the Intel network SOC.
|
||||
|
||||
source "drivers/phy/allwinner/Kconfig"
|
||||
source "drivers/phy/amlogic/Kconfig"
|
||||
source "drivers/phy/broadcom/Kconfig"
|
||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
|
|||
obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
|
||||
obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
|
||||
obj-y += allwinner/ \
|
||||
amlogic/ \
|
||||
broadcom/ \
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
|
@ -258,29 +259,24 @@ static struct mdio_driver bcm_ns_usb3_mdio_driver = {
|
|||
**************************************************/
|
||||
|
||||
static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
|
||||
u32 mask, u32 value, unsigned long timeout)
|
||||
u32 mask, u32 value, int usec)
|
||||
{
|
||||
unsigned long deadline = jiffies + timeout;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
val = readl(addr);
|
||||
if ((val & mask) == value)
|
||||
return 0;
|
||||
cpu_relax();
|
||||
udelay(10);
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
ret = readl_poll_timeout_atomic(addr, val, ((val & mask) == value),
|
||||
10, usec);
|
||||
if (ret)
|
||||
dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
|
||||
|
||||
dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
|
||||
|
||||
return -EBUSY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
|
||||
{
|
||||
return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL,
|
||||
0x0100, 0x0000,
|
||||
usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
|
||||
BCM_NS_USB3_MII_MNG_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int bcm_ns_usb3_platform_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -87,17 +88,11 @@ static const unsigned int usb_extcon_cable[] = {
|
|||
static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
|
||||
struct ns2_phy_driver *driver)
|
||||
{
|
||||
int retry = PLL_LOCK_RETRY;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
udelay(1);
|
||||
val = readl(driver->icfgdrd_regs + usb_reg);
|
||||
if (val & reg_mask)
|
||||
return 0;
|
||||
} while (--retry > 0);
|
||||
|
||||
return -EBUSY;
|
||||
return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg,
|
||||
val, (val & reg_mask), 1,
|
||||
PLL_LOCK_RETRY);
|
||||
}
|
||||
|
||||
static int ns2_drd_phy_init(struct phy *phy)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
@ -109,19 +110,15 @@ static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
|
|||
|
||||
static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)
|
||||
{
|
||||
int retry;
|
||||
u32 rd_data;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
retry = PLL_LOCK_RETRY_COUNT;
|
||||
do {
|
||||
rd_data = readl(addr);
|
||||
if (rd_data & bit)
|
||||
return 0;
|
||||
udelay(1);
|
||||
} while (--retry > 0);
|
||||
ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1,
|
||||
PLL_LOCK_RETRY_COUNT);
|
||||
if (ret)
|
||||
pr_err("%s: FAIL\n", __func__);
|
||||
|
||||
pr_err("%s: FAIL\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
|
||||
|
|
|
@ -97,7 +97,7 @@ struct cdns_reg_pairs {
|
|||
|
||||
struct cdns_salvo_data {
|
||||
u8 reg_offset_shift;
|
||||
struct cdns_reg_pairs *init_sequence_val;
|
||||
const struct cdns_reg_pairs *init_sequence_val;
|
||||
u8 init_sequence_length;
|
||||
};
|
||||
|
||||
|
@ -126,7 +126,7 @@ static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy,
|
|||
* Below bringup sequence pair are from Cadence PHY's User Guide
|
||||
* and NXP platform tuning results.
|
||||
*/
|
||||
static struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
|
||||
static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
|
||||
{0x0830, PHY_PMA_CMN_CTRL1},
|
||||
{0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL},
|
||||
{0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR},
|
||||
|
@ -217,7 +217,7 @@ static int cdns_salvo_phy_init(struct phy *phy)
|
|||
return ret;
|
||||
|
||||
for (i = 0; i < data->init_sequence_length; i++) {
|
||||
struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
|
||||
const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
|
||||
|
||||
cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val);
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ static int cdns_salvo_phy_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops cdns_salvo_phy_ops = {
|
||||
static const struct phy_ops cdns_salvo_phy_ops = {
|
||||
.init = cdns_salvo_phy_init,
|
||||
.power_on = cdns_salvo_phy_power_on,
|
||||
.power_off = cdns_salvo_phy_power_off,
|
||||
|
|
|
@ -172,10 +172,10 @@ struct cdns_sierra_data {
|
|||
u32 pcie_ln_regs;
|
||||
u32 usb_cmn_regs;
|
||||
u32 usb_ln_regs;
|
||||
struct cdns_reg_pairs *pcie_cmn_vals;
|
||||
struct cdns_reg_pairs *pcie_ln_vals;
|
||||
struct cdns_reg_pairs *usb_cmn_vals;
|
||||
struct cdns_reg_pairs *usb_ln_vals;
|
||||
const struct cdns_reg_pairs *pcie_cmn_vals;
|
||||
const struct cdns_reg_pairs *pcie_ln_vals;
|
||||
const struct cdns_reg_pairs *usb_cmn_vals;
|
||||
const struct cdns_reg_pairs *usb_ln_vals;
|
||||
};
|
||||
|
||||
struct cdns_regmap_cdb_context {
|
||||
|
@ -233,7 +233,7 @@ static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
|
|||
.reg_read = cdns_regmap_read, \
|
||||
}
|
||||
|
||||
static struct regmap_config cdns_sierra_lane_cdb_config[] = {
|
||||
static const struct regmap_config cdns_sierra_lane_cdb_config[] = {
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("0"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("1"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("2"),
|
||||
|
@ -252,7 +252,7 @@ static struct regmap_config cdns_sierra_lane_cdb_config[] = {
|
|||
SIERRA_LANE_CDB_REGMAP_CONF("15"),
|
||||
};
|
||||
|
||||
static struct regmap_config cdns_sierra_common_cdb_config = {
|
||||
static const struct regmap_config cdns_sierra_common_cdb_config = {
|
||||
.name = "sierra_common_cdb",
|
||||
.reg_stride = 1,
|
||||
.fast_io = true,
|
||||
|
@ -260,7 +260,7 @@ static struct regmap_config cdns_sierra_common_cdb_config = {
|
|||
.reg_read = cdns_regmap_read,
|
||||
};
|
||||
|
||||
static struct regmap_config cdns_sierra_phy_config_ctrl_config = {
|
||||
static const struct regmap_config cdns_sierra_phy_config_ctrl_config = {
|
||||
.name = "sierra_phy_config_ctrl",
|
||||
.reg_stride = 1,
|
||||
.fast_io = true,
|
||||
|
@ -274,7 +274,7 @@ static int cdns_sierra_phy_init(struct phy *gphy)
|
|||
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
|
||||
struct regmap *regmap;
|
||||
int i, j;
|
||||
struct cdns_reg_pairs *cmn_vals, *ln_vals;
|
||||
const struct cdns_reg_pairs *cmn_vals, *ln_vals;
|
||||
u32 num_cmn_regs, num_ln_regs;
|
||||
|
||||
/* Initialise the PHY registers, unless auto configured */
|
||||
|
@ -654,7 +654,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
|
||||
static const struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
|
||||
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
|
||||
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
|
||||
{0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
|
||||
|
@ -663,7 +663,7 @@ static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
|
|||
};
|
||||
|
||||
/* refclk100MHz_32b_PCIe_ln_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
|
||||
static const struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
|
||||
{0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
|
||||
{0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
|
||||
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
|
||||
|
@ -674,7 +674,7 @@ static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
|
|||
};
|
||||
|
||||
/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
|
||||
static const struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
|
||||
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
|
||||
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
|
||||
{0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
|
||||
|
@ -682,7 +682,7 @@ static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
|
|||
};
|
||||
|
||||
/* refclk100MHz_20b_USB_ln_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
|
||||
static const struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
|
||||
{0xFE0A, SIERRA_DET_STANDEC_A_PREG},
|
||||
{0x000F, SIERRA_DET_STANDEC_B_PREG},
|
||||
{0x55A5, SIERRA_DET_STANDEC_C_PREG},
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,15 +1,20 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Copyright (c) 2017 NXP. */
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define PHY_CTRL0 0x0
|
||||
#define PHY_CTRL0_REF_SSP_EN BIT(2)
|
||||
#define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
|
||||
#define PHY_CTRL0_FSEL_24M 0x2a
|
||||
|
||||
#define PHY_CTRL1 0x4
|
||||
#define PHY_CTRL1_RESET BIT(0)
|
||||
|
@ -20,6 +25,11 @@
|
|||
|
||||
#define PHY_CTRL2 0x8
|
||||
#define PHY_CTRL2_TXENABLEN0 BIT(8)
|
||||
#define PHY_CTRL2_OTG_DISABLE BIT(9)
|
||||
|
||||
#define PHY_CTRL6 0x18
|
||||
#define PHY_CTRL6_ALT_CLK_EN BIT(1)
|
||||
#define PHY_CTRL6_ALT_CLK_SEL BIT(0)
|
||||
|
||||
struct imx8mq_usb_phy {
|
||||
struct phy *phy;
|
||||
|
@ -54,6 +64,44 @@ static int imx8mq_usb_phy_init(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mp_usb_phy_init(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
u32 value;
|
||||
|
||||
/* USB3.0 PHY signal fsel for 24M ref */
|
||||
value = readl(imx_phy->base + PHY_CTRL0);
|
||||
value &= ~PHY_CTRL0_FSEL_MASK;
|
||||
value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);
|
||||
writel(value, imx_phy->base + PHY_CTRL0);
|
||||
|
||||
/* Disable alt_clk_en and use internal MPLL clocks */
|
||||
value = readl(imx_phy->base + PHY_CTRL6);
|
||||
value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN);
|
||||
writel(value, imx_phy->base + PHY_CTRL6);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL1);
|
||||
value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0);
|
||||
value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
|
||||
writel(value, imx_phy->base + PHY_CTRL1);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL0);
|
||||
value |= PHY_CTRL0_REF_SSP_EN;
|
||||
writel(value, imx_phy->base + PHY_CTRL0);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL2);
|
||||
value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE;
|
||||
writel(value, imx_phy->base + PHY_CTRL2);
|
||||
|
||||
udelay(10);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL1);
|
||||
value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
|
||||
writel(value, imx_phy->base + PHY_CTRL1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mq_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
|
@ -76,19 +124,36 @@ static int imx8mq_phy_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops imx8mq_usb_phy_ops = {
|
||||
static const struct phy_ops imx8mq_usb_phy_ops = {
|
||||
.init = imx8mq_usb_phy_init,
|
||||
.power_on = imx8mq_phy_power_on,
|
||||
.power_off = imx8mq_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy_ops imx8mp_usb_phy_ops = {
|
||||
.init = imx8mp_usb_phy_init,
|
||||
.power_on = imx8mq_phy_power_on,
|
||||
.power_off = imx8mq_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx8mq_usb_phy_of_match[] = {
|
||||
{.compatible = "fsl,imx8mq-usb-phy",
|
||||
.data = &imx8mq_usb_phy_ops,},
|
||||
{.compatible = "fsl,imx8mp-usb-phy",
|
||||
.data = &imx8mp_usb_phy_ops,},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
|
||||
|
||||
static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct imx8mq_usb_phy *imx_phy;
|
||||
struct resource *res;
|
||||
const struct phy_ops *phy_ops;
|
||||
|
||||
imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
|
||||
if (!imx_phy)
|
||||
|
@ -105,7 +170,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(imx_phy->base))
|
||||
return PTR_ERR(imx_phy->base);
|
||||
|
||||
imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops);
|
||||
phy_ops = of_device_get_match_data(dev);
|
||||
if (!phy_ops)
|
||||
return -EINVAL;
|
||||
|
||||
imx_phy->phy = devm_phy_create(dev, NULL, phy_ops);
|
||||
if (IS_ERR(imx_phy->phy))
|
||||
return PTR_ERR(imx_phy->phy);
|
||||
|
||||
|
@ -120,12 +189,6 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
|||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8mq_usb_phy_of_match[] = {
|
||||
{.compatible = "fsl,imx8mq-usb-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
|
||||
|
||||
static struct platform_driver imx8mq_usb_phy_driver = {
|
||||
.probe = imx8mq_usb_phy_probe,
|
||||
.driver = {
|
||||
|
|
|
@ -161,7 +161,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops hi3660_phy_ops = {
|
||||
static const struct phy_ops hi3660_phy_ops = {
|
||||
.init = hi3660_phy_init,
|
||||
.exit = hi3660_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Phy drivers for Intel Lightning Mountain(LGM) platform
|
||||
# Phy drivers for Intel platforms
|
||||
#
|
||||
config PHY_INTEL_COMBO
|
||||
bool "Intel ComboPHY driver"
|
||||
config PHY_INTEL_KEEMBAY_EMMC
|
||||
tristate "Intel Keem Bay EMMC PHY driver"
|
||||
depends on (OF && ARM64) || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Choose this option if you have an Intel Keem Bay SoC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called phy-keembay-emmc.ko.
|
||||
|
||||
config PHY_INTEL_LGM_COMBO
|
||||
bool "Intel Lightning Mountain ComboPHY driver"
|
||||
depends on X86 || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
select MFD_SYSCON
|
||||
|
@ -16,8 +28,8 @@ config PHY_INTEL_COMBO
|
|||
chipsets which provides PHYs for various controllers, EMAC,
|
||||
SATA and PCIe.
|
||||
|
||||
config PHY_INTEL_EMMC
|
||||
tristate "Intel EMMC PHY driver"
|
||||
config PHY_INTEL_LGM_EMMC
|
||||
tristate "Intel Lightning Mountain EMMC PHY driver"
|
||||
depends on X86 || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
help
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_INTEL_COMBO) += phy-intel-combo.o
|
||||
obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o
|
||||
obj-$(CONFIG_PHY_INTEL_KEEMBAY_EMMC) += phy-intel-keembay-emmc.o
|
||||
obj-$(CONFIG_PHY_INTEL_LGM_COMBO) += phy-intel-lgm-combo.o
|
||||
obj-$(CONFIG_PHY_INTEL_LGM_EMMC) += phy-intel-lgm-emmc.o
|
||||
|
|
307
drivers/phy/intel/phy-intel-keembay-emmc.c
Normal file
307
drivers/phy/intel/phy-intel-keembay-emmc.c
Normal file
|
@ -0,0 +1,307 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Intel Keem Bay eMMC PHY driver
|
||||
* Copyright (C) 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* eMMC/SD/SDIO core/phy configuration registers */
|
||||
#define PHY_CFG_0 0x24
|
||||
#define SEL_DLY_TXCLK_MASK BIT(29)
|
||||
#define OTAP_DLY_ENA_MASK BIT(27)
|
||||
#define OTAP_DLY_SEL_MASK GENMASK(26, 23)
|
||||
#define DLL_EN_MASK BIT(10)
|
||||
#define PWR_DOWN_MASK BIT(0)
|
||||
|
||||
#define PHY_CFG_2 0x2c
|
||||
#define SEL_FREQ_MASK GENMASK(12, 10)
|
||||
|
||||
#define PHY_STAT 0x40
|
||||
#define CAL_DONE_MASK BIT(6)
|
||||
#define IS_CALDONE(x) ((x) & CAL_DONE_MASK)
|
||||
#define DLL_RDY_MASK BIT(5)
|
||||
#define IS_DLLRDY(x) ((x) & DLL_RDY_MASK)
|
||||
|
||||
/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */
|
||||
#define FREQSEL_200M_170M 0x0
|
||||
#define FREQSEL_170M_140M 0x1
|
||||
#define FREQSEL_140M_110M 0x2
|
||||
#define FREQSEL_110M_80M 0x3
|
||||
#define FREQSEL_80M_50M 0x4
|
||||
|
||||
struct keembay_emmc_phy {
|
||||
struct regmap *syscfg;
|
||||
struct clk *emmcclk;
|
||||
};
|
||||
|
||||
static const struct regmap_config keembay_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
static int keembay_emmc_phy_power(struct phy *phy, bool on_off)
|
||||
{
|
||||
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
unsigned int caldone;
|
||||
unsigned int dllrdy;
|
||||
unsigned int freqsel;
|
||||
unsigned int mhz;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Keep phyctrl_pdb and phyctrl_endll low to allow
|
||||
* initialization of CALIO state M/C DFFs
|
||||
*/
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
|
||||
FIELD_PREP(PWR_DOWN_MASK, 0));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
|
||||
FIELD_PREP(DLL_EN_MASK, 0));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "turn off the dll failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Already finish power off above */
|
||||
if (!on_off)
|
||||
return 0;
|
||||
|
||||
mhz = DIV_ROUND_CLOSEST(clk_get_rate(priv->emmcclk), 1000000);
|
||||
if (mhz <= 200 && mhz >= 170)
|
||||
freqsel = FREQSEL_200M_170M;
|
||||
else if (mhz <= 170 && mhz >= 140)
|
||||
freqsel = FREQSEL_170M_140M;
|
||||
else if (mhz <= 140 && mhz >= 110)
|
||||
freqsel = FREQSEL_140M_110M;
|
||||
else if (mhz <= 110 && mhz >= 80)
|
||||
freqsel = FREQSEL_110M_80M;
|
||||
else if (mhz <= 80 && mhz >= 50)
|
||||
freqsel = FREQSEL_80M_50M;
|
||||
else
|
||||
freqsel = 0x0;
|
||||
|
||||
if (mhz < 50 || mhz > 200)
|
||||
dev_warn(&phy->dev, "Unsupported rate: %d MHz\n", mhz);
|
||||
|
||||
/*
|
||||
* According to the user manual, calpad calibration
|
||||
* cycle takes more than 2us without the minimal recommended
|
||||
* value, so we may need a little margin here
|
||||
*/
|
||||
udelay(5);
|
||||
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
|
||||
FIELD_PREP(PWR_DOWN_MASK, 1));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the user manual, it asks driver to wait 5us for
|
||||
* calpad busy trimming. However it is documented that this value is
|
||||
* PVT(A.K.A. process, voltage and temperature) relevant, so some
|
||||
* failure cases are found which indicates we should be more tolerant
|
||||
* to calpad busy trimming.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(priv->syscfg, PHY_STAT,
|
||||
caldone, IS_CALDONE(caldone),
|
||||
0, 50);
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the frequency of the DLL operation */
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_2, SEL_FREQ_MASK,
|
||||
FIELD_PREP(SEL_FREQ_MASK, freqsel));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Turn on the DLL */
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
|
||||
FIELD_PREP(DLL_EN_MASK, 1));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We turned on the DLL even though the rate was 0 because we the
|
||||
* clock might be turned on later. ...but we can't wait for the DLL
|
||||
* to lock when the rate is 0 because it will never lock with no
|
||||
* input clock.
|
||||
*
|
||||
* Technically we should be checking the lock later when the clock
|
||||
* is turned on, but for now we won't.
|
||||
*/
|
||||
if (mhz == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* After enabling analog DLL circuits docs say that we need 10.2 us if
|
||||
* our source clock is at 50 MHz and that lock time scales linearly
|
||||
* with clock speed. If we are powering on the PHY and the card clock
|
||||
* is super slow (like 100kHz) this could take as long as 5.1 ms as
|
||||
* per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
|
||||
* hopefully we won't be running at 100 kHz, but we should still make
|
||||
* sure we wait long enough.
|
||||
*
|
||||
* NOTE: There appear to be corner cases where the DLL seems to take
|
||||
* extra long to lock for reasons that aren't understood. In some
|
||||
* extreme cases we've seen it take up to over 10ms (!). We'll be
|
||||
* generous and give it 50ms.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(priv->syscfg, PHY_STAT,
|
||||
dllrdy, IS_DLLRDY(dllrdy),
|
||||
0, 50 * USEC_PER_MSEC);
|
||||
if (ret)
|
||||
dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int keembay_emmc_phy_init(struct phy *phy)
|
||||
{
|
||||
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
|
||||
/*
|
||||
* We purposely get the clock here and not in probe to avoid the
|
||||
* circular dependency problem. We expect:
|
||||
* - PHY driver to probe
|
||||
* - SDHCI driver to start probe
|
||||
* - SDHCI driver to register it's clock
|
||||
* - SDHCI driver to get the PHY
|
||||
* - SDHCI driver to init the PHY
|
||||
*
|
||||
* The clock is optional, so upon any error just return it like
|
||||
* any other error to user.
|
||||
*/
|
||||
priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
|
||||
|
||||
return PTR_ERR_OR_ZERO(priv->emmcclk);
|
||||
}
|
||||
|
||||
static int keembay_emmc_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
|
||||
clk_put(priv->emmcclk);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int keembay_emmc_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
/* Delay chain based txclk: enable */
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
|
||||
FIELD_PREP(SEL_DLY_TXCLK_MASK, 1));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "ERROR: delay chain txclk set: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Output tap delay: enable */
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_ENA_MASK,
|
||||
FIELD_PREP(OTAP_DLY_ENA_MASK, 1));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "ERROR: output tap delay set: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Output tap delay */
|
||||
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_SEL_MASK,
|
||||
FIELD_PREP(OTAP_DLY_SEL_MASK, 2));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "ERROR: output tap delay select: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Power up eMMC phy analog blocks */
|
||||
return keembay_emmc_phy_power(phy, true);
|
||||
}
|
||||
|
||||
static int keembay_emmc_phy_power_off(struct phy *phy)
|
||||
{
|
||||
/* Power down eMMC phy analog blocks */
|
||||
return keembay_emmc_phy_power(phy, false);
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.init = keembay_emmc_phy_init,
|
||||
.exit = keembay_emmc_phy_exit,
|
||||
.power_on = keembay_emmc_phy_power_on,
|
||||
.power_off = keembay_emmc_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int keembay_emmc_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct keembay_emmc_phy *priv;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
void __iomem *base;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->syscfg = devm_regmap_init_mmio(dev, base, &keembay_regmap_config);
|
||||
if (IS_ERR(priv->syscfg))
|
||||
return PTR_ERR(priv->syscfg);
|
||||
|
||||
generic_phy = devm_phy_create(dev, np, &ops);
|
||||
if (IS_ERR(generic_phy))
|
||||
return dev_err_probe(dev, PTR_ERR(generic_phy),
|
||||
"failed to create PHY\n");
|
||||
|
||||
phy_set_drvdata(generic_phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id keembay_emmc_phy_dt_ids[] = {
|
||||
{ .compatible = "intel,keembay-emmc-phy" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, keembay_emmc_phy_dt_ids);
|
||||
|
||||
static struct platform_driver keembay_emmc_phy_driver = {
|
||||
.probe = keembay_emmc_phy_probe,
|
||||
.driver = {
|
||||
.name = "keembay-emmc-phy",
|
||||
.of_match_table = keembay_emmc_phy_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(keembay_emmc_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Keem Bay eMMC PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -141,7 +141,7 @@ static int ltq_rcu_usb2_phy_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ltq_rcu_usb2_phy_ops = {
|
||||
static const struct phy_ops ltq_rcu_usb2_phy_ops = {
|
||||
.init = ltq_rcu_usb2_phy_init,
|
||||
.power_on = ltq_rcu_usb2_phy_power_on,
|
||||
.power_off = ltq_rcu_usb2_phy_power_off,
|
||||
|
|
|
@ -349,7 +349,7 @@ static int ltq_vrx200_pcie_phy_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ltq_vrx200_pcie_phy_ops = {
|
||||
static const struct phy_ops ltq_vrx200_pcie_phy_ops = {
|
||||
.init = ltq_vrx200_pcie_phy_init,
|
||||
.exit = ltq_vrx200_pcie_phy_exit,
|
||||
.power_on = ltq_vrx200_pcie_phy_power_on,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -44,15 +45,12 @@ struct mv_hsic_phy {
|
|||
struct clk *clk;
|
||||
};
|
||||
|
||||
static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
|
||||
static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
|
||||
{
|
||||
timeout += jiffies;
|
||||
while (time_is_after_eq_jiffies(timeout)) {
|
||||
if ((readl(reg) & mask) == mask)
|
||||
return true;
|
||||
msleep(1);
|
||||
}
|
||||
return false;
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(reg, val, ((val & mask) == mask),
|
||||
1000, 1000 * ms);
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_init(struct phy *phy)
|
||||
|
@ -60,6 +58,7 @@ static int mv_hsic_phy_init(struct phy *phy)
|
|||
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
|
||||
struct platform_device *pdev = mv_phy->pdev;
|
||||
void __iomem *base = mv_phy->base;
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(mv_phy->clk);
|
||||
|
||||
|
@ -75,14 +74,14 @@ static int mv_hsic_phy_init(struct phy *phy)
|
|||
base + PHY_28NM_HSIC_PLL_CTRL2);
|
||||
|
||||
/* Make sure PHY PLL is locked */
|
||||
if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
|
||||
PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) {
|
||||
ret = wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
|
||||
PHY_28NM_HSIC_H2S_PLL_LOCK, 100);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
|
||||
clk_disable_unprepare(mv_phy->clk);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_power_on(struct phy *phy)
|
||||
|
@ -91,6 +90,7 @@ static int mv_hsic_phy_power_on(struct phy *phy)
|
|||
struct platform_device *pdev = mv_phy->pdev;
|
||||
void __iomem *base = mv_phy->base;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
reg = readl(base + PHY_28NM_HSIC_CTRL);
|
||||
/* Avoid SE0 state when resume for some device will take it as reset */
|
||||
|
@ -108,20 +108,20 @@ static int mv_hsic_phy_power_on(struct phy *phy)
|
|||
*/
|
||||
|
||||
/* Make sure PHY Calibration is ready */
|
||||
if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
|
||||
PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) {
|
||||
ret = wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
|
||||
PHY_28NM_HSIC_H2S_IMPCAL_DONE, 100);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
|
||||
return -ETIMEDOUT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Waiting for HSIC connect int*/
|
||||
if (!wait_for_reg(base + PHY_28NM_HSIC_INT,
|
||||
PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) {
|
||||
ret = wait_for_reg(base + PHY_28NM_HSIC_INT,
|
||||
PHY_28NM_HSIC_CONNECT_INT, 200);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_power_off(struct phy *phy)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -138,15 +139,12 @@ struct mv_usb2_phy {
|
|||
struct clk *clk;
|
||||
};
|
||||
|
||||
static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
|
||||
static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
|
||||
{
|
||||
timeout += jiffies;
|
||||
while (time_is_after_eq_jiffies(timeout)) {
|
||||
if ((readl(reg) & mask) == mask)
|
||||
return true;
|
||||
msleep(1);
|
||||
}
|
||||
return false;
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(reg, val, ((val & mask) == mask),
|
||||
1000, 1000 * ms);
|
||||
}
|
||||
|
||||
static int mv_usb2_phy_28nm_init(struct phy *phy)
|
||||
|
@ -208,24 +206,23 @@ static int mv_usb2_phy_28nm_init(struct phy *phy)
|
|||
*/
|
||||
|
||||
/* Make sure PHY Calibration is ready */
|
||||
if (!wait_for_reg(base + PHY_28NM_CAL_REG,
|
||||
PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
|
||||
HZ / 10)) {
|
||||
ret = wait_for_reg(base + PHY_28NM_CAL_REG,
|
||||
PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
|
||||
100);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS.");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clk;
|
||||
}
|
||||
if (!wait_for_reg(base + PHY_28NM_RX_REG1,
|
||||
PHY_28NM_RX_SQCAL_DONE, HZ / 10)) {
|
||||
ret = wait_for_reg(base + PHY_28NM_RX_REG1,
|
||||
PHY_28NM_RX_SQCAL_DONE, 100);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS.");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clk;
|
||||
}
|
||||
/* Make sure PHY PLL is ready */
|
||||
if (!wait_for_reg(base + PHY_28NM_PLL_REG0,
|
||||
PHY_28NM_PLL_READY, HZ / 10)) {
|
||||
ret = wait_for_reg(base + PHY_28NM_PLL_REG0, PHY_28NM_PLL_READY, 100);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev, "PLL_READY not set after 100mS.");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
|
|
284
drivers/phy/phy-lgm-usb.c
Normal file
284
drivers/phy/phy-lgm-usb.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel LGM USB PHY driver
|
||||
*
|
||||
* Copyright (C) 2020 Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define CTRL1_OFFSET 0x14
|
||||
#define SRAM_EXT_LD_DONE BIT(25)
|
||||
#define SRAM_INIT_DONE BIT(26)
|
||||
|
||||
#define TCPC_OFFSET 0x1014
|
||||
#define TCPC_MUX_CTL GENMASK(1, 0)
|
||||
#define MUX_NC 0
|
||||
#define MUX_USB 1
|
||||
#define MUX_DP 2
|
||||
#define MUX_USBDP 3
|
||||
#define TCPC_FLIPPED BIT(2)
|
||||
#define TCPC_LOW_POWER_EN BIT(3)
|
||||
#define TCPC_VALID BIT(4)
|
||||
#define TCPC_CONN \
|
||||
(TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_USB))
|
||||
#define TCPC_DISCONN \
|
||||
(TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_NC) | TCPC_LOW_POWER_EN)
|
||||
|
||||
static const char *const PHY_RESETS[] = { "phy31", "phy", };
|
||||
static const char *const CTL_RESETS[] = { "apb", "ctrl", };
|
||||
|
||||
struct tca_apb {
|
||||
struct reset_control *resets[ARRAY_SIZE(PHY_RESETS)];
|
||||
struct regulator *vbus;
|
||||
struct work_struct wk;
|
||||
struct usb_phy phy;
|
||||
|
||||
bool regulator_enabled;
|
||||
bool phy_initialized;
|
||||
bool connected;
|
||||
};
|
||||
|
||||
static int get_flipped(struct tca_apb *ta, bool *flipped)
|
||||
{
|
||||
union extcon_property_value property;
|
||||
int ret;
|
||||
|
||||
ret = extcon_get_property(ta->phy.edev, EXTCON_USB_HOST,
|
||||
EXTCON_PROP_USB_TYPEC_POLARITY, &property);
|
||||
if (ret) {
|
||||
dev_err(ta->phy.dev, "no polarity property from extcon\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*flipped = property.intval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_init(struct usb_phy *phy)
|
||||
{
|
||||
struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
|
||||
void __iomem *ctrl1 = phy->io_priv + CTRL1_OFFSET;
|
||||
int val, ret, i;
|
||||
|
||||
if (ta->phy_initialized)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
|
||||
reset_control_deassert(ta->resets[i]);
|
||||
|
||||
ret = readl_poll_timeout(ctrl1, val, val & SRAM_INIT_DONE, 10, 10 * 1000);
|
||||
if (ret) {
|
||||
dev_err(ta->phy.dev, "SRAM init failed, 0x%x\n", val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(readl(ctrl1) | SRAM_EXT_LD_DONE, ctrl1);
|
||||
|
||||
ta->phy_initialized = true;
|
||||
if (!ta->phy.edev) {
|
||||
writel(TCPC_CONN, ta->phy.io_priv + TCPC_OFFSET);
|
||||
return phy->set_vbus(phy, true);
|
||||
}
|
||||
|
||||
schedule_work(&ta->wk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void phy_shutdown(struct usb_phy *phy)
|
||||
{
|
||||
struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
|
||||
int i;
|
||||
|
||||
if (!ta->phy_initialized)
|
||||
return;
|
||||
|
||||
ta->phy_initialized = false;
|
||||
flush_work(&ta->wk);
|
||||
ta->phy.set_vbus(&ta->phy, false);
|
||||
|
||||
ta->connected = false;
|
||||
writel(TCPC_DISCONN, ta->phy.io_priv + TCPC_OFFSET);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
|
||||
reset_control_assert(ta->resets[i]);
|
||||
}
|
||||
|
||||
static int phy_set_vbus(struct usb_phy *phy, int on)
|
||||
{
|
||||
struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
|
||||
int ret;
|
||||
|
||||
if (!!on == ta->regulator_enabled)
|
||||
return 0;
|
||||
|
||||
if (on)
|
||||
ret = regulator_enable(ta->vbus);
|
||||
else
|
||||
ret = regulator_disable(ta->vbus);
|
||||
|
||||
if (!ret)
|
||||
ta->regulator_enabled = on;
|
||||
|
||||
dev_dbg(ta->phy.dev, "set vbus: %d\n", on);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tca_work(struct work_struct *work)
|
||||
{
|
||||
struct tca_apb *ta = container_of(work, struct tca_apb, wk);
|
||||
bool connected;
|
||||
bool flipped = false;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = get_flipped(ta, &flipped);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
connected = extcon_get_state(ta->phy.edev, EXTCON_USB_HOST);
|
||||
if (connected == ta->connected)
|
||||
return;
|
||||
|
||||
ta->connected = connected;
|
||||
if (connected) {
|
||||
val = TCPC_CONN;
|
||||
if (flipped)
|
||||
val |= TCPC_FLIPPED;
|
||||
dev_dbg(ta->phy.dev, "connected%s\n", flipped ? " flipped" : "");
|
||||
} else {
|
||||
val = TCPC_DISCONN;
|
||||
dev_dbg(ta->phy.dev, "disconnected\n");
|
||||
}
|
||||
|
||||
writel(val, ta->phy.io_priv + TCPC_OFFSET);
|
||||
|
||||
ret = ta->phy.set_vbus(&ta->phy, connected);
|
||||
if (ret)
|
||||
dev_err(ta->phy.dev, "failed to set VBUS\n");
|
||||
}
|
||||
|
||||
static int id_notifier(struct notifier_block *nb, unsigned long event, void *ptr)
|
||||
{
|
||||
struct tca_apb *ta = container_of(nb, struct tca_apb, phy.id_nb);
|
||||
|
||||
if (ta->phy_initialized)
|
||||
schedule_work(&ta->wk);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int vbus_notifier(struct notifier_block *nb, unsigned long evnt, void *ptr)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct reset_control *resets[ARRAY_SIZE(CTL_RESETS)];
|
||||
struct device *dev = &pdev->dev;
|
||||
struct usb_phy *phy;
|
||||
struct tca_apb *ta;
|
||||
int i;
|
||||
|
||||
ta = devm_kzalloc(dev, sizeof(*ta), GFP_KERNEL);
|
||||
if (!ta)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ta);
|
||||
INIT_WORK(&ta->wk, tca_work);
|
||||
|
||||
phy = &ta->phy;
|
||||
phy->dev = dev;
|
||||
phy->label = dev_name(dev);
|
||||
phy->type = USB_PHY_TYPE_USB3;
|
||||
phy->init = phy_init;
|
||||
phy->shutdown = phy_shutdown;
|
||||
phy->set_vbus = phy_set_vbus;
|
||||
phy->id_nb.notifier_call = id_notifier;
|
||||
phy->vbus_nb.notifier_call = vbus_notifier;
|
||||
|
||||
phy->io_priv = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(phy->io_priv))
|
||||
return PTR_ERR(phy->io_priv);
|
||||
|
||||
ta->vbus = devm_regulator_get(dev, "vbus");
|
||||
if (IS_ERR(ta->vbus))
|
||||
return PTR_ERR(ta->vbus);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) {
|
||||
resets[i] = devm_reset_control_get_exclusive(dev, CTL_RESETS[i]);
|
||||
if (IS_ERR(resets[i])) {
|
||||
dev_err(dev, "%s reset not found\n", CTL_RESETS[i]);
|
||||
return PTR_ERR(resets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) {
|
||||
ta->resets[i] = devm_reset_control_get_exclusive(dev, PHY_RESETS[i]);
|
||||
if (IS_ERR(ta->resets[i])) {
|
||||
dev_err(dev, "%s reset not found\n", PHY_RESETS[i]);
|
||||
return PTR_ERR(ta->resets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
|
||||
reset_control_assert(resets[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
|
||||
reset_control_assert(ta->resets[i]);
|
||||
/*
|
||||
* Out-of-band reset of the controller after PHY reset will cause
|
||||
* controller malfunctioning, so we should use in-band controller
|
||||
* reset only and leave the controller de-asserted here.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
|
||||
reset_control_deassert(resets[i]);
|
||||
|
||||
/* Need to wait at least 20us after de-assert the controller */
|
||||
usleep_range(20, 100);
|
||||
|
||||
return usb_add_phy_dev(phy);
|
||||
}
|
||||
|
||||
static int phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tca_apb *ta = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_phy(&ta->phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id intel_usb_phy_dt_ids[] = {
|
||||
{ .compatible = "intel,lgm-usb-phy" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, intel_usb_phy_dt_ids);
|
||||
|
||||
static struct platform_driver lgm_phy_driver = {
|
||||
.driver = {
|
||||
.name = "lgm-usb-phy",
|
||||
.of_match_table = intel_usb_phy_dt_ids,
|
||||
},
|
||||
.probe = phy_probe,
|
||||
.remove = phy_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(lgm_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Intel LGM USB PHY driver");
|
||||
MODULE_AUTHOR("Li Yin <yin1.li@intel.com>");
|
||||
MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -72,18 +73,12 @@ struct qcom_apq8064_sata_phy {
|
|||
};
|
||||
|
||||
/* Helper function to do poll and timeout */
|
||||
static int read_poll_timeout(void __iomem *addr, u32 mask)
|
||||
static int poll_timeout(void __iomem *addr, u32 mask)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
|
||||
u32 val;
|
||||
|
||||
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;
|
||||
return readl_relaxed_poll_timeout(addr, val, (val & mask),
|
||||
DELAY_INTERVAL_US, TIMEOUT_MS * 1000);
|
||||
}
|
||||
|
||||
static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
|
||||
|
@ -137,21 +132,21 @@ static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
|
|||
writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
|
||||
|
||||
/* PLL Lock wait */
|
||||
ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
|
||||
ret = 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);
|
||||
ret = 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);
|
||||
ret = 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;
|
||||
|
|
|
@ -48,7 +48,7 @@ static int ipq4019_ss_phy_power_on(struct phy *_phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ipq4019_usb_ss_phy_ops = {
|
||||
static const struct phy_ops ipq4019_usb_ss_phy_ops = {
|
||||
.power_on = ipq4019_ss_phy_power_on,
|
||||
.power_off = ipq4019_ss_phy_power_off,
|
||||
};
|
||||
|
@ -80,7 +80,7 @@ static int ipq4019_hs_phy_power_on(struct phy *_phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ipq4019_usb_hs_phy_ops = {
|
||||
static const struct phy_ops ipq4019_usb_hs_phy_ops = {
|
||||
.power_on = ipq4019_hs_phy_power_on,
|
||||
.power_off = ipq4019_hs_phy_power_off,
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -137,6 +137,9 @@
|
|||
#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
|
||||
|
||||
/* Only for QMP V3 PHY - QSERDES COM registers */
|
||||
#define QSERDES_V3_COM_ATB_SEL1 0x000
|
||||
#define QSERDES_V3_COM_ATB_SEL2 0x004
|
||||
#define QSERDES_V3_COM_FREQ_UPDATE 0x008
|
||||
#define QSERDES_V3_COM_BG_TIMER 0x00c
|
||||
#define QSERDES_V3_COM_SSC_EN_CENTER 0x010
|
||||
#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014
|
||||
|
@ -146,6 +149,13 @@
|
|||
#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024
|
||||
#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028
|
||||
#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034
|
||||
# define QSERDES_V3_COM_BIAS_EN 0x0001
|
||||
# define QSERDES_V3_COM_BIAS_EN_MUX 0x0002
|
||||
# define QSERDES_V3_COM_CLKBUF_R_EN 0x0004
|
||||
# define QSERDES_V3_COM_CLKBUF_L_EN 0x0008
|
||||
# define QSERDES_V3_COM_EN_SYSCLK_TX_SEL 0x0010
|
||||
# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_L 0x0020
|
||||
# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_R 0x0040
|
||||
#define QSERDES_V3_COM_CLK_ENABLE1 0x038
|
||||
#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c
|
||||
#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040
|
||||
|
@ -207,12 +217,36 @@
|
|||
#define QSERDES_V3_COM_CMN_MODE 0x184
|
||||
|
||||
/* Only for QMP V3 PHY - TX registers */
|
||||
#define QSERDES_V3_TX_BIST_MODE_LANENO 0x000
|
||||
#define QSERDES_V3_TX_CLKBUF_ENABLE 0x008
|
||||
#define QSERDES_V3_TX_TX_EMP_POST1_LVL 0x00c
|
||||
# define DP_PHY_TXn_TX_EMP_POST1_LVL_MASK 0x001f
|
||||
# define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN 0x0020
|
||||
|
||||
#define QSERDES_V3_TX_TX_DRV_LVL 0x01c
|
||||
# define DP_PHY_TXn_TX_DRV_LVL_MASK 0x001f
|
||||
# define DP_PHY_TXn_TX_DRV_LVL_MUX_EN 0x0020
|
||||
|
||||
#define QSERDES_V3_TX_RESET_TSYNC_EN 0x024
|
||||
#define QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN 0x028
|
||||
|
||||
#define QSERDES_V3_TX_TX_BAND 0x02c
|
||||
#define QSERDES_V3_TX_SLEW_CNTL 0x030
|
||||
#define QSERDES_V3_TX_INTERFACE_SELECT 0x034
|
||||
#define QSERDES_V3_TX_RES_CODE_LANE_TX 0x03c
|
||||
#define QSERDES_V3_TX_RES_CODE_LANE_RX 0x040
|
||||
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
|
||||
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048
|
||||
#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058
|
||||
#define QSERDES_V3_TX_TRANSCEIVER_BIAS_EN 0x05c
|
||||
#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060
|
||||
#define QSERDES_V3_TX_TX_POL_INV 0x064
|
||||
#define QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN 0x068
|
||||
#define QSERDES_V3_TX_LANE_MODE_1 0x08c
|
||||
#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
|
||||
#define QSERDES_V3_TX_TRAN_DRVR_EMP_EN 0x0c0
|
||||
#define QSERDES_V3_TX_TX_INTERFACE_MODE 0x0c4
|
||||
#define QSERDES_V3_TX_VMODE_CTRL1 0x0f0
|
||||
|
||||
/* Only for QMP V3 PHY - RX registers */
|
||||
#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008
|
||||
|
@ -315,6 +349,52 @@
|
|||
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
|
||||
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
|
||||
|
||||
/* Only for QMP V3 PHY - DP PHY registers */
|
||||
#define QSERDES_V3_DP_PHY_REVISION_ID0 0x000
|
||||
#define QSERDES_V3_DP_PHY_REVISION_ID1 0x004
|
||||
#define QSERDES_V3_DP_PHY_REVISION_ID2 0x008
|
||||
#define QSERDES_V3_DP_PHY_REVISION_ID3 0x00c
|
||||
#define QSERDES_V3_DP_PHY_CFG 0x010
|
||||
#define QSERDES_V3_DP_PHY_PD_CTL 0x018
|
||||
# define DP_PHY_PD_CTL_PWRDN 0x001
|
||||
# define DP_PHY_PD_CTL_PSR_PWRDN 0x002
|
||||
# define DP_PHY_PD_CTL_AUX_PWRDN 0x004
|
||||
# define DP_PHY_PD_CTL_LANE_0_1_PWRDN 0x008
|
||||
# define DP_PHY_PD_CTL_LANE_2_3_PWRDN 0x010
|
||||
# define DP_PHY_PD_CTL_PLL_PWRDN 0x020
|
||||
# define DP_PHY_PD_CTL_DP_CLAMP_EN 0x040
|
||||
#define QSERDES_V3_DP_PHY_MODE 0x01c
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG0 0x020
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG1 0x024
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG2 0x028
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG3 0x02c
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG4 0x030
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG5 0x034
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG6 0x038
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG7 0x03c
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG8 0x040
|
||||
#define QSERDES_V3_DP_PHY_AUX_CFG9 0x044
|
||||
|
||||
#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK 0x048
|
||||
# define PHY_AUX_STOP_ERR_MASK 0x01
|
||||
# define PHY_AUX_DEC_ERR_MASK 0x02
|
||||
# define PHY_AUX_SYNC_ERR_MASK 0x04
|
||||
# define PHY_AUX_ALIGN_ERR_MASK 0x08
|
||||
# define PHY_AUX_REQ_ERR_MASK 0x10
|
||||
|
||||
#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_CLEAR 0x04c
|
||||
#define QSERDES_V3_DP_PHY_AUX_BIST_CFG 0x050
|
||||
|
||||
#define QSERDES_V3_DP_PHY_VCO_DIV 0x064
|
||||
#define QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL 0x06c
|
||||
#define QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL 0x088
|
||||
|
||||
#define QSERDES_V3_DP_PHY_SPARE0 0x0ac
|
||||
#define DP_PHY_SPARE0_MASK 0x0f
|
||||
#define DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT 0x04(0x0004)
|
||||
|
||||
#define QSERDES_V3_DP_PHY_STATUS 0x0c0
|
||||
|
||||
/* Only for QMP V4 PHY - QSERDES COM registers */
|
||||
#define QSERDES_V4_COM_SSC_EN_CENTER 0x010
|
||||
#define QSERDES_V4_COM_SSC_PER1 0x01c
|
||||
|
|
|
@ -142,7 +142,7 @@ static int ralink_usb_phy_power_off(struct phy *_phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ralink_usb_phy_ops = {
|
||||
static const struct phy_ops ralink_usb_phy_ops = {
|
||||
.power_on = ralink_usb_phy_power_on,
|
||||
.power_off = ralink_usb_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -9,6 +9,18 @@ config PHY_ROCKCHIP_DP
|
|||
help
|
||||
Enable this to support the Rockchip Display Port PHY.
|
||||
|
||||
config PHY_ROCKCHIP_DPHY_RX0
|
||||
tristate "Rockchip MIPI Synopsys DPHY RX0 driver"
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
select GENERIC_PHY_MIPI_DPHY
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Rockchip MIPI Synopsys DPHY RX0
|
||||
associated to the Rockchip ISP module present in RK3399 SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called phy-rockchip-dphy-rx0.
|
||||
|
||||
config PHY_ROCKCHIP_EMMC
|
||||
tristate "Rockchip EMMC PHY Driver"
|
||||
depends on ARCH_ROCKCHIP && OF
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -556,41 +557,25 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
|
|||
static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
|
||||
u32 val, u32 cmd)
|
||||
{
|
||||
u32 usec = 100;
|
||||
unsigned int result;
|
||||
int err;
|
||||
|
||||
writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
|
||||
|
||||
do {
|
||||
result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
|
||||
if (result & PHYREG1_CR_ACK)
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
} while (usec-- > 0);
|
||||
|
||||
if (!usec) {
|
||||
dev_err(phy_drd->dev,
|
||||
"CRPORT handshake timeout1 (0x%08x)\n", val);
|
||||
return -ETIME;
|
||||
err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
|
||||
result, (result & PHYREG1_CR_ACK), 1, 100);
|
||||
if (err == -ETIMEDOUT) {
|
||||
dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val);
|
||||
return err;
|
||||
}
|
||||
|
||||
usec = 100;
|
||||
|
||||
writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
|
||||
|
||||
do {
|
||||
result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
|
||||
if (!(result & PHYREG1_CR_ACK))
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
} while (usec-- > 0);
|
||||
|
||||
if (!usec) {
|
||||
dev_err(phy_drd->dev,
|
||||
"CRPORT handshake timeout2 (0x%08x)\n", val);
|
||||
return -ETIME;
|
||||
err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
|
||||
result, !(result & PHYREG1_CR_ACK), 1, 100);
|
||||
if (err == -ETIMEDOUT) {
|
||||
dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -268,7 +268,7 @@ static int samsung_ufs_phy_exit(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops samsung_ufs_phy_ops = {
|
||||
static const struct phy_ops samsung_ufs_phy_ops = {
|
||||
.init = samsung_ufs_phy_init,
|
||||
.exit = samsung_ufs_phy_exit,
|
||||
.power_on = samsung_ufs_phy_power_on,
|
||||
|
|
|
@ -34,3 +34,13 @@ config PHY_UNIPHIER_PCIE
|
|||
help
|
||||
Enable this to support PHY implemented in PCIe controller
|
||||
on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs.
|
||||
|
||||
config PHY_UNIPHIER_AHCI
|
||||
tristate "UniPhier AHCI PHY driver"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
default SATA_AHCI_PLATFORM
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support PHY implemented in AHCI controller
|
||||
on UniPhier SoCs. This driver supports PXs2 and PXs3 SoCs.
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
obj-$(CONFIG_PHY_UNIPHIER_USB2) += phy-uniphier-usb2.o
|
||||
obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3hs.o phy-uniphier-usb3ss.o
|
||||
obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o
|
||||
obj-$(CONFIG_PHY_UNIPHIER_AHCI) += phy-uniphier-ahci.o
|
||||
|
|
321
drivers/phy/socionext/phy-uniphier-ahci.c
Normal file
321
drivers/phy/socionext/phy-uniphier-ahci.c
Normal file
|
@ -0,0 +1,321 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* phy-uniphier-ahci.c - PHY driver for UniPhier AHCI controller
|
||||
* Copyright 2016-2020, Socionext Inc.
|
||||
* Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
struct uniphier_ahciphy_priv {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk *clk, *clk_parent;
|
||||
struct reset_control *rst, *rst_parent;
|
||||
const struct uniphier_ahciphy_soc_data *data;
|
||||
};
|
||||
|
||||
struct uniphier_ahciphy_soc_data {
|
||||
int (*init)(struct uniphier_ahciphy_priv *priv);
|
||||
int (*power_on)(struct uniphier_ahciphy_priv *priv);
|
||||
int (*power_off)(struct uniphier_ahciphy_priv *priv);
|
||||
bool is_ready_high;
|
||||
bool is_phy_clk;
|
||||
};
|
||||
|
||||
/* for PXs2/PXs3 */
|
||||
#define CKCTRL 0x0
|
||||
#define CKCTRL_P0_READY BIT(15)
|
||||
#define CKCTRL_P0_RESET BIT(10)
|
||||
#define CKCTRL_REF_SSP_EN BIT(9)
|
||||
#define TXCTRL0 0x4
|
||||
#define TXCTRL0_AMP_G3_MASK GENMASK(22, 16)
|
||||
#define TXCTRL0_AMP_G2_MASK GENMASK(14, 8)
|
||||
#define TXCTRL0_AMP_G1_MASK GENMASK(6, 0)
|
||||
#define TXCTRL1 0x8
|
||||
#define TXCTRL1_DEEMPH_G3_MASK GENMASK(21, 16)
|
||||
#define TXCTRL1_DEEMPH_G2_MASK GENMASK(13, 8)
|
||||
#define TXCTRL1_DEEMPH_G1_MASK GENMASK(5, 0)
|
||||
#define RXCTRL 0xc
|
||||
#define RXCTRL_LOS_LVL_MASK GENMASK(20, 16)
|
||||
#define RXCTRL_LOS_BIAS_MASK GENMASK(10, 8)
|
||||
#define RXCTRL_RX_EQ_MASK GENMASK(2, 0)
|
||||
|
||||
static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv,
|
||||
bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(priv->base + CKCTRL);
|
||||
|
||||
if (enable) {
|
||||
val |= CKCTRL_REF_SSP_EN;
|
||||
writel(val, priv->base + CKCTRL);
|
||||
val &= ~CKCTRL_P0_RESET;
|
||||
writel(val, priv->base + CKCTRL);
|
||||
} else {
|
||||
val |= CKCTRL_P0_RESET;
|
||||
writel(val, priv->base + CKCTRL);
|
||||
val &= ~CKCTRL_REF_SSP_EN;
|
||||
writel(val, priv->base + CKCTRL);
|
||||
}
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_pxs2_power_on(struct uniphier_ahciphy_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
uniphier_ahciphy_pxs2_enable(priv, true);
|
||||
|
||||
/* wait until PLL is ready */
|
||||
if (priv->data->is_ready_high)
|
||||
ret = readl_poll_timeout(priv->base + CKCTRL, val,
|
||||
(val & CKCTRL_P0_READY), 200, 400);
|
||||
else
|
||||
ret = readl_poll_timeout(priv->base + CKCTRL, val,
|
||||
!(val & CKCTRL_P0_READY), 200, 400);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to check whether PHY PLL is ready\n");
|
||||
uniphier_ahciphy_pxs2_enable(priv, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_pxs2_power_off(struct uniphier_ahciphy_priv *priv)
|
||||
{
|
||||
uniphier_ahciphy_pxs2_enable(priv, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_pxs3_init(struct uniphier_ahciphy_priv *priv)
|
||||
{
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
/* setup port parameter */
|
||||
val = readl(priv->base + TXCTRL0);
|
||||
val &= ~TXCTRL0_AMP_G3_MASK;
|
||||
val |= FIELD_PREP(TXCTRL0_AMP_G3_MASK, 0x73);
|
||||
val &= ~TXCTRL0_AMP_G2_MASK;
|
||||
val |= FIELD_PREP(TXCTRL0_AMP_G2_MASK, 0x46);
|
||||
val &= ~TXCTRL0_AMP_G1_MASK;
|
||||
val |= FIELD_PREP(TXCTRL0_AMP_G1_MASK, 0x42);
|
||||
writel(val, priv->base + TXCTRL0);
|
||||
|
||||
val = readl(priv->base + TXCTRL1);
|
||||
val &= ~TXCTRL1_DEEMPH_G3_MASK;
|
||||
val |= FIELD_PREP(TXCTRL1_DEEMPH_G3_MASK, 0x23);
|
||||
val &= ~TXCTRL1_DEEMPH_G2_MASK;
|
||||
val |= FIELD_PREP(TXCTRL1_DEEMPH_G2_MASK, 0x05);
|
||||
val &= ~TXCTRL1_DEEMPH_G1_MASK;
|
||||
val |= FIELD_PREP(TXCTRL1_DEEMPH_G1_MASK, 0x05);
|
||||
|
||||
val = readl(priv->base + RXCTRL);
|
||||
val &= ~RXCTRL_LOS_LVL_MASK;
|
||||
val |= FIELD_PREP(RXCTRL_LOS_LVL_MASK, 0x9);
|
||||
val &= ~RXCTRL_LOS_BIAS_MASK;
|
||||
val |= FIELD_PREP(RXCTRL_LOS_BIAS_MASK, 0x2);
|
||||
val &= ~RXCTRL_RX_EQ_MASK;
|
||||
val |= FIELD_PREP(RXCTRL_RX_EQ_MASK, 0x1);
|
||||
|
||||
/* dummy read 25 times to make a wait time for the phy to stabilize */
|
||||
for (i = 0; i < 25; i++)
|
||||
readl(priv->base + CKCTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_init(struct phy *phy)
|
||||
{
|
||||
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_parent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(priv->rst_parent);
|
||||
if (ret)
|
||||
goto out_clk_disable;
|
||||
|
||||
if (priv->data->init) {
|
||||
ret = priv->data->init(priv);
|
||||
if (ret)
|
||||
goto out_rst_assert;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_rst_assert:
|
||||
reset_control_assert(priv->rst_parent);
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(priv->clk_parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_exit(struct phy *phy)
|
||||
{
|
||||
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
reset_control_assert(priv->rst_parent);
|
||||
clk_disable_unprepare(priv->clk_parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_power_on(struct phy *phy)
|
||||
{
|
||||
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(priv->rst);
|
||||
if (ret)
|
||||
goto out_clk_disable;
|
||||
|
||||
if (priv->data->power_on) {
|
||||
ret = priv->data->power_on(priv);
|
||||
if (ret)
|
||||
goto out_reset_assert;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_reset_assert:
|
||||
reset_control_assert(priv->rst);
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_ahciphy_power_off(struct phy *phy)
|
||||
{
|
||||
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->data->power_off)
|
||||
ret = priv->data->power_off(priv);
|
||||
|
||||
reset_control_assert(priv->rst);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct phy_ops uniphier_ahciphy_ops = {
|
||||
.init = uniphier_ahciphy_init,
|
||||
.exit = uniphier_ahciphy_exit,
|
||||
.power_on = uniphier_ahciphy_power_on,
|
||||
.power_off = uniphier_ahciphy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int uniphier_ahciphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct uniphier_ahciphy_priv *priv;
|
||||
struct phy *phy;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->data = of_device_get_match_data(dev);
|
||||
if (WARN_ON(!priv->data))
|
||||
return -EINVAL;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
priv->clk_parent = devm_clk_get(dev, "link");
|
||||
if (IS_ERR(priv->clk_parent))
|
||||
return PTR_ERR(priv->clk_parent);
|
||||
|
||||
if (priv->data->is_phy_clk) {
|
||||
priv->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
|
||||
if (IS_ERR(priv->rst_parent))
|
||||
return PTR_ERR(priv->rst_parent);
|
||||
|
||||
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
||||
if (IS_ERR(priv->rst))
|
||||
return PTR_ERR(priv->rst);
|
||||
|
||||
phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops);
|
||||
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 uniphier_ahciphy_soc_data uniphier_pxs2_data = {
|
||||
.power_on = uniphier_ahciphy_pxs2_power_on,
|
||||
.power_off = uniphier_ahciphy_pxs2_power_off,
|
||||
.is_ready_high = false,
|
||||
.is_phy_clk = false,
|
||||
};
|
||||
|
||||
static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = {
|
||||
.init = uniphier_ahciphy_pxs3_init,
|
||||
.power_on = uniphier_ahciphy_pxs2_power_on,
|
||||
.power_off = uniphier_ahciphy_pxs2_power_off,
|
||||
.is_ready_high = true,
|
||||
.is_phy_clk = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id uniphier_ahciphy_match[] = {
|
||||
{
|
||||
.compatible = "socionext,uniphier-pxs2-ahci-phy",
|
||||
.data = &uniphier_pxs2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,uniphier-pxs3-ahci-phy",
|
||||
.data = &uniphier_pxs3_data,
|
||||
},
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, uniphier_ahciphy_match);
|
||||
|
||||
static struct platform_driver uniphier_ahciphy_driver = {
|
||||
.probe = uniphier_ahciphy_probe,
|
||||
.driver = {
|
||||
.name = "uniphier-ahci-phy",
|
||||
.of_match_table = uniphier_ahciphy_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(uniphier_ahciphy_driver);
|
||||
|
||||
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
|
||||
MODULE_DESCRIPTION("UniPhier PHY driver for AHCI controller");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -19,15 +19,38 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define CMU_R004 0x4
|
||||
#define CMU_R060 0x60
|
||||
#define CMU_R07C 0x7c
|
||||
#define CMU_R088 0x88
|
||||
#define CMU_R0D0 0xd0
|
||||
#define CMU_R0E8 0xe8
|
||||
|
||||
#define LANE_R048 0x248
|
||||
#define LANE_R058 0x258
|
||||
#define LANE_R06c 0x26c
|
||||
#define LANE_R070 0x270
|
||||
#define LANE_R070 0x270
|
||||
#define LANE_R19C 0x39c
|
||||
|
||||
#define COMLANE_R004 0xa04
|
||||
#define COMLANE_R138 0xb38
|
||||
#define VERSION 0x70
|
||||
#define VERSION_VAL 0x70
|
||||
|
||||
#define COMLANE_R190 0xb90
|
||||
|
||||
#define COMLANE_R194 0xb94
|
||||
|
||||
#define COMRXEQ_R004 0x1404
|
||||
#define COMRXEQ_R008 0x1408
|
||||
#define COMRXEQ_R00C 0x140c
|
||||
#define COMRXEQ_R014 0x1414
|
||||
#define COMRXEQ_R018 0x1418
|
||||
#define COMRXEQ_R01C 0x141c
|
||||
#define COMRXEQ_R04C 0x144c
|
||||
#define COMRXEQ_R088 0x1488
|
||||
#define COMRXEQ_R094 0x1494
|
||||
#define COMRXEQ_R098 0x1498
|
||||
|
||||
#define SERDES_CTRL 0x1fd0
|
||||
|
||||
#define WIZ_LANEXCTL_STS 0x1fe0
|
||||
|
@ -80,27 +103,136 @@ static const struct regmap_config serdes_am654_regmap_config = {
|
|||
.max_register = 0x1ffc,
|
||||
};
|
||||
|
||||
static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24);
|
||||
static const struct reg_field config_version = REG_FIELD(COMLANE_R138, 16, 23);
|
||||
static const struct reg_field l1_master_cdn_o = REG_FIELD(COMLANE_R190, 9, 9);
|
||||
static const struct reg_field cmu_ok_i_0 = REG_FIELD(COMLANE_R194, 19, 19);
|
||||
static const struct reg_field por_en = REG_FIELD(SERDES_CTRL, 29, 29);
|
||||
static const struct reg_field tx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31);
|
||||
static const struct reg_field rx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15);
|
||||
static const struct reg_field pll_enable = REG_FIELD(WIZ_PLL_CTRL, 29, 31);
|
||||
static const struct reg_field pll_ok = REG_FIELD(WIZ_PLL_CTRL, 28, 28);
|
||||
enum serdes_am654_fields {
|
||||
/* CMU PLL Control */
|
||||
CMU_PLL_CTRL,
|
||||
|
||||
LANE_PLL_CTRL_RXEQ_RXIDLE,
|
||||
|
||||
/* CMU VCO bias current and VREG setting */
|
||||
AHB_PMA_CM_VCO_VBIAS_VREG,
|
||||
AHB_PMA_CM_VCO_BIAS_VREG,
|
||||
|
||||
AHB_PMA_CM_SR,
|
||||
AHB_SSC_GEN_Z_O_20_13,
|
||||
|
||||
/* AHB PMA Lane Configuration */
|
||||
AHB_PMA_LN_AGC_THSEL_VREGH,
|
||||
|
||||
/* AGC and Signal detect threshold for Gen3 */
|
||||
AHB_PMA_LN_GEN3_AGC_SD_THSEL,
|
||||
|
||||
AHB_PMA_LN_RX_SELR_GEN3,
|
||||
AHB_PMA_LN_TX_DRV,
|
||||
|
||||
/* CMU Master Reset */
|
||||
CMU_MASTER_CDN,
|
||||
|
||||
/* P2S ring buffer initial startup pointer difference */
|
||||
P2S_RBUF_PTR_DIFF,
|
||||
|
||||
CONFIG_VERSION,
|
||||
|
||||
/* Lane 1 Master Reset */
|
||||
L1_MASTER_CDN,
|
||||
|
||||
/* CMU OK Status */
|
||||
CMU_OK_I_0,
|
||||
|
||||
/* Mid-speed initial calibration control */
|
||||
COMRXEQ_MS_INIT_CTRL_7_0,
|
||||
|
||||
/* High-speed initial calibration control */
|
||||
COMRXEQ_HS_INIT_CAL_7_0,
|
||||
|
||||
/* Mid-speed recalibration control */
|
||||
COMRXEQ_MS_RECAL_CTRL_7_0,
|
||||
|
||||
/* High-speed recalibration control */
|
||||
COMRXEQ_HS_RECAL_CTRL_7_0,
|
||||
|
||||
/* ATT configuration */
|
||||
COMRXEQ_CSR_ATT_CONFIG,
|
||||
|
||||
/* Edge based boost adaptation window length */
|
||||
COMRXEQ_CSR_EBSTADAPT_WIN_LEN,
|
||||
|
||||
/* COMRXEQ control 3 & 4 */
|
||||
COMRXEQ_CTRL_3_4,
|
||||
|
||||
/* COMRXEQ control 14, 15 and 16*/
|
||||
COMRXEQ_CTRL_14_15_16,
|
||||
|
||||
/* Threshold for errors in pattern data */
|
||||
COMRXEQ_CSR_DLEV_ERR_THRESH,
|
||||
|
||||
/* COMRXEQ control 25 */
|
||||
COMRXEQ_CTRL_25,
|
||||
|
||||
/* Mid-speed rate change calibration control */
|
||||
CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O,
|
||||
|
||||
/* High-speed rate change calibration control */
|
||||
COMRXEQ_HS_RCHANGE_CTRL_7_0,
|
||||
|
||||
/* Serdes reset */
|
||||
POR_EN,
|
||||
|
||||
/* Tx Enable Value */
|
||||
TX0_ENABLE,
|
||||
|
||||
/* Rx Enable Value */
|
||||
RX0_ENABLE,
|
||||
|
||||
/* PLL Enable Value */
|
||||
PLL_ENABLE,
|
||||
|
||||
/* PLL ready for use */
|
||||
PLL_OK,
|
||||
|
||||
/* sentinel */
|
||||
MAX_FIELDS
|
||||
|
||||
};
|
||||
|
||||
static const struct reg_field serdes_am654_reg_fields[] = {
|
||||
[CMU_PLL_CTRL] = REG_FIELD(CMU_R004, 8, 15),
|
||||
[AHB_PMA_CM_VCO_VBIAS_VREG] = REG_FIELD(CMU_R060, 8, 15),
|
||||
[CMU_MASTER_CDN] = REG_FIELD(CMU_R07C, 24, 31),
|
||||
[AHB_PMA_CM_VCO_BIAS_VREG] = REG_FIELD(CMU_R088, 24, 31),
|
||||
[AHB_PMA_CM_SR] = REG_FIELD(CMU_R0D0, 24, 31),
|
||||
[AHB_SSC_GEN_Z_O_20_13] = REG_FIELD(CMU_R0E8, 8, 15),
|
||||
[LANE_PLL_CTRL_RXEQ_RXIDLE] = REG_FIELD(LANE_R048, 8, 15),
|
||||
[AHB_PMA_LN_AGC_THSEL_VREGH] = REG_FIELD(LANE_R058, 16, 23),
|
||||
[AHB_PMA_LN_GEN3_AGC_SD_THSEL] = REG_FIELD(LANE_R06c, 0, 7),
|
||||
[AHB_PMA_LN_RX_SELR_GEN3] = REG_FIELD(LANE_R070, 16, 23),
|
||||
[AHB_PMA_LN_TX_DRV] = REG_FIELD(LANE_R19C, 16, 23),
|
||||
[P2S_RBUF_PTR_DIFF] = REG_FIELD(COMLANE_R004, 0, 7),
|
||||
[CONFIG_VERSION] = REG_FIELD(COMLANE_R138, 16, 23),
|
||||
[L1_MASTER_CDN] = REG_FIELD(COMLANE_R190, 8, 15),
|
||||
[CMU_OK_I_0] = REG_FIELD(COMLANE_R194, 19, 19),
|
||||
[COMRXEQ_MS_INIT_CTRL_7_0] = REG_FIELD(COMRXEQ_R004, 24, 31),
|
||||
[COMRXEQ_HS_INIT_CAL_7_0] = REG_FIELD(COMRXEQ_R008, 0, 7),
|
||||
[COMRXEQ_MS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 8, 15),
|
||||
[COMRXEQ_HS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 16, 23),
|
||||
[COMRXEQ_CSR_ATT_CONFIG] = REG_FIELD(COMRXEQ_R014, 16, 23),
|
||||
[COMRXEQ_CSR_EBSTADAPT_WIN_LEN] = REG_FIELD(COMRXEQ_R018, 16, 23),
|
||||
[COMRXEQ_CTRL_3_4] = REG_FIELD(COMRXEQ_R01C, 8, 15),
|
||||
[COMRXEQ_CTRL_14_15_16] = REG_FIELD(COMRXEQ_R04C, 0, 7),
|
||||
[COMRXEQ_CSR_DLEV_ERR_THRESH] = REG_FIELD(COMRXEQ_R088, 16, 23),
|
||||
[COMRXEQ_CTRL_25] = REG_FIELD(COMRXEQ_R094, 24, 31),
|
||||
[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O] = REG_FIELD(COMRXEQ_R098, 8, 15),
|
||||
[COMRXEQ_HS_RCHANGE_CTRL_7_0] = REG_FIELD(COMRXEQ_R098, 16, 23),
|
||||
[POR_EN] = REG_FIELD(SERDES_CTRL, 29, 29),
|
||||
[TX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31),
|
||||
[RX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15),
|
||||
[PLL_ENABLE] = REG_FIELD(WIZ_PLL_CTRL, 29, 31),
|
||||
[PLL_OK] = REG_FIELD(WIZ_PLL_CTRL, 28, 28),
|
||||
};
|
||||
|
||||
struct serdes_am654 {
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *cmu_master_cdn_o;
|
||||
struct regmap_field *config_version;
|
||||
struct regmap_field *l1_master_cdn_o;
|
||||
struct regmap_field *cmu_ok_i_0;
|
||||
struct regmap_field *por_en;
|
||||
struct regmap_field *tx0_enable;
|
||||
struct regmap_field *rx0_enable;
|
||||
struct regmap_field *pll_enable;
|
||||
struct regmap_field *pll_ok;
|
||||
struct regmap_field *fields[MAX_FIELDS];
|
||||
|
||||
struct device *dev;
|
||||
struct mux_control *control;
|
||||
|
@ -116,12 +248,12 @@ static int serdes_am654_enable_pll(struct serdes_am654 *phy)
|
|||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = regmap_field_write(phy->pll_enable, PLL_ENABLE_STATE);
|
||||
ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_ENABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_field_read_poll_timeout(phy->pll_ok, val, val, 1000,
|
||||
PLL_LOCK_TIME);
|
||||
return regmap_field_read_poll_timeout(phy->fields[PLL_OK], val, val,
|
||||
1000, PLL_LOCK_TIME);
|
||||
}
|
||||
|
||||
static void serdes_am654_disable_pll(struct serdes_am654 *phy)
|
||||
|
@ -129,41 +261,39 @@ static void serdes_am654_disable_pll(struct serdes_am654 *phy)
|
|||
struct device *dev = phy->dev;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_write(phy->pll_enable, PLL_DISABLE_STATE);
|
||||
ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_DISABLE_STATE);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to disable PLL\n");
|
||||
}
|
||||
|
||||
static int serdes_am654_enable_txrx(struct serdes_am654 *phy)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Enable TX */
|
||||
ret = regmap_field_write(phy->tx0_enable, TX0_ENABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_ENABLE_STATE);
|
||||
|
||||
/* Enable RX */
|
||||
ret = regmap_field_write(phy->rx0_enable, RX0_ENABLE_STATE);
|
||||
ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_ENABLE_STATE);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_am654_disable_txrx(struct serdes_am654 *phy)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Disable TX */
|
||||
ret = regmap_field_write(phy->tx0_enable, TX0_DISABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_DISABLE_STATE);
|
||||
|
||||
/* Disable RX */
|
||||
ret = regmap_field_write(phy->rx0_enable, RX0_DISABLE_STATE);
|
||||
ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_DISABLE_STATE);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -187,8 +317,8 @@ static int serdes_am654_power_on(struct phy *x)
|
|||
return ret;
|
||||
}
|
||||
|
||||
return regmap_field_read_poll_timeout(phy->cmu_ok_i_0, val, val,
|
||||
SLEEP_TIME, PLL_LOCK_TIME);
|
||||
return regmap_field_read_poll_timeout(phy->fields[CMU_OK_I_0], val,
|
||||
val, SLEEP_TIME, PLL_LOCK_TIME);
|
||||
}
|
||||
|
||||
static int serdes_am654_power_off(struct phy *x)
|
||||
|
@ -286,19 +416,37 @@ static int serdes_am654_usb3_init(struct serdes_am654 *phy)
|
|||
|
||||
static int serdes_am654_pcie_init(struct serdes_am654 *phy)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
ret = regmap_field_write(phy->config_version, VERSION);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret |= regmap_field_write(phy->fields[CMU_PLL_CTRL], 0x2);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_VBIAS_VREG], 0x98);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_BIAS_VREG], 0x98);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_CM_SR], 0x45);
|
||||
ret |= regmap_field_write(phy->fields[AHB_SSC_GEN_Z_O_20_13], 0xe);
|
||||
ret |= regmap_field_write(phy->fields[LANE_PLL_CTRL_RXEQ_RXIDLE], 0x5);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_AGC_THSEL_VREGH], 0x83);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_GEN3_AGC_SD_THSEL], 0x83);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_RX_SELR_GEN3], 0x81);
|
||||
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_TX_DRV], 0x3b);
|
||||
ret |= regmap_field_write(phy->fields[P2S_RBUF_PTR_DIFF], 0x3);
|
||||
ret |= regmap_field_write(phy->fields[CONFIG_VERSION], VERSION_VAL);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_MS_INIT_CTRL_7_0], 0xf);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_HS_INIT_CAL_7_0], 0x4f);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_MS_RECAL_CTRL_7_0], 0xf);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RECAL_CTRL_7_0], 0x4f);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_ATT_CONFIG], 0x7);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_EBSTADAPT_WIN_LEN], 0x7f);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_3_4], 0xf);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_14_15_16], 0x9a);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_DLEV_ERR_THRESH], 0x32);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_25], 0x80);
|
||||
ret |= regmap_field_write(phy->fields[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O], 0xf);
|
||||
ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RCHANGE_CTRL_7_0], 0x4f);
|
||||
ret |= regmap_field_write(phy->fields[CMU_MASTER_CDN], 0x1);
|
||||
ret |= regmap_field_write(phy->fields[L1_MASTER_CDN], 0x2);
|
||||
|
||||
ret = regmap_field_write(phy->cmu_master_cdn_o, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_field_write(phy->l1_master_cdn_o, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -320,20 +468,19 @@ static int serdes_am654_init(struct phy *x)
|
|||
static int serdes_am654_reset(struct phy *x)
|
||||
{
|
||||
struct serdes_am654 *phy = phy_get_drvdata(x);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
serdes_am654_disable_pll(phy);
|
||||
serdes_am654_disable_txrx(phy);
|
||||
|
||||
ret = regmap_field_write(phy->por_en, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret |= regmap_field_write(phy->fields[POR_EN], 0x1);
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ret = regmap_field_write(phy->por_en, 0x0);
|
||||
ret |= regmap_field_write(phy->fields[POR_EN], 0x0);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -587,66 +734,16 @@ static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy)
|
|||
{
|
||||
struct regmap *regmap = am654_phy->regmap;
|
||||
struct device *dev = am654_phy->dev;
|
||||
int i;
|
||||
|
||||
am654_phy->cmu_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
|
||||
cmu_master_cdn_o);
|
||||
if (IS_ERR(am654_phy->cmu_master_cdn_o)) {
|
||||
dev_err(dev, "CMU_MASTER_CDN_O reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->cmu_master_cdn_o);
|
||||
}
|
||||
|
||||
am654_phy->config_version = devm_regmap_field_alloc(dev, regmap,
|
||||
config_version);
|
||||
if (IS_ERR(am654_phy->config_version)) {
|
||||
dev_err(dev, "CONFIG_VERSION reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->config_version);
|
||||
}
|
||||
|
||||
am654_phy->l1_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
|
||||
l1_master_cdn_o);
|
||||
if (IS_ERR(am654_phy->l1_master_cdn_o)) {
|
||||
dev_err(dev, "L1_MASTER_CDN_O reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->l1_master_cdn_o);
|
||||
}
|
||||
|
||||
am654_phy->cmu_ok_i_0 = devm_regmap_field_alloc(dev, regmap,
|
||||
cmu_ok_i_0);
|
||||
if (IS_ERR(am654_phy->cmu_ok_i_0)) {
|
||||
dev_err(dev, "CMU_OK_I_0 reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->cmu_ok_i_0);
|
||||
}
|
||||
|
||||
am654_phy->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
|
||||
if (IS_ERR(am654_phy->por_en)) {
|
||||
dev_err(dev, "POR_EN reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->por_en);
|
||||
}
|
||||
|
||||
am654_phy->tx0_enable = devm_regmap_field_alloc(dev, regmap,
|
||||
tx0_enable);
|
||||
if (IS_ERR(am654_phy->tx0_enable)) {
|
||||
dev_err(dev, "TX0_ENABLE reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->tx0_enable);
|
||||
}
|
||||
|
||||
am654_phy->rx0_enable = devm_regmap_field_alloc(dev, regmap,
|
||||
rx0_enable);
|
||||
if (IS_ERR(am654_phy->rx0_enable)) {
|
||||
dev_err(dev, "RX0_ENABLE reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->rx0_enable);
|
||||
}
|
||||
|
||||
am654_phy->pll_enable = devm_regmap_field_alloc(dev, regmap,
|
||||
pll_enable);
|
||||
if (IS_ERR(am654_phy->pll_enable)) {
|
||||
dev_err(dev, "PLL_ENABLE reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->pll_enable);
|
||||
}
|
||||
|
||||
am654_phy->pll_ok = devm_regmap_field_alloc(dev, regmap, pll_ok);
|
||||
if (IS_ERR(am654_phy->pll_ok)) {
|
||||
dev_err(dev, "PLL_OK reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->pll_ok);
|
||||
for (i = 0; i < MAX_FIELDS; i++) {
|
||||
am654_phy->fields[i] = devm_regmap_field_alloc(dev,
|
||||
regmap,
|
||||
serdes_am654_reg_fields[i]);
|
||||
if (IS_ERR(am654_phy->fields[i])) {
|
||||
dev_err(dev, "Unable to allocate regmap field %d\n", i);
|
||||
return PTR_ERR(am654_phy->fields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
@ -22,7 +23,7 @@
|
|||
#define AM33XX_GMII_SEL_MODE_RGMII 2
|
||||
|
||||
enum {
|
||||
PHY_GMII_SEL_PORT_MODE,
|
||||
PHY_GMII_SEL_PORT_MODE = 0,
|
||||
PHY_GMII_SEL_RGMII_ID_MODE,
|
||||
PHY_GMII_SEL_RMII_IO_CLK_EN,
|
||||
PHY_GMII_SEL_LAST,
|
||||
|
@ -41,6 +42,7 @@ struct phy_gmii_sel_soc_data {
|
|||
u32 num_ports;
|
||||
u32 features;
|
||||
const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
|
||||
bool use_of_data;
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_priv {
|
||||
|
@ -49,6 +51,8 @@ struct phy_gmii_sel_priv {
|
|||
struct regmap *regmap;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy_gmii_sel_phy_priv *if_phys;
|
||||
u32 num_ports;
|
||||
u32 reg_offset;
|
||||
};
|
||||
|
||||
static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
|
@ -147,13 +151,9 @@ static const
|
|||
struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = {
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -172,16 +172,19 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
|
|||
|
||||
static const
|
||||
struct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = {
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4040, 0, 1),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x0, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x8, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0xC, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x10, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x14, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x18, 0, 2), },
|
||||
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x1C, 0, 2), },
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = {
|
||||
.num_ports = 1,
|
||||
.use_of_data = true,
|
||||
.regfields = phy_gmii_sel_fields_am654,
|
||||
};
|
||||
|
||||
|
@ -228,7 +231,7 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
|
|||
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
|
||||
args->args_count < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (phy_id > priv->soc_data->num_ports)
|
||||
if (phy_id > priv->num_ports)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (phy_id != priv->if_phys[phy_id - 1].id)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -242,68 +245,97 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
|
|||
return priv->if_phys[phy_id].if_phy;
|
||||
}
|
||||
|
||||
static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
|
||||
static int phy_gmii_init_phy(struct phy_gmii_sel_priv *priv, int port,
|
||||
struct phy_gmii_sel_phy_priv *if_phy)
|
||||
{
|
||||
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
|
||||
struct device *dev = priv->dev;
|
||||
const struct reg_field *fields;
|
||||
struct regmap_field *regfield;
|
||||
struct reg_field field;
|
||||
int ret;
|
||||
|
||||
if_phy->id = port;
|
||||
if_phy->priv = priv;
|
||||
|
||||
fields = soc_data->regfields[port - 1];
|
||||
field = *fields++;
|
||||
field.reg += priv->reg_offset;
|
||||
dev_dbg(dev, "%s field %x %d %d\n", __func__,
|
||||
field.reg, field.msb, field.lsb);
|
||||
|
||||
regfield = devm_regmap_field_alloc(dev, priv->regmap, field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phy->fields[PHY_GMII_SEL_PORT_MODE] = regfield;
|
||||
|
||||
field = *fields++;
|
||||
field.reg += priv->reg_offset;
|
||||
if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE] = regfield;
|
||||
dev_dbg(dev, "%s field %x %d %d\n", __func__,
|
||||
field.reg, field.msb, field.lsb);
|
||||
}
|
||||
|
||||
field = *fields;
|
||||
field.reg += priv->reg_offset;
|
||||
if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = regfield;
|
||||
dev_dbg(dev, "%s field %x %d %d\n", __func__,
|
||||
field.reg, field.msb, field.lsb);
|
||||
}
|
||||
|
||||
if_phy->if_phy = devm_phy_create(dev,
|
||||
priv->dev->of_node,
|
||||
&phy_gmii_sel_ops);
|
||||
if (IS_ERR(if_phy->if_phy)) {
|
||||
ret = PTR_ERR(if_phy->if_phy);
|
||||
dev_err(dev, "Failed to create phy%d %d\n", port, ret);
|
||||
return ret;
|
||||
}
|
||||
phy_set_drvdata(if_phy->if_phy, if_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
|
||||
{
|
||||
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
|
||||
struct phy_gmii_sel_phy_priv *if_phys;
|
||||
int i, num_ports, ret;
|
||||
struct device *dev = priv->dev;
|
||||
int i, ret;
|
||||
|
||||
num_ports = priv->soc_data->num_ports;
|
||||
if (soc_data->use_of_data) {
|
||||
const __be32 *offset;
|
||||
u64 size;
|
||||
|
||||
if_phys = devm_kcalloc(priv->dev, num_ports,
|
||||
offset = of_get_address(dev->of_node, 0, &size, NULL);
|
||||
priv->num_ports = size / sizeof(u32);
|
||||
if (!priv->num_ports)
|
||||
return -EINVAL;
|
||||
priv->reg_offset = __be32_to_cpu(*offset);
|
||||
}
|
||||
|
||||
if_phys = devm_kcalloc(dev, priv->num_ports,
|
||||
sizeof(*if_phys), GFP_KERNEL);
|
||||
if (!if_phys)
|
||||
return -ENOMEM;
|
||||
dev_dbg(dev, "%s %d\n", __func__, num_ports);
|
||||
dev_dbg(dev, "%s %d\n", __func__, priv->num_ports);
|
||||
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
const struct reg_field *field;
|
||||
struct regmap_field *regfield;
|
||||
|
||||
if_phys[i].id = i + 1;
|
||||
if_phys[i].priv = priv;
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE];
|
||||
dev_dbg(dev, "%s field %x %d %d\n", __func__,
|
||||
field->reg, field->msb, field->lsb);
|
||||
|
||||
regfield = devm_regmap_field_alloc(dev, priv->regmap, *field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield;
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE];
|
||||
if (field->reg != (~0)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
*field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] =
|
||||
regfield;
|
||||
}
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN];
|
||||
if (field->reg != (~0)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
*field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] =
|
||||
regfield;
|
||||
}
|
||||
|
||||
if_phys[i].if_phy = devm_phy_create(dev,
|
||||
priv->dev->of_node,
|
||||
&phy_gmii_sel_ops);
|
||||
if (IS_ERR(if_phys[i].if_phy)) {
|
||||
ret = PTR_ERR(if_phys[i].if_phy);
|
||||
dev_err(dev, "Failed to create phy%d %d\n", i, ret);
|
||||
for (i = 0; i < priv->num_ports; i++) {
|
||||
ret = phy_gmii_init_phy(priv, i + 1, &if_phys[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]);
|
||||
}
|
||||
|
||||
priv->if_phys = if_phys;
|
||||
|
@ -328,6 +360,7 @@ static int phy_gmii_sel_probe(struct platform_device *pdev)
|
|||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->soc_data = of_id->data;
|
||||
priv->num_ports = priv->soc_data->num_ports;
|
||||
|
||||
priv->regmap = syscon_node_to_regmap(node->parent);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
#define WIZ_SERDES_CTRL 0x404
|
||||
#define WIZ_SERDES_TOP_CTRL 0x408
|
||||
|
|
|
@ -6,23 +6,23 @@
|
|||
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/phy/omap_usb.h>
|
||||
#include <linux/usb/phy_companion.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
#include <linux/phy/omap_usb.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/usb/phy_companion.h>
|
||||
|
||||
#define USB2PHY_ANA_CONFIG1 0x4c
|
||||
#define USB2PHY_DISCON_BYP_LATCH BIT(31)
|
||||
|
@ -89,7 +89,7 @@ static inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
|
|||
}
|
||||
|
||||
/**
|
||||
* omap_usb2_set_comparator - links the comparator present in the sytem with
|
||||
* omap_usb2_set_comparator - links the comparator present in the system with
|
||||
* this phy
|
||||
* @comparator - the companion phy(comparator) for this phy
|
||||
*
|
||||
|
@ -142,7 +142,7 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
|
|||
}
|
||||
|
||||
static int omap_usb_set_peripheral(struct usb_otg *otg,
|
||||
struct usb_gadget *gadget)
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
otg->gadget = gadget;
|
||||
if (!gadget)
|
||||
|
@ -409,7 +409,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(phy->phy_base);
|
||||
|
||||
phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-phy-power");
|
||||
"syscon-phy-power");
|
||||
if (IS_ERR(phy->syscon_phy_power)) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"can't get syscon-phy-power, using control device\n");
|
||||
|
@ -438,7 +438,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
|
||||
|
@ -452,10 +451,10 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
|||
if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
|
||||
return PTR_ERR(phy->wkupclk);
|
||||
} else {
|
||||
dev_warn(&pdev->dev,
|
||||
"found usb_phy_cm_clk32k, please fix DTS\n");
|
||||
}
|
||||
|
||||
dev_warn(&pdev->dev,
|
||||
"found usb_phy_cm_clk32k, please fix DTS\n");
|
||||
}
|
||||
|
||||
phy->optclk = devm_clk_get(phy->dev, "refclk");
|
||||
|
@ -504,7 +503,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
|
||||
usb_add_phy_dev(&phy->phy);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -42,8 +42,6 @@ source "drivers/staging/media/tegra-video/Kconfig"
|
|||
|
||||
source "drivers/staging/media/ipu3/Kconfig"
|
||||
|
||||
source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig"
|
||||
|
||||
source "drivers/staging/media/rkisp1/Kconfig"
|
||||
|
||||
if MEDIA_ANALOG_TV_SUPPORT
|
||||
|
|
|
@ -10,6 +10,5 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/
|
|||
obj-$(CONFIG_TEGRA_VDE) += tegra-vde/
|
||||
obj-$(CONFIG_VIDEO_HANTRO) += hantro/
|
||||
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/
|
||||
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config PHY_ROCKCHIP_DPHY_RX0
|
||||
tristate "Rockchip MIPI Synopsys DPHY RX0 driver"
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
select GENERIC_PHY_MIPI_DPHY
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Rockchip MIPI Synopsys DPHY RX0
|
||||
associated to the Rockchip ISP module present in RK3399 SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called phy-rockchip-dphy-rx0.
|
|
@ -1,2 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o
|
|
@ -1,6 +0,0 @@
|
|||
The main reason for keeping this in staging is because the only driver
|
||||
that uses this is rkisp1, which is also in staging. It should be moved together
|
||||
with rkisp1.
|
||||
|
||||
Please CC patches to Linux Media <linux-media@vger.kernel.org> and
|
||||
Helen Koike <helen.koike@collabora.com>.
|
13
include/dt-bindings/phy/phy-cadence-torrent.h
Normal file
13
include/dt-bindings/phy/phy-cadence-torrent.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This header provides constants for Cadence Torrent SERDES.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_TORRENT_SERDES_H
|
||||
#define _DT_BINDINGS_TORRENT_SERDES_H
|
||||
|
||||
#define TORRENT_SERDES_NO_SSC 0
|
||||
#define TORRENT_SERDES_EXTERNAL_SSC 1
|
||||
#define TORRENT_SERDES_INTERNAL_SSC 2
|
||||
|
||||
#endif /* _DT_BINDINGS_TORRENT_SERDES_H */
|
|
@ -19,5 +19,6 @@
|
|||
#define PHY_TYPE_DP 6
|
||||
#define PHY_TYPE_XPCS 7
|
||||
#define PHY_TYPE_SGMII 8
|
||||
#define PHY_TYPE_QSGMII 9
|
||||
|
||||
#endif /* _DT_BINDINGS_PHY */
|
||||
|
|
|
@ -115,10 +115,12 @@ struct phy_ops {
|
|||
/**
|
||||
* struct phy_attrs - represents phy attributes
|
||||
* @bus_width: Data path width implemented by PHY
|
||||
* @max_link_rate: Maximum link rate supported by PHY (in Mbps)
|
||||
* @mode: PHY mode
|
||||
*/
|
||||
struct phy_attrs {
|
||||
u32 bus_width;
|
||||
u32 max_link_rate;
|
||||
enum phy_mode mode;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue