1 /*-
2 * Copyright (c) 2004 Hidetoshi Shimokawa <simokawa@FreeBSD.ORG>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 /*
29 * FireWire disk device handling.
30 *
31 */
32
33 #include <stand.h>
34
35 #include <machine/bootinfo.h>
36
37 #include <stdarg.h>
38
39 #include <bootstrap.h>
40 #include <btxv86.h>
41 #include <libi386.h>
42 #include <dev/firewire/firewire.h>
43 #include "fwohci.h"
44 #include <dev/dcons/dcons.h>
45
46 /* XXX */
47 #define BIT4x2(x,y) uint8_t y:4, x:4
48 #define BIT16x2(x,y) uint32_t y:16, x:16
49 #define _KERNEL
50 #include <dev/firewire/iec13213.h>
51
52 extern uint32_t dcons_paddr;
53 extern struct console dconsole;
54
55 struct crom_src_buf {
56 struct crom_src src;
57 struct crom_chunk root;
58 struct crom_chunk vendor;
59 struct crom_chunk hw;
60 /* for dcons */
61 struct crom_chunk unit;
62 struct crom_chunk spec;
63 struct crom_chunk ver;
64 };
65
66 static int fw_init(void);
67 static int fw_strategy(void *devdata, int flag, daddr_t dblk,
68 size_t size, char *buf, size_t *rsize);
69 static int fw_open(struct open_file *f, ...);
70 static int fw_close(struct open_file *f);
71 static int fw_print(int verbose);
72 static void fw_cleanup(void);
73
74 void fw_enable(void);
75
76 struct devsw fwohci = {
77 .dv_name = "FW1394", /* 7 chars at most */
78 .dv_type = DEVT_NET,
79 .dv_init = fw_init,
80 .dv_strategy = fw_strategy,
81 .dv_open = fw_open,
82 .dv_close = fw_close,
83 .dv_ioctl = noioctl,
84 .dv_print = fw_print,
85 .dv_cleanup = fw_cleanup,
86 };
87
88 static struct fwohci_softc fwinfo[MAX_OHCI];
89 static int fw_initialized = 0;
90
91 static void
fw_probe(int index,struct fwohci_softc * sc)92 fw_probe(int index, struct fwohci_softc *sc)
93 {
94 int err;
95
96 sc->state = FWOHCI_STATE_INIT;
97 err = biospci_find_devclass(
98 0x0c0010 /* Serial:FireWire:OHCI */,
99 index /* index */,
100 &sc->locator);
101
102 if (err != 0) {
103 sc->state = FWOHCI_STATE_DEAD;
104 return;
105 }
106
107 biospci_write_config(sc->locator,
108 0x4 /* command */,
109 BIOSPCI_16BITS,
110 0x6 /* enable bus master and memory mapped I/O */);
111
112 biospci_read_config(sc->locator, 0x00 /*devid*/, BIOSPCI_32BITS,
113 &sc->devid);
114 biospci_read_config(sc->locator, 0x10 /*base_addr*/, BIOSPCI_32BITS,
115 &sc->base_addr);
116
117 sc->handle = (uint32_t)PTOV(sc->base_addr);
118 sc->bus_id = OREAD(sc, OHCI_BUS_ID);
119
120 return;
121 }
122
123 static int
fw_init(void)124 fw_init(void)
125 {
126 int i, avail;
127 struct fwohci_softc *sc;
128
129 if (fw_initialized)
130 return (0);
131
132 avail = 0;
133 for (i = 0; i < MAX_OHCI; i ++) {
134 sc = &fwinfo[i];
135 fw_probe(i, sc);
136 if (sc->state == FWOHCI_STATE_DEAD)
137 break;
138 avail ++;
139 break;
140 }
141 fw_initialized = 1;
142
143 return (0);
144 }
145
146
147 /*
148 * Print information about OHCI chips
149 */
150 static int
fw_print(int verbose)151 fw_print(int verbose)
152 {
153 char line[80];
154 int i, ret = 0;
155 struct fwohci_softc *sc;
156
157 printf("%s devices:", fwohci.dv_name);
158 if ((ret = pager_output("\n")) != 0)
159 return (ret);
160
161 for (i = 0; i < MAX_OHCI; i ++) {
162 sc = &fwinfo[i];
163 if (sc->state == FWOHCI_STATE_DEAD)
164 break;
165 snprintf(line, sizeof(line), "%d: locator=0x%04x devid=0x%08x"
166 " base_addr=0x%08x handle=0x%08x bus_id=0x%08x\n",
167 i, sc->locator, sc->devid,
168 sc->base_addr, sc->handle, sc->bus_id);
169 ret = pager_output(line);
170 if (ret != 0)
171 break;
172 }
173 return (ret);
174 }
175
176 static int
fw_open(struct open_file * f,...)177 fw_open(struct open_file *f, ...)
178 {
179 #if 0
180 va_list ap;
181 struct i386_devdesc *dev;
182 struct open_disk *od;
183 int error;
184
185 va_start(ap, f);
186 dev = va_arg(ap, struct i386_devdesc *);
187 va_end(ap);
188 #endif
189
190 return (ENXIO);
191 }
192
193 static int
fw_close(struct open_file * f)194 fw_close(struct open_file *f)
195 {
196 return (0);
197 }
198
199 static void
fw_cleanup()200 fw_cleanup()
201 {
202 struct dcons_buf *db;
203
204 /* invalidate dcons buffer */
205 if (dcons_paddr) {
206 db = (struct dcons_buf *)PTOV(dcons_paddr);
207 db->magic = 0;
208 }
209 }
210
211 static int
fw_strategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)212 fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
213 char *buf, size_t *rsize)
214 {
215 return (EIO);
216 }
217
218 static void
fw_init_crom(struct fwohci_softc * sc)219 fw_init_crom(struct fwohci_softc *sc)
220 {
221 struct crom_src *src;
222
223 printf("fw_init_crom\n");
224 sc->crom_src_buf = (struct crom_src_buf *)
225 malloc(sizeof(struct crom_src_buf));
226 if (sc->crom_src_buf == NULL)
227 return;
228
229 src = &sc->crom_src_buf->src;
230 bzero(src, sizeof(struct crom_src));
231
232 /* BUS info sample */
233 src->hdr.info_len = 4;
234
235 src->businfo.bus_name = CSR_BUS_NAME_IEEE1394;
236
237 src->businfo.irmc = 1;
238 src->businfo.cmc = 1;
239 src->businfo.isc = 1;
240 src->businfo.bmc = 1;
241 src->businfo.pmc = 0;
242 src->businfo.cyc_clk_acc = 100;
243 src->businfo.max_rec = sc->maxrec;
244 src->businfo.max_rom = MAXROM_4;
245 #define FW_GENERATION_CHANGEABLE 2
246 src->businfo.generation = FW_GENERATION_CHANGEABLE;
247 src->businfo.link_spd = sc->speed;
248
249 src->businfo.eui64.hi = sc->eui.hi;
250 src->businfo.eui64.lo = sc->eui.lo;
251
252 STAILQ_INIT(&src->chunk_list);
253
254 sc->crom_src = src;
255 sc->crom_root = &sc->crom_src_buf->root;
256 }
257
258 static void
fw_reset_crom(struct fwohci_softc * sc)259 fw_reset_crom(struct fwohci_softc *sc)
260 {
261 struct crom_src_buf *buf;
262 struct crom_src *src;
263 struct crom_chunk *root;
264
265 printf("fw_reset\n");
266 if (sc->crom_src_buf == NULL)
267 fw_init_crom(sc);
268
269 buf = sc->crom_src_buf;
270 src = sc->crom_src;
271 root = sc->crom_root;
272
273 STAILQ_INIT(&src->chunk_list);
274
275 bzero(root, sizeof(struct crom_chunk));
276 crom_add_chunk(src, NULL, root, 0);
277 crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */
278 /* private company_id */
279 crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE);
280 #ifdef __DragonFly__
281 crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project");
282 #else
283 crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project");
284 #endif
285 }
286
287
288 #define ADDR_HI(x) (((x) >> 24) & 0xffffff)
289 #define ADDR_LO(x) ((x) & 0xffffff)
290
291 static void
dcons_crom(struct fwohci_softc * sc)292 dcons_crom(struct fwohci_softc *sc)
293 {
294 struct crom_src_buf *buf;
295 struct crom_src *src;
296 struct crom_chunk *root;
297
298 buf = sc->crom_src_buf;
299 src = sc->crom_src;
300 root = sc->crom_root;
301
302 bzero(&buf->unit, sizeof(struct crom_chunk));
303
304 crom_add_chunk(src, root, &buf->unit, CROM_UDIR);
305 crom_add_entry(&buf->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE);
306 crom_add_simple_text(src, &buf->unit, &buf->spec, "FreeBSD");
307 crom_add_entry(&buf->unit, CSRKEY_VER, DCONS_CSR_VAL_VER);
308 crom_add_simple_text(src, &buf->unit, &buf->ver, "dcons");
309 crom_add_entry(&buf->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr));
310 crom_add_entry(&buf->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr));
311 }
312
313 void
fw_crom(struct fwohci_softc * sc)314 fw_crom(struct fwohci_softc *sc)
315 {
316 struct crom_src *src;
317 void *newrom;
318
319 fw_reset_crom(sc);
320 dcons_crom(sc);
321
322 newrom = malloc(CROMSIZE);
323 src = &sc->crom_src_buf->src;
324 crom_load(src, (uint32_t *)newrom, CROMSIZE);
325 if (bcmp(newrom, sc->config_rom, CROMSIZE) != 0) {
326 /* Bump generation and reload. */
327 src->businfo.generation++;
328
329 /* Handle generation count wraps. */
330 if (src->businfo.generation < 2)
331 src->businfo.generation = 2;
332
333 /* Recalculate CRC to account for generation change. */
334 crom_load(src, (uint32_t *)newrom, CROMSIZE);
335 bcopy(newrom, (void *)sc->config_rom, CROMSIZE);
336 }
337 free(newrom);
338 }
339
340 static int
fw_busreset(struct fwohci_softc * sc)341 fw_busreset(struct fwohci_softc *sc)
342 {
343 int count;
344
345 if (sc->state < FWOHCI_STATE_ENABLED) {
346 printf("fwohci not enabled\n");
347 return(CMD_OK);
348 }
349 fw_crom(sc);
350 fwohci_ibr(sc);
351 count = 0;
352 while (sc->state< FWOHCI_STATE_NORMAL) {
353 fwohci_poll(sc);
354 count ++;
355 if (count > 1000) {
356 printf("give up to wait bus initialize\n");
357 return (-1);
358 }
359 }
360 printf("poll count = %d\n", count);
361 return (0);
362 }
363
364 void
fw_enable(void)365 fw_enable(void)
366 {
367 struct fwohci_softc *sc;
368 int i;
369
370 if (fw_initialized == 0)
371 fw_init();
372
373 for (i = 0; i < MAX_OHCI; i ++) {
374 sc = &fwinfo[i];
375 if (sc->state != FWOHCI_STATE_INIT)
376 break;
377
378 sc->config_rom = (uint32_t *)
379 (((uint32_t)sc->config_rom_buf
380 + (CROMSIZE - 1)) & ~(CROMSIZE - 1));
381 #if 0
382 printf("configrom: %08p %08p\n",
383 sc->config_rom_buf, sc->config_rom);
384 #endif
385 if (fwohci_init(sc, 0) == 0) {
386 sc->state = FWOHCI_STATE_ENABLED;
387 fw_busreset(sc);
388 } else
389 sc->state = FWOHCI_STATE_DEAD;
390 }
391 }
392
393 void
fw_poll(void)394 fw_poll(void)
395 {
396 struct fwohci_softc *sc;
397 int i;
398
399 if (fw_initialized == 0)
400 return;
401
402 for (i = 0; i < MAX_OHCI; i ++) {
403 sc = &fwinfo[i];
404 if (sc->state < FWOHCI_STATE_ENABLED)
405 break;
406 fwohci_poll(sc);
407 }
408 }
409
410 #if 0 /* for debug */
411 static int
412 fw_busreset_cmd(int argc, char *argv[])
413 {
414 struct fwohci_softc *sc;
415 int i;
416
417 for (i = 0; i < MAX_OHCI; i ++) {
418 sc = &fwinfo[i];
419 if (sc->state < FWOHCI_STATE_INIT)
420 break;
421 fw_busreset(sc);
422 }
423 return(CMD_OK);
424 }
425
426 static int
427 fw_poll_cmd(int argc, char *argv[])
428 {
429 fw_poll();
430 return(CMD_OK);
431 }
432
433 static int
434 fw_enable_cmd(int argc, char *argv[])
435 {
436 fw_print(0);
437 fw_enable();
438 return(CMD_OK);
439 }
440
441
442 static int
443 dcons_enable(int argc, char *argv[])
444 {
445 dconsole.c_init(0);
446 fw_enable();
447 dconsole.c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
448 return(CMD_OK);
449 }
450
451 static int
452 dcons_read(int argc, char *argv[])
453 {
454 char c;
455 while (dconsole.c_ready()) {
456 c = dconsole.c_in();
457 printf("%c", c);
458 }
459 printf("\r\n");
460 return(CMD_OK);
461 }
462
463 static int
464 dcons_write(int argc, char *argv[])
465 {
466 int len, i;
467 if (argc < 2)
468 return(CMD_OK);
469
470 len = strlen(argv[1]);
471 for (i = 0; i < len; i ++)
472 dconsole.c_out(argv[1][i]);
473 dconsole.c_out('\r');
474 dconsole.c_out('\n');
475 return(CMD_OK);
476 }
477 COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd);
478 COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd);
479 COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd);
480 COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable);
481 COMMAND_SET(dread, "dread", "read from dcons", dcons_read);
482 COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write);
483 #endif
484