dmaengine: hsu: Export hsu_dma_get_status()

To allow other code to safely read DMA Channel Status Register (where
the register attribute for Channel Error, Descriptor Time Out &
Descriptor Done fields are read-clear), export hsu_dma_get_status().
hsu_dma_irq() is renamed to hsu_dma_do_irq() and requires Status
Register value to be passed in.

Signed-off-by: Chuah, Kim Tatt <kim.tatt.chuah@intel.com>
Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Chuah, Kim Tatt 2016-06-15 13:44:11 +08:00 committed by Greg Kroah-Hartman
parent d03516df83
commit c6f82787a5
4 changed files with 105 additions and 30 deletions

View File

@ -126,28 +126,33 @@ static void hsu_dma_start_transfer(struct hsu_dma_chan *hsuc)
hsu_dma_start_channel(hsuc); hsu_dma_start_channel(hsuc);
} }
static u32 hsu_dma_chan_get_sr(struct hsu_dma_chan *hsuc) /*
{ * hsu_dma_get_status() - get DMA channel status
unsigned long flags; * @chip: HSUART DMA chip
u32 sr; * @nr: DMA channel number
* @status: pointer for DMA Channel Status Register value
spin_lock_irqsave(&hsuc->vchan.lock, flags); *
sr = hsu_chan_readl(hsuc, HSU_CH_SR); * Description:
spin_unlock_irqrestore(&hsuc->vchan.lock, flags); * The function reads and clears the DMA Channel Status Register, checks
* if it was a timeout interrupt and returns a corresponding value.
return sr & ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY); *
} * Caller should provide a valid pointer for the DMA Channel Status
* Register value that will be returned in @status.
irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr) *
* Return:
* 1 for DMA timeout status, 0 for other DMA status, or error code for
* invalid parameters or no interrupt pending.
*/
int hsu_dma_get_status(struct hsu_dma_chip *chip, unsigned short nr,
u32 *status)
{ {
struct hsu_dma_chan *hsuc; struct hsu_dma_chan *hsuc;
struct hsu_dma_desc *desc;
unsigned long flags; unsigned long flags;
u32 sr; u32 sr;
/* Sanity check */ /* Sanity check */
if (nr >= chip->hsu->nr_channels) if (nr >= chip->hsu->nr_channels)
return IRQ_NONE; return -EINVAL;
hsuc = &chip->hsu->chan[nr]; hsuc = &chip->hsu->chan[nr];
@ -155,22 +160,65 @@ irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
* No matter what situation, need read clear the IRQ status * No matter what situation, need read clear the IRQ status
* There is a bug, see Errata 5, HSD 2900918 * There is a bug, see Errata 5, HSD 2900918
*/ */
sr = hsu_dma_chan_get_sr(hsuc); spin_lock_irqsave(&hsuc->vchan.lock, flags);
sr = hsu_chan_readl(hsuc, HSU_CH_SR);
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
/* Check if any interrupt is pending */
sr &= ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY);
if (!sr) if (!sr)
return IRQ_NONE; return -EIO;
/* Timeout IRQ, need wait some time, see Errata 2 */ /* Timeout IRQ, need wait some time, see Errata 2 */
if (sr & HSU_CH_SR_DESCTO_ANY) if (sr & HSU_CH_SR_DESCTO_ANY)
udelay(2); udelay(2);
/*
* At this point, at least one of Descriptor Time Out, Channel Error
* or Descriptor Done bits must be set. Clear the Descriptor Time Out
* bits and if sr is still non-zero, it must be channel error or
* descriptor done which are higher priority than timeout and handled
* in hsu_dma_do_irq(). Else, it must be a timeout.
*/
sr &= ~HSU_CH_SR_DESCTO_ANY; sr &= ~HSU_CH_SR_DESCTO_ANY;
if (!sr)
return IRQ_HANDLED; *status = sr;
return sr ? 0 : 1;
}
EXPORT_SYMBOL_GPL(hsu_dma_get_status);
/*
* hsu_dma_do_irq() - DMA interrupt handler
* @chip: HSUART DMA chip
* @nr: DMA channel number
* @status: Channel Status Register value
*
* Description:
* This function handles Channel Error and Descriptor Done interrupts.
* This function should be called after determining that the DMA interrupt
* is not a normal timeout interrupt, ie. hsu_dma_get_status() returned 0.
*
* Return:
* IRQ_NONE for invalid channel number, IRQ_HANDLED otherwise.
*/
irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr,
u32 status)
{
struct hsu_dma_chan *hsuc;
struct hsu_dma_desc *desc;
unsigned long flags;
/* Sanity check */
if (nr >= chip->hsu->nr_channels)
return IRQ_NONE;
hsuc = &chip->hsu->chan[nr];
spin_lock_irqsave(&hsuc->vchan.lock, flags); spin_lock_irqsave(&hsuc->vchan.lock, flags);
desc = hsuc->desc; desc = hsuc->desc;
if (desc) { if (desc) {
if (sr & HSU_CH_SR_CHE) { if (status & HSU_CH_SR_CHE) {
desc->status = DMA_ERROR; desc->status = DMA_ERROR;
} else if (desc->active < desc->nents) { } else if (desc->active < desc->nents) {
hsu_dma_start_channel(hsuc); hsu_dma_start_channel(hsuc);
@ -184,7 +232,7 @@ irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
EXPORT_SYMBOL_GPL(hsu_dma_irq); EXPORT_SYMBOL_GPL(hsu_dma_do_irq);
static struct hsu_dma_desc *hsu_dma_alloc_desc(unsigned int nents) static struct hsu_dma_desc *hsu_dma_alloc_desc(unsigned int nents)
{ {

View File

@ -27,13 +27,20 @@ static irqreturn_t hsu_pci_irq(int irq, void *dev)
{ {
struct hsu_dma_chip *chip = dev; struct hsu_dma_chip *chip = dev;
u32 dmaisr; u32 dmaisr;
u32 status;
unsigned short i; unsigned short i;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
int err;
dmaisr = readl(chip->regs + HSU_PCI_DMAISR); dmaisr = readl(chip->regs + HSU_PCI_DMAISR);
for (i = 0; i < chip->hsu->nr_channels; i++) { for (i = 0; i < chip->hsu->nr_channels; i++) {
if (dmaisr & 0x1) if (dmaisr & 0x1) {
ret |= hsu_dma_irq(chip, i); err = hsu_dma_get_status(chip, i, &status);
if (err > 0)
ret |= IRQ_HANDLED;
else if (err == 0)
ret |= hsu_dma_do_irq(chip, i, status);
}
dmaisr >>= 1; dmaisr >>= 1;
} }

View File

@ -97,12 +97,24 @@ static int dnv_handle_irq(struct uart_port *p)
{ {
struct mid8250 *mid = p->private_data; struct mid8250 *mid = p->private_data;
unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR); unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR);
u32 status;
int ret = IRQ_NONE; int ret = IRQ_NONE;
int err;
if (fisr & BIT(2)) if (fisr & BIT(2)) {
ret |= hsu_dma_irq(&mid->dma_chip, 1); err = hsu_dma_get_status(&mid->dma_chip, 1, &status);
if (fisr & BIT(1)) if (err > 0)
ret |= hsu_dma_irq(&mid->dma_chip, 0); ret |= IRQ_HANDLED;
else if (err == 0)
ret |= hsu_dma_do_irq(&mid->dma_chip, 1, status);
}
if (fisr & BIT(1)) {
err = hsu_dma_get_status(&mid->dma_chip, 0, &status);
if (err > 0)
ret |= IRQ_HANDLED;
else if (err == 0)
ret |= hsu_dma_do_irq(&mid->dma_chip, 0, status);
}
if (fisr & BIT(0)) if (fisr & BIT(0))
ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR));
return ret; return ret;

View File

@ -39,14 +39,22 @@ struct hsu_dma_chip {
#if IS_ENABLED(CONFIG_HSU_DMA) #if IS_ENABLED(CONFIG_HSU_DMA)
/* Export to the internal users */ /* Export to the internal users */
irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr); int hsu_dma_get_status(struct hsu_dma_chip *chip, unsigned short nr,
u32 *status);
irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr,
u32 status);
/* Export to the platform drivers */ /* Export to the platform drivers */
int hsu_dma_probe(struct hsu_dma_chip *chip); int hsu_dma_probe(struct hsu_dma_chip *chip);
int hsu_dma_remove(struct hsu_dma_chip *chip); int hsu_dma_remove(struct hsu_dma_chip *chip);
#else #else
static inline irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, static inline int hsu_dma_get_status(struct hsu_dma_chip *chip,
unsigned short nr) unsigned short nr, u32 *status)
{
return 0;
}
static inline irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip,
unsigned short nr, u32 status)
{ {
return IRQ_NONE; return IRQ_NONE;
} }