1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 Russell King, Deep Blue Solutions Ltd. 4 * 5 * Partly derived from CP110 comphy driver by Antoine Tenart 6 * <antoine.tenart@bootlin.com> 7 */ 8 #include <linux/delay.h> 9 #include <linux/iopoll.h> 10 #include <linux/module.h> 11 #include <linux/phy/phy.h> 12 #include <linux/phy.h> 13 #include <linux/platform_device.h> 14 15 #define MAX_A38X_COMPHY 6 16 #define MAX_A38X_PORTS 3 17 18 #define COMPHY_CFG1 0x00 19 #define COMPHY_CFG1_GEN_TX(x) ((x) << 26) 20 #define COMPHY_CFG1_GEN_TX_MSK COMPHY_CFG1_GEN_TX(15) 21 #define COMPHY_CFG1_GEN_RX(x) ((x) << 22) 22 #define COMPHY_CFG1_GEN_RX_MSK COMPHY_CFG1_GEN_RX(15) 23 #define GEN_SGMII_1_25GBPS 6 24 #define GEN_SGMII_3_125GBPS 8 25 26 #define COMPHY_STAT1 0x18 27 #define COMPHY_STAT1_PLL_RDY_TX BIT(3) 28 #define COMPHY_STAT1_PLL_RDY_RX BIT(2) 29 30 #define COMPHY_SELECTOR 0xfc 31 32 struct a38x_comphy; 33 34 struct a38x_comphy_lane { 35 void __iomem *base; 36 struct a38x_comphy *priv; 37 unsigned int n; 38 39 int port; 40 }; 41 42 struct a38x_comphy { 43 void __iomem *base; 44 struct device *dev; 45 struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; 46 }; 47 48 static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = { 49 { 0, 0, 0 }, 50 { 4, 5, 0 }, 51 { 0, 4, 0 }, 52 { 0, 0, 4 }, 53 { 0, 3, 0 }, 54 { 0, 0, 3 }, 55 }; 56 57 static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane, 58 unsigned int offset, u32 mask, u32 value) 59 { 60 u32 val; 61 62 val = readl_relaxed(lane->base + offset) & ~mask; 63 writel(val | value, lane->base + offset); 64 } 65 66 static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane, 67 unsigned int gen_tx, unsigned int gen_rx) 68 { 69 a38x_comphy_set_reg(lane, COMPHY_CFG1, 70 COMPHY_CFG1_GEN_TX_MSK | COMPHY_CFG1_GEN_RX_MSK, 71 COMPHY_CFG1_GEN_TX(gen_tx) | 72 COMPHY_CFG1_GEN_RX(gen_rx)); 73 } 74 75 static int a38x_comphy_poll(struct a38x_comphy_lane *lane, 76 unsigned int offset, u32 mask, u32 value) 77 { 78 u32 val; 79 int ret; 80 81 ret = readl_relaxed_poll_timeout_atomic(lane->base + offset, val, 82 (val & mask) == value, 83 1000, 150000); 84 85 if (ret) 86 dev_err(lane->priv->dev, 87 "comphy%u: timed out waiting for status\n", lane->n); 88 89 return ret; 90 } 91 92 /* 93 * We only support changing the speed for comphys configured for GBE. 94 * Since that is all we do, we only poll for PLL ready status. 95 */ 96 static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) 97 { 98 struct a38x_comphy_lane *lane = phy_get_drvdata(phy); 99 unsigned int gen; 100 101 if (mode != PHY_MODE_ETHERNET) 102 return -EINVAL; 103 104 switch (sub) { 105 case PHY_INTERFACE_MODE_SGMII: 106 case PHY_INTERFACE_MODE_1000BASEX: 107 gen = GEN_SGMII_1_25GBPS; 108 break; 109 110 case PHY_INTERFACE_MODE_2500BASEX: 111 gen = GEN_SGMII_3_125GBPS; 112 break; 113 114 default: 115 return -EINVAL; 116 } 117 118 a38x_comphy_set_speed(lane, gen, gen); 119 120 return a38x_comphy_poll(lane, COMPHY_STAT1, 121 COMPHY_STAT1_PLL_RDY_TX | 122 COMPHY_STAT1_PLL_RDY_RX, 123 COMPHY_STAT1_PLL_RDY_TX | 124 COMPHY_STAT1_PLL_RDY_RX); 125 } 126 127 static const struct phy_ops a38x_comphy_ops = { 128 .set_mode = a38x_comphy_set_mode, 129 .owner = THIS_MODULE, 130 }; 131 132 static struct phy *a38x_comphy_xlate(struct device *dev, 133 struct of_phandle_args *args) 134 { 135 struct a38x_comphy_lane *lane; 136 struct phy *phy; 137 u32 val; 138 139 if (WARN_ON(args->args[0] >= MAX_A38X_PORTS)) 140 return ERR_PTR(-EINVAL); 141 142 phy = of_phy_simple_xlate(dev, args); 143 if (IS_ERR(phy)) 144 return phy; 145 146 lane = phy_get_drvdata(phy); 147 if (lane->port >= 0) 148 return ERR_PTR(-EBUSY); 149 150 lane->port = args->args[0]; 151 152 val = readl_relaxed(lane->priv->base + COMPHY_SELECTOR); 153 val = (val >> (4 * lane->n)) & 0xf; 154 155 if (!gbe_mux[lane->n][lane->port] || 156 val != gbe_mux[lane->n][lane->port]) { 157 dev_warn(lane->priv->dev, 158 "comphy%u: not configured for GBE\n", lane->n); 159 phy = ERR_PTR(-EINVAL); 160 } 161 162 return phy; 163 } 164 165 static int a38x_comphy_probe(struct platform_device *pdev) 166 { 167 struct phy_provider *provider; 168 struct device_node *child; 169 struct a38x_comphy *priv; 170 struct resource *res; 171 void __iomem *base; 172 173 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 174 if (!priv) 175 return -ENOMEM; 176 177 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 178 base = devm_ioremap_resource(&pdev->dev, res); 179 if (IS_ERR(base)) 180 return PTR_ERR(base); 181 182 priv->dev = &pdev->dev; 183 priv->base = base; 184 185 for_each_available_child_of_node(pdev->dev.of_node, child) { 186 struct phy *phy; 187 int ret; 188 u32 val; 189 190 ret = of_property_read_u32(child, "reg", &val); 191 if (ret < 0) { 192 dev_err(&pdev->dev, "missing 'reg' property (%d)\n", 193 ret); 194 continue; 195 } 196 197 if (val >= MAX_A38X_COMPHY || priv->lane[val].base) { 198 dev_err(&pdev->dev, "invalid 'reg' property\n"); 199 continue; 200 } 201 202 phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops); 203 if (IS_ERR(phy)) { 204 of_node_put(child); 205 return PTR_ERR(phy); 206 } 207 208 priv->lane[val].base = base + 0x28 * val; 209 priv->lane[val].priv = priv; 210 priv->lane[val].n = val; 211 priv->lane[val].port = -1; 212 phy_set_drvdata(phy, &priv->lane[val]); 213 } 214 215 dev_set_drvdata(&pdev->dev, priv); 216 217 provider = devm_of_phy_provider_register(&pdev->dev, a38x_comphy_xlate); 218 219 return PTR_ERR_OR_ZERO(provider); 220 } 221 222 static const struct of_device_id a38x_comphy_of_match_table[] = { 223 { .compatible = "marvell,armada-380-comphy" }, 224 { }, 225 }; 226 MODULE_DEVICE_TABLE(of, a38x_comphy_of_match_table); 227 228 static struct platform_driver a38x_comphy_driver = { 229 .probe = a38x_comphy_probe, 230 .driver = { 231 .name = "armada-38x-comphy", 232 .of_match_table = a38x_comphy_of_match_table, 233 }, 234 }; 235 module_platform_driver(a38x_comphy_driver); 236 237 MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>"); 238 MODULE_DESCRIPTION("Common PHY driver for Armada 38x SoCs"); 239 MODULE_LICENSE("GPL v2"); 240