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