summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-05-21 18:57:48 -0700
committerGary King <gking@nvidia.com>2010-05-21 19:35:12 -0700
commit6da18fdbed7a1574890f461d676836c2b5ca9f2e (patch)
tree8e35546ba7de8b81def48f8e16b4ddebd4072b8c
parentd2b1af1975c3fe4069c1a09c7be8c2ad1d542230 (diff)
[ARM/tegra] dma: add transfer count query, correct spelling of CONTINUOUS
add an API to return the completed transfer count of a pending, active or finished DMA request CONTINUOUS was misspelled CONTINOUS; fix this in the hsuart and dma drivers Change-Id: I732396f60f876c92fe3b74ad1d2fc02d00868d24
-rw-r--r--arch/arm/mach-tegra/dma.c104
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h6
-rw-r--r--drivers/serial/tegra_hsuart.c2
3 files changed, 79 insertions, 33 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index 423db61998a2..43f8622ee12f 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -95,8 +95,9 @@
#define APB_SEQ_WRAP_SHIFT 16
#define APB_SEQ_WRAP_MASK (0x7<<APB_SEQ_WRAP_SHIFT)
+#define TEGRA_SYSTEM_DMA_AVP_CH_NUM 3
#define TEGRA_SYSTEM_DMA_CH_MIN 0
-#define TEGRA_SYSTEM_DMA_CH_MAX 15
+#define TEGRA_SYSTEM_DMA_CH_MAX (15 - TEGRA_SYSTEM_DMA_AVP_CH_NUM)
#define NV_DMA_MAX_TRASFER_SIZE 0x10000
@@ -193,18 +194,81 @@ int tegra_dma_cancel(struct tegra_dma_channel *ch)
return 0;
}
+/* should be called with the channel lock held */
+static unsigned int dma_active_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req)
+{
+ unsigned int status;
+
+ unsigned int to_transfer;
+ unsigned int req_transfer_count;
+
+ unsigned int bytes_transferred;
+
+ /* Get the transfer count */
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
+ req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+ req_transfer_count += 1;
+ to_transfer += 1;
+
+ bytes_transferred = req_transfer_count;
+
+ if (status & STA_BUSY)
+ bytes_transferred -= to_transfer;
+
+ /* In continuous transfer mode, DMA only tracks the count of the
+ * half DMA buffer. So, if the DMA already finished half the DMA
+ * then add the half buffer to the completed count.
+ *
+ * FIXME: There can be a race here. What if the req to
+ * dequue happens at the same time as the DMA just moved to
+ * the new buffer and SW didn't yet received the interrupt?
+ */
+ if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS)
+ if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
+ bytes_transferred += req_transfer_count;
+
+ bytes_transferred *= 4;
+
+ return bytes_transferred;
+}
+
+unsigned int tegra_dma_transferred_req(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req)
+{
+ unsigned long irq_flags;
+ unsigned int bytes_transferred;
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node)!=req) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return req->bytes_transferred;
+ }
+
+ bytes_transferred = dma_active_count(ch, req);
+
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+ return bytes_transferred;
+}
+EXPORT_SYMBOL(tegra_dma_transferred_req);
+
int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *_req)
{
- unsigned int csr;
- unsigned int status;
struct tegra_dma_req *req = NULL;
int found = 0;
+ unsigned int csr;
unsigned long irq_flags;
- int to_transfer;
- int req_transfer_count;
+ int stop = 0;
spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node)==_req)
+ stop = 1;
+
list_for_each_entry(req, &ch->list, node) {
if (req == _req) {
list_del(&req->node);
@@ -217,6 +281,9 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
return 0;
}
+ if (!stop)
+ goto skip_status;
+
/* STOP the DMA and get the transfer count.
* Getting the transfer count is tricky.
* - Change the source selector to invalid to stop the DMA from
@@ -234,31 +301,7 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
csr |= CSR_ENB;
writel(csr, ch->addr + APB_DMA_CHAN_CSR);
- /* Get the transfer count */
- status = readl(ch->addr + APB_DMA_CHAN_STA);
- to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
- req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
- req_transfer_count += 1;
- to_transfer += 1;
-
- req->bytes_transferred = req_transfer_count;
-
- if (status & STA_BUSY)
- req->bytes_transferred -= to_transfer;
-
- /* In continous transfer mode, DMA only tracks the count of the
- * half DMA buffer. So, if the DMA already finished half the DMA
- * then add the half buffer to the completed count.
- *
- * FIXME: There can be a race here. What if the req to
- * dequue happens at the same time as the DMA just moved to
- * the new buffer and SW didn't yet received the interrupt?
- */
- if (ch->mode & TEGRA_DMA_MODE_CONTINOUS)
- if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
- req->bytes_transferred += req_transfer_count;
-
- req->bytes_transferred *= 4;
+ req->bytes_transferred = dma_active_count(ch, req);
tegra_dma_stop(ch);
if (!list_empty(&ch->list)) {
@@ -268,6 +311,7 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req);
}
+skip_status:
req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
spin_unlock_irqrestore(&ch->lock, irq_flags);
diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
index 59f734038ea9..731b433812a9 100644
--- a/arch/arm/mach-tegra/include/mach/dma.h
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -58,7 +58,7 @@ struct tegra_dma_channel;
enum tegra_dma_mode {
TEGRA_DMA_SHARED = 1,
- TEGRA_DMA_MODE_CONTINOUS = 2,
+ TEGRA_DMA_MODE_CONTINUOUS = 2,
TEGRA_DMA_MODE_ONESHOT = 4,
};
@@ -75,7 +75,6 @@ enum tegra_dma_req_buff_status {
struct tegra_dma_req {
struct list_head node;
- unsigned int modid;
int instance;
/* Called when the req is complete and from the DMA ISR context.
@@ -140,6 +139,9 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
void tegra_dma_dequeue(struct tegra_dma_channel *ch);
void tegra_dma_flush(struct tegra_dma_channel *ch);
+unsigned int tegra_dma_transferred_req(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req);
+
bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
struct tegra_dma_req *req);
bool tegra_dma_is_empty(struct tegra_dma_channel *ch);
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c
index f3a03e44947b..94c6ddb1bb1b 100644
--- a/drivers/serial/tegra_hsuart.c
+++ b/drivers/serial/tegra_hsuart.c
@@ -642,7 +642,7 @@ static int tegra_uart_init_rx_dma(struct tegra_uart_port *t)
dma_addr_t rx_dma_phys;
void *rx_dma_virt;
- t->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINOUS);
+ t->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINUOUS);
if (IS_ERR_OR_NULL(t->rx_dma))
return -ENODEV;