All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch 2.6.25-rc2] SPI -- Freescale iMX SPI controller driver
@ 2008-02-18 15:45 Andrea Paterniani
       [not found] ` <FLEPLOLKEPNLMHOILNHPCEJPECAA.a.paterniani-03BXCEkGbFHYGGNLXY5/rw@public.gmane.org>
  0 siblings, 1 reply; 4+ messages in thread
From: Andrea Paterniani @ 2008-02-18 15:45 UTC (permalink / raw)
  To: Linux ARM Kernel, Andrew Morton, david-b-yBeKhBN/0LDR7s880joybQ,
	SPI Devel General

 Subject: [patch-2.6.25-rc2-spi_imx] arm: SPI controller driver for Freescale iMX
From: Andrea Paterniani <a.paterniani-03BXCEkGbFHYGGNLXY5/rw@public.gmane.org>

Kernel version: linux-2.6.25-rc2.
This patch fixes 2 bugs:
> flush() function revised.
> End of transfers management revised.
> Some cosmetics changes.

Signed-off-by: Andrea Paterniani <a.paterniani-03BXCEkGbFHYGGNLXY5/rw@public.gmane.org>
---

diff -uprN -X linux-2.6.25-rc2/Documentation/dontdiff linux-2.6.25-rc2/drivers/spi/spi_imx.c
linux-2.6.25-rc2-spi_imx/drivers/spi/spi_imx.c
--- linux-2.6.25-rc2/drivers/spi/spi_imx.c	2008-02-18 15:44:24.000000000 +0100
+++ linux-2.6.25-rc2-spi_imx/drivers/spi/spi_imx.c	2008-02-18 15:44:24.000000000 +0100
@@ -270,19 +270,26 @@ struct chip_data {

 static void pump_messages(struct work_struct *work);

-static int flush(struct driver_data *drv_data)
+static void flush(struct driver_data *drv_data)
 {
-	unsigned long limit = loops_per_jiffy << 1;
-	void __iomem *regs = drv_data->regs;
-	volatile u32 d;
+	void __iomem *regs = drv_data->regs;
+	u32 control;

 	dev_dbg(&drv_data->pdev->dev, "flush\n");
+
+	/* Wait for end of transaction */
 	do {
-		while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR)
-			d = readl(regs + SPI_RXDATA);
-	} while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--);
+		control = readl(regs + SPI_CONTROL);
+	} while (control & SPI_CONTROL_XCH);
+
+	/* Release chip select if requested, transfer delays are
+	   handled in pump_transfers */
+	if (drv_data->cs_change)
+		drv_data->cs_control(SPI_CS_DEASSERT);

-	return limit;
+	/* Disable SPI to flush FIFOs */
+	writel(control & ~SPI_CONTROL_SPIEN, regs + SPI_CONTROL);
+	writel(control, regs + SPI_CONTROL);
 }

 static void restore_state(struct driver_data *drv_data)
@@ -570,6 +577,7 @@ static void giveback(struct spi_message
 	writel(0, regs + SPI_INT_STATUS);
 	writel(0, regs + SPI_DMA);

+	/* Unconditioned deselct */
 	drv_data->cs_control(SPI_CS_DEASSERT);

 	message->state = NULL;
@@ -592,13 +600,10 @@ static void dma_err_handler(int channel,
 	/* Disable both rx and tx dma channels */
 	imx_dma_disable(drv_data->rx_channel);
 	imx_dma_disable(drv_data->tx_channel);
-
-	if (flush(drv_data) == 0)
-		dev_err(&drv_data->pdev->dev,
-				"dma_err_handler - flush failed\n");
-
 	unmap_dma_buffers(drv_data);

+	flush(drv_data);
+
 	msg->state = ERROR_STATE;
 	tasklet_schedule(&drv_data->pump_transfers);
 }
@@ -612,8 +617,7 @@ static void dma_tx_handler(int channel,
 	imx_dma_disable(channel);

 	/* Now waits for TX FIFO empty */
-	writel(readl(drv_data->regs + SPI_INT_STATUS) | SPI_INTEN_TE,
-			drv_data->regs + SPI_INT_STATUS);
+	writel(SPI_INTEN_TE, drv_data->regs + SPI_INT_STATUS);
 }

 static irqreturn_t dma_transfer(struct driver_data *drv_data)
@@ -621,19 +625,17 @@ static irqreturn_t dma_transfer(struct d
 	u32 status;
 	struct spi_message *msg = drv_data->cur_msg;
 	void __iomem *regs = drv_data->regs;
-	unsigned long limit;

 	status = readl(regs + SPI_INT_STATUS);

-	if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) {
+	if ((status & (SPI_INTEN_RO | SPI_STATUS_RO)) == (SPI_INTEN_RO | SPI_STATUS_RO)) {
 		writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);

+		imx_dma_disable(drv_data->tx_channel);
 		imx_dma_disable(drv_data->rx_channel);
 		unmap_dma_buffers(drv_data);

-		if (flush(drv_data) == 0)
-			dev_err(&drv_data->pdev->dev,
-				"dma_transfer - flush failed\n");
+		flush(drv_data);

 		dev_warn(&drv_data->pdev->dev,
 				"dma_transfer - fifo overun\n");
@@ -649,20 +651,16 @@ static irqreturn_t dma_transfer(struct d

 		if (drv_data->rx) {
 			/* Wait end of transfer before read trailing data */
-			limit = loops_per_jiffy << 1;
-			while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) &&
-					limit--);
-
-			if (limit == 0)
-				dev_err(&drv_data->pdev->dev,
-					"dma_transfer - end of tx failed\n");
-			else
-				dev_dbg(&drv_data->pdev->dev,
-					"dma_transfer - end of tx\n");
+			while (readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH);

 			imx_dma_disable(drv_data->rx_channel);
 			unmap_dma_buffers(drv_data);

+			/* Release chip select if requested, transfer delays are
+			   handled in pump_transfers() */
+			if (drv_data->cs_change)
+				drv_data->cs_control(SPI_CS_DEASSERT);
+
 			/* Calculate number of trailing data and read them */
 			dev_dbg(&drv_data->pdev->dev,
 				"dma_transfer - test = 0x%08X\n",
@@ -676,19 +674,12 @@ static irqreturn_t dma_transfer(struct d
 			/* Write only transfer */
 			unmap_dma_buffers(drv_data);

-			if (flush(drv_data) == 0)
-				dev_err(&drv_data->pdev->dev,
-					"dma_transfer - flush failed\n");
+			flush(drv_data);
 		}

 		/* End of transfer, update total byte transfered */
 		msg->actual_length += drv_data->len;

-		/* Release chip select if requested, transfer delays are
-		   handled in pump_transfers() */
-		if (drv_data->cs_change)
-			drv_data->cs_control(SPI_CS_DEASSERT);
-
 		/* Move to next transfer */
 		msg->state = next_transfer(drv_data);

@@ -711,44 +702,43 @@ static irqreturn_t interrupt_wronly_tran

 	status = readl(regs + SPI_INT_STATUS);

-	while (status & SPI_STATUS_TH) {
+	if (status & SPI_INTEN_TE) {
+		/* TXFIFO Empty Interrupt on the last transfered word */
+		writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
 		dev_dbg(&drv_data->pdev->dev,
-			"interrupt_wronly_transfer - status = 0x%08X\n", status);
+			"interrupt_wronly_transfer - end of tx\n");

-		/* Pump data */
-		if (write(drv_data)) {
-			writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
-				regs + SPI_INT_STATUS);
+		flush(drv_data);

-			dev_dbg(&drv_data->pdev->dev,
-				"interrupt_wronly_transfer - end of tx\n");
+		/* Update total byte transfered */
+		msg->actual_length += drv_data->len;

-			if (flush(drv_data) == 0)
-				dev_err(&drv_data->pdev->dev,
-					"interrupt_wronly_transfer - "
-					"flush failed\n");
+		/* Move to next transfer */
+		msg->state = next_transfer(drv_data);

-			/* End of transfer, update total byte transfered */
-			msg->actual_length += drv_data->len;
+		/* Schedule transfer tasklet */
+		tasklet_schedule(&drv_data->pump_transfers);

-			/* Release chip select if requested, transfer delays are
-			   handled in pump_transfers */
-			if (drv_data->cs_change)
-				drv_data->cs_control(SPI_CS_DEASSERT);
+		return IRQ_HANDLED;
+	} else {
+		while (status & SPI_STATUS_TH) {
+			dev_dbg(&drv_data->pdev->dev,
+				"interrupt_wronly_transfer - status = 0x%08X\n",
+				status);

-			/* Move to next transfer */
-			msg->state = next_transfer(drv_data);
+			/* Pump data */
+			if (write(drv_data)) {
+				/* End of TXFIFO writes,
+				   now wait until TXFIFO is empty */
+				writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
+				return IRQ_HANDLED;
+			}

-			/* Schedule transfer tasklet */
-			tasklet_schedule(&drv_data->pump_transfers);
+			status = readl(regs + SPI_INT_STATUS);

-			return IRQ_HANDLED;
+			/* We did something */
+			handled = IRQ_HANDLED;
 		}
-
-		status = readl(regs + SPI_INT_STATUS);
-
-		/* We did something */
-		handled = IRQ_HANDLED;
 	}

 	return handled;
@@ -758,45 +748,31 @@ static irqreturn_t interrupt_transfer(st
 {
 	struct spi_message *msg = drv_data->cur_msg;
 	void __iomem *regs = drv_data->regs;
-	u32 status;
+	u32 status, control;
 	irqreturn_t handled = IRQ_NONE;
 	unsigned long limit;

 	status = readl(regs + SPI_INT_STATUS);

-	while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
+	if (status & SPI_INTEN_TE) {
+		/* TXFIFO Empty Interrupt on the last transfered word */
+		writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
 		dev_dbg(&drv_data->pdev->dev,
-			"interrupt_transfer - status = 0x%08X\n", status);
-
-		if (status & SPI_STATUS_RO) {
-			writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
-				regs + SPI_INT_STATUS);
-
-			dev_warn(&drv_data->pdev->dev,
-				"interrupt_transfer - fifo overun\n"
-				"    data not yet written = %d\n"
-				"    data not yet read    = %d\n",
-				data_to_write(drv_data),
-				data_to_read(drv_data));
-
-			if (flush(drv_data) == 0)
-				dev_err(&drv_data->pdev->dev,
-					"interrupt_transfer - flush failed\n");
-
-			msg->state = ERROR_STATE;
-			tasklet_schedule(&drv_data->pump_transfers);
+			"interrupt_transfer - end of tx\n");

-			return IRQ_HANDLED;
-		}
-
-		/* Pump data */
-		read(drv_data);
-		if (write(drv_data)) {
-			writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
-				regs + SPI_INT_STATUS);
+		if (msg->state == ERROR_STATE) {
+			/* RXFIFO overrun was detected and message aborted */
+			flush(drv_data);
+		} else {
+			/* Wait for end of transaction */
+			do {
+				control = readl(regs + SPI_CONTROL);
+			} while (control & SPI_CONTROL_XCH);

-			dev_dbg(&drv_data->pdev->dev,
-				"interrupt_transfer - end of tx\n");
+			/* Release chip select if requested, transfer delays are
+			   handled in pump_transfers */
+			if (drv_data->cs_change)
+				drv_data->cs_control(SPI_CS_DEASSERT);

 			/* Read trailing bytes */
 			limit = loops_per_jiffy << 1;
@@ -810,27 +786,54 @@ static irqreturn_t interrupt_transfer(st
 				dev_dbg(&drv_data->pdev->dev,
 					"interrupt_transfer - end of rx\n");

-			/* End of transfer, update total byte transfered */
+			/* Update total byte transfered */
 			msg->actual_length += drv_data->len;

-			/* Release chip select if requested, transfer delays are
-			   handled in pump_transfers */
-			if (drv_data->cs_change)
-				drv_data->cs_control(SPI_CS_DEASSERT);
-
 			/* Move to next transfer */
 			msg->state = next_transfer(drv_data);
+		}

-			/* Schedule transfer tasklet */
-			tasklet_schedule(&drv_data->pump_transfers);
+		/* Schedule transfer tasklet */
+		tasklet_schedule(&drv_data->pump_transfers);

-			return IRQ_HANDLED;
-		}
+		return IRQ_HANDLED;
+	} else {
+		while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
+			dev_dbg(&drv_data->pdev->dev,
+				"interrupt_transfer - status = 0x%08X\n",
+				status);
+
+			if (status & SPI_STATUS_RO) {
+				/* RXFIFO overrun, abort message end wait
+				   until TXFIFO is empty */
+				writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
+
+				dev_warn(&drv_data->pdev->dev,
+					"interrupt_transfer - fifo overun\n"
+					"    data not yet written = %d\n"
+					"    data not yet read    = %d\n",
+					data_to_write(drv_data),
+					data_to_read(drv_data));
+
+				msg->state = ERROR_STATE;
+
+				return IRQ_HANDLED;
+			}

-		status = readl(regs + SPI_INT_STATUS);
+			/* Pump data */
+			read(drv_data);
+			if (write(drv_data)) {
+				/* End of TXFIFO writes,
+				   now wait until TXFIFO is empty */
+				writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
+				return IRQ_HANDLED;
+			}

-		/* We did something */
-		handled = IRQ_HANDLED;
+			status = readl(regs + SPI_INT_STATUS);
+
+			/* We did something */
+			handled = IRQ_HANDLED;
+		}
 	}

 	return handled;


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2008-02-20 22:20 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-18 15:45 [patch 2.6.25-rc2] SPI -- Freescale iMX SPI controller driver Andrea Paterniani
     [not found] ` <FLEPLOLKEPNLMHOILNHPCEJPECAA.a.paterniani-03BXCEkGbFHYGGNLXY5/rw@public.gmane.org>
2008-02-19  8:05   ` David Brownell
     [not found]     ` <200802190005.41396.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-02-19 10:37       ` R: " Andrea Paterniani
     [not found]         ` <FLEPLOLKEPNLMHOILNHPGEKJECAA.a.paterniani-03BXCEkGbFHYGGNLXY5/rw@public.gmane.org>
2008-02-20 22:20           ` David Brownell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.