1a043e8c7SAdrian Chadd /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro 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
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>
5271e8eac4SAdrian 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
arswitch_split_setpage(device_t dev,uint32_t addr,uint16_t * phy,uint16_t * reg)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
arswitch_readreg16(device_t dev,int addr)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
arswitch_writereg16(device_t dev,int addr,int data)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 void
arswitch_writedbg(device_t dev,int phy,uint16_t dbg_addr,uint16_t dbg_data)109a043e8c7SAdrian Chadd arswitch_writedbg(device_t dev, int phy, uint16_t dbg_addr,
110a043e8c7SAdrian Chadd uint16_t dbg_data)
111a043e8c7SAdrian Chadd {
112a043e8c7SAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy,
113a043e8c7SAdrian Chadd MII_ATH_DBG_ADDR, dbg_addr);
114a043e8c7SAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy,
115a043e8c7SAdrian Chadd MII_ATH_DBG_DATA, dbg_data);
116a043e8c7SAdrian Chadd }
117a043e8c7SAdrian Chadd
1187e1a619dSAdrian Chadd void
arswitch_writemmd(device_t dev,int phy,uint16_t dbg_addr,uint16_t dbg_data)1197e1a619dSAdrian Chadd arswitch_writemmd(device_t dev, int phy, uint16_t dbg_addr,
1207e1a619dSAdrian Chadd uint16_t dbg_data)
1217e1a619dSAdrian Chadd {
1227e1a619dSAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy,
1237e1a619dSAdrian Chadd MII_ATH_MMD_ADDR, dbg_addr);
1247e1a619dSAdrian Chadd (void) MDIO_WRITEREG(device_get_parent(dev), phy,
1257e1a619dSAdrian Chadd MII_ATH_MMD_DATA, dbg_data);
1267e1a619dSAdrian Chadd }
1277e1a619dSAdrian Chadd
12878549b94SAdrian Chadd static uint32_t
arswitch_reg_read32(device_t dev,int phy,int reg)12978549b94SAdrian Chadd arswitch_reg_read32(device_t dev, int phy, int reg)
13078549b94SAdrian Chadd {
13178549b94SAdrian Chadd uint16_t lo, hi;
13278549b94SAdrian Chadd lo = MDIO_READREG(device_get_parent(dev), phy, reg);
13378549b94SAdrian Chadd hi = MDIO_READREG(device_get_parent(dev), phy, reg + 1);
13478549b94SAdrian Chadd
13578549b94SAdrian Chadd return (hi << 16) | lo;
13678549b94SAdrian Chadd }
13778549b94SAdrian Chadd
13878549b94SAdrian Chadd static int
arswitch_reg_write32(device_t dev,int phy,int reg,uint32_t value)13978549b94SAdrian Chadd arswitch_reg_write32(device_t dev, int phy, int reg, uint32_t value)
14078549b94SAdrian Chadd {
14178549b94SAdrian Chadd struct arswitch_softc *sc;
14278549b94SAdrian Chadd int r;
14378549b94SAdrian Chadd uint16_t lo, hi;
14478549b94SAdrian Chadd
14578549b94SAdrian Chadd sc = device_get_softc(dev);
14678549b94SAdrian Chadd lo = value & 0xffff;
14778549b94SAdrian Chadd hi = (uint16_t) (value >> 16);
14878549b94SAdrian Chadd
14978549b94SAdrian Chadd if (sc->mii_lo_first) {
15078549b94SAdrian Chadd r = MDIO_WRITEREG(device_get_parent(dev),
15178549b94SAdrian Chadd phy, reg, lo);
15278549b94SAdrian Chadd r |= MDIO_WRITEREG(device_get_parent(dev),
15378549b94SAdrian Chadd phy, reg + 1, hi);
15478549b94SAdrian Chadd } else {
15578549b94SAdrian Chadd r = MDIO_WRITEREG(device_get_parent(dev),
15678549b94SAdrian Chadd phy, reg + 1, hi);
15778549b94SAdrian Chadd r |= MDIO_WRITEREG(device_get_parent(dev),
15878549b94SAdrian Chadd phy, reg, lo);
15978549b94SAdrian Chadd }
16078549b94SAdrian Chadd
16178549b94SAdrian Chadd return r;
16278549b94SAdrian Chadd }
16378549b94SAdrian Chadd
16478549b94SAdrian Chadd int
arswitch_readreg(device_t dev,int addr)16578549b94SAdrian Chadd arswitch_readreg(device_t dev, int addr)
166a043e8c7SAdrian Chadd {
167a043e8c7SAdrian Chadd uint16_t phy, reg;
168a043e8c7SAdrian Chadd
169a043e8c7SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®);
17078549b94SAdrian Chadd return arswitch_reg_read32(dev, 0x10 | phy, reg);
171a043e8c7SAdrian Chadd }
172a043e8c7SAdrian Chadd
173a043e8c7SAdrian Chadd int
arswitch_writereg(device_t dev,int addr,int value)17478549b94SAdrian Chadd arswitch_writereg(device_t dev, int addr, int value)
17578549b94SAdrian Chadd {
17678549b94SAdrian Chadd uint16_t phy, reg;
17778549b94SAdrian Chadd
17878549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®);
17978549b94SAdrian Chadd return (arswitch_reg_write32(dev, 0x10 | phy, reg, value));
18078549b94SAdrian Chadd }
18178549b94SAdrian Chadd
18278549b94SAdrian Chadd /*
18378549b94SAdrian Chadd * Read/write 16 bit values in the switch register space.
18478549b94SAdrian Chadd *
18578549b94SAdrian Chadd * Some of the registers are control registers (eg the MDIO
18678549b94SAdrian Chadd * data versus control space) and so need to be treated
18778549b94SAdrian Chadd * differently.
18878549b94SAdrian Chadd */
18978549b94SAdrian Chadd int
arswitch_readreg_lsb(device_t dev,int addr)190a043e8c7SAdrian Chadd arswitch_readreg_lsb(device_t dev, int addr)
191a043e8c7SAdrian Chadd {
192a043e8c7SAdrian Chadd
193a043e8c7SAdrian Chadd return (arswitch_readreg16(dev, addr));
194a043e8c7SAdrian Chadd }
195a043e8c7SAdrian Chadd
196a043e8c7SAdrian Chadd int
arswitch_readreg_msb(device_t dev,int addr)197a043e8c7SAdrian Chadd arswitch_readreg_msb(device_t dev, int addr)
198a043e8c7SAdrian Chadd {
199a043e8c7SAdrian Chadd
200a043e8c7SAdrian Chadd return (arswitch_readreg16(dev, addr + 2) << 16);
201a043e8c7SAdrian Chadd }
202a043e8c7SAdrian Chadd
203a043e8c7SAdrian Chadd int
arswitch_writereg_lsb(device_t dev,int addr,int data)204a043e8c7SAdrian Chadd arswitch_writereg_lsb(device_t dev, int addr, int data)
205a043e8c7SAdrian Chadd {
206a043e8c7SAdrian Chadd
207a043e8c7SAdrian Chadd return (arswitch_writereg16(dev, addr, data & 0xffff));
208a043e8c7SAdrian Chadd }
209a043e8c7SAdrian Chadd
210a043e8c7SAdrian Chadd int
arswitch_writereg_msb(device_t dev,int addr,int data)211a043e8c7SAdrian Chadd arswitch_writereg_msb(device_t dev, int addr, int data)
212a043e8c7SAdrian Chadd {
213a043e8c7SAdrian Chadd
21426aff2abSAleksandr Rybalko return (arswitch_writereg16(dev, addr + 2, (data >> 16) & 0xffff));
215a043e8c7SAdrian Chadd }
216a043e8c7SAdrian Chadd
217a043e8c7SAdrian Chadd int
arswitch_modifyreg(device_t dev,int addr,int mask,int set)218a043e8c7SAdrian Chadd arswitch_modifyreg(device_t dev, int addr, int mask, int set)
219a043e8c7SAdrian Chadd {
220a043e8c7SAdrian Chadd int value;
22178549b94SAdrian Chadd uint16_t phy, reg;
222a043e8c7SAdrian Chadd
223e6aeff0cSSepherosa Ziehau ARSWITCH_LOCK_ASSERT((struct arswitch_softc *)device_get_softc(dev),
224e6aeff0cSSepherosa Ziehau MA_OWNED);
2251b334c8bSAdrian Chadd
22678549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®);
22778549b94SAdrian Chadd
22878549b94SAdrian Chadd value = arswitch_reg_read32(dev, 0x10 | phy, reg);
229a043e8c7SAdrian Chadd value &= ~mask;
230a043e8c7SAdrian Chadd value |= set;
23178549b94SAdrian Chadd return (arswitch_reg_write32(dev, 0x10 | phy, reg, value));
232a043e8c7SAdrian Chadd }
233b9f07b86SLuiz Otavio O Souza
234b9f07b86SLuiz Otavio O Souza int
arswitch_waitreg(device_t dev,int addr,int mask,int val,int timeout)235b9f07b86SLuiz Otavio O Souza arswitch_waitreg(device_t dev, int addr, int mask, int val, int timeout)
236b9f07b86SLuiz Otavio O Souza {
2371b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev);
238b9f07b86SLuiz Otavio O Souza int err, v;
23978549b94SAdrian Chadd uint16_t phy, reg;
24078549b94SAdrian Chadd
2411b334c8bSAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
2421b334c8bSAdrian Chadd
24378549b94SAdrian Chadd arswitch_split_setpage(dev, addr, &phy, ®);
244b9f07b86SLuiz Otavio O Souza
245b9f07b86SLuiz Otavio O Souza err = -1;
246b9f07b86SLuiz Otavio O Souza while (1) {
24778549b94SAdrian Chadd v = arswitch_reg_read32(dev, 0x10 | phy, reg);
248b9f07b86SLuiz Otavio O Souza v &= mask;
249b9f07b86SLuiz Otavio O Souza if (v == val) {
250b9f07b86SLuiz Otavio O Souza err = 0;
251b9f07b86SLuiz Otavio O Souza break;
252b9f07b86SLuiz Otavio O Souza }
253b9f07b86SLuiz Otavio O Souza if (!timeout)
254b9f07b86SLuiz Otavio O Souza break;
255b9f07b86SLuiz Otavio O Souza DELAY(1);
256b9f07b86SLuiz Otavio O Souza timeout--;
257b9f07b86SLuiz Otavio O Souza }
2581b334c8bSAdrian Chadd if (err != 0) {
2591b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY,
2601b334c8bSAdrian Chadd "%s: waitreg failed; addr=0x%08x, mask=0x%08x, val=0x%08x\n",
2611b334c8bSAdrian Chadd __func__, addr, mask, val);
2621b334c8bSAdrian Chadd }
263b9f07b86SLuiz Otavio O Souza return (err);
264b9f07b86SLuiz Otavio O Souza }
265