xref: /netbsd-src/sys/arch/mips/cavium/dev/octeon_smi.c (revision aec6f0cf2ee0e8ce1a23f9d46109cdee745ca66f)
1 /*	$NetBSD: octeon_smi.c,v 1.9 2022/09/29 07:00:46 skrll Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Internet Initiative Japan, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: octeon_smi.c,v 1.9 2022/09/29 07:00:46 skrll Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/device.h>
36 #include <sys/mbuf.h>
37 #include <sys/queue.h>
38 #include <sys/kmem.h>
39 
40 #include <mips/locore.h>
41 #include <mips/cavium/octeonvar.h>
42 #include <mips/cavium/dev/octeon_fpareg.h>
43 #include <mips/cavium/dev/octeon_fpavar.h>
44 #include <mips/cavium/dev/octeon_pipreg.h>
45 #include <mips/cavium/dev/octeon_smireg.h>
46 #include <mips/cavium/dev/octeon_smivar.h>
47 #include <mips/cavium/include/iobusvar.h>
48 
49 #include <dev/fdt/fdtvar.h>
50 
51 /*
52  * System Management Interface
53  *
54  *
55  * CN30XX  - 1 SMI interface
56  * CN31XX  - 1 SMI interface
57  * CN38XX  - 1 SMI interface
58  * CN50XX  - 1 SMI interface
59  * CN52XX  - 2 SMI interfaces
60  * CN56XX  - 2 SMI interfaces
61  * CN58XX  - 1 SMI interface
62  * CN61XX  - 2 SMI interfaces
63  * CN63XX  - 2 SMI interfaces
64  * CN66XX  - 2 SMI interfaces
65  * CN68XX  - 4 SMI interfaces
66  * CN70XX  - 2 SMI interfaces
67  * CN73XX  - 2 SMI interfaces
68  * CN78XX  - 4 SMI interfaces
69  * CNF71XX - 2 SMI interfaces
70  * CNF75XX - 2 SMI interfaces
71  */
72 
73 static int	octsmi_iobus_match(device_t, struct cfdata *, void *);
74 static void	octsmi_iobus_attach(device_t, device_t, void *);
75 
76 static int	octsmi_fdt_match(device_t, struct cfdata *, void *);
77 static void	octsmi_fdt_attach(device_t, device_t, void *);
78 
79 static void	octsmi_attach_common(struct octsmi_softc *, int);
80 
81 struct octsmi_instance {
82 	struct octsmi_softc *	sc;
83 	int			phandle;
84 	TAILQ_ENTRY(octsmi_instance) next;
85 };
86 
87 static TAILQ_HEAD(, octsmi_instance) octsmi_instances =
88     TAILQ_HEAD_INITIALIZER(octsmi_instances);
89 
90 #define	_SMI_RD8(sc, off) \
91 	bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, (off))
92 #define	_SMI_WR8(sc, off, v) \
93 	bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, (off), (v))
94 
95 CFATTACH_DECL_NEW(octsmi_iobus, sizeof(struct octsmi_softc),
96     octsmi_iobus_match, octsmi_iobus_attach, NULL, NULL);
97 
98 CFATTACH_DECL_NEW(octsmi_fdt, sizeof(struct octsmi_softc),
99     octsmi_fdt_match, octsmi_fdt_attach, NULL, NULL);
100 
101 static const struct device_compatible_entry compat_data[] = {
102 	{ .compat = "cavium,octeon-3860-mdio" },
103 	DEVICE_COMPAT_EOL
104 };
105 
106 static int
octsmi_iobus_match(device_t parent,struct cfdata * cf,void * aux)107 octsmi_iobus_match(device_t parent, struct cfdata *cf, void *aux)
108 {
109 	struct iobus_attach_args *aa = aux;
110 
111 	if (strcmp(cf->cf_name, aa->aa_name) != 0)
112 		return 0;
113 	if (aa->aa_unitno < SMI_NUNITS)
114 		return 1;
115 	else
116 		return 0;
117 }
118 
119 static void
octsmi_iobus_attach(device_t parent,device_t self,void * aux)120 octsmi_iobus_attach(device_t parent, device_t self, void *aux)
121 {
122 	struct octsmi_softc *sc = device_private(self);
123 	struct iobus_attach_args *aa = aux;
124 	int status;
125 
126 	sc->sc_dev = self;
127 	sc->sc_regt = aa->aa_bust;
128 
129 	aprint_normal("\n");
130 
131 	status = bus_space_map(sc->sc_regt, aa->aa_unit->addr, SMI_SIZE, 0,
132 	    &sc->sc_regh);
133 	if (status != 0) {
134 		aprint_error_dev(self, "could not map registers\n");
135 		return;
136 	}
137 
138 	octsmi_attach_common(sc, 0);
139 }
140 
141 static int
octsmi_fdt_match(device_t parent,struct cfdata * cf,void * aux)142 octsmi_fdt_match(device_t parent, struct cfdata *cf, void *aux)
143 {
144 	struct fdt_attach_args * const faa = aux;
145 
146 	return of_compatible_match(faa->faa_phandle, compat_data);
147 }
148 
149 static void
octsmi_fdt_attach(device_t parent,device_t self,void * aux)150 octsmi_fdt_attach(device_t parent, device_t self, void *aux)
151 {
152 	struct octsmi_softc *sc = device_private(self);
153 	struct fdt_attach_args * const faa = aux;
154 	const int phandle = faa->faa_phandle;
155 	bus_addr_t addr;
156 	bus_size_t size;
157 
158 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
159 		aprint_error(": couldn't get registers\n");
160 		return;
161 	}
162 
163 	sc->sc_dev = self;
164 	sc->sc_regt = faa->faa_bst;
165 
166 	if (bus_space_map(sc->sc_regt, addr, size, 0, &sc->sc_regh) != 0) {
167 		aprint_error(": couldn't map registers\n");
168 		return;
169 	}
170 
171 	aprint_normal("\n");
172 
173 	octsmi_attach_common(sc, phandle);
174 }
175 
176 static void
octsmi_attach_common(struct octsmi_softc * sc,int phandle)177 octsmi_attach_common(struct octsmi_softc *sc, int phandle)
178 {
179 	struct octsmi_instance *oi;
180 
181 	oi = kmem_alloc(sizeof(*oi), KM_SLEEP);
182 	oi->sc = sc;
183 	oi->phandle = phandle;
184 	TAILQ_INSERT_TAIL(&octsmi_instances, oi, next);
185 
186 	const uint64_t magic_value =
187 	    SMI_CLK_PREAMBLE |
188 	    __SHIFTIN(0x4, SMI_CLK_SAMPLE) |		/* XXX magic 0x4 */
189 	    __SHIFTIN(0x64, SMI_CLK_PHASE);		/* XXX magic 0x64 */
190 	_SMI_WR8(sc, SMI_CLK_OFFSET, magic_value);
191 	_SMI_WR8(sc, SMI_EN_OFFSET, SMI_EN_EN);
192 }
193 
194 int
octsmi_read(struct octsmi_softc * sc,int phy_addr,int reg,uint16_t * val)195 octsmi_read(struct octsmi_softc *sc, int phy_addr, int reg, uint16_t *val)
196 {
197 	uint64_t smi_rd;
198 	int timo;
199 
200 	_SMI_WR8(sc, SMI_CMD_OFFSET,
201 	    __SHIFTIN(SMI_CMD_PHY_OP_READ, SMI_CMD_PHY_OP) |
202 	    __SHIFTIN(phy_addr, SMI_CMD_PHY_ADR) |
203 	    __SHIFTIN(reg, SMI_CMD_REG_ADR));
204 
205 	timo = 10000;
206 	smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
207 	while (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
208 		if (timo-- == 0)
209 			return ETIMEDOUT;
210 		delay(10);
211 		smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
212 	}
213 
214 	if (ISSET(smi_rd, SMI_RD_DAT_VAL)) {
215 		*val = (smi_rd & SMI_RD_DAT_DAT);
216 		return 0;
217 	}
218 
219 	return -1;
220 }
221 
222 int
octsmi_write(struct octsmi_softc * sc,int phy_addr,int reg,uint16_t value)223 octsmi_write(struct octsmi_softc *sc, int phy_addr, int reg, uint16_t value)
224 {
225 	uint64_t smi_wr;
226 	int timo;
227 
228 	smi_wr = 0;
229 	SET(smi_wr, value);
230 	_SMI_WR8(sc, SMI_WR_DAT_OFFSET, smi_wr);
231 
232 	_SMI_WR8(sc, SMI_CMD_OFFSET,
233 	    __SHIFTIN(SMI_CMD_PHY_OP_WRITE, SMI_CMD_PHY_OP) |
234 	    __SHIFTIN(phy_addr, SMI_CMD_PHY_ADR) |
235 	    __SHIFTIN(reg, SMI_CMD_REG_ADR));
236 
237 	timo = 10000;
238 	smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
239 	while (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
240 		if (timo-- == 0) {
241 			return ETIMEDOUT;
242 		}
243 		delay(10);
244 		smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
245 	}
246 	if (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
247 		/* XXX log */
248 		printf("ERROR: octsmi_write(0x%x, 0x%x, 0x%hx) timed out.\n",
249 		    phy_addr, reg, value);
250 	}
251 
252 	return 0;
253 }
254 
255 struct octsmi_softc *
octsmi_lookup(int phandle,int port)256 octsmi_lookup(int phandle, int port)
257 {
258 	struct octsmi_instance *oi;
259 
260 #if notyet
261 	TAILQ_FOREACH(oi, &octsmi_instances, list) {
262 		if (oi->phandle == phandle)
263 			return oi->sc;
264 	}
265 
266 	return NULL;
267 #else
268 	oi = TAILQ_FIRST(&octsmi_instances);
269 	return oi == NULL ? NULL : oi->sc;
270 #endif
271 }
272