blob: 9a327c67283f30279ee65e82724b6db0504d134a [file] [log] [blame]
/*
* hsi_driver_dma.c
*
* Implements HSI low level interface driver functionality with DMA support.
*
* Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
* Copyright (C) 2009 Texas Instruments, Inc.
*
* Author: Carlos Chinea <carlos.chinea@nokia.com>
* Author: Sebastien JAN <s-jan@ti.com>
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <linux/dma-mapping.h>
#include "hsi_driver.h"
#define HSI_SYNC_WRITE 0
#define HSI_SYNC_READ 1
#define HSI_L3_TPUT 13428 /* 13428 KiB/s => ~110 Mbit/s */
static unsigned char hsi_sync_table[2][2][8] = {
{
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}
}, {
{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
{0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
}
};
/**
* hsi_is_dma_read_int_pending - Indicates if a DMA read interrupt is pending
* @hsi_ctrl - HSI controller of the GDD.
*
* Needs to be called holding the hsi_controller lock
*
* Returns true if DMA read interrupt is pending, else false
*/
bool hsi_is_dma_read_int_pending(struct hsi_dev *hsi_ctrl)
{
void __iomem *base = hsi_ctrl->base;
unsigned int gdd_lch = 0;
u32 status_reg = 0;
int i, j;
status_reg = hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
status_reg &= hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
if (!status_reg)
return false;
/* Scan all enabled DMA channels */
for (gdd_lch = 0; gdd_lch < hsi_ctrl->gdd_chan_count; gdd_lch++) {
if (!(status_reg & HSI_GDD_LCH(gdd_lch)))
continue;
for (i = 0; i < hsi_ctrl->max_p; i++)
for (j = 0; j < hsi_ctrl->hsi_port[i].max_ch; j++)
if (hsi_ctrl->hsi_port[i].
hsi_channel[j].read_data.lch == gdd_lch)
return true;
}
return false;
}
/**
* hsi_get_free_lch - Get a free GDD(DMA) logical channel
* @hsi_ctrl - HSI controller of the GDD.
*
* Needs to be called holding the hsi_controller lock
*
* Returns the logical channel number, or -EBUSY if none available
*/
static int hsi_get_free_lch(struct hsi_dev *hsi_ctrl)
{
unsigned int enable_reg;
int i, lch;
enable_reg = hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
lch = hsi_ctrl->last_gdd_lch;
for (i = 0; i < hsi_ctrl->gdd_chan_count; i++) {
if (++lch >= hsi_ctrl->gdd_chan_count)
lch = 0;
if ((enable_reg & HSI_GDD_LCH(lch)) == 0) {
hsi_ctrl->last_gdd_lch = lch;
return lch;
}
}
return -EBUSY;
}
/**
* hsi_driver_write_dma - Program GDD [DMA] to write data from memory to
* the hsi channel buffer.
* @hsi_channel - pointer to the hsi_channel to write data to.
* @data - 32-bit word pointer to the data.
* @size - Number of 32bit words to be transfered.
*
* hsi_controller lock must be held before calling this function.
*
* Return 0 on success and < 0 on error.
*/
int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 * data,
unsigned int size)
{
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;
if ((size < 1) || (data == NULL))
return -EINVAL;
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 write on"
" HSI channel %d.\n", lch,
hsi_channel->channel_number);
}
/* NOTE: Getting a free gdd logical channel and
* reserve it must be done atomicaly. */
hsi_channel->write_data.lch = lch;
/* Sync is required for SSI but not for HSI */
sync = hsi_sync_table[HSI_SYNC_WRITE][port - 1][channel];
src_addr = dma_map_single(hsi_ctrl->dev, data, size * 4, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(hsi_ctrl->dev, src_addr))) {
dev_err(hsi_ctrl->dev, "Failed to create DMA write mapping.\n");
return -ENOMEM;
}
tmp = HSI_SRC_BURST_4x32_BIT|
HSI_SRC_MEMORY_PORT |
HSI_DST_BURST_4x32_BIT |
HSI_DST_PERIPHERAL_PORT | HSI_DATA_TYPE_S32;
hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));
tmp = HSI_SRC_AMODE_POSTINC | HSI_DST_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 to FIFO.\n");
return -EFAULT;
}
/* HSI CDSA register takes a FIFO ID when copying to FIFO */
hsi_outl(fifo, base, HSI_GDD_CDSA_REG(lch));
} else {
dest_addr = hsi_ctrl->phy_base + HSI_HST_BUFFER_CH_REG(port,
channel);
/* SSI CDSA register always takes a 32-bit address */
hsi_outl(dest_addr, base, HSI_GDD_CDSA_REG(lch));
}
/* HSI CSSA register takes a 32-bit address when copying from memory */
/* SSI CSSA register always takes a 32-bit address */
hsi_outl(src_addr, base, HSI_GDD_CSSA_REG(lch));
hsi_outw(size, 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* hsi_driver_cancel_read_dma - Cancel an ongoing GDD [DMA] read for the
* specified hsi channel.
* @hsi_ch - pointer to the hsi_channel to cancel DMA read.
*
* 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 available at expected
* address.
* 0 : DMA transfer is already over, data already available at
* expected address.
*
* Note: whatever returned value, read callback will not be called after cancel.
*/
int hsi_driver_cancel_read_dma(struct hsi_channel *hsi_ch)
{
int lch = hsi_ch->read_data.lch;
struct hsi_dev *hsi_ctrl = hsi_ch->hsi_port->hsi_controller;
u16 ccr, gdd_csr;
u32 status_reg;
dma_addr_t dma_h;
size_t size;
/* Re-enable interrupts for polling if needed */
if (hsi_ch->flags & HSI_CH_RX_POLL)
hsi_driver_enable_read_interrupt(hsi_ch, NULL);
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, "Read 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 - Access to the buffer is now safe */
dma_h = hsi_inl(hsi_ctrl->base, HSI_GDD_CDSA_REG(lch));
size = hsi_inw(hsi_ctrl->base, HSI_GDD_CEN_REG(lch)) * 4;
dma_unmap_single(hsi_ctrl->dev, dma_h, size, DMA_FROM_DEVICE);
hsi_reset_ch_read(hsi_ch);
return status_reg & HSI_GDD_LCH(lch) ? 0 : -ECANCELED;
}
/**
* hsi_get_info_from_gdd_lch - Retrieve channels information from DMA channel
* @hsi_ctrl - HSI device control structure
* @lch - DMA logical channel
* @port - HSI port
* @channel - HSI channel
* @is_read_path - channel is used for reading
*
* Updates the port, channel and is_read_path parameters depending on the
* lch DMA channel status.
*
* Return 0 on success and < 0 on error.
*/
int hsi_get_info_from_gdd_lch(struct hsi_dev *hsi_ctrl, unsigned int lch,
unsigned int *port, unsigned int *channel,
unsigned int *is_read_path)
{
int i, j;
int err = -1;
for (i = 0; i < hsi_ctrl->max_p; i++)
for (j = 0; j < hsi_ctrl->hsi_port[i].max_ch; j++)
if (hsi_ctrl->hsi_port[i].
hsi_channel[j].read_data.lch == lch) {
*is_read_path = 1;
*port = i + 1;
*channel = j;
err = 0;
goto get_info_bk;
} else if (hsi_ctrl->hsi_port[i].
hsi_channel[j].write_data.lch == lch) {
*is_read_path = 0;
*port = i + 1;
*channel = j;
err = 0;
goto get_info_bk;
}
get_info_bk:
return err;
}
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);
}
}
static u32 hsi_process_dma_event(struct hsi_dev *hsi_ctrl)
{
void __iomem *base = hsi_ctrl->base;
unsigned int gdd_lch = 0;
u32 status_reg = 0;
u32 lch_served = 0;
unsigned int gdd_max_count = hsi_ctrl->gdd_chan_count;
status_reg = hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
status_reg &= hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
if (!status_reg) {
dev_dbg(hsi_ctrl->dev, "DMA : no event, exit.\n");
return 0;
}
for (gdd_lch = 0; gdd_lch < gdd_max_count; gdd_lch++) {
if (status_reg & HSI_GDD_LCH(gdd_lch)) {
do_hsi_gdd_lch(hsi_ctrl, gdd_lch);
lch_served |= HSI_GDD_LCH(gdd_lch);
}
}
/* Acknowledge interrupt for DMA channel */
hsi_outl(lch_served, base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
return status_reg;
}
static void do_hsi_gdd_tasklet(unsigned long device)
{
struct hsi_dev *hsi_ctrl = (struct hsi_dev *)device;
dev_dbg(hsi_ctrl->dev, "DMA Tasklet : clock_enabled=%d\n",
hsi_ctrl->clock_enabled);
spin_lock(&hsi_ctrl->lock);
hsi_clocks_enable(hsi_ctrl->dev, __func__);
hsi_ctrl->in_dma_tasklet = true;
hsi_process_dma_event(hsi_ctrl);
hsi_ctrl->in_dma_tasklet = false;
hsi_clocks_disable(hsi_ctrl->dev, __func__);
spin_unlock(&hsi_ctrl->lock);
enable_irq(hsi_ctrl->gdd_irq);
}
static irqreturn_t hsi_gdd_mpu_handler(int irq, void *p)
{
struct hsi_dev *hsi_ctrl = p;
tasklet_hi_schedule(&hsi_ctrl->hsi_gdd_tasklet);
/* Disable interrupt until Bottom Half has cleared the IRQ status */
/* register */
disable_irq_nosync(hsi_ctrl->gdd_irq);
return IRQ_HANDLED;
}
int __init hsi_gdd_init(struct hsi_dev *hsi_ctrl, const char *irq_name)
{
tasklet_init(&hsi_ctrl->hsi_gdd_tasklet, do_hsi_gdd_tasklet,
(unsigned long)hsi_ctrl);
dev_info(hsi_ctrl->dev, "Registering IRQ %s (%d)\n",
irq_name, hsi_ctrl->gdd_irq);
if (request_irq(hsi_ctrl->gdd_irq, hsi_gdd_mpu_handler,
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH,
irq_name, hsi_ctrl) < 0) {
dev_err(hsi_ctrl->dev, "FAILED to request GDD IRQ %d\n",
hsi_ctrl->gdd_irq);
return -EBUSY;
}
return 0;
}
void hsi_gdd_exit(struct hsi_dev *hsi_ctrl)
{
tasklet_kill(&hsi_ctrl->hsi_gdd_tasklet);
free_irq(hsi_ctrl->gdd_irq, hsi_ctrl);
}