From patchwork Mon Apr 1 16:51:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 10880299 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2C994139A for ; Mon, 1 Apr 2019 16:52:17 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 10E892864E for ; Mon, 1 Apr 2019 16:52:17 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 043D12868C; Mon, 1 Apr 2019 16:52:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3F8362864E for ; Mon, 1 Apr 2019 16:52:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=P1hSmKGytfVn8jADRZMnwBkmu3Tf/sxbJEO2QUtD0e0=; b=o+wk3gDjTon6Uq hd+9S7Hcpi+25lvJfDG2WxSGvWUU2Tk9B1FsLOJO+8Z10Ldw3If/532F78mm1TZjKcb26RZu/3XKJ dgA+2jPk9UakHpDR/uOtMADGTSb7bwjcuirnZZ/2MTwXPICcPcVMfmPZxH7T68cKoSnurniXD0hBp 8nWGruGlUcLgzGpEti1Y5H+SIvV8stJeWgshAjt0XgKhpZRvX1G8Ej2VwvhkIH6V7rQRgrdCvQ17q FJHmXpFXdPkQSDrqhbJTec12pJMMV12dAkyXo1Zle8eNgkQB52SAaDAALSxTKTgZJGY3fy0csUyd5 8TBz7lAe7alCPdm5FH8Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1hB0AP-0007rW-Hf; Mon, 01 Apr 2019 16:52:13 +0000 Received: from relay1-d.mail.gandi.net ([217.70.183.193]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1hB0A5-0007RG-BT for linux-arm-kernel@lists.infradead.org; Mon, 01 Apr 2019 16:51:55 +0000 X-Originating-IP: 81.185.163.178 Received: from localhost.localdomain (178.163.185.81.rev.sfr.net [81.185.163.178]) (Authenticated sender: miquel.raynal@bootlin.com) by relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 35F2B24000A; Mon, 1 Apr 2019 16:51:47 +0000 (UTC) From: Miquel Raynal To: Gregory Clement , Jason Cooper , Andrew Lunn , Sebastian Hesselbarth , Kishon Vijay Abraham I Subject: [PATCH 02/15] phy: mvebu-cp110-comphy: Add SMC call support Date: Mon, 1 Apr 2019 18:51:18 +0200 Message-Id: <20190401165131.23370-3-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190401165131.23370-1-miquel.raynal@bootlin.com> References: <20190401165131.23370-1-miquel.raynal@bootlin.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190401_095153_691664_AAD62F6F X-CRM114-Status: GOOD ( 20.49 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, Antoine Tenart , Russell King , Maxime Chevallier , Nadav Haklai , Rob Herring , Thomas Petazzoni , Miquel Raynal , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Keep the exact same list of supported configurations but first try to use the firmware's implementation. If it fails, try the legacy method: Linux implementation. Signed-off-by: Miquel Raynal --- drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 200 +++++++++++++++---- 1 file changed, 164 insertions(+), 36 deletions(-) diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 3f50a1521f01..4aaf161503d6 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -5,6 +5,7 @@ * Antoine Tenart */ +#include #include #include #include @@ -115,51 +116,88 @@ #define MVEBU_COMPHY_LANES 6 #define MVEBU_COMPHY_PORTS 3 +#define COMPHY_SIP_POWER_ON 0x82000001 +#define COMPHY_SIP_POWER_OFF 0x82000002 + +/* + * A lane is described by the following bitfields: + * [ 1- 0]: COMPHY polarity invertion + * [ 2- 7]: COMPHY speed + * [ 5-11]: COMPHY port index + * [12-16]: COMPHY mode + * [17]: Clock source + */ +#define COMPHY_FW_POL_OFFSET 0 +#define COMPHY_FW_POL_MASK GENMASK(1, 0) +#define COMPHY_FW_SPEED_OFFSET 2 +#define COMPHY_FW_SPEED_MASK GENMASK(7, 2) +#define COMPHY_FW_SPEED_MAX COMPHY_FW_SPEED_MASK +#define COMPHY_FW_PORT_OFFSET 8 +#define COMPHY_FW_PORT_MASK GENMASK(11, 8) +#define COMPHY_FW_MODE_OFFSET 12 +#define COMPHY_FW_MODE_MASK GENMASK(16, 12) + +#define COMPHY_FW_PARAM_FULL(mode, port, speed, pol) \ + ((((pol) << COMPHY_FW_POL_OFFSET) & COMPHY_FW_POL_MASK) | \ + (((mode) << COMPHY_FW_MODE_OFFSET) & COMPHY_FW_MODE_MASK) | \ + (((port) << COMPHY_FW_PORT_OFFSET) & COMPHY_FW_PORT_MASK) | \ + (((speed) << COMPHY_FW_SPEED_OFFSET) & COMPHY_FW_SPEED_MASK)) + +#define COMPHY_FW_PARAM(mode, port) \ + COMPHY_FW_PARAM_FULL(mode, port, COMPHY_FW_SPEED_MAX, 0) + +#define COMPHY_FW_MODE_SGMII 0x2 /* SGMII 1G */ +#define COMPHY_FW_MODE_HS_SGMII 0x3 /* SGMII 2.5G */ +#define COMPHY_FW_MODE_SFI 0x9 /* XFI: 0x8 (is treated like SFI) */ + struct mvebu_comphy_conf { enum phy_mode mode; int submode; unsigned lane; unsigned port; u32 mux; + u32 fw_mode; }; -#define MVEBU_COMPHY_CONF(_lane, _port, _submode, _mux) \ +#define MVEBU_COMPHY_CONF(_lane, _port, _submode, _mux, _fw) \ { \ .lane = _lane, \ .port = _port, \ .mode = PHY_MODE_ETHERNET, \ .submode = _submode, \ .mux = _mux, \ + .fw_mode = _fw, \ } static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = { /* lane 0 */ - MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1), + MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), /* lane 1 */ - MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1), + MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), /* lane 2 */ - MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1), - MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1), + MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1, COMPHY_FW_MODE_SFI), /* lane 3 */ - MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2), - MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2), + MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII), + MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII), /* lane 4 */ - MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2), - MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2), - MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2), - MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1), + MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII), + MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII), + MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2, COMPHY_FW_MODE_SFI), + MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), /* lane 5 */ - MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1), + MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), }; struct mvebu_comphy_priv { void __iomem *base; struct regmap *regmap; struct device *dev; + unsigned long cp_phys; }; struct mvebu_comphy_lane { @@ -170,8 +208,18 @@ struct mvebu_comphy_lane { int port; }; -static int mvebu_comphy_get_mux(int lane, int port, - enum phy_mode mode, int submode) +static int mvebu_comphy_smc(unsigned long function, unsigned long phys, + unsigned long lane, unsigned long mode) +{ + struct arm_smccc_res res; + + arm_smccc_smc(function, phys, lane, mode, 0, 0, 0, 0, &res); + + return res.a0; +} + +static int mvebu_comphy_get_mode(bool fw_mode, int lane, int port, + enum phy_mode mode, int submode) { int i, n = ARRAY_SIZE(mvebu_comphy_cp110_modes); @@ -190,7 +238,22 @@ static int mvebu_comphy_get_mux(int lane, int port, if (i == n) return -EINVAL; - return mvebu_comphy_cp110_modes[i].mux; + if (fw_mode) + return mvebu_comphy_cp110_modes[i].fw_mode; + else + return mvebu_comphy_cp110_modes[i].mux; +} + +static inline int mvebu_comphy_get_mux(int lane, int port, + enum phy_mode mode, int submode) +{ + return mvebu_comphy_get_mode(false, lane, port, mode, submode); +} + +static inline int mvebu_comphy_get_fw_mode(int lane, int port, + enum phy_mode mode, int submode) +{ + return mvebu_comphy_get_mode(true, lane, port, mode, submode); } static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane) @@ -476,7 +539,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy) return mvebu_comphy_init_plls(lane); } -static int mvebu_comphy_power_on(struct phy *phy) +static int mvebu_comphy_power_on_legacy(struct phy *phy) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; @@ -517,6 +580,50 @@ static int mvebu_comphy_power_on(struct phy *phy) return ret; } +static int mvebu_comphy_power_on(struct phy *phy) +{ + struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); + struct mvebu_comphy_priv *priv = lane->priv; + u32 fw_param = 0; + int fw_mode; + int ret; + + fw_mode = mvebu_comphy_get_fw_mode(lane->id, lane->port, + lane->mode, lane->submode); + if (fw_mode < 0) + goto try_legacy; + + /* Try SMC flow first */ + switch (lane->mode) { + case PHY_MODE_ETHERNET: + dev_dbg(priv->dev, "set lane %d to Ethernet mode\n", lane->id); + switch (lane->submode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_10GKR: + fw_param = COMPHY_FW_PARAM(fw_mode, lane->port); + break; + default: + dev_err(priv->dev, "unsupported Ethernet mode (%d)\n", + lane->submode); + return -ENOTSUPP; + } + break; + default: + dev_err(priv->dev, "unsupported PHY mode (%d)\n", lane->mode); + return -ENOTSUPP; + } + + ret = mvebu_comphy_smc(COMPHY_SIP_POWER_ON, priv->cp_phys, lane->id, + fw_param); + if (!ret) + return ret; + +try_legacy: + /* Fallback to Linux's implementation */ + return mvebu_comphy_power_on_legacy(phy); +} + static int mvebu_comphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { @@ -528,7 +635,7 @@ static int mvebu_comphy_set_mode(struct phy *phy, if (submode == PHY_INTERFACE_MODE_1000BASEX) submode = PHY_INTERFACE_MODE_SGMII; - if (mvebu_comphy_get_mux(lane->id, lane->port, mode, submode) < 0) + if (mvebu_comphy_get_fw_mode(lane->id, lane->port, mode, submode) < 0) return -EINVAL; lane->mode = mode; @@ -536,27 +643,42 @@ static int mvebu_comphy_set_mode(struct phy *phy, return 0; } +static int mvebu_comphy_power_off_legacy(struct phy *phy) +{ + struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); + struct mvebu_comphy_priv *priv = lane->priv; + u32 val; + + val = readl(priv->base + MVEBU_COMPHY_SERDES_CFG1(lane->id)); + val &= ~(MVEBU_COMPHY_SERDES_CFG1_RESET | + MVEBU_COMPHY_SERDES_CFG1_CORE_RESET | + MVEBU_COMPHY_SERDES_CFG1_RF_RESET); + writel(val, priv->base + MVEBU_COMPHY_SERDES_CFG1(lane->id)); + + regmap_read(priv->regmap, MVEBU_COMPHY_SELECTOR, &val); + val &= ~(0xf << MVEBU_COMPHY_SELECTOR_PHY(lane->id)); + regmap_write(priv->regmap, MVEBU_COMPHY_SELECTOR, val); + + regmap_read(priv->regmap, MVEBU_COMPHY_PIPE_SELECTOR, &val); + val &= ~(0xf << MVEBU_COMPHY_PIPE_SELECTOR_PIPE(lane->id)); + regmap_write(priv->regmap, MVEBU_COMPHY_PIPE_SELECTOR, val); + + return 0; +} + static int mvebu_comphy_power_off(struct phy *phy) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; - u32 val; + int ret; - val = readl(priv->base + MVEBU_COMPHY_SERDES_CFG1(lane->id)); - val &= ~(MVEBU_COMPHY_SERDES_CFG1_RESET | - MVEBU_COMPHY_SERDES_CFG1_CORE_RESET | - MVEBU_COMPHY_SERDES_CFG1_RF_RESET); - writel(val, priv->base + MVEBU_COMPHY_SERDES_CFG1(lane->id)); + ret = mvebu_comphy_smc(COMPHY_SIP_POWER_OFF, priv->cp_phys, + lane->id, 0); + if (!ret) + return ret; - regmap_read(priv->regmap, MVEBU_COMPHY_SELECTOR, &val); - val &= ~(0xf << MVEBU_COMPHY_SELECTOR_PHY(lane->id)); - regmap_write(priv->regmap, MVEBU_COMPHY_SELECTOR, val); - - regmap_read(priv->regmap, MVEBU_COMPHY_PIPE_SELECTOR, &val); - val &= ~(0xf << MVEBU_COMPHY_PIPE_SELECTOR_PIPE(lane->id)); - regmap_write(priv->regmap, MVEBU_COMPHY_PIPE_SELECTOR, val); - - return 0; + /* Fallback to Linux's implementation */ + return mvebu_comphy_power_off_legacy(phy); } static const struct phy_ops mvebu_comphy_ops = { @@ -607,6 +729,12 @@ static int mvebu_comphy_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + /* + * Hack to retrieve a physical offset relative to this CP that will be + * given to the firmware + */ + priv->cp_phys = res->start; + for_each_available_child_of_node(pdev->dev.of_node, child) { struct mvebu_comphy_lane *lane; struct phy *phy;