1164426Ssam/*- 2186352Ssam * Copyright (c) 2006-2008 Sam Leffler, Errno Consulting 3164426Ssam * All rights reserved. 4164426Ssam * 5164426Ssam * Redistribution and use in source and binary forms, with or without 6164426Ssam * modification, are permitted provided that the following conditions 7164426Ssam * are met: 8164426Ssam * 1. Redistributions of source code must retain the above copyright 9164426Ssam * notice, this list of conditions and the following disclaimer, 10164426Ssam * without modification. 11164426Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12164426Ssam * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13164426Ssam * redistribution must be conditioned upon including a substantially 14164426Ssam * similar Disclaimer requirement for further binary redistribution. 15164426Ssam * 16164426Ssam * NO WARRANTY 17164426Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18164426Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19164426Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20164426Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21164426Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22164426Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23164426Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24164426Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25164426Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26164426Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27164426Ssam * THE POSSIBILITY OF SUCH DAMAGES. 28164426Ssam */ 29164426Ssam 30164426Ssam/*- 31164426Ssam * Copyright (c) 2001-2005, Intel Corporation. 32164426Ssam * All rights reserved. 33164426Ssam * 34164426Ssam * Redistribution and use in source and binary forms, with or without 35164426Ssam * modification, are permitted provided that the following conditions 36164426Ssam * are met: 37164426Ssam * 1. Redistributions of source code must retain the above copyright 38164426Ssam * notice, this list of conditions and the following disclaimer. 39164426Ssam * 2. Redistributions in binary form must reproduce the above copyright 40164426Ssam * notice, this list of conditions and the following disclaimer in the 41164426Ssam * documentation and/or other materials provided with the distribution. 42164426Ssam * 3. Neither the name of the Intel Corporation nor the names of its contributors 43164426Ssam * may be used to endorse or promote products derived from this software 44164426Ssam * without specific prior written permission. 45164426Ssam * 46164426Ssam * 47164426Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 48164426Ssam * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49164426Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50164426Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 51164426Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 52164426Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 53164426Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54164426Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 55164426Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 56164426Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 57164426Ssam * SUCH DAMAGE. 58164426Ssam*/ 59164426Ssam#include <sys/cdefs.h> 60164426Ssam__FBSDID("$FreeBSD$"); 61164426Ssam 62164426Ssam/* 63164426Ssam * Intel XScale Network Processing Engine (NPE) support. 64164426Ssam * 65164426Ssam * Each NPE has an ixpnpeX device associated with it that is 66164426Ssam * attached at boot. Depending on the microcode loaded into 67164426Ssam * an NPE there may be an Ethernet interface (npeX) or some 68164426Ssam * other network interface (e.g. for ATM). This file has support 69164426Ssam * for loading microcode images and the associated NPE CPU 70164426Ssam * manipulations (start, stop, reset). 71164426Ssam * 72164426Ssam * The code here basically replaces the npeDl and npeMh classes 73164426Ssam * in the Intel Access Library (IAL). 74164426Ssam * 75172358Scognet * NB: Microcode images are loaded with firmware(9). To 76172358Scognet * include microcode in a static kernel include the 77172358Scognet * ixpnpe_fw device. Otherwise the firmware will be 78172358Scognet * automatically loaded from the filesystem. 79164426Ssam */ 80164426Ssam#include <sys/param.h> 81164426Ssam#include <sys/systm.h> 82164426Ssam#include <sys/kernel.h> 83164426Ssam#include <sys/malloc.h> 84164426Ssam#include <sys/module.h> 85164426Ssam#include <sys/time.h> 86164426Ssam#include <sys/bus.h> 87164426Ssam#include <sys/resource.h> 88164426Ssam#include <sys/rman.h> 89164426Ssam#include <sys/sysctl.h> 90164426Ssam 91172358Scognet#include <sys/linker.h> 92172358Scognet#include <sys/firmware.h> 93172358Scognet 94164426Ssam#include <machine/bus.h> 95164426Ssam#include <machine/cpu.h> 96164426Ssam#include <machine/cpufunc.h> 97164426Ssam#include <machine/resource.h> 98164426Ssam#include <machine/intr.h> 99164426Ssam#include <arm/xscale/ixp425/ixp425reg.h> 100164426Ssam#include <arm/xscale/ixp425/ixp425var.h> 101164426Ssam 102164426Ssam#include <arm/xscale/ixp425/ixp425_npereg.h> 103164426Ssam#include <arm/xscale/ixp425/ixp425_npevar.h> 104164426Ssam 105164426Ssamstruct ixpnpe_softc { 106186352Ssam device_t sc_dev; 107186352Ssam bus_space_tag_t sc_iot; 108186352Ssam bus_space_handle_t sc_ioh; 109186352Ssam bus_size_t sc_size; /* size of mapped register window */ 110186352Ssam struct resource *sc_irq; /* IRQ resource */ 111186352Ssam void *sc_ih; /* interrupt handler */ 112186352Ssam struct mtx sc_mtx; /* mailbox lock */ 113186352Ssam uint32_t sc_msg[2]; /* reply msg collected in ixpnpe_intr */ 114186352Ssam int sc_msgwaiting; /* sc_msg holds valid data */ 115186420Ssam int sc_npeid; 116186420Ssam int sc_nrefs; /* # of references */ 117164426Ssam 118186352Ssam int validImage; /* valid ucode image loaded */ 119186352Ssam int started; /* NPE is started */ 120186352Ssam uint8_t functionalityId;/* ucode functionality ID */ 121186352Ssam int insMemSize; /* size of instruction memory */ 122186352Ssam int dataMemSize; /* size of data memory */ 123186352Ssam uint32_t savedExecCount; 124186352Ssam uint32_t savedEcsDbgCtxtReg2; 125164426Ssam}; 126186420Ssamstatic struct ixpnpe_softc *npes[NPE_MAX]; 127164426Ssam 128164426Ssam#define IX_NPEDL_NPEIMAGE_FIELD_MASK 0xff 129164426Ssam 130164426Ssam/* used to read download map from version in microcode image */ 131164426Ssam#define IX_NPEDL_BLOCK_TYPE_INSTRUCTION 0x00000000 132164426Ssam#define IX_NPEDL_BLOCK_TYPE_DATA 0x00000001 133164426Ssam#define IX_NPEDL_BLOCK_TYPE_STATE 0x00000002 134164426Ssam#define IX_NPEDL_END_OF_DOWNLOAD_MAP 0x0000000F 135164426Ssam 136164426Ssam/* 137164426Ssam * masks used to extract address info from State information context 138164426Ssam * register addresses as read from microcode image 139164426Ssam */ 140164426Ssam#define IX_NPEDL_MASK_STATE_ADDR_CTXT_REG 0x0000000F 141164426Ssam#define IX_NPEDL_MASK_STATE_ADDR_CTXT_NUM 0x000000F0 142164426Ssam 143164426Ssam/* LSB offset of Context Number field in State-Info Context Address */ 144164426Ssam#define IX_NPEDL_OFFSET_STATE_ADDR_CTXT_NUM 4 145164426Ssam 146164426Ssam/* size (in words) of single State Information entry (ctxt reg address|data) */ 147164426Ssam#define IX_NPEDL_STATE_INFO_ENTRY_SIZE 2 148164426Ssam 149164426Ssamtypedef struct { 150186352Ssam uint32_t type; 151186352Ssam uint32_t offset; 152164426Ssam} IxNpeDlNpeMgrDownloadMapBlockEntry; 153164426Ssam 154164426Ssamtypedef union { 155186352Ssam IxNpeDlNpeMgrDownloadMapBlockEntry block; 156186352Ssam uint32_t eodmMarker; 157164426Ssam} IxNpeDlNpeMgrDownloadMapEntry; 158164426Ssam 159164426Ssamtypedef struct { 160186352Ssam /* 1st entry in the download map (there may be more than one) */ 161186352Ssam IxNpeDlNpeMgrDownloadMapEntry entry[1]; 162164426Ssam} IxNpeDlNpeMgrDownloadMap; 163164426Ssam 164164426Ssam/* used to access an instruction or data block in a microcode image */ 165164426Ssamtypedef struct { 166186352Ssam uint32_t npeMemAddress; 167186352Ssam uint32_t size; 168186352Ssam uint32_t data[1]; 169164426Ssam} IxNpeDlNpeMgrCodeBlock; 170164426Ssam 171164426Ssam/* used to access each Context Reg entry state-information block */ 172164426Ssamtypedef struct { 173186352Ssam uint32_t addressInfo; 174186352Ssam uint32_t value; 175164426Ssam} IxNpeDlNpeMgrStateInfoCtxtRegEntry; 176164426Ssam 177164426Ssam/* used to access a state-information block in a microcode image */ 178164426Ssamtypedef struct { 179186352Ssam uint32_t size; 180186352Ssam IxNpeDlNpeMgrStateInfoCtxtRegEntry ctxtRegEntry[1]; 181164426Ssam} IxNpeDlNpeMgrStateInfoBlock; 182164426Ssam 183164426Ssamstatic int npe_debug = 0; 184164426SsamSYSCTL_INT(_debug, OID_AUTO, ixp425npe, CTLFLAG_RW, &npe_debug, 185186352Ssam 0, "IXP4XX NPE debug msgs"); 186164426SsamTUNABLE_INT("debug.ixp425npe", &npe_debug); 187164426Ssam#define DPRINTF(dev, fmt, ...) do { \ 188164426Ssam if (npe_debug) device_printf(dev, fmt, __VA_ARGS__); \ 189164426Ssam} while (0) 190164426Ssam#define DPRINTFn(n, dev, fmt, ...) do { \ 191164426Ssam if (npe_debug >= n) printf(fmt, __VA_ARGS__); \ 192164426Ssam} while (0) 193164426Ssam 194164426Ssamstatic int npe_checkbits(struct ixpnpe_softc *, uint32_t reg, uint32_t); 195164426Ssamstatic int npe_isstopped(struct ixpnpe_softc *); 196164426Ssamstatic int npe_load_ins(struct ixpnpe_softc *, 197164426Ssam const IxNpeDlNpeMgrCodeBlock *bp, int verify); 198164426Ssamstatic int npe_load_data(struct ixpnpe_softc *, 199164426Ssam const IxNpeDlNpeMgrCodeBlock *bp, int verify); 200164426Ssamstatic int npe_load_stateinfo(struct ixpnpe_softc *, 201164426Ssam const IxNpeDlNpeMgrStateInfoBlock *bp, int verify); 202164426Ssamstatic int npe_load_image(struct ixpnpe_softc *, 203164426Ssam const uint32_t *imageCodePtr, int verify); 204164426Ssamstatic int npe_cpu_reset(struct ixpnpe_softc *); 205164426Ssamstatic int npe_cpu_start(struct ixpnpe_softc *); 206164426Ssamstatic int npe_cpu_stop(struct ixpnpe_softc *); 207164426Ssamstatic void npe_cmd_issue_write(struct ixpnpe_softc *, 208164426Ssam uint32_t cmd, uint32_t addr, uint32_t data); 209164426Ssamstatic uint32_t npe_cmd_issue_read(struct ixpnpe_softc *, 210164426Ssam uint32_t cmd, uint32_t addr); 211164426Ssamstatic int npe_ins_write(struct ixpnpe_softc *, 212164426Ssam uint32_t addr, uint32_t data, int verify); 213164426Ssamstatic int npe_data_write(struct ixpnpe_softc *, 214164426Ssam uint32_t addr, uint32_t data, int verify); 215164426Ssamstatic void npe_ecs_reg_write(struct ixpnpe_softc *, 216164426Ssam uint32_t reg, uint32_t data); 217164426Ssamstatic uint32_t npe_ecs_reg_read(struct ixpnpe_softc *, uint32_t reg); 218164426Ssamstatic void npe_issue_cmd(struct ixpnpe_softc *, uint32_t command); 219164426Ssamstatic void npe_cpu_step_save(struct ixpnpe_softc *); 220164426Ssamstatic int npe_cpu_step(struct ixpnpe_softc *, uint32_t npeInstruction, 221164426Ssam uint32_t ctxtNum, uint32_t ldur); 222164426Ssamstatic void npe_cpu_step_restore(struct ixpnpe_softc *); 223164426Ssamstatic int npe_logical_reg_read(struct ixpnpe_softc *, 224164426Ssam uint32_t regAddr, uint32_t regSize, 225164426Ssam uint32_t ctxtNum, uint32_t *regVal); 226164426Ssamstatic int npe_logical_reg_write(struct ixpnpe_softc *, 227164426Ssam uint32_t regAddr, uint32_t regVal, 228164426Ssam uint32_t regSize, uint32_t ctxtNum, int verify); 229164426Ssamstatic int npe_physical_reg_write(struct ixpnpe_softc *, 230164426Ssam uint32_t regAddr, uint32_t regValue, int verify); 231164426Ssamstatic int npe_ctx_reg_write(struct ixpnpe_softc *, uint32_t ctxtNum, 232164426Ssam uint32_t ctxtReg, uint32_t ctxtRegVal, int verify); 233164426Ssam 234164426Ssamstatic void ixpnpe_intr(void *arg); 235164426Ssam 236164426Ssamstatic uint32_t 237164426Ssamnpe_reg_read(struct ixpnpe_softc *sc, bus_size_t off) 238164426Ssam{ 239186352Ssam uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off); 240186352Ssam DPRINTFn(9, sc->sc_dev, "%s(0x%lx) => 0x%x\n", __func__, off, v); 241186352Ssam return v; 242164426Ssam} 243164426Ssam 244164426Ssamstatic void 245164426Ssamnpe_reg_write(struct ixpnpe_softc *sc, bus_size_t off, uint32_t val) 246164426Ssam{ 247186352Ssam DPRINTFn(9, sc->sc_dev, "%s(0x%lx, 0x%x)\n", __func__, off, val); 248186352Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val); 249164426Ssam} 250164426Ssam 251164426Ssamstruct ixpnpe_softc * 252186352Ssamixpnpe_attach(device_t dev, int npeid) 253164426Ssam{ 254186352Ssam struct npeconfig { 255186352Ssam uint32_t base; 256186352Ssam uint32_t size; 257186352Ssam int irq; 258186352Ssam uint32_t ins_memsize; 259186352Ssam uint32_t data_memsize; 260186352Ssam }; 261186352Ssam static const struct npeconfig npeconfigs[NPE_MAX] = { 262186352Ssam [NPE_A] = { 263186352Ssam .base = IXP425_NPE_A_HWBASE, 264186352Ssam .size = IXP425_NPE_A_SIZE, 265186352Ssam .irq = IXP425_INT_NPE_A, 266186352Ssam .ins_memsize = IX_NPEDL_INS_MEMSIZE_WORDS_NPEA, 267186352Ssam .data_memsize = IX_NPEDL_DATA_MEMSIZE_WORDS_NPEA 268186352Ssam }, 269186352Ssam [NPE_B] = { 270186352Ssam .base = IXP425_NPE_B_HWBASE, 271186352Ssam .size = IXP425_NPE_B_SIZE, 272186352Ssam .irq = IXP425_INT_NPE_B, 273186352Ssam .ins_memsize = IX_NPEDL_INS_MEMSIZE_WORDS_NPEB, 274186352Ssam .data_memsize = IX_NPEDL_DATA_MEMSIZE_WORDS_NPEB 275186352Ssam }, 276186352Ssam [NPE_C] = { 277186352Ssam .base = IXP425_NPE_C_HWBASE, 278186352Ssam .size = IXP425_NPE_C_SIZE, 279186352Ssam .irq = IXP425_INT_NPE_C, 280186352Ssam .ins_memsize = IX_NPEDL_INS_MEMSIZE_WORDS_NPEC, 281186352Ssam .data_memsize = IX_NPEDL_DATA_MEMSIZE_WORDS_NPEC 282186352Ssam }, 283186352Ssam }; 284186352Ssam struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 285186352Ssam struct ixpnpe_softc *sc; 286186352Ssam const struct npeconfig *config; 287186352Ssam int rid; 288164426Ssam 289186352Ssam if (npeid >= NPE_MAX) { 290186352Ssam device_printf(dev, "%s: bad npeid %d\n", __func__, npeid); 291186352Ssam return NULL; 292186352Ssam } 293186420Ssam sc = npes[npeid]; 294186420Ssam if (sc != NULL) { 295186420Ssam sc->sc_nrefs++; 296186420Ssam return sc; 297186420Ssam } 298186352Ssam config = &npeconfigs[npeid]; 299164426Ssam 300186352Ssam /* XXX M_BUS */ 301186352Ssam sc = malloc(sizeof(struct ixpnpe_softc), M_TEMP, M_WAITOK | M_ZERO); 302186352Ssam sc->sc_dev = dev; 303186352Ssam sc->sc_iot = sa->sc_iot; 304186352Ssam mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "npe driver", MTX_DEF); 305186420Ssam sc->sc_npeid = npeid; 306186420Ssam sc->sc_nrefs = 1; 307164426Ssam 308186352Ssam sc->sc_size = config->size; 309194325Ssam if (cpu_is_ixp42x()) { 310194325Ssam /* NB: instruction/data memory sizes are NPE-dependent */ 311194325Ssam sc->insMemSize = config->ins_memsize; 312194325Ssam sc->dataMemSize = config->data_memsize; 313194325Ssam } else { 314194325Ssam sc->insMemSize = IXP46X_NPEDL_INS_MEMSIZE_WORDS; 315194325Ssam sc->dataMemSize = IXP46X_NPEDL_DATA_MEMSIZE_WORDS; 316194325Ssam } 317164426Ssam 318186352Ssam if (bus_space_map(sc->sc_iot, config->base, sc->sc_size, 0, &sc->sc_ioh)) 319186352Ssam panic("%s: Cannot map registers", device_get_name(dev)); 320164426Ssam 321186352Ssam /* 322186352Ssam * Setup IRQ and handler for NPE message support. 323186352Ssam */ 324186352Ssam rid = 0; 325186352Ssam sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 326186352Ssam config->irq, config->irq, 1, RF_ACTIVE); 327186352Ssam if (sc->sc_irq == NULL) 328186352Ssam panic("%s: Unable to allocate irq %u", device_get_name(dev), 329186352Ssam config->irq); 330186352Ssam /* XXX could be a source of entropy */ 331186352Ssam bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 332186352Ssam NULL, ixpnpe_intr, sc, &sc->sc_ih); 333186352Ssam /* 334186352Ssam * Enable output fifo interrupts (NB: must also set OFIFO Write Enable) 335186352Ssam */ 336186352Ssam npe_reg_write(sc, IX_NPECTL, 337186352Ssam npe_reg_read(sc, IX_NPECTL) | (IX_NPECTL_OFE | IX_NPECTL_OFWE)); 338164426Ssam 339186420Ssam npes[npeid] = sc; 340186420Ssam 341186352Ssam return sc; 342164426Ssam} 343164426Ssam 344164426Ssamvoid 345164426Ssamixpnpe_detach(struct ixpnpe_softc *sc) 346164426Ssam{ 347186420Ssam if (--sc->sc_nrefs == 0) { 348186420Ssam npes[sc->sc_npeid] = NULL; 349164426Ssam 350186420Ssam /* disable output fifo interrupts */ 351186420Ssam npe_reg_write(sc, IX_NPECTL, 352186420Ssam npe_reg_read(sc, IX_NPECTL) &~ (IX_NPECTL_OFE | IX_NPECTL_OFWE)); 353186420Ssam 354186420Ssam bus_teardown_intr(sc->sc_dev, sc->sc_irq, sc->sc_ih); 355186420Ssam bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); 356186420Ssam mtx_destroy(&sc->sc_mtx); 357186420Ssam free(sc, M_TEMP); 358186420Ssam } 359164426Ssam} 360164426Ssam 361164426Ssamint 362164426Ssamixpnpe_stopandreset(struct ixpnpe_softc *sc) 363164426Ssam{ 364186352Ssam int error; 365164426Ssam 366186352Ssam mtx_lock(&sc->sc_mtx); 367186352Ssam error = npe_cpu_stop(sc); /* stop NPE */ 368186352Ssam if (error == 0) 369186352Ssam error = npe_cpu_reset(sc); /* reset it */ 370186352Ssam if (error == 0) 371186352Ssam sc->started = 0; /* mark stopped */ 372186352Ssam mtx_unlock(&sc->sc_mtx); 373164426Ssam 374186352Ssam DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error); 375186352Ssam return error; 376164426Ssam} 377164426Ssam 378164426Ssamstatic int 379164426Ssamixpnpe_start_locked(struct ixpnpe_softc *sc) 380164426Ssam{ 381186352Ssam int error; 382164426Ssam 383186352Ssam if (!sc->started) { 384186352Ssam error = npe_cpu_start(sc); 385186352Ssam if (error == 0) 386186420Ssam sc->started = 1; 387186352Ssam } else 388186352Ssam error = 0; 389164426Ssam 390186352Ssam DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error); 391186352Ssam return error; 392164426Ssam} 393164426Ssam 394164426Ssamint 395164426Ssamixpnpe_start(struct ixpnpe_softc *sc) 396164426Ssam{ 397164426Ssam int ret; 398164426Ssam 399164426Ssam mtx_lock(&sc->sc_mtx); 400164426Ssam ret = ixpnpe_start_locked(sc); 401164426Ssam mtx_unlock(&sc->sc_mtx); 402164426Ssam return (ret); 403164426Ssam} 404164426Ssam 405164426Ssamint 406164426Ssamixpnpe_stop(struct ixpnpe_softc *sc) 407164426Ssam{ 408186352Ssam int error; 409164426Ssam 410186352Ssam mtx_lock(&sc->sc_mtx); 411186352Ssam error = npe_cpu_stop(sc); 412186352Ssam if (error == 0) 413186352Ssam sc->started = 0; 414186352Ssam mtx_unlock(&sc->sc_mtx); 415164426Ssam 416186352Ssam DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error); 417186352Ssam return error; 418164426Ssam} 419164426Ssam 420164426Ssam/* 421164426Ssam * Indicates the start of an NPE Image, in new NPE Image Library format. 422164426Ssam * 2 consecutive occurances indicates the end of the NPE Image Library 423164426Ssam */ 424164426Ssam#define NPE_IMAGE_MARKER 0xfeedf00d 425164426Ssam 426164426Ssam/* 427164426Ssam * NPE Image Header definition, used in new NPE Image Library format 428164426Ssam */ 429164426Ssamtypedef struct { 430186352Ssam uint32_t marker; 431186352Ssam uint32_t id; 432186352Ssam uint32_t size; 433164426Ssam} IxNpeDlImageMgrImageHeader; 434164426Ssam 435164426Ssamstatic int 436164426Ssamnpe_findimage(struct ixpnpe_softc *sc, 437164426Ssam const uint32_t *imageLibrary, uint32_t imageId, 438164426Ssam const uint32_t **imagePtr, uint32_t *imageSize) 439164426Ssam{ 440186352Ssam const IxNpeDlImageMgrImageHeader *image; 441186352Ssam uint32_t offset = 0; 442164426Ssam 443186352Ssam while (imageLibrary[offset] == NPE_IMAGE_MARKER) { 444186352Ssam image = (const IxNpeDlImageMgrImageHeader *) 445186352Ssam &imageLibrary[offset]; 446186352Ssam offset += sizeof(IxNpeDlImageMgrImageHeader)/sizeof(uint32_t); 447186352Ssam 448186352Ssam DPRINTF(sc->sc_dev, "%s: off %u mark 0x%x id 0x%x size %u\n", 449186352Ssam __func__, offset, image->marker, image->id, image->size); 450186352Ssam if (image->id == imageId) { 451186352Ssam *imagePtr = imageLibrary + offset; 452186352Ssam *imageSize = image->size; 453186352Ssam return 0; 454186352Ssam } 455186352Ssam /* 2 consecutive NPE_IMAGE_MARKER's indicates end of library */ 456186352Ssam if (image->id == NPE_IMAGE_MARKER) { 457186352Ssam DPRINTF(sc->sc_dev, "imageId 0x%08x not found in " 458186352Ssam "image library header\n", imageId); 459186352Ssam /* reached end of library, image not found */ 460186352Ssam return ESRCH; 461186352Ssam } 462186352Ssam offset += image->size; 463186352Ssam } 464186352Ssam return ESRCH; 465164426Ssam} 466164426Ssam 467186420Ssamstatic int 468186420Ssamixpnpe_load_firmware(struct ixpnpe_softc *sc, const char *imageName, 469186420Ssam uint32_t imageId) 470164426Ssam{ 471186352Ssam static const char *devname[4] = 472186352Ssam { "IXP425", "IXP435/IXP465", "DeviceID#2", "DeviceID#3" }; 473186352Ssam uint32_t imageSize; 474186352Ssam const uint32_t *imageCodePtr; 475186352Ssam const struct firmware *fw; 476186352Ssam int error; 477164426Ssam 478186352Ssam DPRINTF(sc->sc_dev, "load %s, imageId 0x%08x\n", imageName, imageId); 479164426Ssam 480164426Ssam#if 0 481186352Ssam IxFeatureCtrlDeviceId devid = IX_NPEDL_DEVICEID_FROM_IMAGEID_GET(imageId); 482186352Ssam /* 483186352Ssam * Checking if image being loaded is meant for device that is running. 484186352Ssam * Image is forward compatible. i.e Image built for IXP42X should run 485186352Ssam * on IXP46X but not vice versa. 486186352Ssam */ 487186352Ssam if (devid > (ixFeatureCtrlDeviceRead() & IX_FEATURE_CTRL_DEVICE_TYPE_MASK)) 488186352Ssam return EINVAL; 489164426Ssam#endif 490186352Ssam error = ixpnpe_stopandreset(sc); /* stop and reset the NPE */ 491186352Ssam if (error != 0) 492186352Ssam return error; 493164426Ssam 494186352Ssam fw = firmware_get(imageName); 495186352Ssam if (fw == NULL) 496186352Ssam return ENOENT; 497172358Scognet 498186352Ssam /* Locate desired image in files w/ combined images */ 499186352Ssam error = npe_findimage(sc, fw->data, imageId, &imageCodePtr, &imageSize); 500186352Ssam if (error != 0) 501186352Ssam goto done; 502164426Ssam 503186352Ssam device_printf(sc->sc_dev, 504186352Ssam "load fw image %s.NPE-%c Func 0x%x Rev %u.%u\n", 505186352Ssam devname[NPEIMAGE_DEVID(imageId)], 'A' + NPEIMAGE_NPEID(imageId), 506186352Ssam NPEIMAGE_FUNCID(imageId), NPEIMAGE_MAJOR(imageId), 507186352Ssam NPEIMAGE_MINOR(imageId)); 508186352Ssam 509186352Ssam /* 510186352Ssam * If download was successful, store image Id in list of 511186352Ssam * currently loaded images. If a critical error occured 512186352Ssam * during download, record that the NPE has an invalid image 513186352Ssam */ 514186352Ssam mtx_lock(&sc->sc_mtx); 515186352Ssam error = npe_load_image(sc, imageCodePtr, 1 /*VERIFY*/); 516186352Ssam if (error == 0) { 517186352Ssam sc->validImage = 1; 518186352Ssam error = ixpnpe_start_locked(sc); 519186352Ssam } else { 520186352Ssam sc->validImage = 0; 521186352Ssam } 522186352Ssam sc->functionalityId = IX_NPEDL_FUNCTIONID_FROM_IMAGEID_GET(imageId); 523186352Ssam mtx_unlock(&sc->sc_mtx); 524164426Ssamdone: 525186352Ssam firmware_put(fw, FIRMWARE_UNLOAD); 526186352Ssam DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error); 527186352Ssam return error; 528164426Ssam} 529164426Ssam 530186420Ssamstatic int 531186420Ssamoverride_imageid(device_t dev, const char *resname, uint32_t *val) 532186420Ssam{ 533186420Ssam int unit = device_get_unit(dev); 534186420Ssam int resval; 535186420Ssam 536186420Ssam if (resource_int_value("npe", unit, resname, &resval) != 0) 537186420Ssam return 0; 538186420Ssam /* XXX validate */ 539186420Ssam if (bootverbose) 540186420Ssam device_printf(dev, "using npe.%d.%s=0x%x override\n", 541186420Ssam unit, resname, resval); 542186420Ssam *val = resval; 543186420Ssam return 1; 544186420Ssam} 545186420Ssam 546164426Ssamint 547186420Ssamixpnpe_init(struct ixpnpe_softc *sc) 548186420Ssam{ 549186420Ssam static const uint32_t npeconfig[NPE_MAX] = { 550186420Ssam [NPE_A] = IXP425_NPE_A_IMAGEID, 551186420Ssam [NPE_B] = IXP425_NPE_B_IMAGEID, 552186420Ssam [NPE_C] = IXP425_NPE_C_IMAGEID, 553186420Ssam }; 554186420Ssam uint32_t imageid, msg[2]; 555186420Ssam int error; 556186420Ssam 557186420Ssam if (sc->started) 558186420Ssam return 0; 559186420Ssam /* 560186420Ssam * Load NPE firmware and start it running. We assume 561186420Ssam * that minor version bumps remain compatible so probe 562186420Ssam * the firmware image starting with the expected version 563186420Ssam * and then bump the minor version up to the max. 564186420Ssam */ 565186420Ssam if (!override_imageid(sc->sc_dev, "imageid", &imageid)) 566186420Ssam imageid = npeconfig[sc->sc_npeid]; 567186420Ssam for (;;) { 568186420Ssam error = ixpnpe_load_firmware(sc, "npe_fw", imageid); 569186420Ssam if (error == 0) 570186420Ssam break; 571186420Ssam /* 572186420Ssam * ESRCH is returned when the requested image 573186420Ssam * is not present 574186420Ssam */ 575186420Ssam if (error != ESRCH) { 576186420Ssam device_printf(sc->sc_dev, 577186420Ssam "cannot init NPE (error %d)\n", error); 578186420Ssam return error; 579186420Ssam } 580186420Ssam /* bump the minor version up to the max possible */ 581186420Ssam if (NPEIMAGE_MINOR(imageid) == 0xff) { 582186420Ssam device_printf(sc->sc_dev, "cannot locate firmware " 583186420Ssam "(imageid 0x%08x)\n", imageid); 584186420Ssam return error; 585186420Ssam } 586186420Ssam imageid++; 587186420Ssam } 588186420Ssam /* NB: firmware should respond with a status msg */ 589186420Ssam if (ixpnpe_recvmsg_sync(sc, msg) != 0) { 590186420Ssam device_printf(sc->sc_dev, 591186420Ssam "firmware did not respond as expected\n"); 592186420Ssam return EIO; 593186420Ssam } 594186420Ssam return 0; 595186420Ssam} 596186420Ssam 597186420Ssamint 598164426Ssamixpnpe_getfunctionality(struct ixpnpe_softc *sc) 599164426Ssam{ 600186352Ssam return (sc->validImage ? sc->functionalityId : 0); 601164426Ssam} 602164426Ssam 603164426Ssamstatic int 604164426Ssamnpe_checkbits(struct ixpnpe_softc *sc, uint32_t reg, uint32_t expectedBitsSet) 605164426Ssam{ 606186352Ssam uint32_t val; 607164426Ssam 608186352Ssam val = npe_reg_read(sc, reg); 609186352Ssam DPRINTFn(5, sc->sc_dev, "%s(0x%x, 0x%x) => 0x%x (%u)\n", 610186352Ssam __func__, reg, expectedBitsSet, val, 611186352Ssam (val & expectedBitsSet) == expectedBitsSet); 612186352Ssam return ((val & expectedBitsSet) == expectedBitsSet); 613164426Ssam} 614164426Ssam 615164426Ssamstatic int 616164426Ssamnpe_isstopped(struct ixpnpe_softc *sc) 617164426Ssam{ 618186352Ssam return npe_checkbits(sc, 619186352Ssam IX_NPEDL_REG_OFFSET_EXCTL, IX_NPEDL_EXCTL_STATUS_STOP); 620164426Ssam} 621164426Ssam 622164426Ssamstatic int 623164426Ssamnpe_load_ins(struct ixpnpe_softc *sc, 624164426Ssam const IxNpeDlNpeMgrCodeBlock *bp, int verify) 625164426Ssam{ 626186352Ssam uint32_t npeMemAddress; 627186352Ssam int i, blockSize; 628164426Ssam 629186352Ssam npeMemAddress = bp->npeMemAddress; 630186352Ssam blockSize = bp->size; /* NB: instruction/data count */ 631186352Ssam if (npeMemAddress + blockSize > sc->insMemSize) { 632186352Ssam device_printf(sc->sc_dev, 633186352Ssam "Block size %u too big for NPE memory\n", blockSize); 634186352Ssam return EINVAL; /* XXX */ 635164426Ssam } 636186352Ssam for (i = 0; i < blockSize; i++, npeMemAddress++) { 637186352Ssam if (npe_ins_write(sc, npeMemAddress, bp->data[i], verify) != 0) { 638186352Ssam device_printf(sc->sc_dev, 639186352Ssam "NPE instruction write failed"); 640186352Ssam return EIO; 641186352Ssam } 642186352Ssam } 643186352Ssam return 0; 644164426Ssam} 645164426Ssam 646164426Ssamstatic int 647164426Ssamnpe_load_data(struct ixpnpe_softc *sc, 648164426Ssam const IxNpeDlNpeMgrCodeBlock *bp, int verify) 649164426Ssam{ 650186352Ssam uint32_t npeMemAddress; 651186352Ssam int i, blockSize; 652164426Ssam 653186352Ssam npeMemAddress = bp->npeMemAddress; 654186352Ssam blockSize = bp->size; /* NB: instruction/data count */ 655186352Ssam if (npeMemAddress + blockSize > sc->dataMemSize) { 656186352Ssam device_printf(sc->sc_dev, 657186352Ssam "Block size %u too big for NPE memory\n", blockSize); 658186352Ssam return EINVAL; 659164426Ssam } 660186352Ssam for (i = 0; i < blockSize; i++, npeMemAddress++) { 661186352Ssam if (npe_data_write(sc, npeMemAddress, bp->data[i], verify) != 0) { 662186352Ssam device_printf(sc->sc_dev, "NPE data write failed\n"); 663186352Ssam return EIO; 664186352Ssam } 665186352Ssam } 666186352Ssam return 0; 667164426Ssam} 668164426Ssam 669164426Ssamstatic int 670164426Ssamnpe_load_stateinfo(struct ixpnpe_softc *sc, 671164426Ssam const IxNpeDlNpeMgrStateInfoBlock *bp, int verify) 672164426Ssam{ 673186352Ssam int i, nentries, error; 674186352Ssam 675186352Ssam npe_cpu_step_save(sc); 676164426Ssam 677186352Ssam /* for each state-info context register entry in block */ 678186352Ssam nentries = bp->size / IX_NPEDL_STATE_INFO_ENTRY_SIZE; 679186352Ssam error = 0; 680186352Ssam for (i = 0; i < nentries; i++) { 681186352Ssam /* each state-info entry is 2 words (address, value) */ 682186352Ssam uint32_t regVal = bp->ctxtRegEntry[i].value; 683186352Ssam uint32_t addrInfo = bp->ctxtRegEntry[i].addressInfo; 684164426Ssam 685186352Ssam uint32_t reg = (addrInfo & IX_NPEDL_MASK_STATE_ADDR_CTXT_REG); 686186352Ssam uint32_t cNum = (addrInfo & IX_NPEDL_MASK_STATE_ADDR_CTXT_NUM) >> 687186352Ssam IX_NPEDL_OFFSET_STATE_ADDR_CTXT_NUM; 688186352Ssam 689186352Ssam /* error-check Context Register No. and Context Number values */ 690186352Ssam if (!(0 <= reg && reg < IX_NPEDL_CTXT_REG_MAX)) { 691186352Ssam device_printf(sc->sc_dev, 692186352Ssam "invalid Context Register %u\n", reg); 693186352Ssam error = EINVAL; 694186352Ssam break; 695186352Ssam } 696186352Ssam if (!(0 <= cNum && cNum < IX_NPEDL_CTXT_NUM_MAX)) { 697186352Ssam device_printf(sc->sc_dev, 698186352Ssam "invalid Context Number %u\n", cNum); 699186352Ssam error = EINVAL; 700186352Ssam break; 701186352Ssam } 702186352Ssam /* NOTE that there is no STEVT register for Context 0 */ 703186352Ssam if (cNum == 0 && reg == IX_NPEDL_CTXT_REG_STEVT) { 704186352Ssam device_printf(sc->sc_dev, 705186352Ssam "no STEVT for Context 0\n"); 706186352Ssam error = EINVAL; 707186352Ssam break; 708186352Ssam } 709164426Ssam 710186352Ssam if (npe_ctx_reg_write(sc, cNum, reg, regVal, verify) != 0) { 711186352Ssam device_printf(sc->sc_dev, 712186352Ssam "write of state-info to NPE failed\n"); 713186352Ssam error = EIO; 714186352Ssam break; 715186352Ssam } 716164426Ssam } 717164426Ssam 718186352Ssam npe_cpu_step_restore(sc); 719186352Ssam return error; 720164426Ssam} 721164426Ssam 722164426Ssamstatic int 723164426Ssamnpe_load_image(struct ixpnpe_softc *sc, 724164426Ssam const uint32_t *imageCodePtr, int verify) 725164426Ssam{ 726164426Ssam#define EOM(marker) ((marker) == IX_NPEDL_END_OF_DOWNLOAD_MAP) 727186352Ssam const IxNpeDlNpeMgrDownloadMap *downloadMap; 728186352Ssam int i, error; 729164426Ssam 730186352Ssam if (!npe_isstopped(sc)) { /* verify NPE is stopped */ 731186352Ssam device_printf(sc->sc_dev, 732186352Ssam "cannot load image, NPE not stopped\n"); 733186352Ssam return EIO; 734186352Ssam } 735164426Ssam 736186352Ssam /* 737186352Ssam * Read Download Map, checking each block type and calling 738186352Ssam * appropriate function to perform download 739186352Ssam */ 740186352Ssam error = 0; 741186352Ssam downloadMap = (const IxNpeDlNpeMgrDownloadMap *) imageCodePtr; 742186352Ssam for (i = 0; !EOM(downloadMap->entry[i].eodmMarker); i++) { 743186352Ssam /* calculate pointer to block to be downloaded */ 744186352Ssam const uint32_t *bp = imageCodePtr + 745186352Ssam downloadMap->entry[i].block.offset; 746186352Ssam switch (downloadMap->entry[i].block.type) { 747186352Ssam case IX_NPEDL_BLOCK_TYPE_INSTRUCTION: 748186352Ssam error = npe_load_ins(sc, 749186352Ssam (const IxNpeDlNpeMgrCodeBlock *) bp, verify); 750186352Ssam DPRINTF(sc->sc_dev, "%s: inst, error %d\n", 751186352Ssam __func__, error); 752186352Ssam break; 753186352Ssam case IX_NPEDL_BLOCK_TYPE_DATA: 754186352Ssam error = npe_load_data(sc, 755186352Ssam (const IxNpeDlNpeMgrCodeBlock *) bp, verify); 756186352Ssam DPRINTF(sc->sc_dev, "%s: data, error %d\n", 757186352Ssam __func__, error); 758186352Ssam break; 759186352Ssam case IX_NPEDL_BLOCK_TYPE_STATE: 760186352Ssam error = npe_load_stateinfo(sc, 761186352Ssam (const IxNpeDlNpeMgrStateInfoBlock *) bp, verify); 762186352Ssam DPRINTF(sc->sc_dev, "%s: state, error %d\n", 763186352Ssam __func__, error); 764186352Ssam break; 765186352Ssam default: 766186352Ssam device_printf(sc->sc_dev, 767186352Ssam "unknown block type 0x%x in download map\n", 768186352Ssam downloadMap->entry[i].block.type); 769186352Ssam error = EIO; /* XXX */ 770186352Ssam break; 771186352Ssam } 772186352Ssam if (error != 0) 773186352Ssam break; 774164426Ssam } 775186352Ssam return error; 776164426Ssam#undef EOM 777164426Ssam} 778164426Ssam 779164426Ssam/* contains Reset values for Context Store Registers */ 780164426Ssamstatic const struct { 781186352Ssam uint32_t regAddr; 782186352Ssam uint32_t regResetVal; 783164426Ssam} ixNpeDlEcsRegResetValues[] = { 784186352Ssam { IX_NPEDL_ECS_BG_CTXT_REG_0, IX_NPEDL_ECS_BG_CTXT_REG_0_RESET }, 785186352Ssam { IX_NPEDL_ECS_BG_CTXT_REG_1, IX_NPEDL_ECS_BG_CTXT_REG_1_RESET }, 786186352Ssam { IX_NPEDL_ECS_BG_CTXT_REG_2, IX_NPEDL_ECS_BG_CTXT_REG_2_RESET }, 787186352Ssam { IX_NPEDL_ECS_PRI_1_CTXT_REG_0, IX_NPEDL_ECS_PRI_1_CTXT_REG_0_RESET }, 788186352Ssam { IX_NPEDL_ECS_PRI_1_CTXT_REG_1, IX_NPEDL_ECS_PRI_1_CTXT_REG_1_RESET }, 789186352Ssam { IX_NPEDL_ECS_PRI_1_CTXT_REG_2, IX_NPEDL_ECS_PRI_1_CTXT_REG_2_RESET }, 790186352Ssam { IX_NPEDL_ECS_PRI_2_CTXT_REG_0, IX_NPEDL_ECS_PRI_2_CTXT_REG_0_RESET }, 791186352Ssam { IX_NPEDL_ECS_PRI_2_CTXT_REG_1, IX_NPEDL_ECS_PRI_2_CTXT_REG_1_RESET }, 792186352Ssam { IX_NPEDL_ECS_PRI_2_CTXT_REG_2, IX_NPEDL_ECS_PRI_2_CTXT_REG_2_RESET }, 793186352Ssam { IX_NPEDL_ECS_DBG_CTXT_REG_0, IX_NPEDL_ECS_DBG_CTXT_REG_0_RESET }, 794186352Ssam { IX_NPEDL_ECS_DBG_CTXT_REG_1, IX_NPEDL_ECS_DBG_CTXT_REG_1_RESET }, 795186352Ssam { IX_NPEDL_ECS_DBG_CTXT_REG_2, IX_NPEDL_ECS_DBG_CTXT_REG_2_RESET }, 796186352Ssam { IX_NPEDL_ECS_INSTRUCT_REG, IX_NPEDL_ECS_INSTRUCT_REG_RESET } 797164426Ssam}; 798164426Ssam 799164426Ssam/* contains Reset values for Context Store Registers */ 800164426Ssamstatic const uint32_t ixNpeDlCtxtRegResetValues[] = { 801186352Ssam IX_NPEDL_CTXT_REG_RESET_STEVT, 802186352Ssam IX_NPEDL_CTXT_REG_RESET_STARTPC, 803186352Ssam IX_NPEDL_CTXT_REG_RESET_REGMAP, 804186352Ssam IX_NPEDL_CTXT_REG_RESET_CINDEX, 805164426Ssam}; 806164426Ssam 807164426Ssam#define IX_NPEDL_PARITY_BIT_MASK 0x3F00FFFF 808164426Ssam#define IX_NPEDL_CONFIG_CTRL_REG_MASK 0x3F3FFFFF 809164426Ssam 810194378Ssam#if 0 811194378Ssam/* 812194378Ssam * Reset the NPE and its coprocessor using the 813194378Ssam * fuse bits in the feature control register. 814194378Ssam */ 815194378Ssamstatic void 816194378Ssamnpe_reset(int npeid) 817194378Ssam{ 818194378Ssam uint32_t mask = EXP_FCTRL_NPEA << npeid; 819194378Ssam uint32_t v; 820194378Ssam 821194378Ssam v = ixp4xx_read_feature_bits(); 822194378Ssam ixp4xx_write_feature_bits(v &~ mask); 823194378Ssam /* un-fuse and un-reset the NPE & coprocessor */ 824194378Ssam ixp4xx_write_feature_bits(v | mask); 825194378Ssam} 826194378Ssam#endif 827194378Ssam 828164426Ssamstatic int 829164426Ssamnpe_cpu_reset(struct ixpnpe_softc *sc) 830164426Ssam{ 831164426Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 832186352Ssam uint32_t ctxtReg; /* identifies Context Store reg (0-3) */ 833186352Ssam uint32_t regAddr; 834186352Ssam uint32_t regVal; 835186352Ssam uint32_t ixNpeConfigCtrlRegVal; 836186352Ssam int i, error = 0; 837186352Ssam 838186352Ssam /* pre-store the NPE Config Control Register Value */ 839186352Ssam ixNpeConfigCtrlRegVal = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_CTL); 840186352Ssam ixNpeConfigCtrlRegVal |= 0x3F000000; 841164426Ssam 842186352Ssam /* disable the parity interrupt */ 843186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_CTL, 844186352Ssam (ixNpeConfigCtrlRegVal & IX_NPEDL_PARITY_BIT_MASK)); 845186352Ssam DPRINTFn(2, sc->sc_dev, "%s: dis parity int, CTL => 0x%x\n", 846186352Ssam __func__, ixNpeConfigCtrlRegVal & IX_NPEDL_PARITY_BIT_MASK); 847186352Ssam 848186352Ssam npe_cpu_step_save(sc); 849164426Ssam 850164426Ssam /* 851186352Ssam * Clear the FIFOs. 852164426Ssam */ 853186352Ssam while (npe_checkbits(sc, 854186352Ssam IX_NPEDL_REG_OFFSET_WFIFO, IX_NPEDL_MASK_WFIFO_VALID)) { 855186352Ssam /* read from the Watch-point FIFO until empty */ 856186352Ssam (void) npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WFIFO); 857164426Ssam } 858164426Ssam 859186352Ssam while (npe_checkbits(sc, 860186352Ssam IX_NPEDL_REG_OFFSET_STAT, IX_NPEDL_MASK_STAT_OFNE)) { 861186352Ssam /* read from the outFIFO until empty */ 862186352Ssam (void) npe_reg_read(sc, IX_NPEDL_REG_OFFSET_FIFO); 863186352Ssam } 864186352Ssam 865186352Ssam while (npe_checkbits(sc, 866186352Ssam IX_NPEDL_REG_OFFSET_STAT, IX_NPEDL_MASK_STAT_IFNE)) { 867186352Ssam /* 868186352Ssam * Step execution of the NPE intruction to read inFIFO using 869186352Ssam * the Debug Executing Context stack. 870186352Ssam */ 871186352Ssam error = npe_cpu_step(sc, IX_NPEDL_INSTR_RD_FIFO, 0, 0); 872186352Ssam if (error != 0) { 873186352Ssam DPRINTF(sc->sc_dev, "%s: cannot step (1), error %u\n", 874186352Ssam __func__, error); 875186352Ssam npe_cpu_step_restore(sc); 876186352Ssam return error; 877186352Ssam } 878186352Ssam } 879186352Ssam 880186352Ssam /* 881186352Ssam * Reset the mailbox reg 882186352Ssam */ 883186352Ssam /* ...from XScale side */ 884186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_MBST, IX_NPEDL_REG_RESET_MBST); 885186352Ssam /* ...from NPE side */ 886186352Ssam error = npe_cpu_step(sc, IX_NPEDL_INSTR_RESET_MBOX, 0, 0); 887164426Ssam if (error != 0) { 888186352Ssam DPRINTF(sc->sc_dev, "%s: cannot step (2), error %u\n", 889186352Ssam __func__, error); 890186352Ssam npe_cpu_step_restore(sc); 891186352Ssam return error; 892164426Ssam } 893164426Ssam 894186352Ssam /* 895186352Ssam * Reset the physical registers in the NPE register file: 896186352Ssam * Note: no need to save/restore REGMAP for Context 0 here 897186352Ssam * since all Context Store regs are reset in subsequent code. 898186352Ssam */ 899186352Ssam for (regAddr = 0; 900186352Ssam regAddr < IX_NPEDL_TOTAL_NUM_PHYS_REG && error == 0; 901186352Ssam regAddr++) { 902186352Ssam /* for each physical register in the NPE reg file, write 0 : */ 903186352Ssam error = npe_physical_reg_write(sc, regAddr, 0, TRUE); 904164426Ssam if (error != 0) { 905186352Ssam DPRINTF(sc->sc_dev, "%s: cannot write phy reg," 906186352Ssam "error %u\n", __func__, error); 907186352Ssam npe_cpu_step_restore(sc); 908186352Ssam return error; /* abort reset */ 909164426Ssam } 910164426Ssam } 911164426Ssam 912186352Ssam /* 913186352Ssam * Reset the context store: 914186352Ssam */ 915186352Ssam for (i = IX_NPEDL_CTXT_NUM_MIN; i <= IX_NPEDL_CTXT_NUM_MAX; i++) { 916186352Ssam /* set each context's Context Store registers to reset values */ 917186352Ssam for (ctxtReg = 0; ctxtReg < IX_NPEDL_CTXT_REG_MAX; ctxtReg++) { 918186352Ssam /* NOTE that there is no STEVT register for Context 0 */ 919186352Ssam if (i == 0 && ctxtReg == IX_NPEDL_CTXT_REG_STEVT) 920186352Ssam continue; 921186352Ssam regVal = ixNpeDlCtxtRegResetValues[ctxtReg]; 922186352Ssam error = npe_ctx_reg_write(sc, i, ctxtReg, 923186352Ssam regVal, TRUE); 924186352Ssam if (error != 0) { 925186352Ssam DPRINTF(sc->sc_dev, "%s: cannot write ctx reg," 926186352Ssam "error %u\n", __func__, error); 927186352Ssam npe_cpu_step_restore(sc); 928186352Ssam return error; /* abort reset */ 929186352Ssam } 930186352Ssam } 931186352Ssam } 932164426Ssam 933186352Ssam npe_cpu_step_restore(sc); 934164426Ssam 935186352Ssam /* write Reset values to Execution Context Stack registers */ 936186352Ssam for (i = 0; i < N(ixNpeDlEcsRegResetValues); i++) 937186352Ssam npe_ecs_reg_write(sc, 938186352Ssam ixNpeDlEcsRegResetValues[i].regAddr, 939186352Ssam ixNpeDlEcsRegResetValues[i].regResetVal); 940164426Ssam 941186352Ssam /* clear the profile counter */ 942186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_CLR_PROFILE_CNT); 943186352Ssam 944186352Ssam /* clear registers EXCT, AP0, AP1, AP2 and AP3 */ 945186352Ssam for (regAddr = IX_NPEDL_REG_OFFSET_EXCT; 946186352Ssam regAddr <= IX_NPEDL_REG_OFFSET_AP3; 947186352Ssam regAddr += sizeof(uint32_t)) 948186352Ssam npe_reg_write(sc, regAddr, 0); 949164426Ssam 950186352Ssam /* Reset the Watch-count register */ 951186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_WC, 0); 952194378Ssam#if 0 953186352Ssam /* 954186352Ssam * WR IXA00055043 - Remove IMEM Parity Introduced by NPE Reset Operation 955194378Ssam * XXX Removed because it breaks IXP435 operation; e.g. on Gateworks 956194378Ssam * XXX 2358 boards reseting NPE-A after NPE-C is running causes both 957194378Ssam * XXX npe's to stop working 958186352Ssam */ 959194378Ssam npe_reset(sc->sc_npeid); 960194378Ssam#endif 961186352Ssam /* 962186352Ssam * Call NpeMgr function to stop the NPE again after the Feature Control 963186352Ssam * has unfused and Un-Reset the NPE and its associated Coprocessors. 964186352Ssam */ 965186352Ssam error = npe_cpu_stop(sc); 966164426Ssam 967186352Ssam /* restore NPE configuration bus Control Register - Parity Settings */ 968186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_CTL, 969186352Ssam (ixNpeConfigCtrlRegVal & IX_NPEDL_CONFIG_CTRL_REG_MASK)); 970186352Ssam DPRINTFn(2, sc->sc_dev, "%s: restore CTL => 0x%x\n", 971186352Ssam __func__, npe_reg_read(sc, IX_NPEDL_REG_OFFSET_CTL)); 972186352Ssam 973186352Ssam return error; 974164426Ssam#undef N 975164426Ssam} 976164426Ssam 977164426Ssamstatic int 978164426Ssamnpe_cpu_start(struct ixpnpe_softc *sc) 979164426Ssam{ 980186352Ssam uint32_t ecsRegVal; 981164426Ssam 982186352Ssam /* 983186352Ssam * Ensure only Background Context Stack Level is Active by turning off 984186352Ssam * the Active bit in each of the other Executing Context Stack levels. 985186352Ssam */ 986186352Ssam ecsRegVal = npe_ecs_reg_read(sc, IX_NPEDL_ECS_PRI_1_CTXT_REG_0); 987186352Ssam ecsRegVal &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE; 988186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_PRI_1_CTXT_REG_0, ecsRegVal); 989164426Ssam 990186352Ssam ecsRegVal = npe_ecs_reg_read(sc, IX_NPEDL_ECS_PRI_2_CTXT_REG_0); 991186352Ssam ecsRegVal &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE; 992186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_PRI_2_CTXT_REG_0, ecsRegVal); 993164426Ssam 994186352Ssam ecsRegVal = npe_ecs_reg_read(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0); 995186352Ssam ecsRegVal &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE; 996186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0, ecsRegVal); 997186352Ssam 998186352Ssam /* clear the pipeline */ 999186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); 1000186352Ssam 1001186352Ssam /* start NPE execution by issuing cmd through EXCTL register on NPE */ 1002186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_START); 1003164426Ssam 1004186352Ssam /* 1005186352Ssam * Check execution status of NPE to verify operation was successful. 1006186352Ssam */ 1007186352Ssam return npe_checkbits(sc, 1008186352Ssam IX_NPEDL_REG_OFFSET_EXCTL, IX_NPEDL_EXCTL_STATUS_RUN) ? 0 : EIO; 1009164426Ssam} 1010164426Ssam 1011164426Ssamstatic int 1012164426Ssamnpe_cpu_stop(struct ixpnpe_softc *sc) 1013164426Ssam{ 1014186352Ssam /* stop NPE execution by issuing cmd through EXCTL register on NPE */ 1015186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_STOP); 1016164426Ssam 1017186352Ssam /* verify that NPE Stop was successful */ 1018186352Ssam return npe_checkbits(sc, 1019186352Ssam IX_NPEDL_REG_OFFSET_EXCTL, IX_NPEDL_EXCTL_STATUS_STOP) ? 0 : EIO; 1020164426Ssam} 1021164426Ssam 1022164426Ssam#define IX_NPEDL_REG_SIZE_BYTE 8 1023164426Ssam#define IX_NPEDL_REG_SIZE_SHORT 16 1024164426Ssam#define IX_NPEDL_REG_SIZE_WORD 32 1025164426Ssam 1026164426Ssam/* 1027164426Ssam * Introduce extra read cycles after issuing read command to NPE 1028164426Ssam * so that we read the register after the NPE has updated it 1029164426Ssam * This is to overcome race condition between XScale and NPE 1030164426Ssam */ 1031164426Ssam#define IX_NPEDL_DELAY_READ_CYCLES 2 1032164426Ssam/* 1033164426Ssam * To mask top three MSBs of 32bit word to download into NPE IMEM 1034164426Ssam */ 1035164426Ssam#define IX_NPEDL_MASK_UNUSED_IMEM_BITS 0x1FFFFFFF; 1036164426Ssam 1037164426Ssamstatic void 1038164426Ssamnpe_cmd_issue_write(struct ixpnpe_softc *sc, 1039164426Ssam uint32_t cmd, uint32_t addr, uint32_t data) 1040164426Ssam{ 1041186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXDATA, data); 1042186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXAD, addr); 1043186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCTL, cmd); 1044164426Ssam} 1045164426Ssam 1046164426Ssamstatic uint32_t 1047164426Ssamnpe_cmd_issue_read(struct ixpnpe_softc *sc, uint32_t cmd, uint32_t addr) 1048164426Ssam{ 1049186352Ssam uint32_t data; 1050186352Ssam int i; 1051164426Ssam 1052186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXAD, addr); 1053186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCTL, cmd); 1054186352Ssam for (i = 0; i <= IX_NPEDL_DELAY_READ_CYCLES; i++) 1055186352Ssam data = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_EXDATA); 1056186352Ssam return data; 1057164426Ssam} 1058164426Ssam 1059164426Ssamstatic int 1060164426Ssamnpe_ins_write(struct ixpnpe_softc *sc, uint32_t addr, uint32_t data, int verify) 1061164426Ssam{ 1062186352Ssam DPRINTFn(4, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, addr, data); 1063186352Ssam npe_cmd_issue_write(sc, IX_NPEDL_EXCTL_CMD_WR_INS_MEM, addr, data); 1064186352Ssam if (verify) { 1065186352Ssam uint32_t rdata; 1066164426Ssam 1067186352Ssam /* 1068186352Ssam * Write invalid data to this reg, so we can see if we're 1069186352Ssam * reading the EXDATA register too early. 1070186352Ssam */ 1071186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXDATA, ~data); 1072164426Ssam 1073186352Ssam /* 1074186352Ssam * Disabled since top 3 MSB are not used for Azusa 1075186352Ssam * hardware Refer WR:IXA00053900 1076186352Ssam */ 1077186352Ssam data &= IX_NPEDL_MASK_UNUSED_IMEM_BITS; 1078164426Ssam 1079186352Ssam rdata = npe_cmd_issue_read(sc, IX_NPEDL_EXCTL_CMD_RD_INS_MEM, 1080186352Ssam addr); 1081186352Ssam rdata &= IX_NPEDL_MASK_UNUSED_IMEM_BITS; 1082164426Ssam 1083186352Ssam if (data != rdata) 1084186352Ssam return EIO; 1085186352Ssam } 1086186352Ssam return 0; 1087164426Ssam} 1088164426Ssam 1089164426Ssamstatic int 1090164426Ssamnpe_data_write(struct ixpnpe_softc *sc, uint32_t addr, uint32_t data, int verify) 1091164426Ssam{ 1092186352Ssam DPRINTFn(4, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, addr, data); 1093186352Ssam npe_cmd_issue_write(sc, IX_NPEDL_EXCTL_CMD_WR_DATA_MEM, addr, data); 1094186352Ssam if (verify) { 1095186352Ssam /* 1096186352Ssam * Write invalid data to this reg, so we can see if we're 1097186352Ssam * reading the EXDATA register too early. 1098186352Ssam */ 1099186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXDATA, ~data); 1100186352Ssam if (data != npe_cmd_issue_read(sc, IX_NPEDL_EXCTL_CMD_RD_DATA_MEM, addr)) 1101186352Ssam return EIO; 1102186352Ssam } 1103186352Ssam return 0; 1104164426Ssam} 1105164426Ssam 1106164426Ssamstatic void 1107164426Ssamnpe_ecs_reg_write(struct ixpnpe_softc *sc, uint32_t reg, uint32_t data) 1108164426Ssam{ 1109186352Ssam npe_cmd_issue_write(sc, IX_NPEDL_EXCTL_CMD_WR_ECS_REG, reg, data); 1110164426Ssam} 1111164426Ssam 1112164426Ssamstatic uint32_t 1113164426Ssamnpe_ecs_reg_read(struct ixpnpe_softc *sc, uint32_t reg) 1114164426Ssam{ 1115186352Ssam return npe_cmd_issue_read(sc, IX_NPEDL_EXCTL_CMD_RD_ECS_REG, reg); 1116164426Ssam} 1117164426Ssam 1118164426Ssamstatic void 1119164426Ssamnpe_issue_cmd(struct ixpnpe_softc *sc, uint32_t command) 1120164426Ssam{ 1121186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCTL, command); 1122164426Ssam} 1123164426Ssam 1124164426Ssamstatic void 1125164426Ssamnpe_cpu_step_save(struct ixpnpe_softc *sc) 1126164426Ssam{ 1127186352Ssam /* turn off the halt bit by clearing Execution Count register. */ 1128186352Ssam /* save reg contents 1st and restore later */ 1129186352Ssam sc->savedExecCount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_EXCT); 1130186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCT, 0); 1131164426Ssam 1132186352Ssam /* ensure that IF and IE are on (temporarily), so that we don't end up 1133186352Ssam * stepping forever */ 1134186352Ssam sc->savedEcsDbgCtxtReg2 = npe_ecs_reg_read(sc, 1135186352Ssam IX_NPEDL_ECS_DBG_CTXT_REG_2); 1136164426Ssam 1137186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_2, 1138186352Ssam (sc->savedEcsDbgCtxtReg2 | IX_NPEDL_MASK_ECS_DBG_REG_2_IF | 1139186352Ssam IX_NPEDL_MASK_ECS_DBG_REG_2_IE)); 1140164426Ssam} 1141164426Ssam 1142164426Ssamstatic int 1143164426Ssamnpe_cpu_step(struct ixpnpe_softc *sc, uint32_t npeInstruction, 1144164426Ssam uint32_t ctxtNum, uint32_t ldur) 1145164426Ssam{ 1146164426Ssam#define IX_NPE_DL_MAX_NUM_OF_RETRIES 1000000 1147186352Ssam uint32_t ecsDbgRegVal; 1148186352Ssam uint32_t oldWatchcount, newWatchcount; 1149186352Ssam int tries; 1150164426Ssam 1151186352Ssam /* set the Active bit, and the LDUR, in the debug level */ 1152186352Ssam ecsDbgRegVal = IX_NPEDL_MASK_ECS_REG_0_ACTIVE | 1153186352Ssam (ldur << IX_NPEDL_OFFSET_ECS_REG_0_LDUR); 1154164426Ssam 1155186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0, ecsDbgRegVal); 1156164426Ssam 1157186352Ssam /* 1158186352Ssam * Set CCTXT at ECS DEBUG L3 to specify in which context to execute the 1159186352Ssam * instruction, and set SELCTXT at ECS DEBUG Level to specify which 1160186352Ssam * context store to access. 1161186352Ssam * Debug ECS Level Reg 1 has form 0x000n000n, where n = context number 1162186352Ssam */ 1163186352Ssam ecsDbgRegVal = (ctxtNum << IX_NPEDL_OFFSET_ECS_REG_1_CCTXT) | 1164186352Ssam (ctxtNum << IX_NPEDL_OFFSET_ECS_REG_1_SELCTXT); 1165164426Ssam 1166186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_1, ecsDbgRegVal); 1167164426Ssam 1168186352Ssam /* clear the pipeline */ 1169186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); 1170164426Ssam 1171186352Ssam /* load NPE instruction into the instruction register */ 1172186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_INSTRUCT_REG, npeInstruction); 1173164426Ssam 1174186352Ssam /* need this value later to wait for completion of NPE execution step */ 1175186352Ssam oldWatchcount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WC); 1176164426Ssam 1177186352Ssam /* issue a Step One command via the Execution Control register */ 1178186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_STEP); 1179164426Ssam 1180186352Ssam /* 1181186352Ssam * Force the XScale to wait until the NPE has finished execution step 1182186352Ssam * NOTE that this delay will be very small, just long enough to allow a 1183186352Ssam * single NPE instruction to complete execution; if instruction 1184186352Ssam * execution is not completed before timeout retries, exit the while 1185186352Ssam * loop. 1186186352Ssam */ 1187164426Ssam newWatchcount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WC); 1188186352Ssam for (tries = 0; tries < IX_NPE_DL_MAX_NUM_OF_RETRIES && 1189186352Ssam newWatchcount == oldWatchcount; tries++) { 1190186352Ssam /* Watch Count register incr's when NPE completes an inst */ 1191186352Ssam newWatchcount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WC); 1192186352Ssam } 1193186352Ssam return (tries < IX_NPE_DL_MAX_NUM_OF_RETRIES) ? 0 : EIO; 1194164426Ssam#undef IX_NPE_DL_MAX_NUM_OF_RETRIES 1195164426Ssam} 1196164426Ssam 1197164426Ssamstatic void 1198164426Ssamnpe_cpu_step_restore(struct ixpnpe_softc *sc) 1199164426Ssam{ 1200186352Ssam /* clear active bit in debug level */ 1201186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0, 0); 1202164426Ssam 1203186352Ssam /* clear the pipeline */ 1204186352Ssam npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); 1205164426Ssam 1206186352Ssam /* restore Execution Count register contents. */ 1207186352Ssam npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCT, sc->savedExecCount); 1208164426Ssam 1209186352Ssam /* restore IF and IE bits to original values */ 1210186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_2, sc->savedEcsDbgCtxtReg2); 1211164426Ssam} 1212164426Ssam 1213164426Ssamstatic int 1214164426Ssamnpe_logical_reg_read(struct ixpnpe_softc *sc, 1215164426Ssam uint32_t regAddr, uint32_t regSize, 1216164426Ssam uint32_t ctxtNum, uint32_t *regVal) 1217164426Ssam{ 1218186352Ssam uint32_t npeInstruction, mask; 1219186352Ssam int error; 1220164426Ssam 1221186352Ssam switch (regSize) { 1222186352Ssam case IX_NPEDL_REG_SIZE_BYTE: 1223186352Ssam npeInstruction = IX_NPEDL_INSTR_RD_REG_BYTE; 1224186352Ssam mask = 0xff; 1225186352Ssam break; 1226186352Ssam case IX_NPEDL_REG_SIZE_SHORT: 1227186352Ssam npeInstruction = IX_NPEDL_INSTR_RD_REG_SHORT; 1228186352Ssam mask = 0xffff; 1229186352Ssam break; 1230186352Ssam case IX_NPEDL_REG_SIZE_WORD: 1231186352Ssam npeInstruction = IX_NPEDL_INSTR_RD_REG_WORD; 1232186352Ssam mask = 0xffffffff; 1233186352Ssam break; 1234186352Ssam default: 1235186352Ssam return EINVAL; 1236186352Ssam } 1237164426Ssam 1238186352Ssam /* make regAddr be the SRC and DEST operands (e.g. movX d0, d0) */ 1239186352Ssam npeInstruction |= (regAddr << IX_NPEDL_OFFSET_INSTR_SRC) | 1240186352Ssam (regAddr << IX_NPEDL_OFFSET_INSTR_DEST); 1241164426Ssam 1242186352Ssam /* step execution of NPE inst using Debug Executing Context stack */ 1243186352Ssam error = npe_cpu_step(sc, npeInstruction, ctxtNum, 1244186352Ssam IX_NPEDL_RD_INSTR_LDUR); 1245186352Ssam if (error != 0) { 1246186352Ssam DPRINTF(sc->sc_dev, "%s(0x%x, %u, %u), cannot step, error %d\n", 1247186352Ssam __func__, regAddr, regSize, ctxtNum, error); 1248186352Ssam return error; 1249186352Ssam } 1250186352Ssam /* read value of register from Execution Data register */ 1251186352Ssam *regVal = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_EXDATA); 1252164426Ssam 1253186352Ssam /* align value from left to right */ 1254186352Ssam *regVal = (*regVal >> (IX_NPEDL_REG_SIZE_WORD - regSize)) & mask; 1255164426Ssam 1256186352Ssam return 0; 1257164426Ssam} 1258164426Ssam 1259164426Ssamstatic int 1260164426Ssamnpe_logical_reg_write(struct ixpnpe_softc *sc, uint32_t regAddr, uint32_t regVal, 1261164426Ssam uint32_t regSize, uint32_t ctxtNum, int verify) 1262164426Ssam{ 1263186352Ssam int error; 1264164426Ssam 1265186352Ssam DPRINTFn(4, sc->sc_dev, "%s(0x%x, 0x%x, %u, %u)\n", 1266186352Ssam __func__, regAddr, regVal, regSize, ctxtNum); 1267186352Ssam if (regSize == IX_NPEDL_REG_SIZE_WORD) { 1268186352Ssam /* 1269186352Ssam * NPE register addressing is left-to-right: e.g. |d0|d1|d2|d3| 1270186352Ssam * Write upper half-word (short) to |d0|d1| 1271186352Ssam */ 1272186352Ssam error = npe_logical_reg_write(sc, regAddr, 1273186352Ssam regVal >> IX_NPEDL_REG_SIZE_SHORT, 1274186352Ssam IX_NPEDL_REG_SIZE_SHORT, ctxtNum, verify); 1275186352Ssam if (error != 0) 1276186352Ssam return error; 1277164426Ssam 1278186352Ssam /* Write lower half-word (short) to |d2|d3| */ 1279186352Ssam error = npe_logical_reg_write(sc, 1280186352Ssam regAddr + sizeof(uint16_t), 1281186352Ssam regVal & 0xffff, 1282186352Ssam IX_NPEDL_REG_SIZE_SHORT, ctxtNum, verify); 1283186352Ssam } else { 1284186352Ssam uint32_t npeInstruction; 1285164426Ssam 1286186352Ssam switch (regSize) { 1287186352Ssam case IX_NPEDL_REG_SIZE_BYTE: 1288186352Ssam npeInstruction = IX_NPEDL_INSTR_WR_REG_BYTE; 1289186352Ssam regVal &= 0xff; 1290186352Ssam break; 1291186352Ssam case IX_NPEDL_REG_SIZE_SHORT: 1292186352Ssam npeInstruction = IX_NPEDL_INSTR_WR_REG_SHORT; 1293186352Ssam regVal &= 0xffff; 1294186352Ssam break; 1295186352Ssam default: 1296186352Ssam return EINVAL; 1297186352Ssam } 1298186352Ssam /* fill dest operand field of inst with dest reg addr */ 1299186352Ssam npeInstruction |= (regAddr << IX_NPEDL_OFFSET_INSTR_DEST); 1300164426Ssam 1301186352Ssam /* fill src operand field of inst with least-sig 5 bits of val*/ 1302186352Ssam npeInstruction |= 1303186352Ssam ((regVal & IX_NPEDL_MASK_IMMED_INSTR_SRC_DATA) << 1304186352Ssam IX_NPEDL_OFFSET_INSTR_SRC); 1305164426Ssam 1306186352Ssam /* fill coprocessor field of inst with most-sig 11 bits of val*/ 1307186352Ssam npeInstruction |= 1308186352Ssam ((regVal & IX_NPEDL_MASK_IMMED_INSTR_COPROC_DATA) << 1309186352Ssam IX_NPEDL_DISPLACE_IMMED_INSTR_COPROC_DATA); 1310164426Ssam 1311186352Ssam /* step execution of NPE intruction using Debug ECS */ 1312186352Ssam error = npe_cpu_step(sc, npeInstruction, 1313186352Ssam ctxtNum, IX_NPEDL_WR_INSTR_LDUR); 1314186352Ssam } 1315186352Ssam if (error != 0) { 1316186352Ssam DPRINTF(sc->sc_dev, "%s(0x%x, 0x%x, %u, %u), error %u " 1317186352Ssam "writing reg\n", __func__, regAddr, regVal, regSize, 1318186352Ssam ctxtNum, error); 1319186352Ssam return error; 1320186352Ssam } 1321186352Ssam if (verify) { 1322186352Ssam uint32_t retRegVal; 1323186352Ssam 1324186352Ssam error = npe_logical_reg_read(sc, regAddr, regSize, ctxtNum, 1325186352Ssam &retRegVal); 1326186352Ssam if (error == 0 && regVal != retRegVal) 1327186352Ssam error = EIO; /* XXX ambiguous */ 1328186352Ssam } 1329164426Ssam return error; 1330164426Ssam} 1331164426Ssam 1332164426Ssam/* 1333164426Ssam * There are 32 physical registers used in an NPE. These are 1334164426Ssam * treated as 16 pairs of 32-bit registers. To write one of the pair, 1335164426Ssam * write the pair number (0-16) to the REGMAP for Context 0. Then write 1336164426Ssam * the value to register 0 or 4 in the regfile, depending on which 1337164426Ssam * register of the pair is to be written 1338164426Ssam */ 1339164426Ssamstatic int 1340164426Ssamnpe_physical_reg_write(struct ixpnpe_softc *sc, 1341164426Ssam uint32_t regAddr, uint32_t regValue, int verify) 1342164426Ssam{ 1343186352Ssam int error; 1344164426Ssam 1345186352Ssam /* 1346186352Ssam * Set REGMAP for context 0 to (regAddr >> 1) to choose which pair 1347186352Ssam * (0-16) of physical registers to write . 1348186352Ssam */ 1349186352Ssam error = npe_logical_reg_write(sc, IX_NPEDL_CTXT_REG_ADDR_REGMAP, 1350186352Ssam (regAddr >> IX_NPEDL_OFFSET_PHYS_REG_ADDR_REGMAP), 1351186352Ssam IX_NPEDL_REG_SIZE_SHORT, 0, verify); 1352186352Ssam if (error == 0) { 1353186352Ssam /* regAddr = 0 or 4 */ 1354186352Ssam regAddr = (regAddr & IX_NPEDL_MASK_PHYS_REG_ADDR_LOGICAL_ADDR) * 1355186352Ssam sizeof(uint32_t); 1356186352Ssam error = npe_logical_reg_write(sc, regAddr, regValue, 1357186352Ssam IX_NPEDL_REG_SIZE_WORD, 0, verify); 1358186352Ssam } 1359186352Ssam return error; 1360164426Ssam} 1361164426Ssam 1362164426Ssamstatic int 1363164426Ssamnpe_ctx_reg_write(struct ixpnpe_softc *sc, uint32_t ctxtNum, 1364164426Ssam uint32_t ctxtReg, uint32_t ctxtRegVal, int verify) 1365164426Ssam{ 1366186352Ssam DPRINTFn(4, sc->sc_dev, "%s(%u, %u, %u)\n", 1367186352Ssam __func__, ctxtNum, ctxtReg, ctxtRegVal); 1368186352Ssam /* 1369186352Ssam * Context 0 has no STARTPC. Instead, this value is used to set 1370186352Ssam * NextPC for Background ECS, to set where NPE starts executing code 1371186352Ssam */ 1372186352Ssam if (ctxtNum == 0 && ctxtReg == IX_NPEDL_CTXT_REG_STARTPC) { 1373186352Ssam /* read BG_CTXT_REG_0, update NEXTPC bits, & write back to reg*/ 1374186352Ssam uint32_t v = npe_ecs_reg_read(sc, IX_NPEDL_ECS_BG_CTXT_REG_0); 1375186352Ssam v &= ~IX_NPEDL_MASK_ECS_REG_0_NEXTPC; 1376186352Ssam v |= (ctxtRegVal << IX_NPEDL_OFFSET_ECS_REG_0_NEXTPC) & 1377186352Ssam IX_NPEDL_MASK_ECS_REG_0_NEXTPC; 1378164426Ssam 1379186352Ssam npe_ecs_reg_write(sc, IX_NPEDL_ECS_BG_CTXT_REG_0, v); 1380186352Ssam return 0; 1381186352Ssam } else { 1382186352Ssam static const struct { 1383186352Ssam uint32_t regAddress; 1384186352Ssam uint32_t regSize; 1385186352Ssam } regAccInfo[IX_NPEDL_CTXT_REG_MAX] = { 1386186352Ssam { IX_NPEDL_CTXT_REG_ADDR_STEVT, 1387186352Ssam IX_NPEDL_REG_SIZE_BYTE }, 1388186352Ssam { IX_NPEDL_CTXT_REG_ADDR_STARTPC, 1389186352Ssam IX_NPEDL_REG_SIZE_SHORT }, 1390186352Ssam { IX_NPEDL_CTXT_REG_ADDR_REGMAP, 1391186352Ssam IX_NPEDL_REG_SIZE_SHORT }, 1392186352Ssam { IX_NPEDL_CTXT_REG_ADDR_CINDEX, 1393186352Ssam IX_NPEDL_REG_SIZE_BYTE } 1394186352Ssam }; 1395186352Ssam return npe_logical_reg_write(sc, regAccInfo[ctxtReg].regAddress, 1396186352Ssam ctxtRegVal, regAccInfo[ctxtReg].regSize, ctxtNum, verify); 1397186352Ssam } 1398164426Ssam} 1399164426Ssam 1400164426Ssam/* 1401164426Ssam * NPE Mailbox support. 1402164426Ssam */ 1403164426Ssam#define IX_NPEMH_MAXTRIES 100000 1404164426Ssam 1405164426Ssamstatic int 1406186352Ssamofifo_wait(struct ixpnpe_softc *sc) 1407164426Ssam{ 1408186352Ssam int i; 1409164426Ssam 1410186352Ssam for (i = 0; i < IX_NPEMH_MAXTRIES; i++) { 1411186352Ssam if (npe_reg_read(sc, IX_NPESTAT) & IX_NPESTAT_OFNE) 1412186352Ssam return 1; 1413186352Ssam DELAY(10); 1414186352Ssam } 1415186352Ssam device_printf(sc->sc_dev, "%s: timeout, last status 0x%x\n", 1416186352Ssam __func__, npe_reg_read(sc, IX_NPESTAT)); 1417186352Ssam return 0; 1418164426Ssam} 1419164426Ssam 1420186352Ssamstatic int 1421186352Ssamgetmsg(struct ixpnpe_softc *sc, uint32_t msg[2]) 1422186352Ssam{ 1423186352Ssam mtx_assert(&sc->sc_mtx, MA_OWNED); 1424186352Ssam 1425186352Ssam if (!ofifo_wait(sc)) 1426186352Ssam return EAGAIN; 1427186352Ssam msg[0] = npe_reg_read(sc, IX_NPEFIFO); 1428186352Ssam DPRINTF(sc->sc_dev, "%s: msg0 0x%x\n", __func__, msg[0]); 1429186352Ssam if (!ofifo_wait(sc)) 1430186352Ssam return EAGAIN; 1431186352Ssam msg[1] = npe_reg_read(sc, IX_NPEFIFO); 1432186352Ssam DPRINTF(sc->sc_dev, "%s: msg1 0x%x\n", __func__, msg[1]); 1433186352Ssam return 0; 1434186352Ssam} 1435186352Ssam 1436164426Ssamstatic void 1437164426Ssamixpnpe_intr(void *arg) 1438164426Ssam{ 1439186352Ssam struct ixpnpe_softc *sc = arg; 1440186352Ssam uint32_t status; 1441164426Ssam 1442186352Ssam mtx_lock(&sc->sc_mtx); 1443186352Ssam status = npe_reg_read(sc, IX_NPESTAT); 1444186352Ssam DPRINTF(sc->sc_dev, "%s: status 0x%x\n", __func__, status); 1445186352Ssam if ((status & IX_NPESTAT_OFINT) == 0) { 1446186352Ssam /* NB: should not happen */ 1447186352Ssam device_printf(sc->sc_dev, "%s: status 0x%x\n", 1448186352Ssam __func__, status); 1449186352Ssam /* XXX must silence interrupt? */ 1450186352Ssam mtx_unlock(&sc->sc_mtx); 1451186352Ssam return; 1452164426Ssam } 1453186352Ssam /* 1454186352Ssam * A message is waiting in the output FIFO, copy it so 1455186352Ssam * the interrupt will be silenced. 1456186352Ssam */ 1457186352Ssam if (getmsg(sc, sc->sc_msg) == 0) 1458186352Ssam sc->sc_msgwaiting = 1; 1459186352Ssam mtx_unlock(&sc->sc_mtx); 1460164426Ssam} 1461164426Ssam 1462164426Ssamstatic int 1463186352Ssamififo_wait(struct ixpnpe_softc *sc) 1464164426Ssam{ 1465186352Ssam int i; 1466164426Ssam 1467186352Ssam for (i = 0; i < IX_NPEMH_MAXTRIES; i++) { 1468186352Ssam if (npe_reg_read(sc, IX_NPESTAT) & IX_NPESTAT_IFNF) 1469186352Ssam return 1; 1470186352Ssam DELAY(10); 1471186352Ssam } 1472186352Ssam device_printf(sc->sc_dev, "%s: timeout, last status 0x%x\n", 1473186352Ssam __func__, npe_reg_read(sc, IX_NPESTAT)); 1474186352Ssam return 0; 1475164426Ssam} 1476164426Ssam 1477164426Ssamstatic int 1478186352Ssamputmsg(struct ixpnpe_softc *sc, const uint32_t msg[2]) 1479164426Ssam{ 1480186352Ssam mtx_assert(&sc->sc_mtx, MA_OWNED); 1481164426Ssam 1482186352Ssam DPRINTF(sc->sc_dev, "%s: msg 0x%x:0x%x\n", __func__, msg[0], msg[1]); 1483186352Ssam if (!ififo_wait(sc)) 1484186352Ssam return EIO; 1485164426Ssam npe_reg_write(sc, IX_NPEFIFO, msg[0]); 1486186352Ssam if (!ififo_wait(sc)) 1487186352Ssam return EIO; 1488186352Ssam npe_reg_write(sc, IX_NPEFIFO, msg[1]); 1489164426Ssam 1490186352Ssam return 0; 1491164426Ssam} 1492164426Ssam 1493186352Ssam/* 1494186352Ssam * Send a msg to the NPE and wait for a reply. We spin as 1495186352Ssam * we may be called early with interrupts not properly setup. 1496186352Ssam */ 1497186352Ssamint 1498186352Ssamixpnpe_sendandrecvmsg_sync(struct ixpnpe_softc *sc, 1499186352Ssam const uint32_t send[2], uint32_t recv[2]) 1500164426Ssam{ 1501186352Ssam int error; 1502164426Ssam 1503186352Ssam mtx_lock(&sc->sc_mtx); 1504186352Ssam error = putmsg(sc, send); 1505186352Ssam if (error == 0) 1506186352Ssam error = getmsg(sc, recv); 1507186352Ssam mtx_unlock(&sc->sc_mtx); 1508186352Ssam 1509186352Ssam return error; 1510164426Ssam} 1511164426Ssam 1512164426Ssam/* 1513186352Ssam * Send a msg to the NPE w/o waiting for a reply. 1514164426Ssam */ 1515164426Ssamint 1516186352Ssamixpnpe_sendmsg_async(struct ixpnpe_softc *sc, const uint32_t msg[2]) 1517164426Ssam{ 1518186352Ssam int error; 1519164426Ssam 1520186352Ssam mtx_lock(&sc->sc_mtx); 1521186352Ssam error = putmsg(sc, msg); 1522186352Ssam mtx_unlock(&sc->sc_mtx); 1523164426Ssam 1524186352Ssam return error; 1525164426Ssam} 1526164426Ssam 1527186352Ssamstatic int 1528186352Ssamrecvmsg_locked(struct ixpnpe_softc *sc, uint32_t msg[2]) 1529186352Ssam{ 1530186352Ssam mtx_assert(&sc->sc_mtx, MA_OWNED); 1531164426Ssam 1532186352Ssam DPRINTF(sc->sc_dev, "%s: msgwaiting %d\n", __func__, sc->sc_msgwaiting); 1533186352Ssam if (sc->sc_msgwaiting) { 1534186352Ssam msg[0] = sc->sc_msg[0]; 1535186352Ssam msg[1] = sc->sc_msg[1]; 1536186352Ssam sc->sc_msgwaiting = 0; 1537186352Ssam return 0; 1538186352Ssam } 1539186352Ssam return EAGAIN; 1540186352Ssam} 1541186352Ssam 1542186352Ssam/* 1543186352Ssam * Receive any msg previously received from the NPE. If nothing 1544186352Ssam * is available we return EAGAIN and the caller is required to 1545186352Ssam * do a synchronous receive or try again later. 1546186352Ssam */ 1547164426Ssamint 1548186352Ssamixpnpe_recvmsg_async(struct ixpnpe_softc *sc, uint32_t msg[2]) 1549164426Ssam{ 1550186352Ssam int error; 1551164426Ssam 1552186352Ssam mtx_lock(&sc->sc_mtx); 1553186352Ssam error = recvmsg_locked(sc, msg); 1554186352Ssam mtx_unlock(&sc->sc_mtx); 1555164426Ssam 1556186352Ssam return error; 1557164426Ssam} 1558164426Ssam 1559186352Ssam/* 1560186352Ssam * Receive a msg from the NPE. If one was received asynchronously 1561186352Ssam * then it's returned; otherwise we poll synchronously. 1562186352Ssam */ 1563164426Ssamint 1564186352Ssamixpnpe_recvmsg_sync(struct ixpnpe_softc *sc, uint32_t msg[2]) 1565164426Ssam{ 1566186352Ssam int error; 1567164426Ssam 1568186352Ssam mtx_lock(&sc->sc_mtx); 1569186352Ssam error = recvmsg_locked(sc, msg); 1570186352Ssam if (error == EAGAIN) 1571186352Ssam error = getmsg(sc, msg); 1572186352Ssam mtx_unlock(&sc->sc_mtx); 1573164426Ssam 1574186352Ssam return error; 1575164426Ssam} 1576