xref: /netbsd-src/sys/arch/mips/cavium/dev/octeon_smi.c (revision aec6f0cf2ee0e8ce1a23f9d46109cdee745ca66f)
1*aec6f0cfSskrll /*	$NetBSD: octeon_smi.c,v 1.9 2022/09/29 07:00:46 skrll Exp $	*/
2f693c922Shikaru 
3f693c922Shikaru /*
4f693c922Shikaru  * Copyright (c) 2007 Internet Initiative Japan, Inc.
5f693c922Shikaru  * All rights reserved.
6f693c922Shikaru  *
7f693c922Shikaru  * Redistribution and use in source and binary forms, with or without
8f693c922Shikaru  * modification, are permitted provided that the following conditions
9f693c922Shikaru  * are met:
10f693c922Shikaru  * 1. Redistributions of source code must retain the above copyright
11f693c922Shikaru  *    notice, this list of conditions and the following disclaimer.
12f693c922Shikaru  * 2. Redistributions in binary form must reproduce the above copyright
13f693c922Shikaru  *    notice, this list of conditions and the following disclaimer in the
14f693c922Shikaru  *    documentation and/or other materials provided with the distribution.
15f693c922Shikaru  *
16f693c922Shikaru  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17f693c922Shikaru  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f693c922Shikaru  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f693c922Shikaru  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20f693c922Shikaru  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f693c922Shikaru  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f693c922Shikaru  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f693c922Shikaru  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f693c922Shikaru  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f693c922Shikaru  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f693c922Shikaru  * SUCH DAMAGE.
27f693c922Shikaru  */
28f693c922Shikaru 
29f693c922Shikaru #include <sys/cdefs.h>
30*aec6f0cfSskrll __KERNEL_RCSID(0, "$NetBSD: octeon_smi.c,v 1.9 2022/09/29 07:00:46 skrll Exp $");
31f693c922Shikaru 
32f693c922Shikaru #include <sys/param.h>
33f693c922Shikaru #include <sys/systm.h>
34092c6bf7Ssimonb #include <sys/bus.h>
35092c6bf7Ssimonb #include <sys/device.h>
36f693c922Shikaru #include <sys/mbuf.h>
3784507484Sjmcneill #include <sys/queue.h>
3884507484Sjmcneill #include <sys/kmem.h>
39092c6bf7Ssimonb 
40f693c922Shikaru #include <mips/locore.h>
41f693c922Shikaru #include <mips/cavium/octeonvar.h>
42b9fcd28bSsimonb #include <mips/cavium/dev/octeon_fpareg.h>
43f693c922Shikaru #include <mips/cavium/dev/octeon_fpavar.h>
44f693c922Shikaru #include <mips/cavium/dev/octeon_pipreg.h>
45f693c922Shikaru #include <mips/cavium/dev/octeon_smireg.h>
46f693c922Shikaru #include <mips/cavium/dev/octeon_smivar.h>
47092c6bf7Ssimonb #include <mips/cavium/include/iobusvar.h>
48f693c922Shikaru 
4984507484Sjmcneill #include <dev/fdt/fdtvar.h>
5084507484Sjmcneill 
51092c6bf7Ssimonb /*
52092c6bf7Ssimonb  * System Management Interface
53092c6bf7Ssimonb  *
54092c6bf7Ssimonb  *
55092c6bf7Ssimonb  * CN30XX  - 1 SMI interface
56092c6bf7Ssimonb  * CN31XX  - 1 SMI interface
57092c6bf7Ssimonb  * CN38XX  - 1 SMI interface
58092c6bf7Ssimonb  * CN50XX  - 1 SMI interface
59092c6bf7Ssimonb  * CN52XX  - 2 SMI interfaces
60092c6bf7Ssimonb  * CN56XX  - 2 SMI interfaces
61092c6bf7Ssimonb  * CN58XX  - 1 SMI interface
62092c6bf7Ssimonb  * CN61XX  - 2 SMI interfaces
63092c6bf7Ssimonb  * CN63XX  - 2 SMI interfaces
64092c6bf7Ssimonb  * CN66XX  - 2 SMI interfaces
65092c6bf7Ssimonb  * CN68XX  - 4 SMI interfaces
66092c6bf7Ssimonb  * CN70XX  - 2 SMI interfaces
67092c6bf7Ssimonb  * CN73XX  - 2 SMI interfaces
68092c6bf7Ssimonb  * CN78XX  - 4 SMI interfaces
69092c6bf7Ssimonb  * CNF71XX - 2 SMI interfaces
70092c6bf7Ssimonb  * CNF75XX - 2 SMI interfaces
71092c6bf7Ssimonb  */
72f693c922Shikaru 
7384507484Sjmcneill static int	octsmi_iobus_match(device_t, struct cfdata *, void *);
7484507484Sjmcneill static void	octsmi_iobus_attach(device_t, device_t, void *);
75f693c922Shikaru 
7684507484Sjmcneill static int	octsmi_fdt_match(device_t, struct cfdata *, void *);
7784507484Sjmcneill static void	octsmi_fdt_attach(device_t, device_t, void *);
7884507484Sjmcneill 
7984507484Sjmcneill static void	octsmi_attach_common(struct octsmi_softc *, int);
8084507484Sjmcneill 
8184507484Sjmcneill struct octsmi_instance {
8284507484Sjmcneill 	struct octsmi_softc *	sc;
8384507484Sjmcneill 	int			phandle;
8484507484Sjmcneill 	TAILQ_ENTRY(octsmi_instance) next;
8584507484Sjmcneill };
8684507484Sjmcneill 
8784507484Sjmcneill static TAILQ_HEAD(, octsmi_instance) octsmi_instances =
8884507484Sjmcneill     TAILQ_HEAD_INITIALIZER(octsmi_instances);
89f693c922Shikaru 
90f693c922Shikaru #define	_SMI_RD8(sc, off) \
91f693c922Shikaru 	bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, (off))
92f693c922Shikaru #define	_SMI_WR8(sc, off, v) \
93f693c922Shikaru 	bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, (off), (v))
94f693c922Shikaru 
9584507484Sjmcneill CFATTACH_DECL_NEW(octsmi_iobus, sizeof(struct octsmi_softc),
9684507484Sjmcneill     octsmi_iobus_match, octsmi_iobus_attach, NULL, NULL);
9784507484Sjmcneill 
9884507484Sjmcneill CFATTACH_DECL_NEW(octsmi_fdt, sizeof(struct octsmi_softc),
9984507484Sjmcneill     octsmi_fdt_match, octsmi_fdt_attach, NULL, NULL);
10084507484Sjmcneill 
1016e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
1026e54367aSthorpej 	{ .compat = "cavium,octeon-3860-mdio" },
1036e54367aSthorpej 	DEVICE_COMPAT_EOL
10484507484Sjmcneill };
105092c6bf7Ssimonb 
106092c6bf7Ssimonb static int
octsmi_iobus_match(device_t parent,struct cfdata * cf,void * aux)10784507484Sjmcneill octsmi_iobus_match(device_t parent, struct cfdata *cf, void *aux)
108092c6bf7Ssimonb {
109092c6bf7Ssimonb 	struct iobus_attach_args *aa = aux;
110092c6bf7Ssimonb 
111092c6bf7Ssimonb 	if (strcmp(cf->cf_name, aa->aa_name) != 0)
112092c6bf7Ssimonb 		return 0;
113092c6bf7Ssimonb 	if (aa->aa_unitno < SMI_NUNITS)
114092c6bf7Ssimonb 		return 1;
115092c6bf7Ssimonb 	else
116092c6bf7Ssimonb 		return 0;
117092c6bf7Ssimonb }
118092c6bf7Ssimonb 
119092c6bf7Ssimonb static void
octsmi_iobus_attach(device_t parent,device_t self,void * aux)12084507484Sjmcneill octsmi_iobus_attach(device_t parent, device_t self, void *aux)
121092c6bf7Ssimonb {
122092c6bf7Ssimonb 	struct octsmi_softc *sc = device_private(self);
123092c6bf7Ssimonb 	struct iobus_attach_args *aa = aux;
124092c6bf7Ssimonb 	int status;
125092c6bf7Ssimonb 
126092c6bf7Ssimonb 	sc->sc_dev = self;
127092c6bf7Ssimonb 	sc->sc_regt = aa->aa_bust;
128092c6bf7Ssimonb 
129092c6bf7Ssimonb 	aprint_normal("\n");
130092c6bf7Ssimonb 
131092c6bf7Ssimonb 	status = bus_space_map(sc->sc_regt, aa->aa_unit->addr, SMI_SIZE, 0,
132092c6bf7Ssimonb 	    &sc->sc_regh);
133092c6bf7Ssimonb 	if (status != 0) {
134092c6bf7Ssimonb 		aprint_error_dev(self, "could not map registers\n");
135092c6bf7Ssimonb 		return;
136092c6bf7Ssimonb 	}
137092c6bf7Ssimonb 
13884507484Sjmcneill 	octsmi_attach_common(sc, 0);
13984507484Sjmcneill }
14084507484Sjmcneill 
14184507484Sjmcneill static int
octsmi_fdt_match(device_t parent,struct cfdata * cf,void * aux)14284507484Sjmcneill octsmi_fdt_match(device_t parent, struct cfdata *cf, void *aux)
14384507484Sjmcneill {
14484507484Sjmcneill 	struct fdt_attach_args * const faa = aux;
14584507484Sjmcneill 
1466e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
14784507484Sjmcneill }
14884507484Sjmcneill 
14984507484Sjmcneill static void
octsmi_fdt_attach(device_t parent,device_t self,void * aux)15084507484Sjmcneill octsmi_fdt_attach(device_t parent, device_t self, void *aux)
15184507484Sjmcneill {
15284507484Sjmcneill 	struct octsmi_softc *sc = device_private(self);
15384507484Sjmcneill 	struct fdt_attach_args * const faa = aux;
15484507484Sjmcneill 	const int phandle = faa->faa_phandle;
15584507484Sjmcneill 	bus_addr_t addr;
15684507484Sjmcneill 	bus_size_t size;
15784507484Sjmcneill 
15884507484Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
15984507484Sjmcneill 		aprint_error(": couldn't get registers\n");
16084507484Sjmcneill 		return;
16184507484Sjmcneill 	}
16284507484Sjmcneill 
16384507484Sjmcneill 	sc->sc_dev = self;
16484507484Sjmcneill 	sc->sc_regt = faa->faa_bst;
16584507484Sjmcneill 
16684507484Sjmcneill 	if (bus_space_map(sc->sc_regt, addr, size, 0, &sc->sc_regh) != 0) {
16784507484Sjmcneill 		aprint_error(": couldn't map registers\n");
16884507484Sjmcneill 		return;
16984507484Sjmcneill 	}
17084507484Sjmcneill 
17184507484Sjmcneill 	aprint_normal("\n");
17284507484Sjmcneill 
17384507484Sjmcneill 	octsmi_attach_common(sc, phandle);
17484507484Sjmcneill }
17584507484Sjmcneill 
17684507484Sjmcneill static void
octsmi_attach_common(struct octsmi_softc * sc,int phandle)17784507484Sjmcneill octsmi_attach_common(struct octsmi_softc *sc, int phandle)
17884507484Sjmcneill {
17984507484Sjmcneill 	struct octsmi_instance *oi;
18084507484Sjmcneill 
18184507484Sjmcneill 	oi = kmem_alloc(sizeof(*oi), KM_SLEEP);
18284507484Sjmcneill 	oi->sc = sc;
18384507484Sjmcneill 	oi->phandle = phandle;
18484507484Sjmcneill 	TAILQ_INSERT_TAIL(&octsmi_instances, oi, next);
185092c6bf7Ssimonb 
186092c6bf7Ssimonb 	const uint64_t magic_value =
187092c6bf7Ssimonb 	    SMI_CLK_PREAMBLE |
188092c6bf7Ssimonb 	    __SHIFTIN(0x4, SMI_CLK_SAMPLE) |		/* XXX magic 0x4 */
189092c6bf7Ssimonb 	    __SHIFTIN(0x64, SMI_CLK_PHASE);		/* XXX magic 0x64 */
190092c6bf7Ssimonb 	_SMI_WR8(sc, SMI_CLK_OFFSET, magic_value);
191092c6bf7Ssimonb 	_SMI_WR8(sc, SMI_EN_OFFSET, SMI_EN_EN);
192092c6bf7Ssimonb }
193092c6bf7Ssimonb 
194f693c922Shikaru int
octsmi_read(struct octsmi_softc * sc,int phy_addr,int reg,uint16_t * val)1953f508e4dSsimonb octsmi_read(struct octsmi_softc *sc, int phy_addr, int reg, uint16_t *val)
196f693c922Shikaru {
197f693c922Shikaru 	uint64_t smi_rd;
198f693c922Shikaru 	int timo;
199f693c922Shikaru 
200b9fcd28bSsimonb 	_SMI_WR8(sc, SMI_CMD_OFFSET,
201b9fcd28bSsimonb 	    __SHIFTIN(SMI_CMD_PHY_OP_READ, SMI_CMD_PHY_OP) |
202b9fcd28bSsimonb 	    __SHIFTIN(phy_addr, SMI_CMD_PHY_ADR) |
203b9fcd28bSsimonb 	    __SHIFTIN(reg, SMI_CMD_REG_ADR));
204f693c922Shikaru 
205f693c922Shikaru 	timo = 10000;
206f693c922Shikaru 	smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
207f693c922Shikaru 	while (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
208f693c922Shikaru 		if (timo-- == 0)
209a5cdd4b4Smsaitoh 			return ETIMEDOUT;
210f693c922Shikaru 		delay(10);
211f693c922Shikaru 		smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
212f693c922Shikaru 	}
213a5cdd4b4Smsaitoh 
214a5cdd4b4Smsaitoh 	if (ISSET(smi_rd, SMI_RD_DAT_VAL)) {
215a5cdd4b4Smsaitoh 		*val = (smi_rd & SMI_RD_DAT_DAT);
216a5cdd4b4Smsaitoh 		return 0;
217a5cdd4b4Smsaitoh 	}
218a5cdd4b4Smsaitoh 
219f693c922Shikaru 	return -1;
220f693c922Shikaru }
221f693c922Shikaru 
222a5cdd4b4Smsaitoh int
octsmi_write(struct octsmi_softc * sc,int phy_addr,int reg,uint16_t value)2233f508e4dSsimonb octsmi_write(struct octsmi_softc *sc, int phy_addr, int reg, uint16_t value)
224f693c922Shikaru {
225f693c922Shikaru 	uint64_t smi_wr;
226f693c922Shikaru 	int timo;
227f693c922Shikaru 
228f693c922Shikaru 	smi_wr = 0;
229f693c922Shikaru 	SET(smi_wr, value);
230f693c922Shikaru 	_SMI_WR8(sc, SMI_WR_DAT_OFFSET, smi_wr);
231f693c922Shikaru 
232b9fcd28bSsimonb 	_SMI_WR8(sc, SMI_CMD_OFFSET,
233b9fcd28bSsimonb 	    __SHIFTIN(SMI_CMD_PHY_OP_WRITE, SMI_CMD_PHY_OP) |
234b9fcd28bSsimonb 	    __SHIFTIN(phy_addr, SMI_CMD_PHY_ADR) |
235b9fcd28bSsimonb 	    __SHIFTIN(reg, SMI_CMD_REG_ADR));
236f693c922Shikaru 
237f693c922Shikaru 	timo = 10000;
238f693c922Shikaru 	smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
239f693c922Shikaru 	while (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
240a5cdd4b4Smsaitoh 		if (timo-- == 0) {
241a5cdd4b4Smsaitoh 			return ETIMEDOUT;
242a5cdd4b4Smsaitoh 		}
243f693c922Shikaru 		delay(10);
244f693c922Shikaru 		smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
245f693c922Shikaru 	}
246092c6bf7Ssimonb 	if (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
247092c6bf7Ssimonb 		/* XXX log */
248092c6bf7Ssimonb 		printf("ERROR: octsmi_write(0x%x, 0x%x, 0x%hx) timed out.\n",
249092c6bf7Ssimonb 		    phy_addr, reg, value);
250092c6bf7Ssimonb 	}
251a5cdd4b4Smsaitoh 
252a5cdd4b4Smsaitoh 	return 0;
253f693c922Shikaru }
254f693c922Shikaru 
255092c6bf7Ssimonb struct octsmi_softc *
octsmi_lookup(int phandle,int port)256092c6bf7Ssimonb octsmi_lookup(int phandle, int port)
257f693c922Shikaru {
25884507484Sjmcneill 	struct octsmi_instance *oi;
259f693c922Shikaru 
26084507484Sjmcneill #if notyet
26184507484Sjmcneill 	TAILQ_FOREACH(oi, &octsmi_instances, list) {
26284507484Sjmcneill 		if (oi->phandle == phandle)
26384507484Sjmcneill 			return oi->sc;
26484507484Sjmcneill 	}
265f693c922Shikaru 
26684507484Sjmcneill 	return NULL;
26784507484Sjmcneill #else
26884507484Sjmcneill 	oi = TAILQ_FIRST(&octsmi_instances);
26984507484Sjmcneill 	return oi == NULL ? NULL : oi->sc;
27084507484Sjmcneill #endif
271092c6bf7Ssimonb }
272