From patchwork Sun Apr 19 15:48:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 238024 List-Id: U-Boot discussion From: marek.behun at nic.cz (=?UTF-8?q?Marek=20Beh=C3=BAn?=) Date: Sun, 19 Apr 2020 17:48:42 +0200 Subject: [PATCH u-boot-marvell 03/11] phy: port Armada 37xx COMPHY from Linux In-Reply-To: <20200419154850.25868-1-marek.behun@nic.cz> References: <20200419154850.25868-1-marek.behun@nic.cz> Message-ID: <20200419154850.25868-4-marek.behun@nic.cz> This patch ports the A3700 COMPHY driver from Linux. Signed-off-by: Marek Beh?n --- drivers/phy/Kconfig | 8 + drivers/phy/Makefile | 1 + drivers/phy/phy-mvebu-a3700-comphy.c | 315 +++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/phy/phy-mvebu-a3700-comphy.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index f655f1af3c..04307c5876 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -217,6 +217,14 @@ config PHY_MTK_TPHY multi-ports is first version, otherwise is second veriosn, so you can easily distinguish them by banks layout. +config PHY_MVEBU_A3700_COMPHY + tristate "Marvell A3700 comphy driver" + depends on PHY && ARMADA_3700 + 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 bool "Marvell A3700 UTMI driver" depends on PHY && ARMADA_3700 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index d76e26dd83..0ee88b1251 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -24,4 +24,5 @@ obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o +obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o diff --git a/drivers/phy/phy-mvebu-a3700-comphy.c b/drivers/phy/phy-mvebu-a3700-comphy.c new file mode 100644 index 0000000000..f657626f65 --- /dev/null +++ b/drivers/phy/phy-mvebu-a3700-comphy.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell + * + * Authors: + * Evan Wang + * Miqu?l Raynal + * + * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. + * SMC call initial support done by Grzegorz Jaszczyk. + * + * Ported from Linux to U-Boot by Marek Behun + */ + +#include +#include +#include +#include +#include +#include +#include + +#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_NOT_SUPPORTED (-1) + +#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) (COMPHY_FW_NET(mode, idx, speed)) + +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_NONE, _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_SGMII_2500, 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_SGMII_2500, 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 { + unsigned int id; + enum phy_mode mode; + int submode; + int port; +}; + +struct mvebu_a3700_comphy { + struct mvebu_a3700_comphy_lane lanes[MVEBU_A3700_COMPHY_LANES]; +}; + +static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, + unsigned long mode) +{ + struct pt_regs regs; + + regs.regs[0] = function; + regs.regs[1] = lane; + regs.regs[2] = mode; + + smc_call(®s); + + return regs.regs[0]; +} + +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 && + (mode != PHY_MODE_ETHERNET || + mvebu_a3700_comphy_modes[i].submode == submode)) + break; + } + + if (i == n) + return -EINVAL; + + return mvebu_a3700_comphy_modes[i].fw_mode; +} + +static inline struct mvebu_a3700_comphy_lane *phy_to_lane(struct phy *phy) +{ + struct mvebu_a3700_comphy *priv = dev_get_priv(phy->dev); + + return &priv->lanes[phy->id]; +} + +static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct mvebu_a3700_comphy_lane *lane = phy_to_lane(phy); + int fw_mode; + + 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_to_lane(phy); + u32 fw_param; + int fw_mode; + int ret; + + 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_SGMII_2500: + 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); + break; + default: + dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); + return -ENOTSUPP; + } + + ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); + if (ret == COMPHY_FW_NOT_SUPPORTED) + dev_err(lane->dev, + "unsupported SMC call, try updating your firmware\n"); + + return ret; +} + +static int mvebu_a3700_comphy_power_off(struct phy *phy) +{ + struct mvebu_a3700_comphy_lane *lane = phy_to_lane(phy); + + return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); +} + +static int mvebu_a3700_comphy_xlate(struct phy *phy, + struct ofnode_phandle_args *args) +{ + struct mvebu_a3700_comphy_lane *lane; + + if (WARN_ON(args->args_count > 2 || + args->args[0] >= MVEBU_A3700_COMPHY_LANES || + args->args[1] >= MVEBU_A3700_COMPHY_PORTS)) + return -EINVAL; + + phy->id = args->args[0]; + lane = phy_to_lane(phy); + lane->port = args->args[1]; + + return 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, + .of_xlate = mvebu_a3700_comphy_xlate, +}; + +static int mvebu_a3700_comphy_probe(struct udevice *dev) +{ + struct mvebu_a3700_comphy *priv = dev_get_priv(dev); + ofnode child; + + dev_for_each_subnode(child, dev) { + struct mvebu_a3700_comphy_lane *lane; + u32 lane_id; + int ret; + + ret = ofnode_read_u32(child, "reg", &lane_id); + if (ret < 0) { + dev_err(parent, "missing 'reg' property (%d)\n", ret); + continue; + } + + if (lane_id >= MVEBU_A3700_COMPHY_LANES) { + dev_err(parent, "invalid 'reg' property\n"); + continue; + } + + lane = &priv->lanes[lane_id]; + lane->mode = PHY_MODE_INVALID; + lane->submode = PHY_INTERFACE_MODE_NONE; + lane->id = lane_id; + lane->port = -1; + } + + return 0; +} + +static const struct udevice_id mvebu_a3700_comphy_of_match_table[] = { + { .compatible = "marvell,comphy-a3700" }, + { }, +}; + +U_BOOT_DRIVER(mvebu_a3700_comphy) = { + .name = "mvebu-a3700-comphy", + .id = UCLASS_PHY, + .of_match = mvebu_a3700_comphy_of_match_table, + .ops = &mvebu_a3700_comphy_ops, + .probe = mvebu_a3700_comphy_probe, + .priv_auto_alloc_size = sizeof(struct mvebu_a3700_comphy), +};