/* * $QNXLicenseC: * Copyright 2007, QNX Software Systems. All Rights Reserved. * * You must obtain a written license from and pay applicable license fees to QNX * Software Systems before you may reproduce, modify or distribute this software, * or any work that includes all or part of this software. Free development * licenses are available for evaluation and non-commercial purposes. For more * information visit http://licensing.qnx.com or email licensing@qnx.com. * * This file may contain contributions from others. Please review this entire * file for other proprietary rights or license notices, as well as the QNX * Development Suite License Guide at http://licensing.qnx.com/license-guide/ * for other information. * $ */ #include "mpc85xx.h" #include #define PPC_E300C3 0x8085 static void mpc85xx_stop(struct ifnet *ifp, int disable); static void mpc85xx_destroy(mpc85xx_dev_t *mpc85xx, int how); static void mpc85xx_reset(mpc85xx_dev_t *mpc85xx); static int mpc85xx_init(struct ifnet *ifp); static void mpc85xx_rx_mbuf_free(mpc85xx_dev_t *mpc85xx); static int mpc85xx_attach(struct device *, struct device *, void *); static int mpc85xx_detach(struct device *, int); static void mpc85xx_shutdown(void *); struct tsec_params { paddr_t paddr; unsigned int irq_present; unsigned int irq_rx; unsigned int irq_tx; unsigned int irq_err; unsigned int mac_present; unsigned char mac[6]; unsigned int phy_present; unsigned int phy_addr; }; struct mpc_arg { void *dll_hdl; char *options; int idx; uint32_t iobase; struct tsec_params *tparms_p; }; CFATTACH_DECL(mpc85xx, sizeof(mpc85xx_dev_t), NULL, mpc85xx_attach, mpc85xx_detach, NULL); #define NIC_PRIORITY 21 static char *mpc_opts [] = { "receive", // 0 "transmit", // 1 "pauseignore", // 2 "pausesuppress", // 3 "loopback", // 4 "fifo", // 5 "probe_phy", // 6 "phy_addr", // 7 "syspage", // 8 "phy_incr", // 9 "kermask", // 10 "etsec", // 11 "enable_tx_hw_csum", // 12 "rx_delay", // 13 "rx_frame", // 14 NULL }; // // called from mpc85xx_detect() // static int mpc85xx_parse_options (mpc85xx_dev_t *mpc85xx, char *options, nic_config_t *cfg) { char *value, *restore, *c; int opt; int rc = 0; int err = EOK; restore = NULL; while (options && *options != '\0') { c = options; restore = strchr(options, ','); opt = getsubopt (&options, mpc_opts, &value); switch (opt) { case 0: if (mpc85xx) { mpc85xx->num_rx_descriptors = strtoul (value, 0, 0); } break; case 1: if (mpc85xx) { mpc85xx->num_tx_descriptors = strtoul (value, 0, 0); } break; case 2: if (mpc85xx) { mpc85xx->flowctl_flag &= ~MACCFG1_RX_FLOW; } break; case 3: if (mpc85xx) { mpc85xx->flowctl_flag &= ~MACCFG1_TX_FLOW; } break; case 4: if (mpc85xx) { mpc85xx->loopback |= MACCFG1_LP_BACK; } break; case 5: if (mpc85xx) { mpc85xx->fifo = strtoul(value, 0, 0); } break; case 6: if (mpc85xx) { mpc85xx->probe_phy = strtol_loc(value, 0, 0); } break; case 7: if (cfg) { cfg->phy_addr = strtol_loc(value, 0, 0); } break; case 8: if (mpc85xx) { mpc85xx->use_syspage = 1; } break; case 9: if (mpc85xx) { mpc85xx->phy_incr = strtol_loc(value, 0, 0); } break; case 10: if (mpc85xx) { if (value) { mpc85xx->kermask = strtol_loc(value, 0, 0); if (mpc85xx->kermask) { mpc85xx->kermask = 1; } } } break; case 11: if (mpc85xx) { if (value) { mpc85xx->etsec = strtol_loc(value, 0, 0); } } break; case 12: if (mpc85xx) { if (value) { mpc85xx->enable_tx_hw_csum = strtol_loc(value, 0, 0); } } break; case 13: if (mpc85xx) { if (value) { mpc85xx->rx_delay = strtol_loc(value, 0, 0); } } break; case 14: if (mpc85xx) { if (value) { mpc85xx->rx_frame = strtol_loc(value, 0, 0); } } break; default: if (nic_parse_options (cfg, value) != EOK) { log(LOG_ERR, "%s(): unknown option %s", __FUNCTION__, c); err = EINVAL; rc = -1; } break; } if (restore != NULL) *restore = ','; } errno = err; return (rc); } // // convert virtual to physical address // static paddr_t vtophys (void *addr) { off64_t offset; if (mem_offset64 (addr, NOFD, 1, &offset, 0) == -1) { return (-1); } return (offset); } // // called from mpc85xx_detect() to allocate resources // static int mpc85xx_attach(struct device *parent, struct device *self, void *aux) { mpc85xx_dev_t *mpc85xx; nic_config_t *cfg; int rc, idx; struct ifnet *ifp; size_t size; char *options; uint32_t iobase; struct tsec_params *tparms_p; struct _iopkt_self *iopkt; struct mpc_arg *mpc_arg; int single; iopkt = iopkt_selfp; mpc85xx = (mpc85xx_dev_t *)self; mpc_arg = aux; options = mpc_arg->options; idx = mpc_arg->idx; iobase = mpc_arg->iobase; tparms_p = mpc_arg->tparms_p; mpc85xx->dev.dv_dll_hdl = mpc_arg->dll_hdl; mpc85xx->iopkt = iopkt; mpc85xx->iid_rx = -1; mpc85xx->iid_err = -1; ifp = &mpc85xx->ecom.ec_if; ifp->if_softc = mpc85xx; cfg = &mpc85xx->cfg; if (cfg->verbose > 3) { log(LOG_ERR, "%s(): starting: idx %d\n", __FUNCTION__, idx); } if ((mpc85xx->sdhook = shutdownhook_establish(mpc85xx_shutdown, mpc85xx)) == NULL) { return ENOMEM; } mpc85xx->iobase = mpc85xx->phy_base = (uintptr_t) iobase; // set default mac address cfg->current_address[0] = 0x00; cfg->current_address[1] = 0x01; cfg->current_address[2] = 0x02; cfg->current_address[3] = 0x03; cfg->current_address[4] = 0x04; cfg->current_address[5] = 0x05; if (tparms_p == NULL) { switch (idx) { case 0: mpc85xx->iobase += MPC_TSEC1_BASE; if (cfg->num_irqs == 0) { if (PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C1) { cfg->irq[0] = PPC83xx_INTR_GEN_TSEC1_Tx; cfg->irq[1] = PPC83xx_INTR_GEN_TSEC1_Rx; cfg->irq[2] = PPC83xx_INTR_GEN_TSEC1_Err; } else if (PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C3) { /* MPC8313E errata - IRQs are swapped in IPIC */ cfg->irq[0] = PPC83xx_INTR_GEN_TSEC2_Err; cfg->irq[1] = PPC83xx_INTR_GEN_TSEC2_Rx; cfg->irq[2] = PPC83xx_INTR_GEN_TSEC2_Tx; } else { cfg->irq[0] = PPC85xx_INTR_TSEC1_TX; cfg->irq[1] = PPC85xx_INTR_TSEC1_RX; cfg->irq[2] = PPC85xx_INTR_TSEC1_ERROR; } cfg->num_irqs = 3; } break; case 1: mpc85xx->iobase += MPC_TSEC2_BASE; if (cfg->num_irqs == 0) { if (PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C1) { cfg->irq[0] = PPC83xx_INTR_GEN_TSEC2_Tx; cfg->irq[1] = PPC83xx_INTR_GEN_TSEC2_Rx; cfg->irq[2] = PPC83xx_INTR_GEN_TSEC2_Err; } else if (PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C3) { /* MPC8313E errata - IRQs are swapped in IPIC */ cfg->irq[0] = PPC83xx_INTR_GEN_TSEC1_Err; cfg->irq[1] = PPC83xx_INTR_GEN_TSEC1_Rx; cfg->irq[2] = PPC83xx_INTR_GEN_TSEC1_Tx; } else { cfg->irq[0] = PPC85xx_INTR_TSEC2_TX; cfg->irq[1] = PPC85xx_INTR_TSEC2_RX; cfg->irq[2] = PPC85xx_INTR_TSEC2_ERROR; } cfg->num_irqs = 3; } break; case 2: mpc85xx->iobase += MPC_TSEC3_BASE; if (cfg->num_irqs == 0) { cfg->irq[0] = PPC85xx_INTR_TSEC3_TX; cfg->irq[1] = PPC85xx_INTR_TSEC3_RX; cfg->irq[2] = PPC85xx_INTR_TSEC3_ERROR; cfg->num_irqs = 3; } break; case 3: mpc85xx->iobase += MPC_TSEC4_BASE; if (cfg->num_irqs == 0) { cfg->irq[0] = PPC85xx_INTR_TSEC4_TX; cfg->irq[1] = PPC85xx_INTR_TSEC4_RX; cfg->irq[2] = PPC85xx_INTR_TSEC4_ERROR; cfg->num_irqs = 3; } break; } cfg->phy_addr = 0; mpc85xx->phy_incr = 1; } else { // // we were passed a valid tsec struct (from syspage) // mpc85xx->iobase = tparms_p->paddr; cfg->irq[0] = tparms_p->irq_tx; cfg->irq[1] = tparms_p->irq_rx; cfg->irq[2] = tparms_p->irq_err; cfg->num_irqs = 3; if (tparms_p->mac_present) { memcpy(cfg->current_address, tparms_p->mac, 6); } cfg->phy_addr = tparms_p->phy_addr; } mpc85xx->phy_base += MPC_TSEC1_BASE; /* PHYs are based off the primary TSEC */ /* set some defaults for the command line options */ cfg->flags = NIC_FLAG_MULTICAST; cfg->media_rate = cfg->duplex = -1; cfg->priority = IRUPT_PRIO_DEFAULT; cfg->iftype = IFT_ETHER; cfg->lan = -1; strcpy((char *)cfg->uptype, "en"); cfg->verbose = 1; // XXX debug - set verbose=0 for normal output // tell stack we can tx and rx oversize packets mpc85xx->ecom.ec_capabilities |= ETHERCAP_JUMBO_MTU; mpc85xx->num_tx_descriptors = DEFAULT_NUM_TX_DESCRIPTORS; mpc85xx->num_rx_descriptors = DEFAULT_NUM_RX_DESCRIPTORS; mpc85xx->flowctl_flag = MACCFG1_RX_FLOW | MACCFG1_TX_FLOW; mpc85xx->loopback = 0; mpc85xx->enable_tx_hw_csum = 0; // do not do hw csum unless cmd line says to mpc85xx->on_83xx = 0; if (PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C1) { // // 83xx suffers from tx fifo underruns under load // Try to deal with this by upping the transmit // threshold (actually, anything over (512-37) // wont make a difference - see fifo_tx_sp) and // note the weird values for the starve registers - // on the 83xx we want to ALWAYS run the tsec "starved" // at the highest possible priority for the dma arbiter // mpc85xx->fifo = 480; mpc85xx->fifo_starve = 509; mpc85xx->fifo_starve_shutoff = 510; mpc85xx->on_83xx = 1; // remember for transmit de-frag } else { // // 85xx is faster, 64 bytes is good for performance // to reduce transmit latency. No tx fifo underruns observed // mpc85xx->fifo = 0x40; mpc85xx->fifo_starve = mpc85xx->fifo / 4; mpc85xx->fifo_starve_shutoff = mpc85xx->fifo / 2; } mpc85xx->probe_phy = 0; mpc85xx->kermask = 1; // XXX debug mpc85xx->rx_delay = 500; mpc85xx->rx_frame = 12; mpc85xx->etsec = -1; // dunno if tsec or etsec cfg->device_index = 0xFFFFFFFF; if (rc = mpc85xx_parse_options(mpc85xx, options, cfg)) { log(LOG_ERR, "%s(): mpc85xx_parse_options() failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 1); return rc; } cfg->lan = mpc85xx->dev.dv_unit; single = (cfg->device_index != 0xFFFFFFFF); cfg->device_index = idx; if (tparms_p != NULL) { if (cfg->verbose) { log(LOG_ERR, "%s(): IF %d: Base register 0x%08X", __FUNCTION__, idx, tparms_p->paddr); log(LOG_ERR, "%s(): IF %d: IRQ Tx %2d Rx %2d Err %2d", __FUNCTION__, idx, tparms_p->irq_tx, tparms_p->irq_rx, tparms_p->irq_err); if (tparms_p->mac_present) { log(LOG_ERR, "%s(): IF %d: MAC %02X:%02X:%02X:%02X:%02X:%02X", __FUNCTION__, idx, tparms_p->mac[0],tparms_p->mac[1],tparms_p->mac[2],tparms_p->mac[3], tparms_p->mac[4],tparms_p->mac[5]); } log(LOG_ERR, "%s(): IF %d: PHY address %d", __FUNCTION__, idx, tparms_p->phy_addr); } } /* Only increment the phy / mac if a single device wasn't specified. */ if ((tparms_p == NULL) && !single) { cfg->phy_addr += idx * mpc85xx->phy_incr; } /* MPC8313E phy addresses */ if ((tparms_p == NULL) && PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C3) { if (idx == 0) /* eTSEC1 */ cfg->phy_addr = 31; else if (idx == 1) /* eTSEC2 */ cfg->phy_addr = 4; } if (((tparms_p == NULL) || (!tparms_p->mac_present)) && !single) { cfg->current_address[5] += idx; } // set defaults - only used by nicinfo to display mtu cfg->mtu = cfg->mru = ETH_MAX_PKT_LEN; cfg->media = NIC_MEDIA_802_3; cfg->mac_length = ETH_MAC_LEN; mpc85xx->force_advertise = -1; // did user specify either of speed or duplex on the cmd line? if ((cfg->media_rate != -1) || (cfg->duplex != -1)) { if (cfg->media_rate == -1) { log(LOG_ERR, "%s(): must also specify speed when duplex is specified", __FUNCTION__); mpc85xx_destroy(mpc85xx, 1); return EINVAL; } if (cfg->duplex == -1) { log(LOG_ERR, "%s(): must also specify duplex when speed is specified", __FUNCTION__); mpc85xx_destroy(mpc85xx, 1); return EINVAL; } // we get here, we know both media_rate and duplex are set switch(cfg->media_rate) { case 0: mpc85xx->force_advertise = 0; // disable link break; case 10*1000: mpc85xx->force_advertise = cfg->duplex ? MDI_10bTFD : MDI_10bT; break; case 100*1000: mpc85xx->force_advertise = cfg->duplex ? MDI_100bTFD : MDI_100bT; break; case 1000*1000: mpc85xx->force_advertise = cfg->duplex ? MDI_1000bTFD : MDI_1000bT; break; default: log(LOG_ERR, "%s(): invalid speed: %d", __FUNCTION__, cfg->media_rate/1000); mpc85xx_destroy(mpc85xx, 1); return EINVAL; break; } } // initialize - until mii callback says we have a link ... cfg->flags |= NIC_FLAG_LINK_DOWN; if (cfg->num_mem_windows == 0) { cfg->num_mem_windows = 1; } cfg->mem_window_base[0] = mpc85xx->iobase; cfg->mem_window_size[0] = 0x1000; strcpy((char *)cfg->device_description, "MPC85XX TSEC"); mpc85xx->num_rx_descriptors &= ~3; if (mpc85xx->num_rx_descriptors < MIN_NUM_RX_DESCRIPTORS) { mpc85xx->num_rx_descriptors = MIN_NUM_RX_DESCRIPTORS; } if (mpc85xx->num_rx_descriptors > MAX_NUM_RX_DESCRIPTORS) { mpc85xx->num_rx_descriptors = MAX_NUM_RX_DESCRIPTORS; } mpc85xx->num_tx_descriptors &= ~3; if (mpc85xx->num_tx_descriptors < MIN_NUM_TX_DESCRIPTORS) { mpc85xx->num_tx_descriptors = MIN_NUM_TX_DESCRIPTORS; } if (mpc85xx->num_tx_descriptors > MAX_NUM_TX_DESCRIPTORS) { mpc85xx->num_tx_descriptors = MAX_NUM_TX_DESCRIPTORS; } cfg->revision = NIC_CONFIG_REVISION; if (cfg->verbose) { nic_dump_config(cfg); } // map nic registers into virtual memory if ((mpc85xx->reg = mmap_device_memory (NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_NOCACHE, MAP_SHARED, mpc85xx->iobase)) == MAP_FAILED) { log(LOG_ERR, "%s(): mmap regs failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 2); return rc; } // if not set at cmd line, autodetect eTsec or not if (mpc85xx->etsec == -1) { volatile uint32_t *base = mpc85xx->reg; uint32_t rctrl_read; // older tsec does not support setting of etsec feature bits *(base + MPC_RCTRL) = RCTRL_PAL2; rctrl_read = *(base + MPC_RCTRL); if (*(base + MPC_RCTRL) == RCTRL_PAL2) { mpc85xx->etsec = 1; // tell stack we can do hardware checksumming ifp->if_capabilities_rx = IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; if (mpc85xx->enable_tx_hw_csum) { ifp->if_capabilities_tx = IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; } } else { mpc85xx->etsec = 0; } log(LOG_ERR, "%s(): eTsec: %s", __FUNCTION__, mpc85xx->etsec ? "yes" : "no"); } // map phy registers into virtual memory if ((mpc85xx->phy_reg = mmap_device_memory (NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_NOCACHE, MAP_SHARED, mpc85xx->phy_base)) == MAP_FAILED) { log(LOG_ERR, "%s(): mmap PHY regs failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 3); return rc; } // alloc rx descr ring if ((mpc85xx->rx_bd = mmap (NULL, sizeof (mpc_bd_t) * mpc85xx->num_rx_descriptors, PROT_READ | PROT_WRITE /*| PROT_NOCACHE */, MAP_PRIVATE | MAP_ANON | MAP_PHYS, NOFD, 0)) == MAP_FAILED) { log(LOG_ERR, "%s(): mmap rxd failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 4); return rc; } // alloc tx descr ring if ((mpc85xx->tx_bd = mmap (NULL, sizeof (mpc_bd_t) * mpc85xx->num_tx_descriptors, PROT_READ | PROT_WRITE /*| PROT_NOCACHE */, MAP_PRIVATE | MAP_ANON | MAP_PHYS, NOFD, 0)) == MAP_FAILED) { log(LOG_ERR, "%s(): mmap txd failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 5); return rc; } // alloc mbuf pointer array, corresponding to rx descr ring size = sizeof(struct mbuf *) * mpc85xx->num_rx_descriptors; mpc85xx->rx_pkts = malloc(size, M_DEVBUF, M_NOWAIT); if (mpc85xx->rx_pkts == NULL) { rc = ENOBUFS; log(LOG_ERR, "%s(): malloc rx_pkts failed", __FUNCTION__); mpc85xx_destroy(mpc85xx, 6); return rc; } memset(mpc85xx->rx_pkts, 0x00, size); // alloc mbuf pointer array, corresponding to tx descr ring size = sizeof(struct mbuf *) * mpc85xx->num_tx_descriptors; mpc85xx->tx_pkts = malloc(size, M_DEVBUF, M_NOWAIT); if (mpc85xx->tx_pkts == NULL) { rc = ENOBUFS; log(LOG_ERR, "%s(): malloc tx_pkts failed", __FUNCTION__); mpc85xx_destroy(mpc85xx, 7); return rc; } memset(mpc85xx->tx_pkts, 0x00, size); // alloc FCB for tx hw csum: 1024 tx descriptors times 8 bytes per fcb is 8K bytes if (mpc85xx->etsec) { // one frame control block for each tx descr ring if ((mpc85xx->tx_fcb = mmap (NULL, sizeof (mpc_tx_fcb_t) * mpc85xx->num_tx_descriptors, PROT_READ | PROT_WRITE /*| PROT_NOCACHE */, MAP_PRIVATE | MAP_ANON | MAP_PHYS, NOFD, 0)) == MAP_FAILED) { log(LOG_ERR, "%s(): mmap tx fcb failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 8); return rc; } // need physical address of fcb to stuff tx descr mpc85xx->tx_fcb_phys = vtophys(mpc85xx->tx_fcb); } // three hardware interrupts on TSEC: tx, rx and err. We do not use tx if ((rc = interrupt_entry_init(&mpc85xx->inter_rx, 0, NULL, cfg->priority)) != EOK) { log(LOG_ERR, "%s(): interrupt_entry_init(rx) failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 9); return rc; } if ((rc = interrupt_entry_init(&mpc85xx->inter_err, 0, NULL, IRUPT_PRIO_DEFAULT)) != EOK) { log(LOG_ERR, "%s(): interrupt_entry_init(err) failed: %d", __FUNCTION__, rc); mpc85xx_destroy(mpc85xx, 10); return rc; } // hook up so media devctls work bsd_mii_initmedia(mpc85xx); // interface setup - entry points, etc strcpy(ifp->if_xname, mpc85xx->dev.dv_xname); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = mpc85xx_ioctl; ifp->if_start = mpc85xx_start; // ifp->if_watchdog = mpc85xx_watchdog; ifp->if_init = mpc85xx_init; ifp->if_stop = mpc85xx_stop; IFQ_SET_READY(&ifp->if_snd); if_attach(ifp); ether_ifattach(ifp, mpc85xx->cfg.current_address); // rx interrupt can either hit nic or use kermask mpc85xx->inter_rx.func = mpc85xx_process_interrupt; if (mpc85xx->kermask) { mpc85xx->inter_rx.enable = mpc85xx_enable_rx_kermask; mpc85xx->isrp_rx = mpc85xx_isr_rx_kermask; } else { mpc85xx->inter_rx.enable = mpc85xx_enable_rx; mpc85xx->isrp_rx = mpc85xx_isr_rx; } mpc85xx->inter_rx.arg = mpc85xx; // error interrupt always uses kermask mpc85xx->inter_err.func = mpc85xx_process_interrupt; mpc85xx->inter_err.enable = mpc85xx_enable_err_kermask; mpc85xx->isrp_err = mpc85xx_isr_err_kermask; mpc85xx->inter_err.arg = mpc85xx; if (cfg->verbose > 3) { log(LOG_ERR, "%s(): ending: idx %d\n", __FUNCTION__, idx); } return EOK; } // // called from mpc85xx_create_instance() and mpc85xx_destroy() // static void mpc85xx_destroy(mpc85xx_dev_t *mpc85xx, int how) { struct ifnet *ifp; struct _iopkt_self *iopkt; ifp = &mpc85xx->ecom.ec_if; iopkt = mpc85xx->iopkt; /* FALLTHROUGH all of these */ switch (how) { // // called from mpc85xx_destroy() // case -1: /* * Don't init() while we're dying. Yes it can happen: * ether_ifdetach() calls bridge_ifdetach() which * tries to take us out of promiscuous mode with an * init(). */ mpc85xx->dying = 1; mpc85xx_stop(ifp, 1); mpc85xx_rx_mbuf_free(mpc85xx); ether_ifdetach(ifp); if_detach(ifp); // // called from mpc85xx_create_instance() // case 11: interrupt_entry_remove(&mpc85xx->inter_err, NULL); case 10: interrupt_entry_remove(&mpc85xx->inter_rx, NULL); case 9: if (mpc85xx->etsec) { munmap(mpc85xx->tx_fcb, sizeof(mpc_tx_fcb_t) * mpc85xx->num_tx_descriptors); } case 8: free(mpc85xx->tx_pkts, M_DEVBUF); case 7: free(mpc85xx->rx_pkts, M_DEVBUF); case 6: munmap(mpc85xx->tx_bd, sizeof(mpc_bd_t) * mpc85xx->num_tx_descriptors); case 5: munmap(mpc85xx->rx_bd, sizeof(mpc_bd_t) * mpc85xx->num_rx_descriptors); case 4: munmap_device_memory(mpc85xx->phy_reg, 0x1000); case 3: munmap_device_memory(mpc85xx->reg, 0x1000); case 2: case 1: shutdownhook_disestablish(mpc85xx->sdhook); break; } } // // io-pkt entry point // static void mpc85xx_shutdown(void *arg) { mpc85xx_dev_t *mpc85xx; mpc85xx = arg; mpc85xx_stop(&mpc85xx->ecom.ec_if, 1); } static int mpc85xx_detach(struct device *dev, int flags) { mpc85xx_dev_t *mpc85xx; mpc85xx = (mpc85xx_dev_t *)dev; mpc85xx_destroy(mpc85xx, -1); return EOK; } // // called from mpc85xx_init() and mpc85xx_stop() // static void mpc85xx_reset (mpc85xx_dev_t *mpc85xx) { volatile uint32_t *base = mpc85xx->reg; uint32_t status; int timeout = MPC_TIMEOUT; // mask interrupt in nic register *(base + MPC_IMASK) = 0; if ((*(base + MPC_DMACTRL) & DMACTRL_GTS) == 0) { /* Graceful transmit stop and wait for completion. */ *(base + MPC_DMACTRL) |= DMACTRL_GTS; timeout = MPC_TIMEOUT; do { nanospin_ns (10); if (! --timeout) break; status = *(base + MPC_IEVENT); } while ((status & IEVENT_GTSC) != IEVENT_GTSC); if (! timeout) { log(LOG_ERR, "%s(): DMA GTS stop failed", __FUNCTION__ ); } } /* Disable Rx and Tx */ *(base + MPC_MACCFG1) &= ~(MACCFG1_TXEN | MACCFG1_RXEN); /* Wait for 9.6KBytes worth of data (worst case ~ 8ms) */ delay (9); if ((*(base + MPC_DMACTRL) & DMACTRL_GRS) == 0) { /* Graceful receive stop and wait for completion. */ *(base + MPC_DMACTRL) |= DMACTRL_GRS; timeout = MPC_TIMEOUT; do { nanospin_ns (10); if (! --timeout) break; status = *(base + MPC_IEVENT); } while ((status & IEVENT_GRSC) != IEVENT_GRSC); if (!timeout) { log(LOG_ERR, "%s(): DMA GRS stop failed", __FUNCTION__); } } *(base + MPC_IEVENT) = (IEVENT_GTSC | IEVENT_GRSC); *(base + MPC_MACCFG1) = MACCFG1_SFT_RESET; nanospin_ns (1000); *(base + MPC_MACCFG1) &= ~MACCFG1_SFT_RESET; } // // called from mpc85xx_init() // static void set_phys_addr (mpc85xx_dev_t *mpc85xx) { uint32_t *base = mpc85xx->reg; union { uint32_t addr [2]; uint8_t caddr [8]; } tab; int i, j; memset ((char *) tab.caddr, 0, 8); for (i = 0, j = 5; i < 6; i++, j--) { tab.caddr[i] = mpc85xx->cfg.current_address[j]; } *(base + MPC_MACSTNADDR1) = tab.addr [0]; *(base + MPC_MACSTNADDR2) = tab.addr [1]; } // // io-pkt entry point - initialize hardware re-entrantly // // N.B. Must ensure that this function, and everything it calls // is REALLY ok to call over and over again, because the stack // will call us MORE THAN ONCE // static int mpc85xx_init (struct ifnet *ifp) { mpc85xx_dev_t *mpc85xx = ifp->if_softc; nic_config_t *cfg = &mpc85xx->cfg; volatile uint32_t *base = mpc85xx->reg; int err, i; struct mbuf *m; mpc_bd_t *bd; if (cfg->verbose > 3) { log(LOG_ERR, "%s(): starting: idx %d\n", __FUNCTION__, cfg->device_index); } if (mpc85xx->dying) { log(LOG_ERR, "%s(): dying", __FUNCTION__); return 1; } // clean up and reset nic mpc85xx_stop(ifp, 0); // Transmitter is now locked out // init tx descr ring for (i=0; inum_tx_descriptors; i++) { bd = &mpc85xx->tx_bd[i]; if (i == (mpc85xx->num_tx_descriptors - 1)) { bd->status = TXBD_W; } else { bd->status = 0; } // free any previously txd mbufs if (m = mpc85xx->tx_pkts[i]) { m_freem(m); mpc85xx->tx_pkts[i] = NULL; } // set up this descriptors corresponding fcb for hw csum if (mpc85xx->etsec) { mpc_tx_fcb_t *fcb = &mpc85xx->tx_fcb[i]; fcb->ctrl = 0; // must set during transmit fcb->reserved = 0; // unused fcb->l4_off = 0; // this can be either UDP or TCP fcb->l3_off = TX_FCB_IP_OFF; // layer 3 is always IP fcb->ph_csum = 0; // unused fcb->vlan_ctrl = 0; // unused } } mpc85xx->tx_pidx = mpc85xx->tx_cidx = mpc85xx->tx_descr_inuse = 0; // init rx descr ring for (i=0; inum_rx_descriptors; i++) { bd = &mpc85xx->rx_bd[i]; if (i == (mpc85xx->num_rx_descriptors - 1)) { bd->status = (RXBD_W | RXBD_E | RXBD_I); /* Wrap indicator */ } else { bd->status = (RXBD_E | RXBD_I); } // assign an mbuf to each rx descriptor if (m = mpc85xx->rx_pkts[i]) { // reuse previously allocated mbuf } else { if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) { return ENOMEM; } MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); return ENOMEM; } mpc85xx->rx_pkts[i] = m; } bd->buffer = pool_phys(m->m_data, m->m_ext.ext_page); bd->length = 0; } mpc85xx->rx_cidx = 0; mpc85xx->rx_inprog_head = NULL; mpc85xx->rx_inprog_tail = NULL; mpc85xx->rx_inprog_subtotal_len = 0; // write to MPC_MACSTNADDR1 and MPC_MACSTNADDR2 from cfg->current_address set_phys_addr(mpc85xx); *(base + MPC_TBASE) = vtophys ((void *) mpc85xx->tx_bd); *(base + MPC_RBASE) = vtophys ((void *) mpc85xx->rx_bd); // enable jumbo packets with MACCFG2_HUGE_FRM bit set if (PPC_GET_FAM_MEMBER(get_pvr()) == PPC_E300C3) { /* MPC8313E-rdb eTSEC0 has a switch and needs this */ *(base + MPC_MACCFG2) = (MACCFG2_HUGE_FRM | MACCFG2_IF_MODE_BYT | MACCFG2_PAD_CRC | (cfg->duplex ? MACCFG2_FDX : 0) | MACCFG2_PRE_LEN(7)); } else { *(base + MPC_MACCFG2) = (MACCFG2_HUGE_FRM | MACCFG2_IF_MODE_NIB | MACCFG2_PAD_CRC | (cfg->duplex ? MACCFG2_FDX : 0) | MACCFG2_PRE_LEN(7)); } *(base + MPC_ECNTRL) = (ECNTRL_CLRCNT | ECNTRL_STEN | ECNTRL_AUTOZ); // tell the nic how big each rx buffer is *(base + MPC_MRBLR) = MCLBYTES & ~0x3f; // low 6 bits must be zero - mult of 64 if (cfg->verbose) { log(LOG_ERR, "%s(): rx dma buffer size %d bytes", __FUNCTION__, MCLBYTES & ~0x3f); } // tell nic how big a txd or rxd packet can be *(base + MPC_MAXFRM) = ifp->if_mtu; // save for nicinfo cfg->mtu = cfg->mru = ifp->if_mtu; *(base + MPC_DMACTRL) |= (DMACTRL_WWR | DMACTRL_TBDSEN | DMACTRL_TDSEN /*| DMACTRL_WOP*/); /* Suprisingly enough, when performing loopback tests, the polled mode is FASTER than the * wait mode for smaller packets */ *(base + MPC_DMACTRL) &= ~(DMACTRL_WOP); *(base + MPC_FIFO_TX_THR) = mpc85xx->fifo; *(base + MPC_FIFO_TX_STARVE) = mpc85xx->fifo_starve; *(base + MPC_FIFO_TX_STARVE_SHUTOFF) = mpc85xx->fifo_starve_shutoff; #if 1 /* ask chip put 64bytes in L2 cache */ *(base + MPC_ATTR) = 0x40c0; *(base + MPC_ATTRELI) = 0x00400000; #else *(base + MPC_ATTR) = 0x000000c0; #endif *(base + MPC_IADDR0) = 0x0; *(base + MPC_IADDR1) = 0x0; *(base + MPC_IADDR2) = 0x0; *(base + MPC_IADDR3) = 0x0; *(base + MPC_IADDR4) = 0x0; *(base + MPC_IADDR5) = 0x0; *(base + MPC_IADDR6) = 0x0; *(base + MPC_IADDR7) = 0x0; // set GADDR0 through GADDR7 mpc85xx_set_multicast(mpc85xx); *(base + MPC_CAR1) = 0xffffffff; *(base + MPC_CAR2) = 0xffffffff; *(base + MPC_CAM1) = 0x0; *(base + MPC_CAM2) = 0x0; { // configure receive uint32_t rctrl_val = 0; if ((cfg->flags & NIC_FLAG_PROMISCUOUS) || (ifp->if_flags & IFF_PROMISC)) { rctrl_val |= RCTRL_PROM; } if (mpc85xx->etsec) { // // Setting the 2 PRSDEP bits non-zero tells the // eTsec to prepend an 8 byte receive control // block before the packet data, which tells // us stuff about the packet - see mpc_rx_fcb_t // // We enable all rx checksum calculating by // cranking up the parse bits to 3, and then // enabling both IP and TCP/UDP hw checksum verification. // rctrl_val |= (RCTRL_PRSDEP | RCTRL_IPCSEN | RCTRL_TUCSEN | RCTRL_PAL2); } *(base + MPC_RCTRL) = rctrl_val; if (cfg->verbose > 2) { log(LOG_ERR, "%s(): RCTRL written with 0x%X, reads as 0x%X", __FUNCTION__, rctrl_val, *(base + MPC_RCTRL)); } } { // configure rx interrupt coalescing uint32_t rxic_val = 0; // enable interrupt coalescing? if (mpc85xx->rx_delay || mpc85xx->rx_frame) { if (!mpc85xx->rx_delay || !mpc85xx->rx_frame) { log(LOG_ERR, "%s(): must set BOTH rx_delay %d and rx_frame %d", __FUNCTION__, mpc85xx->rx_delay, mpc85xx->rx_frame); } else { rxic_val = RXIC_ICEN | RXIC_ICFT(mpc85xx->rx_frame) | RXIC_ICTT(mpc85xx->rx_delay); } } *(base + MPC_RXIC) = rxic_val; if (cfg->verbose > 2) { log(LOG_ERR, "%s(): RXIC written with 0x%X, reads as 0x%X", __FUNCTION__, rxic_val, *(base + MPC_RXIC)); } } { // configure transmit uint32_t tctrl_val = *(base + MPC_TCTRL); // enable IP and UDP/TCP tx hw cksum on etsec if (mpc85xx->etsec) { tctrl_val |= (TCTRL_IPCSEN | TCTRL_TUCSEN); } *(base + MPC_TCTRL) = tctrl_val; if (cfg->verbose > 2) { log(LOG_ERR, "%s(): TCTRL written with 0x%X, reads as 0x%X", __FUNCTION__, tctrl_val, *(base + MPC_TCTRL)); } } *(base + MPC_TSTAT) = TSTAT_THLT; *(base + MPC_RSTAT) = RSTAT_QHLT; *(base + MPC_DMACTRL) &= ~(DMACTRL_GRS | DMACTRL_GTS); *(base + MPC_MACCFG1) = (MACCFG1_RXEN | MACCFG1_TXEN | mpc85xx->flowctl_flag | mpc85xx->loopback); mpc85xx_clear_stats(mpc85xx); mpc85xx_init_phy(mpc85xx); // ask for call for detecting PHY link state changes, cleaning up tx descr callout_msec(&mpc85xx->mii_callout, 2 * 1000, mpc85xx_MDI_MonitorPhy, mpc85xx); if (ifp->if_capenable_rx & ~(IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4) ) { return ENOTSUP; // should never happen } mpc85xx->rx_cap_mask = 0; if (ifp->if_capenable_rx & IFCAP_CSUM_IPv4) { mpc85xx->rx_cap_mask |= M_CSUM_IPv4 | M_CSUM_IPv4_BAD; } if (ifp->if_capenable_rx & IFCAP_CSUM_TCPv4) { mpc85xx->rx_cap_mask |= M_CSUM_TCPv4 | M_CSUM_TCP_UDP_BAD; } if (ifp->if_capenable_rx & IFCAP_CSUM_UDPv4) { mpc85xx->rx_cap_mask |= M_CSUM_UDPv4 | M_CSUM_TCP_UDP_BAD; } if (mpc85xx->cfg.verbose) { log(LOG_ERR,"mpc85xx->rx_cap_mask: 0x%X\n", mpc85xx->rx_cap_mask); } // // set tx capabilites // if (ifp->if_capenable_tx & ~(IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4) ) { return ENOTSUP; // should never happen } mpc85xx->tx_cap_mask = 0; if (ifp->if_capenable_rx & IFCAP_CSUM_IPv4) { mpc85xx->tx_cap_mask |= M_CSUM_IPv4 | M_CSUM_IPv4_BAD; } if (ifp->if_capenable_rx & IFCAP_CSUM_TCPv4) { mpc85xx->tx_cap_mask |= M_CSUM_TCPv4 | M_CSUM_TCP_UDP_BAD; } if (ifp->if_capenable_rx & IFCAP_CSUM_UDPv4) { mpc85xx->tx_cap_mask |= M_CSUM_UDPv4 | M_CSUM_TCP_UDP_BAD; } if (mpc85xx->cfg.verbose) { log(LOG_ERR,"mpc85xx->tx_cap_mask: 0x%X\n", mpc85xx->tx_cap_mask); } // Attach to rx and error TSEC hardware interrupts if (mpc85xx->iid_rx == -1) { if ((err = InterruptAttach_r(mpc85xx->cfg.irq[MPC85xx_RX_INT], mpc85xx->isrp_rx, mpc85xx, sizeof(*mpc85xx), _NTO_INTR_FLAGS_TRK_MSK)) < 0) { err = -err; log(LOG_ERR, "%s(): InterruptAttach_r(rx) failed: %d", __FUNCTION__, err); return err; } mpc85xx->iid_rx = err; } if (mpc85xx->iid_err == -1) { if ((err = InterruptAttach_r(mpc85xx->cfg.irq[MPC85xx_ERR_INT], mpc85xx->isrp_err, mpc85xx, sizeof(*mpc85xx), _NTO_INTR_FLAGS_TRK_MSK)) < 0) { err = -err; log(LOG_ERR, "%s(): InterruptAttach_r(err) failed: %d", __FUNCTION__, err); return err; } mpc85xx->iid_err = err; } // finally unmask interrupt in nic hardware register AFTER InterruptAttach_r() *(base + MPC_IMASK) = MPC85xx_ENABLED_IMASK; NW_SIGLOCK(&ifp->if_snd_ex, mpc85xx->iopkt); ifp->if_flags_tx |= IFF_RUNNING; NW_SIGUNLOCK(&ifp->if_snd_ex, mpc85xx->iopkt); ifp->if_flags |= IFF_RUNNING; if (cfg->verbose > 3) { log(LOG_ERR, "%s(): ending: idx %d\n", __FUNCTION__, cfg->device_index); } return EOK; } // // called from mpc85xx_mii_callback() and mpc85xx_ioctl() // void mpc85xx_speeduplex (mpc85xx_dev_t *mpc85xx) { nic_config_t *cfg = &mpc85xx->cfg; volatile uint32_t *base = mpc85xx->reg; int media_rate = cfg->media_rate; int duplex = cfg->duplex; if (media_rate == 1000000) { // If its 1Gb *(base + MPC_MACCFG2) = ((*(base + MPC_MACCFG2) & ~MACCFG2_IF) | MACCFG2_IF_MODE_BYT); } else { // 10/100Mb *(base + MPC_MACCFG2) = ((*(base + MPC_MACCFG2) & ~MACCFG2_IF) | MACCFG2_IF_MODE_NIB); /* Check for reduced mode operation. */ if ( *(base + MPC_ECNTRL) & (ECNTRL_RMM | ECNTRL_RPM)) { if (media_rate == 100000) { *(base + MPC_ECNTRL) |= ECNTRL_R100M; } else { *(base + MPC_ECNTRL) &= ~ECNTRL_R100M; } } } if (duplex) { *(base + MPC_MACCFG2) |= MACCFG2_FDX; } else { *(base + MPC_MACCFG2) &= ~MACCFG2_FDX; } } // // io-pkt entry point. Also called from mpc85xx_destroy() // and mpc85xx_init() // static void mpc85xx_stop(struct ifnet *ifp, int disable) { mpc85xx_dev_t *mpc85xx = ifp->if_softc; int i; // shut down mii probing callout_stop(&mpc85xx->mii_callout); if (mpc85xx->mdi) { MDI_DeRegister(&mpc85xx->mdi); mpc85xx->mdi = NULL; } // stop tx and rx and reset nic mpc85xx_reset(mpc85xx); /* Lock out the transmit side */ NW_SIGLOCK(&ifp->if_snd_ex, mpc85xx->iopkt); for (i = 0; i < 10; i++) { if ((ifp->if_flags_tx & IFF_OACTIVE) == 0) break; NW_SIGUNLOCK(&ifp->if_snd_ex, mpc85xx->iopkt); delay(50); NW_SIGLOCK(&ifp->if_snd_ex, mpc85xx->iopkt); } if (i < 10) { ifp->if_flags_tx &= ~IFF_RUNNING; NW_SIGUNLOCK(&ifp->if_snd_ex, mpc85xx->iopkt); } else { /* Heavy load or bad luck. Try the big gun. */ quiesce_all(); ifp->if_flags_tx &= ~IFF_RUNNING; unquiesce_all(); } /* * The only other transmit entry point is now locked out. * All others are only used by the stack thread which is * currently right here. */ if (mpc85xx->iid_rx != -1) { InterruptDetach(mpc85xx->iid_rx); mpc85xx->iid_rx = -1; } if (mpc85xx->iid_err != -1) { InterruptDetach(mpc85xx->iid_err); mpc85xx->iid_err = -1; } /* Mark the interface as down and cancel the watchdog timer. */ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); ifp->if_timer = 0; } // // called from mpc85xx_destroy() // static void mpc85xx_rx_mbuf_free(mpc85xx_dev_t *mpc85xx) { int i; struct mbuf *m; for (i = 0; i < mpc85xx->num_rx_descriptors; i++) { if (m = mpc85xx->rx_pkts[i]) { m_freem(m); mpc85xx->rx_pkts[i] = NULL; } } } // // called from mpc85xx_detect() below // static uint32_t find_immr_info ( void ) { struct asinfo_entry *asinfo; int32_t i, num, cnt; num = _syspage_ptr->asinfo.entry_size / sizeof(*asinfo); asinfo = SYSPAGE_ENTRY (asinfo); for (i = cnt = 0; i < num; ++i) { char *name; name = __hwi_find_string (asinfo->name); if (strcmp (name, "immr")) { asinfo++; continue; } return ((uint32_t) asinfo->start); } return (0); } // // called from mpc85xx_detect() below // static unsigned ppc8xxx_get_syspage_params(struct tsec_params *tsec, unsigned last) { hwi_tag *tag; int irqnum = 0; unsigned off; unsigned item; char *name; /* Find network hardware information. */ item = hwi_find_item(last, HWI_ITEM_DEVCLASS_NETWORK, "tsec", NULL); if (item == HWI_NULL_OFF) { return(item); } off = item; tsec->paddr = 0; tsec->irq_present = 0; tsec->mac_present = 0; tsec->phy_present = 0; while((off = hwi_next_tag(off, 1)) != HWI_NULL_OFF) { tag = hwi_off2tag(off); name = __hwi_find_string(((hwi_tag *)tag)->prefix.name); if(strcmp(name, HWI_TAG_NAME_location) == 0) { tsec->paddr = (paddr_t)tag->location.base; } else if(strcmp(name, HWI_TAG_NAME_irq) == 0) { switch (irqnum) { case 0: tsec->irq_tx = tag->irq.vector; break; case 1: tsec->irq_rx = tag->irq.vector; break; case 2: tsec->irq_err = tag->irq.vector; break; default: break; } irqnum++; } else if (strcmp(name, HWI_TAG_NAME_nicaddr) == 0) { if (tag->nicaddr.len == 6) { memcpy(tsec->mac, tag->nicaddr.addr, 6); tsec->mac_present = 1; } } else if (strcmp(name, HWI_TAG_NAME_nicphyaddr) == 0) { tsec->phy_addr = tag->nicphyaddr.addr; tsec->phy_present = 1; } } if (irqnum == 3) { tsec->irq_present = 1; } return(item); } // // called from mpc85xx_entry() in mpc85xx.c // int mpc85xx_detect(void *dll_hdl, struct _iopkt_self *iopkt, char *options) { mpc85xx_dev_t *mpc_85xx; // dummy entry used for parsing and detecting only nic_config_t *cfg; int idx=0; int rc=EOK, single; uint32_t iobase; struct mpc_arg mpc_arg; struct device *dev; if ((mpc_85xx = malloc(sizeof(*mpc_85xx), M_TEMP, M_NOWAIT | M_ZERO)) == NULL) { return ENOMEM; } cfg = &mpc_85xx->cfg; mpc_85xx->use_syspage = 0; cfg->device_index = 0xffffffff; if (options) { if (mpc85xx_parse_options(mpc_85xx, options, cfg)) { free(mpc_85xx, M_TEMP); return EINVAL; } } if ((iobase = find_immr_info ()) == 0) { log(LOG_ERR, "%s(): Unable to find base address", __FUNCTION__); free(mpc_85xx, M_TEMP); return ENODEV; } if (mpc_85xx->use_syspage) { unsigned config_offset = HWI_NULL_OFF; struct tsec_params tsec; /* Use info stored in the system page to retrieve configuration. */ config_offset = ppc8xxx_get_syspage_params(&tsec,config_offset); if (config_offset == HWI_NULL_OFF) { log(LOG_ERR, "%s(): No syspage hardware configuration information found", __FUNCTION__); free(mpc_85xx, M_TEMP); return ENODEV; } while (config_offset != HWI_NULL_OFF) { if (tsec.paddr && tsec.irq_present) { /* If device index specified, ignore all other device cfgs. */ if ((cfg->device_index == idx) || (cfg->device_index == 0xffffffff)) { single = 0; if (cfg->device_index == idx) { /* Configured required device... No need to look for * more. */ single = 1; } mpc_arg.dll_hdl = dll_hdl; mpc_arg.options = options; mpc_arg.idx = idx; mpc_arg.iobase = iobase; mpc_arg.tparms_p = &tsec; dev = NULL; /* No parent */ if ((rc = dev_attach("tsec", options, &mpc85xx_ca, &mpc_arg, &single, &dev, NULL)) != EOK) { log(LOG_ERR, "%s(): syspage dev_attach(%d) failed: %d\n", __FUNCTION__, idx, rc); free(mpc_85xx, M_TEMP); return (ENODEV); } if (single != 0) break; } } else { char missing[64]; /* Must specify the base register address and interrupts from * the system page. All other info can be obtained from * default code / command line options. */ strcpy(missing, ""); if (!tsec.paddr) { strcat(missing, "base address "); } if (!tsec.irq_present) { strcat(missing,"interrupt(s) "); } if (!tsec.phy_present) { strcat(missing,"phy address "); } log(LOG_ERR, "%s(): Missing %s in syspage configuration for device index %d", __FUNCTION__, missing, idx); free(mpc_85xx, M_TEMP); return(ENODEV); } idx++; config_offset = ppc8xxx_get_syspage_params(&tsec,config_offset); } } else { // // all cfg information will be auto-determined or specified on command line // int ntsec; if ((get_spr(PPCE500_SPR_SVR) >> 16) == 0x8039) { ntsec = 4; // 8548 etsec } else { ntsec = 2; // 8540 tsec } if (cfg->device_index != 0xffffffff) { idx = cfg->device_index; } while (idx < ntsec) { mpc_arg.dll_hdl = dll_hdl; mpc_arg.options = options; mpc_arg.idx = idx; mpc_arg.iobase = iobase; mpc_arg.tparms_p = NULL; single = 1; dev = NULL; /* No parent */ if ((rc = dev_attach("tsec", options, &mpc85xx_ca, &mpc_arg, &single, &dev, NULL)) != EOK) { log(LOG_ERR, "%s(): cmd line dev_attach(%d) failed: %d\n", __FUNCTION__, idx, rc); free(mpc_85xx, M_TEMP); return (ENODEV); } if (cfg->device_index != 0xffffffff) { /* Only looking at a specific index */ break; } idx++; } } free(mpc_85xx, M_TEMP); return (rc); }