// SPDX-License-Identifier: GPL-2.0-only /* * Intel IXP4xx Network Processor Engine driver for Linux * * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> * * The code is based on publicly available information: * - Intel IXP4xx Developer's Manual and other e-papers * - Intel IXP400 Access Library Software (BSD license) * - previous works by Christian Hohnstaedt <chohnstaedt@innominate.com> * Thanks, Christian.
*/
static u32 npe_cmd_read(struct npe *npe, u32 addr, int cmd)
{
__raw_writel(addr, &npe->regs->exec_addr);
__raw_writel(cmd, &npe->regs->exec_status_cmd); /* Iintroduce extra read cycles after issuing read command to NPE so that we read the register after the NPE has updated it.
This is to overcome race condition between XScale and NPE */
__raw_readl(&npe->regs->exec_data);
__raw_readl(&npe->regs->exec_data); return __raw_readl(&npe->regs->exec_data);
}
staticvoid npe_clear_active(struct npe *npe, u32 reg)
{
u32 val = npe_cmd_read(npe, reg, CMD_RD_ECS_REG);
npe_cmd_write(npe, reg, CMD_WR_ECS_REG, val & ~ECS_REG_0_ACTIVE);
}
staticvoid npe_start(struct npe *npe)
{ /* ensure only Background Context Stack Level is active */
npe_clear_active(npe, ECS_PRI_1_CTXT_REG_0);
npe_clear_active(npe, ECS_PRI_2_CTXT_REG_0);
npe_clear_active(npe, ECS_DBG_CTXT_REG_0);
/* set the Active bit, and the LDUR, in the debug level */
npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG,
ECS_REG_0_ACTIVE | (ldur << ECS_REG_0_LDUR_BITS));
/* set CCTXT at ECS DEBUG L3 to specify in which context to execute the instruction, and set SELCTXT at ECS DEBUG Level to specify which context store to access. Debug ECS Level Reg 1 has form 0x000n000n, where n = context number
*/
npe_cmd_write(npe, ECS_DBG_CTXT_REG_1, CMD_WR_ECS_REG,
(ctx << ECS_REG_1_CCTXT_BITS) |
(ctx << ECS_REG_1_SELCTXT_BITS));
/* clear the pipeline */
__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
/* load NPE instruction into the instruction register */
npe_cmd_write(npe, ECS_INSTRUCT_REG, CMD_WR_ECS_REG, instr);
/* we need this value later to wait for completion of NPE execution
step */
wc = __raw_readl(&npe->regs->watch_count);
/* issue a Step One command via the Execution Control register */
__raw_writel(CMD_NPE_STEP, &npe->regs->exec_status_cmd);
/* Watch Count register increments when NPE completes an instruction */ for (i = 0; i < MAX_RETRIES; i++) { if (wc != __raw_readl(&npe->regs->watch_count)) return 0;
udelay(1);
}
staticint __must_check npe_logical_reg_write8(struct npe *npe, u32 addr,
u8 val, u32 ctx)
{ /* here we build the NPE assembler instruction: mov8 d0, #0 */
u32 instr = INSTR_WR_REG_BYTE | /* OpCode */
addr << 9 | /* base Operand */
(val & 0x1F) << 4 | /* lower 5 bits to immediate data */
(val & ~0x1F) << (18 - 5);/* higher 3 bits to CoProc instr. */ return npe_debug_instr(npe, instr, ctx, 1); /* execute it */
}
staticint __must_check npe_logical_reg_write16(struct npe *npe, u32 addr,
u16 val, u32 ctx)
{ /* here we build the NPE assembler instruction: mov16 d0, #0 */
u32 instr = INSTR_WR_REG_SHORT | /* OpCode */
addr << 9 | /* base Operand */
(val & 0x1F) << 4 | /* lower 5 bits to immediate data */
(val & ~0x1F) << (18 - 5);/* higher 11 bits to CoProc instr. */ return npe_debug_instr(npe, instr, ctx, 1); /* execute it */
}
staticint __must_check npe_logical_reg_write32(struct npe *npe, u32 addr,
u32 val, u32 ctx)
{ /* write in 16 bit steps first the high and then the low value */ if (npe_logical_reg_write16(npe, addr, val >> 16, ctx)) return -ETIMEDOUT; return npe_logical_reg_write16(npe, addr + 2, val & 0xFFFF, ctx);
}
/* pre exec - debug instruction */ /* turn off the halt bit by clearing Execution Count register. */
exec_count = __raw_readl(&npe->regs->exec_count);
__raw_writel(0, &npe->regs->exec_count); /* ensure that IF and IE are on (temporarily), so that we don't end up
stepping forever */
ctx_reg2 = npe_cmd_read(npe, ECS_DBG_CTXT_REG_2, CMD_RD_ECS_REG);
npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2 |
ECS_DBG_REG_2_IF | ECS_DBG_REG_2_IE);
/* clear the FIFOs */ while (__raw_readl(&npe->regs->watchpoint_fifo) & WFIFO_VALID)
; while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) /* read from the outFIFO until empty */
print_npe(KERN_DEBUG, npe, "npe_reset: read FIFO = 0x%X\n",
__raw_readl(&npe->regs->in_out_fifo));
while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) /* step execution of the NPE intruction to read inFIFO using
the Debug Executing Context stack */ if (npe_debug_instr(npe, INSTR_RD_FIFO, 0, 0)) return -ETIMEDOUT;
/* reset the mailbox reg from the XScale side */
__raw_writel(RESET_MBOX_STAT, &npe->regs->mailbox_status); /* from NPE side */ if (npe_debug_instr(npe, INSTR_RESET_MBOX, 0, 0)) return -ETIMEDOUT;
/* Reset the physical registers in the NPE register file */ for (val = 0; val < NPE_PHYS_REG; val++) { if (npe_logical_reg_write16(npe, NPE_REGMAP, val >> 1, 0)) return -ETIMEDOUT; /* address is either 0 or 4 */ if (npe_logical_reg_write32(npe, (val & 1) * 4, 0, 0)) return -ETIMEDOUT;
}
/* Reset the context store = each context's Context Store registers */
/* Context 0 has no STARTPC. Instead, this value is used to set NextPC
for Background ECS, to set where NPE starts executing code */
val = npe_cmd_read(npe, ECS_BG_CTXT_REG_0, CMD_RD_ECS_REG);
val &= ~ECS_REG_0_NEXTPC_MASK;
val |= (0 /* NextPC */ << 16) & ECS_REG_0_NEXTPC_MASK;
npe_cmd_write(npe, ECS_BG_CTXT_REG_0, CMD_WR_ECS_REG, val);
for (i = 0; i < 16; i++) { if (i) { /* Context 0 has no STEVT nor STARTPC */ /* STEVT = off, 0x80 */ if (npe_logical_reg_write8(npe, NPE_STEVT, 0x80, i)) return -ETIMEDOUT; if (npe_logical_reg_write16(npe, NPE_STARTPC, 0, i)) return -ETIMEDOUT;
} /* REGMAP = d0->p0, d8->p2, d16->p4 */ if (npe_logical_reg_write16(npe, NPE_REGMAP, 0x820, i)) return -ETIMEDOUT; if (npe_logical_reg_write8(npe, NPE_CINDEX, 0, i)) return -ETIMEDOUT;
}
/* post exec */ /* clear active bit in debug level */
npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, 0); /* clear the pipeline */
__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); /* restore previous values */
__raw_writel(exec_count, &npe->regs->exec_count);
npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2);
/* write reset values to Execution Context Stack registers */ for (val = 0; val < ARRAY_SIZE(ecs_reset); val++)
npe_cmd_write(npe, ecs_reset[val].reg, CMD_WR_ECS_REG,
ecs_reset[val].val);
/* clear the profile counter */
__raw_writel(CMD_CLR_PROFILE_CNT, &npe->regs->exec_status_cmd);
/* * We need to work on cached values here because the register * will read inverted but needs to be written non-inverted.
*/
val = cpu_ixp4xx_features(npe->rmap); /* reset the NPE */
regmap_write(npe->rmap, IXP4XX_EXP_CNFG2, val & ~reset_bit); /* deassert reset */
regmap_write(npe->rmap, IXP4XX_EXP_CNFG2, val | reset_bit);
for (i = 0; i < MAX_RETRIES; i++) {
val = cpu_ixp4xx_features(npe->rmap); if (val & reset_bit) break; /* NPE is back alive */
udelay(1);
} if (i == MAX_RETRIES) return -ETIMEDOUT;
npe_stop(npe);
/* restore NPE configuration bus Control Register - parity settings */
__raw_writel(ctl, &npe->regs->messaging_control); return 0;
}
int npe_send_message(struct npe *npe, constvoid *msg, constchar *what)
{ const u32 *send = msg; int cycles = 0;
debug_msg(npe, "Trying to send message %s [%08X:%08X]\n",
what, send[0], send[1]);
if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) {
debug_msg(npe, "NPE input FIFO not empty\n"); return -EIO;
}
staticint ixp4xx_npe_probe(struct platform_device *pdev)
{ int i, found = 0; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct resource *res; struct regmap *rmap;
u32 val;
/* This system has only one syscon, so fetch it */
rmap = syscon_regmap_lookup_by_compatible("syscon"); if (IS_ERR(rmap)) return dev_err_probe(dev, PTR_ERR(rmap), "failed to look up syscon\n");
for (i = 0; i < NPE_COUNT; i++) { struct npe *npe = &npe_tab[i];
res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (!res) return -ENODEV;
val = cpu_ixp4xx_features(rmap);
if (!(val & (IXP4XX_FEATURE_RESET_NPEA << i))) {
dev_info(dev, "NPE%d at %pR not available\n",
i, res); continue; /* NPE already disabled or not present */
}
npe->regs = devm_ioremap_resource(dev, res); if (IS_ERR(npe->regs)) return PTR_ERR(npe->regs);
npe->rmap = rmap;
if (npe_reset(npe)) {
dev_info(dev, "NPE%d at %pR does not reset\n",
i, res); continue;
}
npe->valid = 1;
dev_info(dev, "NPE%d at %pR registered\n", i, res);
found++;
}
if (!found) return -ENODEV;
/* Spawn crypto subdevice if using device tree */ if (IS_ENABLED(CONFIG_OF) && np)
devm_of_platform_populate(dev);
return 0;
}
staticvoid ixp4xx_npe_remove(struct platform_device *pdev)
{ int i;
for (i = 0; i < NPE_COUNT; i++) if (npe_tab[i].regs) {
npe_reset(&npe_tab[i]);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.