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