xref: /freebsd/sys/dev/etherswitch/arswitch/arswitch_reg.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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