Index: user/jmallett/octeon/sys/mips/cavium/dev/rgmii/octeon_rgmx.c =================================================================== --- user/jmallett/octeon/sys/mips/cavium/dev/rgmii/octeon_rgmx.c (revision 206675) +++ user/jmallett/octeon/sys/mips/cavium/dev/rgmii/octeon_rgmx.c (revision 206676) @@ -1,2328 +1,2328 @@ /***********************license start*************** * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Cavium Networks nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. * * * For any questions regarding licensing please contact marketing@caviumnetworks.com * ***********************license end**************************************/ /* * octeon_rgmx.c RGMII Ethernet Interfaces on Octeon * */ /* * Driver for the Reduced Gigabit Media Independent Interface (RGMII) * present on the Cavium Networks' Octeon chip. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "octeon_fau.h" #include "octeon_fpa.h" #include "octeon_ipd.h" #include "octeon_pko.h" #include "octeon_pip.h" #include "octeon_rgmx.h" /* The "battleship" boards have 8 ports */ #define OCTEON_RGMX_NUM_PORTS_MAX 8 #define NUM_TX_PACKETS 80 #define NUM_RX_PACKETS 300 #define MAX_RX_BUFS (NUM_RX_PACKETS) * (OCTEON_RGMX_NUM_PORTS_MAX) #define MAX_TX_BUFS (NUM_TX_PACKETS) #define OCTEON_RGMX_DEV_NAME "rgmx" #define OCTEON_RGMX_MIN_PORT 0 #define OCTEON_RGMX_MAX_PORT 19 #define OCTEON_RGMX_OQUEUE_PER_PORT 8 #define OCTEON_RGMX_SCHEDULED_ISRS 1 /* Use Scheduled ISRs from kernel tasks */ #ifndef POW_MAX_LOOP #define POW_MAX_LOOP 0x800 #endif /* * CIU related stuff for enabling POW interrupts */ #define OCTEON_RGMX_CIU_INTX CIU_INT_0 #define OCTEON_RGMX_CIU_ENX CIU_EN_0 MALLOC_DEFINE(M_RGMII_WQE, "rgmii_wqe", "FPA pool for WQEs"); /* Driver data */ struct rgmx_softc_dev { device_t sc_dev; /* Device ID */ uint64_t link_status; struct ifnet *ifp; int sc_unit; u_int port; u_int idx; u_char ieee[6]; u_short txb_size; /* size of TX buffer, in bytes */ /* Transmission buffer management. */ u_short txb_free; /* free bytes in TX buffer */ u_char txb_count; /* number of packets in TX buffer */ u_char txb_sched; /* number of scheduled packets */ /* Media information. */ struct ifmedia media; /* used by if_media. */ u_short mbitmap; /* bitmap for supported media; see bit2media */ int defmedia; /* default media */ struct ifqueue tx_pending_queue; /* Queue of mbuf given to PKO currently */ octeon_pko_sw_queue_info_t *outq_ptr; struct mtx mtx; }; /* * Device methods */ static int rgmii_probe(device_t); static void rgmii_identify(driver_t *, device_t); static int rgmii_attach(device_t); /* * Octeon specific routines */ static int octeon_has_4ports(void); static void octeon_config_rgmii_port(u_int port); static void octeon_rgmx_config_pip(u_int port); static void octeon_line_status_loop(void *); static void octeon_rx_loop(void *); static void octeon_config_hw_units_post_ports(void); static void octeon_config_hw_units_pre_ports(void); static void octeon_config_hw_units_port(struct rgmx_softc_dev *sc, u_int port); static struct rgmx_softc_dev *get_rgmx_softc(u_int port); static void octeon_rgmx_start_port(u_int port); static u_int octeon_rgmx_stop_port(u_int port); static u_int get_rgmx_port_ordinal(u_int port); static void octeon_rgmx_set_mac(u_int port); static void octeon_rgmx_init_sc(struct rgmx_softc_dev *sc, device_t dev, u_int port, u_int num_devices); static int octeon_rgmx_init_ifnet(struct rgmx_softc_dev *sc); static void octeon_rgmx_stop(struct rgmx_softc_dev *sc); static void octeon_rgmx_config_speed(u_int port, u_int); #ifdef DEBUG_RGMX_DUMP static void octeon_dump_rgmx_stats(u_int port); static void octeon_dump_pow_stats(void); #endif #ifdef __not_used__ static void rgmx_timer_periodic(void); #endif static void octeon_rgmx_enable_RED_all(int, int); #ifdef OCTEON_RGMX_SCHEDULED_ISRS static void octeon_rgmx_isr_link(void *context, int pending); static void octeon_rgmx_isr_rxtx(void *context, int pending); static int octeon_rgmx_intr_fast(void *arg); #else static int octeon_rgmx_intr(void *arg); #endif /* Standard driver entry points. These can be static. */ static void octeon_rgmx_init (void *); //static driver_intr_t rgmx_intr; static void octeon_rgmx_config_cam (struct ifnet *); static int octeon_rgmx_ioctl (struct ifnet *, u_long, caddr_t); static void octeon_rgmx_output_start (struct ifnet *); static void octeon_rgmx_output_start_locked (struct ifnet *); static int octeon_rgmx_medchange (struct ifnet *); static void octeon_rgmx_medstat (struct ifnet *, struct ifmediareq *); /* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */ static int const bit2media [] = { IFM_ETHER | IFM_AUTO, IFM_ETHER | IFM_MANUAL, IFM_ETHER | IFM_10_T, IFM_ETHER | IFM_10_2, IFM_ETHER | IFM_10_5, IFM_ETHER | IFM_10_FL, IFM_ETHER | IFM_10_T, /* More can be added here... */ }; /* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */ #define MB_HA 0x0001 #define MB_HM 0x0002 #define MB_HT 0x0004 #define MB_H2 0x0008 #define MB_H5 0x0010 #define MB_HF 0x0020 #define MB_FT 0x0040 #define LEBLEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) static struct rgmx_softc_dev *rgmx_scdev_array[OCTEON_RGMX_NUM_PORTS_MAX] = {NULL}; static u_int port_array[OCTEON_RGMX_NUM_PORTS_MAX] = {0}; static u_int num_devices = 0; static octeon_pko_sw_queue_info_t output_queues_array[OCTEON_RGMX_NUM_PORTS_MAX * OCTEON_RGMX_OQUEUE_PER_PORT]; static struct resource *irq_res; /* Interrupt resource. */ static void *int_handler_tag; #ifdef OCTEON_RGMX_SCHEDULED_ISRS struct task link_isr_task; struct task rxtx_isr_task; struct taskqueue *tq; /* private task queue */ #endif static u_int get_rgmx_port_ordinal (u_int port) { u_int idx; for (idx = 0; idx < OCTEON_RGMX_NUM_PORTS_MAX; idx++) { if (port_array[idx] == port) { return (idx); } } return (-1); } static struct rgmx_softc_dev *get_rgmx_softc (u_int port) { u_int idx; idx = get_rgmx_port_ordinal(port); if (idx != -1) { return (rgmx_scdev_array[idx]); } return (NULL); } static void octeon_rgmx_init_sc (struct rgmx_softc_dev *sc, device_t dev, u_int port, u_int num_devices) { int ii; /* No software-controllable media selection. */ sc->mbitmap = MB_HM; sc->defmedia = MB_HM; sc->sc_dev = dev; sc->port = port; sc->idx = num_devices; sc->link_status = 0; sc->sc_unit = num_devices; sc->mbitmap = MB_HT; sc->defmedia = MB_HT; sc->tx_pending_queue.ifq_maxlen = NUM_TX_PACKETS; sc->tx_pending_queue.ifq_head = sc->tx_pending_queue.ifq_tail = NULL; sc->tx_pending_queue.ifq_len = sc->tx_pending_queue.ifq_drops = 0; mtx_init(&sc->tx_pending_queue.ifq_mtx, "if->sc->txpq.ifqmtx", NULL, MTX_DEF); sc->outq_ptr = &(output_queues_array[num_devices * OCTEON_RGMX_OQUEUE_PER_PORT]); for (ii = 0; ii < 6; ii++) { sc->ieee[ii] = octeon_mac_addr[ii]; } sc->ieee[5] += get_rgmx_port_ordinal(port); } static int octeon_rgmx_init_ifnet (struct rgmx_softc_dev *sc) { struct ifnet *ifp; ifp = sc->ifp = if_alloc(IFT_ETHER); if (NULL == ifp) { device_printf(sc->sc_dev, "can not ifalloc for rgmx port\n"); return (ENOSPC); } /* * Initialize ifnet structure */ ifp->if_softc = sc; if_initname(sc->ifp, device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); ifp->if_start = octeon_rgmx_output_start; ifp->if_ioctl = octeon_rgmx_ioctl; ifp->if_hwassist = CSUM_TCP | CSUM_UDP; ifp->if_capabilities = IFCAP_HWCSUM; ifp->if_capenable = ifp->if_capabilities; ifp->if_init = octeon_rgmx_init; ifp->if_linkmib = NULL; // &sc->mibdata; ifp->if_linkmiblen = 0; // sizeof (sc->mibdata); /* * Set fixed interface flags. */ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; // | IFF_NEEDSGIANT; if (ifp->if_snd.ifq_maxlen == 0) ifp->if_snd.ifq_maxlen = ifqmaxlen; ifmedia_init(&sc->media, 0, octeon_rgmx_medchange, octeon_rgmx_medstat); ifmedia_add(&sc->media, bit2media[0], 0, NULL); ifmedia_set(&sc->media, bit2media[0]); ether_ifattach(sc->ifp, sc->ieee); return (0); } /* Driver methods */ /* ------------------------------------------------------------------- * * rgmii_identify() * * ------------------------------------------------------------------- */ static void rgmii_identify (driver_t *drv, device_t parent) { BUS_ADD_CHILD(parent, 0, "rgmii", 0); } /* ------------------------------------------------------------------- * * rgmii_probe() * * ------------------------------------------------------------------- */ static int rgmii_probe (device_t dev) { if (device_get_unit(dev) != 0) panic("can't probe/attach more rgmii devices\n"); device_set_desc(dev, "Octeon RGMII"); return (0); } /* ------------------------------------------------------------------- * * rgmii_attach() * * ------------------------------------------------------------------- */ static int rgmii_attach (device_t dev) { struct rgmx_softc_dev *sc; device_t child; int iface, port, nr_ports, error; void *softc; int irq_rid; octeon_config_hw_units_pre_ports(); /* Count interfaces and ports*/ octeon_gmxx_inf_mode_t iface_mode; iface_mode.word64 = 0; for (iface = 0; iface < 2; iface++) { iface_mode.word64 = oct_read64(OCTEON_RGMX_INF_MODE(iface)); /* interface is either disabled or SPI */ if (!iface_mode.bits.en) continue; if (octeon_get_chipid() == OCTEON_CN3020_CHIP) { nr_ports = 2; } else { nr_ports = (octeon_has_4ports()) ? 4 : 3; if (iface_mode.bits.type ) { if (octeon_get_chipid() == OCTEON_CN5020_CHIP) nr_ports = 2; else continue; } } oct_write64(OCTEON_RGMX_TX_PRTS(iface), nr_ports); for (port = iface * 16; port < iface * 16 + nr_ports; port++) { child = device_add_child(dev, OCTEON_RGMX_DEV_NAME, num_devices); if (child == NULL) panic("%s: device_add_child() failed\n", __func__); softc = malloc(sizeof(struct rgmx_softc_dev), M_DEVBUF, M_NOWAIT | M_ZERO); if (!softc) { panic("%s malloc failed for softc\n", __func__); } device_set_softc(child, softc); device_set_desc(child, "Octeon RGMII"); sc = device_get_softc(child); if (!sc) { printf(" No sc\n"); num_devices++; continue; } port_array[num_devices] = port; rgmx_scdev_array[num_devices] = sc; RGMX_LOCK_INIT(sc, device_get_nameunit(child)); octeon_rgmx_init_sc(sc, child, port, num_devices); octeon_config_hw_units_port(sc, port); if (octeon_rgmx_init_ifnet(sc)) { device_printf(dev, " ifinit failed for rgmx port %u\n", port); return (ENOSPC); } num_devices++; } } octeon_config_hw_units_post_ports(); irq_rid = 0; irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &irq_rid, 0, 0, 1, RF_SHAREABLE | RF_ACTIVE); if (irq_res == NULL) { device_printf(dev, "failed to allocate irq\n"); return (ENXIO); } #ifdef OCTEON_RGMX_SCHEDULED_ISRS /* * Single task queues for all child devices. Since POW gives us a unified * interrupt based on POW groups, not based on PORTs. */ TASK_INIT(&rxtx_isr_task, 0, octeon_rgmx_isr_rxtx, NULL); TASK_INIT(&link_isr_task, 0, octeon_rgmx_isr_link, NULL); tq = taskqueue_create_fast("octeon_rgmx_taskq", M_NOWAIT, taskqueue_thread_enqueue, &tq); taskqueue_start_threads(&tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); error = bus_setup_intr(dev, irq_res, INTR_TYPE_NET, octeon_rgmx_intr_fast, NULL, NULL, &int_handler_tag); if (error != 0) { device_printf(dev, "bus_setup_intr returned %d\n", error); taskqueue_free(tq); tq = NULL; return (error); } #else /* OCTEON_RGMX_SCHEDULED_ISRS */ error = bus_setup_intr(dev, irq_res, INTR_TYPE_NET, octeon_rgmx_intr, NULL, NULL, &int_handler_tag); if (error != 0) { device_printf(dev, "bus_setup_intr returned %d\n", error); tq = NULL; return (error); } #endif /* OCTEON_RGMX_SCHEDULED_ISRS */ return (bus_generic_attach(dev)); } #define OCTEON_MAX_RGMX_PORT_NUMS 32 #define OCTEON_POW_RX_GROUP_NUM 0 #define OCTEON_POW_TX_GROUP_NUM 1 /* If using TX WQE from PKO */ #define OCTEON_POW_RX_GROUP_MASK (1 << OCTEON_POW_RX_GROUP_NUM) #define OCTEON_POW_TX_GROUP_MASK (1 << OCTEON_POW_TX_GROUP_NUM) #define OCTEON_POW_ALL_OUR_GROUPS_MASK (OCTEON_POW_RX_GROUP_MASK | OCTEON_POW_RX_GROUP_MASK) #define OCTEON_POW_ALL_GROUPS_MASK 0xffff #define OCTEON_POW_WORKQUEUE_INT (0x8001670000000200ull) #define OCTEON_POW_WORKQUEUE_INT_PC (0x8001670000000208ull) #define OCTEON_POW_WORKQUEUE_INT_THRESHOLD(group_num) ((0x8001670000000080ull+((group_num)*0x8))) #define OCTEON_RGMX_POW_NOS_CNT (0x8001670000000228ull) #define OCTEON_POW_INT_CNTR(core) (0x8001670000000100ull+((core)*0x8)) #define OCTEON_POW_INPT_Q_ALL_QOS (0x8001670000000388ull) #define OCTEON_POW_INPT_QOS_GRP(grp) (0x8001670000000340ull + ((grp) * 0x8)) #define NUM_RX_PACKETS_CTL (MAX_RX_BUFS + 3000) #define NUM_TX_PACKETS_CTL 40 #define FPA_NOPOOL 0 #define OCTEON_FPA_RX_PACKET_POOL 0 #define OCTEON_FPA_RX_PACKET_POOL_WORDS 208 /* 2048 bytes */ #define OCTEON_FPA_RX_PACKET_POOL_ELEM_SIZE (OCTEON_FPA_RX_PACKET_POOL_WORDS) #define OCTEON_FPA_RX_PACKET_POOL_ELEMENTS (MAX_RX_BUFS) #define OCTEON_RX_MAX_SIZE (OCTEON_FPA_RX_PACKET_POOL_WORDS * sizeof(uint64_t)) #define OCTEON_FPA_WQE_RX_POOL 1 #define OCTEON_FPA_WQE_RX_WORDS (OCTEON_CACHE_LINE_SIZE/8) #define OCTEON_FPA_WQE_RX_POOL_ELEM_SIZE (OCTEON_FPA_WQE_RX_WORDS) #define OCTEON_FPA_WQE_RX_POOL_ELEMENTS (NUM_RX_PACKETS_CTL) #define OCTEON_FPA_TX_PACKET_POOL 2 #define OCTEON_FPA_TX_PACKET_POOL_WORDS 208 /* 2048 bytes */ #define OCTEON_FPA_TX_PACKET_POOL_ELEM_SIZE (OCTEON_FPA_TX_PACKET_POOL_WORDS) #define OCTEON_FPA_TX_PACKET_POOL_ELEMENTS (MAX_TX_BUFS) #define OCTEON_TX_MAX_SIZE (OCTEON_FPA_TX_PACKET_POOL_WORDS * sizeof(uint64_t)) #define OCTEON_FPA_TX_CMDBUF_POOL 3 #define OCTEON_FPA_TX_CMD_SIZE 2 #define OCTEON_FPA_TX_CMD_NUM 300 #define OCTEON_FPA_TX_CMDBUF_POOL_WORDS (OCTEON_FPA_TX_CMD_SIZE * OCTEON_FPA_TX_CMD_NUM) #define OCTEON_FPA_TX_CMDBUF_POOL_ELEM_SIZE (OCTEON_FPA_TX_CMDBUF_POOL_WORDS +1) #define OCTEON_FPA_TX_CMDBUF_POOL_ELEMENTS (30 * OCTEON_RGMX_NUM_PORTS_MAX) #define FIRST_PARTICLE_SKIP 0 #define NOT_FIRST_PARTICLE_SKIP 0 #define ENABLE_BACK_PRESSURE 0 #define RGMX_MAX_PAK_RECEIVE 5000000 #ifdef OCTEON_RGMX_SCHEDULED_ISRS static void octeon_rgmx_isr_link (void *context, int pending) { octeon_line_status_loop(NULL); } static void octeon_rgmx_isr_rxtx (void *context, int pending) { octeon_rx_loop(NULL); } /********************************************************************* * * Fast Interrupt Service routine * *********************************************************************/ //#define OCTEON_RGMX_POW_TIME_THR_INTS 1 static int octeon_rgmx_intr_fast(void *arg) { int handled_flag = 0; uint64_t ciu_summary; ciu_summary = ciu_get_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX); if (ciu_summary & CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)) { /* * Timer Interrupt for link status checks * Acknowledging it will mask it for this cycle. */ ciu_clear_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX, CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)); taskqueue_enqueue(taskqueue_fast, &link_isr_task); handled_flag = 1; } if (ciu_summary & OCTEON_POW_ALL_GROUPS_MASK) { #ifndef OCTEON_RGMX_POW_TIME_THR_INTS /* * When using POW IQ/DSQ size based interrupts, then * ack the interrupts right away. So they don't interrupt * until the queue size goes to 0 again. */ oct_write64(OCTEON_POW_WORKQUEUE_INT, 0x10001 << OCTEON_POW_RX_GROUP_NUM); #else /* * We use POW thresholds based interrupt signalled on timer * countdown. Acknowledge it now so that it doesn't * interrupt us until next countdown to zero. */ oct_write64(OCTEON_POW_WORKQUEUE_INT, 0x1 << OCTEON_POW_RX_GROUP_NUM); #endif taskqueue_enqueue(tq, &rxtx_isr_task); handled_flag = 1; } return ((handled_flag) ? FILTER_HANDLED : FILTER_STRAY); } #else /* ! OCTEON_RGMX_SCHEDULED_ISRS */ /* * octeon_rgmx_intr * * This is direct inline isr. Will do all its work and heavy-lifting in interrupt context. * * Also note that the RGMX_LOCK/UNLOCK code will have to checked/added, since that is new and * was not supported with this model. */ static int octeon_rgmx_intr (void *arg) { int flag = 0; uint64_t ciu_summary; /* * read ciu to see if any bits are pow */ while (1) { ciu_summary = ciu_get_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX); if ((ciu_summary & (OCTEON_POW_ALL_GROUPS_MASK | CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1))) == 0) { break; } flag = 1; if (ciu_summary & OCTEON_POW_ALL_GROUPS_MASK) { octeon_rx_loop(NULL); /* * Acknowledge the interrupt after processing queues. */ oct_write64(OCTEON_POW_WORKQUEUE_INT, OCTEON_POW_RX_GROUP_MASK); } if (ciu_summary & CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)) { octeon_line_status_loop(NULL); ciu_clear_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX, CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)); } } return ((flag) ? FILTER_HANDLED : FILTER_STRAY); } #endif /* OCTEON_RGMX_SCHEDULED_ISRS */ static struct mbuf *octeon_rgmx_build_new_rx_mbuf(struct ifnet *ifp, void *data_start, u_int totlen); static struct mbuf *octeon_rgmx_build_new_rx_mbuf (struct ifnet *ifp, void *data_start, u_int totlen) { struct mbuf *m, *m0, *newm; caddr_t newdata; int len; if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) { #ifdef LEDEBUG if_printf(ifp, "invalid packet size %d; dropping\n", totlen); #endif return (NULL); } MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 == NULL) { return (NULL); } /* Initialize packet header info. */ m0->m_pkthdr.rcvif = ifp; m0->m_pkthdr.len = totlen; m0->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m0->m_pkthdr.csum_data = 0xffff; len = MHLEN; m = m0; while (totlen > 0) { if (totlen >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) goto octeon_rgmx_build_new_rx_mbuf_bad; len = MCLBYTES; } if (m == m0) { newdata = (caddr_t)ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN; len -= newdata - m->m_data; m->m_data = newdata; } /* Set the length of this mbuf. */ m->m_len = len = min(totlen, len); bcopy(data_start, mtod(m, caddr_t), len); data_start = (void *) (((u_long) (data_start)) + len); totlen -= len; if (totlen > 0) { MGET(newm, M_DONTWAIT, MT_DATA); if (newm == 0) goto octeon_rgmx_build_new_rx_mbuf_bad; len = MLEN; m = m->m_next = newm; } } return (m0); octeon_rgmx_build_new_rx_mbuf_bad: m_freem(m0); return (NULL); } //#define DEBUG_RX 1 static void octeon_rgmx_rx_process_work (octeon_wqe_t *work, u_int port) { struct rgmx_softc_dev *sc; struct ifnet *ifp; u_int len; void *data_start, *new_data_start; struct mbuf *mbuf; //#define DEBUG_RX_PKT_DUMP 1 #ifdef DEBUG_RX_PKT_DUMP int i; u_char *dc; #endif data_start = octeon_pow_pktptr_to_kbuffer(work->packet_ptr); //#define DEBUG_RX2 #ifdef DEBUG_RX2 printf(" WQE 0x%X: port:%u ", work, port); printf(" Grp: %u, %llX Tag: %u %llX type: %u 0x%llx\n", work->grp, work->grp, work->tag, work->tag, work->tag_type, work->tag_type); #endif if ((port >= OCTEON_RGMX_MIN_PORT) || (port <= OCTEON_RGMX_MAX_PORT)) { sc = get_rgmx_softc(port); if (!sc || !sc->ifp) { printf(" octeon_rgmx_rx_process_work No sc or sc->ifp - port:%u", port); } else { ifp = sc->ifp; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if (!work->word2.bits.rcv_error) { len = work->len; /* * We cannot pass the same FPA phys-buffer higher up. * User space will not be able to use this phys-buffer. * * Start building a mbuf packet here using data_start & len. */ new_data_start = data_start; if (!work->word2.bits.not_IP) { new_data_start = (void *) (((unsigned long) (new_data_start)) + 14); /* mark it as checksum checked */ } else { new_data_start = (void *) (((unsigned long) (new_data_start)) + 8); } #ifdef DEBUG_RX_PKT_DUMP dc = new_data_start; printf("In:\n"); for (i = 0; i < len; i++) { if (!(i % 16)) printf ("\n"); printf(" %02X", dc[i]); } #endif mbuf = octeon_rgmx_build_new_rx_mbuf(ifp, new_data_start, len); if (mbuf) { // printf(" Passing pkt to ifp: pkt_len: %u len: %u ", mbuf->m_pkthdr.len, mbuf->m_len); #ifdef DEBUG_RX_PKT_DUMP dc = mtod(mbuf, u_char *); printf("\n"); printf("In: "); for (i = 0; i < mbuf->m_len; i++) { if (!(i % 16)) printf ("\n"); printf(" %02X", dc[i]); } #endif /* Feed the packet to upper layer. */ (*ifp->if_input)(ifp, mbuf); ifp->if_ipackets++; } else { /* mbuf error */ if_printf(ifp, "mbuf rx construct error\n"); printf(" mbuf rx construct error\n"); ifp->if_ierrors++; } /* mbuf error */ } else { /* rcv_error */ ifp->if_ierrors++; } /* rcv_error */ } /* IFF_DRV_RUNNING */ } /* sc && sc->ifp */ } else { /* port number */ printf(" rgmx_rx:%u bad port\n", port); } octeon_fpa_free(data_start, OCTEON_FPA_RX_PACKET_POOL, 0); octeon_fpa_free((void *)work, OCTEON_FPA_WQE_RX_POOL, 0); } /* ------------------------------------------------------------------- * * octeon_rx_loop() * * ------------------------------------------------------------------- */ //#define OCTEON_VISUAL_RGMX 1 #ifdef OCTEON_VISUAL_RGMX static int where0 = 0; static int where1 = 0; #endif static void octeon_rx_loop (void *unused) { u_int core_id; uint64_t prev_grp_mask; u_int pak_count; octeon_wqe_t *work; core_id = octeon_get_core_num(); pak_count = 0; /* Only allow work for our group */ prev_grp_mask = oct_read64(OCTEON_POW_CORE_GROUP_MASK(core_id)); oct_write64(OCTEON_POW_CORE_GROUP_MASK(core_id), OCTEON_POW_ALL_GROUPS_MASK); #ifdef OCTEON_VISUAL_RGMX octeon_led_run_wheel(&where0, 3); #endif while(1) { if (pak_count++ > RGMX_MAX_PAK_RECEIVE) { break; } work = octeon_pow_work_request_sync(OCTEON_POW_WAIT); if (work == NULL) { /* * No more incoming packets. We can take a break now. */ break; } #ifdef OCTEON_VISUAL_RGMX octeon_led_run_wheel(&where1, 4); #endif octeon_rgmx_rx_process_work(work, work->ipprt); } oct_write64(OCTEON_POW_CORE_GROUP_MASK(core_id), prev_grp_mask); } static void *octeon_rgmx_write_mbufs_to_fpa_buff (struct rgmx_softc_dev *sc, struct mbuf *m, u_int len) { struct mbuf *mp; void *data_area; u_char *write_offset; /* * FIXME * * Compare len with max FPA-tx-packet size. Or else we will possibly corrupt the next pkt. */ /* * Get an FPA buffer from Xmit-packets FPA pool */ data_area = octeon_fpa_alloc(OCTEON_FPA_TX_PACKET_POOL); if (!data_area) { /* * Fail. No room. No resources. */ return (NULL); } /* * Transfer the data from mbuf chain to the transmission buffer. */ write_offset = data_area; for (mp = m; mp != 0; mp = mp->m_next) { if (mp->m_len) { bcopy(mtod(mp, caddr_t), write_offset, mp->m_len); write_offset = (u_char *) (((u_long) write_offset) + mp->m_len); } } return (data_area); } static u_int octeon_rgmx_pko_xmit_packet (struct rgmx_softc_dev *sc, void *out_buff, u_int len, u_int checksum) { octeon_pko_command_word0_t pko_cmd; octeon_pko_packet_ptr_t pko_pkt_word; u_long temp; u_short xmit_cmd_index; uint64_t *xmit_cmd_ptr; uint64_t xmit_cmd_state; int queue = 0; // we should randomize queue # based on core num. Using same // queue 0 for this port, by all cores on is less efficient. /* * Prepare the PKO buffer and command word. * Cmd Buf Word 0 * No FAU * Set #-segs and #-bytes */ pko_cmd.word64 = 0; pko_cmd.bits.segs = 1; pko_cmd.bits.total_bytes = len; if (checksum) { pko_cmd.bits.ipoffp1 = ETHER_HDR_LEN + 1; /* IPOffP1 is +1 based. 1 means offset 0 */ } /* * Build the PKO buffer pointer. PKO Cmd Buf Word 1 */ pko_pkt_word.word64 = 0; pko_pkt_word.bits.addr = OCTEON_PTR2PHYS(out_buff); pko_pkt_word.bits.pool = OCTEON_FPA_TX_PACKET_POOL; pko_pkt_word.bits.size = 2048; // dummy. Actual len is above. #ifdef DEBUG_TX printf(" PKO: 0x%llX 0x%llX ", pko_cmd.word64, pko_pkt_word.word64); #endif /* * Get the queue command ptr location from the per port per queue, pko info struct. */ octeon_spinlock_lock(&(sc->outq_ptr[queue].lock)); #ifdef DEBUG_TX printf(" xmit: sc->outq_ptr[queue].xmit_command_state: 0x%llX ", sc->outq_ptr[queue].xmit_command_state); #endif xmit_cmd_state = sc->outq_ptr[queue].xmit_command_state; sc->outq_ptr[queue].xmit_command_state = xmit_cmd_state + 2; temp = (u_long) (xmit_cmd_state >> OCTEON_PKO_INDEX_BITS); #ifdef DEBUG_TX printf(" temp: 0x%X ", temp); #endif xmit_cmd_ptr = (uint64_t *) MIPS_PHYS_TO_KSEG0(temp); xmit_cmd_index = xmit_cmd_state & OCTEON_PKO_INDEX_MASK; xmit_cmd_ptr += xmit_cmd_index; /* * We end the PKO cmd buffer at odd boundary. Towards the end we will have * 4 or 3 or 2 or 1 or 0 word remaining. Case of 4, 2, or 0 can never happen. * We only care when we have 3 words remaining. In this case we write our 2 words * for PKO command and 3rd word as chain for next PKO cmd buffer. */ xmit_cmd_ptr[0] = pko_cmd.word64; if (xmit_cmd_index < (OCTEON_FPA_TX_CMDBUF_POOL_WORDS - 2)) { /* * Plenty of space left. Write our 2nd word and worry the next time. */ xmit_cmd_ptr[1] = pko_pkt_word.word64; } else { /* * 3 words or less are left. We write our 2nd word now and then put in a chain link * to new PKO cmd buf. */ uint64_t phys_cmd_buf = octeon_fpa_alloc_phys(OCTEON_FPA_TX_CMDBUF_POOL); if (!phys_cmd_buf) { /* * FPA pool for xmit-buffer-commands is empty. */ sc->outq_ptr[queue].xmit_command_state -= 2; octeon_spinlock_unlock(&(sc->outq_ptr[queue].lock)); return (0); } xmit_cmd_ptr[1] = pko_pkt_word.word64; xmit_cmd_ptr[2] = phys_cmd_buf; sc->outq_ptr[queue].xmit_command_state = (phys_cmd_buf << OCTEON_PKO_INDEX_BITS); } /* * Unlock queue structures. */ octeon_spinlock_unlock(&(sc->outq_ptr[queue].lock)); /* * 2 words incremented in PKO. Ring the doorbell. */ #ifdef DEBUG_TX printf(" Ringing doorbell: Port %u Queue %u words 2", sc->port, octeon_pko_get_base_queue(sc->port) + queue); #endif octeon_pko_ring_doorbell(sc->port, octeon_pko_get_base_queue(sc->port) + queue, 2); return (1); } static void octeon_rgmx_xmit_mark_buffers_done(struct rgmx_softc_dev *sc, u_int n); static void octeon_rgmx_xmit_mark_buffers_done (struct rgmx_softc_dev *sc, u_int n) { struct mbuf *m; u_int i; for (i = 0; i < n; i++) { /* * Remove packets in queue. Leaving a lag of 3, to allow for PKO in-flight xmission */ if (_IF_QLEN(&sc->tx_pending_queue) > 4) { IF_DEQUEUE(&sc->tx_pending_queue, m); if (!m) { break; // Queue became empty now. Break out. } /* * Return the mbuf to system. */ m_freem(m); } } if (!i) { return; // Nothing removed from queue. } /* * The transmitter is no more active. * Reset output active flag and watchdog timer. */ sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } #define OCTEON_RGMX_FLUSH_N_XMIT_MBUFS_EACH_LOOP 5 #define OCTEON_RGMX_FLUSH_PENDING_MBUFS_MAX 1000 #ifdef __not_used__ /* * octeon_rgmx_output_flush * * Drop all packets queued at ifnet layer. */ static void octeon_rgmx_output_flush (struct ifnet *ifp) { struct mbuf *m; u_int max_flush = OCTEON_RGMX_FLUSH_PENDING_MBUFS_MAX; /* Arbitrarily high number */ while (max_flush-- && _IF_QLEN(&ifp->if_snd)) { /* * Get the next mbuf Packet chain to flush. */ IF_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { /* No more packets to flush */ break; } _IF_DROP(&ifp->if_snd); m_freem(m); ifp->if_oerrors++; } } #endif /* * octeon_rgmx_output_start * * Start output on interface. */ static void octeon_rgmx_output_start (struct ifnet *ifp) { struct rgmx_softc_dev *sc = ifp->if_softc; RGMX_LOCK(sc); octeon_rgmx_output_start_locked(ifp); RGMX_UNLOCK(sc); } /* * octeon_rgmx_output_start_locked * * Start output on interface. Assume Driver locked */ static void octeon_rgmx_output_start_locked (struct ifnet *ifp) { struct rgmx_softc_dev *sc = ifp->if_softc; struct mbuf *m; u_int len, need_l4_checksum; void *out_buff; /* * Take out some of the last queued mbuf's from xmit-pending queue */ octeon_rgmx_xmit_mark_buffers_done(sc, OCTEON_RGMX_FLUSH_N_XMIT_MBUFS_EACH_LOOP); while (1) { /* * See if there is room to put another packet in the buffer. * We *could* do better job by peeking the send queue to * know the length of the next packet. Current version just * tests against the worst case (i.e., longest packet). FIXME. * * When adding the packet-peek feature, don't forget adding a * test on txb_count against QUEUEING_MAX. * There is a little chance the packet count exceeds * the limit. Assume transmission buffer is 8KB (2x8KB * configuration) and an application sends a bunch of small * (i.e., minimum packet sized) packets rapidly. An 8KB * buffer can hold 130 blocks of 62 bytes long... */ /* * If unable to send more. */ if (_IF_QLEN(&sc->tx_pending_queue) >= MAX_TX_BUFS) { printf(" Xmit not possible. NO room %u", _IF_QLEN(&sc->tx_pending_queue)); goto indicate_active; } /* * Get the next mbuf chain for a packet to send. */ IF_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { /* No more packets to send. */ goto indicate_inactive; } len = m->m_pkthdr.len; /* * Should never send big packets. If such a packet is passed, * it should be a bug of upper layer. We just ignore it. * ... Partial (too short) packets, neither. */ if (len < ETHER_HDR_LEN || len > ETHER_MAX_LEN - ETHER_CRC_LEN) { /* * Fail. Bad packet size. Return the mbuf to system. */ if_printf(ifp, "got an out-of-spec packet (%u bytes) to send\n", len); m_freem(m); goto indicate_active; } /* * Copy the mbuf chain into the transmission buffer. * txb_* variables are updated as necessary. */ out_buff = octeon_rgmx_write_mbufs_to_fpa_buff(sc, m, len); if (!out_buff) { /* * No FPA physical buf resource. * Let's requeue it back. And slow it down for a while. */ IF_PREPEND(&ifp->if_snd, m); goto indicate_active; } need_l4_checksum = (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) ? 1 : 0; /* * put the mbuf onto pending queue */ //#define DEBUG_TX_PKT_DUMP 1 #ifdef DEBUG_TX_PKT_DUMP int ii; u_char *dc = out_buff; printf("\n"); printf("Out: "); for (ii = 0; ii < len; ii++) printf(" %X", dc[ii]); printf("\n"); #endif ETHER_BPF_MTAP(ifp, m); IF_ENQUEUE(&sc->tx_pending_queue, m); /* * Pass the mbuf data packet to PKO for xmission. */ octeon_rgmx_pko_xmit_packet(sc, out_buff, len, need_l4_checksum); ifp->if_opackets++; } indicate_inactive: /* * We are using the !OACTIVE flag to indicate to * the outside world that we can accept an * additional packet rather than that the * transmitter is _actually_ active. Indeed, the * transmitter may be active, but if we haven't * filled all the buffers with data then we still * want to accept more. */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; indicate_active: /* * The transmitter is active, and there are no room for * more outgoing packets in the transmission buffer. */ ifp->if_oerrors++; // sc->mibdata.dot3StatsInternalMacTransmitErrors++; ifp->if_drv_flags |= IFF_DRV_OACTIVE; return; } /* ------------------------------------------------------------------- * * octeon_config_hw_units() * * ------------------------------------------------------------------- * * * Initialize Octeon hardware components. To get the RGMX going. * */ static void octeon_config_hw_units_pre_ports (void) { /* Enable FPA */ octeon_enable_fpa(); /* Enable PKO */ octeon_pko_enable(); /* Init PKO */ octeon_pko_init(); /* Fill FPA */ /* * Input Buffers Pool * Pool 0 */ octeon_fpa_fill_pool_mem(OCTEON_FPA_RX_PACKET_POOL, OCTEON_FPA_RX_PACKET_POOL_ELEM_SIZE, OCTEON_FPA_RX_PACKET_POOL_ELEMENTS); /* * WQE Blocks Pool * Pool 1 */ octeon_fpa_fill_pool_mem(OCTEON_FPA_WQE_RX_POOL, OCTEON_FPA_WQE_RX_POOL_ELEM_SIZE, OCTEON_FPA_WQE_RX_POOL_ELEMENTS); /* * PKO Command Pool * Pool 3 */ octeon_fpa_fill_pool_mem(OCTEON_FPA_TX_CMDBUF_POOL, OCTEON_FPA_TX_CMDBUF_POOL_ELEM_SIZE, OCTEON_FPA_TX_CMDBUF_POOL_ELEMENTS); /* * Output Buffers Pool * Pool 2 */ octeon_fpa_fill_pool_mem(OCTEON_FPA_TX_PACKET_POOL, OCTEON_FPA_TX_PACKET_POOL_ELEM_SIZE, OCTEON_FPA_TX_PACKET_POOL_ELEMENTS); octeon_rgmx_enable_RED_all(OCTEON_FPA_RX_PACKET_POOL_ELEMENTS >> 2, OCTEON_FPA_RX_PACKET_POOL_ELEMENTS >> 3); /* Configure IPD */ octeon_ipd_config(OCTEON_FPA_RX_PACKET_POOL_WORDS, FIRST_PARTICLE_SKIP / 8, NOT_FIRST_PARTICLE_SKIP / 8, FIRST_PARTICLE_SKIP / 128, NOT_FIRST_PARTICLE_SKIP / 128, OCTEON_FPA_WQE_RX_POOL, OCTEON_IPD_OPC_MODE_STF, ENABLE_BACK_PRESSURE); /* * PKO setup Output Command Buffers */ octeon_pko_config_cmdbuf_global_defaults(OCTEON_FPA_TX_CMDBUF_POOL, OCTEON_FPA_TX_CMDBUF_POOL_ELEM_SIZE); } static void octeon_config_hw_units_port (struct rgmx_softc_dev *sc, u_int port) { const u_int priorities[8] = {8,8,8,8,8,8,8,8}; u_int total_queues, base_queue; octeon_config_rgmii_port(port); total_queues = octeon_pko_get_num_queues(port); base_queue = octeon_pko_get_base_queue(port); /* Packet output configures Queue and Ports */ octeon_pko_config_port(port, base_queue, total_queues, priorities, OCTEON_FPA_TX_CMDBUF_POOL, sc->outq_ptr); octeon_rgmx_set_mac(port); /* Setup Port input tagging */ octeon_rgmx_config_pip(port); } typedef union { uint64_t word64; struct { uint64_t rsvd3 : 35; uint64_t enable : 1; uint64_t time_thr : 4; uint64_t rsvd2 : 1; uint64_t ds_thr : 11; uint64_t rsvd : 1; uint64_t iq_thr : 11; } bits; } octeon_rgmx_pow_int_threshold_t; typedef union { uint64_t word64; struct { uint64_t rsvd : 36; uint64_t tc_cnt : 4; uint64_t ds_cnt : 12; uint64_t iq_cnt : 12; } bits; } octeon_rgmx_pow_int_cnt_t; typedef union { uint64_t word64; struct { uint64_t rsvd3 : 4; uint64_t thr_freq : 28; // R/O uint64_t rsvd2 : 4; uint64_t thr_period : 20; uint64_t rsvd : 8; } bits; } octeon_rgmx_pow_int_pc_t; typedef union { uint64_t word64; struct { uint64_t rsvd : 52; uint64_t nos_cnt : 12; } bits; } octeon_rgmx_pow_nos_cnt; typedef union { uint64_t word64; struct { uint64_t rsvd : 32; uint64_t inb_pkts : 32; } bits; } octeon_rgmx_pip_inb_pkts; typedef union { uint64_t word64; struct { uint64_t rsvd : 48; uint64_t inb_errs : 16; } bits; } octeon_rgmx_pip_inb_errs; typedef union { uint64_t word64; struct { uint64_t rsvd : 32; uint64_t iq_cnt : 32; } bits; } octeon_pow_inpt_q_all_qos; typedef union { uint64_t word64; struct { uint64_t rsvd : 32; uint64_t iq_cnt : 32; } bits; } octeon_pow_inpt_q_grp_qos; static void octeon_config_hw_units_post_ports (void) { octeon_rgmx_pow_int_threshold_t thr; octeon_rgmx_pow_int_pc_t intpc; thr.word64 = 0; intpc.word64 = 0; intpc.bits.thr_freq = (500 * 1000 * 1000) / (1000 * 16 * 256); #ifdef OCTEON_RGMX_POW_TIME_THR_INTS thr.bits.enable = 1; thr.bits.time_thr = 0xf; oct_write64(OCTEON_POW_WORKQUEUE_INT_THRESHOLD(OCTEON_POW_RX_GROUP_NUM), thr.word64); oct_write64(OCTEON_POW_WORKQUEUE_INT_PC, intpc.word64); #else thr.bits.ds_thr = thr.bits.iq_thr = 1; // Only if doing absolute queue-cnt interrupts. oct_write64(OCTEON_POW_WORKQUEUE_INT_THRESHOLD(OCTEON_POW_RX_GROUP_NUM), thr.word64); #endif - ciu_enable_interrupts(OCTEON_CORE_ID, OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX, + ciu_enable_interrupts(PCPU_GET(cpuid), OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX, (OCTEON_POW_RX_GROUP_MASK | CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)), CIU_MIPS_IP2); ciu_clear_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX, CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)); octeon_ciu_start_gtimer(CIU_GENTIMER_NUM_1, OCTEON_GENTIMER_PERIODIC, OCTEON_GENTIMER_LEN_1SEC); /* * Enable IPD */ octeon_ipd_enable(); } static void octeon_rgmx_config_pip (u_int port) { octeon_pip_gbl_cfg_t pip_config; octeon_pip_port_cfg_t pip_port_config; octeon_pip_port_tag_cfg_t pip_tag_config; /* * PIP Global config */ pip_config.word64 = 0; pip_config.bits.max_l2 = 1; oct_write64(OCTEON_PIP_GBL_CFG, pip_config.word64); /* * PIP Port config */ pip_port_config.word64 = 0; pip_port_config.bits.mode = OCTEON_PIP_PORT_CFG_MODE_SKIPL2; pip_port_config.bits.qos = port & 0x7; pip_port_config.bits.crc_en = 1; /* * PIP -> POW tags config * * We don't use any pkt input fields for tag hash, except for Port# */ pip_tag_config.word64 = 0; pip_tag_config.bits.grptag = 0; pip_tag_config.bits.grptagmask = 0xf; pip_tag_config.bits.grptagbase = 1; pip_tag_config.bits.ip6_src_flag = 0; pip_tag_config.bits.ip6_dst_flag = 0; pip_tag_config.bits.ip6_sprt_flag = 0; pip_tag_config.bits.ip6_dprt_flag = 0; pip_tag_config.bits.ip6_nxth_flag = 0; pip_tag_config.bits.ip4_src_flag = 1; pip_tag_config.bits.ip4_dst_flag = 1; pip_tag_config.bits.ip4_sprt_flag = 1; pip_tag_config.bits.ip4_dprt_flag = 1; pip_tag_config.bits.ip4_pctl_flag = 1; pip_tag_config.bits.tcp6_tag_type = 0; pip_tag_config.bits.tcp4_tag_type = 0; pip_tag_config.bits.ip6_tag_type = 0; pip_tag_config.bits.ip4_tag_type = 0; pip_tag_config.bits.inc_prt_flag = 1; pip_tag_config.bits.non_tag_type = OCTEON_POW_TAG_TYPE_NULL; pip_tag_config.bits.grp = OCTEON_POW_RX_GROUP_NUM; octeon_pip_config_port(port, pip_port_config, pip_tag_config); oct_write64(OCTEON_POW_CORE_GROUP_MASK(OUR_CORE), OCTEON_POW_ALL_GROUPS_MASK); } /* * octeon_rgmx_stop_port * */ static u_int octeon_rgmx_stop_port (u_int port) { int interface = INTERFACE(port); int index = INDEX(port); octeon_rgmx_prtx_cfg_t gmx_cfg; u_int last_enabled = 0; gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, interface)); last_enabled = (gmx_cfg.bits.en == 1); gmx_cfg.bits.en = 0; oct_write64(OCTEON_RGMX_PRTX_CFG(index, interface), gmx_cfg.word64); return (last_enabled); } static void octeon_rgmx_start_port(u_int port) { int interface = INTERFACE(port); int index = INDEX(port); octeon_rgmx_prtx_cfg_t gmx_cfg; gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, interface)); gmx_cfg.bits.en = 1; oct_write64(OCTEON_RGMX_PRTX_CFG(index, interface), gmx_cfg.word64); } static void octeon_rgmx_stop (struct rgmx_softc_dev *sc) { octeon_rgmx_stop_port(sc->port); /* Reset transmitter variables and interface flags. */ sc->ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING); sc->txb_count = 0; sc->txb_sched = 0; } /* Change the media selection. */ static int octeon_rgmx_medchange (struct ifnet *ifp) { struct rgmx_softc_dev *sc = ifp->if_softc; #ifdef DIAGNOSTIC /* If_media should not pass any request for a media which this interface doesn't support. */ int b; for (b = 0; bit2media[b] != 0; b++) { if (bit2media[b] == sc->media.ifm_media) break; } if (((1 << b) & sc->mbitmap) == 0) { if_printf(sc->ifp, "got an unsupported media request (0x%x)\n", sc->media.ifm_media); return EINVAL; } #endif /* We don't actually change media when the interface is down. fe_init() will do the job, instead. Should we also wait until the transmission buffer being empty? Changing the media when we are sending a frame will cause two garbages on wires, one on old media and another on new. FIXME */ if (sc->ifp->if_flags & IFF_UP) { printf(" Media change requested while IF is up\n"); } else { printf(" Media change requested while IF is Down\n"); } return 0; } static void octeon_rgmx_medstat (struct ifnet *ifp, struct ifmediareq *ifm) { struct rgmx_softc_dev *sc = ifp->if_softc; octeon_rgmx_rxx_rx_inbnd_t link_status; octeon_rgmx_config_speed(sc->port, 1); RGMX_LOCK(sc); ifm->ifm_status = IFM_AVALID; ifm->ifm_active = IFM_ETHER; /* * Parse link status. */ link_status.word64 = sc->link_status; if (!link_status.bits.status) { RGMX_UNLOCK(sc); return; } ifm->ifm_status |= IFM_ACTIVE; switch (link_status.bits.speed) { case 0: ifm->ifm_active |= IFM_10_T; break; case 1: ifm->ifm_active |= IFM_100_TX; break; case 2: ifm->ifm_active |= IFM_1000_T;; break; default: /* Unknown! */ break; } /* * Check duplex. */ if (link_status.bits.duplex == 1) ifm->ifm_active |= IFM_FDX; else ifm->ifm_active |= IFM_HDX; RGMX_UNLOCK(sc); } static void octeon_rgmx_config_cam(struct ifnet *ifp) { struct rgmx_softc_dev *sc = ifp->if_softc; u_int port = sc->port; int index = INDEX(port); int iface = INTERFACE(port); u_int last_enabled; uint64_t adr_ctl; last_enabled = octeon_rgmx_stop_port(port); adr_ctl = oct_read64(OCTEON_RGMX_RXX_ADR_CTL(index, iface)); /* * Always accept broadcast traffic. */ if ((adr_ctl & OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST) == 0) adr_ctl |= OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST; /* * Accept all multicast in all multicast mode and in * promiscuous mode. * * XXX Since we don't handle programming the CAM for * multicast filtering, always accept all multicast. */ adr_ctl &= ~OCTEON_RGMX_ADRCTL_REJECT_ALL_MULTICAST; adr_ctl |= OCTEON_RGMX_ADRCTL_ACCEPT_ALL_MULTICAST; /* * In promiscuous mode, the CAM is shut off, so reject everything. * Otherwise, filter using the CAM. */ if ((ifp->if_flags & IFF_PROMISC) != 0) { adr_ctl &= ~OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC; adr_ctl |= OCTEON_RGMX_ADRCTL_CAM_MODE_REJECT_DMAC; } else { adr_ctl &= ~OCTEON_RGMX_ADRCTL_CAM_MODE_REJECT_DMAC; adr_ctl |= OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC; } oct_write64(OCTEON_RGMX_RXX_ADR_CTL(index, iface), adr_ctl); /* * If in promiscuous mode, disable the CAM. */ if ((ifp->if_flags & IFF_PROMISC) != 0) oct_write64(OCTEON_RGMX_RXX_ADR_CAM_EN(index, iface), 0); else oct_write64(OCTEON_RGMX_RXX_ADR_CAM_EN(index, iface), 1); if (last_enabled) octeon_rgmx_start_port(port); } static int octeon_rgmx_ioctl (struct ifnet * ifp, u_long command, caddr_t data) { struct rgmx_softc_dev *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error = 0; if (!sc) { printf(" octeon_rgmx_ioctl. No sc\n"); return (0); } switch (command) { case SIOCSIFFLAGS: /* * Switch interface state between "running" and * "stopped", reflecting the UP flag. */ if (ifp->if_flags & IFF_UP) { /* * New state is IFF_UP * Restart or Start now, if driver is not running currently. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { octeon_rgmx_init(sc); } octeon_rgmx_config_cam(ifp); } else { /* * New state is IFF_DOWN. * Stop & shut it down now, if driver is running currently. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { octeon_rgmx_stop(sc); } } break; case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: /* Let if_media to handle these commands and to call us back. */ error = ifmedia_ioctl(ifp, ifr, &sc->media, command); break; case SIOCSIFCAP: { int mask; ifp->if_hwassist &= ~CSUM_TSO; ifp->if_capenable &= ~IFCAP_VLAN_HWTAGGING; mask = ifr->ifr_reqcap ^ ifp->if_capenable; if (mask & IFCAP_HWCSUM) { ifp->if_capenable ^= IFCAP_HWCSUM; if (ifp->if_capenable & IFCAP_TXCSUM) { ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); } else { ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP); } } } break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void octeon_rgmx_init (void *xsc) { struct rgmx_softc_dev *sc = xsc; /* Enable interrupts. */ /* For RGMX they are already enabled earlier */ /* Enable transmitter and receiver. */ /* For RGMX they are already enabled earlier */ /* Flush out all HW receive buffers for this interface. */ /* For RGMX, no means to flush an individual port */ /* Set 'running' flag, because we are now running. */ sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Set the HW Address filter. aka program Mac-addr & Multicast filters */ /* For RGMX this was taken care of via set_mac_addr() */ /* Kick start the output */ /* Hopefully PKO is running and will pick up packets via the timer or receive loop */ /* Set link status. */ octeon_rgmx_config_speed(sc->port, 1); } static void octeon_rgmx_config_speed (u_int port, u_int report_link) { int index = INDEX(port); int iface = INTERFACE(port); struct rgmx_softc_dev *sc; octeon_rgmx_rxx_rx_inbnd_t link_status, old_link_status; octeon_rgmx_prtx_cfg_t gmx_cfg; uint64_t val64_tx_clk, val64_tx_slot, val64_tx_burst; u_int last_enabled; sc = get_rgmx_softc(port); if (!sc) { printf(" config_speed didn't find sc int:%u port:%u", iface, port); return; } /* * Look up interface-port speed params */ link_status.word64 = oct_read64(OCTEON_RGMX_RXX_RX_INBND(index, iface)); RGMX_LOCK(sc); /* * Compre to prev known state. If same then nothing to do. */ if (link_status.word64 == sc->link_status) { RGMX_UNLOCK(sc); return; } old_link_status.word64 = sc->link_status; /* * Compare to previous state modulo link status. If only link * status is different, we don't need to change media. */ if (old_link_status.bits.duplex != link_status.bits.duplex || old_link_status.bits.speed != link_status.bits.speed) { last_enabled = octeon_rgmx_stop_port(port); gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, iface)); /* * Duplex * XXX Set based on link_status.bits.duplex? */ gmx_cfg.bits.duplex = 1; switch (link_status.bits.speed) { case 0: /* 10Mbps */ gmx_cfg.bits.speed = 0; gmx_cfg.bits.slottime = 0; val64_tx_clk = 50; val64_tx_slot = 0x40; val64_tx_burst = 0; break; case 1: /* 100Mbps */ gmx_cfg.bits.speed = 0; gmx_cfg.bits.slottime = 0; val64_tx_clk = 5; val64_tx_slot = 0x40; val64_tx_burst = 0; break; case 2: /* 1Gbps */ gmx_cfg.bits.speed = 1; gmx_cfg.bits.slottime = 1; val64_tx_clk = 1; val64_tx_slot = 0x200; val64_tx_burst = 0x2000; break; case 3: /* ?? */ default: gmx_cfg.bits.speed = 1; gmx_cfg.bits.slottime = 1; val64_tx_clk = 1; val64_tx_slot = 0x200; val64_tx_burst = 0x2000; break; } oct_write64(OCTEON_RGMX_TXX_CLK(index, iface), val64_tx_clk); oct_write64(OCTEON_RGMX_TXX_SLOT(index, iface), val64_tx_slot); oct_write64(OCTEON_RGMX_TXX_BURST(index, iface), val64_tx_burst); oct_write64(OCTEON_RGMX_PRTX_CFG(index, iface), gmx_cfg.word64); if (last_enabled) octeon_rgmx_start_port(port); } /* * Now check and possibly change link status. */ if (link_status.bits.status != old_link_status.bits.status) { if (report_link) { if (link_status.bits.status) { if_link_state_change(sc->ifp, LINK_STATE_UP); } else { if_link_state_change(sc->ifp, LINK_STATE_DOWN); } } } if (report_link) { sc->link_status = link_status.word64; } else { /* * We can't update link status proper since we can't * change it in the interface, so keep the old link * status intact but note the current speed and duplex * settings. */ link_status.bits.status = old_link_status.bits.status; sc->link_status = link_status.word64; } RGMX_UNLOCK(sc); } #ifdef DEBUG_RGMX_DUMP static void octeon_dump_rgmx_stats (u_int port) { } #endif #ifdef __not_used__ static void rgmx_timer_periodic (void) { u_int port; int index; struct rgmx_softc_dev *sc; struct ifnet *ifp; for (index = 0; index < OCTEON_RGMX_NUM_PORTS_MAX; index ++) { port = port_array[index]; sc = rgmx_scdev_array[index]; /* * Skip over ports/slots not in service. */ if ((port < OCTEON_RGMX_MIN_PORT) || (port > OCTEON_RGMX_MAX_PORT)) { continue; } if ((NULL == sc) || (((struct rgmx_softc_dev *)-1) == sc)) { continue; } /* * Now look for anamolous conditions */ if (sc != get_rgmx_softc(port)) { printf(" port %u sc %p not in sync with index: %u\n", port, sc, index); continue; } if (sc->port != port) { printf(" port %u sc %p port-> %u not in sync with index: %u\n", port, sc, sc->port, index); continue; } ifp = sc->ifp; if (ifp == NULL) { printf(" port %u sc %p . Bad ifp %p\n", port, sc, ifp); continue; } /* * Check if packets queued at ifnet layer. Kick start output if we can. */ if (sc->ifp->if_flags & IFF_UP) { octeon_rgmx_output_start(ifp); } else { octeon_rgmx_output_flush(ifp); } /* * Check if line status changed ? Adjust ourselves. */ octeon_rgmx_config_speed(port, 1); } } #endif #ifdef DEBUG_RGMX_DUMP static void octeon_dump_pow_stats(void) { octeon_rgmx_pow_nos_cnt nos_cnt; octeon_rgmx_pow_int_pc_t intpc; octeon_rgmx_pow_int_threshold_t thr; octeon_rgmx_pow_int_cnt_t int_cnt; int core = octeon_get_core_num(); octeon_pow_inpt_q_all_qos inpt_q_all; octeon_pow_inpt_q_grp_qos inpt_q_grp; octeon_rgmx_pip_inb_pkts pkts; octeon_rgmx_pip_inb_errs errs; static u_int pkts0 = 0; static u_int pkts1 = 0; static u_int errs0 = 0; static u_int errs1 = 0; int i; nos_cnt.word64 = oct_read64(OCTEON_RGMX_POW_NOS_CNT); if (nos_cnt.bits.nos_cnt) printf(" *** No sched cnt %u\n", nos_cnt.bits.nos_cnt); printf(" \nGroup mask: 0x%llX WorkQueue Int : 0x%llX\n", oct_read64(OCTEON_POW_CORE_GROUP_MASK(OUR_CORE)), oct_read64(OCTEON_POW_WORKQUEUE_INT)); intpc.word64 = oct_read64(OCTEON_POW_WORKQUEUE_INT_PC); printf(" Intr Periodic Cntr: PC %u thr: %u\n", intpc.bits.thr_freq, intpc.bits.thr_period); thr.word64 = oct_read64(OCTEON_POW_WORKQUEUE_INT_THRESHOLD(OCTEON_POW_RX_GROUP_NUM)); printf(" Thresholds iq %u ds %u time %u enable %u\n", thr.bits.iq_thr, thr.bits.ds_thr, thr.bits.time_thr, thr.bits.enable); int_cnt.word64 = oct_read64(OCTEON_POW_INT_CNTR(core)); printf(" Int_cnt iq_cnt %u ds_cnt %u tc_cnt %u\n", int_cnt.bits.iq_cnt, int_cnt.bits.ds_cnt, int_cnt.bits.tc_cnt); pkts.word64 = oct_read64(OCTEON_PIP_STAT_INB_PKTS(16)); pkts0 += pkts.bits.inb_pkts; errs.word64 = oct_read64(OCTEON_PIP_STAT_INB_ERRS(16)); errs0 += errs.bits.inb_errs; pkts.word64 = oct_read64(OCTEON_PIP_STAT_INB_PKTS(17)); pkts1 += pkts.bits.inb_pkts; errs.word64 = oct_read64(OCTEON_PIP_STAT_INB_ERRS(17)); errs1 += errs.bits.inb_errs; printf(" PIP inbound pkts(16): %u Errors: %u inbound(17): %u Errors: %u\n", pkts0, errs0, pkts1, errs1); inpt_q_all.word64 = oct_read64(OCTEON_POW_INPT_Q_ALL_QOS); printf(" All queued pkt in qos Levels: %u -- ", inpt_q_all.bits.iq_cnt); for (i = 0 ; i < 7; i++) { inpt_q_grp.word64 = oct_read64(OCTEON_POW_INPT_QOS_GRP(i)); if (inpt_q_grp.bits.iq_cnt) printf(" Grp-%u: %u ", i, inpt_q_grp.bits.iq_cnt); } } #endif /* ------------------------------------------------------------------- * * octeon_line_status_loop() * * ------------------------------------------------------------------- */ static void octeon_line_status_loop (void *unused) { struct rgmx_softc_dev *sc; u_int idx; for (idx = 0; idx < num_devices; idx++) { sc = rgmx_scdev_array[idx]; if (sc && sc->ifp) { if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { octeon_rgmx_config_speed(sc->port, 1); octeon_rgmx_output_start(sc->ifp); } } } //#define DEBUG_RGMX_DUMP #ifdef DEBUG_RGMX_DUMP static int count = 0; if (++count > 5) { count = 0; // octeon_dump_fpa_pool(OCTEON_FPA_RX_PACKET_POOL); // octeon_dump_fpa_pool(OCTEON_FPA_WQE_RX_POOL); // octeon_dump_fpa_pool(OCTEON_FPA_TX_PACKET_POOL); octeon_dump_rgmx_stats(16); octeon_dump_pow_stats(); } #endif } /* ------------------------------------------------------------------- * * octeon_rgmx_set_mac * * ------------------------------------------------------------------- * * * octeon_rgmx_set_mac * * Program the ethernet HW address * */ static void octeon_rgmx_set_mac (u_int port) { struct rgmx_softc_dev *sc; u_int iface = INTERFACE(port); u_int index = INDEX(port); int ii; uint64_t mac = 0; u_int last_enabled; sc = get_rgmx_softc(port); if (!sc) { printf(" octeon_rgmx_set_mac Missing sc. port:%u", port); return; } for (ii = 0; ii < 6; ii++) { mac = (mac << 8) | (uint64_t)(sc->ieee[ii]); } last_enabled = octeon_rgmx_stop_port(port); oct_write64(OCTEON_RGMX_SMACX(index, iface), mac); oct_write64(OCTEON_RGMX_RXX_ADR_CAM0(index, iface), sc->ieee[0]); oct_write64(OCTEON_RGMX_RXX_ADR_CAM1(index, iface), sc->ieee[1]); oct_write64(OCTEON_RGMX_RXX_ADR_CAM2(index, iface), sc->ieee[2]); oct_write64(OCTEON_RGMX_RXX_ADR_CAM3(index, iface), sc->ieee[3]); oct_write64(OCTEON_RGMX_RXX_ADR_CAM4(index, iface), sc->ieee[4]); oct_write64(OCTEON_RGMX_RXX_ADR_CAM5(index, iface), sc->ieee[5]); oct_write64(OCTEON_RGMX_RXX_ADR_CTL(index, iface), OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST | OCTEON_RGMX_ADRCTL_ACCEPT_ALL_MULTICAST | OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC); oct_write64(OCTEON_RGMX_RXX_ADR_CAM_EN(index, iface), 1); if (last_enabled) octeon_rgmx_start_port(port); } /* ------------------------------------------------------------------- * * octeon_config_rgmii_port() * * ------------------------------------------------------------------- */ static void octeon_config_rgmii_port (u_int port) { u_int iface = INTERFACE(port); u_int index = INDEX(port); /* * Configure an RGMII port */ octeon_rgmx_prtx_cfg_t gmx_cfg; /* Enable ASX */ oct_write64(OCTEON_ASXX_RX_PRT_EN(iface), oct_read64(OCTEON_ASXX_RX_PRT_EN(iface)) | (1<sc_dev; /* * Make sure that sc/dev are the parent Root structs. Not one * of the rgmxN childs. */ if (int_handler_tag != NULL) { bus_teardown_intr(dev, irq_res, int_handler_tag); int_handler_tag = NULL; } #ifdef OCTEON_RGMX_SCHEDULED_ISRS if (tq != NULL) { taskqueue_drain(tq, &rxtx_isr_task); taskqueue_drain(taskqueue_fast, &link_isr_task); taskqueue_free(tq); tq = NULL; } #endif } #endif static device_method_t rgmii_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rgmii_probe), DEVMETHOD(device_identify, rgmii_identify), DEVMETHOD(device_attach, rgmii_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), { 0, 0 } }; static driver_t rgmii_driver = { "rgmii", rgmii_methods, sizeof(struct rgmx_softc_dev) }; static devclass_t rgmii_devclass; DRIVER_MODULE(rgmii, nexus, rgmii_driver, rgmii_devclass, 0, 0); Index: user/jmallett/octeon/sys/mips/cavium/octeon_machdep.c =================================================================== --- user/jmallett/octeon/sys/mips/cavium/octeon_machdep.c (revision 206675) +++ user/jmallett/octeon/sys/mips/cavium/octeon_machdep.c (revision 206676) @@ -1,849 +1,857 @@ /*- * Copyright (c) 2006 Wojciech A. Koszek * 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__mips_n64) #define MAX_APP_DESC_ADDR 0xffffffffafffffff #else #define MAX_APP_DESC_ADDR 0xafffffff #endif extern int *edata; extern int *end; uint64_t ciu_get_en_reg_addr_new(int corenum, int intx, int enx, int ciu_ip); void ciu_dump_interrutps_enabled(int core_num, int intx, int enx, int ciu_ip); static void octeon_boot_params_init(register_t ptr); static uint64_t ciu_get_intr_sum_reg_addr(int core_num, int intx, int enx); static uint64_t ciu_get_intr_en_reg_addr(int core_num, int intx, int enx); static __inline void mips_wr_ebase(u_int32_t a0) { __asm __volatile("mtc0 %[a0], $15, 1 ;" : : [a0] "r"(a0)); mips_barrier(); } void platform_cpu_init() { /* Nothing special yet */ } /* * Perform a board-level soft-reset. */ void platform_reset(void) { oct_write64(OCTEON_CIU_SOFT_RST, 1); } void octeon_led_write_char(int char_position, char val) { uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8); if (!octeon_board_real()) return; char_position &= 0x7; /* only 8 chars */ ptr += char_position; oct_write8_x8(ptr, val); } void octeon_led_write_char0(char val) { uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8); if (!octeon_board_real()) return; oct_write8_x8(ptr, val); } void octeon_led_write_hexchar(int char_position, char hexval) { uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8); char char1, char2; if (!octeon_board_real()) return; char1 = (hexval >> 4) & 0x0f; char1 = (char1 < 10)?char1+'0':char1+'7'; char2 = (hexval & 0x0f); char2 = (char2 < 10)?char2+'0':char2+'7'; char_position &= 0x7; /* only 8 chars */ if (char_position > 6) char_position = 6; ptr += char_position; oct_write8_x8(ptr, char1); ptr++; oct_write8_x8(ptr, char2); } void octeon_led_write_string(const char *str) { uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8); int i; if (!octeon_board_real()) return; for (i=0; i<8; i++, ptr++) { if (str && *str) oct_write8_x8(ptr, *str++); else oct_write8_x8(ptr, ' '); oct_read64(OCTEON_MIO_BOOT_BIST_STAT); } } static char progress[8] = { '-', '/', '|', '\\', '-', '/', '|', '\\'}; void octeon_led_run_wheel(int *prog_count, int led_position) { if (!octeon_board_real()) return; octeon_led_write_char(led_position, progress[*prog_count]); *prog_count += 1; *prog_count &= 0x7; } void octeon_led_write_hex(uint32_t wl) { char nbuf[80]; sprintf(nbuf, "%X", wl); octeon_led_write_string(nbuf); } /* * octeon_debug_symbol * * Does nothing. * Used to mark the point for simulator to begin tracing */ void octeon_debug_symbol(void) { } void octeon_ciu_stop_gtimer(int timer) { oct_write64(OCTEON_CIU_GENTIMER_ADDR(timer), 0ll); } void octeon_ciu_start_gtimer(int timer, u_int one_shot, uint64_t time_cycles) { octeon_ciu_gentimer gentimer; gentimer.word64 = 0; gentimer.bits.one_shot = one_shot; gentimer.bits.len = time_cycles - 1; oct_write64(OCTEON_CIU_GENTIMER_ADDR(timer), gentimer.word64); } /* * octeon_ciu_reset * * Shutdown all CIU to IP2, IP3 mappings */ void octeon_ciu_reset(void) { octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_0); octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_1); octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_2); octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_3); ciu_disable_intr(CIU_THIS_CORE, CIU_INT_0, CIU_EN_0); ciu_disable_intr(CIU_THIS_CORE, CIU_INT_0, CIU_EN_1); ciu_disable_intr(CIU_THIS_CORE, CIU_INT_1, CIU_EN_0); ciu_disable_intr(CIU_THIS_CORE, CIU_INT_1, CIU_EN_1); ciu_clear_int_summary(CIU_THIS_CORE, CIU_INT_0, CIU_EN_0, 0ll); ciu_clear_int_summary(CIU_THIS_CORE, CIU_INT_1, CIU_EN_0, 0ll); ciu_clear_int_summary(CIU_THIS_CORE, CIU_INT_1, CIU_EN_1, 0ll); } /* * mips_disable_interrupt_controllers * * Disable interrupts in the CPU controller */ void mips_disable_interrupt_controls(void) { /* * Disable interrupts in CIU. */ octeon_ciu_reset(); } /* * ciu_get_intr_sum_reg_addr */ static uint64_t ciu_get_intr_sum_reg_addr(int core_num, int intx, int enx) { uint64_t ciu_intr_sum_reg_addr; if (enx == CIU_EN_0) ciu_intr_sum_reg_addr = OCTEON_CIU_SUMMARY_BASE_ADDR + (core_num * 0x10) + (intx * 0x8); else ciu_intr_sum_reg_addr = OCTEON_CIU_SUMMARY_INT1_ADDR; return (ciu_intr_sum_reg_addr); } /* * ciu_get_intr_en_reg_addr */ static uint64_t ciu_get_intr_en_reg_addr(int core_num, int intx, int enx) { uint64_t ciu_intr_reg_addr; ciu_intr_reg_addr = OCTEON_CIU_ENABLE_BASE_ADDR + ((enx == 0) ? 0x0 : 0x8) + (intx * 0x10) + (core_num * 0x20); return (ciu_intr_reg_addr); } /* * ciu_get_intr_reg_addr * * 200 ---int0,en0 ip2 * 208 ---int0,en1 ip2 ----> this is wrong... this is watchdog * * 210 ---int0,en0 ip3 -- * 218 ---int0,en1 ip3 ----> same here.. .this is watchdog... right? * * 220 ---int1,en0 ip2 * 228 ---int1,en1 ip2 * 230 ---int1,en0 ip3 -- * 238 ---int1,en1 ip3 * */ uint64_t ciu_get_en_reg_addr_new(int corenum, int intx, int enx, int ciu_ip) { uint64_t ciu_intr_reg_addr = OCTEON_CIU_ENABLE_BASE_ADDR; /* XXX kasserts? */ if (enx < CIU_EN_0 || enx > CIU_EN_1) { printf("%s: invalid enx value %d, should be %d or %d\n", __func__, enx, CIU_EN_0, CIU_EN_1); return 0; } if (intx < CIU_INT_0 || intx > CIU_INT_1) { printf("%s: invalid intx value %d, should be %d or %d\n", __func__, enx, CIU_INT_0, CIU_INT_1); return 0; } if (ciu_ip < CIU_MIPS_IP2 || ciu_ip > CIU_MIPS_IP3) { printf("%s: invalid ciu_ip value %d, should be %d or %d\n", __func__, ciu_ip, CIU_MIPS_IP2, CIU_MIPS_IP3); return 0; } ciu_intr_reg_addr += (enx * 0x8); ciu_intr_reg_addr += (ciu_ip * 0x10); ciu_intr_reg_addr += (intx * 0x20); return (ciu_intr_reg_addr); } /* * ciu_get_int_summary */ uint64_t ciu_get_int_summary(int core_num, int intx, int enx) { uint64_t ciu_intr_sum_reg_addr; if (core_num == CIU_THIS_CORE) core_num = octeon_get_core_num(); ciu_intr_sum_reg_addr = ciu_get_intr_sum_reg_addr(core_num, intx, enx); return (oct_read64(ciu_intr_sum_reg_addr)); } //#define DEBUG_CIU 1 #ifdef DEBUG_CIU #define DEBUG_CIU_SUM 1 #define DEBUG_CIU_EN 1 #endif /* * ciu_clear_int_summary */ void ciu_clear_int_summary(int core_num, int intx, int enx, uint64_t write_bits) { uint32_t cpu_status_bits; uint64_t ciu_intr_sum_reg_addr; //#define DEBUG_CIU_SUM 1 #ifdef DEBUG_CIU_SUM uint64_t ciu_intr_sum_bits; #endif if (core_num == CIU_THIS_CORE) { core_num = octeon_get_core_num(); } #ifdef DEBUG_CIU_SUM printf(" CIU: core %u clear sum IntX %u Enx %u Bits: 0x%llX\n", core_num, intx, enx, write_bits); #endif cpu_status_bits = intr_disable(); ciu_intr_sum_reg_addr = ciu_get_intr_sum_reg_addr(core_num, intx, enx); #ifdef DEBUG_CIU_SUM ciu_intr_sum_bits = oct_read64(ciu_intr_sum_reg_addr); /* unneeded dummy read */ printf(" CIU: status: 0x%X reg_addr: 0x%llX Val: 0x%llX -> 0x%llX", cpu_status_bits, ciu_intr_sum_reg_addr, ciu_intr_sum_bits, ciu_intr_sum_bits | write_bits); #endif oct_write64(ciu_intr_sum_reg_addr, write_bits); oct_read64(OCTEON_MIO_BOOT_BIST_STAT); /* Bus Barrier */ #ifdef DEBUG_CIU_SUM printf(" Readback: 0x%llX\n\n ", (uint64_t) oct_read64(ciu_intr_sum_reg_addr)); #endif intr_restore(cpu_status_bits); } /* * ciu_disable_intr */ void ciu_disable_intr(int core_num, int intx, int enx) { uint32_t cpu_status_bits; uint64_t ciu_intr_reg_addr; if (core_num == CIU_THIS_CORE) core_num = octeon_get_core_num(); cpu_status_bits = intr_disable(); ciu_intr_reg_addr = ciu_get_intr_en_reg_addr(core_num, intx, enx); oct_read64(ciu_intr_reg_addr); /* Dummy read */ oct_write64(ciu_intr_reg_addr, 0LL); oct_read64(OCTEON_MIO_BOOT_BIST_STAT); /* Bus Barrier */ intr_restore(cpu_status_bits); } void ciu_dump_interrutps_enabled(int core_num, int intx, int enx, int ciu_ip) { uint64_t ciu_intr_reg_addr; uint64_t ciu_intr_bits; if (core_num == CIU_THIS_CORE) { core_num = octeon_get_core_num(); } #ifndef OCTEON_SMP_1 ciu_intr_reg_addr = ciu_get_intr_en_reg_addr(core_num, intx, enx); #else ciu_intr_reg_addr = ciu_get_en_reg_addr_new(core_num, intx, enx, ciu_ip); #endif if (!ciu_intr_reg_addr) { printf("Bad call to %s\n", __func__); while(1); return; } ciu_intr_bits = oct_read64(ciu_intr_reg_addr); printf(" CIU core %d int: %d en: %d ip: %d Add: %#llx enabled: %#llx SR: %x\n", core_num, intx, enx, ciu_ip, (unsigned long long)ciu_intr_reg_addr, (unsigned long long)ciu_intr_bits, mips_rd_status()); } /* * ciu_enable_interrupts */ void ciu_enable_interrupts(int core_num, int intx, int enx, uint64_t set_these_interrupt_bits, int ciu_ip) { uint32_t cpu_status_bits; uint64_t ciu_intr_reg_addr; uint64_t ciu_intr_bits; if (core_num == CIU_THIS_CORE) core_num = octeon_get_core_num(); //#define DEBUG_CIU_EN 1 #ifdef DEBUG_CIU_EN printf(" CIU: core %u enabling Intx %u Enx %u IP %d Bits: 0x%llX\n", core_num, intx, enx, ciu_ip, set_these_interrupt_bits); #endif cpu_status_bits = intr_disable(); #ifndef OCTEON_SMP_1 ciu_intr_reg_addr = ciu_get_intr_en_reg_addr(core_num, intx, enx); #else ciu_intr_reg_addr = ciu_get_en_reg_addr_new(core_num, intx, enx, ciu_ip); #endif if (!ciu_intr_reg_addr) { printf("Bad call to %s\n", __func__); while(1); return; /* XXX */ } ciu_intr_bits = oct_read64(ciu_intr_reg_addr); #ifdef DEBUG_CIU_EN printf(" CIU: status: 0x%X reg_addr: 0x%llX Val: 0x%llX -> 0x%llX", cpu_status_bits, ciu_intr_reg_addr, ciu_intr_bits, ciu_intr_bits | set_these_interrupt_bits); #endif ciu_intr_bits |= set_these_interrupt_bits; oct_write64(ciu_intr_reg_addr, ciu_intr_bits); -#ifdef OCTEON_SMP +#ifdef SMP mips_wbflush(); #endif oct_read64(OCTEON_MIO_BOOT_BIST_STAT); /* Bus Barrier */ #ifdef DEBUG_CIU_EN printf(" Readback: 0x%llX\n\n ", (uint64_t)oct_read64(ciu_intr_reg_addr)); #endif intr_restore(cpu_status_bits); } unsigned long octeon_get_clock_rate(void) { return octeon_cpu_clock; } static void octeon_memory_init(void) { uint32_t realmem_bytes; if (octeon_board_real()) { realmem_bytes = (octeon_dram - PAGE_SIZE); realmem_bytes &= ~(PAGE_SIZE - 1); } else { /* Simulator we limit to 96 meg */ realmem_bytes = (96 << 20); } /* phys_avail regions are in bytes */ phys_avail[0] = (MIPS_KSEG0_TO_PHYS((vm_offset_t)&end) + PAGE_SIZE) & ~(PAGE_SIZE - 1); if (octeon_board_real()) { if (realmem_bytes > OCTEON_DRAM_FIRST_256_END) phys_avail[1] = OCTEON_DRAM_FIRST_256_END; else phys_avail[1] = realmem_bytes; realmem_bytes -= OCTEON_DRAM_FIRST_256_END; realmem_bytes &= ~(PAGE_SIZE - 1); } else { /* Simulator gets 96Meg period. */ phys_avail[1] = (96 << 20); } /*- * Octeon Memory looks as follows: * PA * 0000 0000 to 0x0 0000 0000 0000 * 0FFF FFFF First 256 MB memory Maps to 0x0 0000 0FFF FFFF * * 1000 0000 to 0x1 0000 1000 0000 * 1FFF FFFF Uncached Bu I/O space.converted to 0x1 0000 1FFF FFFF * * 2FFF FFFF to Cached 0x0 0000 2000 0000 * FFFF FFFF all dram mem above the first 512M 0x3 FFFF FFFF FFFF * */ physmem = btoc(phys_avail[1] - phys_avail[0]); if ((octeon_board_real()) && (realmem_bytes > OCTEON_DRAM_FIRST_256_END)) { /* take out the upper non-cached 1/2 */ realmem_bytes -= OCTEON_DRAM_FIRST_256_END; realmem_bytes &= ~(PAGE_SIZE - 1); /* Now map the rest of the memory */ phys_avail[2] = 0x20000000; phys_avail[3] = ((uint32_t) 0x20000000 + realmem_bytes); physmem += btoc(phys_avail[3] - phys_avail[2]); } realmem = physmem; printf("Total DRAM Size %#X\n", (uint32_t) octeon_dram); printf("Bank 0 = %#08lX -> %#08lX\n", (long)phys_avail[0], (long)phys_avail[1]); printf("Bank 1 = %#08lX -> %#08lX\n", (long)phys_avail[2], (long)phys_avail[3]); } void platform_start(__register_t a0, __register_t a1, __register_t a2 __unused, __register_t a3) { uint64_t platform_counter_freq; /* Initialize pcpu stuff */ mips_pcpu0_init(); mips_timer_early_init(OCTEON_CLOCK_DEFAULT); cninit(); octeon_ciu_reset(); octeon_boot_params_init(a3); bootverbose = 1; /* * For some reason on the cn38xx simulator ebase register is set to * 0x80001000 at bootup time. Move it back to the default, but * when we move to having support for multiple executives, we need * to rethink this. */ mips_wr_ebase(0x80000000); octeon_memory_init(); init_param1(); init_param2(physmem); mips_cpu_init(); pmap_bootstrap(); mips_proc0_init(); mutex_init(); kdb_init(); #ifdef KDB if (boothowto & RB_KDB) kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); #endif platform_counter_freq = octeon_get_clock_rate(); mips_timer_init_params(platform_counter_freq, 0); + +#ifdef SMP + /* + * Clear any pending IPIs and enable the IPI interrupt. + */ + oct_write64(OCTEON_CIU_MBOX_CLRX(0), 0xffffffff); + ciu_enable_interrupts(0, CIU_INT_1, CIU_EN_0, OCTEON_CIU_ENABLE_MBOX_INTR, CIU_MIPS_IP3); +#endif } /* impSTART: This stuff should move back into the Cavium SDK */ /* **************************************************************************************** * * APP/BOOT DESCRIPTOR STUFF * **************************************************************************************** */ /* Define the struct that is initialized by the bootloader used by the * startup code. * * Copyright (c) 2004, 2005, 2006 Cavium Networks. * * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this * notice is included verbatim in any distributions. No written agreement, * license, or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their authors * and need not follow the licensing terms described here, provided that * the new terms are clearly indicated on the first page of each file where * they apply. */ #define OCTEON_CURRENT_DESC_VERSION 6 #define OCTEON_ARGV_MAX_ARGS (64) #define OCTOEN_SERIAL_LEN 20 typedef struct { /* Start of block referenced by assembly code - do not change! */ uint32_t desc_version; uint32_t desc_size; uint64_t stack_top; uint64_t heap_base; uint64_t heap_end; uint64_t entry_point; /* Only used by bootloader */ uint64_t desc_vaddr; /* End of This block referenced by assembly code - do not change! */ uint32_t exception_base_addr; uint32_t stack_size; uint32_t heap_size; uint32_t argc; /* Argc count for application */ uint32_t argv[OCTEON_ARGV_MAX_ARGS]; uint32_t flags; uint32_t core_mask; uint32_t dram_size; /**< DRAM size in megabyes */ uint32_t phy_mem_desc_addr; /**< physical address of free memory descriptor block*/ uint32_t debugger_flags_base_addr; /**< used to pass flags from app to debugger */ uint32_t eclock_hz; /**< CPU clock speed, in hz */ uint32_t dclock_hz; /**< DRAM clock speed, in hz */ uint32_t spi_clock_hz; /**< SPI4 clock in hz */ uint16_t board_type; uint8_t board_rev_major; uint8_t board_rev_minor; uint16_t chip_type; uint8_t chip_rev_major; uint8_t chip_rev_minor; char board_serial_number[OCTOEN_SERIAL_LEN]; uint8_t mac_addr_base[6]; uint8_t mac_addr_count; uint64_t cvmx_desc_vaddr; } octeon_boot_descriptor_t; typedef struct { uint32_t major_version; uint32_t minor_version; uint64_t stack_top; uint64_t heap_base; uint64_t heap_end; uint64_t desc_vaddr; uint32_t exception_base_addr; uint32_t stack_size; uint32_t flags; uint32_t core_mask; uint32_t dram_size; /**< DRAM size in megabyes */ uint32_t phy_mem_desc_addr; /**< physical address of free memory descriptor block*/ uint32_t debugger_flags_base_addr; /**< used to pass flags from app to debugger */ uint32_t eclock_hz; /**< CPU clock speed, in hz */ uint32_t dclock_hz; /**< DRAM clock speed, in hz */ uint32_t spi_clock_hz; /**< SPI4 clock in hz */ uint16_t board_type; uint8_t board_rev_major; uint8_t board_rev_minor; uint16_t chip_type; uint8_t chip_rev_major; uint8_t chip_rev_minor; char board_serial_number[OCTOEN_SERIAL_LEN]; uint8_t mac_addr_base[6]; uint8_t mac_addr_count; } cvmx_bootinfo_t; uint32_t octeon_cpu_clock; uint64_t octeon_dram; uint32_t octeon_bd_ver = 0, octeon_cvmx_bd_ver = 0, octeon_board_rev_major, octeon_board_rev_minor, octeon_board_type; uint8_t octeon_mac_addr[6] = { 0 }; int octeon_core_mask, octeon_mac_addr_count; int octeon_chip_rev_major = 0, octeon_chip_rev_minor = 0, octeon_chip_type = 0; static octeon_boot_descriptor_t *app_desc_ptr; static cvmx_bootinfo_t *cvmx_desc_ptr; #define OCTEON_BOARD_TYPE_NONE 0 #define OCTEON_BOARD_TYPE_SIM 1 #define OCTEON_BOARD_TYPE_CN3010_EVB_HS5 11 #define OCTEON_CLOCK_MIN (100 * 1000 * 1000) #define OCTEON_CLOCK_MAX (800 * 1000 * 1000) #define OCTEON_DRAM_DEFAULT (256 * 1024 * 1024) #define OCTEON_DRAM_MIN 30 #define OCTEON_DRAM_MAX 3000 int octeon_board_real(void) { switch (octeon_board_type) { case OCTEON_BOARD_TYPE_NONE: case OCTEON_BOARD_TYPE_SIM: return 0; case OCTEON_BOARD_TYPE_CN3010_EVB_HS5: /* * XXX * The CAM-0100 identifies itself as type 11, revision 0.0, * despite its being rather real. Disable the revision check * for type 11. */ return 1; default: if (octeon_board_rev_major == 0) return 0; return 1; } } static void octeon_process_app_desc_ver_unknown(void) { printf(" Unknown Boot-Descriptor: Using Defaults\n"); octeon_cpu_clock = OCTEON_CLOCK_DEFAULT; octeon_dram = OCTEON_DRAM_DEFAULT; octeon_board_rev_major = octeon_board_rev_minor = octeon_board_type = 0; octeon_core_mask = 1; octeon_chip_type = octeon_chip_rev_major = octeon_chip_rev_minor = 0; octeon_mac_addr[0] = 0x00; octeon_mac_addr[1] = 0x0f; octeon_mac_addr[2] = 0xb7; octeon_mac_addr[3] = 0x10; octeon_mac_addr[4] = 0x09; octeon_mac_addr[5] = 0x06; octeon_mac_addr_count = 1; } static int octeon_process_app_desc_ver_6(void) { /* XXX Why is 0x00000000ffffffffULL a bad value? */ if (app_desc_ptr->cvmx_desc_vaddr == 0 || app_desc_ptr->cvmx_desc_vaddr == 0xfffffffful) { printf ("Bad cvmx_desc_ptr %p\n", cvmx_desc_ptr); return 1; } cvmx_desc_ptr = (cvmx_bootinfo_t *)(intptr_t)app_desc_ptr->cvmx_desc_vaddr; cvmx_desc_ptr = (cvmx_bootinfo_t *) ((intptr_t)cvmx_desc_ptr | MIPS_KSEG0_START); octeon_cvmx_bd_ver = (cvmx_desc_ptr->major_version * 100) + cvmx_desc_ptr->minor_version; if (cvmx_desc_ptr->major_version != 1) { panic("Incompatible CVMX descriptor from bootloader: %d.%d %p\n", (int) cvmx_desc_ptr->major_version, (int) cvmx_desc_ptr->minor_version, cvmx_desc_ptr); } octeon_core_mask = cvmx_desc_ptr->core_mask; octeon_cpu_clock = cvmx_desc_ptr->eclock_hz; octeon_board_type = cvmx_desc_ptr->board_type; octeon_board_rev_major = cvmx_desc_ptr->board_rev_major; octeon_board_rev_minor = cvmx_desc_ptr->board_rev_minor; octeon_chip_type = cvmx_desc_ptr->chip_type; octeon_chip_rev_major = cvmx_desc_ptr->chip_rev_major; octeon_chip_rev_minor = cvmx_desc_ptr->chip_rev_minor; octeon_mac_addr[0] = cvmx_desc_ptr->mac_addr_base[0]; octeon_mac_addr[1] = cvmx_desc_ptr->mac_addr_base[1]; octeon_mac_addr[2] = cvmx_desc_ptr->mac_addr_base[2]; octeon_mac_addr[3] = cvmx_desc_ptr->mac_addr_base[3]; octeon_mac_addr[4] = cvmx_desc_ptr->mac_addr_base[4]; octeon_mac_addr[5] = cvmx_desc_ptr->mac_addr_base[5]; octeon_mac_addr_count = cvmx_desc_ptr->mac_addr_count; if (app_desc_ptr->dram_size > 16*1024*1024) octeon_dram = (uint64_t)app_desc_ptr->dram_size; else octeon_dram = (uint64_t)app_desc_ptr->dram_size << 20; return 0; } static void octeon_boot_params_init(register_t ptr) { int bad_desc = 1; if (ptr != 0 && ptr < MAX_APP_DESC_ADDR) { app_desc_ptr = (octeon_boot_descriptor_t *)(intptr_t)ptr; octeon_bd_ver = app_desc_ptr->desc_version; if (app_desc_ptr->desc_version < 6) panic("Your boot code is too old to be supported.\n"); if (app_desc_ptr->desc_version >= 6) bad_desc = octeon_process_app_desc_ver_6(); } if (bad_desc) octeon_process_app_desc_ver_unknown(); printf("Boot Descriptor Ver: %u -> %u/%u", octeon_bd_ver, octeon_cvmx_bd_ver/100, octeon_cvmx_bd_ver%100); printf(" CPU clock: %uMHz Core Mask: %#x\n", octeon_cpu_clock/1000000, octeon_core_mask); printf(" Dram: %u MB", (uint32_t)(octeon_dram >> 20)); printf(" Board Type: %u Revision: %u/%u\n", octeon_board_type, octeon_board_rev_major, octeon_board_rev_minor); printf(" Octeon Chip: %u Rev %u/%u", octeon_chip_type, octeon_chip_rev_major, octeon_chip_rev_minor); printf(" Mac Address %02X.%02X.%02X.%02X.%02X.%02X (%d)\n", octeon_mac_addr[0], octeon_mac_addr[1], octeon_mac_addr[2], octeon_mac_addr[3], octeon_mac_addr[4], octeon_mac_addr[5], octeon_mac_addr_count); } /* impEND: This stuff should move back into the Cavium SDK */ Index: user/jmallett/octeon/sys/mips/cavium/octeon_mp.c =================================================================== --- user/jmallett/octeon/sys/mips/cavium/octeon_mp.c (revision 206675) +++ user/jmallett/octeon/sys/mips/cavium/octeon_mp.c (revision 206676) @@ -1,89 +1,102 @@ /*- * Copyright (c) 2004-2010 Juli Mallett * 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include unsigned octeon_ap_boot = ~0; void platform_ipi_send(int cpuid) { - panic("%s: not yet implemented.", __func__); + oct_write64(OCTEON_CIU_MBOX_SETX(cpuid), 1); + mips_wbflush(); } void platform_ipi_clear(void) { - panic("%s: not yet implemented.", __func__); + uint64_t action; + + action = oct_read64(OCTEON_CIU_MBOX_CLRX(PCPU_GET(cpuid))); + KASSERT(action == 1, ("unexpected IPIs: %#jx", (uintmax_t)action)); + oct_write64(OCTEON_CIU_MBOX_CLRX(PCPU_GET(cpuid)), action); } int platform_ipi_intrnum(void) { return (1); } void platform_init_ap(int cpuid) { /* * Set the exception base. - * - * XXX Low bits seem to be used for cpuid? */ - mips_wr_prid1(0x80000000); + mips_wr_prid1(0x80000000 | cpuid); + + /* + * Set up interrupts, clear IPIs and unmask the IPI interrupt. + */ + octeon_ciu_reset(); + + oct_write64(OCTEON_CIU_MBOX_CLRX(cpuid), 0xffffffff); + ciu_enable_interrupts(cpuid, CIU_INT_1, CIU_EN_0, OCTEON_CIU_ENABLE_MBOX_INTR, CIU_MIPS_IP3); + + mips_wbflush(); } int platform_num_processors(void) { return (fls(octeon_core_mask)); } int platform_start_ap(int cpuid) { if (atomic_cmpset_32(&octeon_ap_boot, ~0, cpuid) == 0) return (-1); for (;;) { DELAY(1000); if (atomic_cmpset_32(&octeon_ap_boot, 0, ~0) != 0) return (0); printf("Waiting for cpu%d to start\n", cpuid); } } Index: user/jmallett/octeon/sys/mips/cavium/octeon_pcmap_regs.h =================================================================== --- user/jmallett/octeon/sys/mips/cavium/octeon_pcmap_regs.h (revision 206675) +++ user/jmallett/octeon/sys/mips/cavium/octeon_pcmap_regs.h (revision 206676) @@ -1,914 +1,906 @@ /***********************license start*************** * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Cavium Networks nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. * * * For any questions regarding licensing please contact marketing@caviumnetworks.com * ***********************license end**************************************/ /* * This product includes software developed by the University of * California, Berkeley and its contributors." */ /* $FreeBSD$ */ #ifndef __OCTEON_PCMAP_REGS_H__ #define __OCTEON_PCMAP_REGS_H__ #include "opt_cputype.h" #define OCTEON_CACHE_LINE_SIZE 0x80 /* 128 bytes cache line size */ #define IS_OCTEON_ALIGNED(p) (!((u_long)(p) & 0x7f)) #define OCTEON_ALIGN(p) (((u_long)(p) + ((OCTEON_CACHE_LINE_SIZE) - 1)) & ~((OCTEON_CACHE_LINE_SIZE) - 1)) #ifndef LOCORE -/* XXXimp: From Cavium's include/pcpu.h, need to port that over */ -#ifndef OCTEON_SMP -#define OCTEON_CORE_ID 0 -#else -extern struct pcpu *cpuid_to_pcpu[]; -#define OCTEON_CORE_ID (mips_rd_coreid()) -#endif - /* * Utility inlines & macros */ /* turn the variable name into a string */ #define OCTEON_TMP_STR(x) OCTEON_TMP_STR2(x) #define OCTEON_TMP_STR2(x) #x #define OCTEON_PREFETCH_PREF0(address, offset) \ __asm __volatile ( ".set mips64\n" \ ".set noreorder\n" \ "pref 0, " OCTEON_TMP_STR(offset) "(%0)\n" \ ".set reorder\n" \ ".set mips0\n" \ : \ : "r" (address) ); #define OCTEON_PREFETCH(address, offset) OCTEON_PREFETCH_PREF0(address,offset) #define OCTEON_PREFETCH0(address) OCTEON_PREFETCH(address, 0) #define OCTEON_PREFETCH128(address) OCTEON_PREFETCH(address, 128) #define OCTEON_SYNCIOBDMA __asm __volatile (".word 0x8f" : : :"memory") #define OCTEON_SYNCW __asm __volatile (".word 0x10f" : : ) #define OCTEON_SYNCW __asm __volatile (".word 0x10f" : : ) #define OCTEON_SYNCWS __asm __volatile (".word 0x14f" : : ) #if defined(__mips_n64) #define oct_write64(a, v) (*(volatile uint64_t *)(a) = (uint64_t)(v)) #define oct_write8_x8(a, v) (*(volatile uint8_t *)(a) = (uint8_t)(v)) #define OCT_READ(n, t) \ static inline t oct_read ## n(uintptr_t a) \ { \ volatile t *p = (volatile t *)a; \ return (*p); \ } OCT_READ(8, uint8_t); OCT_READ(16, uint16_t); OCT_READ(32, uint32_t); OCT_READ(64, uint64_t); #elif defined(__mips_n32) || defined(__mips_o32) #if defined(__mips_n32) static inline void oct_write64 (uint64_t csr_addr, uint64_t val64) { __asm __volatile ( ".set push\n" ".set mips64\n" "sd %0, 0(%1)\n" ".set pop\n" : : "r"(val64), "r"(csr_addr)); } static inline void oct_write8_x8 (uint64_t csr_addr, uint8_t val8) { __asm __volatile ( ".set push\n" ".set mips64\n" "sb %0, 0(%1)\n" ".set pop\n" : : "r"(val8), "r"(csr_addr)); } #define OCT_READ(n, t, insn) \ static inline t oct_read ## n(uint64_t a) \ { \ uint64_t tmp; \ \ __asm __volatile ( \ ".set push\n" \ ".set mips64\n" \ insn "\t%0, 0(%1)\n" \ ".set pop\n" \ : "=r"(tmp) \ : "r"(a)); \ return ((t)tmp); \ } OCT_READ(8, uint8_t, "lb"); OCT_READ(16, uint16_t, "lh"); OCT_READ(32, uint32_t, "lw"); OCT_READ(64, uint64_t, "ld"); #else /* * XXX * Add o32 variants that load the address into a register and the result out * of a register properly, and simply disable interrupts before and after and * hope that we don't need to refill or modify the TLB to access the address. * I'd be a lot happier if csr_addr were a physical address and we mapped it * into XKPHYS here so that we could guarantee that interrupts were the only * kind of exception we needed to worry about. * * Also, some of this inline assembly is needlessly verbose. Oh, well. */ static inline void oct_write64 (uint64_t csr_addr, uint64_t val64) { uint32_t csr_addrh = csr_addr >> 32; uint32_t csr_addrl = csr_addr; uint32_t valh = val64 >> 32; uint32_t vall = val64; uint32_t tmp1; uint32_t tmp2; uint32_t tmp3; register_t sr; sr = intr_disable(); __asm __volatile ( ".set push\n" ".set mips64\n" ".set noreorder\n" ".set noat\n" "dsll %0, %3, 32\n" "dsll %1, %5, 32\n" "dsll %2, %4, 32\n" "dsrl %2, %2, 32\n" "or %0, %0, %2\n" "dsll %2, %6, 32\n" "dsrl %2, %2, 32\n" "or %1, %1, %2\n" "sd %0, 0(%1)\n" ".set pop\n" : "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3) : "r" (valh), "r" (vall), "r" (csr_addrh), "r" (csr_addrl)); intr_restore(sr); } static inline void oct_write8_x8 (uint64_t csr_addr, uint8_t val8) { uint32_t csr_addrh = csr_addr >> 32; uint32_t csr_addrl = csr_addr; uint32_t tmp1; uint32_t tmp2; register_t sr; sr = intr_disable(); __asm __volatile ( ".set push\n" ".set mips64\n" ".set noreorder\n" ".set noat\n" "dsll %0, %3, 32\n" "dsll %1, %4, 32\n" "dsrl %1, %1, 32\n" "or %0, %0, %1\n" "sb %2, 0(%0)\n" ".set pop\n" : "=&r" (tmp1), "=&r" (tmp2) : "r" (val8), "r" (csr_addrh), "r" (csr_addrl)); intr_restore(sr); } #define OCT_READ(n, t, insn) \ static inline t oct_read ## n(uint64_t csr_addr) \ { \ uint32_t csr_addrh = csr_addr >> 32; \ uint32_t csr_addrl = csr_addr; \ uint32_t tmp1, tmp2; \ register_t sr; \ \ sr = intr_disable(); \ \ __asm __volatile ( \ ".set push\n" \ ".set mips64\n" \ ".set noreorder\n" \ ".set noat\n" \ "dsll %1, %2, 32\n" \ "dsll %0, %3, 32\n" \ "dsrl %0, %0, 32\n" \ "or %1, %1, %0\n" \ "lb %1, 0(%1)\n" \ ".set pop\n" \ : "=&r" (tmp1), "=&r" (tmp2) \ : "r" (csr_addrh), "r" (csr_addrl)); \ \ intr_restore(sr); \ \ return ((t)tmp2); \ } OCT_READ(8, uint8_t, "lb"); OCT_READ(16, uint16_t, "lh"); OCT_READ(32, uint32_t, "lw"); static inline uint64_t oct_read64 (uint64_t csr_addr) { uint32_t csr_addrh = csr_addr >> 32; uint32_t csr_addrl = csr_addr; uint32_t valh; uint32_t vall; register_t sr; sr = intr_disable(); __asm __volatile ( ".set push\n" ".set mips64\n" ".set noreorder\n" ".set noat\n" "dsll %0, %2, 32\n" "dsll %1, %3, 32\n" "dsrl %1, %1, 32\n" "or %0, %0, %1\n" "ld %1, 0(%0)\n" "dsrl %0, %1, 32\n" "dsll %1, %1, 32\n" "dsrl %1, %1, 32\n" ".set pop\n" : "=&r" (valh), "=&r" (vall) : "r" (csr_addrh), "r" (csr_addrl)); intr_restore(sr); return ((uint64_t)valh << 32) | vall; } #endif #endif #define oct_write64_int64(a, v) (oct_write64(a, (int64_t)(v))) /* * Most write bus transactions are actually 64-bit on Octeon. */ static inline void oct_write8 (uint64_t csr_addr, uint8_t val8) { oct_write64(csr_addr, (uint64_t) val8); } static inline void oct_write16 (uint64_t csr_addr, uint16_t val16) { oct_write64(csr_addr, (uint64_t) val16); } static inline void oct_write32 (uint64_t csr_addr, uint32_t val32) { oct_write64(csr_addr, (uint64_t) val32); } #define oct_readint32(a) ((int32_t)oct_read32((a))) #define OCTEON_HW_BASE ((volatile uint64_t *) 0L) #define OCTEON_REG_OFFSET (-4 * 1024ll) /* local scratchpad reg base */ #define OCTEON_SCRATCH_BASE ((volatile uint8_t *)(OCTEON_HW_BASE + \ OCTEON_REG_OFFSET)) #define OCTEON_SCR_SCRATCH 8 #define OCTEON_SCRATCH_0 16 #define OCTEON_SCRATCH_1 24 #define OCTEON_SCRATCH_2 32 #define OCTEON_CHORD_HEX(dest_ptr) \ ({ __asm __volatile( \ ".set push\n" \ ".set mips3\n" \ ".set noreorder\n" \ ".set noat\n" \ ".word 0x7c02f03b \n"\ "nop\n" \ "nop\n" \ "nop\n" \ "nop\n" \ "sd $2,0(%0)\n" \ ".set pop" \ : /* no outputs */ : "r" (dest_ptr) : "$2"); \ }) static inline uint64_t oct_scratch_read64 (uint64_t address) { return(*((volatile uint64_t *)(OCTEON_SCRATCH_BASE + address))); } static inline void oct_scratch_write64 (uint64_t address, uint64_t value) { *((volatile uint64_t *)(OCTEON_SCRATCH_BASE + address)) = value; } /* * Octeon Address Space Definitions */ typedef enum { OCTEON_MIPS_SPACE_XKSEG = 3LL, OCTEON_MIPS_SPACE_XKPHYS = 2LL, OCTEON_MIPS_SPACE_XSSEG = 1LL, OCTEON_MIPS_SPACE_XUSEG = 0LL } octeon_mips_space_t; typedef enum { OCTEON_MIPS_XKSEG_SPACE_KSEG0 = 0LL, OCTEON_MIPS_XKSEG_SPACE_KSEG1 = 1LL, OCTEON_MIPS_XKSEG_SPACE_SSEG = 2LL, OCTEON_MIPS_XKSEG_SPACE_KSEG3 = 3LL } octeon_mips_xkseg_space_t; /* *********************************************************************** * 32 bit mode alert * The kseg0 calc below might fail in xkphys. */ /* * We limit the allocated device physical blocks to low mem. So use Kseg0 */ /* * Need to go back to kernel to find v->p mappings & vice-versa * We are getting non 1-1 mappings. * #define OCTEON_PTR2PHYS(addr) ((unsigned long) addr & 0x7fffffff) */ #define OCTEON_PTR2PHYS(addr) octeon_ptr_to_phys(addr) /* PTR_SIZE == sizeof(uint32_t) */ #if defined(__mips_n32) || defined(__mips_o32) #define mipsx_addr_size uint32_t // u_int64 #define MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT 30 // 62 #define MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED 0x1fffffff // 0x1fffffff #else #define mipsx_addr_size uint64_t #define MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT 62 #define MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED 0x1fffffffffffffff #endif #define octeon_ptr_to_phys(ptr) \ (((((mipsx_addr_size) ptr) >> MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT) == 2) ? \ ((mipsx_addr_size) ptr & MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED) : \ (vtophys(ptr))) #ifdef CODE_FOR_64_BIT_NEEDED static inline mipsx_addr_size octeon_ptr_to_phys (void *ptr) { if ((((mipsx_addr_size) ptr) >> MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT) == 2) { /* * KSEG0 based address ? */ return ((mipsx_addr_size) ptr & MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED); } else { /* * Ask kernel/vm to give us the phys translation. */ return (vtophys(ptr)); } } #endif #define OCTEON_IO_SEG OCTEON_MIPS_SPACE_XKPHYS #define OCTEON_ADD_SEG(segment, add) ((((uint64_t)segment) << 62) | (add)) #define OCTEON_ADD_IO_SEG(add) OCTEON_ADD_SEG(OCTEON_IO_SEG, (add)) #define OCTEON_ADDR_DID(did) (OCTEON_ADDR_DIDSPACE(did) << 40) #define OCTEON_ADDR_DIDSPACE(did) (((OCTEON_IO_SEG) << 22) | ((1ULL) << 8) | (did)) #define OCTEON_ADDR_FULL_DID(did,subdid) (((did) << 3) | (subdid)) #define OCTEON_CIU_PP_RST OCTEON_ADD_IO_SEG(0x0001070000000700ull) #define OCTEON_CIU_SOFT_RST OCTEON_ADD_IO_SEG(0x0001070000000740ull) #define OCTEON_OCTEON_DID_TAG 12ULL /* * octeon_addr_t */ typedef union { uint64_t word64; struct { octeon_mips_space_t R : 2; uint64_t offset :62; } sva; // mapped or unmapped virtual address struct { uint64_t zeroes :33; uint64_t offset :31; } suseg; // mapped USEG virtual addresses (typically) struct { uint64_t ones :33; octeon_mips_xkseg_space_t sp : 2; uint64_t offset :29; } sxkseg; // mapped or unmapped virtual address struct { octeon_mips_space_t R :2; // CVMX_MIPS_SPACE_XKPHYS in this case uint64_t cca : 3; // ignored by octeon uint64_t mbz :10; uint64_t pa :49; // physical address } sxkphys; // physical address accessed through xkphys unmapped virtual address struct { uint64_t mbz :15; uint64_t is_io : 1; // if set, the address is uncached and resides on MCB bus uint64_t did : 8; // the hardware ignores this field when is_io==0, else device ID uint64_t unaddr: 4; // the hardware ignores <39:36> in Octeon I uint64_t offset :36; } sphys; // physical address struct { uint64_t zeroes :24; // techically, <47:40> are dont-cares uint64_t unaddr: 4; // the hardware ignores <39:36> in Octeon I uint64_t offset :36; } smem; // physical mem address struct { uint64_t mem_region :2; uint64_t mbz :13; uint64_t is_io : 1; // 1 in this case uint64_t did : 8; // the hardware ignores this field when is_io==0, else device ID uint64_t unaddr: 4; // the hardware ignores <39:36> in Octeon I uint64_t offset :36; } sio; // physical IO address struct { uint64_t didspace : 24; uint64_t unused : 40; } sfilldidspace; } octeon_addr_t; typedef union { uint64_t word64; struct { uint32_t word32hi; uint32_t word32lo; } bits; } octeon_word_t; /* * octeon_build_io_address * * Builds a memory address for I/O based on the Major 5bits and Sub DID 3bits */ static inline uint64_t octeon_build_io_address (uint64_t major_did, uint64_t sub_did) { return ((0x1ull << 48) | (major_did << 43) | (sub_did << 40)); } /* * octeon_build_mask * * Builds a bit mask given the required size in bits. * * @param bits Number of bits in the mask * @return The mask */ static inline uint64_t octeon_build_mask (uint64_t bits) { return ~((~0x0ull) << bits); } /* * octeon_build_bits * * Perform mask and shift to place the supplied value into * the supplied bit rage. * * Example: octeon_build_bits(39,24,value) *
  * 6       5       4       3       3       2       1
  * 3       5       7       9       1       3       5       7      0
  * +-------+-------+-------+-------+-------+-------+-------+------+
  * 000000000000000000000000___________value000000000000000000000000
  * 
