octeon_smi.c revision 1.7 1 /* $NetBSD: octeon_smi.c,v 1.7 2021/01/27 03:10:21 thorpej 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.7 2021/01/27 03:10:21 thorpej 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/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/queue.h>
39 #include <sys/kmem.h>
40
41 #include <mips/locore.h>
42 #include <mips/cavium/octeonvar.h>
43 #include <mips/cavium/dev/octeon_fpareg.h>
44 #include <mips/cavium/dev/octeon_fpavar.h>
45 #include <mips/cavium/dev/octeon_pipreg.h>
46 #include <mips/cavium/dev/octeon_smireg.h>
47 #include <mips/cavium/dev/octeon_smivar.h>
48 #include <mips/cavium/include/iobusvar.h>
49
50 #include <dev/fdt/fdtvar.h>
51
52 /*
53 * System Management Interface
54 *
55 *
56 * CN30XX - 1 SMI interface
57 * CN31XX - 1 SMI interface
58 * CN38XX - 1 SMI interface
59 * CN50XX - 1 SMI interface
60 * CN52XX - 2 SMI interfaces
61 * CN56XX - 2 SMI interfaces
62 * CN58XX - 1 SMI interface
63 * CN61XX - 2 SMI interfaces
64 * CN63XX - 2 SMI interfaces
65 * CN66XX - 2 SMI interfaces
66 * CN68XX - 4 SMI interfaces
67 * CN70XX - 2 SMI interfaces
68 * CN73XX - 2 SMI interfaces
69 * CN78XX - 4 SMI interfaces
70 * CNF71XX - 2 SMI interfaces
71 * CNF75XX - 2 SMI interfaces
72 */
73
74 static int octsmi_iobus_match(device_t, struct cfdata *, void *);
75 static void octsmi_iobus_attach(device_t, device_t, void *);
76
77 static int octsmi_fdt_match(device_t, struct cfdata *, void *);
78 static void octsmi_fdt_attach(device_t, device_t, void *);
79
80 static void octsmi_attach_common(struct octsmi_softc *, int);
81
82 struct octsmi_instance {
83 struct octsmi_softc * sc;
84 int phandle;
85 TAILQ_ENTRY(octsmi_instance) next;
86 };
87
88 static TAILQ_HEAD(, octsmi_instance) octsmi_instances =
89 TAILQ_HEAD_INITIALIZER(octsmi_instances);
90
91 #define _SMI_RD8(sc, off) \
92 bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, (off))
93 #define _SMI_WR8(sc, off, v) \
94 bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, (off), (v))
95
96 CFATTACH_DECL_NEW(octsmi_iobus, sizeof(struct octsmi_softc),
97 octsmi_iobus_match, octsmi_iobus_attach, NULL, NULL);
98
99 CFATTACH_DECL_NEW(octsmi_fdt, sizeof(struct octsmi_softc),
100 octsmi_fdt_match, octsmi_fdt_attach, NULL, NULL);
101
102 static const struct device_compatible_entry compat_data[] = {
103 { .compat = "cavium,octeon-3860-mdio" },
104 DEVICE_COMPAT_EOL
105 };
106
107 static int
108 octsmi_iobus_match(device_t parent, struct cfdata *cf, void *aux)
109 {
110 struct iobus_attach_args *aa = aux;
111
112 if (strcmp(cf->cf_name, aa->aa_name) != 0)
113 return 0;
114 if (aa->aa_unitno < SMI_NUNITS)
115 return 1;
116 else
117 return 0;
118 }
119
120 static void
121 octsmi_iobus_attach(device_t parent, device_t self, void *aux)
122 {
123 struct octsmi_softc *sc = device_private(self);
124 struct iobus_attach_args *aa = aux;
125 int status;
126
127 sc->sc_dev = self;
128 sc->sc_regt = aa->aa_bust;
129
130 aprint_normal("\n");
131
132 status = bus_space_map(sc->sc_regt, aa->aa_unit->addr, SMI_SIZE, 0,
133 &sc->sc_regh);
134 if (status != 0) {
135 aprint_error_dev(self, "could not map registers\n");
136 return;
137 }
138
139 octsmi_attach_common(sc, 0);
140 }
141
142 static int
143 octsmi_fdt_match(device_t parent, struct cfdata *cf, void *aux)
144 {
145 struct fdt_attach_args * const faa = aux;
146
147 return of_compatible_match(faa->faa_phandle, compat_data);
148 }
149
150 static void
151 octsmi_fdt_attach(device_t parent, device_t self, void *aux)
152 {
153 struct octsmi_softc *sc = device_private(self);
154 struct fdt_attach_args * const faa = aux;
155 const int phandle = faa->faa_phandle;
156 bus_addr_t addr;
157 bus_size_t size;
158
159 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
160 aprint_error(": couldn't get registers\n");
161 return;
162 }
163
164 sc->sc_dev = self;
165 sc->sc_regt = faa->faa_bst;
166
167 if (bus_space_map(sc->sc_regt, addr, size, 0, &sc->sc_regh) != 0) {
168 aprint_error(": couldn't map registers\n");
169 return;
170 }
171
172 aprint_normal("\n");
173
174 octsmi_attach_common(sc, phandle);
175 }
176
177 static void
178 octsmi_attach_common(struct octsmi_softc *sc, int phandle)
179 {
180 struct octsmi_instance *oi;
181
182 oi = kmem_alloc(sizeof(*oi), KM_SLEEP);
183 oi->sc = sc;
184 oi->phandle = phandle;
185 TAILQ_INSERT_TAIL(&octsmi_instances, oi, next);
186
187 const uint64_t magic_value =
188 SMI_CLK_PREAMBLE |
189 __SHIFTIN(0x4, SMI_CLK_SAMPLE) | /* XXX magic 0x4 */
190 __SHIFTIN(0x64, SMI_CLK_PHASE); /* XXX magic 0x64 */
191 _SMI_WR8(sc, SMI_CLK_OFFSET, magic_value);
192 _SMI_WR8(sc, SMI_EN_OFFSET, SMI_EN_EN);
193 }
194
195 int
196 octsmi_read(struct octsmi_softc *sc, int phy_addr, int reg, uint16_t *val)
197 {
198 uint64_t smi_rd;
199 int timo;
200
201 _SMI_WR8(sc, SMI_CMD_OFFSET,
202 __SHIFTIN(SMI_CMD_PHY_OP_READ, SMI_CMD_PHY_OP) |
203 __SHIFTIN(phy_addr, SMI_CMD_PHY_ADR) |
204 __SHIFTIN(reg, SMI_CMD_REG_ADR));
205
206 timo = 10000;
207 smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
208 while (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
209 if (timo-- == 0)
210 return ETIMEDOUT;
211 delay(10);
212 smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
213 }
214
215 if (ISSET(smi_rd, SMI_RD_DAT_VAL)) {
216 *val = (smi_rd & SMI_RD_DAT_DAT);
217 return 0;
218 }
219
220 return -1;
221 }
222
223 int
224 octsmi_write(struct octsmi_softc *sc, int phy_addr, int reg, uint16_t value)
225 {
226 uint64_t smi_wr;
227 int timo;
228
229 smi_wr = 0;
230 SET(smi_wr, value);
231 _SMI_WR8(sc, SMI_WR_DAT_OFFSET, smi_wr);
232
233 _SMI_WR8(sc, SMI_CMD_OFFSET,
234 __SHIFTIN(SMI_CMD_PHY_OP_WRITE, SMI_CMD_PHY_OP) |
235 __SHIFTIN(phy_addr, SMI_CMD_PHY_ADR) |
236 __SHIFTIN(reg, SMI_CMD_REG_ADR));
237
238 timo = 10000;
239 smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
240 while (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
241 if (timo-- == 0) {
242 return ETIMEDOUT;
243 }
244 delay(10);
245 smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
246 }
247 if (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
248 /* XXX log */
249 printf("ERROR: octsmi_write(0x%x, 0x%x, 0x%hx) timed out.\n",
250 phy_addr, reg, value);
251 }
252
253 return 0;
254 }
255
256 struct octsmi_softc *
257 octsmi_lookup(int phandle, int port)
258 {
259 struct octsmi_instance *oi;
260
261 #if notyet
262 TAILQ_FOREACH(oi, &octsmi_instances, list) {
263 if (oi->phandle == phandle)
264 return oi->sc;
265 }
266
267 return NULL;
268 #else
269 oi = TAILQ_FIRST(&octsmi_instances);
270 return oi == NULL ? NULL : oi->sc;
271 #endif
272 }
273