diff options
Diffstat (limited to 'stand/i386/libfirewire/firewire.c')
-rw-r--r-- | stand/i386/libfirewire/firewire.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/stand/i386/libfirewire/firewire.c b/stand/i386/libfirewire/firewire.c new file mode 100644 index 0000000..4840325 --- /dev/null +++ b/stand/i386/libfirewire/firewire.c @@ -0,0 +1,484 @@ +/*- + * Copyright (c) 2004 Hidetoshi Shimokawa <simokawa@FreeBSD.ORG> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * FireWire disk device handling. + * + */ + +#include <stand.h> + +#include <machine/bootinfo.h> + +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.h> +#include <libi386.h> +#include "fwohci.h" +#include <dev/dcons/dcons.h> + +/* XXX */ +#define BIT4x2(x,y) uint8_t y:4, x:4 +#define BIT16x2(x,y) uint32_t y:16, x:16 +#define _KERNEL +#include <dev/firewire/iec13213.h> + +extern uint32_t dcons_paddr; +extern struct console dconsole; + +struct crom_src_buf { + struct crom_src src; + struct crom_chunk root; + struct crom_chunk vendor; + struct crom_chunk hw; + /* for dcons */ + struct crom_chunk unit; + struct crom_chunk spec; + struct crom_chunk ver; +}; + +static int fw_init(void); +static int fw_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int fw_open(struct open_file *f, ...); +static int fw_close(struct open_file *f); +static int fw_print(int verbose); +static void fw_cleanup(void); + +void fw_enable(void); + +struct devsw fwohci = { + "FW1394", /* 7 chars at most */ + DEVT_NET, + fw_init, + fw_strategy, + fw_open, + fw_close, + noioctl, + fw_print, + fw_cleanup +}; + +static struct fwohci_softc fwinfo[MAX_OHCI]; +static int fw_initialized = 0; + +static void +fw_probe(int index, struct fwohci_softc *sc) +{ + int err; + + sc->state = FWOHCI_STATE_INIT; + err = biospci_find_devclass( + 0x0c0010 /* Serial:FireWire:OHCI */, + index /* index */, + &sc->locator); + + if (err != 0) { + sc->state = FWOHCI_STATE_DEAD; + return; + } + + biospci_write_config(sc->locator, + 0x4 /* command */, + 0x6 /* enable bus master and memory mapped I/O */, + 1 /* word */); + + biospci_read_config(sc->locator, 0x00 /*devid*/, 2 /*dword*/, + &sc->devid); + biospci_read_config(sc->locator, 0x10 /*base_addr*/, 2 /*dword*/, + &sc->base_addr); + + sc->handle = (uint32_t)PTOV(sc->base_addr); + sc->bus_id = OREAD(sc, OHCI_BUS_ID); + + return; +} + +static int +fw_init(void) +{ + int i, avail; + struct fwohci_softc *sc; + + if (fw_initialized) + return (0); + + avail = 0; + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + fw_probe(i, sc); + if (sc->state == FWOHCI_STATE_DEAD) + break; + avail ++; + break; + } + fw_initialized = 1; + + return (0); +} + + +/* + * Print information about OHCI chips + */ +static int +fw_print(int verbose) +{ + char line[80]; + int i, ret = 0; + struct fwohci_softc *sc; + + printf("%s devices:", fwohci.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state == FWOHCI_STATE_DEAD) + break; + snprintf(line, sizeof(line), "%d: locator=0x%04x devid=0x%08x" + " base_addr=0x%08x handle=0x%08x bus_id=0x%08x\n", + i, sc->locator, sc->devid, + sc->base_addr, sc->handle, sc->bus_id); + ret = pager_output(line); + if (ret != 0) + break; + } + return (ret); +} + +static int +fw_open(struct open_file *f, ...) +{ +#if 0 + va_list ap; + struct i386_devdesc *dev; + struct open_disk *od; + int error; + + va_start(ap, f); + dev = va_arg(ap, struct i386_devdesc *); + va_end(ap); +#endif + + return (ENXIO); +} + +static int +fw_close(struct open_file *f) +{ + return (0); +} + +static void +fw_cleanup() +{ + struct dcons_buf *db; + + /* invalidate dcons buffer */ + if (dcons_paddr) { + db = (struct dcons_buf *)PTOV(dcons_paddr); + db->magic = 0; + } +} + +static int +fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + return (EIO); +} + +static void +fw_init_crom(struct fwohci_softc *sc) +{ + struct crom_src *src; + + printf("fw_init_crom\n"); + sc->crom_src_buf = (struct crom_src_buf *) + malloc(sizeof(struct crom_src_buf)); + if (sc->crom_src_buf == NULL) + return; + + src = &sc->crom_src_buf->src; + bzero(src, sizeof(struct crom_src)); + + /* BUS info sample */ + src->hdr.info_len = 4; + + src->businfo.bus_name = CSR_BUS_NAME_IEEE1394; + + src->businfo.irmc = 1; + src->businfo.cmc = 1; + src->businfo.isc = 1; + src->businfo.bmc = 1; + src->businfo.pmc = 0; + src->businfo.cyc_clk_acc = 100; + src->businfo.max_rec = sc->maxrec; + src->businfo.max_rom = MAXROM_4; +#define FW_GENERATION_CHANGEABLE 2 + src->businfo.generation = FW_GENERATION_CHANGEABLE; + src->businfo.link_spd = sc->speed; + + src->businfo.eui64.hi = sc->eui.hi; + src->businfo.eui64.lo = sc->eui.lo; + + STAILQ_INIT(&src->chunk_list); + + sc->crom_src = src; + sc->crom_root = &sc->crom_src_buf->root; +} + +static void +fw_reset_crom(struct fwohci_softc *sc) +{ + struct crom_src_buf *buf; + struct crom_src *src; + struct crom_chunk *root; + + printf("fw_reset\n"); + if (sc->crom_src_buf == NULL) + fw_init_crom(sc); + + buf = sc->crom_src_buf; + src = sc->crom_src; + root = sc->crom_root; + + STAILQ_INIT(&src->chunk_list); + + bzero(root, sizeof(struct crom_chunk)); + crom_add_chunk(src, NULL, root, 0); + crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */ + /* private company_id */ + crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE); +#ifdef __DragonFly__ + crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project"); +#else + crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project"); +#endif +} + + +#define ADDR_HI(x) (((x) >> 24) & 0xffffff) +#define ADDR_LO(x) ((x) & 0xffffff) + +static void +dcons_crom(struct fwohci_softc *sc) +{ + struct crom_src_buf *buf; + struct crom_src *src; + struct crom_chunk *root; + + buf = sc->crom_src_buf; + src = sc->crom_src; + root = sc->crom_root; + + bzero(&buf->unit, sizeof(struct crom_chunk)); + + crom_add_chunk(src, root, &buf->unit, CROM_UDIR); + crom_add_entry(&buf->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE); + crom_add_simple_text(src, &buf->unit, &buf->spec, "FreeBSD"); + crom_add_entry(&buf->unit, CSRKEY_VER, DCONS_CSR_VAL_VER); + crom_add_simple_text(src, &buf->unit, &buf->ver, "dcons"); + crom_add_entry(&buf->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr)); + crom_add_entry(&buf->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr)); +} + +void +fw_crom(struct fwohci_softc *sc) +{ + struct crom_src *src; + void *newrom; + + fw_reset_crom(sc); + dcons_crom(sc); + + newrom = malloc(CROMSIZE); + src = &sc->crom_src_buf->src; + crom_load(src, (uint32_t *)newrom, CROMSIZE); + if (bcmp(newrom, sc->config_rom, CROMSIZE) != 0) { + /* Bump generation and reload. */ + src->businfo.generation++; + + /* Handle generation count wraps. */ + if (src->businfo.generation < 2) + src->businfo.generation = 2; + + /* Recalculate CRC to account for generation change. */ + crom_load(src, (uint32_t *)newrom, CROMSIZE); + bcopy(newrom, (void *)sc->config_rom, CROMSIZE); + } + free(newrom); +} + +static int +fw_busreset(struct fwohci_softc *sc) +{ + int count; + + if (sc->state < FWOHCI_STATE_ENABLED) { + printf("fwohci not enabled\n"); + return(CMD_OK); + } + fw_crom(sc); + fwohci_ibr(sc); + count = 0; + while (sc->state< FWOHCI_STATE_NORMAL) { + fwohci_poll(sc); + count ++; + if (count > 1000) { + printf("give up to wait bus initialize\n"); + return (-1); + } + } + printf("poll count = %d\n", count); + return (0); +} + +void +fw_enable(void) +{ + struct fwohci_softc *sc; + int i; + + if (fw_initialized == 0) + fw_init(); + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state != FWOHCI_STATE_INIT) + break; + + sc->config_rom = (uint32_t *) + (((uint32_t)sc->config_rom_buf + + (CROMSIZE - 1)) & ~(CROMSIZE - 1)); +#if 0 + printf("configrom: %08p %08p\n", + sc->config_rom_buf, sc->config_rom); +#endif + if (fwohci_init(sc, 0) == 0) { + sc->state = FWOHCI_STATE_ENABLED; + fw_busreset(sc); + } else + sc->state = FWOHCI_STATE_DEAD; + } +} + +void +fw_poll(void) +{ + struct fwohci_softc *sc; + int i; + + if (fw_initialized == 0) + return; + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state < FWOHCI_STATE_ENABLED) + break; + fwohci_poll(sc); + } +} + +#if 0 /* for debug */ +static int +fw_busreset_cmd(int argc, char *argv[]) +{ + struct fwohci_softc *sc; + int i; + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state < FWOHCI_STATE_INIT) + break; + fw_busreset(sc); + } + return(CMD_OK); +} + +static int +fw_poll_cmd(int argc, char *argv[]) +{ + fw_poll(); + return(CMD_OK); +} + +static int +fw_enable_cmd(int argc, char *argv[]) +{ + fw_print(0); + fw_enable(); + return(CMD_OK); +} + + +static int +dcons_enable(int argc, char *argv[]) +{ + dconsole.c_init(0); + fw_enable(); + dconsole.c_flags |= C_ACTIVEIN | C_ACTIVEOUT; + return(CMD_OK); +} + +static int +dcons_read(int argc, char *argv[]) +{ + char c; + while (dconsole.c_ready()) { + c = dconsole.c_in(); + printf("%c", c); + } + printf("\r\n"); + return(CMD_OK); +} + +static int +dcons_write(int argc, char *argv[]) +{ + int len, i; + if (argc < 2) + return(CMD_OK); + + len = strlen(argv[1]); + for (i = 0; i < len; i ++) + dconsole.c_out(argv[1][i]); + dconsole.c_out('\r'); + dconsole.c_out('\n'); + return(CMD_OK); +} +COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd); +COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd); +COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd); +COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable); +COMMAND_SET(dread, "dread", "read from dcons", dcons_read); +COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write); +#endif |