* * @param high_bit Highest bit value can occupy (inclusive) 0-63 * @param low_bit Lowest bit value can occupy inclusive 0-high_bit * @param value Value to use * @return Value masked and shifted */ static inline uint64_t octeon_build_bits (uint64_t high_bit, uint64_t low_bit, uint64_t value) { return ((value & octeon_build_mask(high_bit - low_bit + 1)) << low_bit); } /********************** simple spinlocks ***************/ typedef struct { volatile uint32_t value; } octeon_spinlock_t; // note - macros not expanded in inline ASM, so values hardcoded #define OCTEON_SPINLOCK_UNLOCKED_VAL 0 #define OCTEON_SPINLOCK_LOCKED_VAL 1 /** * Initialize a spinlock * * @param lock Lock to initialize */ static inline void octeon_spinlock_init(octeon_spinlock_t *lock) { lock->value = OCTEON_SPINLOCK_UNLOCKED_VAL; } /** * Releases lock * * @param lock pointer to lock structure */ static inline void octeon_spinlock_unlock(octeon_spinlock_t *lock) { OCTEON_SYNCWS; lock->value = 0; OCTEON_SYNCWS; } /** * Gets lock, spins until lock is taken * * @param lock pointer to lock structure */ static inline void octeon_spinlock_lock(octeon_spinlock_t *lock) { unsigned int tmp; __asm __volatile( ".set noreorder \n" "1: ll %1, %0 \n" " bnez %1, 1b \n" " li %1, 1 \n" " sc %1, %0 \n" " beqz %1, 1b \n" " nop \n" ".set reorder \n" : "+m" (lock->value), "=&r" (tmp ) : : "memory"); } /********************** end simple spinlocks ***************/ /* ------------------------------------------------------------------- * * octeon_get_chipid() * * ------------------------------------------------------------------- */ #define OCTEON_CN31XX_CHIP 0x000d0100 #define OCTEON_CN30XX_CHIP 0x000d0200 #define OCTEON_CN3020_CHIP 0x000d0112 #define OCTEON_CN5020_CHIP 0x000d0601 static inline uint32_t octeon_get_chipid(void) { uint32_t id; __asm __volatile ("mfc0 %0, $15,0" : "=r" (id)); return (id); } static inline uint32_t octeon_get_except_base_reg (void) { uint32_t tmp; __asm volatile ( " .set mips64r2 \n" " .set noreorder \n" " mfc0 %0, $15, 1 \n" " .set reorder \n" : "=&r" (tmp) : ); return(tmp); } static inline unsigned int get_coremask (void) { return(~(oct_read64(OCTEON_CIU_PP_RST)) & 0xffff); } static inline uint32_t octeon_get_core_num (void) { return (0x3FF & octeon_get_except_base_reg()); } static inline uint64_t octeon_get_cycle(void) { /* ABI == 32 */ uint32_t tmp_low, tmp_hi; __asm __volatile ( " .set push \n" " .set mips64r2 \n" " .set noreorder \n" " rdhwr %[tmpl], $31 \n" " dadd %[tmph], %[tmpl], $0 \n" " dsrl %[tmph], 32 \n" " dsll %[tmpl], 32 \n" " dsrl %[tmpl], 32 \n" " .set pop \n" : [tmpl] "=&r" (tmp_low), [tmph] "=&r" (tmp_hi) : ); return(((uint64_t)tmp_hi << 32) + tmp_low); } /** * Wait for the specified number of cycle * * @param cycles */ static inline void octeon_wait (uint64_t cycles) { uint64_t done = octeon_get_cycle() + cycles; while (octeon_get_cycle() < done) { /* Spin */ } } /* * octeon_machdep.c * * Direct to Board Support level. */ extern void octeon_led_write_char(int char_position, char val); extern void octeon_led_write_hexchar(int char_position, char hexval); extern void octeon_led_write_hex(uint32_t wl); extern void octeon_led_write_string(const char *str); extern void octeon_reset(void); extern void octeon_led_write_char0(char val); extern void octeon_led_run_wheel(int *pos, int led_position); extern void octeon_debug_symbol(void); extern void mips_disable_interrupt_controls(void); extern uint32_t octeon_cpu_clock; extern uint64_t octeon_dram; extern uint32_t octeon_bd_ver, octeon_board_rev_major, octeon_board_rev_minor, octeon_board_type; extern uint8_t octeon_mac_addr[6]; extern int octeon_core_mask, octeon_mac_addr_count, octeon_chip_rev_major, octeon_chip_rev_minor, octeon_chip_type; extern void bzero_64(void *str, size_t len); extern void bzero_32(void *str, size_t len); extern void bzero_16(void *str, size_t len); extern void bzero_old(void *str, size_t len); extern void octeon_ciu_reset(void); extern void ciu_disable_intr(int core_num, int intx, int enx); extern void ciu_enable_interrupts (int core_num, int intx, int enx, uint64_t set_these_interrupt_bits, int ciu_ip); extern void ciu_clear_int_summary(int core_num, int intx, int enx, uint64_t write_bits); extern uint64_t ciu_get_int_summary(int core_num, int intx, int enx); extern void octeon_ciu_start_gtimer(int timer, u_int one_shot, uint64_t time_cycles); extern void octeon_ciu_stop_gtimer(int timer); extern int octeon_board_real(void); extern unsigned long octeon_get_clock_rate(void); typedef union { uint64_t word64; struct { uint64_t reserved : 27; /* Not used */ uint64_t one_shot : 1; /* Oneshot ? */ uint64_t len : 36; /* len of timer in clock cycles - 1 */ } bits; } octeon_ciu_gentimer; #endif /* LOCORE */ /* * R4K Address space definitions */ #define ADRSPC_K0BASE (0x80000000) #define ADRSPC_K0SIZE (0x20000000) #define ADRSPC_K1BASE (0xA0000000) #define ADRSPC_K1SIZE (0x20000000) #define ADRSPC_KSBASE (0xC0000000) #define ADRSPC_KSSIZE (0x20000000) #define ADRSPC_K3BASE (0xE0000000) #define ADRSPC_K3SIZE (0x20000000) #define ADRSPC_KUBASE (0x00000000) #define ADRSPC_KUSIZE (0x80000000) #define KSEG_MSB_ADDR 0xFFFFFFFF #define OCTEON_CLOCK_DEFAULT (500 * 1000 * 1000) /* * Octeon Boot Bus BIST Status * Mostly used for dummy read to ensure all prev I/Os are write-complete. */ #define OCTEON_MIO_BOOT_BIST_STAT 0x80011800000000F8ull /* * Octeon UART unit */ #define OCTEON_MIO_UART0 0x8001180000000800ull #define OCTEON_MIO_UART1 0x8001180000000C00ull #define OCTEON_MIO_UART0_THR 0x8001180000000840ull #define OCTEON_MIO_UART1_THR 0x8001180000000C40ull #define OCTEON_MIO_UART0_LSR 0x8001180000000828ull #define OCTEON_MIO_UART1_LSR 0x8001180000000C28ull #define OCTEON_MIO_UART0_RBR 0x8001180000000800ull #define OCTEON_MIO_UART1_RBR 0x8001180000000C00ull #define OCTEON_MIO_UART0_USR 0x8001180000000938ull #define OCTEON_MIO_UART1_USR 0x8001180000000D38ull #define OCTEON_MIO_ADDR_HI24 0x800118 #define OCTEON_MIO_UART_SIZE 0x400ull /* * EBT3000 LED Unit */ #define OCTEON_CHAR_LED_BASE_ADDR (0x1d020000 | (0x1ffffffffull << 31)) #define OCTEON_FPA_QUEUES 8 /* * Octeon FPA I/O Registers */ #define OCTEON_FPA_CTL_STATUS 0x8001180028000050ull #define OCTEON_FPA_FPF_SIZE 0x8001180028000058ull #define OCTEON_FPA_FPF_MARKS 0x8001180028000000ull #define OCTEON_FPA_INT_SUMMARY 0x8001180028000040ull #define OCTEON_FPA_INT_ENABLE 0x8001180028000048ull #define OCTEON_FPA_QUEUE_AVAILABLE 0x8001180028000098ull #define OCTEON_FPA_PAGE_INDEX 0x80011800280000f0ull /* * Octeon PKO Unit */ #define OCTEON_PKO_REG_FLAGS 0x8001180050000000ull #define OCTEON_PKO_REG_READ_IDX 0x8001180050000008ull #define OCTEON_PKO_CMD_BUF 0x8001180050000010ull #define OCTEON_PKO_GMX_PORT_MODE 0x8001180050000018ull #define OCTEON_PKO_REG_CRC_ENABLE 0x8001180050000020ull #define OCTEON_PKO_QUEUE_MODE 0x8001180050000048ull #define OCTEON_PKO_MEM_QUEUE_PTRS 0x8001180050001000ull #define OCTEON_PKO_MEM_COUNT0 0x8001180050001080ull #define OCTEON_PKO_MEM_COUNT1 0x8001180050001088ull #define OCTEON_PKO_MEM_DEBUG0 0x8001180050001100ull #define OCTEON_PKO_MEM_DEBUG1 0x8001180050001108ull #define OCTEON_PKO_MEM_DEBUG2 0x8001180050001110ull #define OCTEON_PKO_MEM_DEBUG3 0x8001180050001118ull #define OCTEON_PKO_MEM_DEBUG4 0x8001180050001120ull #define OCTEON_PKO_MEM_DEBUG5 0x8001180050001128ull #define OCTEON_PKO_MEM_DEBUG6 0x8001180050001130ull #define OCTEON_PKO_MEM_DEBUG7 0x8001180050001138ull #define OCTEON_PKO_MEM_DEBUG8 0x8001180050001140ull #define OCTEON_PKO_MEM_DEBUG9 0x8001180050001148ull /* * Octeon IPD Unit */ #define OCTEON_IPD_1ST_MBUFF_SKIP 0x80014F0000000000ull #define OCTEON_IPD_NOT_1ST_MBUFF_SKIP 0x80014F0000000008ull #define OCTEON_IPD_PACKET_MBUFF_SIZE 0x80014F0000000010ull #define OCTEON_IPD_1ST_NEXT_PTR_BACK 0x80014F0000000150ull #define OCTEON_IPD_2ND_NEXT_PTR_BACK 0x80014F0000000158ull #define OCTEON_IPD_WQE_FPA_QUEUE 0x80014F0000000020ull #define OCTEON_IPD_CTL_STATUS 0x80014F0000000018ull #define OCTEON_IPD_QOSX_RED_MARKS(queue) (0x80014F0000000178ull + ((queue) * 8)) #define OCTEON_IPD_RED_Q_PARAM(queue) (0x80014F00000002E0ull + ((queue) * 8)) #define OCTEON_IPD_PORT_BP_PAGE_COUNT(port) (0x80014F0000000028ull + ((port) * 8)) #define OCTEON_IPD_BP_PORT_RED_END 0x80014F0000000328ull #define OCTEON_IPD_RED_PORT_ENABLE 0x80014F00000002D8ull /* * Octeon CIU Unit */ #define OCTEON_CIU_ENABLE_BASE_ADDR 0x8001070000000200ull #define OCTEON_CIU_SUMMARY_BASE_ADDR 0x8001070000000000ull #define OCTEON_CIU_SUMMARY_INT1_ADDR 0x8001070000000108ull #define OCTEON_CIU_MBOX_SETX(offset) (0x8001070000000600ull+((offset)*8)) #define OCTEON_CIU_MBOX_CLRX(offset) (0x8001070000000680ull+((offset)*8)) #define OCTEON_CIU_ENABLE_MBOX_INTR 0x0000000300000000ull /* bits 32, 33 */ #define CIU_MIPS_IP2 0 #define CIU_MIPS_IP3 1 #define CIU_INT_0 CIU_MIPS_IP2 #define CIU_INT_1 CIU_MIPS_IP3 #define CIU_EN_0 0 #define CIU_EN_1 1 #define CIU_THIS_CORE -1 #define CIU_UART_BITS_UART0 (0x1ull << 34) // Bit 34 #define CIU_UART_BITS_UART1 (0x1ull << 35) // Bit 35 #define CIU_GENTIMER_BITS_ENABLE(timer) (0x1ull << (52 + (timer))) // Bit 52..55 #define CIU_GENTIMER_NUM_0 0 #define CIU_GENTIMER_NUM_1 1 #define CIU_GENTIMER_NUM_2 2 #define CIU_GENTIMER_NUM_3 3 #define OCTEON_GENTIMER_ONESHOT 1 #define OCTEON_GENTIMER_PERIODIC 0 #define OCTEON_CIU_GENTIMER_ADDR(timer) (0x8001070000000480ull + ((timer) * 0x8)) #define OCTEON_GENTIMER_LEN_1MS (0x7a120ull) /* Back of envelope. 500Mhz Octeon */ // FIXME IF WRONG #define OCTEON_GENTIMER_LEN_1SEC ((OCTEON_GENTIMER_LEN_1MS) * 1000) /* * Physical Memory Banks */ /* 1st BANK */ #define OCTEON_DRAM_FIRST_256_START 0x00000000ull #define OCTEON_DRAM_FIRST_256_END (0x10000000ull - 1ull) #define OCTEON_DRAM_RESERVED_END 0X1FFF000ULL /* 32 Meg Reserved for Mips Kernel MD Ops */ #define OCTEON_DRAM_FIRST_BANK_SIZE (OCTEON_DRAM_FIRST_256_END - OCTEON_DRAM_FIRST_256_START + 1) /* 2nd BANK */ #define OCTEON_DRAM_SECOND_256_START (0x0000000410000000ull) #define OCTEON_DRAM_SECOND_256_END (0x0000000420000000ull - 1ull) /* Requires 64 bit paddr */ #define OCTEON_DRAM_SECOND_BANK_SIZE (OCTEON_DRAM_SECOND_256_END - OCTEON_DRAM_SECOND_256_START + 1ull) /* 3rd BANK */ #define OCTEON_DRAM_ABOVE_512_START 0x20000000ull #define OCTEON_DRAM_ABOVE_512_END (0x0000000300000000ull - 1ull) /* To be calculated as remaining */ #define OCTEON_DRAM_THIRD_BANK_SIZE (OCTEON_DRAM_ABOVE_512_END - OCTEON_DRAM_ABOVE_512_START + 1ull) #endif /* !OCTEON_PCMAP_REGS_H__ */