1 /* $NetBSD: if_cs_mainbus.c,v 1.9 2022/02/16 23:49:26 riastradh Exp $ */
2
3 /*
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at Sandburst Corp.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: if_cs_mainbus.c,v 1.9 2022/02/16 23:49:26 riastradh Exp $");
34
35 #include <sys/param.h>
36 #include <sys/device.h>
37 #include <sys/systm.h>
38 #include <sys/socket.h>
39 #include <sys/bus.h>
40
41 #include <net/if.h>
42 #include <net/if_ether.h>
43 #include <net/if_media.h>
44 #ifdef INET
45 #include <netinet/in.h>
46 #include <netinet/if_inarp.h>
47 #endif
48
49 #include <powerpc/psl.h>
50
51 #include <machine/pio.h>
52 #include <machine/pmppc.h>
53 #include <evbppc/pmppc/dev/mainbus.h>
54
55 #include <dev/ic/cs89x0reg.h>
56 #include <dev/ic/cs89x0var.h>
57
58 #include <sys/callout.h>
59
60 #define ATSN_EEPROM_MAC_OFFSET 0x20
61
62
63 static void cs_check_eeprom(struct cs_softc *sc);
64
65 static int cs_mainbus_match(device_t, cfdata_t, void *);
66 static void cs_mainbus_attach(device_t, device_t, void *);
67
68 CFATTACH_DECL_NEW(cs_mainbus, sizeof(struct cs_softc),
69 cs_mainbus_match, cs_mainbus_attach, NULL, NULL);
70
71 int
cs_mainbus_match(device_t parent,cfdata_t cf,void * aux)72 cs_mainbus_match(device_t parent, cfdata_t cf, void *aux)
73 {
74 struct mainbus_attach_args *maa = aux;
75
76 return (strcmp(maa->mb_name, "cs") == 0);
77 }
78
79 #if 0
80 static u_int64_t
81 in64(uint a)
82 {
83 union {
84 double d;
85 u_int64_t i;
86 } u;
87 double save, *dp = (double *)a;
88 u_int32_t msr, nmsr;
89
90 __asm volatile("mfmsr %0" : "=r"(msr));
91 nmsr = (msr | PSL_FP) & ~(PSL_FE0 | PSL_FE1);
92 __asm volatile("mtmsr %0" :: "r"(nmsr));
93 __asm volatile("mfmsr %0" : "=r"(nmsr)); /* some interlock nonsense */
94 __asm volatile(
95 "stfd 0,%0\n\
96 lfd 0,%1\n\
97 stfd 0,%2\n\
98 lfd 0,%0"
99 : "=m"(save), "=m"(*dp)
100 : "m"(u.d)
101 );
102 __asm volatile("eieio; sync" ::: "memory");
103 __asm volatile("mtmsr %0" :: "r"(msr));
104 return (u.i);
105 }
106 #endif
107
108 static void
out64(uint a,u_int64_t v)109 out64(uint a, u_int64_t v)
110 {
111 union {
112 double d;
113 u_int64_t i;
114 } u;
115 double save, *dp = (double *)a;
116 u_int32_t msr, nmsr;
117 int s;
118
119 s = splhigh();
120 u.i = v;
121 __asm volatile("mfmsr %0" : "=r"(msr));
122 nmsr = (msr | PSL_FP) & ~(PSL_FE0 | PSL_FE1);
123 __asm volatile("mtmsr %0" :: "r"(nmsr));
124 __asm volatile("mfmsr %0" : "=r"(nmsr)); /* some interlock nonsense */
125 __asm volatile(
126 "stfd 0,%0\n\
127 lfd 0,%2\n\
128 stfd 0,%1\n\
129 lfd 0,%0"
130 : "=m"(save), "=m"(*dp)
131 : "m"(u.d)
132 );
133 __asm volatile("eieio; sync" ::: "memory");
134 __asm volatile("mtmsr %0" :: "r"(msr));
135 splx(s);
136 }
137
138 static u_int8_t
cs_io_read_1(struct cs_softc * sc,bus_size_t offs)139 cs_io_read_1(struct cs_softc *sc, bus_size_t offs)
140 {
141 u_int32_t a, v;
142
143 a = sc->sc_ioh + (offs << 2);
144 v = in8(a);
145 return v;
146 }
147
148 static u_int16_t
cs_io_read_2(struct cs_softc * sc,bus_size_t offs)149 cs_io_read_2(struct cs_softc *sc, bus_size_t offs)
150 {
151 u_int32_t a, v;
152
153 a = sc->sc_ioh + (offs << 2);
154 v = in16(a);
155 return v;
156 }
157
158 static void
cs_io_read_multi_2(struct cs_softc * sc,bus_size_t offs,u_int16_t * buf,bus_size_t cnt)159 cs_io_read_multi_2(struct cs_softc *sc, bus_size_t offs, u_int16_t *buf,
160 bus_size_t cnt)
161 {
162 u_int32_t a, v;
163
164 a = sc->sc_ioh + (offs << 2);
165 while (cnt--) {
166 v = in16(a);
167 *buf++ = bswap16(v);
168 }
169 }
170
171 static void
cs_io_write_2(struct cs_softc * sc,bus_size_t offs,u_int16_t data)172 cs_io_write_2(struct cs_softc *sc, bus_size_t offs, u_int16_t data)
173 {
174 u_int32_t a;
175 u_int64_t v;
176
177 a = sc->sc_ioh + (offs << 2);
178 v = (u_int64_t)data << 48;
179 out64(a, v);
180
181 (void)in16(a); /* CPC700 write post bug */
182 }
183
184 static void
cs_io_write_multi_2(struct cs_softc * sc,bus_size_t offs,const u_int16_t * buf,bus_size_t cnt)185 cs_io_write_multi_2(struct cs_softc *sc, bus_size_t offs,
186 const u_int16_t *buf, bus_size_t cnt)
187 {
188 u_int16_t v;
189 double save, *dp;
190 union {
191 double d;
192 u_int64_t i;
193 } u;
194 u_int32_t msr, nmsr;
195 int s;
196
197 dp = (double *)(sc->sc_ioh + (offs << 2));
198
199 s = splhigh();
200 __asm volatile("mfmsr %0" : "=r"(msr));
201 nmsr = (msr | PSL_FP) & ~(PSL_FE0 | PSL_FE1);
202 __asm volatile("mtmsr %0" :: "r"(nmsr));
203 __asm volatile("mfmsr %0" : "=r"(nmsr)); /* some interlock nonsense */
204 __asm volatile("stfd 0,%0" : "=m"(save));
205
206 while (cnt--) {
207 v = *buf++;
208 v = bswap16(v);
209 u.i = (u_int64_t)v << 48;
210 __asm volatile("lfd 0,%1\nstfd 0,%0" : "=m"(*dp) : "m"(u.d) );
211 __asm volatile("eieio; sync" ::: "memory");
212 }
213 __asm volatile("lfd 0,%0" :: "m"(save));
214 __asm volatile("mtmsr %0" :: "r"(msr));
215 splx(s);
216 }
217
218 static u_int16_t
cs_mem_read_2(struct cs_softc * sc,bus_size_t offs)219 cs_mem_read_2(struct cs_softc *sc, bus_size_t offs)
220 {
221 panic("cs_mem_read_2");
222 }
223
224 static void
cs_mem_write_2(struct cs_softc * sc,bus_size_t offs,u_int16_t data)225 cs_mem_write_2(struct cs_softc *sc, bus_size_t offs, u_int16_t data)
226 {
227 panic("cs_mem_write_2");
228 }
229
230 static void
cs_mem_write_region_2(struct cs_softc * sc,bus_size_t offs,const u_int16_t * buf,bus_size_t cnt)231 cs_mem_write_region_2(struct cs_softc *sc, bus_size_t offs,
232 const u_int16_t *buf, bus_size_t cnt)
233 {
234 panic("cs_mem_write_region_2");
235 }
236
237 void
cs_mainbus_attach(device_t parent,device_t self,void * aux)238 cs_mainbus_attach(device_t parent, device_t self, void *aux)
239 {
240 struct cs_softc *sc = device_private(self);
241 struct mainbus_attach_args *maa = aux;
242 int media[1] = { IFM_ETHER | IFM_10_T };
243
244 printf("\n");
245
246 sc->sc_dev = self;
247 sc->sc_iot = maa->mb_bt;
248 sc->sc_memt = maa->mb_bt;
249 sc->sc_irq = maa->mb_irq;
250
251 if (bus_space_map(sc->sc_iot, PMPPC_CS_IO, CS8900_IOSIZE*4,
252 0, &sc->sc_ioh)) {
253 printf("%s: failed to map io\n", device_xname(self));
254 return;
255 }
256
257 cs_check_eeprom(sc);
258
259 sc->sc_ih = intr_establish(sc->sc_irq, IST_LEVEL, IPL_NET, cs_intr, sc);
260 if (!sc->sc_ih) {
261 printf("%s: unable to establish interrupt\n",
262 device_xname(self));
263 goto fail;
264 }
265
266 sc->sc_cfgflags = CFGFLG_NOT_EEPROM;
267
268 sc->sc_io_read_1 = cs_io_read_1;
269 sc->sc_io_read_2 = cs_io_read_2;
270 sc->sc_io_read_multi_2 = cs_io_read_multi_2;
271 sc->sc_io_write_2 = cs_io_write_2;
272 sc->sc_io_write_multi_2 = cs_io_write_multi_2;
273 sc->sc_mem_read_2 = cs_mem_read_2;
274 sc->sc_mem_write_2 = cs_mem_write_2;
275 sc->sc_mem_write_region_2 = cs_mem_write_region_2;
276
277 /*
278 * We need interrupt on INTRQ0 from the CS8900 (that's what wired
279 * to the UIC). The MI driver subtracts 10 from the irq, so
280 * use 10 as the irq.
281 */
282 sc->sc_irq = 10;
283
284 /* Use half duplex 10baseT. */
285 if (cs_attach(sc, NULL, media, 1, IFM_ETHER | IFM_10_T)) {
286 printf("%s: unable to attach\n", device_xname(self));
287 goto fail;
288 }
289
290 return;
291
292 fail:
293 /* XXX disestablish, unmap */
294 return;
295 }
296
297
298 /*
299 * EEPROM initialization code.
300 */
301
302 static uint16_t default_eeprom_cfg[] =
303 { 0xA100, 0x2020, 0x0300, 0x0000, 0x0000,
304 0x102C, 0x1000, 0x0008, 0x2158, 0x0000,
305 0x0000, 0x0000 };
306
307 static uint16_t
cs_readreg(struct cs_softc * sc,uint pp_offset)308 cs_readreg(struct cs_softc *sc, uint pp_offset)
309 {
310 cs_io_write_2(sc, PORT_PKTPG_PTR, pp_offset);
311 (void)cs_io_read_2(sc, PORT_PKTPG_PTR);
312 return (cs_io_read_2(sc, PORT_PKTPG_DATA));
313 }
314
315 static void
cs_writereg(struct cs_softc * sc,uint pp_offset,uint16_t value)316 cs_writereg(struct cs_softc *sc, uint pp_offset, uint16_t value)
317 {
318 cs_io_write_2(sc, PORT_PKTPG_PTR, pp_offset);
319 (void)cs_io_read_2(sc, PORT_PKTPG_PTR);
320 cs_io_write_2(sc, PORT_PKTPG_DATA, value);
321 (void)cs_io_read_2(sc, PORT_PKTPG_DATA);
322 }
323
324 static int
cs_wait_eeprom_ready(struct cs_softc * sc)325 cs_wait_eeprom_ready(struct cs_softc *sc)
326 {
327 int ms;
328
329 /*
330 * Check to see if the EEPROM is ready, a timeout is used -
331 * just in case EEPROM is ready when SI_BUSY in the
332 * PP_SelfST is clear.
333 */
334 ms = 0;
335 while(cs_readreg(sc, PKTPG_SELF_ST) & SELF_ST_SI_BUSY) {
336 delay(1000);
337 if (ms++ > 20)
338 return 0;
339 }
340 return 1;
341 }
342
343 static void
cs_wr_eeprom(struct cs_softc * sc,uint16_t offset,uint16_t data)344 cs_wr_eeprom(struct cs_softc *sc, uint16_t offset, uint16_t data)
345 {
346
347 /* Check to make sure EEPROM is ready. */
348 if (!cs_wait_eeprom_ready(sc)) {
349 printf("%s: write EEPROM not ready\n",
350 device_xname(sc->sc_dev));
351 return;
352 }
353
354 /* Enable writing. */
355 cs_writereg(sc, PKTPG_EEPROM_CMD, EEPROM_WRITE_ENABLE);
356
357 /* Wait for WRITE_ENABLE command to complete. */
358 if (!cs_wait_eeprom_ready(sc)) {
359 printf("%s: EEPROM WRITE_ENABLE timeout",
360 device_xname(sc->sc_dev));
361 } else {
362 /* Write data into EEPROM_DATA register. */
363 cs_writereg(sc, PKTPG_EEPROM_DATA, data);
364 delay(1000);
365 cs_writereg(sc, PKTPG_EEPROM_CMD, EEPROM_CMD_WRITE | offset);
366
367 /* Wait for WRITE_REGISTER command to complete. */
368 if (!cs_wait_eeprom_ready(sc)) {
369 printf("%s: EEPROM WRITE_REGISTER timeout\n",
370 device_xname(sc->sc_dev));
371 }
372 }
373
374 /* Disable writing. */
375 cs_writereg(sc, PKTPG_EEPROM_CMD, EEPROM_WRITE_DISABLE);
376
377 /* Wait for WRITE_DISABLE command to complete. */
378 if (!cs_wait_eeprom_ready(sc)) {
379 printf("%s: WRITE_DISABLE timeout\n", device_xname(sc->sc_dev));
380 }
381 }
382
383 static uint16_t
cs_rd_eeprom(struct cs_softc * sc,uint16_t offset)384 cs_rd_eeprom(struct cs_softc *sc, uint16_t offset)
385 {
386
387 if (!cs_wait_eeprom_ready(sc)) {
388 printf("%s: read EEPROM not ready\n", device_xname(sc->sc_dev));
389 return 0;
390 }
391 cs_writereg(sc, PKTPG_EEPROM_CMD, EEPROM_CMD_READ | offset);
392
393 if (!cs_wait_eeprom_ready(sc)) {
394 printf("%s: EEPROM_READ timeout\n", device_xname(sc->sc_dev));
395 return 0;
396 }
397 return cs_readreg(sc, PKTPG_EEPROM_DATA);
398 }
399
400 static void
cs_check_eeprom(struct cs_softc * sc)401 cs_check_eeprom(struct cs_softc *sc)
402 {
403 uint8_t checksum;
404 int i;
405 uint16_t tmp;
406
407 /*
408 * If the SELFST[EEPROMOK] is set, then assume EEPROM configuration
409 * is valid.
410 */
411 if (cs_readreg(sc, PKTPG_SELF_ST) & SELF_ST_EEP_OK) {
412 printf("%s: EEPROM OK, skipping initialization\n",
413 device_xname(sc->sc_dev));
414 return;
415 }
416 printf("%s: updating EEPROM\n", device_xname(sc->sc_dev));
417
418 /*
419 * Calculate the size (in bytes) of the default config array and write
420 * it to the lower byte of the array itself.
421 */
422 default_eeprom_cfg[0] |= sizeof(default_eeprom_cfg);
423
424 /*
425 * Read the MAC address from its Artesyn-specified offset in the EEPROM.
426 */
427 for (i = 0; i < 3; i++) {
428 tmp = cs_rd_eeprom(sc, ATSN_EEPROM_MAC_OFFSET + i);
429 default_eeprom_cfg[EEPROM_MAC + i] = bswap16(tmp);
430 }
431
432 /*
433 * Program the EEPROM with our default configuration,
434 * calculating checksum as we proceed.
435 */
436 checksum = 0;
437 for (i = 0; i < sizeof(default_eeprom_cfg)/2 ; i++) {
438 tmp = default_eeprom_cfg[i];
439 cs_wr_eeprom(sc, i, tmp);
440 checksum += tmp >> 8;
441 checksum += tmp & 0xff;
442 }
443
444 /*
445 * The CS8900a datasheet calls for the two's complement of the checksum
446 * to be prgrammed in the most significant byte of the last word of the
447 * header.
448 */
449 checksum = ~checksum + 1;
450 cs_wr_eeprom(sc, i++, checksum << 8);
451 /* write "end of data" flag */
452 cs_wr_eeprom(sc, i, 0xffff);
453 }
454