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
53extern uint32_t dcons_paddr;
54extern struct console dconsole;
55
56struct 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
67static int	fw_init(void);
68static int	fw_strategy(void *devdata, int flag, daddr_t dblk,
69		    size_t size, char *buf, size_t *rsize);
70static int	fw_open(struct open_file *f, ...);
71static int	fw_close(struct open_file *f);
72static void	fw_print(int verbose);
73static void	fw_cleanup(void);
74
75void		fw_enable(void);
76
77struct 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
89static struct fwohci_softc fwinfo[MAX_OHCI];
90static int fw_initialized = 0;
91
92static void
93fw_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
124static int
125fw_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 */
151static void
152fw_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
168static int
169fw_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
185static int
186fw_close(struct open_file *f)
187{
188    return (0);
189}
190
191static void
192fw_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
203static int
204fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
205{
206	return (EIO);
207}
208
209static void
210fw_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
248static void
249fw_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
281static void
282dcons_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
303void
304fw_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
327static int
328fw_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
351void
352fw_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
380void
381fw_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 */
398static int
399fw_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
413static int
414fw_poll_cmd(int argc, char *argv[])
415{
416	fw_poll();
417	return(CMD_OK);
418}
419
420static int
421fw_enable_cmd(int argc, char *argv[])
422{
423	fw_print(0);
424	fw_enable();
425	return(CMD_OK);
426}
427
428
429static int
430dcons_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
438static int
439dcons_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
450static int
451dcons_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}
464COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd);
465COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd);
466COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd);
467COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable);
468COMMAND_SET(dread, "dread", "read from dcons", dcons_read);
469COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write);
470#endif
471