phy: for 5.1
*) Add a new driver to support Armada 3700 COMPHY IP (supports SATA, USB3, PCIe) *) Add a new driver to support Armada UTMI PHY *) Add a new driver to support Cadence D-PHY *) Extend omap-usb2 PHY driver to be used for AM654 USB2 PHY *) Extend qcom-qmp PHY driver to be used for UFS PHY and USB3 PHY in Qualcomm MSM8998 *) Extend qcom-qusb2 PHY driver to support QUSB2 PHY in Qualcomm MSM8998 *) Remove module specific code that is present for drivers that can be only built-in *) Allow Freescale IMX8MQ USB to be used for multiple SoCs and not just i.MX8MQ *) Cleanups such as switch to SPDX identifier, use readl_poll_timeout macro, remove unused headers etc., Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> -----BEGIN PGP SIGNATURE----- iQJCBAABCgAsFiEEUXMr/TfP2p4suIY5Dlx4XIBNgtkFAlxiwzIOHGtpc2hvbkB0 aS5jb20ACgkQDlx4XIBNgtnxAQ/+Lb12tqZImrTm5A+KIvk3Kaq47TC0dwsNbfg4 Y8VWEAXP3Qicrc8uRxJDQdN2Jztf681kBOmTpa+RopwiaWTPQ/62MGTWOWIJHAoz LLXUo4FYF/bWDpszAh9CrVQVzZ6K6kq33fnLnJaYtYt0zyZS3pHnClApUgT+Ahz3 2HW1M/XPKHJYyNH5N1lCqiGdOQM4aKYn9sy+AlxWN9XCWLKA1W9hxzF+wcIH7RAB VCixtug8PJ4D2XMyN6SzF99N4OWEmsoES3PC/28/kMs6JfwPUcFTCFYOhE1i+g0l rF1b7nvhxXpw6PYG4Ub/O/ltIyU+0ll2sFT3nm6gEbNPbl3FeDlDNXGQupMwHvB7 N0DPVDZ9w0Xru6FzWQFcmW2PHbauCtTTYv6t+RxmdwtN4ituXwwxoO/xz4Ah1bTL Aqvt162uEWdCgqeW/nbq3b9MDITLnBbQ5pevsboaRj/GHXcF9K6D0oFsuWLb56KQ PzxulrPToBTWxbOyThA8CV1QI79re9Sd+05Niptg33vTAAw4Oh9+wKeqO8ZYPrvK gb2n5SEZIiZgcevA6M3Iy7GiWJ1Zg3p+uQxhBp39cKQsZbl6BtqXtOeG11wm2xOu g7D6vntoNMz+wj08/qemHgttrcJ5P8RA5k4PFZIMyCaJgK9Z5vTQH8zOfja4qefG LWC+U6Q= =wNhh -----END PGP SIGNATURE----- Merge tag 'phy-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next Kishon writes: phy: for 5.1 *) Add a new driver to support Armada 3700 COMPHY IP (supports SATA, USB3, PCIe) *) Add a new driver to support Armada UTMI PHY *) Add a new driver to support Cadence D-PHY *) Extend omap-usb2 PHY driver to be used for AM654 USB2 PHY *) Extend qcom-qmp PHY driver to be used for UFS PHY and USB3 PHY in Qualcomm MSM8998 *) Extend qcom-qusb2 PHY driver to support QUSB2 PHY in Qualcomm MSM8998 *) Remove module specific code that is present for drivers that can be only built-in *) Allow Freescale IMX8MQ USB to be used for multiple SoCs and not just i.MX8MQ *) Cleanups such as switch to SPDX identifier, use readl_poll_timeout macro, remove unused headers etc., Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> * tag 'phy-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy: (32 commits) phy: qcom-qmp: Add QMP UFS PHY support for msm8998 dt-bindings: phy-qcom-qmp: Add qcom,msm8998-qmp-ufs-phy phy: bcm-sr-pcie: Change operation when PIPEMUX=1 phy: Add Cadence D-PHY support dt-bindings: phy: Move the Cadence D-PHY bindings phy: dphy: Clarify lanes parameter documentation phy: dphy: Change units of wakeup and init parameters phy: dphy: Remove unused header MAINTAINERS: phy: fill Armada 3700 PHY drivers entry dt-bindings: phy: mvebu-utmi: add UTMI PHY bindings phy: add A3700 UTMI PHY driver MAINTAINERS: phy: add entry for Armada 3700 COMPHY driver dt-bindings: phy: mvebu-comphy: extend the file to describe a3700 bindings phy: add A3700 COMPHY support phy: mvebu-cp110-comphy: fix port check in ->xlate() phy: armada375-usb2: switch to SPDX license identifier phy: make phy-armada375-usb2 explicitly non-modular phy: make phy-mvebu-sata explicitly non-modular phy: make phy-core explicitly non-modular phy: qcom-qusb2: Add QUSB2 PHY support for msm8998 ...
This commit is contained in:
commit
0220dcd113
@ -31,28 +31,7 @@ Required subnodes:
|
||||
- one subnode per DSI device connected on the DSI bus. Each DSI device should
|
||||
contain a reg property encoding its virtual channel.
|
||||
|
||||
Cadence DPHY
|
||||
============
|
||||
|
||||
Cadence DPHY block.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be set to "cdns,dphy".
|
||||
- reg: physical base address and length of the DPHY registers.
|
||||
- clocks: DPHY reference clocks.
|
||||
- clock-names: must contain "psm" and "pll_ref".
|
||||
- #phy-cells: must be set to 0.
|
||||
|
||||
|
||||
Example:
|
||||
dphy0: dphy@fd0e0000{
|
||||
compatible = "cdns,dphy";
|
||||
reg = <0x0 0xfd0e0000 0x0 0x1000>;
|
||||
clocks = <&psm_clk>, <&pll_ref_clk>;
|
||||
clock-names = "psm", "pll_ref";
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
dsi0: dsi@fd0c0000 {
|
||||
compatible = "cdns,dsi";
|
||||
reg = <0x0 0xfd0c0000 0x0 0x1000>;
|
||||
|
20
Documentation/devicetree/bindings/phy/cdns,dphy.txt
Normal file
20
Documentation/devicetree/bindings/phy/cdns,dphy.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Cadence DPHY
|
||||
============
|
||||
|
||||
Cadence DPHY block.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be set to "cdns,dphy".
|
||||
- reg: physical base address and length of the DPHY registers.
|
||||
- clocks: DPHY reference clocks.
|
||||
- clock-names: must contain "psm" and "pll_ref".
|
||||
- #phy-cells: must be set to 0.
|
||||
|
||||
Example:
|
||||
dphy0: dphy@fd0e0000{
|
||||
compatible = "cdns,dphy";
|
||||
reg = <0x0 0xfd0e0000 0x0 0x1000>;
|
||||
clocks = <&psm_clk>, <&pll_ref_clk>;
|
||||
clock-names = "psm", "pll_ref";
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -1,16 +1,27 @@
|
||||
mvebu comphy driver
|
||||
-------------------
|
||||
MVEBU comphy drivers
|
||||
--------------------
|
||||
|
||||
A comphy controller can be found on Marvell Armada 7k/8k on the CP110. It
|
||||
provides a number of shared PHYs used by various interfaces (network, sata,
|
||||
usb, PCIe...).
|
||||
COMPHY controllers can be found on the following Marvell MVEBU SoCs:
|
||||
* Armada 7k/8k (on the CP110)
|
||||
* Armada 3700
|
||||
It provides a number of shared PHYs used by various interfaces (network, SATA,
|
||||
USB, PCIe...).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "marvell,comphy-cp110"
|
||||
- reg: should contain the comphy register location and length.
|
||||
- marvell,system-controller: should contain a phandle to the
|
||||
system controller node.
|
||||
- compatible: should be one of:
|
||||
* "marvell,comphy-cp110" for Armada 7k/8k
|
||||
* "marvell,comphy-a3700" for Armada 3700
|
||||
- reg: should contain the COMPHY register(s) location(s) and length(s).
|
||||
* 1 entry for Armada 7k/8k
|
||||
* 4 entries for Armada 3700 along with the corresponding reg-names
|
||||
properties, memory areas are:
|
||||
* Generic COMPHY registers
|
||||
* Lane 1 (PCIe/GbE)
|
||||
* Lane 0 (USB3/GbE)
|
||||
* Lane 2 (SATA/USB3)
|
||||
- marvell,system-controller: should contain a phandle to the system
|
||||
controller node (only for Armada 7k/8k)
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
|
||||
@ -18,11 +29,11 @@ A sub-node is required for each comphy lane provided by the comphy.
|
||||
|
||||
Required properties (child nodes):
|
||||
|
||||
- reg: comphy lane number.
|
||||
- #phy-cells : from the generic phy bindings, must be 1. Defines the
|
||||
- reg: COMPHY lane number.
|
||||
- #phy-cells : from the generic PHY bindings, must be 1. Defines the
|
||||
input port to use for a given comphy lane.
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
cpm_comphy: phy@120000 {
|
||||
compatible = "marvell,comphy-cp110";
|
||||
@ -41,3 +52,33 @@ Example:
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
comphy: phy@18300 {
|
||||
compatible = "marvell,comphy-a3700";
|
||||
reg = <0x18300 0x300>,
|
||||
<0x1F000 0x400>,
|
||||
<0x5C000 0x400>,
|
||||
<0xe0178 0x8>;
|
||||
reg-names = "comphy",
|
||||
"lane1_pcie_gbe",
|
||||
"lane0_usb3_gbe",
|
||||
"lane2_sata_usb3";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
||||
comphy0: phy@0 {
|
||||
reg = <0>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
comphy1: phy@1 {
|
||||
reg = <1>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
comphy2: phy@2 {
|
||||
reg = <2>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
38
Documentation/devicetree/bindings/phy/phy-mvebu-utmi.txt
Normal file
38
Documentation/devicetree/bindings/phy/phy-mvebu-utmi.txt
Normal file
@ -0,0 +1,38 @@
|
||||
MVEBU A3700 UTMI PHY
|
||||
--------------------
|
||||
|
||||
USB2 UTMI+ PHY controllers can be found on the following Marvell MVEBU SoCs:
|
||||
* Armada 3700
|
||||
|
||||
On Armada 3700, there are two USB controllers, one is compatible with the USB2
|
||||
and USB3 specifications and supports OTG. The other one is USB2 compliant and
|
||||
only supports host mode. Both of these controllers come with a slightly
|
||||
different UTMI PHY.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
* "marvell,a3700-utmi-host-phy" for the PHY connected to
|
||||
the USB2 host-only controller.
|
||||
* "marvell,a3700-utmi-otg-phy" for the PHY connected to
|
||||
the USB3 and USB2 OTG capable controller.
|
||||
- reg: PHY IP register range.
|
||||
- marvell,usb-misc-reg: handle on the "USB miscellaneous registers" shared
|
||||
region covering registers related to both the host
|
||||
controller and the PHY.
|
||||
- #phy-cells: Standard property (Documentation: phy-bindings.txt) Should be 0.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
usb2_utmi_host_phy: phy@5f000 {
|
||||
compatible = "marvell,armada-3700-utmi-host-phy";
|
||||
reg = <0x5f000 0x800>;
|
||||
marvell,usb-misc-reg = <&usb2_syscon>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
usb2_syscon: system-controller@5f800 {
|
||||
compatible = "marvell,armada-3700-usb2-host-misc", "syscon";
|
||||
reg = <0x5f800 0x800>;
|
||||
};
|
@ -23,6 +23,8 @@ Optional properties:
|
||||
register files". When set driver will request its
|
||||
phandle as one companion-grf for some special SoCs
|
||||
(e.g RV1108).
|
||||
- extcon : phandle to the extcon device providing the cable state for
|
||||
the otg phy.
|
||||
|
||||
Required nodes : a sub-node is required for each port the phy provides.
|
||||
The sub-node name is used to identify host or otg port,
|
||||
|
@ -9,6 +9,8 @@ Required properties:
|
||||
"qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074
|
||||
"qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
|
||||
"qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
|
||||
"qcom,msm8998-qmp-usb3-phy" for USB3 QMP V3 phy on msm8998,
|
||||
"qcom,msm8998-qmp-ufs-phy" for UFS QMP phy on msm8998,
|
||||
"qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845,
|
||||
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
|
||||
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
|
||||
@ -42,6 +44,10 @@ Required properties:
|
||||
"aux", "cfg_ahb", "ref".
|
||||
For "qcom,msm8996-qmp-usb3-phy" must contain:
|
||||
"aux", "cfg_ahb", "ref".
|
||||
For "qcom,msm8998-qmp-usb3-phy" must contain:
|
||||
"aux", "cfg_ahb", "ref".
|
||||
For "qcom,msm8998-qmp-ufs-phy" must contain:
|
||||
"ref", "ref_aux".
|
||||
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
||||
"aux", "cfg_ahb", "ref", "com_aux".
|
||||
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
||||
@ -61,6 +67,9 @@ Required properties:
|
||||
"phy", "common", "cfg".
|
||||
For "qcom,msm8996-qmp-usb3-phy" must contain
|
||||
"phy", "common".
|
||||
For "qcom,msm8998-qmp-usb3-phy" must contain
|
||||
"phy", "common".
|
||||
For "qcom,msm8998-qmp-ufs-phy": no resets are listed.
|
||||
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
||||
"phy", "common".
|
||||
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
||||
|
@ -6,6 +6,7 @@ QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
|
||||
Required properties:
|
||||
- compatible: compatible list, contains
|
||||
"qcom,msm8996-qusb2-phy" for 14nm PHY on msm8996,
|
||||
"qcom,msm8998-qusb2-phy" for 10nm PHY on msm8998,
|
||||
"qcom,sdm845-qusb2-phy" for 10nm PHY on sdm845.
|
||||
|
||||
- reg: offset and length of the PHY register set.
|
||||
|
@ -5,6 +5,8 @@ This file provides information on what the device node for the R-Car generation
|
||||
|
||||
Required properties:
|
||||
- compatible: "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1
|
||||
SoC.
|
||||
"renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0
|
||||
SoC.
|
||||
"renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
|
||||
SoC.
|
||||
|
@ -35,6 +35,7 @@ Required properties:
|
||||
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.
|
||||
|
@ -9089,6 +9089,14 @@ F: drivers/gpu/drm/armada/
|
||||
F: include/uapi/drm/armada_drm.h
|
||||
F: Documentation/devicetree/bindings/display/armada/
|
||||
|
||||
MARVELL ARMADA 3700 PHY DRIVERS
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
S: Maintained
|
||||
F: drivers/phy/marvell/phy-mvebu-a3700-comphy.c
|
||||
F: drivers/phy/marvell/phy-mvebu-a3700-utmi.c
|
||||
F: Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt
|
||||
F: Documentation/devicetree/bindings/phy/phy-mvebu-utmi.txt
|
||||
|
||||
MARVELL CRYPTO DRIVER
|
||||
M: Boris Brezillon <bbrezillon@kernel.org>
|
||||
M: Arnaud Ebalard <arno@natisbad.org>
|
||||
|
@ -78,8 +78,8 @@ struct sr_pcie_phy_core {
|
||||
static const u8 pipemux_table[] = {
|
||||
/* PIPEMUX = 0, EP 1x16 */
|
||||
0x00,
|
||||
/* PIPEMUX = 1, EP 2x8 */
|
||||
0x00,
|
||||
/* PIPEMUX = 1, EP 1x8 + RC 1x8, core 7 */
|
||||
0x80,
|
||||
/* PIPEMUX = 2, EP 4x4 */
|
||||
0x00,
|
||||
/* PIPEMUX = 3, RC 2x8, cores 0, 7 */
|
||||
|
@ -1,6 +1,7 @@
|
||||
#
|
||||
# Phy drivers for Cadence PHYs
|
||||
#
|
||||
|
||||
config PHY_CADENCE_DP
|
||||
tristate "Cadence MHDP DisplayPort PHY driver"
|
||||
depends on OF
|
||||
@ -9,9 +10,19 @@ config PHY_CADENCE_DP
|
||||
help
|
||||
Support for Cadence MHDP DisplayPort PHY.
|
||||
|
||||
config PHY_CADENCE_DPHY
|
||||
tristate "Cadence D-PHY Support"
|
||||
depends on HAS_IOMEM && OF
|
||||
select GENERIC_PHY
|
||||
select GENERIC_PHY_MIPI_DPHY
|
||||
help
|
||||
Choose this option if you have a Cadence D-PHY in your
|
||||
system. If M is selected, the module will be called
|
||||
cdns-dphy.
|
||||
|
||||
config PHY_CADENCE_SIERRA
|
||||
tristate "Cadence Sierra PHY Driver"
|
||||
depends on OF && HAS_IOMEM && RESET_CONTROLLER
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Cadence Sierra PHY driver
|
||||
Enable this to support the Cadence Sierra PHY driver
|
||||
|
@ -1,2 +1,3 @@
|
||||
obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o
|
||||
obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o
|
||||
obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o
|
||||
|
391
drivers/phy/cadence/cdns-dphy.c
Normal file
391
drivers/phy/cadence/cdns-dphy.c
Normal file
@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright: 2017-2018 Cadence Design Systems, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
|
||||
#define REG_WAKEUP_TIME_NS 800
|
||||
#define DPHY_PLL_RATE_HZ 108000000
|
||||
|
||||
/* DPHY registers */
|
||||
#define DPHY_PMA_CMN(reg) (reg)
|
||||
#define DPHY_PMA_LCLK(reg) (0x100 + (reg))
|
||||
#define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg))
|
||||
#define DPHY_PMA_RCLK(reg) (0x600 + (reg))
|
||||
#define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg))
|
||||
#define DPHY_PCS(reg) (0xb00 + (reg))
|
||||
|
||||
#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
|
||||
#define DPHY_CMN_SSM_EN BIT(0)
|
||||
#define DPHY_CMN_TX_MODE_EN BIT(9)
|
||||
|
||||
#define DPHY_CMN_PWM DPHY_PMA_CMN(0x40)
|
||||
#define DPHY_CMN_PWM_DIV(x) ((x) << 20)
|
||||
#define DPHY_CMN_PWM_LOW(x) ((x) << 10)
|
||||
#define DPHY_CMN_PWM_HIGH(x) (x)
|
||||
|
||||
#define DPHY_CMN_FBDIV DPHY_PMA_CMN(0x4c)
|
||||
#define DPHY_CMN_FBDIV_VAL(low, high) (((high) << 11) | ((low) << 22))
|
||||
#define DPHY_CMN_FBDIV_FROM_REG (BIT(10) | BIT(21))
|
||||
|
||||
#define DPHY_CMN_OPIPDIV DPHY_PMA_CMN(0x50)
|
||||
#define DPHY_CMN_IPDIV_FROM_REG BIT(0)
|
||||
#define DPHY_CMN_IPDIV(x) ((x) << 1)
|
||||
#define DPHY_CMN_OPDIV_FROM_REG BIT(6)
|
||||
#define DPHY_CMN_OPDIV(x) ((x) << 7)
|
||||
|
||||
#define DPHY_PSM_CFG DPHY_PCS(0x4)
|
||||
#define DPHY_PSM_CFG_FROM_REG BIT(0)
|
||||
#define DPHY_PSM_CLK_DIV(x) ((x) << 1)
|
||||
|
||||
#define DSI_HBP_FRAME_OVERHEAD 12
|
||||
#define DSI_HSA_FRAME_OVERHEAD 14
|
||||
#define DSI_HFP_FRAME_OVERHEAD 6
|
||||
#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4
|
||||
#define DSI_BLANKING_FRAME_OVERHEAD 6
|
||||
#define DSI_NULL_FRAME_OVERHEAD 6
|
||||
#define DSI_EOT_PKT_SIZE 4
|
||||
|
||||
struct cdns_dphy_cfg {
|
||||
u8 pll_ipdiv;
|
||||
u8 pll_opdiv;
|
||||
u16 pll_fbdiv;
|
||||
unsigned int nlanes;
|
||||
};
|
||||
|
||||
enum cdns_dphy_clk_lane_cfg {
|
||||
DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
|
||||
DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
|
||||
DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
|
||||
DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
|
||||
};
|
||||
|
||||
struct cdns_dphy;
|
||||
struct cdns_dphy_ops {
|
||||
int (*probe)(struct cdns_dphy *dphy);
|
||||
void (*remove)(struct cdns_dphy *dphy);
|
||||
void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
|
||||
void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
|
||||
enum cdns_dphy_clk_lane_cfg cfg);
|
||||
void (*set_pll_cfg)(struct cdns_dphy *dphy,
|
||||
const struct cdns_dphy_cfg *cfg);
|
||||
unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
|
||||
};
|
||||
|
||||
struct cdns_dphy {
|
||||
struct cdns_dphy_cfg cfg;
|
||||
void __iomem *regs;
|
||||
struct clk *psm_clk;
|
||||
struct clk *pll_ref_clk;
|
||||
const struct cdns_dphy_ops *ops;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
|
||||
struct cdns_dphy_cfg *cfg,
|
||||
struct phy_configure_opts_mipi_dphy *opts,
|
||||
unsigned int *dsi_hfp_ext)
|
||||
{
|
||||
unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
|
||||
u64 dlane_bps;
|
||||
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
|
||||
return -EINVAL;
|
||||
else if (pll_ref_hz < 19200000)
|
||||
cfg->pll_ipdiv = 1;
|
||||
else if (pll_ref_hz < 38400000)
|
||||
cfg->pll_ipdiv = 2;
|
||||
else if (pll_ref_hz < 76800000)
|
||||
cfg->pll_ipdiv = 4;
|
||||
else
|
||||
cfg->pll_ipdiv = 8;
|
||||
|
||||
dlane_bps = opts->hs_clk_rate;
|
||||
|
||||
if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
|
||||
return -EINVAL;
|
||||
else if (dlane_bps >= 1250000000)
|
||||
cfg->pll_opdiv = 1;
|
||||
else if (dlane_bps >= 630000000)
|
||||
cfg->pll_opdiv = 2;
|
||||
else if (dlane_bps >= 320000000)
|
||||
cfg->pll_opdiv = 4;
|
||||
else if (dlane_bps >= 160000000)
|
||||
cfg->pll_opdiv = 8;
|
||||
|
||||
cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
|
||||
cfg->pll_ipdiv,
|
||||
pll_ref_hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
|
||||
{
|
||||
unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
|
||||
unsigned long psm_div;
|
||||
|
||||
if (!psm_clk_hz || psm_clk_hz > 100000000)
|
||||
return -EINVAL;
|
||||
|
||||
psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
|
||||
if (dphy->ops->set_psm_div)
|
||||
dphy->ops->set_psm_div(dphy, psm_div);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
|
||||
enum cdns_dphy_clk_lane_cfg cfg)
|
||||
{
|
||||
if (dphy->ops->set_clk_lane_cfg)
|
||||
dphy->ops->set_clk_lane_cfg(dphy, cfg);
|
||||
}
|
||||
|
||||
static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
|
||||
const struct cdns_dphy_cfg *cfg)
|
||||
{
|
||||
if (dphy->ops->set_pll_cfg)
|
||||
dphy->ops->set_pll_cfg(dphy, cfg);
|
||||
}
|
||||
|
||||
static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
|
||||
{
|
||||
return dphy->ops->get_wakeup_time_ns(dphy);
|
||||
}
|
||||
|
||||
static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
|
||||
{
|
||||
/* Default wakeup time is 800 ns (in a simulated environment). */
|
||||
return 800;
|
||||
}
|
||||
|
||||
static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
|
||||
const struct cdns_dphy_cfg *cfg)
|
||||
{
|
||||
u32 fbdiv_low, fbdiv_high;
|
||||
|
||||
fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
|
||||
fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
|
||||
|
||||
writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
|
||||
DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
|
||||
DPHY_CMN_OPDIV(cfg->pll_opdiv),
|
||||
dphy->regs + DPHY_CMN_OPIPDIV);
|
||||
writel(DPHY_CMN_FBDIV_FROM_REG |
|
||||
DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
|
||||
dphy->regs + DPHY_CMN_FBDIV);
|
||||
writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
|
||||
DPHY_CMN_PWM_DIV(0x8),
|
||||
dphy->regs + DPHY_CMN_PWM);
|
||||
}
|
||||
|
||||
static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
|
||||
{
|
||||
writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
|
||||
dphy->regs + DPHY_PSM_CFG);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the reference implementation of DPHY hooks. Specific integration of
|
||||
* this IP may have to re-implement some of them depending on how they decided
|
||||
* to wire things in the SoC.
|
||||
*/
|
||||
static const struct cdns_dphy_ops ref_dphy_ops = {
|
||||
.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
|
||||
.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
|
||||
.set_psm_div = cdns_dphy_ref_set_psm_div,
|
||||
};
|
||||
|
||||
static int cdns_dphy_config_from_opts(struct phy *phy,
|
||||
struct phy_configure_opts_mipi_dphy *opts,
|
||||
struct cdns_dphy_cfg *cfg)
|
||||
{
|
||||
struct cdns_dphy *dphy = phy_get_drvdata(phy);
|
||||
unsigned int dsi_hfp_ext = 0;
|
||||
int ret;
|
||||
|
||||
ret = phy_mipi_dphy_config_validate(opts);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
|
||||
opts, &dsi_hfp_ext);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
struct cdns_dphy_cfg cfg = { 0 };
|
||||
|
||||
if (mode != PHY_MODE_MIPI_DPHY)
|
||||
return -EINVAL;
|
||||
|
||||
return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
|
||||
}
|
||||
|
||||
static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
|
||||
{
|
||||
struct cdns_dphy *dphy = phy_get_drvdata(phy);
|
||||
struct cdns_dphy_cfg cfg = { 0 };
|
||||
int ret;
|
||||
|
||||
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Configure the internal PSM clk divider so that the DPHY has a
|
||||
* 1MHz clk (or something close).
|
||||
*/
|
||||
ret = cdns_dphy_setup_psm(dphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
|
||||
* and 8 data lanes, each clk lane can be attache different set of
|
||||
* data lanes. The 2 groups are named 'left' and 'right', so here we
|
||||
* just say that we want the 'left' clk lane to drive the 'left' data
|
||||
* lanes.
|
||||
*/
|
||||
cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
|
||||
|
||||
/*
|
||||
* Configure the DPHY PLL that will be used to generate the TX byte
|
||||
* clk.
|
||||
*/
|
||||
cdns_dphy_set_pll_cfg(dphy, &cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_dphy_power_on(struct phy *phy)
|
||||
{
|
||||
struct cdns_dphy *dphy = phy_get_drvdata(phy);
|
||||
|
||||
clk_prepare_enable(dphy->psm_clk);
|
||||
clk_prepare_enable(dphy->pll_ref_clk);
|
||||
|
||||
/* Start TX state machine. */
|
||||
writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
|
||||
dphy->regs + DPHY_CMN_SSM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_dphy_power_off(struct phy *phy)
|
||||
{
|
||||
struct cdns_dphy *dphy = phy_get_drvdata(phy);
|
||||
|
||||
clk_disable_unprepare(dphy->pll_ref_clk);
|
||||
clk_disable_unprepare(dphy->psm_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops cdns_dphy_ops = {
|
||||
.configure = cdns_dphy_configure,
|
||||
.validate = cdns_dphy_validate,
|
||||
.power_on = cdns_dphy_power_on,
|
||||
.power_off = cdns_dphy_power_off,
|
||||
};
|
||||
|
||||
static int cdns_dphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct cdns_dphy *dphy;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
|
||||
if (!dphy)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&pdev->dev, dphy);
|
||||
|
||||
dphy->ops = of_device_get_match_data(&pdev->dev);
|
||||
if (!dphy->ops)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dphy->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dphy->regs))
|
||||
return PTR_ERR(dphy->regs);
|
||||
|
||||
dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
|
||||
if (IS_ERR(dphy->psm_clk))
|
||||
return PTR_ERR(dphy->psm_clk);
|
||||
|
||||
dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
|
||||
if (IS_ERR(dphy->pll_ref_clk))
|
||||
return PTR_ERR(dphy->pll_ref_clk);
|
||||
|
||||
if (dphy->ops->probe) {
|
||||
ret = dphy->ops->probe(dphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
|
||||
if (IS_ERR(dphy->phy)) {
|
||||
dev_err(&pdev->dev, "failed to create PHY\n");
|
||||
if (dphy->ops->remove)
|
||||
dphy->ops->remove(dphy);
|
||||
return PTR_ERR(dphy->phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(dphy->phy, dphy);
|
||||
phy_provider = devm_of_phy_provider_register(&pdev->dev,
|
||||
of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static int cdns_dphy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (dphy->ops->remove)
|
||||
dphy->ops->remove(dphy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cdns_dphy_of_match[] = {
|
||||
{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
|
||||
|
||||
static struct platform_driver cdns_dphy_platform_driver = {
|
||||
.probe = cdns_dphy_probe,
|
||||
.remove = cdns_dphy_remove,
|
||||
.driver = {
|
||||
.name = "cdns-mipi-dphy",
|
||||
.of_match_table = cdns_dphy_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(cdns_dphy_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -2,4 +2,4 @@ config PHY_FSL_IMX8MQ_USB
|
||||
tristate "Freescale i.MX8M USB3 PHY"
|
||||
depends on OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
default SOC_IMX8MQ
|
||||
default ARCH_MXC && ARM64
|
||||
|
@ -21,6 +21,27 @@ config PHY_BERLIN_USB
|
||||
help
|
||||
Enable this to support the USB PHY on Marvell Berlin SoCs.
|
||||
|
||||
config PHY_MVEBU_A3700_COMPHY
|
||||
tristate "Marvell A3700 comphy driver"
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on HAVE_ARM_SMCCC
|
||||
default y
|
||||
select GENERIC_PHY
|
||||
help
|
||||
This driver allows to control the comphy, a hardware block providing
|
||||
shared serdes PHYs on Marvell Armada 3700. Its serdes lanes can be
|
||||
used by various controllers: Ethernet, SATA, USB3, PCIe.
|
||||
|
||||
config PHY_MVEBU_A3700_UTMI
|
||||
tristate "Marvell A3700 UTMI driver"
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
depends on OF
|
||||
default y
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support Marvell A3700 UTMI PHY driver.
|
||||
|
||||
config PHY_MVEBU_CP110_COMPHY
|
||||
tristate "Marvell CP110 comphy driver"
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
|
@ -2,6 +2,8 @@
|
||||
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
|
||||
obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o
|
||||
obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o
|
||||
obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
|
||||
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
|
||||
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* USB cluster support for Armada 375 platform.
|
||||
*
|
||||
@ -5,10 +6,6 @@
|
||||
*
|
||||
* Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2 or later. This program is licensed "as is"
|
||||
* without any warranty of any kind, whether express or implied.
|
||||
*
|
||||
* Armada 375 comes with an USB2 host and device controller and an
|
||||
* USB3 controller. The USB cluster control register allows to manage
|
||||
* common features of both USB controllers.
|
||||
@ -18,7 +15,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -142,7 +138,6 @@ static const struct of_device_id of_usb_cluster_table[] = {
|
||||
{ .compatible = "marvell,armada-375-usb-cluster", },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
|
||||
|
||||
static struct platform_driver armada375_usb_phy_driver = {
|
||||
.probe = armada375_usb_phy_probe,
|
||||
@ -151,8 +146,4 @@ static struct platform_driver armada375_usb_phy_driver = {
|
||||
.name = "armada-375-usb-cluster",
|
||||
}
|
||||
};
|
||||
module_platform_driver(armada375_usb_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Armada 375 USB cluster driver");
|
||||
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
builtin_platform_driver(armada375_usb_phy_driver);
|
||||
|
318
drivers/phy/marvell/phy-mvebu-a3700-comphy.c
Normal file
318
drivers/phy/marvell/phy-mvebu-a3700-comphy.c
Normal file
@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Marvell
|
||||
*
|
||||
* Authors:
|
||||
* Evan Wang <xswang@marvell.com>
|
||||
* Miquèl Raynal <miquel.raynal@bootlin.com>
|
||||
*
|
||||
* Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
|
||||
* SMC call initial support done by Grzegorz Jaszczyk.
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MVEBU_A3700_COMPHY_LANES 3
|
||||
#define MVEBU_A3700_COMPHY_PORTS 2
|
||||
|
||||
/* COMPHY Fast SMC function identifiers */
|
||||
#define COMPHY_SIP_POWER_ON 0x82000001
|
||||
#define COMPHY_SIP_POWER_OFF 0x82000002
|
||||
#define COMPHY_SIP_PLL_LOCK 0x82000003
|
||||
|
||||
#define COMPHY_FW_MODE_SATA 0x1
|
||||
#define COMPHY_FW_MODE_SGMII 0x2
|
||||
#define COMPHY_FW_MODE_HS_SGMII 0x3
|
||||
#define COMPHY_FW_MODE_USB3H 0x4
|
||||
#define COMPHY_FW_MODE_USB3D 0x5
|
||||
#define COMPHY_FW_MODE_PCIE 0x6
|
||||
#define COMPHY_FW_MODE_RXAUI 0x7
|
||||
#define COMPHY_FW_MODE_XFI 0x8
|
||||
#define COMPHY_FW_MODE_SFI 0x9
|
||||
#define COMPHY_FW_MODE_USB3 0xa
|
||||
|
||||
#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
|
||||
#define COMPHY_FW_SPEED_2_5G 1
|
||||
#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */
|
||||
#define COMPHY_FW_SPEED_5G 3
|
||||
#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */
|
||||
#define COMPHY_FW_SPEED_6G 5
|
||||
#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */
|
||||
#define COMPHY_FW_SPEED_MAX 0x3F
|
||||
|
||||
#define COMPHY_FW_MODE(mode) ((mode) << 12)
|
||||
#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
|
||||
((idx) << 8) | \
|
||||
((speed) << 2))
|
||||
#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
|
||||
((width) << 18))
|
||||
|
||||
struct mvebu_a3700_comphy_conf {
|
||||
unsigned int lane;
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
unsigned int port;
|
||||
u32 fw_mode;
|
||||
};
|
||||
|
||||
#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
|
||||
{ \
|
||||
.lane = _lane, \
|
||||
.mode = _mode, \
|
||||
.submode = _smode, \
|
||||
.port = _port, \
|
||||
.fw_mode = _fw, \
|
||||
}
|
||||
|
||||
#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
|
||||
MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
|
||||
|
||||
#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
|
||||
MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
|
||||
|
||||
static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
|
||||
/* lane 0 */
|
||||
MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
|
||||
COMPHY_FW_MODE_USB3H),
|
||||
MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
|
||||
COMPHY_FW_MODE_SGMII),
|
||||
MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
|
||||
COMPHY_FW_MODE_HS_SGMII),
|
||||
/* lane 1 */
|
||||
MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
|
||||
COMPHY_FW_MODE_PCIE),
|
||||
MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
|
||||
COMPHY_FW_MODE_SGMII),
|
||||
MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
|
||||
COMPHY_FW_MODE_HS_SGMII),
|
||||
/* lane 2 */
|
||||
MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
|
||||
COMPHY_FW_MODE_SATA),
|
||||
MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
|
||||
COMPHY_FW_MODE_USB3H),
|
||||
};
|
||||
|
||||
struct mvebu_a3700_comphy_lane {
|
||||
struct device *dev;
|
||||
unsigned int id;
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
int port;
|
||||
};
|
||||
|
||||
static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
|
||||
unsigned long mode)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
}
|
||||
|
||||
static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
|
||||
enum phy_mode mode,
|
||||
int submode)
|
||||
{
|
||||
int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes);
|
||||
|
||||
/* Unused PHY mux value is 0x0 */
|
||||
if (mode == PHY_MODE_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (mvebu_a3700_comphy_modes[i].lane == lane &&
|
||||
mvebu_a3700_comphy_modes[i].port == port &&
|
||||
mvebu_a3700_comphy_modes[i].mode == mode &&
|
||||
mvebu_a3700_comphy_modes[i].submode == submode)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == n)
|
||||
return -EINVAL;
|
||||
|
||||
return mvebu_a3700_comphy_modes[i].fw_mode;
|
||||
}
|
||||
|
||||
static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
|
||||
int submode)
|
||||
{
|
||||
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
int fw_mode;
|
||||
|
||||
if (submode == PHY_INTERFACE_MODE_1000BASEX)
|
||||
submode = PHY_INTERFACE_MODE_SGMII;
|
||||
|
||||
fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
|
||||
submode);
|
||||
if (fw_mode < 0) {
|
||||
dev_err(lane->dev, "invalid COMPHY mode\n");
|
||||
return fw_mode;
|
||||
}
|
||||
|
||||
/* Just remember the mode, ->power_on() will do the real setup */
|
||||
lane->mode = mode;
|
||||
lane->submode = submode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_a3700_comphy_power_on(struct phy *phy)
|
||||
{
|
||||
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
u32 fw_param;
|
||||
int fw_mode;
|
||||
|
||||
fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
|
||||
lane->mode, lane->submode);
|
||||
if (fw_mode < 0) {
|
||||
dev_err(lane->dev, "invalid COMPHY mode\n");
|
||||
return fw_mode;
|
||||
}
|
||||
|
||||
switch (lane->mode) {
|
||||
case PHY_MODE_USB_HOST_SS:
|
||||
dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
|
||||
fw_param = COMPHY_FW_MODE(fw_mode);
|
||||
break;
|
||||
case PHY_MODE_SATA:
|
||||
dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
|
||||
fw_param = COMPHY_FW_MODE(fw_mode);
|
||||
break;
|
||||
case PHY_MODE_ETHERNET:
|
||||
switch (lane->submode) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
|
||||
lane->id);
|
||||
fw_param = COMPHY_FW_NET(fw_mode, lane->port,
|
||||
COMPHY_FW_SPEED_1_25G);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n",
|
||||
lane->id);
|
||||
fw_param = COMPHY_FW_NET(fw_mode, lane->port,
|
||||
COMPHY_FW_SPEED_3_125G);
|
||||
break;
|
||||
default:
|
||||
dev_err(lane->dev, "unsupported PHY submode (%d)\n",
|
||||
lane->submode);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
break;
|
||||
case PHY_MODE_PCIE:
|
||||
dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
|
||||
fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
|
||||
COMPHY_FW_SPEED_5G,
|
||||
phy->attrs.bus_width);
|
||||
break;
|
||||
default:
|
||||
dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
|
||||
}
|
||||
|
||||
static int mvebu_a3700_comphy_power_off(struct phy *phy)
|
||||
{
|
||||
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
|
||||
return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
|
||||
}
|
||||
|
||||
static const struct phy_ops mvebu_a3700_comphy_ops = {
|
||||
.power_on = mvebu_a3700_comphy_power_on,
|
||||
.power_off = mvebu_a3700_comphy_power_off,
|
||||
.set_mode = mvebu_a3700_comphy_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct mvebu_a3700_comphy_lane *lane;
|
||||
struct phy *phy;
|
||||
|
||||
if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
phy = of_phy_simple_xlate(dev, args);
|
||||
if (IS_ERR(phy))
|
||||
return phy;
|
||||
|
||||
lane = phy_get_drvdata(phy);
|
||||
lane->port = args->args[0];
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *provider;
|
||||
struct device_node *child;
|
||||
|
||||
for_each_available_child_of_node(pdev->dev.of_node, child) {
|
||||
struct mvebu_a3700_comphy_lane *lane;
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
u32 lane_id;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", &lane_id);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "missing 'reg' property (%d)\n",
|
||||
ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
|
||||
dev_err(&pdev->dev, "invalid 'reg' property\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL);
|
||||
if (!lane)
|
||||
return -ENOMEM;
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, child,
|
||||
&mvebu_a3700_comphy_ops);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
lane->dev = &pdev->dev;
|
||||
lane->mode = PHY_MODE_INVALID;
|
||||
lane->submode = PHY_INTERFACE_MODE_NA;
|
||||
lane->id = lane_id;
|
||||
lane->port = -1;
|
||||
phy_set_drvdata(phy, lane);
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(&pdev->dev,
|
||||
mvebu_a3700_comphy_xlate);
|
||||
return PTR_ERR_OR_ZERO(provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
|
||||
{ .compatible = "marvell,comphy-a3700" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
|
||||
|
||||
static struct platform_driver mvebu_a3700_comphy_driver = {
|
||||
.probe = mvebu_a3700_comphy_probe,
|
||||
.driver = {
|
||||
.name = "mvebu-a3700-comphy",
|
||||
.of_match_table = mvebu_a3700_comphy_of_match_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mvebu_a3700_comphy_driver);
|
||||
|
||||
MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Common PHY driver for A3700");
|
||||
MODULE_LICENSE("GPL v2");
|
278
drivers/phy/marvell/phy-mvebu-a3700-utmi.c
Normal file
278
drivers/phy/marvell/phy-mvebu-a3700-utmi.c
Normal file
@ -0,0 +1,278 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Marvell
|
||||
*
|
||||
* Authors:
|
||||
* Igal Liberman <igall@marvell.com>
|
||||
* Miquèl Raynal <miquel.raynal@bootlin.com>
|
||||
*
|
||||
* Marvell A3700 UTMI PHY driver
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Armada 3700 UTMI PHY registers */
|
||||
#define USB2_PHY_PLL_CTRL_REG0 0x0
|
||||
#define PLL_REF_DIV_OFF 0
|
||||
#define PLL_REF_DIV_MASK GENMASK(6, 0)
|
||||
#define PLL_REF_DIV_5 5
|
||||
#define PLL_FB_DIV_OFF 16
|
||||
#define PLL_FB_DIV_MASK GENMASK(24, 16)
|
||||
#define PLL_FB_DIV_96 96
|
||||
#define PLL_SEL_LPFR_OFF 28
|
||||
#define PLL_SEL_LPFR_MASK GENMASK(29, 28)
|
||||
#define PLL_READY BIT(31)
|
||||
#define USB2_PHY_CAL_CTRL 0x8
|
||||
#define PHY_PLLCAL_DONE BIT(31)
|
||||
#define PHY_IMPCAL_DONE BIT(23)
|
||||
#define USB2_RX_CHAN_CTRL1 0x18
|
||||
#define USB2PHY_SQCAL_DONE BIT(31)
|
||||
#define USB2_PHY_OTG_CTRL 0x34
|
||||
#define PHY_PU_OTG BIT(4)
|
||||
#define USB2_PHY_CHRGR_DETECT 0x38
|
||||
#define PHY_CDP_EN BIT(2)
|
||||
#define PHY_DCP_EN BIT(3)
|
||||
#define PHY_PD_EN BIT(4)
|
||||
#define PHY_PU_CHRG_DTC BIT(5)
|
||||
#define PHY_CDP_DM_AUTO BIT(7)
|
||||
#define PHY_ENSWITCH_DP BIT(12)
|
||||
#define PHY_ENSWITCH_DM BIT(13)
|
||||
|
||||
/* Armada 3700 USB miscellaneous registers */
|
||||
#define USB2_PHY_CTRL(usb32) (usb32 ? 0x20 : 0x4)
|
||||
#define RB_USB2PHY_PU BIT(0)
|
||||
#define USB2_DP_PULLDN_DEV_MODE BIT(5)
|
||||
#define USB2_DM_PULLDN_DEV_MODE BIT(6)
|
||||
#define RB_USB2PHY_SUSPM(usb32) (usb32 ? BIT(14) : BIT(7))
|
||||
|
||||
#define PLL_LOCK_DELAY_US 10000
|
||||
#define PLL_LOCK_TIMEOUT_US 1000000
|
||||
|
||||
/**
|
||||
* struct mvebu_a3700_utmi_caps - PHY capabilities
|
||||
*
|
||||
* @usb32: Flag indicating which PHY is in use (impacts the register map):
|
||||
* - The UTMI PHY wired to the USB3/USB2 controller (otg)
|
||||
* - The UTMI PHY wired to the USB2 controller (host only)
|
||||
* @ops: PHY operations
|
||||
*/
|
||||
struct mvebu_a3700_utmi_caps {
|
||||
int usb32;
|
||||
const struct phy_ops *ops;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mvebu_a3700_utmi - PHY driver data
|
||||
*
|
||||
* @regs: PHY registers
|
||||
* @usb_mis: Regmap with USB miscellaneous registers including PHY ones
|
||||
* @caps: PHY capabilities
|
||||
* @phy: PHY handle
|
||||
*/
|
||||
struct mvebu_a3700_utmi {
|
||||
void __iomem *regs;
|
||||
struct regmap *usb_misc;
|
||||
const struct mvebu_a3700_utmi_caps *caps;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static int mvebu_a3700_utmi_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
|
||||
struct device *dev = &phy->dev;
|
||||
int usb32 = utmi->caps->usb32;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Setup PLL. 40MHz clock used to be the default, being 25MHz now.
|
||||
* See "PLL Settings for Typical REFCLK" table.
|
||||
*/
|
||||
reg = readl(utmi->regs + USB2_PHY_PLL_CTRL_REG0);
|
||||
reg &= ~(PLL_REF_DIV_MASK | PLL_FB_DIV_MASK | PLL_SEL_LPFR_MASK);
|
||||
reg |= (PLL_REF_DIV_5 << PLL_REF_DIV_OFF) |
|
||||
(PLL_FB_DIV_96 << PLL_FB_DIV_OFF);
|
||||
writel(reg, utmi->regs + USB2_PHY_PLL_CTRL_REG0);
|
||||
|
||||
/* Enable PHY pull up and disable USB2 suspend */
|
||||
regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
|
||||
RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU,
|
||||
RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU);
|
||||
|
||||
if (usb32) {
|
||||
/* Power up OTG module */
|
||||
reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
|
||||
reg |= PHY_PU_OTG;
|
||||
writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
|
||||
|
||||
/* Disable PHY charger detection */
|
||||
reg = readl(utmi->regs + USB2_PHY_CHRGR_DETECT);
|
||||
reg &= ~(PHY_CDP_EN | PHY_DCP_EN | PHY_PD_EN | PHY_PU_CHRG_DTC |
|
||||
PHY_CDP_DM_AUTO | PHY_ENSWITCH_DP | PHY_ENSWITCH_DM);
|
||||
writel(reg, utmi->regs + USB2_PHY_CHRGR_DETECT);
|
||||
|
||||
/* Disable PHY DP/DM pull-down (used for device mode) */
|
||||
regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
|
||||
USB2_DP_PULLDN_DEV_MODE |
|
||||
USB2_DM_PULLDN_DEV_MODE, 0);
|
||||
}
|
||||
|
||||
/* Wait for PLL calibration */
|
||||
ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
|
||||
reg & PHY_PLLCAL_DONE,
|
||||
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to end USB2 PLL calibration\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for impedance calibration */
|
||||
ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
|
||||
reg & PHY_IMPCAL_DONE,
|
||||
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to end USB2 impedance calibration\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for squelch calibration */
|
||||
ret = readl_poll_timeout(utmi->regs + USB2_RX_CHAN_CTRL1, reg,
|
||||
reg & USB2PHY_SQCAL_DONE,
|
||||
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to end USB2 unknown calibration\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for PLL to be locked */
|
||||
ret = readl_poll_timeout(utmi->regs + USB2_PHY_PLL_CTRL_REG0, reg,
|
||||
reg & PLL_READY,
|
||||
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to lock USB2 PLL\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mvebu_a3700_utmi_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
|
||||
int usb32 = utmi->caps->usb32;
|
||||
u32 reg;
|
||||
|
||||
/* Disable PHY pull-up and enable USB2 suspend */
|
||||
reg = readl(utmi->regs + USB2_PHY_CTRL(usb32));
|
||||
reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32));
|
||||
writel(reg, utmi->regs + USB2_PHY_CTRL(usb32));
|
||||
|
||||
/* Power down OTG module */
|
||||
if (usb32) {
|
||||
reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
|
||||
reg &= ~PHY_PU_OTG;
|
||||
writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops mvebu_a3700_utmi_phy_ops = {
|
||||
.power_on = mvebu_a3700_utmi_phy_power_on,
|
||||
.power_off = mvebu_a3700_utmi_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = {
|
||||
.usb32 = true,
|
||||
.ops = &mvebu_a3700_utmi_phy_ops,
|
||||
};
|
||||
|
||||
static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = {
|
||||
.usb32 = false,
|
||||
.ops = &mvebu_a3700_utmi_phy_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id mvebu_a3700_utmi_of_match[] = {
|
||||
{
|
||||
.compatible = "marvell,a3700-utmi-otg-phy",
|
||||
.data = &mvebu_a3700_utmi_otg_phy_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,a3700-utmi-host-phy",
|
||||
.data = &mvebu_a3700_utmi_host_phy_caps,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match);
|
||||
|
||||
static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mvebu_a3700_utmi *utmi;
|
||||
struct phy_provider *provider;
|
||||
struct resource *res;
|
||||
|
||||
utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
|
||||
if (!utmi)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get UTMI memory region */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "Missing UTMI PHY memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
utmi->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(utmi->regs))
|
||||
return PTR_ERR(utmi->regs);
|
||||
|
||||
/* Get miscellaneous Host/PHY region */
|
||||
utmi->usb_misc = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"marvell,usb-misc-reg");
|
||||
if (IS_ERR(utmi->usb_misc)) {
|
||||
dev_err(dev,
|
||||
"Missing USB misc purpose system controller\n");
|
||||
return PTR_ERR(utmi->usb_misc);
|
||||
}
|
||||
|
||||
/* Retrieve PHY capabilities */
|
||||
utmi->caps = of_device_get_match_data(dev);
|
||||
|
||||
/* Instantiate the PHY */
|
||||
utmi->phy = devm_phy_create(dev, NULL, utmi->caps->ops);
|
||||
if (IS_ERR(utmi->phy)) {
|
||||
dev_err(dev, "Failed to create the UTMI PHY\n");
|
||||
return PTR_ERR(utmi->phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(utmi->phy, utmi);
|
||||
|
||||
/* Ensure the PHY is powered off */
|
||||
utmi->caps->ops->power_off(utmi->phy);
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(provider);
|
||||
}
|
||||
|
||||
static struct platform_driver mvebu_a3700_utmi_driver = {
|
||||
.probe = mvebu_a3700_utmi_phy_probe,
|
||||
.driver = {
|
||||
.name = "mvebu-a3700-utmi-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mvebu_a3700_utmi_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mvebu_a3700_utmi_driver);
|
||||
|
||||
MODULE_AUTHOR("Igal Liberman <igall@marvell.com>");
|
||||
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -580,8 +580,6 @@ static struct phy *mvebu_comphy_xlate(struct device *dev,
|
||||
return phy;
|
||||
|
||||
lane = phy_get_drvdata(phy);
|
||||
if (lane->port >= 0)
|
||||
return ERR_PTR(-EBUSY);
|
||||
lane->port = args->args[0];
|
||||
|
||||
return phy;
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/io.h>
|
||||
@ -122,7 +122,6 @@ static const struct of_device_id phy_mvebu_sata_of_match[] = {
|
||||
{ .compatible = "marvell,mvebu-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match);
|
||||
|
||||
static struct platform_driver phy_mvebu_sata_driver = {
|
||||
.probe = phy_mvebu_sata_probe,
|
||||
@ -131,8 +130,4 @@ static struct platform_driver phy_mvebu_sata_driver = {
|
||||
.of_match_table = phy_mvebu_sata_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(phy_mvebu_sata_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
|
||||
MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
builtin_platform_driver(phy_mvebu_sata_driver);
|
||||
|
@ -65,12 +65,12 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
|
||||
*/
|
||||
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
|
||||
|
||||
cfg->init = 100000000;
|
||||
cfg->init = 100;
|
||||
cfg->lpx = 60000;
|
||||
cfg->ta_get = 5 * cfg->lpx;
|
||||
cfg->ta_go = 4 * cfg->lpx;
|
||||
cfg->ta_sure = 2 * cfg->lpx;
|
||||
cfg->wakeup = 1000000000;
|
||||
cfg->wakeup = 1000;
|
||||
|
||||
cfg->hs_clk_rate = hs_clk_rate;
|
||||
cfg->lanes = lanes;
|
||||
@ -143,7 +143,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
|
||||
if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->init < 100000000)
|
||||
if (cfg->init < 100)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->lpx < 50000)
|
||||
@ -158,7 +158,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
|
||||
if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->wakeup < 1000000000)
|
||||
if (cfg->wakeup < 1000)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
@ -1112,14 +1112,4 @@ static int __init phy_core_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(phy_core_init);
|
||||
|
||||
static void __exit phy_core_exit(void)
|
||||
{
|
||||
class_destroy(phy_class);
|
||||
}
|
||||
module_exit(phy_core_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Generic PHY Framework");
|
||||
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
device_initcall(phy_core_init);
|
||||
|
@ -687,6 +687,116 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x15),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
|
||||
};
|
||||
|
||||
|
||||
/* struct qmp_phy_cfg - per-PHY initialization config */
|
||||
struct qmp_phy_cfg {
|
||||
/* phy-type - PCIE/UFS/USB */
|
||||
@ -1036,6 +1146,33 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
|
||||
.type = PHY_TYPE_USB3,
|
||||
.nlanes = 1,
|
||||
|
||||
.serdes_tbl = msm8998_usb3_serdes_tbl,
|
||||
.serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl),
|
||||
.tx_tbl = msm8998_usb3_tx_tbl,
|
||||
.tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl),
|
||||
.rx_tbl = msm8998_usb3_rx_tbl,
|
||||
.rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl),
|
||||
.pcs_tbl = msm8998_usb3_pcs_tbl,
|
||||
.pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl),
|
||||
.clk_list = msm8996_phy_clk_l,
|
||||
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
|
||||
.reset_list = msm8996_usb3phy_reset_l,
|
||||
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = qmp_v3_usb3phy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START | PCS_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
.mask_pcs_ready = PHYSTATUS,
|
||||
|
||||
.is_dual_lane_phy = true,
|
||||
};
|
||||
|
||||
static void qcom_qmp_phy_configure(void __iomem *base,
|
||||
const unsigned int *regs,
|
||||
const struct qmp_phy_init_tbl tbl[],
|
||||
@ -1735,6 +1872,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
|
||||
}, {
|
||||
.compatible = "qcom,msm8996-qmp-usb3-phy",
|
||||
.data = &msm8996_usb3phy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,msm8998-qmp-ufs-phy",
|
||||
.data = &sdm845_ufsphy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,ipq8074-qmp-pcie-phy",
|
||||
.data = &ipq8074_pciephy_cfg,
|
||||
@ -1747,6 +1887,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
|
||||
}, {
|
||||
.compatible = "qcom,sdm845-qmp-ufs-phy",
|
||||
.data = &sdm845_ufsphy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,msm8998-qmp-usb3-phy",
|
||||
.data = &msm8998_usb3phy_cfg,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
@ -174,6 +174,7 @@
|
||||
#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4
|
||||
#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8
|
||||
#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc
|
||||
#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0
|
||||
#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8
|
||||
#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc
|
||||
#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0
|
||||
@ -201,6 +202,7 @@
|
||||
#define QSERDES_V3_COM_DEBUG_BUS2 0x170
|
||||
#define QSERDES_V3_COM_DEBUG_BUS3 0x174
|
||||
#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178
|
||||
#define QSERDES_V3_COM_CMN_MODE 0x184
|
||||
|
||||
/* Only for QMP V3 PHY - TX registers */
|
||||
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
|
||||
@ -211,6 +213,7 @@
|
||||
#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
|
||||
|
||||
/* Only for QMP V3 PHY - RX registers */
|
||||
#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008
|
||||
#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
|
||||
#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
|
||||
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024
|
||||
@ -219,6 +222,7 @@
|
||||
#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
|
||||
#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
|
||||
#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
|
||||
#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040
|
||||
#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044
|
||||
#define QSERDES_V3_RX_RX_TERM_BW 0x07c
|
||||
#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc
|
||||
|
@ -152,6 +152,31 @@ static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
|
||||
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
|
||||
};
|
||||
|
||||
static const unsigned int msm8998_regs_layout[] = {
|
||||
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
|
||||
[QUSB2PHY_PLL_STATUS] = 0x1a0,
|
||||
[QUSB2PHY_PORT_TUNE1] = 0x23c,
|
||||
[QUSB2PHY_PORT_TUNE2] = 0x240,
|
||||
[QUSB2PHY_PORT_TUNE3] = 0x244,
|
||||
[QUSB2PHY_PORT_TUNE4] = 0x248,
|
||||
[QUSB2PHY_PORT_TEST1] = 0x24c,
|
||||
[QUSB2PHY_PORT_TEST2] = 0x250,
|
||||
[QUSB2PHY_PORT_POWERDOWN] = 0x210,
|
||||
[QUSB2PHY_INTR_CTRL] = 0x22c,
|
||||
};
|
||||
|
||||
static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
|
||||
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x13),
|
||||
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
|
||||
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
|
||||
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a),
|
||||
|
||||
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xa5),
|
||||
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x09),
|
||||
|
||||
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
|
||||
};
|
||||
|
||||
static const unsigned int sdm845_regs_layout[] = {
|
||||
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
|
||||
[QUSB2PHY_PLL_STATUS] = 0x1a0,
|
||||
@ -221,6 +246,18 @@ static const struct qusb2_phy_cfg msm8996_phy_cfg = {
|
||||
.autoresume_en = BIT(3),
|
||||
};
|
||||
|
||||
static const struct qusb2_phy_cfg msm8998_phy_cfg = {
|
||||
.tbl = msm8998_init_tbl,
|
||||
.tbl_num = ARRAY_SIZE(msm8998_init_tbl),
|
||||
.regs = msm8998_regs_layout,
|
||||
|
||||
.disable_ctrl = POWER_DOWN,
|
||||
.mask_core_ready = CORE_READY_STATUS,
|
||||
.has_pll_override = true,
|
||||
.autoresume_en = BIT(0),
|
||||
.update_tune1_with_efuse = true,
|
||||
};
|
||||
|
||||
static const struct qusb2_phy_cfg sdm845_phy_cfg = {
|
||||
.tbl = sdm845_init_tbl,
|
||||
.tbl_num = ARRAY_SIZE(sdm845_init_tbl),
|
||||
@ -733,6 +770,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,msm8996-qusb2-phy",
|
||||
.data = &msm8996_phy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,msm8998-qusb2-phy",
|
||||
.data = &msm8998_phy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,sdm845-qusb2-phy",
|
||||
.data = &sdm845_phy_cfg,
|
||||
|
@ -23,24 +23,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
|
||||
({ \
|
||||
ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
|
||||
might_sleep_if(timeout_us); \
|
||||
for (;;) { \
|
||||
(val) = readl(addr); \
|
||||
if (cond) \
|
||||
break; \
|
||||
if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
|
||||
(val) = readl(addr); \
|
||||
break; \
|
||||
} \
|
||||
if (sleep_us) \
|
||||
usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
|
||||
} \
|
||||
(cond) ? 0 : -ETIMEDOUT; \
|
||||
})
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \
|
||||
{ \
|
||||
|
@ -55,16 +55,16 @@ enum rockchip_usb2phy_host_state {
|
||||
};
|
||||
|
||||
/**
|
||||
* Different states involved in USB charger detection.
|
||||
* USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
|
||||
* enum usb_chg_state - Different states involved in USB charger detection.
|
||||
* @USB_CHG_STATE_UNDEFINED: USB charger is not connected or detection
|
||||
* process is not yet started.
|
||||
* USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
|
||||
* USB_CHG_STATE_DCD_DONE Data pin contact is detected.
|
||||
* USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
|
||||
* @USB_CHG_STATE_WAIT_FOR_DCD: Waiting for Data pins contact.
|
||||
* @USB_CHG_STATE_DCD_DONE: Data pin contact is detected.
|
||||
* @USB_CHG_STATE_PRIMARY_DONE: Primary detection is completed (Detects
|
||||
* between SDP and DCP/CDP).
|
||||
* USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
|
||||
* between DCP and CDP).
|
||||
* USB_CHG_STATE_DETECTED USB charger type is determined.
|
||||
* @USB_CHG_STATE_SECONDARY_DONE: Secondary detection is completed (Detects
|
||||
* between DCP and CDP).
|
||||
* @USB_CHG_STATE_DETECTED: USB charger type is determined.
|
||||
*/
|
||||
enum usb_chg_state {
|
||||
USB_CHG_STATE_UNDEFINED = 0,
|
||||
@ -94,7 +94,7 @@ struct usb2phy_reg {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_chg_det_reg: usb charger detect registers
|
||||
* struct rockchip_chg_det_reg - usb charger detect registers
|
||||
* @cp_det: charging port detected successfully.
|
||||
* @dcp_det: dedicated charging port detected successfully.
|
||||
* @dp_det: assert data pin connect successfully.
|
||||
@ -120,7 +120,7 @@ struct rockchip_chg_det_reg {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
|
||||
* struct rockchip_usb2phy_port_cfg - usb-phy port configuration.
|
||||
* @phy_sus: phy suspend register.
|
||||
* @bvalid_det_en: vbus valid rise detection enable register.
|
||||
* @bvalid_det_st: vbus valid rise detection status register.
|
||||
@ -148,10 +148,11 @@ struct rockchip_usb2phy_port_cfg {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_usb2phy_cfg: usb-phy configuration.
|
||||
* struct rockchip_usb2phy_cfg - usb-phy configuration.
|
||||
* @reg: the address offset of grf for usb-phy config.
|
||||
* @num_ports: specify how many ports that the phy has.
|
||||
* @clkout_ctl: keep on/turn off output clk of phy.
|
||||
* @port_cfgs: usb-phy port configurations.
|
||||
* @chg_det: charger detection registers.
|
||||
*/
|
||||
struct rockchip_usb2phy_cfg {
|
||||
@ -163,12 +164,10 @@ struct rockchip_usb2phy_cfg {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_usb2phy_port: usb-phy port data.
|
||||
* struct rockchip_usb2phy_port - usb-phy port data.
|
||||
* @phy: generic phy.
|
||||
* @port_id: flag for otg port or host port.
|
||||
* @suspended: phy suspended flag.
|
||||
* @utmi_avalid: utmi avalid status usage flag.
|
||||
* true - use avalid to get vbus status
|
||||
* flase - use bvalid to get vbus status
|
||||
* @vbus_attached: otg device vbus status.
|
||||
* @bvalid_irq: IRQ number assigned for vbus valid rise detection.
|
||||
* @ls_irq: IRQ number assigned for linestate detection.
|
||||
@ -178,7 +177,7 @@ struct rockchip_usb2phy_cfg {
|
||||
* @chg_work: charge detect work.
|
||||
* @otg_sm_work: OTG state machine work.
|
||||
* @sm_work: HOST state machine work.
|
||||
* @phy_cfg: port register configuration, assigned by driver data.
|
||||
* @port_cfg: port register configuration, assigned by driver data.
|
||||
* @event_nb: hold event notification callback.
|
||||
* @state: define OTG enumeration states before device reset.
|
||||
* @mode: the dr_mode of the controller.
|
||||
@ -187,7 +186,6 @@ struct rockchip_usb2phy_port {
|
||||
struct phy *phy;
|
||||
unsigned int port_id;
|
||||
bool suspended;
|
||||
bool utmi_avalid;
|
||||
bool vbus_attached;
|
||||
int bvalid_irq;
|
||||
int ls_irq;
|
||||
@ -203,12 +201,13 @@ struct rockchip_usb2phy_port {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_usb2phy: usb2.0 phy driver data.
|
||||
* struct rockchip_usb2phy - usb2.0 phy driver data.
|
||||
* @dev: pointer to device.
|
||||
* @grf: General Register Files regmap.
|
||||
* @usbgrf: USB General Register Files regmap.
|
||||
* @clk: clock struct of phy input clk.
|
||||
* @clk480m: clock struct of phy output clk.
|
||||
* @clk_hw: clock struct of phy output clk management.
|
||||
* @clk480m_hw: clock struct of phy output clk management.
|
||||
* @chg_state: states involved in USB charger detection.
|
||||
* @chg_type: USB charger types.
|
||||
* @dcd_retries: The retry count used to track Data contact
|
||||
@ -542,12 +541,8 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
unsigned long delay;
|
||||
bool vbus_attach, sch_work, notify_charger;
|
||||
|
||||
if (rport->utmi_avalid)
|
||||
vbus_attach = property_enabled(rphy->grf,
|
||||
&rport->port_cfg->utmi_avalid);
|
||||
else
|
||||
vbus_attach = property_enabled(rphy->grf,
|
||||
&rport->port_cfg->utmi_bvalid);
|
||||
vbus_attach = property_enabled(rphy->grf,
|
||||
&rport->port_cfg->utmi_bvalid);
|
||||
|
||||
sch_work = false;
|
||||
notify_charger = false;
|
||||
@ -1021,9 +1016,6 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
|
||||
INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
|
||||
|
||||
rport->utmi_avalid =
|
||||
of_property_read_bool(child_np, "rockchip,utmi-avalid");
|
||||
|
||||
/*
|
||||
* Some SoCs use one interrupt with otg-id/otg-bvalid/linestate
|
||||
* interrupts muxed together, so probe the otg-mux interrupt first,
|
||||
|
@ -33,12 +33,11 @@ config OMAP_CONTROL_PHY
|
||||
|
||||
config OMAP_USB2
|
||||
tristate "OMAP USB2 PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
depends on ARCH_OMAP2PLUS || ARCH_K3
|
||||
depends on USB_SUPPORT
|
||||
select GENERIC_PHY
|
||||
select USB_PHY
|
||||
select OMAP_CONTROL_PHY
|
||||
depends on OMAP_OCP2SCP
|
||||
select OMAP_CONTROL_PHY if ARCH_OMAP2PLUS
|
||||
help
|
||||
Enable this to support the transceiver that is part of SOC. This
|
||||
driver takes care of all the PHY functionality apart from comparator.
|
||||
@ -50,7 +49,6 @@ config TI_PIPE3
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
select OMAP_CONTROL_PHY
|
||||
depends on OMAP_OCP2SCP
|
||||
help
|
||||
Enable this to support the PIPE3 PHY that is part of TI SOCs. This
|
||||
driver takes care of all the PHY functionality apart from comparator.
|
||||
|
@ -36,6 +36,10 @@
|
||||
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
|
||||
#define USB2PHY_ANA_CONFIG1 0x4c
|
||||
|
||||
#define AM654_USB2_OTG_PD BIT(8)
|
||||
#define AM654_USB2_VBUS_DET_EN BIT(5)
|
||||
#define AM654_USB2_VBUSVALID_DET_EN BIT(4)
|
||||
|
||||
/**
|
||||
* omap_usb2_set_comparator - links the comparator present in the sytem with
|
||||
* this phy
|
||||
@ -135,9 +139,9 @@ static int omap_usb_power_on(struct phy *x)
|
||||
|
||||
static int omap_usb2_disable_clocks(struct omap_usb *phy)
|
||||
{
|
||||
clk_disable(phy->wkupclk);
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
if (!IS_ERR(phy->optclk))
|
||||
clk_disable(phy->optclk);
|
||||
clk_disable_unprepare(phy->optclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -146,14 +150,14 @@ static int omap_usb2_enable_clocks(struct omap_usb *phy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(phy->wkupclk);
|
||||
ret = clk_prepare_enable(phy->wkupclk);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (!IS_ERR(phy->optclk)) {
|
||||
ret = clk_enable(phy->optclk);
|
||||
ret = clk_prepare_enable(phy->optclk);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
|
||||
goto err1;
|
||||
@ -245,6 +249,15 @@ static const struct usb_phy_data am437x_usb2_data = {
|
||||
.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data am654_usb2_data = {
|
||||
.label = "am654_usb2",
|
||||
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
|
||||
.mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
|
||||
AM654_USB2_VBUSVALID_DET_EN,
|
||||
.power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
|
||||
.power_off = AM654_USB2_OTG_PD,
|
||||
};
|
||||
|
||||
static const struct of_device_id omap_usb2_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,omap-usb2",
|
||||
@ -266,6 +279,10 @@ static const struct of_device_id omap_usb2_id_table[] = {
|
||||
.compatible = "ti,am437x-usb2",
|
||||
.data = &am437x_usb2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am654-usb2",
|
||||
.data = &am654_usb2_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
|
||||
@ -346,13 +363,52 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
otg->set_host = omap_usb_set_host;
|
||||
otg->set_peripheral = omap_usb_set_peripheral;
|
||||
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n",
|
||||
PTR_ERR(phy->wkupclk));
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
|
||||
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
phy->optclk = devm_clk_get(phy->dev, "refclk");
|
||||
if (IS_ERR(phy->optclk)) {
|
||||
if (PTR_ERR(phy->optclk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
|
||||
phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
|
||||
|
||||
if (IS_ERR(phy->optclk)) {
|
||||
if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"unable to get usb_otg_ss_refclk960m\n");
|
||||
}
|
||||
} else {
|
||||
dev_warn(&pdev->dev,
|
||||
"found usb_otg_ss_refclk960m, please fix DTS\n");
|
||||
}
|
||||
}
|
||||
|
||||
otg->set_host = omap_usb_set_host;
|
||||
otg->set_peripheral = omap_usb_set_peripheral;
|
||||
if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
|
||||
otg->set_vbus = omap_usb_set_vbus;
|
||||
otg->set_vbus = omap_usb_set_vbus;
|
||||
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
|
||||
otg->start_srp = omap_usb_start_srp;
|
||||
otg->usb_phy = &phy->phy;
|
||||
otg->start_srp = omap_usb_start_srp;
|
||||
otg->usb_phy = &phy->phy;
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
@ -367,42 +423,12 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
omap_usb_power_off(generic_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(phy->dev,
|
||||
of_phy_simple_xlate);
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
pm_runtime_disable(phy->dev);
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n");
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
|
||||
pm_runtime_disable(phy->dev);
|
||||
return PTR_ERR(phy->wkupclk);
|
||||
} else {
|
||||
dev_warn(&pdev->dev,
|
||||
"found usb_phy_cm_clk32k, please fix DTS\n");
|
||||
}
|
||||
}
|
||||
clk_prepare(phy->wkupclk);
|
||||
|
||||
phy->optclk = devm_clk_get(phy->dev, "refclk");
|
||||
if (IS_ERR(phy->optclk)) {
|
||||
dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
|
||||
phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
|
||||
if (IS_ERR(phy->optclk)) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"unable to get usb_otg_ss_refclk960m\n");
|
||||
} else {
|
||||
dev_warn(&pdev->dev,
|
||||
"found usb_otg_ss_refclk960m, please fix DTS\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(phy->optclk))
|
||||
clk_prepare(phy->optclk);
|
||||
|
||||
usb_add_phy_dev(&phy->phy);
|
||||
|
||||
@ -413,9 +439,6 @@ static int omap_usb2_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_usb *phy = platform_get_drvdata(pdev);
|
||||
|
||||
clk_unprepare(phy->wkupclk);
|
||||
if (!IS_ERR(phy->optclk))
|
||||
clk_unprepare(phy->optclk);
|
||||
usb_remove_phy(&phy->phy);
|
||||
pm_runtime_disable(phy->dev);
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
#ifndef __PHY_MIPI_DPHY_H_
|
||||
#define __PHY_MIPI_DPHY_H_
|
||||
|
||||
#include <video/videomode.h>
|
||||
|
||||
/**
|
||||
* struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set
|
||||
*
|
||||
@ -192,10 +190,10 @@ struct phy_configure_opts_mipi_dphy {
|
||||
/**
|
||||
* @init:
|
||||
*
|
||||
* Time, in picoseconds for the initialization period to
|
||||
* Time, in microseconds for the initialization period to
|
||||
* complete.
|
||||
*
|
||||
* Minimum value: 100000000 ps
|
||||
* Minimum value: 100 us
|
||||
*/
|
||||
unsigned int init;
|
||||
|
||||
@ -246,11 +244,11 @@ struct phy_configure_opts_mipi_dphy {
|
||||
/**
|
||||
* @wakeup:
|
||||
*
|
||||
* Time, in picoseconds, that a transmitter drives a Mark-1
|
||||
* Time, in microseconds, that a transmitter drives a Mark-1
|
||||
* state prior to a Stop state in order to initiate an exit
|
||||
* from ULPS.
|
||||
*
|
||||
* Minimum value: 1000000000 ps
|
||||
* Minimum value: 1000 us
|
||||
*/
|
||||
unsigned int wakeup;
|
||||
|
||||
@ -271,7 +269,8 @@ struct phy_configure_opts_mipi_dphy {
|
||||
/**
|
||||
* @lanes:
|
||||
*
|
||||
* Number of active data lanes used for the transmissions.
|
||||
* Number of active, consecutive, data lanes, starting from
|
||||
* lane 0, used for the transmissions.
|
||||
*/
|
||||
unsigned char lanes;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user