From 59a32a4624d81a014fecbeb31e63946984dbbc01 Mon Sep 17 00:00:00 2001 From: Chase Southwood Date: Sat, 8 Mar 2014 22:00:54 -0600 Subject: [PATCH] Staging: comedi: convert while loops to timeouts in s626.c This patch changes a handful of while loops to timeouts to prevent infinite looping on hardware failure. A couple such loops are in a function (s626_debi_transfer()) which is called from critical sections, so comedi_timeout() is unusable for them, and an iterative timeout is used instead. For the while loops in a context where comedi_timeout() is allowed, a new callback function, s626_send_dac_eoc(), has been defined to evaluate the conditions that the while loops are testing. The new callback employs a switch statement based on a simple new enum so that it is usable for all of the different conditions tested in while loops in s626_send_dac(). The proper comedi_timeout() calls are then used. Signed-off-by: Chase Southwood Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/s626.c | 87 ++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c index 5ba4b4a..fab8ccf 100644 --- a/drivers/staging/comedi/drivers/s626.c +++ b/drivers/staging/comedi/drivers/s626.c @@ -209,6 +209,8 @@ static const struct comedi_lrange s626_range_table = { static void s626_debi_transfer(struct comedi_device *dev) { struct s626_private *devpriv = dev->private; + static const int timeout = 10000; + int i; /* Initiate upload of shadow RAM to DEBI control register */ s626_mc_enable(dev, S626_MC2_UPLD_DEBI, S626_P_MC2); @@ -217,12 +219,22 @@ static void s626_debi_transfer(struct comedi_device *dev) * Wait for completion of upload from shadow RAM to * DEBI control register. */ - while (!s626_mc_test(dev, S626_MC2_UPLD_DEBI, S626_P_MC2)) - ; + for (i = 0; i < timeout; i++) { + if (s626_mc_test(dev, S626_MC2_UPLD_DEBI, S626_P_MC2)) + break; + udelay(1); + } + if (i == timeout) + comedi_error(dev, "Timeout while uploading to DEBI control register."); /* Wait until DEBI transfer is done */ - while (readl(devpriv->mmio + S626_P_PSR) & S626_PSR_DEBI_S) - ; + for (i = 0; i < timeout; i++) { + if (!(readl(devpriv->mmio + S626_P_PSR) & S626_PSR_DEBI_S)) + break; + udelay(1); + } + if (i == timeout) + comedi_error(dev, "DEBI transfer timeout."); } /* @@ -351,6 +363,48 @@ static const uint8_t s626_trimadrs[] = { 0x40, 0x41, 0x42, 0x50, 0x51, 0x52, 0x53, 0x60, 0x61, 0x62, 0x63 }; +enum { + s626_send_dac_wait_not_mc1_a2out, + s626_send_dac_wait_ssr_af2_out, + s626_send_dac_wait_fb_buffer2_msb_00, + s626_send_dac_wait_fb_buffer2_msb_ff +}; + +static int s626_send_dac_eoc(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned long context) +{ + struct s626_private *devpriv = dev->private; + unsigned int status; + + switch (context) { + case s626_send_dac_wait_not_mc1_a2out: + status = readl(devpriv->mmio + S626_P_MC1); + if (!(status & S626_MC1_A2OUT)) + return 0; + break; + case s626_send_dac_wait_ssr_af2_out: + status = readl(devpriv->mmio + S626_P_SSR); + if (status & S626_SSR_AF2_OUT) + return 0; + break; + case s626_send_dac_wait_fb_buffer2_msb_00: + status = readl(devpriv->mmio + S626_P_FB_BUFFER2); + if (!(status & 0xff000000)) + return 0; + break; + case s626_send_dac_wait_fb_buffer2_msb_ff: + status = readl(devpriv->mmio + S626_P_FB_BUFFER2); + if (status & 0xff000000) + return 0; + break; + default: + return -EINVAL; + } + return -EBUSY; +} + /* * Private helper function: Transmit serial data to DAC via Audio * channel 2. Assumes: (1) TSL2 slot records initialized, and (2) @@ -359,6 +413,7 @@ static const uint8_t s626_trimadrs[] = { static void s626_send_dac(struct comedi_device *dev, uint32_t val) { struct s626_private *devpriv = dev->private; + int ret; /* START THE SERIAL CLOCK RUNNING ------------- */ @@ -404,8 +459,10 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val) * Done by polling the DMAC enable flag; this flag is automatically * cleared when the transfer has finished. */ - while (readl(devpriv->mmio + S626_P_MC1) & S626_MC1_A2OUT) - ; + ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc, + s626_send_dac_wait_not_mc1_a2out); + if (ret) + comedi_error(dev, "DMA transfer timeout."); /* START THE OUTPUT STREAM TO THE TARGET DAC -------------------- */ @@ -425,8 +482,10 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val) * finished transferring the DAC's data DWORD from the output FIFO * to the output buffer register. */ - while (!(readl(devpriv->mmio + S626_P_SSR) & S626_SSR_AF2_OUT)) - ; + ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc, + s626_send_dac_wait_ssr_af2_out); + if (ret) + comedi_error(dev, "TSL timeout waiting for slot 1 to execute."); /* * Set up to trap execution at slot 0 when the TSL sequencer cycles @@ -466,8 +525,10 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val) * from 0xFF to 0x00, which slot 0 causes to happen by shifting * out/in on SD2 the 0x00 that is always referenced by slot 5. */ - while (readl(devpriv->mmio + S626_P_FB_BUFFER2) & 0xff000000) - ; + ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc, + s626_send_dac_wait_fb_buffer2_msb_00); + if (ret) + comedi_error(dev, "TSL timeout waiting for slot 0 to execute."); } /* * Either (1) we were too late setting the slot 0 trap; the TSL @@ -486,8 +547,10 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val) * the next DAC write. This is detected when FB_BUFFER2 MSB changes * from 0x00 to 0xFF. */ - while (!(readl(devpriv->mmio + S626_P_FB_BUFFER2) & 0xff000000)) - ; + ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc, + s626_send_dac_wait_fb_buffer2_msb_ff); + if (ret) + comedi_error(dev, "TSL timeout waiting for slot 0 to execute."); } /* -- 2.7.4