mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
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:
@@ -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");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user