diff options
Diffstat (limited to 'target/linux/mvebu/patches-4.19/542-phy-add-A3700-COMPHY-support.patch')
-rw-r--r-- | target/linux/mvebu/patches-4.19/542-phy-add-A3700-COMPHY-support.patch | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-4.19/542-phy-add-A3700-COMPHY-support.patch b/target/linux/mvebu/patches-4.19/542-phy-add-A3700-COMPHY-support.patch new file mode 100644 index 0000000000..0964da03a8 --- /dev/null +++ b/target/linux/mvebu/patches-4.19/542-phy-add-A3700-COMPHY-support.patch @@ -0,0 +1,381 @@ +From 9695375a3f4a604406f2e61f2b735eca1de931ed Mon Sep 17 00:00:00 2001 +From: Miquel Raynal <miquel.raynal@bootlin.com> +Date: Tue, 8 Jan 2019 17:31:20 +0100 +Subject: [PATCH] phy: add A3700 COMPHY support + +Add a driver to support COMPHY, a hardware block providing shared +serdes PHYs on Marvell Armada 3700. This driver uses SMC calls and +rely on having an up-to-date firmware. + +SATA, PCie and USB3 host mode have been tested successfully with an +ESPRESSObin. (HS)SGMII mode cannot be tested with this platform. + +Evan worked on the original driver structure and Grzegorz on the SMC +calls rework. The structure of this driver has been copied from +Antoine Tenart work on CP110 COMPHY driver. + +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Co-developed-by: Evan Wang <xswang@marvell.com> +Signed-off-by: Evan Wang <xswang@marvell.com> +Co-developed-by: Grzegorz Jaszczyk <jaz@semihalf.com> +Signed-off-by: Grzegorz Jaszczyk <jaz@semihalf.com> +Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> +--- + drivers/phy/marvell/Kconfig | 12 + + drivers/phy/marvell/Makefile | 1 + + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 318 +++++++++++++++++++++++++++ + 3 files changed, 331 insertions(+) + create mode 100644 drivers/phy/marvell/phy-mvebu-a3700-comphy.c + +--- a/drivers/phy/marvell/Kconfig ++++ b/drivers/phy/marvell/Kconfig +@@ -21,6 +21,18 @@ 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_CP110_COMPHY + tristate "Marvell CP110 comphy driver" + depends on ARCH_MVEBU || COMPILE_TEST +--- a/drivers/phy/marvell/Makefile ++++ b/drivers/phy/marvell/Makefile +@@ -2,6 +2,7 @@ + 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_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 +--- /dev/null ++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +@@ -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"); |