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