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