[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