From 56fc78090d103bec8ba76f44bad8386a7aca2a2e Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Wed, 3 Jul 2013 10:16:30 +0200 Subject: GRSPW_PKT: Add support for Interrupt-codes Update: Daniel Hellstrom updated SpW-IRQ implementation accoring to changes in hardware register layout and features. --- c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c | 309 ++++++++++++++++++++++++-- 1 file changed, 292 insertions(+), 17 deletions(-) (limited to 'c/src/lib/libbsp/sparc/shared/spw') diff --git a/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c b/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c index 1696c3c02b..23d9d9cccb 100644 --- a/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c +++ b/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c @@ -87,6 +87,19 @@ struct grspw_regs { * up to 4 channels are supported */ struct grspw_dma_regs dma[4]; + + volatile unsigned int icctrl; + volatile unsigned int icrx; + volatile unsigned int icack; + volatile unsigned int ictimeout; + volatile unsigned int ictickomask; + volatile unsigned int icaamask; + volatile unsigned int icrlpresc; + volatile unsigned int icrlisr; + volatile unsigned int icrlintack; + volatile unsigned int resv2; + volatile unsigned int icisr; + volatile unsigned int resv3; }; /* GRSPW - Control Register - 0x00 */ @@ -95,10 +108,13 @@ struct grspw_regs { #define GRSPW_CTRL_RC_BIT 29 #define GRSPW_CTRL_NCH_BIT 27 #define GRSPW_CTRL_PO_BIT 26 +#define GRSPW_CTRL_ID_BIT 24 +#define GRSPW_CTRL_LE_BIT 22 #define GRSPW_CTRL_PS_BIT 21 #define GRSPW_CTRL_NP_BIT 20 #define GRSPW_CTRL_RD_BIT 17 #define GRSPW_CTRL_RE_BIT 16 +#define GRSPW_CTRL_TF_BIT 12 #define GRSPW_CTRL_TR_BIT 11 #define GRSPW_CTRL_TT_BIT 10 #define GRSPW_CTRL_LI_BIT 9 @@ -116,10 +132,13 @@ struct grspw_regs { #define GRSPW_CTRL_RC (1<tcisr = NULL; priv->tcisr_arg = NULL; + priv->icisr = NULL; + priv->icisr_arg = NULL; grspw_stats_clr(priv); @@ -664,6 +727,14 @@ spw_link_state_t grspw_link_state(void *d) return (status & GRSPW_STS_LS) >> GRSPW_STS_LS_BIT; } +/* Enable Global IRQ only if some irq source is set */ +static inline int grspw_is_irqsource_set(unsigned int ctrl, unsigned int icctrl) +{ + return (ctrl & GRSPW_CTRL_IRQSRC_MASK) || + (icctrl & GRSPW_ICCTRL_IRQSRC_MASK); +} + + /* options and clkdiv [in/out]: set to -1 to only read current config */ void grspw_link_ctrl(void *d, int *options, int *clkdiv) { @@ -685,8 +756,8 @@ void grspw_link_ctrl(void *d, int *options, int *clkdiv) ctrl = (ctrl & ~GRSPW_LINK_CFG) | (*options & GRSPW_LINK_CFG); - /* Enable Global IRQ only of LI or TQ is set */ - if (ctrl & (GRSPW_CTRL_LI|GRSPW_CTRL_TQ)) + /* Enable Global IRQ only if some irq source is set */ + if (grspw_is_irqsource_set(ctrl, REG_READ(®s->icctrl))) ctrl |= GRSPW_CTRL_IE; else ctrl &= ~GRSPW_CTRL_IE; @@ -728,10 +799,10 @@ void grspw_tc_ctrl(void *d, int *options) ctrl &= ~(GRSPW_CTRL_TR|GRSPW_CTRL_TT|GRSPW_CTRL_TQ); ctrl |= (*options & 0xd) << GRSPW_CTRL_TQ_BIT; - /* Enable Global IRQ only of LI or TQ is set */ - if (ctrl & (GRSPW_CTRL_LI|GRSPW_CTRL_TQ)) + /* Enable Global IRQ only if some irq source is set */ + if (grspw_is_irqsource_set(ctrl, REG_READ(®s->icctrl))) ctrl |= GRSPW_CTRL_IE; - else + else ctrl &= ~GRSPW_CTRL_IE; REG_WRITE(®s->ctrl, ctrl); @@ -755,15 +826,158 @@ void grspw_tc_isr(void *d, void (*tcisr)(void *data, int tc), void *data) * TIMECNT = bits 5 to 0 */ void grspw_tc_time(void *d, int *time) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + + if (time == NULL) + return; + if (*time != -1) + REG_WRITE(®s->time, *time & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL)); + *time = REG_READ(®s->time) & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL); +} + +/* Generate Tick-In for the given Interrupt-code and check for generation + * error. + * + * Returns zero on success and non-zero on failure + */ +int grspw_ic_tickin(void *d, int ic) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + IRQFLAGS_TYPE irqflags; + unsigned int icctrl, mask; + + /* Prepare before turning off IRQ */ + mask = 0x3f << GRSPW_ICCTRL_TXIRQ_BIT; + ic = ((ic << GRSPW_ICCTRL_TXIRQ_BIT) & mask) | + GRSPW_ICCTRL_II | GRSPW_ICCTRL_ID; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + icctrl = REG_READ(®s->icctrl); + icctrl &= ~mask; + icctrl |= ic; + REG_WRITE(®s->icctrl, icctrl); /* Generate SpW Interrupt Tick-In */ + /* the ID bit is valid after two clocks, so we not to wait here */ + icctrl = REG_READ(®s->icctrl); /* Check SpW-Int generation error */ + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + + return icctrl & GRSPW_ICCTRL_ID; +} + +#define ICOPTS_CTRL_MASK ICOPTS_EN_FLAGFILTER +#define ICOPTS_ICCTRL_MASK \ + (ICOPTS_INTNUM | ICOPTS_EN_SPWIRQ_ON_EE | ICOPTS_EN_SPWIRQ_ON_IA | \ + ICOPTS_EN_PRIO | ICOPTS_EN_TIMEOUTIRQ | ICOPTS_EN_ACKIRQ | \ + ICOPTS_EN_TICKOUTIRQ | ICOPTS_EN_RX | ICOPTS_EN_TX | \ + ICOPTS_BASEIRQ) + +/* Control Interrupt-code settings of core + * Write if not pointing to -1, always read current value + * + * TODO: A lot of code duplication with grspw_tc_ctrl + */ +void grspw_ic_ctrl(void *d, unsigned int *options) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + unsigned int icctrl; + IRQFLAGS_TYPE irqflags; + + if (options == NULL) + return; + + if (*options != -1) { + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + ctrl = REG_READ(®s->ctrl); + ctrl &= ~GRSPW_CTRL_TF; /* Depends on one to one relation between + * irqopts bits and ctrl bits */ + ctrl |= (*options & ICOPTS_CTRL_MASK) << + (GRSPW_CTRL_TF_BIT - 0); + + icctrl = REG_READ(®s->icctrl); + icctrl &= ~ICOPTS_ICCTRL_MASK; /* Depends on one to one relation between + * irqopts bits and icctrl bits */ + icctrl |= *options & ICOPTS_ICCTRL_MASK; + + /* Enable Global IRQ only if some irq source is set */ + if (grspw_is_irqsource_set(ctrl, icctrl)) + ctrl |= GRSPW_CTRL_IE; + else + ctrl &= ~GRSPW_CTRL_IE; + + REG_WRITE(®s->ctrl, ctrl); + REG_WRITE(®s->icctrl, icctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } + *options = ((REG_READ(®s->ctrl) & ICOPTS_CTRL_MASK) | + (REG_READ(®s->icctrl) & ICOPTS_ICCTRL_MASK)); +} + +void grspw_ic_config(void *d, int rw, struct spwpkt_ic_config *cfg) { struct grspw_priv *priv = d; struct grspw_regs *regs = priv->regs; - if (time == NULL) + if (!cfg) return; - if (*time != -1) - REG_WRITE(®s->time, *time & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL)); - *time = REG_READ(®s->time) & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL); + + if (rw & 1) { + REG_WRITE(®s->ictickomask, cfg->tomask); + REG_WRITE(®s->icaamask, cfg->aamask); + REG_WRITE(®s->icrlpresc, cfg->scaler); + REG_WRITE(®s->icrlisr, cfg->isr_reload); + REG_WRITE(®s->icrlintack, cfg->ack_reload); + } + if (rw & 2) { + cfg->tomask = REG_READ(®s->ictickomask); + cfg->aamask = REG_READ(®s->icaamask); + cfg->scaler = REG_READ(®s->icrlpresc); + cfg->isr_reload = REG_READ(®s->icrlisr); + cfg->ack_reload = REG_READ(®s->icrlintack); + } +} + +/* Read or Write Interrupt-code status registers */ +void grspw_ic_sts(void *d, unsigned int *rxirq, unsigned int *rxack, unsigned int *intto) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + + /* No locking needed since the status bits are clear-on-write */ + + if (rxirq) { + if (*rxirq != 0) + REG_WRITE(®s->icrx, *rxirq); + else + *rxirq = REG_READ(®s->icrx); + } + + if (rxack) { + if (*rxack != 0) + REG_WRITE(®s->icack, *rxack); + else + *rxack = REG_READ(®s->icack); + } + + if (intto) { + if (*intto != 0) + REG_WRITE(®s->ictimeout, *intto); + else + *intto = REG_READ(®s->ictimeout); + } +} + +/* Assign handler function to Interrupt-code tick out IRQ */ +void grspw_ic_isr(void *d, spwpkt_ic_isr_t handler, void *data) +{ + struct grspw_priv *priv = d; + + priv->icisr_arg = data; + priv->icisr = handler; } /* Set (not -1) and/or read RMAP options. */ @@ -2253,8 +2467,9 @@ void grspw_work_func(rtems_task_argument unused) STATIC void grspw_isr(void *data) { struct grspw_priv *priv = data; - unsigned int dma_stat, stat, stat_clrmsk, ctrl, timecode; - int i, handled = 0, message = WORK_NONE; + unsigned int dma_stat, stat, stat_clrmsk, ctrl, icctrl, timecode; + unsigned int rxirq, rxack, intto; + int i, handled = 0, message = WORK_NONE, call_user_int_isr; #ifdef RTEMS_HAS_SMP IRQFLAGS_TYPE irqflags; #endif @@ -2267,9 +2482,40 @@ STATIC void grspw_isr(void *data) * smallest possible interrupt latency */ if ((stat & GRSPW_STS_TO) && (priv->tcisr != NULL)) { - /* Timecode received. Let custom function handle this */ - timecode = priv->regs->time; - (priv->tcisr)(priv->tcisr_arg, timecode); + ctrl = REG_READ(&priv->regs->ctrl); + if (ctrl & GRSPW_CTRL_TQ) { + /* Timecode received. Let custom function handle this */ + timecode = REG_READ(&priv->regs->time) & + (GRSPW_TIME_CTRL | GRSPW_TIME_TCNT); + (priv->tcisr)(priv->tcisr_arg, timecode); + } + } + + /* Get Interrupt status from hardware */ + icctrl = REG_READ(&priv->regs->icctrl); + if ((icctrl & GRSPW_ICCTRL_IRQSRC_MASK) && (priv->icisr != NULL)) { + call_user_int_isr = 0; + rxirq = rxack = intto = 0; + + if ((icctrl & GRSPW_ICCTRL_IQ) && + (rxirq = REG_READ(&priv->regs->icrx)) != 0) + call_user_int_isr = 1; + + if ((icctrl & GRSPW_ICCTRL_AQ) && + (rxack = REG_READ(&priv->regs->icack)) != 0) + call_user_int_isr = 1; + + if ((icctrl & GRSPW_ICCTRL_TQ) && + (intto = REG_READ(&priv->regs->ictimeout)) != 0) + call_user_int_isr = 1; + + /* Let custom functions handle this POTENTIAL SPW interrupt. The + * user function is called even if no such IRQ has happened! + * User must make sure to clear all interrupts that have been + * handled from the three registers by writing a one. + */ + if (call_user_int_isr) + priv->icisr(priv->icisr_arg, rxirq, rxack, intto); } /* An Error occured? */ @@ -2421,12 +2667,21 @@ STATIC void grspw_hw_stop(struct grspw_priv *priv) STATIC void grspw_hw_softreset(struct grspw_priv *priv) { int i; + unsigned int tmp; for (i=0; ihwsup.ndma_chans; i++) grspw_hw_dma_softreset(&priv->dma[i]); REG_WRITE(&priv->regs->status, 0xffffffff); REG_WRITE(&priv->regs->time, 0); + /* Clear all but valuable reset values of ICCTRL */ + tmp = REG_READ(&priv->regs->icctrl); + tmp &= GRSPW_ICCTRL_INUM | GRSPW_ICCTRL_BIRQ | GRSPW_ICCTRL_TXIRQ; + tmp |= GRSPW_ICCTRL_ID; + REG_WRITE(&priv->regs->icctrl, tmp); + REG_WRITE(&priv->regs->icrx, 0xffffffff); + REG_WRITE(&priv->regs->icack, 0xffffffff); + REG_WRITE(&priv->regs->ictimeout, 0xffffffff); } int grspw_dev_count(void) @@ -2503,7 +2758,7 @@ static int grspw2_init3(struct drvmgr_dev *dev) struct amba_dev_info *ambadev; struct ambapp_core *pnpinfo; int i, size; - unsigned int ctrl; + unsigned int ctrl, icctrl, numi; union drvmgr_key_value *value; GRSPW_DBG("GRSPW[%d] on bus %s\n", dev->minor_drv, @@ -2538,6 +2793,13 @@ static int grspw2_init3(struct drvmgr_dev *dev) priv->hwsup.rx_unalign = (ctrl & GRSPW_CTRL_RX) >> GRSPW_CTRL_RX_BIT; priv->hwsup.nports = 1 + ((ctrl & GRSPW_CTRL_PO) >> GRSPW_CTRL_PO_BIT); priv->hwsup.ndma_chans = 1 + ((ctrl & GRSPW_CTRL_NCH) >> GRSPW_CTRL_NCH_BIT); + priv->hwsup.irq = ((ctrl & GRSPW_CTRL_ID) >> GRSPW_CTRL_ID_BIT); + icctrl = REG_READ(&priv->regs->icctrl); + numi = (icctrl & GRSPW_ICCTRL_NUMI) >> GRSPW_ICCTRL_NUMI_BIT; + if (numi > 0) + priv->hwsup.irq_num = 1 << (numi - 1); + else + priv->hwsup.irq_num = 0; /* Construct hardware version identification */ priv->hwsup.hw_version = pnpinfo->device << 16 | pnpinfo->apb_slv->ver; @@ -2552,6 +2814,19 @@ static int grspw2_init3(struct drvmgr_dev *dev) priv->hwsup.strip_pid = 0; } + /* Probe width of SpaceWire Interrupt ISR timers. All have the same + * width... so only the first is probed, if no timer result will be + * zero. + */ + REG_WRITE(&priv->regs->icrlpresc, 0x7fffffff); + ctrl = REG_READ(&priv->regs->icrlpresc); + REG_WRITE(&priv->regs->icrlpresc, 0); + priv->hwsup.itmr_width = 0; + while (ctrl & 1) { + priv->hwsup.itmr_width++; + ctrl = ctrl >> 1; + } + /* Let user limit the number of DMA channels on this core to save * space. Only the first nDMA channels will be available. */ -- cgit v1.2.3