[PATCH 04/13] mpc52xx: LocalPlus driver: rewrite interrupt routines, fix errors
Roman Fietze
roman.fietze at telemotive.de
Tue Dec 22 18:01:53 EST 2009
Use SCLPC bit definitions from mpc52xx.h for better readability.
Rewrite IRQ handlers, make them work for DMA.
Fix module unload error.
Signed-off-by: Roman Fietze <roman.fietze at telemotive.de>
---
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 306 ++++++++++++-------------
1 files changed, 149 insertions(+), 157 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
index 2763d5e..2fd1f3f 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -46,6 +46,34 @@ struct mpc52xx_lpbfifo {
/* The MPC5200 has only one fifo, so only need one instance structure */
static struct mpc52xx_lpbfifo lpbfifo;
+
+/**
+ * mpc52xx_lpbfifo_is_write - return true if it's a WRITE request
+ */
+static inline int mpc52xx_lpbfifo_is_write(int flags)
+{
+ return flags & MPC52XX_LPBFIFO_FLAG_WRITE;
+}
+
+
+/**
+ * mpc52xx_lpbfifo_is_dma - return true if it's a DMA request
+ */
+static inline int mpc52xx_lpbfifo_is_dma(int flags)
+{
+ return !(flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
+}
+
+
+/**
+ * mpc52xx_lpbfifo_is_poll_dma - return true if it's a polled DMA request
+ */
+static inline int mpc52xx_lpbfifo_is_poll_dma(int flags)
+{
+ return flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
+}
+
+
/**
* mpc52xx_lpbfifo_kick - Trigger the next block of data to be transfered
*/
@@ -57,16 +85,23 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
u32 *data;
int i;
int bit_fields;
- int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
- int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
- int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
+ int rflags = req->flags;
/* Set and clear the reset bits; is good practice in User Manual */
- out_be32(&lpbfifo.regs->enable, 0x01010000);
+ out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_ENABLE_RF);
+
+ /* Set width, chip select and READ mode */
+ out_be32(&lpbfifo.regs->start_address, req->offset + req->pos);
+
+ /* Set CS and BPT */
+ bit_fields = MPC52xx_SCLPC_CONTROL_CS(req->cs) | 0x8;
+ if (!(mpc52xx_lpbfifo_is_write(rflags))) {
+ bit_fields |= MPC52xx_SCLPC_CONTROL_RWB_RECEIVE; /* read mode */
+ bit_fields |= MPC52xx_SCLPC_CONTROL_FLUSH;
+ }
+ out_be32(&lpbfifo.regs->control, bit_fields);
- /* set master enable bit */
- out_be32(&lpbfifo.regs->enable, 0x00000001);
- if (!dma) {
+ if (!mpc52xx_lpbfifo_is_dma(rflags)) {
/* While the FIFO can be setup for transfer sizes as large as
* 16M-1, the FIFO itself is only 512 bytes deep and it does
* not generate interrupts for FIFO full events (only transfer
@@ -80,7 +115,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
transfer_size = 512;
/* Load the FIFO with data */
- if (write) {
+ if (mpc52xx_lpbfifo_is_write(rflags)) {
reg = &lpbfifo.regs->fifo_data;
data = req->data + req->pos;
for (i = 0; i < transfer_size; i += 4)
@@ -88,7 +123,9 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
}
/* Unmask both error and completion irqs */
- out_be32(&lpbfifo.regs->enable, 0x00000301);
+ out_be32(&lpbfifo.regs->enable, (MPC52xx_SCLPC_ENABLE_AIE |
+ MPC52xx_SCLPC_ENABLE_NIE |
+ MPC52xx_SCLPC_ENABLE_ME));
} else {
/* Choose the correct direction
*
@@ -97,16 +134,16 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
* there is a performance impacit. However, if it is wrong there
* is a risk of DMA not transferring the last chunk of data
*/
- if (write) {
- out_be32(&lpbfifo.regs->fifo_alarm, 0x1e4);
- out_8(&lpbfifo.regs->fifo_control, 7);
+ if (mpc52xx_lpbfifo_is_write(rflags)) {
+ out_be32(&lpbfifo.regs->fifo_alarm, MPC52xx_SCLPC_FIFO_SIZE - 28);
+ out_be32(&lpbfifo.regs->fifo_control, MPC52xx_SLPC_FIFO_CONTROL_GR(7));
lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task;
} else {
- out_be32(&lpbfifo.regs->fifo_alarm, 0x1ff);
- out_8(&lpbfifo.regs->fifo_control, 0);
+ out_be32(&lpbfifo.regs->fifo_alarm, MPC52xx_SCLPC_FIFO_SIZE - 1);
+ out_be32(&lpbfifo.regs->fifo_control, MPC52xx_SLPC_FIFO_CONTROL_GR(0));
lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task;
- if (poll_dma) {
+ if (mpc52xx_lpbfifo_is_poll_dma(rflags)) {
if (lpbfifo.dma_irqs_enabled) {
disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
lpbfifo.dma_irqs_enabled = 0;
@@ -119,63 +156,34 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
}
}
+ /* error irq & master enabled bit */
+ out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_AIE | MPC52xx_SCLPC_ENABLE_NIE | MPC52xx_SCLPC_ENABLE_ME);
+
bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
bd->status = transfer_size;
- if (!write) {
- /*
- * In the DMA read case, the DMA doesn't complete,
- * possibly due to incorrect watermarks in the ALARM
- * and CONTROL regs. For now instead of trying to
- * determine the right watermarks that will make this
- * work, just increase the number of bytes the FIFO is
- * expecting.
- *
- * When submitting another operation, the FIFO will get
- * reset, so the condition of the FIFO waiting for a
- * non-existent 4 bytes will get cleared.
- */
- transfer_size += 4; /* BLECH! */
- }
bd->data[0] = req->data_phys + req->pos;
bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
-
- /* error irq & master enabled bit */
- bit_fields = 0x00000201;
-
- /* Unmask irqs */
- if (write && (!poll_dma))
- bit_fields |= 0x00000100; /* completion irq too */
- out_be32(&lpbfifo.regs->enable, bit_fields);
}
- /* Set transfer size, width, chip select and READ mode */
- out_be32(&lpbfifo.regs->start_address,
- req->offset + req->pos);
- out_be32(&lpbfifo.regs->packet_size.packet_size, transfer_size);
-
- bit_fields = req->cs << 24 | 0x000008;
- if (!write)
- bit_fields |= 0x010000; /* read mode */
- out_be32(&lpbfifo.regs->control, bit_fields);
-
- /* Kick it off */
- out_8(&lpbfifo.regs->packet_size.restart, 0x01);
- if (dma)
+ /* Set packet size and kick it off */
+ out_be32(&lpbfifo.regs->packet_size.packet_size, MPC52xx_SCLPC_PACKET_SIZE_RESTART | transfer_size);
+ if (mpc52xx_lpbfifo_is_dma(rflags))
bcom_enable(lpbfifo.bcom_cur_task);
}
/**
- * mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO
+ * mpc52xx_lpbfifo_sclpc_irq - IRQ handler for LPB FIFO
*
- * On transmit, the dma completion irq triggers before the fifo completion
- * triggers. Handle the dma completion here instead of the LPB FIFO Bestcomm
- * task completion irq becuase everyting is not really done until the LPB FIFO
- * completion irq triggers.
+ * On transmit, the dma completion irq triggers before the fifo
+ * completion triggers. Handle the dma completion here instead of the
+ * LPB FIFO Bestcomm task completion irq because everything is not
+ * really done until the LPB FIFO completion irq triggers.
*
* In other words:
* For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on
- * transmit, the fifo completion irq is the "Fat Lady". The opera (or in this
- * case the DMA/FIFO operation) is not finished until the "Fat Lady" sings.
+ * transmit, the fifo completion irq is the "Fat Lady". The opera (or in
+ * this case the DMA/FIFO operation) is not finished until the "Fat
+ * Lady" sings.
*
* Reasons for entering this routine:
* 1) PIO mode rx and tx completion irq
@@ -205,17 +213,17 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
* extra fiddling is done to make sure all paths lead to the same
* outbound code.
*/
-static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id)
+static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq, void *dev_id)
{
struct mpc52xx_lpbfifo_request *req;
- u32 status = in_8(&lpbfifo.regs->bytes_done_status.status);
+ u32 status_count = in_be32(&lpbfifo.regs->bytes_done_status.bytes_done);
void __iomem *reg;
u32 *data;
- int count, i;
+ size_t i;
int do_callback = 0;
u32 ts;
unsigned long flags;
- int dma, write, poll_dma;
+ int rflags;
spin_lock_irqsave(&lpbfifo.lock, flags);
ts = get_tbl();
@@ -223,87 +231,79 @@ static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id)
req = lpbfifo.req;
if (!req) {
spin_unlock_irqrestore(&lpbfifo.lock, flags);
- pr_err("bogus LPBFIFO IRQ\n");
+ pr_err("bogus SCLPC IRQ\n");
return IRQ_HANDLED;
}
- dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
- write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
- poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
+ rflags = req->flags;
- if (dma && !write) {
- spin_unlock_irqrestore(&lpbfifo.lock, flags);
- pr_err("bogus LPBFIFO IRQ (dma and not writting)\n");
- return IRQ_HANDLED;
- }
-
- if ((status & 0x01) == 0) {
+ /* check normal termination bit */
+ if (!(status_count & MPC52xx_SCLPC_STATUS_NT))
goto out;
- }
/* check abort bit */
- if (status & 0x10) {
- out_be32(&lpbfifo.regs->enable, 0x01010000);
+ if (status_count & MPC52xx_SCLPC_STATUS_AT) {
+ out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_ENABLE_RF);
do_callback = 1;
goto out;
}
- /* Read result from hardware */
- count = in_be32(&lpbfifo.regs->bytes_done_status.bytes_done);
- count &= 0x00ffffff;
+ if (!mpc52xx_lpbfifo_is_dma(rflags)) {
- if (!dma && !write) {
- /* copy the data out of the FIFO */
- reg = &lpbfifo.regs->fifo_data;
- data = req->data + req->pos;
- for (i = 0; i < count; i += 4)
- *data++ = in_be32(reg);
- }
+ /* bytes done */
+ status_count &= MPC52xx_SCLPC_STATUS_BYTES_DONE_MASK;
- /* Update transfer position and count */
- req->pos += count;
+ if (!mpc52xx_lpbfifo_is_write(rflags)) {
+ /* copy the data out of the FIFO */
+ reg = &lpbfifo.regs->fifo_data;
+ data = req->data + req->pos;
+ for (i = 0; i < status_count; i += sizeof(u32))
+ *data++ = in_be32(reg);
+ }
- /* Decide what to do next */
- if (req->size - req->pos)
- mpc52xx_lpbfifo_kick(req); /* more work to do */
- else
+ /* Update transfer position and count */
+ req->pos += status_count;
+
+ /* Decide what to do next */
+ if (req->size - req->pos)
+ mpc52xx_lpbfifo_kick(req); /* more work to do */
+ else
+ do_callback = 1;
+ }
+ else {
do_callback = 1;
+ }
out:
/* Clear the IRQ */
- out_8(&lpbfifo.regs->bytes_done_status.status, 0x01);
+ out_8(&lpbfifo.regs->bytes_done_status.status, BIT(0));
- if (dma && (status & 0x11)) {
- /*
- * Count the DMA as complete only when the FIFO completion
- * status or abort bits are set.
- *
- * (status & 0x01) should always be the case except sometimes
- * when using polled DMA.
- *
- * (status & 0x10) {transfer aborted}: This case needs more
- * testing.
- */
- bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
- }
req->last_byte = ((u8 *)req->data)[req->size - 1];
+ if (irq != 0) /* don't increment on polled case */
+ req->irq_count++;
+
/* When the do_callback flag is set; it means the transfer is finished
* so set the FIFO as idle */
- if (do_callback)
+ if (do_callback) {
lpbfifo.req = NULL;
+ out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_ENABLE_RF);
- if (irq != 0) /* don't increment on polled case */
- req->irq_count++;
+ req->irq_ticks += get_tbl() - ts;
+ spin_unlock_irqrestore(&lpbfifo.lock, flags);
- req->irq_ticks += get_tbl() - ts;
- spin_unlock_irqrestore(&lpbfifo.lock, flags);
+ /* Spinlock is released; it is now safe to call the callback */
+ if (req->callback)
+ req->callback(req);
- /* Spinlock is released; it is now safe to call the callback */
- if (do_callback && req->callback)
- req->callback(req);
+ return IRQ_HANDLED;
+ }
+ else {
+ req->irq_ticks += get_tbl() - ts;
+ spin_unlock_irqrestore(&lpbfifo.lock, flags);
- return IRQ_HANDLED;
+ return IRQ_HANDLED;
+ }
}
/**
@@ -313,48 +313,30 @@ out:
*/
static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id)
{
- struct mpc52xx_lpbfifo_request *req;
+ struct mpc52xx_lpbfifo *lpbfifo = dev_id;
unsigned long flags;
- u32 status;
- u32 ts;
-
- spin_lock_irqsave(&lpbfifo.lock, flags);
- ts = get_tbl();
-
- req = lpbfifo.req;
- if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) {
- spin_unlock_irqrestore(&lpbfifo.lock, flags);
- return IRQ_HANDLED;
- }
- if (irq != 0) /* don't increment on polled case */
- req->irq_count++;
+ spin_lock_irqsave(&lpbfifo->lock, flags);
+ // ts = get_tbl();
- if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) {
- spin_unlock_irqrestore(&lpbfifo.lock, flags);
+ if (!bcom_buffer_done(lpbfifo->bcom_cur_task)) {
- req->buffer_not_done_cnt++;
- if ((req->buffer_not_done_cnt % 1000) == 0)
- pr_err("transfer stalled\n");
+ if (bcom_queue_empty(lpbfifo->bcom_cur_task)) {
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
+ dev_err(lpbfifo->dev, "DMA queue empty\n");
+ }
+ else {
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
+ dev_err(lpbfifo->dev, "DMA buffer not done\n");
+ }
return IRQ_HANDLED;
}
- bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
-
- req->last_byte = ((u8 *)req->data)[req->size - 1];
-
- req->pos = status & 0x00ffffff;
-
- /* Mark the FIFO as idle */
- lpbfifo.req = NULL;
+ bcom_retrieve_buffer(lpbfifo->bcom_cur_task, NULL, NULL);
+ // req->irq_ticks += get_tbl() - ts;
- /* Release the lock before calling out to the callback. */
- req->irq_ticks += get_tbl() - ts;
- spin_unlock_irqrestore(&lpbfifo.lock, flags);
-
- if (req->callback)
- req->callback(req);
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
return IRQ_HANDLED;
}
@@ -365,14 +347,12 @@ static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id)
void mpc52xx_lpbfifo_poll(void)
{
struct mpc52xx_lpbfifo_request *req = lpbfifo.req;
- int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
- int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
/*
* For more information, see comments on the "Fat Lady"
*/
- if (dma && write)
- mpc52xx_lpbfifo_irq(0, NULL);
+ if (mpc52xx_lpbfifo_is_dma(req->flags) && (req->flags & MPC52XX_LPBFIFO_FLAG_WRITE))
+ mpc52xx_lpbfifo_sclpc_irq(0, NULL);
else
mpc52xx_lpbfifo_bcom_irq(0, NULL);
}
@@ -406,6 +386,7 @@ int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req)
mpc52xx_lpbfifo_kick(req);
spin_unlock_irqrestore(&lpbfifo.lock, flags);
+
return 0;
}
EXPORT_SYMBOL(mpc52xx_lpbfifo_submit);
@@ -419,7 +400,7 @@ void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req)
/* Put it into reset and clear the state */
bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);
bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);
- out_be32(&lpbfifo.regs->enable, 0x01010000);
+ out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_ENABLE_RF);
lpbfifo.req = NULL;
}
spin_unlock_irqrestore(&lpbfifo.lock, flags);
@@ -449,16 +430,16 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struct of_device_id *match)
spin_lock_init(&lpbfifo.lock);
/* Put FIFO into reset */
- out_be32(&lpbfifo.regs->enable, 0x01010000);
+ out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_ENABLE_RF);
/* register the interrupt handler */
- rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0,
+ rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_sclpc_irq, 0,
"mpc52xx-lpbfifo", &lpbfifo);
if (rc)
goto err_irq;
/* Request the Bestcomm receive (fifo --> memory) task and IRQ */
- lpbfifo.bcom_rx_task = bcom_gen_bd_rx_init(2,
+ lpbfifo.bcom_rx_task = bcom_gen_bd_rx_init(16,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
16*1024*1024);
@@ -472,16 +453,27 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struct of_device_id *match)
goto err_bcom_rx_irq;
/* Request the Bestcomm transmit (memory --> fifo) task and IRQ */
- lpbfifo.bcom_tx_task = bcom_gen_bd_tx_init(2,
+ lpbfifo.bcom_tx_task = bcom_gen_bd_tx_init(16,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC,
BCOM_IPR_SCLPC);
if (!lpbfifo.bcom_tx_task)
goto err_bcom_tx;
+ rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),
+ mpc52xx_lpbfifo_bcom_irq, 0,
+ "mpc52xx-lpbfifo-rx", &lpbfifo);
+ if (rc)
+ goto err_bcom_tx_irq;
+
+ lpbfifo.dma_irqs_enabled = 1;
+
lpbfifo.dev = &op->dev;
+
return 0;
+err_bcom_tx_irq:
+ free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo);
err_bcom_tx:
free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);
err_bcom_rx_irq:
--
1.6.5.5
--
Roman Fietze Telemotive AG Büro Mühlhausen
Breitwiesen 73347 Mühlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
More information about the Linuxppc-dev
mailing list