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: stable/9/sys/boot/i386/libfirewire/firewire.c 264817 2014-04-23 12:09:15Z brueffer $");
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 void	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 void
fw_print(int verbose)152 fw_print(int verbose)
153 {
154 	int i;
155 	struct fwohci_softc *sc;
156 
157 	for (i = 0; i < MAX_OHCI; i ++) {
158 		sc = &fwinfo[i];
159 		if (sc->state == FWOHCI_STATE_DEAD)
160 			break;
161 		printf("%d: locator=0x%04x devid=0x%08x"
162 			" base_addr=0x%08x handle=0x%08x bus_id=0x%08x\n",
163 			i, sc->locator, sc->devid,
164 			sc->base_addr, sc->handle, sc->bus_id);
165 	}
166 }
167 
168 static int
fw_open(struct open_file * f,...)169 fw_open(struct open_file *f, ...)
170 {
171 #if 0
172     va_list			ap;
173     struct i386_devdesc		*dev;
174     struct open_disk		*od;
175     int				error;
176 
177     va_start(ap, f);
178     dev = va_arg(ap, struct i386_devdesc *);
179     va_end(ap);
180 #endif
181 
182     return (ENXIO);
183 }
184 
185 static int
fw_close(struct open_file * f)186 fw_close(struct open_file *f)
187 {
188     return (0);
189 }
190 
191 static void
fw_cleanup()192 fw_cleanup()
193 {
194     struct dcons_buf *db;
195 
196     /* invalidate dcons buffer */
197     if (dcons_paddr) {
198 	db = (struct dcons_buf *)PTOV(dcons_paddr);
199 	db->magic = 0;
200     }
201 }
202 
203 static int
fw_strategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)204 fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
205 {
206 	return (EIO);
207 }
208 
209 static void
fw_init_crom(struct fwohci_softc * sc)210 fw_init_crom(struct fwohci_softc *sc)
211 {
212 	struct crom_src *src;
213 
214 	printf("fw_init_crom\n");
215 	sc->crom_src_buf = (struct crom_src_buf *)
216 		malloc(sizeof(struct crom_src_buf));
217 	if (sc->crom_src_buf == NULL)
218 		return;
219 
220 	src = &sc->crom_src_buf->src;
221 	bzero(src, sizeof(struct crom_src));
222 
223 	/* BUS info sample */
224 	src->hdr.info_len = 4;
225 
226 	src->businfo.bus_name = CSR_BUS_NAME_IEEE1394;
227 
228 	src->businfo.irmc = 1;
229 	src->businfo.cmc = 1;
230 	src->businfo.isc = 1;
231 	src->businfo.bmc = 1;
232 	src->businfo.pmc = 0;
233 	src->businfo.cyc_clk_acc = 100;
234 	src->businfo.max_rec = sc->maxrec;
235 	src->businfo.max_rom = MAXROM_4;
236 	src->businfo.generation = 1;
237 	src->businfo.link_spd = sc->speed;
238 
239 	src->businfo.eui64.hi = sc->eui.hi;
240 	src->businfo.eui64.lo = sc->eui.lo;
241 
242 	STAILQ_INIT(&src->chunk_list);
243 
244 	sc->crom_src = src;
245 	sc->crom_root = &sc->crom_src_buf->root;
246 }
247 
248 static void
fw_reset_crom(struct fwohci_softc * sc)249 fw_reset_crom(struct fwohci_softc *sc)
250 {
251 	struct crom_src_buf *buf;
252 	struct crom_src *src;
253 	struct crom_chunk *root;
254 
255 	printf("fw_reset\n");
256 	if (sc->crom_src_buf == NULL)
257 		fw_init_crom(sc);
258 
259 	buf = sc->crom_src_buf;
260 	src = sc->crom_src;
261 	root = sc->crom_root;
262 
263 	STAILQ_INIT(&src->chunk_list);
264 
265 	bzero(root, sizeof(struct crom_chunk));
266 	crom_add_chunk(src, NULL, root, 0);
267 	crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */
268 	/* private company_id */
269 	crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE);
270 #ifdef __DragonFly__
271 	crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project");
272 #else
273 	crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project");
274 #endif
275 }
276 
277 
278 #define ADDR_HI(x)	(((x) >> 24) & 0xffffff)
279 #define ADDR_LO(x)	((x) & 0xffffff)
280 
281 static void
dcons_crom(struct fwohci_softc * sc)282 dcons_crom(struct fwohci_softc *sc)
283 {
284 	struct crom_src_buf *buf;
285 	struct crom_src *src;
286 	struct crom_chunk *root;
287 
288 	buf = sc->crom_src_buf;
289 	src = sc->crom_src;
290 	root = sc->crom_root;
291 
292 	bzero(&buf->unit, sizeof(struct crom_chunk));
293 
294 	crom_add_chunk(src, root, &buf->unit, CROM_UDIR);
295 	crom_add_entry(&buf->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE);
296 	crom_add_simple_text(src, &buf->unit, &buf->spec, "FreeBSD");
297 	crom_add_entry(&buf->unit, CSRKEY_VER, DCONS_CSR_VAL_VER);
298 	crom_add_simple_text(src, &buf->unit, &buf->ver, "dcons");
299 	crom_add_entry(&buf->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr));
300 	crom_add_entry(&buf->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr));
301 }
302 
303 void
fw_crom(struct fwohci_softc * sc)304 fw_crom(struct fwohci_softc *sc)
305 {
306 	struct crom_src *src;
307 	void *newrom;
308 
309 	fw_reset_crom(sc);
310 	dcons_crom(sc);
311 
312 	newrom = malloc(CROMSIZE);
313 	src = &sc->crom_src_buf->src;
314 	crom_load(src, (uint32_t *)newrom, CROMSIZE);
315 	if (bcmp(newrom, sc->config_rom, CROMSIZE) != 0) {
316 		/* bump generation and reload */
317 		src->businfo.generation ++;
318 		/* generation must be between 0x2 and 0xF */
319 		if (src->businfo.generation < 2)
320 			src->businfo.generation ++;
321 		crom_load(src, (uint32_t *)newrom, CROMSIZE);
322 		bcopy(newrom, (void *)sc->config_rom, CROMSIZE);
323 	}
324 	free(newrom);
325 }
326 
327 static int
fw_busreset(struct fwohci_softc * sc)328 fw_busreset(struct fwohci_softc *sc)
329 {
330 	int count;
331 
332 	if (sc->state < FWOHCI_STATE_ENABLED) {
333 		printf("fwohci not enabled\n");
334 		return(CMD_OK);
335 	}
336 	fw_crom(sc);
337 	fwohci_ibr(sc);
338 	count = 0;
339 	while (sc->state< FWOHCI_STATE_NORMAL) {
340 		fwohci_poll(sc);
341 		count ++;
342 		if (count > 1000) {
343 			printf("give up to wait bus initialize\n");
344 			return (-1);
345 		}
346 	}
347 	printf("poll count = %d\n", count);
348 	return (0);
349 }
350 
351 void
fw_enable(void)352 fw_enable(void)
353 {
354 	struct fwohci_softc *sc;
355 	int i;
356 
357 	if (fw_initialized == 0)
358 		fw_init();
359 
360 	for (i = 0; i < MAX_OHCI; i ++) {
361 		sc = &fwinfo[i];
362 		if (sc->state != FWOHCI_STATE_INIT)
363 			break;
364 
365 		sc->config_rom = (uint32_t *)
366 			(((uint32_t)sc->config_rom_buf
367 				+ (CROMSIZE - 1)) & ~(CROMSIZE - 1));
368 #if 0
369 		printf("configrom: %08p %08p\n",
370 			sc->config_rom_buf, sc->config_rom);
371 #endif
372 		if (fwohci_init(sc, 0) == 0) {
373 			sc->state = FWOHCI_STATE_ENABLED;
374 			fw_busreset(sc);
375 		} else
376 			sc->state = FWOHCI_STATE_DEAD;
377 	}
378 }
379 
380 void
fw_poll(void)381 fw_poll(void)
382 {
383 	struct fwohci_softc *sc;
384 	int i;
385 
386 	if (fw_initialized == 0)
387 		return;
388 
389 	for (i = 0; i < MAX_OHCI; i ++) {
390 		sc = &fwinfo[i];
391 		if (sc->state < FWOHCI_STATE_ENABLED)
392 			break;
393 		fwohci_poll(sc);
394 	}
395 }
396 
397 #if 0 /* for debug */
398 static int
399 fw_busreset_cmd(int argc, char *argv[])
400 {
401 	struct fwohci_softc *sc;
402 	int i;
403 
404 	for (i = 0; i < MAX_OHCI; i ++) {
405 		sc = &fwinfo[i];
406 		if (sc->state < FWOHCI_STATE_INIT)
407 			break;
408 		fw_busreset(sc);
409 	}
410 	return(CMD_OK);
411 }
412 
413 static int
414 fw_poll_cmd(int argc, char *argv[])
415 {
416 	fw_poll();
417 	return(CMD_OK);
418 }
419 
420 static int
421 fw_enable_cmd(int argc, char *argv[])
422 {
423 	fw_print(0);
424 	fw_enable();
425 	return(CMD_OK);
426 }
427 
428 
429 static int
430 dcons_enable(int argc, char *argv[])
431 {
432 	dconsole.c_init(0);
433 	fw_enable();
434 	dconsole.c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
435 	return(CMD_OK);
436 }
437 
438 static int
439 dcons_read(int argc, char *argv[])
440 {
441 	char c;
442 	while (dconsole.c_ready()) {
443 		c = dconsole.c_in();
444 		printf("%c", c);
445 	}
446 	printf("\r\n");
447 	return(CMD_OK);
448 }
449 
450 static int
451 dcons_write(int argc, char *argv[])
452 {
453 	int len, i;
454 	if (argc < 2)
455 		return(CMD_OK);
456 
457 	len = strlen(argv[1]);
458 	for (i = 0; i < len; i ++)
459 		dconsole.c_out(argv[1][i]);
460 	dconsole.c_out('\r');
461 	dconsole.c_out('\n');
462 	return(CMD_OK);
463 }
464 COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd);
465 COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd);
466 COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd);
467 COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable);
468 COMMAND_SET(dread, "dread", "read from dcons", dcons_read);
469 COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write);
470 #endif
471