1a043e8c7SAdrian Chadd /*- 2*718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*718cf2ccSPedro F. Giffuni * 4a043e8c7SAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 5a043e8c7SAdrian Chadd * All rights reserved. 6a043e8c7SAdrian Chadd * 7a043e8c7SAdrian Chadd * Redistribution and use in source and binary forms, with or without 8a043e8c7SAdrian Chadd * modification, are permitted provided that the following conditions 9a043e8c7SAdrian Chadd * are met: 10a043e8c7SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 11a043e8c7SAdrian Chadd * notice, this list of conditions and the following disclaimer. 12a043e8c7SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 13a043e8c7SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 14a043e8c7SAdrian Chadd * documentation and/or other materials provided with the distribution. 15a043e8c7SAdrian Chadd * 16a043e8c7SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17a043e8c7SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18a043e8c7SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19a043e8c7SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20a043e8c7SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21a043e8c7SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22a043e8c7SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23a043e8c7SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24a043e8c7SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25a043e8c7SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26a043e8c7SAdrian Chadd * SUCH DAMAGE. 27a043e8c7SAdrian Chadd * 28a043e8c7SAdrian Chadd * $FreeBSD$ 29a043e8c7SAdrian Chadd */ 30a043e8c7SAdrian Chadd 31a043e8c7SAdrian Chadd #include <sys/param.h> 32a043e8c7SAdrian Chadd #include <sys/bus.h> 33a043e8c7SAdrian Chadd #include <sys/errno.h> 34a043e8c7SAdrian Chadd #include <sys/kernel.h> 35a043e8c7SAdrian Chadd #include <sys/module.h> 36a043e8c7SAdrian Chadd #include <sys/socket.h> 37a043e8c7SAdrian Chadd #include <sys/sockio.h> 38a043e8c7SAdrian Chadd #include <sys/sysctl.h> 39a043e8c7SAdrian Chadd #include <sys/systm.h> 40a043e8c7SAdrian Chadd 41a043e8c7SAdrian Chadd #include <net/if.h> 42a043e8c7SAdrian Chadd #include <net/if_arp.h> 43a043e8c7SAdrian Chadd #include <net/ethernet.h> 44a043e8c7SAdrian Chadd #include <net/if_dl.h> 45a043e8c7SAdrian Chadd #include <net/if_media.h> 46a043e8c7SAdrian Chadd #include <net/if_types.h> 47a043e8c7SAdrian Chadd 48a043e8c7SAdrian Chadd #include <machine/bus.h> 49efce3748SRui Paulo #include <dev/iicbus/iic.h> 50a043e8c7SAdrian Chadd #include <dev/iicbus/iiconf.h> 51a043e8c7SAdrian Chadd #include <dev/iicbus/iicbus.h> 52a043e8c7SAdrian Chadd #include <dev/mii/mii.h> 53a043e8c7SAdrian Chadd #include <dev/mii/miivar.h> 5471e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 55a043e8c7SAdrian Chadd 56a043e8c7SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 57a043e8c7SAdrian Chadd 58a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h> 59a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h> 60a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h> 61a043e8c7SAdrian Chadd 62a043e8c7SAdrian Chadd #include "mdio_if.h" 63a043e8c7SAdrian Chadd #include "miibus_if.h" 64a043e8c7SAdrian Chadd #include "etherswitch_if.h" 65a043e8c7SAdrian Chadd 66a043e8c7SAdrian Chadd static inline void 67a043e8c7SAdrian Chadd arswitch_split_setpage(device_t dev, uint32_t addr, uint16_t *phy, 68a043e8c7SAdrian Chadd uint16_t *reg) 69a043e8c7SAdrian Chadd { 70a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 71a043e8c7SAdrian Chadd uint16_t page; 72a043e8c7SAdrian Chadd 7378549b94SAdrian Chadd page = (addr >> 9) & 0x1ff; 7478549b94SAdrian Chadd *phy = (addr >> 6) & 0x7; 7578549b94SAdrian Chadd *reg = (addr >> 1) & 0x1f; 76a043e8c7SAdrian Chadd 779604b6acSLuiz Otavio O Souza if (sc->page != page) { 78a043e8c7SAdrian Chadd MDIO_WRITEREG(device_get_parent(dev), 0x18, 0, page); 7978549b94SAdrian Chadd DELAY(2000); 80a043e8c7SAdrian Chadd sc->page = page; 81a043e8c7SAdrian Chadd } 829604b6acSLuiz Otavio O Souza } 83a043e8c7SAdrian Chadd 84a043e8c7SAdrian Chadd /* 85a043e8c7SAdrian Chadd * Read half a register. Some of the registers define control bits, and 86a043e8c7SAdrian Chadd * the sequence of half-word accesses matters. The register addresses 87a043e8c7SAdrian Chadd * are word-even (mod 4). 88a043e8c7SAdrian Chadd */ 89a043e8c7SAdrian Chadd static inline int 90a043e8c7SAdrian Chadd arswitch_readreg16(device_t dev, int addr) 91a043e8c7SAdrian Chadd { 92a043e8c7SAdrian Chadd uint16_t phy, reg; 93a043e8c7SAdrian Chadd 94a043e8c7SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 9578549b94SAdrian Chadd return (MDIO_READREG(device_get_parent(dev), 0x10 | phy, reg)); 9678549b94SAdrian Chadd } 9778549b94SAdrian Chadd 9878549b94SAdrian Chadd /* 9978549b94SAdrian Chadd * Write half a register. See above! 10078549b94SAdrian Chadd */ 10178549b94SAdrian Chadd static inline int 10278549b94SAdrian Chadd arswitch_writereg16(device_t dev, int addr, int data) 10378549b94SAdrian Chadd { 10478549b94SAdrian Chadd uint16_t phy, reg; 10578549b94SAdrian Chadd 10678549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 10778549b94SAdrian Chadd return (MDIO_WRITEREG(device_get_parent(dev), 0x10 | phy, reg, data)); 108a043e8c7SAdrian Chadd } 109a043e8c7SAdrian Chadd 110a043e8c7SAdrian Chadd /* 111a043e8c7SAdrian Chadd * XXX NOTE: 112a043e8c7SAdrian Chadd * 113a043e8c7SAdrian Chadd * This may not work for AR7240 series embedded switches - 114a043e8c7SAdrian Chadd * the per-PHY register space doesn't seem to be exposed. 115a043e8c7SAdrian Chadd * 116a043e8c7SAdrian Chadd * In that instance, it may be required to speak via 117a043e8c7SAdrian Chadd * the internal switch PHY MDIO bus indirection. 118a043e8c7SAdrian Chadd */ 119a043e8c7SAdrian Chadd void 120a043e8c7SAdrian Chadd arswitch_writedbg(device_t dev, int phy, uint16_t dbg_addr, 121a043e8c7SAdrian Chadd uint16_t dbg_data) 122a043e8c7SAdrian Chadd { 123a043e8c7SAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 124a043e8c7SAdrian Chadd MII_ATH_DBG_ADDR, dbg_addr); 125a043e8c7SAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 126a043e8c7SAdrian Chadd MII_ATH_DBG_DATA, dbg_data); 127a043e8c7SAdrian Chadd } 128a043e8c7SAdrian Chadd 1297e1a619dSAdrian Chadd void 1307e1a619dSAdrian Chadd arswitch_writemmd(device_t dev, int phy, uint16_t dbg_addr, 1317e1a619dSAdrian Chadd uint16_t dbg_data) 1327e1a619dSAdrian Chadd { 1337e1a619dSAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 1347e1a619dSAdrian Chadd MII_ATH_MMD_ADDR, dbg_addr); 1357e1a619dSAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 1367e1a619dSAdrian Chadd MII_ATH_MMD_DATA, dbg_data); 1377e1a619dSAdrian Chadd } 1387e1a619dSAdrian Chadd 13978549b94SAdrian Chadd static uint32_t 14078549b94SAdrian Chadd arswitch_reg_read32(device_t dev, int phy, int reg) 14178549b94SAdrian Chadd { 14278549b94SAdrian Chadd uint16_t lo, hi; 14378549b94SAdrian Chadd lo = MDIO_READREG(device_get_parent(dev), phy, reg); 14478549b94SAdrian Chadd hi = MDIO_READREG(device_get_parent(dev), phy, reg + 1); 14578549b94SAdrian Chadd 14678549b94SAdrian Chadd return (hi << 16) | lo; 14778549b94SAdrian Chadd } 14878549b94SAdrian Chadd 14978549b94SAdrian Chadd static int 15078549b94SAdrian Chadd arswitch_reg_write32(device_t dev, int phy, int reg, uint32_t value) 15178549b94SAdrian Chadd { 15278549b94SAdrian Chadd struct arswitch_softc *sc; 15378549b94SAdrian Chadd int r; 15478549b94SAdrian Chadd uint16_t lo, hi; 15578549b94SAdrian Chadd 15678549b94SAdrian Chadd sc = device_get_softc(dev); 15778549b94SAdrian Chadd lo = value & 0xffff; 15878549b94SAdrian Chadd hi = (uint16_t) (value >> 16); 15978549b94SAdrian Chadd 16078549b94SAdrian Chadd if (sc->mii_lo_first) { 16178549b94SAdrian Chadd r = MDIO_WRITEREG(device_get_parent(dev), 16278549b94SAdrian Chadd phy, reg, lo); 16378549b94SAdrian Chadd r |= MDIO_WRITEREG(device_get_parent(dev), 16478549b94SAdrian Chadd phy, reg + 1, hi); 16578549b94SAdrian Chadd } else { 16678549b94SAdrian Chadd r = MDIO_WRITEREG(device_get_parent(dev), 16778549b94SAdrian Chadd phy, reg + 1, hi); 16878549b94SAdrian Chadd r |= MDIO_WRITEREG(device_get_parent(dev), 16978549b94SAdrian Chadd phy, reg, lo); 17078549b94SAdrian Chadd } 17178549b94SAdrian Chadd 17278549b94SAdrian Chadd return r; 17378549b94SAdrian Chadd } 17478549b94SAdrian Chadd 17578549b94SAdrian Chadd int 17678549b94SAdrian Chadd arswitch_readreg(device_t dev, int addr) 177a043e8c7SAdrian Chadd { 178a043e8c7SAdrian Chadd uint16_t phy, reg; 179a043e8c7SAdrian Chadd 180a043e8c7SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 18178549b94SAdrian Chadd return arswitch_reg_read32(dev, 0x10 | phy, reg); 182a043e8c7SAdrian Chadd } 183a043e8c7SAdrian Chadd 184a043e8c7SAdrian Chadd int 18578549b94SAdrian Chadd arswitch_writereg(device_t dev, int addr, int value) 18678549b94SAdrian Chadd { 18778549b94SAdrian Chadd struct arswitch_softc *sc; 18878549b94SAdrian Chadd uint16_t phy, reg; 18978549b94SAdrian Chadd 19078549b94SAdrian Chadd sc = device_get_softc(dev); 19178549b94SAdrian Chadd 19278549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 19378549b94SAdrian Chadd return (arswitch_reg_write32(dev, 0x10 | phy, reg, value)); 19478549b94SAdrian Chadd } 19578549b94SAdrian Chadd 19678549b94SAdrian Chadd /* 19778549b94SAdrian Chadd * Read/write 16 bit values in the switch register space. 19878549b94SAdrian Chadd * 19978549b94SAdrian Chadd * Some of the registers are control registers (eg the MDIO 20078549b94SAdrian Chadd * data versus control space) and so need to be treated 20178549b94SAdrian Chadd * differently. 20278549b94SAdrian Chadd */ 20378549b94SAdrian Chadd int 204a043e8c7SAdrian Chadd arswitch_readreg_lsb(device_t dev, int addr) 205a043e8c7SAdrian Chadd { 206a043e8c7SAdrian Chadd 207a043e8c7SAdrian Chadd return (arswitch_readreg16(dev, addr)); 208a043e8c7SAdrian Chadd } 209a043e8c7SAdrian Chadd 210a043e8c7SAdrian Chadd int 211a043e8c7SAdrian Chadd arswitch_readreg_msb(device_t dev, int addr) 212a043e8c7SAdrian Chadd { 213a043e8c7SAdrian Chadd 214a043e8c7SAdrian Chadd return (arswitch_readreg16(dev, addr + 2) << 16); 215a043e8c7SAdrian Chadd } 216a043e8c7SAdrian Chadd 217a043e8c7SAdrian Chadd int 218a043e8c7SAdrian Chadd arswitch_writereg_lsb(device_t dev, int addr, int data) 219a043e8c7SAdrian Chadd { 220a043e8c7SAdrian Chadd 221a043e8c7SAdrian Chadd return (arswitch_writereg16(dev, addr, data & 0xffff)); 222a043e8c7SAdrian Chadd } 223a043e8c7SAdrian Chadd 224a043e8c7SAdrian Chadd int 225a043e8c7SAdrian Chadd arswitch_writereg_msb(device_t dev, int addr, int data) 226a043e8c7SAdrian Chadd { 227a043e8c7SAdrian Chadd 22826aff2abSAleksandr Rybalko return (arswitch_writereg16(dev, addr + 2, (data >> 16) & 0xffff)); 229a043e8c7SAdrian Chadd } 230a043e8c7SAdrian Chadd 231a043e8c7SAdrian Chadd int 232a043e8c7SAdrian Chadd arswitch_modifyreg(device_t dev, int addr, int mask, int set) 233a043e8c7SAdrian Chadd { 234a043e8c7SAdrian Chadd int value; 23578549b94SAdrian Chadd uint16_t phy, reg; 236a043e8c7SAdrian Chadd 237e6aeff0cSSepherosa Ziehau ARSWITCH_LOCK_ASSERT((struct arswitch_softc *)device_get_softc(dev), 238e6aeff0cSSepherosa Ziehau MA_OWNED); 2391b334c8bSAdrian Chadd 24078549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 24178549b94SAdrian Chadd 24278549b94SAdrian Chadd value = arswitch_reg_read32(dev, 0x10 | phy, reg); 243a043e8c7SAdrian Chadd value &= ~mask; 244a043e8c7SAdrian Chadd value |= set; 24578549b94SAdrian Chadd return (arswitch_reg_write32(dev, 0x10 | phy, reg, value)); 246a043e8c7SAdrian Chadd } 247b9f07b86SLuiz Otavio O Souza 248b9f07b86SLuiz Otavio O Souza int 249b9f07b86SLuiz Otavio O Souza arswitch_waitreg(device_t dev, int addr, int mask, int val, int timeout) 250b9f07b86SLuiz Otavio O Souza { 2511b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 252b9f07b86SLuiz Otavio O Souza int err, v; 25378549b94SAdrian Chadd uint16_t phy, reg; 25478549b94SAdrian Chadd 2551b334c8bSAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 2561b334c8bSAdrian Chadd 25778549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 258b9f07b86SLuiz Otavio O Souza 259b9f07b86SLuiz Otavio O Souza err = -1; 260b9f07b86SLuiz Otavio O Souza while (1) { 26178549b94SAdrian Chadd v = arswitch_reg_read32(dev, 0x10 | phy, reg); 262b9f07b86SLuiz Otavio O Souza v &= mask; 263b9f07b86SLuiz Otavio O Souza if (v == val) { 264b9f07b86SLuiz Otavio O Souza err = 0; 265b9f07b86SLuiz Otavio O Souza break; 266b9f07b86SLuiz Otavio O Souza } 267b9f07b86SLuiz Otavio O Souza if (!timeout) 268b9f07b86SLuiz Otavio O Souza break; 269b9f07b86SLuiz Otavio O Souza DELAY(1); 270b9f07b86SLuiz Otavio O Souza timeout--; 271b9f07b86SLuiz Otavio O Souza } 2721b334c8bSAdrian Chadd if (err != 0) { 2731b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 2741b334c8bSAdrian Chadd "%s: waitreg failed; addr=0x%08x, mask=0x%08x, val=0x%08x\n", 2751b334c8bSAdrian Chadd __func__, addr, mask, val); 2761b334c8bSAdrian Chadd } 277b9f07b86SLuiz Otavio O Souza return (err); 278b9f07b86SLuiz Otavio O Souza } 279