/* $NetBSD: firewire.c,v 1.55 2022/05/22 11:27:35 andvar Exp $ */
/*-
* Copyright (c) 2003 Hidetoshi Shimokawa
* Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the acknowledgement as bellow:
*
* This product includes software developed by K. Kobayashi and H. Shimokawa
*
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* $FreeBSD: src/sys/dev/firewire/firewire.c,v 1.110 2009/04/07 02:33:46 sbruno Exp $
*
*/
mutex_enter(&fc->atq->q_mtx);
fw_xferq_drain(fc->atq);
mutex_exit(&fc->atq->q_mtx);
mutex_enter(&fc->ats->q_mtx);
fw_xferq_drain(fc->ats);
mutex_exit(&fc->ats->q_mtx);
for (i = 0; i < fc->nisodma; i++)
fw_xferq_drain(fc->it[i]);
mutex_enter(&fc->tlabel_lock);
for (i = 0; i < 0x40; i++)
while ((xfer = STAILQ_FIRST(&fc->tlabels[i])) != NULL) {
if (firewire_debug)
printf("tl=%d flag=%d\n", i, xfer->flag);
xfer->resp = EAGAIN;
STAILQ_REMOVE_HEAD(&fc->tlabels[i], tlabel);
STAILQ_INSERT_TAIL(&xfer_drain, xfer, tlabel);
}
mutex_exit(&fc->tlabel_lock);
/*
* Called after bus reset.
*/
void
fw_busreset(struct firewire_comm *fc, uint32_t new_status)
{
struct firewire_softc *sc = device_private(fc->bdev);
struct firewire_dev_list *devlist;
struct firewire_dev_comm *fdc;
struct crom_src *src;
uint32_t *newrom;
if (fc->status == FWBUSMGRELECT)
callout_stop(&fc->bmr_callout);
fc->status = new_status;
fw_reset_csr(fc);
if (fc->status == FWBUSNOTREADY)
fw_init_crom(fc);
fw_reset_crom(fc);
/* How many safe this access? */
SLIST_FOREACH(devlist, &sc->devlist, link) {
fdc = device_private(devlist->dev);
if (fdc->post_busreset != NULL)
fdc->post_busreset(fdc);
}
/*
* If the old config rom needs to be overwritten,
* bump the businfo.generation indicator to
* indicate that we need to be reprobed
* See 1394a-2000 8.3.2.5.4 for more details.
* generation starts at 2 and rolls over at 0xF
* back to 2.
*
* A generation of 0 indicates a device
* that is not 1394a-2000 compliant.
* A generation of 1 indicates a device that
* does not change its Bus Info Block or
* Configuration ROM.
*/
#define FW_MAX_GENERATION 0xF
newrom = malloc(CROMSIZE, M_FW, M_NOWAIT | M_ZERO);
src = &fc->crom_src_buf->src;
crom_load(src, newrom, CROMSIZE);
if (memcmp(newrom, fc->config_rom, CROMSIZE) != 0) {
if (src->businfo.generation++ > FW_MAX_GENERATION)
src->businfo.generation = FW_GENERATION_CHANGEABLE;
memcpy((void *)fc->config_rom, newrom, CROMSIZE);
}
free(newrom, M_FW);
}
/* Call once after reboot */
void
fw_init(struct firewire_comm *fc)
{
int i;
/* Initialize Async handlers */
STAILQ_INIT(&fc->binds);
for (i = 0; i < 0x40; i++)
STAILQ_INIT(&fc->tlabels[i]);
/* DV depend CSRs see blue book */
#if 0
CSRARC(fc, oMPR) = 0x3fff0001; /* # output channel = 1 */
CSRARC(fc, oPCR) = 0x8000007a;
for (i = 4; i < 0x7c/4; i+=4)
CSRARC(fc, i + oPCR) = 0x8000007a;
CSRARC(fc, iMPR) = 0x00ff0001; /* # input channel = 1 */
CSRARC(fc, iPCR) = 0x803f0000;
for (i = 4; i < 0x7c/4; i+=4)
CSRARC(fc, i + iPCR) = 0x0;
#endif
fc->crom_src_buf = NULL;
}
/*
* Called by HCI driver when it has determined the number of
* isochronous DMA channels.
*/
void
fw_init_isodma(struct firewire_comm *fc)
{
unsigned i;
for (i = 0; i < fc->nisodma; i++) {
fc->it[i]->queued = 0;
fc->ir[i]->queued = 0;
mutex_exit(&fc->fc_mtx);
aprint_error_dev(fc->bdev, "no such binding\n");
return 1;
found:
#if 0
/* shall we do this? */
for (xfer = STAILQ_FIRST(&fwb->xferlist); xfer != NULL; xfer = next) {
next = STAILQ_NEXT(xfer, link);
fw_xfer_free(xfer);
}
STAILQ_INIT(&fwb->xferlist);
#endif
return 0;
}
int
fw_xferlist_add(struct fw_xferlist *q, struct malloc_type *type, int slen,
int rlen, int n, struct firewire_comm *fc, void *sc,
void (*hand)(struct fw_xfer *))
{
struct fw_xfer *xfer;
int i;
for (i = 0; i < n; i++) {
xfer = fw_xfer_alloc_buf(type, slen, rlen);
if (xfer == NULL)
return n;
xfer->fc = fc;
xfer->sc = sc;
xfer->hand = hand;
STAILQ_INSERT_TAIL(q, xfer, link);
}
return n;
}
mutex_enter(&fc->tlabel_lock);
for (i = 0; i < 0x40; i++) {
while ((xfer = STAILQ_FIRST(&fc->tlabels[i])) != NULL) {
if ((xfer->flag & FWXF_SENT) == 0)
/* not sent yet */
break;
if (timercmp(&xfer->tv, &tv, >))
/* the rests are newer than this */
break;
aprint_error_dev(fc->bdev,
"split transaction timeout: tl=0x%x flag=0x%02x\n",
i, xfer->flag);
fw_dump_hdr(&xfer->send.hdr, "send");
xfer->resp = ETIMEDOUT;
STAILQ_REMOVE_HEAD(&fc->tlabels[i], tlabel);
STAILQ_INSERT_TAIL(&xfer_timeout, xfer, tlabel);
}
}
mutex_exit(&fc->tlabel_lock);
fc->timeout(fc);
/*
* At boot stage, the device interrupt is disabled and
* We encounter a timeout easily. To avoid this,
* ignore clock interrupt for a while.
*/
if (watchdog_clock > WATCHDOG_HZ * 15)
firewire_xfer_timeout(fc);
else
watchdog_clock++;
mutex_enter(&fc->tlabel_lock);
#if 1 /* make sure the label is allocated */
STAILQ_FOREACH(txfer, &fc->tlabels[xfer->tl], tlabel)
if (txfer == xfer)
break;
if (txfer == NULL) {
mutex_exit(&fc->tlabel_lock);
aprint_error_dev(fc->bdev,
"the xfer is not in the queue (tlabel=%d, flag=0x%x)\n",
xfer->tl, xfer->flag);
fw_dump_hdr(&xfer->send.hdr, "send");
fw_dump_hdr(&xfer->recv.hdr, "recv");
KASSERT(FALSE);
return;
}
#endif
/*
* To obtain XFER structure by transaction label.
*/
static struct fw_xfer *
fw_tl2xfer(struct firewire_comm *fc, int node, int tlabel, int tcode)
{
struct fw_xfer *xfer;
int req;
mutex_enter(&fc->tlabel_lock);
STAILQ_FOREACH(xfer, &fc->tlabels[tlabel], tlabel)
if (xfer->send.hdr.mode.hdr.dst == node) {
mutex_exit(&fc->tlabel_lock);
KASSERT(xfer->tl == tlabel);
/* extra sanity check */
req = xfer->send.hdr.mode.hdr.tcode;
if (xfer->fc->tcode[req].valid_res != tcode) {
aprint_error_dev(fc->bdev,
"invalid response tcode (0x%x for 0x%x)\n",
tcode, req);
return NULL;
}
if (firewire_debug > 2)
printf("fw_tl2xfer: found tl=%d\n", tlabel);
return xfer;
}
mutex_exit(&fc->tlabel_lock);
if (firewire_debug > 1)
printf("fw_tl2xfer: not found tl=%d\n", tlabel);
return NULL;
}
/*
* To configure PHY.
*/
static void
fw_phy_config(struct firewire_comm *fc, int root_node, int gap_count)
{
struct fw_xfer *xfer;
struct fw_pkt *fp;
/* Invalidate all devices, just after bus reset. */
if (firewire_debug)
printf("iterate and invalidate all nodes\n");
mutex_enter(&fc->fc_mtx);
STAILQ_FOREACH(fwdev, &fc->devices, link)
if (fwdev->status != FWDEVINVAL) {
fwdev->status = FWDEVINVAL;
fwdev->rcnt = 0;
if (firewire_debug)
printf("Invalidate Dev ID: %08x%08x\n",
fwdev->eui.hi, fwdev->eui.lo);
} else
if (firewire_debug)
printf("Dev ID: %08x%08x already invalid\n",
fwdev->eui.hi, fwdev->eui.lo);
mutex_exit(&fc->fc_mtx);
fc = dfwdev->fc;
csr = dfwdev->csrrom;
node = dfwdev->dst;
/* First quad */
err = fw_explore_read_quads(dfwdev, CSRROMOFF, csr, 1);
if (err) {
aprint_error_dev(fc->bdev,
"node%d: explore_read_quads failure\n", node);
dfwdev->status = FWDEVINVAL;
return -1;
}
hdr = (struct csrhdr *)csr;
if (hdr->info_len != 4) {
if (firewire_debug)
printf("node%d: wrong bus info len(%d)\n",
node, hdr->info_len);
dfwdev->status = FWDEVINVAL;
return -1;
}
/* bus info */
err = fw_explore_read_quads(dfwdev, CSRROMOFF + 0x04, &csr[1], 4);
if (err) {
aprint_error_dev(fc->bdev, "node%d: error reading 0x04\n",
node);
dfwdev->status = FWDEVINVAL;
return -1;
}
binfo = (struct bus_info *)&csr[1];
if (binfo->bus_name != CSR_BUS_NAME_IEEE1394) {
aprint_error_dev(fc->bdev, "node%d: invalid bus name 0x%08x\n",
node, binfo->bus_name);
dfwdev->status = FWDEVINVAL;
return -1;
}
if (firewire_debug)
printf("node(%d) BUS INFO BLOCK:\n"
"irmc(%d) cmc(%d) isc(%d) bmc(%d) pmc(%d) "
"cyc_clk_acc(%d) max_rec(%d) max_rom(%d) "
"generation(%d) link_spd(%d)\n",
node, binfo->irmc, binfo->cmc, binfo->isc,
binfo->bmc, binfo->pmc, binfo->cyc_clk_acc,
binfo->max_rec, binfo->max_rom,
binfo->generation, binfo->link_spd);
mutex_enter(&fc->fc_mtx);
STAILQ_FOREACH(fwdev, &fc->devices, link)
if (FW_EUI64_EQUAL(fwdev->eui, binfo->eui64))
break;
mutex_exit(&fc->fc_mtx);
if (fwdev == NULL) {
/* new device */
fwdev = malloc(sizeof(struct fw_device), M_FW, M_WAITOK | M_ZERO);
fwdev->fc = fc;
fwdev->eui = binfo->eui64;
fwdev->dst = dfwdev->dst;
fwdev->maxrec = dfwdev->maxrec;
fwdev->status = FWDEVNEW;
/*
* Pre-1394a-2000 didn't have link_spd in
* the Bus Info block, so try and use the
* speed map value.
* 1394a-2000 compliant devices only use
* the Bus Info Block link spd value, so
* ignore the speed map altogether. SWB
*/
if (binfo->link_spd == FWSPD_S100 /* 0 */) {
aprint_normal_dev(fc->bdev,
"Pre 1394a-2000 detected\n");
fwdev->speed = fc->speed_map->speed[fc->nodeid][node];
} else
fwdev->speed = binfo->link_spd;
/*
* Test this speed with a read to the CSRROM.
* If it fails, slow down the speed and retry.
*/
while (fwdev->speed > FWSPD_S100 /* 0 */) {
err = fw_explore_read_quads(fwdev, CSRROMOFF,
&speed_test, 1);
if (err) {
aprint_error_dev(fc->bdev, "fwdev->speed(%s)"
" decremented due to negotiation\n",
fw_linkspeed[fwdev->speed]);
fwdev->speed--;
} else
break;
}
/*
* If the fwdev is not found in the
* fc->devices TAILQ, then we will add it.
*/
pfwdev = NULL;
mutex_enter(&fc->fc_mtx);
STAILQ_FOREACH(tfwdev, &fc->devices, link) {
if (tfwdev->eui.hi > fwdev->eui.hi ||
(tfwdev->eui.hi == fwdev->eui.hi &&
tfwdev->eui.lo > fwdev->eui.lo))
break;
pfwdev = tfwdev;
}
if (pfwdev == NULL)
STAILQ_INSERT_HEAD(&fc->devices, fwdev, link);
else
STAILQ_INSERT_AFTER(&fc->devices, pfwdev, fwdev, link);
mutex_exit(&fc->fc_mtx);
if (err) {
if (firewire_debug)
printf("explore csrblock failed err(%d)\n", err);
fwdev->status = FWDEVINVAL;
fwdev->csrrom[0] = 0;
}
return err;
}
/*
* Find the self_id packet for a node, ignoring sequels.
*/
static union fw_self_id *
fw_find_self_id(struct firewire_comm *fc, int node)
{
uint32_t i;
union fw_self_id *s;
for (i = 0; i < fc->topology_map->self_id_count; i++) {
s = &fc->topology_map->self_id[i];
if (s->p0.sequel)
continue;
if (s->p0.phy_id == node)
return s;
}
return 0;
}
static void
fw_explore(struct firewire_comm *fc)
{
struct fw_device *dfwdev;
union fw_self_id *fwsid;
int node, err, i, todo, todo2, trys;
char nodes[63];
todo = 0;
dfwdev = malloc(sizeof(*dfwdev), M_TEMP, M_WAITOK);
/*
* Tell config we've scanned the bus.
*
* XXX This is not right -- we haven't actually scanned it. We
* probably ought to call this after the first bus exploration.
*
* bool once = false;
* ...
* fw_attach_dev(fc);
* if (!once) {
* config_pending_decr();
* once = true;
* }
*/
config_pending_decr(fc->bdev);
switch (reg->val) {
case CSR_PROTAVC:
return "av/c";
case CSR_PROTCAL:
return "cal";
case CSR_PROTEHS:
return "ehs";
case CSR_PROTHAVI:
return "havi";
case CSR_PROTCAM104:
return "cam104";
case CSR_PROTCAM120:
return "cam120";
case CSR_PROTCAM130:
return "cam130";
case CSR_PROTDPP:
return "printer";
case CSR_PROTIICP:
return "iicp";
case CSRVAL_T10SBP2:
return "sbp";
default:
if (firewire_debug)
printf("%s: reg->val 0x%x\n",
__func__, reg->val);
return "sbp";
}
}
for (fwdev = STAILQ_FIRST(&fc->devices); fwdev != NULL; fwdev = next) {
next = STAILQ_NEXT(fwdev, link);
if (fwdev->rcnt > 0 && fwdev->rcnt > hold_count) {
/*
* Remove devices which have not been seen
* for a while.
*/
SLIST_FOREACH(devlist, &sc->devlist, link)
if (devlist->fwdev == fwdev)
break;
if (devlist == NULL)
continue;
if (devlist->fwdev != fwdev)
panic("already detached");
/*
* Find the root node, if it is not
* Cycle Master Capable, then we should
* override this and become the Cycle
* Master
*/
static int
fw_bmr(struct firewire_comm *fc)
{
struct fw_device fwdev;
union fw_self_id *self_id;
int cmstr;
uint32_t quad;
/* Check to see if the current root node is cycle master capable */
self_id = fw_find_self_id(fc, fc->max_node);
if (fc->max_node > 0) {
/* XXX check cmc bit of businfo block rather than contender */
if (self_id->p0.link_active && self_id->p0.contender)
cmstr = fc->max_node;
else {
aprint_normal_dev(fc->bdev,
"root node is not cycle master capable\n");
/* XXX shall we be the cycle master? */
cmstr = fc->nodeid;
/* XXX need bus reset */
}
} else
cmstr = -1;
aprint_normal_dev(fc->bdev, "bus manager %d%s\n",
CSRARC(fc, BUS_MGR_ID),
(CSRARC(fc, BUS_MGR_ID) != fc->nodeid) ? " (me)" : "");
if (CSRARC(fc, BUS_MGR_ID) != fc->nodeid)
/* We are not the bus manager */
return 0;
/* Optimize gapcount */
if (fc->max_hop <= MAX_GAPHOP)
fw_phy_config(fc, cmstr, gap_cnt[fc->max_hop]);
/* If we are the cycle master, nothing to do */
if (cmstr == fc->nodeid || cmstr == -1)
return 0;
/* Bus probe has not finished, make dummy fwdev for cmstr */
memset(&fwdev, 0, sizeof(fwdev));
fwdev.fc = fc;
fwdev.dst = cmstr;
fwdev.speed = 0;
fwdev.maxrec = 8; /* 512 */
fwdev.status = FWDEVINIT;
/* Set cmstr bit on the cycle master */
quad = htonl(1 << 8);
fwmem_write_quad(&fwdev, NULL, 0/*spd*/, 0xffff, 0xf0000000 | STATE_SET,
&quad, fw_asy_callback_free);