/* HST_ACCEPTED interrupt processing */ static void hsi_do_channel_tx(struct hsi_channel *ch) { struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller; void __iomem *base = hsi_ctrl->base; unsigned int n_ch; unsigned int n_p; unsigned int irq; long buff_offset; n_ch = ch->channel_number; n_p = ch->hsi_port->port_number; irq = ch->hsi_port->n_irq; dev_dbg(hsi_ctrl->dev, "Data Accepted interrupt for channel %d.\n", n_ch); hsi_driver_disable_write_interrupt(ch); if (ch->write_data.addr == NULL) { dev_err(hsi_ctrl->dev, "Error, NULL Write address.\n"); hsi_reset_ch_write(ch); } else { buff_offset = hsi_hst_buffer_reg(hsi_ctrl, n_p, n_ch); if (buff_offset >= 0) { hsi_outl(*(ch->write_data.addr), base, buff_offset); ch->write_data.addr = NULL; } } spin_unlock(&hsi_ctrl->lock); dev_dbg(hsi_ctrl->dev, "Calling ch %d write callback.\n", n_ch); (*ch->write_done) (ch->dev, 1); spin_lock(&hsi_ctrl->lock); }
/** * hsi_driver_cancel_write_interrupt - Cancel pending write interrupt. * @dev - hsi device channel where to cancel the pending interrupt. * * Return: -ECANCELED : write cancel success, data not transfered to TX FIFO * 0 : transfer is already over, data already transfered to TX FIFO * * Note: whatever returned value, write callback will not be called after * write cancel. */ int hsi_driver_cancel_write_interrupt(struct hsi_channel *ch) { struct hsi_port *p = ch->hsi_port; unsigned int port = p->port_number; unsigned int channel = ch->channel_number; void __iomem *base = p->hsi_controller->base; u32 status_reg; long buff_offset; status_reg = hsi_inl(base, HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel)); if (!(status_reg & HSI_HST_DATAACCEPT(channel))) { dev_dbg(&ch->dev->device, "Write cancel on not " "enabled channel %d ENABLE REG 0x%08X", channel, status_reg); } status_reg &= hsi_inl(base, HSI_SYS_MPU_STATUS_CH_REG(port, p->n_irq, channel)); hsi_outl_and(~HSI_HST_DATAACCEPT(channel), base, HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel)); buff_offset = hsi_hst_bufstate_f_reg(p->hsi_controller, port, channel); if (buff_offset >= 0) hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), base, buff_offset); hsi_reset_ch_write(ch); return status_reg & HSI_HST_DATAACCEPT(channel) ? 0 : -ECANCELED; }
static void do_channel_tx(struct hsi_channel *ch) { struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller; void __iomem *base = hsi_ctrl->base; unsigned int n_ch; unsigned int n_p; unsigned int irq; long buff_offset; n_ch = ch->channel_number; n_p = ch->hsi_port->port_number; irq = ch->hsi_port->n_irq; spin_lock(&hsi_ctrl->lock); if (ch->write_data.addr == NULL) { hsi_outl_and(~HSI_HST_DATAACCEPT(n_ch), base, HSI_SYS_MPU_ENABLE_CH_REG(n_p, irq, n_ch)); hsi_reset_ch_write(ch); spin_unlock(&hsi_ctrl->lock); (*ch->write_done)(ch->dev, 1); } else { buff_offset = hsi_hst_buffer_reg(hsi_ctrl, n_p, n_ch); if (buff_offset >= 0) { hsi_outl(*(ch->write_data.addr), base, buff_offset); ch->write_data.addr = NULL; } spin_unlock(&hsi_ctrl->lock); } }
/** * hsi_driver_cancel_write_dma - Cancel an ongoing GDD [DMA] write for the * specified hsi channel. * @hsi_ch - pointer to the hsi_channel to cancel DMA write. * * hsi_controller lock must be held before calling this function. * * Return: -ENXIO : No DMA channel found for specified HSI channel * -ECANCELED : DMA cancel success, data not transfered to TX FIFO * 0 : DMA transfer is already over, data already transfered to TX FIFO * * Note: whatever returned value, write callback will not be called after * write cancel. */ int hsi_driver_cancel_write_dma(struct hsi_channel *hsi_ch) { int lch = hsi_ch->write_data.lch; unsigned int port = hsi_ch->hsi_port->port_number; unsigned int channel = hsi_ch->channel_number; struct hsi_dev *hsi_ctrl = hsi_ch->hsi_port->hsi_controller; u16 ccr, gdd_csr; long buff_offset; u32 status_reg; dma_addr_t dma_h; size_t size; if (lch < 0) { dev_err(&hsi_ch->dev->device, "No DMA channel found for HSI " "channel %d\n", hsi_ch->channel_number); return -ENXIO; } ccr = hsi_inw(hsi_ctrl->base, HSI_GDD_CCR_REG(lch)); if (!(ccr & HSI_CCR_ENABLE)) { dev_dbg(&hsi_ch->dev->device, "Write cancel on not " "enabled logical channel %d CCR REG 0x%04X\n", lch, ccr); } status_reg = hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG); status_reg &= hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG); hsi_outw_and(~HSI_CCR_ENABLE, hsi_ctrl->base, HSI_GDD_CCR_REG(lch)); /* Clear CSR register by reading it, as it is cleared automaticaly */ /* by HW after SW read. */ gdd_csr = hsi_inw(hsi_ctrl->base, HSI_GDD_CSR_REG(lch)); hsi_outl_and(~HSI_GDD_LCH(lch), hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG); hsi_outl(HSI_GDD_LCH(lch), hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG); /* Unmap DMA region */ dma_h = hsi_inl(hsi_ctrl->base, HSI_GDD_CSSA_REG(lch)); size = hsi_inw(hsi_ctrl->base, HSI_GDD_CEN_REG(lch)) * 4; dma_unmap_single(hsi_ctrl->dev, dma_h, size, DMA_TO_DEVICE); buff_offset = hsi_hst_bufstate_f_reg(hsi_ctrl, port, channel); if (buff_offset >= 0) hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), hsi_ctrl->base, buff_offset); hsi_reset_ch_write(hsi_ch); return status_reg & HSI_GDD_LCH(lch) ? 0 : -ECANCELED; }
static void do_hsi_gdd_lch(struct hsi_dev *hsi_ctrl, unsigned int gdd_lch) { void __iomem *base = hsi_ctrl->base; struct platform_device *pdev = to_platform_device(hsi_ctrl->dev); struct hsi_channel *ch; unsigned int port; unsigned int channel; unsigned int is_read_path; u32 gdd_csr; dma_addr_t dma_h; size_t size; int fifo, fifo_words_avail; if (hsi_get_info_from_gdd_lch(hsi_ctrl, gdd_lch, &port, &channel, &is_read_path) < 0) { dev_err(hsi_ctrl->dev, "Unable to match the DMA channel %d with" " an HSI channel\n", gdd_lch); return; } else { dev_dbg(hsi_ctrl->dev, "DMA event on gdd_lch=%d => port=%d, " "channel=%d, read=%d\n", gdd_lch, port, channel, is_read_path); } hsi_outl_and(~HSI_GDD_LCH(gdd_lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG); /* Warning : CSR register is cleared automaticaly by HW after SW read */ gdd_csr = hsi_inw(base, HSI_GDD_CSR_REG(gdd_lch)); if (!(gdd_csr & HSI_CSR_TOUT)) { if (is_read_path) { /* Read path */ dma_h = hsi_inl(base, HSI_GDD_CDSA_REG(gdd_lch)); size = hsi_inw(base, HSI_GDD_CEN_REG(gdd_lch)) * 4; dma_sync_single_for_cpu(hsi_ctrl->dev, dma_h, size, DMA_FROM_DEVICE); dma_unmap_single(hsi_ctrl->dev, dma_h, size, DMA_FROM_DEVICE); ch = hsi_ctrl_get_ch(hsi_ctrl, port, channel); hsi_reset_ch_read(ch); dev_dbg(hsi_ctrl->dev, "Calling ch %d read callback " "(size %d).\n", channel, size/4); spin_unlock(&hsi_ctrl->lock); ch->read_done(ch->dev, size / 4); spin_lock(&hsi_ctrl->lock); /* Check if FIFO is correctly emptied */ if (hsi_driver_device_is_hsi(pdev)) { fifo = hsi_fifo_get_id(hsi_ctrl, channel, port); if (unlikely(fifo < 0)) { dev_err(hsi_ctrl->dev, "No valid FIFO " "id found for channel %d.\n", channel); return; } fifo_words_avail = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo); if (fifo_words_avail) dev_dbg(hsi_ctrl->dev, "FIFO %d not empty " "after DMA copy, remaining " "%d/%d frames\n", fifo, fifo_words_avail, HSI_HSR_FIFO_SIZE); } /* Re-enable interrupts for polling if needed */ if (ch->flags & HSI_CH_RX_POLL) hsi_driver_enable_read_interrupt(ch, NULL); } else { /* Write path */ dma_h = hsi_inl(base, HSI_GDD_CSSA_REG(gdd_lch)); size = hsi_inw(base, HSI_GDD_CEN_REG(gdd_lch)) * 4; dma_unmap_single(hsi_ctrl->dev, dma_h, size, DMA_TO_DEVICE); ch = hsi_ctrl_get_ch(hsi_ctrl, port, channel); hsi_reset_ch_write(ch); dev_dbg(hsi_ctrl->dev, "Calling ch %d write callback " "(size %d).\n", channel, size/4); spin_unlock(&hsi_ctrl->lock); ch->write_done(ch->dev, size / 4); spin_lock(&hsi_ctrl->lock); } } else { dev_err(hsi_ctrl->dev, "Time-out overflow Error on GDD transfer" " on gdd channel %d\n", gdd_lch); spin_unlock(&hsi_ctrl->lock); /* TODO : need to perform a DMA soft reset */ hsi_port_event_handler(&hsi_ctrl->hsi_port[port - 1], HSI_EVENT_ERROR, NULL); spin_lock(&hsi_ctrl->lock); } }