diff options
author | Kim Phillips <kim.phillips@freescale.com> | 2011-11-21 08:13:27 (GMT) |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2011-11-21 08:21:51 (GMT) |
commit | c3e337f88a5b3784cb3c806ffd650d06adff1ea5 (patch) | |
tree | 12ca78132c4ccc74257bf2ad634e1c06a062d2bd /drivers/crypto/talitos.c | |
parent | ad42d5fc85383278663ecb58a24f6547ad0ba735 (diff) | |
download | linux-c3e337f88a5b3784cb3c806ffd650d06adff1ea5.tar.xz |
crypto: talitos - support for channel remap and 2nd IRQ
Some later SEC v3.x are equipped with a second IRQ line.
By correctly assigning IRQ affinity, this feature can be
used to increase performance on dual core parts, like the
MPC8572E and P2020E.
The existence of the 2nd IRQ is determined from the device
node's interrupt property. If present, the driver remaps
two of four channels, which in turn makes those channels
trigger their interrupts on the 2nd line instead of the first.
To handle single- and dual-IRQ combinations efficiently,
talitos gets two new interrupt handlers and back-half workers.
[includes a fix to MCR_LO's address.]
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/talitos.c')
-rw-r--r-- | drivers/crypto/talitos.c | 207 |
1 files changed, 138 insertions, 69 deletions
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 7f82e91..92c0ca7 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -122,7 +122,7 @@ struct talitos_private { struct device *dev; struct platform_device *ofdev; void __iomem *reg; - int irq; + int irq[2]; /* SEC version geometry (from device tree node) */ unsigned int num_channels; @@ -146,7 +146,7 @@ struct talitos_private { atomic_t last_chan ____cacheline_aligned; /* request callback tasklet */ - struct tasklet_struct done_task; + struct tasklet_struct done_task[2]; /* list of registered algorithms */ struct list_head alg_list; @@ -226,13 +226,19 @@ static int reset_device(struct device *dev) { struct talitos_private *priv = dev_get_drvdata(dev); unsigned int timeout = TALITOS_TIMEOUT; + u32 mcr = TALITOS_MCR_SWR; - setbits32(priv->reg + TALITOS_MCR, TALITOS_MCR_SWR); + setbits32(priv->reg + TALITOS_MCR, mcr); while ((in_be32(priv->reg + TALITOS_MCR) & TALITOS_MCR_SWR) && --timeout) cpu_relax(); + if (priv->irq[1] != NO_IRQ) { + mcr = TALITOS_MCR_RCA1 | TALITOS_MCR_RCA3; + setbits32(priv->reg + TALITOS_MCR, mcr); + } + if (timeout == 0) { dev_err(dev, "failed to reset device\n"); return -EIO; @@ -401,21 +407,32 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) /* * process completed requests for channels that have done status */ -static void talitos_done(unsigned long data) -{ - struct device *dev = (struct device *)data; - struct talitos_private *priv = dev_get_drvdata(dev); - int ch; - - for (ch = 0; ch < priv->num_channels; ch++) - flush_channel(dev, ch, 0, 0); - - /* At this point, all completed channels have been processed. - * Unmask done interrupts for channels completed later on. - */ - setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT); - setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT); -} +#define DEF_TALITOS_DONE(name, ch_done_mask) \ +static void talitos_done_##name(unsigned long data) \ +{ \ + struct device *dev = (struct device *)data; \ + struct talitos_private *priv = dev_get_drvdata(dev); \ + \ + if (ch_done_mask & 1) \ + flush_channel(dev, 0, 0, 0); \ + if (priv->num_channels == 1) \ + goto out; \ + if (ch_done_mask & (1 << 2)) \ + flush_channel(dev, 1, 0, 0); \ + if (ch_done_mask & (1 << 4)) \ + flush_channel(dev, 2, 0, 0); \ + if (ch_done_mask & (1 << 6)) \ + flush_channel(dev, 3, 0, 0); \ + \ +out: \ + /* At this point, all completed channels have been processed */ \ + /* Unmask done interrupts for channels completed later on. */ \ + setbits32(priv->reg + TALITOS_IMR, ch_done_mask); \ + setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT); \ +} +DEF_TALITOS_DONE(4ch, TALITOS_ISR_4CHDONE) +DEF_TALITOS_DONE(ch0_2, TALITOS_ISR_CH_0_2_DONE) +DEF_TALITOS_DONE(ch1_3, TALITOS_ISR_CH_1_3_DONE) /* * locate current (offending) descriptor @@ -584,7 +601,7 @@ static void talitos_error(unsigned long data, u32 isr, u32 isr_lo) } } } - if (reset_dev || isr & ~TALITOS_ISR_CHERR || isr_lo) { + if (reset_dev || isr & ~TALITOS_ISR_4CHERR || isr_lo) { dev_err(dev, "done overflow, internal time out, or rngu error: " "ISR 0x%08x_%08x\n", isr, isr_lo); @@ -597,30 +614,35 @@ static void talitos_error(unsigned long data, u32 isr, u32 isr_lo) } } -static irqreturn_t talitos_interrupt(int irq, void *data) -{ - struct device *dev = data; - struct talitos_private *priv = dev_get_drvdata(dev); - u32 isr, isr_lo; - - isr = in_be32(priv->reg + TALITOS_ISR); - isr_lo = in_be32(priv->reg + TALITOS_ISR_LO); - /* Acknowledge interrupt */ - out_be32(priv->reg + TALITOS_ICR, isr); - out_be32(priv->reg + TALITOS_ICR_LO, isr_lo); - - if (unlikely((isr & ~TALITOS_ISR_CHDONE) || isr_lo)) - talitos_error((unsigned long)data, isr, isr_lo); - else - if (likely(isr & TALITOS_ISR_CHDONE)) { - /* mask further done interrupts. */ - clrbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_DONE); - /* done_task will unmask done interrupts at exit */ - tasklet_schedule(&priv->done_task); - } - - return (isr || isr_lo) ? IRQ_HANDLED : IRQ_NONE; -} +#define DEF_TALITOS_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet) \ +static irqreturn_t talitos_interrupt_##name(int irq, void *data) \ +{ \ + struct device *dev = data; \ + struct talitos_private *priv = dev_get_drvdata(dev); \ + u32 isr, isr_lo; \ + \ + isr = in_be32(priv->reg + TALITOS_ISR); \ + isr_lo = in_be32(priv->reg + TALITOS_ISR_LO); \ + /* Acknowledge interrupt */ \ + out_be32(priv->reg + TALITOS_ICR, isr & (ch_done_mask | ch_err_mask)); \ + out_be32(priv->reg + TALITOS_ICR_LO, isr_lo); \ + \ + if (unlikely((isr & ~TALITOS_ISR_4CHDONE) & ch_err_mask || isr_lo)) \ + talitos_error((unsigned long)data, isr, isr_lo); \ + else \ + if (likely(isr & ch_done_mask)) { \ + /* mask further done interrupts. */ \ + clrbits32(priv->reg + TALITOS_IMR, ch_done_mask); \ + /* done_task will unmask done interrupts at exit */ \ + tasklet_schedule(&priv->done_task[tlet]); \ + } \ + \ + return (isr & (ch_done_mask | ch_err_mask) || isr_lo) ? IRQ_HANDLED : \ + IRQ_NONE; \ +} +DEF_TALITOS_INTERRUPT(4ch, TALITOS_ISR_4CHDONE, TALITOS_ISR_4CHERR, 0) +DEF_TALITOS_INTERRUPT(ch0_2, TALITOS_ISR_CH_0_2_DONE, TALITOS_ISR_CH_0_2_ERR, 0) +DEF_TALITOS_INTERRUPT(ch1_3, TALITOS_ISR_CH_1_3_DONE, TALITOS_ISR_CH_1_3_ERR, 1) /* * hwrng @@ -2558,12 +2580,15 @@ static int talitos_remove(struct platform_device *ofdev) kfree(priv->chan); - if (priv->irq != NO_IRQ) { - free_irq(priv->irq, dev); - irq_dispose_mapping(priv->irq); - } + for (i = 0; i < 2; i++) + if (priv->irq[i] != NO_IRQ) { + free_irq(priv->irq[i], dev); + irq_dispose_mapping(priv->irq[i]); + } - tasklet_kill(&priv->done_task); + tasklet_kill(&priv->done_task[0]); + if (priv->irq[1] != NO_IRQ) + tasklet_kill(&priv->done_task[1]); iounmap(priv->reg); @@ -2628,6 +2653,54 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev, return t_alg; } +static int talitos_probe_irq(struct platform_device *ofdev) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->dev.of_node; + struct talitos_private *priv = dev_get_drvdata(dev); + int err; + + priv->irq[0] = irq_of_parse_and_map(np, 0); + if (priv->irq[0] == NO_IRQ) { + dev_err(dev, "failed to map irq\n"); + return -EINVAL; + } + + priv->irq[1] = irq_of_parse_and_map(np, 1); + + /* get the primary irq line */ + if (priv->irq[1] == NO_IRQ) { + err = request_irq(priv->irq[0], talitos_interrupt_4ch, 0, + dev_driver_string(dev), dev); + goto primary_out; + } + + err = request_irq(priv->irq[0], talitos_interrupt_ch0_2, 0, + dev_driver_string(dev), dev); + if (err) + goto primary_out; + + /* get the secondary irq line */ + err = request_irq(priv->irq[1], talitos_interrupt_ch1_3, 0, + dev_driver_string(dev), dev); + if (err) { + dev_err(dev, "failed to request secondary irq\n"); + irq_dispose_mapping(priv->irq[1]); + priv->irq[1] = NO_IRQ; + } + + return err; + +primary_out: + if (err) { + dev_err(dev, "failed to request primary irq\n"); + irq_dispose_mapping(priv->irq[0]); + priv->irq[0] = NO_IRQ; + } + + return err; +} + static int talitos_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; @@ -2644,28 +2717,22 @@ static int talitos_probe(struct platform_device *ofdev) priv->ofdev = ofdev; - tasklet_init(&priv->done_task, talitos_done, (unsigned long)dev); - - INIT_LIST_HEAD(&priv->alg_list); - - priv->irq = irq_of_parse_and_map(np, 0); - - if (priv->irq == NO_IRQ) { - dev_err(dev, "failed to map irq\n"); - err = -EINVAL; + err = talitos_probe_irq(ofdev); + if (err) goto err_out; - } - /* get the irq line */ - err = request_irq(priv->irq, talitos_interrupt, 0, - dev_driver_string(dev), dev); - if (err) { - dev_err(dev, "failed to request irq %d\n", priv->irq); - irq_dispose_mapping(priv->irq); - priv->irq = NO_IRQ; - goto err_out; + if (priv->irq[1] == NO_IRQ) { + tasklet_init(&priv->done_task[0], talitos_done_4ch, + (unsigned long)dev); + } else { + tasklet_init(&priv->done_task[0], talitos_done_ch0_2, + (unsigned long)dev); + tasklet_init(&priv->done_task[1], talitos_done_ch1_3, + (unsigned long)dev); } + INIT_LIST_HEAD(&priv->alg_list); + priv->reg = of_iomap(np, 0); if (!priv->reg) { dev_err(dev, "failed to of_iomap\n"); @@ -2713,9 +2780,11 @@ static int talitos_probe(struct platform_device *ofdev) goto err_out; } - for (i = 0; i < priv->num_channels; i++) - priv->chan[i].reg = priv->reg + TALITOS_CH_BASE_OFFSET + - TALITOS_CH_STRIDE * (i + 1); + for (i = 0; i < priv->num_channels; i++) { + priv->chan[i].reg = priv->reg + TALITOS_CH_STRIDE * (i + 1); + if ((priv->irq[1] == NO_IRQ) || !(i & 1)) + priv->chan[i].reg += TALITOS_CH_BASE_OFFSET; + } for (i = 0; i < priv->num_channels; i++) { spin_lock_init(&priv->chan[i].head_lock); |