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