summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/pci/ldtinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/pci/ldtinit.c')
-rw-r--r--cfe/cfe/pci/ldtinit.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/cfe/cfe/pci/ldtinit.c b/cfe/cfe/pci/ldtinit.c
new file mode 100644
index 0000000..26025af
--- /dev/null
+++ b/cfe/cfe/pci/ldtinit.c
@@ -0,0 +1,641 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * LDT Fabric Initialization File: ldtinit.c
+ *
+ *********************************************************************
+ *
+ * Copyright 2001,2002,2003
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions
+ * as they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. The "Broadcom Corporation"
+ * name may not be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR 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), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************* */
+/*
+ * Copyright 2001,2002
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions as
+ * they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. Neither the "Broadcom
+ * Corporation" name nor any trademark or logo of Broadcom
+ * Corporation may be used to endorse or promote products
+ * derived from this software without the prior written
+ * permission of Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR 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), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * ldtinit.c: generic LDT fabric initialization and capability
+ * management.
+ */
+
+#include "lib_types.h"
+#include "lib_printf.h"
+#include "cfe_timer.h"
+
+#include "pcivar.h"
+#include "pcireg.h"
+#include "ldtreg.h"
+
+/* Write-to-clear bit masks */
+
+#if CFG_LDT_REV_017 /* XXX not really the right test */
+#define LDT_LINKCTRL_WC (LDT_LINKCTRL_CRCERROR_MASK)
+#else
+#define LDT_LINKCTRL_WC (LDT_LINKCTRL_LINKFAIL | LDT_LINKCTRL_CRCERROR_MASK)
+#endif
+
+
+/* LDT capability lookup. */
+
+unsigned
+pci_find_ldt_cap (pcitag_t tag, int secondary)
+{
+ pcireg_t cpr;
+ pcireg_t cr;
+ int offset, prev;
+ int type;
+
+ cpr = pci_conf_read(tag, PCI_CAPLISTPTR_REG);
+ offset = PCI_CAPLIST_PTR(cpr) &~ 0x3;
+ prev = 0;
+
+ while (offset != 0 && offset != prev) {
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "read cap offset %04x\n", offset);
+#endif
+ cr = pci_conf_read(tag, offset);
+ if (PCI_CAPLIST_CAP(cr) == PCI_CAP_LDT) {
+ type = LDT_COMMAND_TYPE(cr);
+ if (secondary && type == LDT_COMMAND_TYPE_HOST)
+ return offset;
+ if (!secondary && type == LDT_COMMAND_TYPE_SLAVE)
+ return offset;
+ }
+ prev = offset;
+ offset = PCI_CAPLIST_NEXT(cr) &~ 0x3;
+ }
+ return 0;
+}
+
+
+/* LDT utility functions, mostly for capabilities. */
+
+static pcireg_t
+ldt_get_link(pcitag_t tag, int offset, int index)
+{
+ return pci_conf_read(tag, offset + LDT_LINK_OFF(index));
+}
+
+static void
+ldt_set_link(pcitag_t tag, int offset, int index, pcireg_t lr)
+{
+ pci_conf_write(tag, offset + LDT_LINK_OFF(index), lr);
+}
+
+#if (LDT_DEBUG != 0)
+static void
+ldt_show_cap(pcitag_t tag, int offset, int secondary)
+{
+ printf(" Cmd %08x", pci_conf_read(tag, offset));
+ offset += 4;
+ printf(" Lnk0 %08x", pci_conf_read(tag, offset));
+ offset += 4;
+ if (!secondary) {
+ printf(" Lnk1 %08x", pci_conf_read(tag, offset));
+ offset += 4;
+ }
+ printf(" Freq0 %08x", pci_conf_read(tag, offset));
+ offset += 4;
+ if (!secondary) {
+ printf(" Freq1 %08x", pci_conf_read(tag, offset));
+ offset += 4;
+ }
+ printf("\n");
+}
+#else
+static void
+ldt_show_cap(pcitag_t tag, int offset, int secondary)
+{
+}
+#endif
+
+
+/* LDT bus initialization and sizing. */
+
+/* We expect the entire chain to be ready at approximately the same
+ time, but we add some delay here for possible node-to-node
+ differences.
+
+ Empirically, neither InitDone nor LinkFail is reported for an
+ unconnected link. Thus we do not expect the outgoing link of a
+ terminating tunnel node to become ready.
+
+ Also, CRC errors are observed to occur with InitDone, so link
+ errors do not necessarily force LinkFail.
+*/
+
+static int
+ldt_wait_ready (pcitag_t tag, int offset, int index)
+{
+ int count;
+ pcireg_t lr;
+ int linkerr;
+
+ linkerr = 0;
+ count = 0x10000; /* empirical */
+ do {
+ if (--count == 0)
+ return 1;
+ lr = ldt_get_link(tag, offset, index);
+ if ((lr & (LDT_LINKCTRL_LINKFAIL | LDT_LINKCTRL_CRCERROR_MASK)) != 0)
+ linkerr = 1;
+ } while ((lr & (LDT_LINKCTRL_INITDONE | LDT_LINKCTRL_LINKFAIL)) == 0);
+
+ return linkerr;
+}
+
+static void
+ldt_end_chain (pcitag_t tag, int offset, int index)
+{
+ pcireg_t lr, t;
+
+ lr = ldt_get_link(tag, offset, index);
+ lr |= LDT_LINKCTRL_EOC;
+ ldt_set_link(tag, offset, index, lr);
+ lr |= LDT_LINKCTRL_TXOFF;
+ ldt_set_link(tag, offset, index, lr);
+ t = ldt_get_link(tag, offset, index); /* push */
+}
+
+
+static uint16_t
+ldt_freq_cap (pcitag_t tag, int offset, int index)
+{
+ pcireg_t cmd, cr;
+ uint16_t freq_cap;
+
+ cmd = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ if (LDT_COMMAND_TYPE(cmd) == LDT_COMMAND_TYPE_HOST) {
+ cr = pci_conf_read(tag, offset + LDT_FREQ_OFF);
+ if (LDT_REVISION_ID(cr) == LDT_REV_017) {
+ /* REV 0.17 has restricted support for setting
+ frequencies. We assume that this is the host bridge in
+ pseudo-1.0x mode, in which case the desired maximum
+ frequency was left in the LDT_FREQ register and all
+ lower frequencies are supported. */
+ freq_cap = (1 << (LDT_LINKFREQ(cr) + 1)) - 1;
+ } else {
+ freq_cap = LDT_LINKFREQ_CAP(cr);
+ }
+ } else {
+ cr = pci_conf_read(tag, offset + LDT_FREQ0_OFF);
+ if (LDT_REVISION_ID(cr) == LDT_REV_017) {
+ /* REV 0.17 has restricted support for setting frequencies.
+ This is not the host bridge. What to do? XXX */
+ freq_cap = (1 << LDT_FREQ_200);
+ } else {
+ cr = pci_conf_read(tag, offset + LDT_FREQn_OFF(index));
+ freq_cap = LDT_LINKFREQ_CAP(cr);
+ }
+ }
+ return freq_cap;
+}
+
+static uint8_t
+ldt_max_freq (uint16_t freq_cap)
+{
+ unsigned ldt_freq;
+
+ /* 200 MHz (encoded as 1 << 0) is required for all devices */
+ freq_cap |= (1 << LDT_FREQ_200);
+
+ ldt_freq = 0;
+
+ while (freq_cap != 1) {
+ ldt_freq++;
+ freq_cap >>= 1;
+ }
+
+ return (ldt_freq >= LDT_FREQ_200 && ldt_freq <= LDT_FREQ_1000) ?
+ ldt_freq : LDT_FREQ_200;
+}
+
+static void
+ldt_set_freq (pcitag_t tag, int offset, int index, uint8_t freq)
+{
+ pcireg_t cmd, cr;
+
+ cmd = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ if (LDT_COMMAND_TYPE(cmd) == LDT_COMMAND_TYPE_HOST) {
+ cr = pci_conf_read(tag, offset + LDT_FREQ_OFF);
+ cr &=~ LDT_LINKFREQ_MASK;
+ cr |= (freq << LDT_LINKFREQ_SHIFT);
+ pci_conf_write(tag, offset + LDT_FREQ_OFF, cr);
+ } else {
+ cr = pci_conf_read(tag, offset + LDT_FREQn_OFF(index));
+ cr &=~ LDT_LINKFREQ_MASK;
+ cr |= (freq << LDT_LINKFREQ_SHIFT);
+ pci_conf_write(tag, offset + LDT_FREQn_OFF(index), cr);
+ }
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "set link %d freq = %02x\n", index, freq);
+#endif
+}
+
+
+/* LDT fabric initialization. See LDT Spec, Section 13.3. */
+static int
+ldt_fabric_init (pcitag_t br_tag, int br_offset, int bus, pci_flags_t flags)
+{
+ int next_free_id;
+ pcitag_t prev_tag, tag;
+ int offset, prev_offset;
+ int link, prev_link;
+ uint16_t prev_cap;
+ pcireg_t cmd, lr, id, t;
+ int double_ended;
+ int linkerr;
+
+ prev_tag = br_tag; prev_offset = br_offset; prev_link = 0;
+ /* Since there is no direct peer-to-peer traffic, there is no
+ point in configuring a downstream link with more capability
+ than the current one. */
+ prev_cap = ldt_freq_cap(br_tag, br_offset, 0);
+
+ next_free_id = 1;
+ double_ended = 0;
+
+#if (LDT_DEBUG != 0)
+ printf("Link sizing for bus %d, bridge freq cap %04x\n",
+ bus, ldt_freq_cap(br_tag, br_offset, 0));
+#endif
+ for (;;) {
+
+ tag = pci_make_tag(bus, 0, 0);
+
+ id = pci_conf_read(tag, PCI_ID_REG);
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "ldt_fabric_init: id register %08x\n", id);
+#endif
+ if (PCI_VENDOR(id) == 0xffff) {
+ /* The incoming link had InitDone set, but we got an NXA
+ trying to read the vendor id. Either the reverse link
+ is broken or we have found an LDT-Lite node. For now,
+ assume the latter. Since an LDT-Lite device cannot be
+ a tunnel, assume the chain terminates here. */
+ pci_tagprintf(tag, "assumed LDT-LITE device (virtual unit %d)\n",
+ next_free_id);
+ break;
+ }
+
+ offset = pci_find_ldt_cap(tag, LDT_PRIMARY);
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "ldt_fabric_init: offset %08x\n", offset);
+#endif
+ if (offset == 0) {
+ /* There is no primary interface; we must have found a host. */
+ offset = pci_find_ldt_cap(tag, LDT_SECONDARY);
+ if (offset != 0) {
+ lr = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ lr |= LDT_COMMAND_DOUBLE_ENDED;
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, lr);
+ double_ended = 1;
+ }
+ break;
+ }
+
+ /* Otherwise, we have the primary interface. */
+
+ /* Rewrite the old value to set master host bit. */
+ cmd = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "ldt_fabric_init: set master host\n");
+#endif
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, cmd);
+ cmd = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF); /* push */
+
+ id = pci_conf_read(tag, PCI_ID_REG);
+
+ /* Update the unit id, gingerly. */
+ cmd &= ~LDT_COMMAND_UNIT_ID_MASK;
+ cmd |= (next_free_id << LDT_COMMAND_UNIT_ID_SHIFT);
+#if (LDT_DEBUG != 0)
+ pci_tagprintf(tag, "ldt_fabric_init: set unit id %d\n", next_free_id);
+#endif
+ if (!pci_conf_write_acked(tag, offset + LDT_COMMAND_CAP_OFF, cmd)) {
+ pci_tagprintf(tag, "no ack of id update to %d\n", next_free_id);
+ }
+
+ /* The unit id just changed. Update the tag */
+ tag = pci_make_tag(bus, next_free_id, 0);
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "ldt_fabric_init: check unit id\n");
+#endif
+ t = pci_conf_read(tag, PCI_ID_REG);
+ if (t != id) {
+ pci_tagprintf(tag, "id mismatch: old %08x, new %08x\n", id, t);
+ }
+
+ next_free_id += LDT_COMMAND_UNIT_COUNT(cmd);
+
+ link = LDT_COMMAND_MASTER_HOST(cmd); /* Upstream link index */
+
+ /* LDT Rev 0.17 does not support frequency updates. */
+ if ((flags & PCI_FLG_LDT_REV_017) == 0) {
+ /* Find common frequency for upstream link. */
+ uint16_t link_cap, freq_cap_in, freq_cap_out;
+ uint8_t ldt_freq;
+
+ freq_cap_out = ldt_freq_cap(prev_tag, prev_offset, prev_link);
+ freq_cap_in = ldt_freq_cap(tag, offset, link);
+ link_cap = freq_cap_in & freq_cap_out;
+ ldt_freq = ldt_max_freq(link_cap & prev_cap);
+
+#if (LDT_DEBUG != 0)
+ pci_tagprintf(tag, "set freq %d\n", ldt_freq);
+#endif
+ /* Set up frequency registers, next warm reset installs. */
+ ldt_set_freq(prev_tag, prev_offset, prev_link, ldt_freq);
+ ldt_set_freq(tag, offset, link, ldt_freq);
+
+ prev_cap &= link_cap;
+ }
+
+ link ^= 1; /* Downstream */
+ linkerr = ldt_wait_ready(tag, offset, link);
+ lr = ldt_get_link(tag, offset, link);
+ ldt_set_link(tag, offset, link, lr | LDT_LINKCTRL_WC);
+
+#if (LDT_DEBUG != 0)
+ pci_tagprintf(tag, "node: up %d down %d:\n", link ^ 1, link);
+#endif
+ ldt_show_cap(tag, offset, LDT_PRIMARY);
+
+ if (linkerr || next_free_id > 0x1f) {
+ /* No downstream link or too many devices, set end of chain */
+ ldt_end_chain(tag, offset, link);
+ break;
+ }
+
+ prev_tag = tag; prev_offset = offset; prev_link = link;
+ }
+
+ return double_ended;
+}
+
+
+static int
+ldt_fabric_reinit (int bus)
+{
+ int next_free_id;
+ pcitag_t tag;
+ int offset;
+ int link;
+ pcireg_t cmd, lr, id, t;
+ int linkerr;
+
+ next_free_id = 1;
+
+#if (LDT_DEBUG != 0)
+ printf("Link resizing for bus %d\n", bus);
+#endif
+ for (;;) {
+
+ tag = pci_make_tag(bus, 0, 0);
+
+ id = pci_conf_read(tag, PCI_ID_REG);
+ if (PCI_VENDOR(id) == 0xffff) {
+ /* Per the init pass, assume this indicates a link to an
+ LDT-Lite node, and the chain terminates here. */
+ break;
+ }
+
+ offset = pci_find_ldt_cap(tag, LDT_PRIMARY);
+ if (offset == 0) {
+ /* There is no primary interface; we must have found a host. */
+ offset = pci_find_ldt_cap(tag, LDT_SECONDARY);
+ if (offset != 0) {
+ lr = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ lr |= LDT_COMMAND_DOUBLE_ENDED;
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, lr);
+ }
+ break;
+ }
+
+ /* Otherwise, we have the primary interface. */
+
+ /* Rewrite the old value to set master host bit. */
+ cmd = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, cmd);
+ cmd = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+
+ id = pci_conf_read(tag, PCI_ID_REG);
+
+ /* (Re)update the unit id. See above. */
+ cmd &= ~LDT_COMMAND_UNIT_ID_MASK;
+ cmd |= (next_free_id << LDT_COMMAND_UNIT_ID_SHIFT);
+ if (!pci_conf_write_acked(tag, offset + LDT_COMMAND_CAP_OFF, cmd)) {
+ pci_tagprintf(tag, "no ack of id update to %d\n", next_free_id);
+ }
+
+ /* The unit id just changed. Update the tag */
+ tag = pci_make_tag(bus, next_free_id, 0);
+ t = pci_conf_read(tag, PCI_ID_REG); /* for good measure */
+ if (t != id) {
+ pci_tagprintf(tag, "id mismatch: old %08x, new %08x\n", id, t);
+ }
+
+ next_free_id += LDT_COMMAND_UNIT_COUNT(cmd);
+
+ link = LDT_COMMAND_MASTER_HOST(cmd); /* Upstream link index */
+ link ^= 1; /* Downstream */
+
+ linkerr = ldt_wait_ready(tag, offset, link);
+
+ lr = ldt_get_link(tag, offset, link);
+ ldt_set_link(tag, offset, link, lr | LDT_LINKCTRL_WC);
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "node: up %d down %d:\n", link ^ 1, link);
+ ldt_show_cap(tag, offset, LDT_PRIMARY);
+#endif
+ if (linkerr || next_free_id > 0x1f) {
+ /* No downstream link or too many devices, set end of chain */
+ ldt_end_chain(tag, offset, link);
+ break;
+ }
+ }
+ return next_free_id - 1;
+}
+
+
+/* LDT link reset (warm or cold as set by caller) */
+
+void
+ldt_link_reset (pcitag_t tag, int delay)
+{
+ pcireg_t brctl;
+
+ /* This code may be necessary for LDT buses behind bridges (none
+ of which yet exist) but seems to be a bad idea for the SB-1250
+ LDT bus in pass 1 parts. Note that if we do reset, we must
+ delay to give any attached devices a chance to (re)initialize
+ per the PCI spec. */
+
+ /* Attempt a Secondary Bus Reset. */
+ brctl = pci_conf_read(tag, PPB_BRCTL_INTERRUPT_REG);
+ brctl |= PPB_BRCTL_SECONDARY_RESET;
+ pci_conf_write(tag, PPB_BRCTL_INTERRUPT_REG, brctl);
+
+ brctl = pci_conf_read(tag, PPB_BRCTL_INTERRUPT_REG);
+ if ((brctl & PPB_BRCTL_SECONDARY_RESET) != 0) {
+ int i;
+ /* Bit can be written, assume soft reset is implemented. */
+ brctl &=~ PPB_BRCTL_SECONDARY_RESET;
+ pci_conf_write(tag, PPB_BRCTL_INTERRUPT_REG, brctl);
+
+ /* Add some delay (duration is a guess) */
+ for (i = 0; i < delay; i++)
+ (void)pci_conf_read(tag, PPB_BRCTL_INTERRUPT_REG);
+ /* Alternatively, wait for LinkFail or InitDone. */
+ }
+}
+
+
+/* LDT bridge and fabric initialization for a secondary chain */
+
+int
+ldt_chain_init (pcitag_t tag, int bus, pci_flags_t flags)
+{
+ int offset;
+ int double_ended;
+ int linkerr;
+ pcireg_t cr, lr;
+ int ndev, no_probe;
+
+#if 0
+ /* This code may be necessary for LDT buses behind bridges (none
+ of which yet exist) but seems to be a bad idea for the SB-1250
+ LDT bus in pass 1 parts. Note that if we do reset, we must
+ delay to give any attached devices a chance to (re)initialize
+ per the PCI spec. */
+
+ /* Attempt a Secondary Bus Reset. */
+ ldt_link_reset(tag, 100);
+#endif /* 0 */
+
+ /* To avoid a chip erratum, we must prevent Type 0 configuration
+ probes that get NXAs on a double hosted chain. */
+ no_probe = 0;
+
+ offset = pci_find_ldt_cap(tag, LDT_SECONDARY);
+ if (offset != 0) {
+ linkerr = ldt_wait_ready(tag, offset, 0);
+
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "bridge secondary:\n");
+ ldt_show_cap(tag, offset, LDT_SECONDARY);
+#endif
+ if (linkerr) {
+ pci_tagprintf(tag, "secondary bad or never ready\n");
+ } else {
+ lr = ldt_get_link(tag, offset, 0);
+ if ((lr & LDT_LINKCTRL_INITDONE) != 0)
+ double_ended = ldt_fabric_init(tag, offset, bus, flags);
+ else {
+ ldt_end_chain(tag, offset, 0);
+ double_ended = 0;
+ }
+ cr = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ if (double_ended)
+ cr |= LDT_COMMAND_DOUBLE_ENDED;
+ else
+ cr &=~ LDT_COMMAND_DOUBLE_ENDED;
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, cr);
+
+ /* Rev 0.17 does not support dynamic link resizing. */
+ if ((flags & PCI_FLG_LDT_REV_017) == 0) {
+ /* Issue a warm reset to update link frequencies. */
+ cr = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ cr |= LDT_COMMAND_WARM_RESET;
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, cr);
+ cr = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ ldt_link_reset(tag, 100);
+ ldt_wait_ready(tag, offset, 0);
+
+#if (LDT_DEBUG > 1)
+ pci_tagprintf(tag, "bridge secondary:\n");
+ ldt_show_cap(tag, offset, LDT_SECONDARY);
+#endif
+ /* After reset, let secondary devices reinitialize. */
+ cfe_sleep(CFE_HZ/2);
+
+ ndev = ldt_fabric_reinit(bus);
+
+ if (double_ended) {
+ cr = pci_conf_read(tag, offset + LDT_COMMAND_CAP_OFF);
+ cr |= LDT_COMMAND_DOUBLE_ENDED;
+ pci_conf_write(tag, offset + LDT_COMMAND_CAP_OFF, cr);
+
+ /* Bug workaround -- don't scan simple dual-hosted chain */
+ if (ndev == 0)
+ no_probe = 1;
+ }
+ }
+ }
+ }
+
+ return no_probe;
+}