diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/s626.c')
-rw-r--r-- | drivers/staging/comedi/drivers/s626.c | 174 |
1 files changed, 140 insertions, 34 deletions
diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c index 19da1db..95fadf3 100644 --- a/drivers/staging/comedi/drivers/s626.c +++ b/drivers/staging/comedi/drivers/s626.c @@ -106,16 +106,16 @@ struct s626_private { struct s626_enc_info { /* Pointers to functions that differ for A and B counters: */ /* Return clock enable. */ - uint16_t(*get_enable)(struct comedi_device *dev, + uint16_t (*get_enable)(struct comedi_device *dev, const struct s626_enc_info *k); /* Return interrupt source. */ - uint16_t(*get_int_src)(struct comedi_device *dev, + uint16_t (*get_int_src)(struct comedi_device *dev, const struct s626_enc_info *k); /* Return preload trigger source. */ - uint16_t(*get_load_trig)(struct comedi_device *dev, + uint16_t (*get_load_trig)(struct comedi_device *dev, const struct s626_enc_info *k); /* Return standardized operating mode. */ - uint16_t(*get_mode)(struct comedi_device *dev, + uint16_t (*get_mode)(struct comedi_device *dev, const struct s626_enc_info *k); /* Generate soft index strobe. */ void (*pulse_index)(struct comedi_device *dev, @@ -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,23 @@ 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,14 +364,57 @@ 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) * dacpol contains valid target image. */ -static void s626_send_dac(struct comedi_device *dev, uint32_t val) +static int 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 +460,12 @@ 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."); + return ret; + } /* START THE OUTPUT STREAM TO THE TARGET DAC -------------------- */ @@ -425,8 +485,12 @@ 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."); + return ret; + } /* * Set up to trap execution at slot 0 when the TSL sequencer cycles @@ -466,8 +530,13 @@ 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."); + return ret; + } } /* * Either (1) we were too late setting the slot 0 trap; the TSL @@ -486,14 +555,19 @@ 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."); + return ret; + } + return 0; } /* * Private helper function: Write setpoint to an application DAC channel. */ -static void s626_set_dac(struct comedi_device *dev, uint16_t chan, +static int s626_set_dac(struct comedi_device *dev, uint16_t chan, int16_t dacdata) { struct s626_private *devpriv = dev->private; @@ -556,10 +630,10 @@ static void s626_set_dac(struct comedi_device *dev, uint16_t chan, val |= ((uint32_t)(chan & 1) << 15); /* Address the DAC channel * within the device. */ val |= (uint32_t)dacdata; /* Include DAC setpoint data. */ - s626_send_dac(dev, val); + return s626_send_dac(dev, val); } -static void s626_write_trim_dac(struct comedi_device *dev, uint8_t logical_chan, +static int s626_write_trim_dac(struct comedi_device *dev, uint8_t logical_chan, uint8_t dac_data) { struct s626_private *devpriv = dev->private; @@ -606,17 +680,22 @@ static void s626_write_trim_dac(struct comedi_device *dev, uint8_t logical_chan, * Address the DAC channel within the trimdac device. * Include DAC setpoint data. */ - s626_send_dac(dev, (chan << 8) | dac_data); + return s626_send_dac(dev, (chan << 8) | dac_data); } -static void s626_load_trim_dacs(struct comedi_device *dev) +static int s626_load_trim_dacs(struct comedi_device *dev) { uint8_t i; + int ret; /* Copy TrimDac setpoint values from EEPROM to TrimDacs. */ - for (i = 0; i < ARRAY_SIZE(s626_trimchan); i++) - s626_write_trim_dac(dev, i, + for (i = 0; i < ARRAY_SIZE(s626_trimchan); i++) { + ret = s626_write_trim_dac(dev, i, s626_i2c_read(dev, s626_trimadrs[i])); + if (ret) + return ret; + } + return 0; } /* ****** COUNTER FUNCTIONS ******* */ @@ -1846,6 +1925,20 @@ static int s626_ai_rinsn(struct comedi_device *dev, } #endif +static int s626_ai_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; + + status = readl(devpriv->mmio + S626_P_PSR); + if (status & S626_PSR_GPIO2) + return 0; + return -EBUSY; +} + static int s626_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) @@ -1856,6 +1949,7 @@ static int s626_ai_insn_read(struct comedi_device *dev, uint16_t adc_spec = 0; uint32_t gpio_image; uint32_t tmp; + int ret; int n; /* @@ -1897,8 +1991,9 @@ static int s626_ai_insn_read(struct comedi_device *dev, */ /* Wait for ADC done */ - while (!(readl(devpriv->mmio + S626_P_PSR) & S626_PSR_GPIO2)) - ; + ret = comedi_timeout(dev, s, insn, s626_ai_eoc, 0); + if (ret) + return ret; /* Fetch ADC data */ if (n != 0) { @@ -2299,6 +2394,7 @@ static int s626_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, { struct s626_private *devpriv = dev->private; int i; + int ret; uint16_t chan = CR_CHAN(insn->chanspec); int16_t dacdata; @@ -2307,7 +2403,9 @@ static int s626_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, devpriv->ao_readback[CR_CHAN(insn->chanspec)] = data[i]; dacdata -= (0x1fff); - s626_set_dac(dev, chan, dacdata); + ret = s626_set_dac(dev, chan, dacdata); + if (ret) + return ret; } return i; @@ -2543,12 +2641,13 @@ static int s626_allocate_dma_buffers(struct comedi_device *dev) return 0; } -static void s626_initialize(struct comedi_device *dev) +static int s626_initialize(struct comedi_device *dev) { struct s626_private *devpriv = dev->private; dma_addr_t phys_buf; uint16_t chan; int i; + int ret; /* Enable DEBI and audio pins, enable I2C interface */ s626_mc_enable(dev, S626_MC1_DEBI | S626_MC1_AUDIO | S626_MC1_I2C, @@ -2749,7 +2848,9 @@ static void s626_initialize(struct comedi_device *dev) * sometimes causes the first few TrimDAC writes to malfunction. */ s626_load_trim_dacs(dev); - s626_load_trim_dacs(dev); + ret = s626_load_trim_dacs(dev); + if (ret) + return ret; /* * Manually init all gate array hardware in case this is a soft @@ -2763,8 +2864,11 @@ static void s626_initialize(struct comedi_device *dev) * Init all DAC outputs to 0V and init all DAC setpoint and * polarity images. */ - for (chan = 0; chan < S626_DAC_CHANNELS; chan++) - s626_set_dac(dev, chan, 0); + for (chan = 0; chan < S626_DAC_CHANNELS; chan++) { + ret = s626_set_dac(dev, chan, 0); + if (ret) + return ret; + } /* Init counters */ s626_counters_init(dev); @@ -2780,6 +2884,8 @@ static void s626_initialize(struct comedi_device *dev) /* Initialize the digital I/O subsystem */ s626_dio_init(dev); + + return 0; } static int s626_auto_attach(struct comedi_device *dev, @@ -2900,9 +3006,9 @@ static int s626_auto_attach(struct comedi_device *dev, s->insn_read = s626_enc_insn_read; s->insn_write = s626_enc_insn_write; - s626_initialize(dev); - - dev_info(dev->class_dev, "%s attached\n", dev->board_name); + ret = s626_initialize(dev); + if (ret) + return ret; return 0; } |