⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pxa2xx_spi.c

📁 pxa3xx ssp driver for linux
💻 C
📖 第 1 页 / 共 4 页
字号:
         msg = drv_data->cur_msg;
         drv_data->cur_msg = NULL;
         drv_data->cur_transfer = NULL;
         drv_data->cur_chip = NULL;
         queue_work(drv_data->workqueue, &drv_data->pump_messages);
         spin_unlock_irqrestore(&drv_data->lock, flags);
 
         last_transfer = list_entry(msg->transfers.prev,
                                         struct spi_transfer,
                                         transfer_list);
 
         if (!last_transfer->cs_change)
                 drv_data->cs_control(PXA2XX_CS_DEASSERT);
 
         msg->state = NULL;
         if (msg->complete)
                 msg->complete(msg->context);
 }
 
 static int wait_ssp_rx_stall(void *ioaddr)
 {
         unsigned long limit = loops_per_jiffy << 1;
 
         while ((read_SSSR(ioaddr) & SSSR_BSY) && limit--)
                 cpu_relax();
 
         return limit;
 }
 
 static int wait_dma_channel_stop(int channel)
 {
         unsigned long limit = loops_per_jiffy << 1;
 
         while (!(DCSR(channel) & DCSR_STOPSTATE) && limit--)
                 cpu_relax();
 
         return limit;
 }
 
 static void dma_handler(int channel, void *data, struct pt_regs *regs)
 {
         struct driver_data *drv_data = data;
         struct spi_message *msg = drv_data->cur_msg;
         void *reg = drv_data->ioaddr;
         u32 irq_status = DCSR(channel) & DMA_INT_MASK;
         u32 trailing_sssr = 0;

         if (irq_status & DCSR_BUSERR) {
 
                 /* Disable interrupts, clear status and reset DMA */
                 write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
                 write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                 if (drv_data->ssp_type != PXA25x_SSP)
                         write_SSTO(0, reg);
                 write_SSSR(drv_data->clear_sr, reg);
                 DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
                 DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
 
                 if (flush(drv_data) == 0)
                         dev_err(&drv_data->pdev->dev,
                                         "dma_handler: flush fail\n");
 
                 unmap_dma_buffers(drv_data);
 
                 if (channel == drv_data->tx_channel)
                         dev_err(&drv_data->pdev->dev,
                                 "dma_handler: bad bus address on "
                                 "tx channel %d, source %x target = %x\n",
                                 channel, DSADR(channel), DTADR(channel));
                 else
                         dev_err(&drv_data->pdev->dev,
                                 "dma_handler: bad bus address on "
                                 "rx channel %d, source %x target = %x\n",
                                 channel, DSADR(channel), DTADR(channel));
 
                 msg->state = ERROR_STATE;
                 tasklet_schedule(&drv_data->pump_transfers);
         }
 
         /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */
         if ((drv_data->ssp_type == PXA25x_SSP)
                 && (channel == drv_data->tx_channel)
                 && (irq_status & DCSR_ENDINTR)) {
 
                 /* Wait for rx to stall */
                 if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
                         dev_err(&drv_data->pdev->dev,
                                 "dma_handler: ssp rx stall failed\n");
 
                 /* Clear and disable interrupts on SSP and DMA channels*/
                 write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                 write_SSSR(drv_data->clear_sr, reg);
                 DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
                 DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
                 if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
                         dev_err(&drv_data->pdev->dev,
                                 "dma_handler: dma rx channel stop failed\n");
 
                 unmap_dma_buffers(drv_data);
 
                 /* Read trailing bytes */
                 /* Calculate number of trailing bytes, read them */
                 trailing_sssr = read_SSSR(reg);
                 if ((trailing_sssr & 0xf008) != 0xf000) {
                         drv_data->rx = drv_data->rx_end -
                                         (((trailing_sssr >> 12) & 0x0f) + 1);
                         drv_data->read(drv_data);
                 }
                 msg->actual_length += drv_data->len;
 
                 /* Release chip select if requested, transfer delays are
                  * handled in pump_transfers */
                 if (drv_data->cs_change)
                         drv_data->cs_control(PXA2XX_CS_DEASSERT);
 
                 /* Move to next transfer */
                 msg->state = next_transfer(drv_data);
 
                 /* Schedule transfer tasklet */
                 tasklet_schedule(&drv_data->pump_transfers);
         }
 }
 
 static irqreturn_t dma_transfer(struct driver_data *drv_data)
 {
         u32 irq_status;
         u32 trailing_sssr = 0;
         struct spi_message *msg = drv_data->cur_msg;
         void *reg = drv_data->ioaddr;
 
         irq_status = read_SSSR(reg) & drv_data->mask_sr;
         if (irq_status & SSSR_ROR) {
                 /* Clear and disable interrupts on SSP and DMA channels*/
                 write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
                 write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                 if (drv_data->ssp_type != PXA25x_SSP)
                         write_SSTO(0, reg);
                 write_SSSR(drv_data->clear_sr, reg);
                 DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
                 DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
                 unmap_dma_buffers(drv_data);
 
                 if (flush(drv_data) == 0)
                         dev_err(&drv_data->pdev->dev,
                                         "dma_transfer: flush fail\n");
 
                 dev_warn(&drv_data->pdev->dev, "dma_transfer: fifo overun\n");
 
                 drv_data->cur_msg->state = ERROR_STATE;
                 tasklet_schedule(&drv_data->pump_transfers);
 
                 return IRQ_HANDLED;
         }
 
         /* Check for false positive timeout */
         if ((irq_status & SSSR_TINT) && DCSR(drv_data->tx_channel) & DCSR_RUN) {
                 write_SSSR(SSSR_TINT, reg);
                 return IRQ_HANDLED;
         }
 
         if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
 
                 /* Clear and disable interrupts on SSP and DMA channels*/
                 write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                 if (drv_data->ssp_type != PXA25x_SSP)
                         write_SSTO(0, reg);
                 write_SSSR(drv_data->clear_sr, reg);
                 DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
                 DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
 
                 if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
                         dev_err(&drv_data->pdev->dev,
                                 "dma_transfer: dma rx channel stop failed\n");
 
                 if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
                         dev_err(&drv_data->pdev->dev,
                                 "dma_transfer: ssp rx stall failed\n");
 
                 unmap_dma_buffers(drv_data);
 
                 /* Calculate number of trailing bytes, read them */
                 trailing_sssr = read_SSSR(reg);
                 if ((trailing_sssr & 0xf008) != 0xf000) {
                         drv_data->rx = drv_data->rx_end -
                                         (((trailing_sssr >> 12) & 0x0f) + 1);
                         drv_data->read(drv_data);
                 }
                 msg->actual_length += drv_data->len;
 
                 /* Release chip select if requested, transfer delays are
                  * handled in pump_transfers */
                 if (drv_data->cs_change)
                         drv_data->cs_control(PXA2XX_CS_DEASSERT);
 
                 /* Move to next transfer */
                 msg->state = next_transfer(drv_data);
 
                 /* Schedule transfer tasklet */
                 tasklet_schedule(&drv_data->pump_transfers);
 
                 return IRQ_HANDLED;
         }
 
         /* Opps problem detected */
         return IRQ_NONE;
 }
 
 static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
 {
         struct spi_message *msg = drv_data->cur_msg;
         void *reg = drv_data->ioaddr;
         unsigned long limit = loops_per_jiffy << 1;
         u32 irq_status;
         u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ?
                         drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS;
 
         while ((irq_status = read_SSSR(reg) & irq_mask)) {
 
                 if (irq_status & SSSR_ROR) {
 
                         /* Clear and disable interrupts */
                         write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
                         write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
                         if (drv_data->ssp_type != PXA25x_SSP)
                                 write_SSTO(0, reg);
                         write_SSSR(drv_data->clear_sr, reg);
 
                         if (flush(drv_data) == 0)
                                 dev_err(&drv_data->pdev->dev,
                                         "interrupt_transfer: flush fail\n");
 
                         /* Stop the SSP */
 
                         dev_warn(&drv_data->pdev->dev,
                                         "interrupt_transfer: fifo overun\n");
 
                         msg->state = ERROR_STATE;
                         tasklet_schedule(&drv_data->pump_transfers);
 
                         return IRQ_HANDLED;
                 }
 
                 /* Look for false positive timeout */
                 if ((irq_status & SSSR_TINT)
                                 && (drv_data->rx < drv_data->rx_end))
                         write_SSSR(SSSR_TINT, reg);
 
                 /* Pump data */
                 drv_data->read(drv_data);
                 drv_data->write(drv_data);
 
                 if (drv_data->tx == drv_data->tx_end) {
                         /* Disable tx interrupt */
                         write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg);
                         irq_mask = drv_data->mask_sr & ~SSSR_TFS;
 
                         /* PXA25x_SSP has no timeout, read trailing bytes */
                         if (drv_data->ssp_type == PXA25x_SSP) {
                                 while ((read_SSSR(reg) & SSSR_BSY) && limit--)
                                         drv_data->read(drv_data);
 
                                 if (limit == 0)
                                         dev_err(&drv_data->pdev->dev,
                                                 "interrupt_transfer: "
                                                 "trailing byte read failed\n");
                         }
                 }
 
                 if ((irq_status & SSSR_TINT)
                                 || (drv_data->rx == drv_data->rx_end)) {
 
                         /* Clear timeout */
                         write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
                         if (drv_data->ssp_type != PXA25x_SSP)
                                 write_SSTO(0, reg);
                         write_SSSR(drv_data->clear_sr, reg);
 
                         /* Update total byte transfered */
                         msg->actual_length += drv_data->len;
 
                         /* Release chip select if requested, transfer delays are
                          * handled in pump_transfers */
                         if (drv_data->cs_change)
                                 drv_data->cs_control(PXA2XX_CS_DEASSERT);
 
                         /* Move to next transfer */
                         msg->state = next_transfer(drv_data);
 
                         /* Schedule transfer tasklet */
                         tasklet_schedule(&drv_data->pump_transfers);
                 }
         }
 
         /* We did something */
         return IRQ_HANDLED;
 }
 
 static irqreturn_t ssp_int(int irq, void *dev_id, struct pt_regs *regs)
 {
         struct driver_data *drv_data = (struct driver_data *)dev_id;
         void *reg = drv_data->ioaddr;
 
         if (!drv_data->cur_msg) {
 
                 write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
                 write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
                 if (drv_data->ssp_type != PXA25x_SSP)
                         write_SSTO(0, reg);
                 write_SSSR(drv_data->clear_sr, reg);
 
                 dev_err(&drv_data->pdev->dev, "bad message state "
                                 "in interrupt handler");
 
                 /* Never fail */
                 return IRQ_HANDLED;
         }
 
         return drv_data->transfer_handler(drv_data);
 }
 
 static void pump_transfers(unsigned long data)
 {
         struct driver_data *drv_data = (struct driver_data *)data;
         struct spi_message *message = NULL;
         struct spi_transfer *transfer = NULL;
         struct spi_transfer *previous = NULL;
         struct chip_data *chip = NULL;
         void *reg = drv_data->ioaddr;
         u32 clk_div = 0;
         u8 bits = 0;
         u32 speed = 0;
         u32 cr0;
 
         /* Get current state information */
         message = drv_data->cur_msg;
         transfer = drv_data->cur_transfer;
         chip = drv_data->cur_chip;
 
         /* Handle for abort */
         if (message->state == ERROR_STATE) {
                 message->status = -EIO;
                 giveback(drv_data);
                 return;
         }
 
         /* Handle end of message */
         if (message->state == DONE_STATE) {
                 message->status = 0;
                 giveback(drv_data);
                 return;
         }
 
         /* Delay if requested at end of transfer*/
         if (message->state == RUNNING_STATE) {
                 previous = list_entry(transfer->transfer_list.prev,
                                         struct spi_transfer,
                                         transfer_list);
                 if (previous->delay_usecs)
                         udelay(previous->delay_usecs);
         }
 
         /* Setup the transfer state based on the type of transfer */
         if (flush(drv_data) == 0) {
                 dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
                 message->status = -EIO;
                 giveback(drv_data);
                 return;
         }
         drv_data->n_bytes = chip->n_bytes;
         drv_data->dma_width = chip->dma_width;
         drv_data->cs_control = chip->cs_control;
         drv_data->tx = (void *)transfer->tx_buf;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -