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