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> 47*efce3748SRui 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> 52a043e8c7SAdrian Chadd #include <dev/etherswitch/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 71a043e8c7SAdrian Chadd page = ((addr) >> 9) & 0xffff; 72a043e8c7SAdrian Chadd *phy = (((addr) >> 6) & 0x07) | 0x10; 73a043e8c7SAdrian 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); 77a043e8c7SAdrian Chadd sc->page = page; 78a043e8c7SAdrian Chadd } 799604b6acSLuiz Otavio O Souza } 80a043e8c7SAdrian Chadd 81a043e8c7SAdrian Chadd /* 82a043e8c7SAdrian Chadd * Read half a register. Some of the registers define control bits, and 83a043e8c7SAdrian Chadd * the sequence of half-word accesses matters. The register addresses 84a043e8c7SAdrian Chadd * are word-even (mod 4). 85a043e8c7SAdrian Chadd */ 86a043e8c7SAdrian Chadd static inline int 87a043e8c7SAdrian Chadd arswitch_readreg16(device_t dev, int addr) 88a043e8c7SAdrian Chadd { 89a043e8c7SAdrian Chadd uint16_t phy, reg; 90a043e8c7SAdrian Chadd 91a043e8c7SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 92a043e8c7SAdrian Chadd return (MDIO_READREG(device_get_parent(dev), phy, reg)); 93a043e8c7SAdrian Chadd } 94a043e8c7SAdrian Chadd 95a043e8c7SAdrian Chadd /* 96a043e8c7SAdrian Chadd * XXX NOTE: 97a043e8c7SAdrian Chadd * 98a043e8c7SAdrian Chadd * This may not work for AR7240 series embedded switches - 99a043e8c7SAdrian Chadd * the per-PHY register space doesn't seem to be exposed. 100a043e8c7SAdrian Chadd * 101a043e8c7SAdrian Chadd * In that instance, it may be required to speak via 102a043e8c7SAdrian Chadd * the internal switch PHY MDIO bus indirection. 103a043e8c7SAdrian Chadd */ 104a043e8c7SAdrian Chadd void 105a043e8c7SAdrian Chadd arswitch_writedbg(device_t dev, int phy, uint16_t dbg_addr, 106a043e8c7SAdrian Chadd uint16_t dbg_data) 107a043e8c7SAdrian Chadd { 108a043e8c7SAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 109a043e8c7SAdrian Chadd MII_ATH_DBG_ADDR, dbg_addr); 110a043e8c7SAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 111a043e8c7SAdrian Chadd MII_ATH_DBG_DATA, dbg_data); 112a043e8c7SAdrian Chadd } 113a043e8c7SAdrian Chadd 1147e1a619dSAdrian Chadd void 1157e1a619dSAdrian Chadd arswitch_writemmd(device_t dev, int phy, uint16_t dbg_addr, 1167e1a619dSAdrian Chadd uint16_t dbg_data) 1177e1a619dSAdrian Chadd { 1187e1a619dSAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 1197e1a619dSAdrian Chadd MII_ATH_MMD_ADDR, dbg_addr); 1207e1a619dSAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy, 1217e1a619dSAdrian Chadd MII_ATH_MMD_DATA, dbg_data); 1227e1a619dSAdrian Chadd } 1237e1a619dSAdrian Chadd 124a043e8c7SAdrian Chadd /* 125a043e8c7SAdrian Chadd * Write half a register 126a043e8c7SAdrian Chadd */ 127a043e8c7SAdrian Chadd static inline int 128a043e8c7SAdrian Chadd arswitch_writereg16(device_t dev, int addr, int data) 129a043e8c7SAdrian Chadd { 130a043e8c7SAdrian Chadd uint16_t phy, reg; 131a043e8c7SAdrian Chadd 132a043e8c7SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®); 133a043e8c7SAdrian Chadd return (MDIO_WRITEREG(device_get_parent(dev), phy, reg, data)); 134a043e8c7SAdrian Chadd } 135a043e8c7SAdrian Chadd 136a043e8c7SAdrian Chadd int 137a043e8c7SAdrian Chadd arswitch_readreg_lsb(device_t dev, int addr) 138a043e8c7SAdrian Chadd { 139a043e8c7SAdrian Chadd 140a043e8c7SAdrian Chadd return (arswitch_readreg16(dev, addr)); 141a043e8c7SAdrian Chadd } 142a043e8c7SAdrian Chadd 143a043e8c7SAdrian Chadd int 144a043e8c7SAdrian Chadd arswitch_readreg_msb(device_t dev, int addr) 145a043e8c7SAdrian Chadd { 146a043e8c7SAdrian Chadd 147a043e8c7SAdrian Chadd return (arswitch_readreg16(dev, addr + 2) << 16); 148a043e8c7SAdrian Chadd } 149a043e8c7SAdrian Chadd 150a043e8c7SAdrian Chadd int 151a043e8c7SAdrian Chadd arswitch_writereg_lsb(device_t dev, int addr, int data) 152a043e8c7SAdrian Chadd { 153a043e8c7SAdrian Chadd 154a043e8c7SAdrian Chadd return (arswitch_writereg16(dev, addr, data & 0xffff)); 155a043e8c7SAdrian Chadd } 156a043e8c7SAdrian Chadd 157a043e8c7SAdrian Chadd int 158a043e8c7SAdrian Chadd arswitch_writereg_msb(device_t dev, int addr, int data) 159a043e8c7SAdrian Chadd { 160a043e8c7SAdrian Chadd 16126aff2abSAleksandr Rybalko return (arswitch_writereg16(dev, addr + 2, (data >> 16) & 0xffff)); 162a043e8c7SAdrian Chadd } 163a043e8c7SAdrian Chadd 164a043e8c7SAdrian Chadd int 165a043e8c7SAdrian Chadd arswitch_readreg(device_t dev, int addr) 166a043e8c7SAdrian Chadd { 167a043e8c7SAdrian Chadd 168a043e8c7SAdrian Chadd return (arswitch_readreg_lsb(dev, addr) | 169a043e8c7SAdrian Chadd arswitch_readreg_msb(dev, addr)); 170a043e8c7SAdrian Chadd } 171a043e8c7SAdrian Chadd 172a043e8c7SAdrian Chadd int 173a043e8c7SAdrian Chadd arswitch_writereg(device_t dev, int addr, int value) 174a043e8c7SAdrian Chadd { 17526ca36d4SAdrian Chadd struct arswitch_softc *sc; 17626ca36d4SAdrian Chadd int r; 17726ca36d4SAdrian Chadd 17826ca36d4SAdrian Chadd sc = device_get_softc(dev); 179a043e8c7SAdrian Chadd 180a043e8c7SAdrian Chadd /* XXX Check the first write too? */ 18126ca36d4SAdrian Chadd if (sc->mii_lo_first) { 18226ca36d4SAdrian Chadd r = arswitch_writereg_lsb(dev, addr, value); 18326ca36d4SAdrian Chadd r |= arswitch_writereg_msb(dev, addr, value); 18426ca36d4SAdrian Chadd } else { 18526ca36d4SAdrian Chadd r = arswitch_writereg_msb(dev, addr, value); 18626ca36d4SAdrian Chadd r |= arswitch_writereg_lsb(dev, addr, value); 18726ca36d4SAdrian Chadd } 18826ca36d4SAdrian Chadd 18926ca36d4SAdrian Chadd return r; 190a043e8c7SAdrian Chadd } 191a043e8c7SAdrian Chadd 192a043e8c7SAdrian Chadd int 193a043e8c7SAdrian Chadd arswitch_modifyreg(device_t dev, int addr, int mask, int set) 194a043e8c7SAdrian Chadd { 195a043e8c7SAdrian Chadd int value; 196a043e8c7SAdrian Chadd 197a043e8c7SAdrian Chadd value = arswitch_readreg(dev, addr); 198a043e8c7SAdrian Chadd value &= ~mask; 199a043e8c7SAdrian Chadd value |= set; 200a043e8c7SAdrian Chadd return (arswitch_writereg(dev, addr, value)); 201a043e8c7SAdrian Chadd } 202b9f07b86SLuiz Otavio O Souza 203b9f07b86SLuiz Otavio O Souza int 204b9f07b86SLuiz Otavio O Souza arswitch_waitreg(device_t dev, int addr, int mask, int val, int timeout) 205b9f07b86SLuiz Otavio O Souza { 206b9f07b86SLuiz Otavio O Souza int err, v; 207b9f07b86SLuiz Otavio O Souza 208b9f07b86SLuiz Otavio O Souza err = -1; 209b9f07b86SLuiz Otavio O Souza while (1) { 210b9f07b86SLuiz Otavio O Souza v = arswitch_readreg(dev, addr); 211b9f07b86SLuiz Otavio O Souza v &= mask; 212b9f07b86SLuiz Otavio O Souza if (v == val) { 213b9f07b86SLuiz Otavio O Souza err = 0; 214b9f07b86SLuiz Otavio O Souza break; 215b9f07b86SLuiz Otavio O Souza } 216b9f07b86SLuiz Otavio O Souza if (!timeout) 217b9f07b86SLuiz Otavio O Souza break; 218b9f07b86SLuiz Otavio O Souza DELAY(1); 219b9f07b86SLuiz Otavio O Souza timeout--; 220b9f07b86SLuiz Otavio O Souza } 221b9f07b86SLuiz Otavio O Souza return (err); 222b9f07b86SLuiz Otavio O Souza } 223