1 /* $OpenBSD: mvsw.c,v 1.4 2021/09/06 19:55:27 patrick Exp $ */ 2 /* 3 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/timeout.h> 22 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_misc.h> 28 #include <dev/ofw/fdt.h> 29 30 #include <dev/mii/mii.h> 31 32 /* Registers */ 33 34 /* SMI registers */ 35 #define MVSW_SMI_CMD 0x00 36 #define MVSW_SMI_CMD_BUSY 0x8000 37 #define MVSW_SMI_CMD_C45 0x0000 38 #define MVSW_SMI_CMD_C22 0x1000 39 #define MVSW_SMI_CMD_C22_READ 0x0800 40 #define MVSW_SMI_CMD_C22_WRITE 0x0400 41 #define MVSW_SMI_CMD_C45_READ 0x0c00 42 #define MVSW_SMI_CMD_C45_READINC 0x0800 43 #define MVSW_SMI_CMD_C45_WRITE 0x0400 44 #define MVSW_SMI_CMD_C45_ADDR 0x0000 45 #define MVSW_SMI_CMD_DEVAD(x) ((x) << 5) 46 #define MVSW_SMI_CMD_REGAD(x) ((x) << 0) 47 #define MVSW_SMI_DATA 0x01 48 49 #define MVSW_SMI_TIMEOUT 1600 50 51 /* Switch registers */ 52 #define MVSW_PORT(x) (0x10 + (x)) 53 #define MVSW_G2 0x1c 54 55 #define MVSW_PORT_SWITCHID 0x03 56 #define MVSW_PORT_SWITCHID_PROD_MASK 0xfff0 57 #define MVSW_PORT_SWITCHID_PROD_88E6141 0x3400 58 #define MVSW_PORT_SWITCHID_PROD_88E6341 0x3410 59 #define MVSW_PORT_SWITCHID_REV_MASK 0x000f 60 #define MVSW_PORT_CTRL 0x04 61 #define MVSW_PORT_CTRL_STATE_MASK 0x0003 62 #define MVSW_PORT_CTRL_STATE_FORWARD 0x0003 63 #define MVSW_G2_SMI_PHY_CMD 0x18 64 #define MVSW_G2_SMI_PHY_DATA 0x19 65 66 /* SERDES registers */ 67 #define MVSW_SERDES(x) (0x10 + (x)) 68 #define MVSW_SERDES_BMCR (0x2000 + MII_BMCR) 69 70 /* XXX #include <dev/mii/mdio.h> */ 71 #define MDIO_MMD_PHYXS 4 72 73 struct mvsw_softc { 74 struct device sc_dev; 75 76 struct mii_bus *sc_mdio; 77 int sc_reg; 78 }; 79 80 int mvsw_match(struct device *, void *, void *); 81 void mvsw_attach(struct device *, struct device *, void *); 82 83 struct cfattach mvsw_ca = { 84 sizeof (struct mvsw_softc), mvsw_match, mvsw_attach 85 }; 86 87 struct cfdriver mvsw_cd = { 88 NULL, "mvsw", DV_DULL 89 }; 90 91 int mvsw_smi_read(struct mvsw_softc *, int, int); 92 void mvsw_smi_write(struct mvsw_softc *, int, int, int); 93 int mvsw_phy_read(struct mvsw_softc *, int, int); 94 void mvsw_phy_write(struct mvsw_softc *, int, int, int); 95 int mvsw_serdes_read(struct mvsw_softc *, int, int, int); 96 void mvsw_serdes_write(struct mvsw_softc *, int, int, int, int); 97 98 void mvsw_port_enable(struct mvsw_softc *, int); 99 void mvsw_phy_enable(struct mvsw_softc *, int); 100 void mvsw_serdes_enable(struct mvsw_softc *, int); 101 102 int 103 mvsw_match(struct device *parent, void *match, void *aux) 104 { 105 struct fdt_attach_args *faa = aux; 106 107 return OF_is_compatible(faa->fa_node, "marvell,mv88e6085"); 108 } 109 110 void 111 mvsw_attach(struct device *parent, struct device *self, void *aux) 112 { 113 struct mvsw_softc *sc = (struct mvsw_softc *)self; 114 struct fdt_attach_args *faa = aux; 115 int ports, port, node; 116 uint32_t phy; 117 uint16_t swid; 118 119 if (faa->fa_nreg < 1) { 120 printf(": no registers\n"); 121 return; 122 } 123 124 sc->sc_reg = faa->fa_reg[0].addr; 125 printf(" phy %d", sc->sc_reg); 126 127 sc->sc_mdio = mii_bynode(OF_parent(faa->fa_node)); 128 if (sc->sc_mdio == NULL) { 129 printf(": can't find mdio bus\n"); 130 return; 131 } 132 133 swid = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); 134 switch (swid & MVSW_PORT_SWITCHID_PROD_MASK) { 135 case MVSW_PORT_SWITCHID_PROD_88E6141: 136 printf(": 88E6141"); 137 break; 138 case MVSW_PORT_SWITCHID_PROD_88E6341: 139 printf(": 88E6341"); 140 break; 141 default: 142 printf(": unknown product 0x%04x\n", 143 swid & MVSW_PORT_SWITCHID_PROD_MASK); 144 return; 145 } 146 printf(" rev %d\n", swid & MVSW_PORT_SWITCHID_REV_MASK); 147 148 ports = OF_getnodebyname(faa->fa_node, "ports"); 149 if (ports == 0) 150 return; 151 for (port = OF_child(ports); port; port = OF_peer(port)) { 152 phy = OF_getpropint(port, "phy-handle", 0); 153 node = OF_getnodebyphandle(phy); 154 if (node) 155 mvsw_phy_enable(sc, node); 156 else 157 mvsw_serdes_enable(sc, port); 158 159 mvsw_port_enable(sc, port); 160 } 161 } 162 163 static inline int 164 mvsw_read(struct mvsw_softc *sc, int reg) 165 { 166 struct mii_bus *md = sc->sc_mdio; 167 return md->md_readreg(md->md_cookie, sc->sc_reg, reg); 168 } 169 170 static inline void 171 mvsw_write(struct mvsw_softc *sc, int reg, int val) 172 { 173 struct mii_bus *md = sc->sc_mdio; 174 md->md_writereg(md->md_cookie, sc->sc_reg, reg, val); 175 } 176 177 int 178 mvsw_smi_wait(struct mvsw_softc *sc) 179 { 180 int i; 181 182 for (i = 0; i < MVSW_SMI_TIMEOUT; i++) { 183 if ((mvsw_read(sc, MVSW_SMI_CMD) & MVSW_SMI_CMD_BUSY) == 0) 184 return 0; 185 delay(10); 186 } 187 188 printf("%s: SMI timeout\n", sc->sc_dev.dv_xname); 189 return ETIMEDOUT; 190 } 191 192 int 193 mvsw_smi_read(struct mvsw_softc *sc, int phy, int reg) 194 { 195 if (mvsw_smi_wait(sc)) 196 return -1; 197 198 mvsw_write(sc, MVSW_SMI_CMD, MVSW_SMI_CMD_BUSY | 199 MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) | 200 MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_READ); 201 202 if (mvsw_smi_wait(sc)) 203 return -1; 204 205 return mvsw_read(sc, MVSW_SMI_DATA); 206 } 207 208 void 209 mvsw_smi_write(struct mvsw_softc *sc, int phy, int reg, int val) 210 { 211 if (mvsw_smi_wait(sc)) 212 return; 213 214 mvsw_write(sc, MVSW_SMI_DATA, val); 215 mvsw_write(sc, MVSW_SMI_CMD, MVSW_SMI_CMD_BUSY | 216 MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) | 217 MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_WRITE); 218 219 mvsw_smi_wait(sc); 220 } 221 222 int 223 mvsw_phy_wait(struct mvsw_softc *sc) 224 { 225 int i; 226 227 for (i = 0; i < MVSW_SMI_TIMEOUT; i++) { 228 if ((mvsw_smi_read(sc, MVSW_G2, 229 MVSW_G2_SMI_PHY_CMD) & MVSW_SMI_CMD_BUSY) == 0) 230 return 0; 231 delay(10); 232 } 233 234 printf("%s: SMI PHY timeout\n", sc->sc_dev.dv_xname); 235 return ETIMEDOUT; 236 } 237 238 int 239 mvsw_phy_read(struct mvsw_softc *sc, int phy, int reg) 240 { 241 if (mvsw_phy_wait(sc)) 242 return -1; 243 244 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD, 245 MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) | 246 MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_READ); 247 248 if (mvsw_phy_wait(sc)) 249 return -1; 250 251 return mvsw_smi_read(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA); 252 } 253 254 void 255 mvsw_phy_write(struct mvsw_softc *sc, int phy, int reg, int val) 256 { 257 if (mvsw_phy_wait(sc)) 258 return; 259 260 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, val); 261 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD, 262 MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) | 263 MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_WRITE); 264 } 265 266 int 267 mvsw_serdes_read(struct mvsw_softc *sc, int port, int dev, int addr) 268 { 269 if (mvsw_phy_wait(sc)) 270 return -1; 271 272 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, addr); 273 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD, 274 MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) | 275 MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_ADDR); 276 277 if (mvsw_phy_wait(sc)) 278 return -1; 279 280 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD, 281 MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) | 282 MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_READ); 283 284 if (mvsw_phy_wait(sc)) 285 return -1; 286 287 return mvsw_smi_read(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA); 288 } 289 290 void 291 mvsw_serdes_write(struct mvsw_softc *sc, int port, int dev, int addr, int val) 292 { 293 if (mvsw_phy_wait(sc)) 294 return; 295 296 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, addr); 297 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD, 298 MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) | 299 MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_ADDR); 300 301 if (mvsw_phy_wait(sc)) 302 return; 303 304 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, val); 305 mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD, 306 MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) | 307 MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_WRITE); 308 } 309 310 void 311 mvsw_port_enable(struct mvsw_softc *sc, int node) 312 { 313 uint16_t val; 314 int port; 315 316 port = OF_getpropint(node, "reg", -1); 317 if (port == -1) 318 return; 319 320 /* Enable port. */ 321 val = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL); 322 val &= ~MVSW_PORT_CTRL_STATE_MASK; 323 val |= MVSW_PORT_CTRL_STATE_FORWARD; 324 mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL, val); 325 } 326 327 void 328 mvsw_phy_enable(struct mvsw_softc *sc, int node) 329 { 330 uint16_t val; 331 int phy; 332 333 phy = OF_getpropint(node, "reg", -1); 334 if (phy == -1) 335 return; 336 337 /* Power-up PHY. */ 338 val = mvsw_phy_read(sc, phy, MII_BMCR); 339 val &= ~BMCR_PDOWN; 340 mvsw_phy_write(sc, phy, MII_BMCR, val); 341 } 342 343 void 344 mvsw_serdes_enable(struct mvsw_softc *sc, int node) 345 { 346 uint16_t val; 347 int port; 348 349 port = OF_getpropint(node, "reg", -1); 350 if (port == -1) 351 return; 352 353 /* Power-up SERDES. */ 354 val = mvsw_serdes_read(sc, MVSW_SERDES(port), 355 MDIO_MMD_PHYXS, MVSW_SERDES_BMCR); 356 val &= ~BMCR_PDOWN; 357 val |= BMCR_AUTOEN; 358 mvsw_serdes_write(sc, MVSW_SERDES(port), 359 MDIO_MMD_PHYXS, MVSW_SERDES_BMCR, val); 360 } 361