From 80d8223ed4145841dafc18d1ad22c548ce5e0ff4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 29 Jun 2020 23:16:21 +0100 Subject: phy: armada-38x: further augmentation of setup Further augmentation of the comphy setup. Signed-off-by: Russell King --- arch/arm/boot/dts/armada-38x.dtsi | 5 +- drivers/phy/marvell/phy-armada38x-comphy.c | 147 +++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 20 deletions(-) diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi index 9b1a24cc5e91..404943b5cdac 100644 --- a/arch/arm/boot/dts/armada-38x.dtsi +++ b/arch/arm/boot/dts/armada-38x.dtsi @@ -342,8 +342,9 @@ comphy: phy@18300 { compatible = "marvell,armada-380-comphy"; - reg-names = "comphy", "conf"; - reg = <0x18300 0x100>, <0x18460 4>; + reg-names = "comphy", "pipe", "conf"; + reg = <0x18300 0x100>, <0xa0000 0x3000>, + <0x18460 4>; #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 0fe408964334..0e86f484269c 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -15,32 +15,49 @@ #define MAX_A38X_COMPHY 6 #define MAX_A38X_PORTS 3 +/* Common PHY registers */ +#define COMPHY_REG_SIZE 0x28 #define COMPHY_CFG1 0x00 +#define COMPHY_CFG1_RX_INIT BIT(30) #define COMPHY_CFG1_GEN_TX(x) ((x) << 26) #define COMPHY_CFG1_GEN_TX_MSK COMPHY_CFG1_GEN_TX(15) #define COMPHY_CFG1_GEN_RX(x) ((x) << 22) #define COMPHY_CFG1_GEN_RX_MSK COMPHY_CFG1_GEN_RX(15) +#define COMPHY_CFG1_POWER_UP_TX BIT(18) +#define COMPHY_CFG1_POWER_UP_RX BIT(17) +#define COMPHY_CFG1_POWER_UP_PLL BIT(16) #define GEN_SGMII_1_25GBPS 6 #define GEN_SGMII_3_125GBPS 8 #define COMPHY_STAT1 0x18 #define COMPHY_STAT1_PLL_RDY_TX BIT(3) #define COMPHY_STAT1_PLL_RDY_RX BIT(2) +#define COMPHY_STAT1_RX_INIT_DONE BIT(0) #define COMPHY_SELECTOR 0xfc +/* Common PHY and Pipe Registers */ +#define PIPE_REG_SIZE 0x800 +#define PIPE_POWER_CTRL 0x148 +#define PIPE_POWER_CTRL_SFT_RST_NO_REG BIT(10) +/* u-boot sets bit 0 during comphy initialisation, but it is undocumented */ +#define PIPE_POWER_CTRL_BIT0 BIT(0) + struct a38x_comphy; struct a38x_comphy_lane { void __iomem *base; + void __iomem *pipe; struct a38x_comphy *priv; unsigned int n; int port; + u8 gen; }; struct a38x_comphy { void __iomem *base; + void __iomem *pipe; void __iomem *conf; struct device *dev; struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; @@ -88,6 +105,7 @@ static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane, COMPHY_CFG1_GEN_RX(gen_rx)); } +/* Poll every 1ms for 150ms for a status */ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, unsigned int offset, u32 mask, u32 value) { @@ -105,6 +123,97 @@ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, return ret; } +static int a38x_comphy_power_up(struct a38x_comphy_lane *lane) +{ + /* Power up TX, RX and PLL */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, + COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | + COMPHY_CFG1_POWER_UP_PLL); + + /* Wait for power up */ + return a38x_comphy_poll(lane, COMPHY_STAT1, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX); +} + +static int a38x_comphy_rx_init(struct a38x_comphy_lane *lane) +{ + int ret; + + /* Perform RX init */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, COMPHY_CFG1_RX_INIT); + + /* Wait for RX init done */ + ret = a38x_comphy_poll(lane, COMPHY_STAT1, COMPHY_STAT1_RX_INIT_DONE, + COMPHY_STAT1_RX_INIT_DONE); + + /* Clear RX init */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, COMPHY_CFG1_RX_INIT, 0); + + return ret; +} + +static int a38x_comphy_power_off(struct phy *phy) +{ + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + + a38x_set_conf(lane, false); + + /* Soft reset */ + if (lane->pipe) { + u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; + u32 val; + + val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); + writel(val | rst, lane->pipe + PIPE_POWER_CTRL); + } + + a38x_comphy_set_reg(lane, COMPHY_CFG1, + COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | + COMPHY_CFG1_POWER_UP_PLL, 0); + + return 0; +} + +static int a38x_comphy_power_on(struct phy *phy) +{ + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + int ret; + + /* Program the GEN settings */ + a38x_comphy_set_speed(lane, lane->gen, lane->gen); + + /* Soft reset */ + if (lane->pipe) { + u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; + u32 val; + + val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); + writel(val | rst, lane->pipe + PIPE_POWER_CTRL); + writel(val & ~rst, lane->pipe + PIPE_POWER_CTRL); + } + + /* Power up TX, RX and PLL and wait */ + ret = a38x_comphy_power_up(lane); + if (ret) + goto fail; + + /* Perform RX init */ + ret = a38x_comphy_rx_init(lane); + if (ret) + goto fail; + + a38x_set_conf(lane, true); + + return 0; + +fail: + a38x_comphy_power_off(phy); + return ret; +} + /* * We only support changing the speed for comphys configured for GBE. * Since that is all we do, we only poll for PLL ready status. @@ -113,7 +222,6 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) { struct a38x_comphy_lane *lane = phy_get_drvdata(phy); unsigned int gen; - int ret; if (mode != PHY_MODE_ETHERNET) return -EINVAL; @@ -132,23 +240,14 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) return -EINVAL; } - a38x_set_conf(lane, false); - - a38x_comphy_set_speed(lane, gen, gen); - - ret = a38x_comphy_poll(lane, COMPHY_STAT1, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX); - - if (ret == 0) - a38x_set_conf(lane, true); + lane->gen = gen; - return ret; + return 0; } static const struct phy_ops a38x_comphy_ops = { + .power_on = a38x_comphy_power_on, + .power_off = a38x_comphy_power_off, .set_mode = a38x_comphy_set_mode, .owner = THIS_MODULE, }; @@ -193,6 +292,7 @@ static int a38x_comphy_probe(struct platform_device *pdev) struct a38x_comphy *priv; struct resource *res; void __iomem *base; + void __iomem *pipe = NULL; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -202,8 +302,16 @@ static int a38x_comphy_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipe"); + if (res) { + pipe = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + } + priv->dev = &pdev->dev; priv->base = base; + priv->pipe = pipe; /* Optional */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); @@ -230,16 +338,19 @@ static int a38x_comphy_probe(struct platform_device *pdev) continue; } + priv->lane[val].base = base + COMPHY_REG_SIZE * val; + if (pipe) + priv->lane[val].pipe = pipe + PIPE_REG_SIZE * val; + priv->lane[val].priv = priv; + priv->lane[val].n = val; + priv->lane[val].port = -1; + phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops); if (IS_ERR(phy)) { of_node_put(child); return PTR_ERR(phy); } - priv->lane[val].base = base + 0x28 * val; - priv->lane[val].priv = priv; - priv->lane[val].n = val; - priv->lane[val].port = -1; phy_set_drvdata(phy, &priv->lane[val]); } -- cgit