/** * hsi_unpoll - HSI poll feature, disables data interrupt on frame reception * @dev - hsi device channel reference to apply the I/O control * (or port associated to it) * * Return 0 on success, a negative value on failure. * */ int hsi_unpoll(struct hsi_device *dev) { struct hsi_channel *ch; struct hsi_dev *hsi_ctrl; if (unlikely(!dev || !dev->ch)) return -EINVAL; dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch); if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) { dev_err(dev->device.parent, "HSI device NOT open\n"); return -EINVAL; } ch = dev->ch; hsi_ctrl = ch->hsi_port->hsi_controller; spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); ch->flags &= ~HSI_CH_RX_POLL; hsi_driver_disable_read_interrupt(ch); hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); return 0; }
/** * hsi_close - close given hsi device channel * @dev - reference to hsi device channel. */ void hsi_close(struct hsi_device *dev) { struct hsi_dev *hsi_ctrl; if (!dev || !dev->ch) { pr_err(LOG_NAME "Trying to close wrong HSI device %p\n", dev); return; } dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch); hsi_ctrl = dev->ch->hsi_port->hsi_controller; spin_lock_bh(&hsi_ctrl->lock); hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); if (dev->ch->flags & HSI_CH_OPEN) { dev->ch->flags &= ~HSI_CH_OPEN; __hsi_write_cancel(dev->ch); __hsi_read_cancel(dev->ch); } if (dev->ch->flags & HSI_CH_RX_POLL) { /* Disable RX interrupt for polling */ dev->ch->flags &= ~HSI_CH_RX_POLL; hsi_driver_disable_read_interrupt(dev->ch); } hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number, __func__); spin_unlock_bh(&hsi_ctrl->lock); }
/* HSR_AVAILABLE interrupt processing */ static void hsi_do_channel_rx(struct hsi_channel *ch) { struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller; void __iomem *base = ch->hsi_port->hsi_controller->base; unsigned int n_ch; unsigned int n_p; unsigned int irq; long buff_offset; int rx_poll = 0; int data_read = 0; int fifo, fifo_words_avail; n_ch = ch->channel_number; n_p = ch->hsi_port->port_number; irq = ch->hsi_port->n_irq; dev_dbg(hsi_ctrl->dev, "Data Available interrupt for channel %d.\n", n_ch); /* Check if there is data in FIFO available for reading */ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) { fifo = hsi_fifo_get_id(hsi_ctrl, n_ch, n_p); if (unlikely(fifo < 0)) { dev_err(hsi_ctrl->dev, "No valid FIFO id found for " "channel %d.\n", n_ch); return; } fifo_words_avail = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo); if (!fifo_words_avail) { dev_dbg(hsi_ctrl->dev, "WARNING: RX FIFO %d empty before CPU copy\n", fifo); /* Do not disable interrupt becaue another interrupt */ /* can still come, this time with a real frame. */ return; } } /* Disable interrupts if not needed for polling */ if (!(ch->flags & HSI_CH_RX_POLL)) hsi_driver_disable_read_interrupt(ch); /* * Check race condition: RX transmission initiated but DMA transmission * already started - acknowledge then ignore interrupt occurence */ if (ch->read_data.lch != -1) { dev_warn(hsi_ctrl->dev, "Race condition between RX Int ch %d and DMA %0x\n", n_ch, ch->read_data.lch); goto done; } if (ch->flags & HSI_CH_RX_POLL) rx_poll = 1; if (ch->read_data.addr) { buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, n_p, n_ch); if (buff_offset >= 0) { data_read = 1; *(ch->read_data.addr) = hsi_inl(base, buff_offset); } } hsi_reset_ch_read(ch); done: if (rx_poll) { spin_unlock(&hsi_ctrl->lock); hsi_port_event_handler(ch->hsi_port, HSI_EVENT_HSR_DATAAVAILABLE, (void *)n_ch); spin_lock(&hsi_ctrl->lock); } if (data_read) { spin_unlock(&hsi_ctrl->lock); dev_dbg(hsi_ctrl->dev, "Calling ch %d read callback.\n", n_ch); (*ch->read_done) (ch->dev, 1); spin_lock(&hsi_ctrl->lock); } }
/** * hsi_driver_read_dma - Program GDD [DMA] to write data to memory from * the hsi channel buffer. * @hsi_channel - pointer to the hsi_channel to read data from. * @data - 32-bit word pointer where to store the incoming data. * @size - Number of 32bit words to be transfered to the buffer. * * hsi_controller lock must be held before calling this function. * * Return 0 on success and < 0 on error. */ int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 * data, unsigned int count) { struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller; void __iomem *base = hsi_ctrl->base; unsigned int port = hsi_channel->hsi_port->port_number; unsigned int channel = hsi_channel->channel_number; unsigned int sync; int lch; dma_addr_t src_addr; dma_addr_t dest_addr; u16 tmp; int fifo; lch = hsi_get_free_lch(hsi_ctrl); if (lch < 0) { dev_err(hsi_ctrl->dev, "No free DMA channels.\n"); return -EBUSY; /* No free GDD logical channels. */ } else { dev_dbg(hsi_ctrl->dev, "Allocated DMA channel %d for read on" " HSI channel %d.\n", lch, hsi_channel->channel_number); } /* When DMA is used for Rx, disable the Rx Interrupt. * (else DATAAVAILLABLE event would get triggered on first * received data word) * (Rx interrupt might be active for polling feature) */ hsi_driver_disable_read_interrupt(hsi_channel); /* * NOTE: Gettting a free gdd logical channel and * reserve it must be done atomicaly. */ hsi_channel->read_data.lch = lch; /* Sync is required for SSI but not for HSI */ sync = hsi_sync_table[HSI_SYNC_READ][port - 1][channel]; dest_addr = dma_map_single(hsi_ctrl->dev, data, count * 4, DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(hsi_ctrl->dev, dest_addr))) { dev_err(hsi_ctrl->dev, "Failed to create DMA read mapping.\n"); return -ENOMEM; } tmp = HSI_DST_BURST_4x32_BIT | HSI_DST_MEMORY_PORT | HSI_SRC_BURST_4x32_BIT | HSI_SRC_PERIPHERAL_PORT | HSI_DATA_TYPE_S32; hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch)); tmp = HSI_DST_AMODE_POSTINC | HSI_SRC_AMODE_CONST | sync; hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch)); hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CCIR_REG(lch)); if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) { fifo = hsi_fifo_get_id(hsi_ctrl, channel, port); if (unlikely(fifo < 0)) { dev_err(hsi_ctrl->dev, "No valid FIFO id for DMA " "transfer from FIFO.\n"); return -EFAULT; } /* HSI CSSA register takes a FIFO ID when copying from FIFO */ hsi_outl(fifo, base, HSI_GDD_CSSA_REG(lch)); } else{ src_addr = hsi_ctrl->phy_base + HSI_HSR_BUFFER_CH_REG(port, channel); /* SSI CSSA register always takes a 32-bit address */ hsi_outl(src_addr, base, HSI_GDD_CSSA_REG(lch)); } /* HSI CDSA register takes a 32-bit address when copying to memory */ /* SSI CDSA register always takes a 32-bit address */ hsi_outl(dest_addr, base, HSI_GDD_CDSA_REG(lch)); hsi_outw(count, base, HSI_GDD_CEN_REG(lch)); /* TODO : Need to clean interrupt status here to avoid spurious int */ hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG); hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch)); return 0; }