All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
	David Brownell
	<dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>,
	Grant Likely
	<grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Cc: uclinux-dist-devel-ZG0+EudsQA8dtHy/vicBwGD2FQJk+8+b@public.gmane.org,
	Yi Li <yi.li-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Subject: [PATCH 04/28] Blackfin SPI: utilize the SPI interrupt in PIO mode
Date: Sun, 17 Oct 2010 18:59:17 -0400	[thread overview]
Message-ID: <1287356381-31495-5-git-send-email-vapier@gentoo.org> (raw)
In-Reply-To: <1287356381-31495-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>

From: Yi Li <yi.li-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>

The current behavior in PIO mode is to poll the SPI status registers which
can obviously lead to higher latencies when doing a lot of SPI traffic.
There is a SPI interrupt which can be used instead to signal individual
completion of transactions.

Signed-off-by: Yi Li <yi.li-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
---
 drivers/spi/spi_bfin5xx.c |  235 +++++++++++++++++++++++++++++++++++----------
 1 files changed, 185 insertions(+), 50 deletions(-)

diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c
index b835254..3736c35 100644
--- a/drivers/spi/spi_bfin5xx.c
+++ b/drivers/spi/spi_bfin5xx.c
@@ -92,6 +92,9 @@ struct driver_data {
 	dma_addr_t rx_dma;
 	dma_addr_t tx_dma;
 
+	int irq_requested;
+	int spi_irq;
+
 	size_t rx_map_len;
 	size_t tx_map_len;
 	u8 n_bytes;
@@ -115,6 +118,7 @@ struct chip_data {
 	u16 cs_chg_udelay;	/* Some devices require > 255usec delay */
 	u32 cs_gpio;
 	u16 idle_tx_val;
+	u8 pio_interrupt;	/* use spi data irq */
 	void (*write) (struct driver_data *);
 	void (*read) (struct driver_data *);
 	void (*duplex) (struct driver_data *);
@@ -525,6 +529,79 @@ static void bfin_spi_giveback(struct driver_data *drv_data)
 		msg->complete(msg->context);
 }
 
+/* spi data irq handler */
+static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
+{
+	struct driver_data *drv_data = dev_id;
+	struct chip_data *chip = drv_data->cur_chip;
+	struct spi_message *msg = drv_data->cur_msg;
+	int n_bytes = drv_data->n_bytes;
+
+	/* wait until transfer finished. */
+	while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+		cpu_relax();
+
+	if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) ||
+		(drv_data->rx && drv_data->rx >= (drv_data->rx_end - n_bytes))) {
+		/* last read */
+		if (drv_data->rx) {
+			dev_dbg(&drv_data->pdev->dev, "last read\n");
+			if (n_bytes == 2)
+				*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+			else if (n_bytes == 1)
+				*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+			drv_data->rx += n_bytes;
+		}
+
+		msg->actual_length += drv_data->len_in_bytes;
+		if (drv_data->cs_change)
+			bfin_spi_cs_deactive(drv_data, chip);
+		/* Move to next transfer */
+		msg->state = bfin_spi_next_transfer(drv_data);
+
+		disable_irq(drv_data->spi_irq);
+
+		/* Schedule transfer tasklet */
+		tasklet_schedule(&drv_data->pump_transfers);
+		return IRQ_HANDLED;
+	}
+
+	if (drv_data->rx && drv_data->tx) {
+		/* duplex */
+		dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
+		if (drv_data->n_bytes == 2) {
+			*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+			write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+		} else if (drv_data->n_bytes == 1) {
+			*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+			write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+		}
+	} else if (drv_data->rx) {
+		/* read */
+		dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
+		if (drv_data->n_bytes == 2)
+			*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+		else if (drv_data->n_bytes == 1)
+			*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+		write_TDBR(drv_data, chip->idle_tx_val);
+	} else if (drv_data->tx) {
+		/* write */
+		dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
+		bfin_spi_dummy_read(drv_data);
+		if (drv_data->n_bytes == 2)
+			write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+		else if (drv_data->n_bytes == 1)
+			write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+	}
+
+	if (drv_data->tx)
+		drv_data->tx += n_bytes;
+	if (drv_data->rx)
+		drv_data->rx += n_bytes;
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
 {
 	struct driver_data *drv_data = dev_id;
@@ -700,6 +777,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
 
 	default:
 		/* No change, the same as default setting */
+		transfer->bits_per_word = chip->bits_per_word;
 		drv_data->n_bytes = chip->n_bytes;
 		width = chip->width;
 		drv_data->write = drv_data->tx ? chip->write : bfin_spi_null_writer;
@@ -842,60 +920,86 @@ static void bfin_spi_pump_transfers(unsigned long data)
 		dma_enable_irq(drv_data->dma_channel);
 		local_irq_restore(flags);
 
-	} else {
-		/* IO mode write then read */
-		dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
+		return;
+	}
 
-		/* we always use SPI_WRITE mode. SPI_READ mode
-		   seems to have problems with setting up the
-		   output value in TDBR prior to the transfer. */
+	if (chip->pio_interrupt) {
+		/* use write mode. spi irq should have been disabled */
+		cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD));
 		write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
 
-		if (full_duplex) {
-			/* full duplex mode */
-			BUG_ON((drv_data->tx_end - drv_data->tx) !=
-			       (drv_data->rx_end - drv_data->rx));
-			dev_dbg(&drv_data->pdev->dev,
-				"IO duplex: cr is 0x%x\n", cr);
-
-			drv_data->duplex(drv_data);
-
-			if (drv_data->tx != drv_data->tx_end)
-				tranf_success = 0;
-		} else if (drv_data->tx != NULL) {
-			/* write only half duplex */
-			dev_dbg(&drv_data->pdev->dev,
-				"IO write: cr is 0x%x\n", cr);
+		/* discard old RX data and clear RXS */
+		bfin_spi_dummy_read(drv_data);
 
-			drv_data->write(drv_data);
+		/* start transfer */
+		if (drv_data->tx == NULL)
+			write_TDBR(drv_data, chip->idle_tx_val);
+		else {
+			if (transfer->bits_per_word == 8)
+				write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+			else if (transfer->bits_per_word == 16)
+				write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+			drv_data->tx += drv_data->n_bytes;
+		}
 
-			if (drv_data->tx != drv_data->tx_end)
-				tranf_success = 0;
-		} else if (drv_data->rx != NULL) {
-			/* read only half duplex */
-			dev_dbg(&drv_data->pdev->dev,
-				"IO read: cr is 0x%x\n", cr);
+		/* once TDBR is empty, interrupt is triggered */
+		enable_irq(drv_data->spi_irq);
+		return;
+	}
 
-			drv_data->read(drv_data);
-			if (drv_data->rx != drv_data->rx_end)
-				tranf_success = 0;
-		}
+	/* IO mode */
+	dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
+
+	/* we always use SPI_WRITE mode. SPI_READ mode
+	   seems to have problems with setting up the
+	   output value in TDBR prior to the transfer. */
+	write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
+
+	if (full_duplex) {
+		/* full duplex mode */
+		BUG_ON((drv_data->tx_end - drv_data->tx) !=
+		       (drv_data->rx_end - drv_data->rx));
+		dev_dbg(&drv_data->pdev->dev,
+			"IO duplex: cr is 0x%x\n", cr);
+
+		drv_data->duplex(drv_data);
+
+		if (drv_data->tx != drv_data->tx_end)
+			tranf_success = 0;
+	} else if (drv_data->tx != NULL) {
+		/* write only half duplex */
+		dev_dbg(&drv_data->pdev->dev,
+			"IO write: cr is 0x%x\n", cr);
+
+		drv_data->write(drv_data);
+
+		if (drv_data->tx != drv_data->tx_end)
+			tranf_success = 0;
+	} else if (drv_data->rx != NULL) {
+		/* read only half duplex */
+		dev_dbg(&drv_data->pdev->dev,
+			"IO read: cr is 0x%x\n", cr);
+
+		drv_data->read(drv_data);
+		if (drv_data->rx != drv_data->rx_end)
+			tranf_success = 0;
+	}
 
-		if (!tranf_success) {
-			dev_dbg(&drv_data->pdev->dev,
-				"IO write error!\n");
-			message->state = ERROR_STATE;
-		} else {
-			/* Update total byte transfered */
-			message->actual_length += drv_data->len_in_bytes;
-			/* Move to next transfer of this msg */
-			message->state = bfin_spi_next_transfer(drv_data);
-			if (drv_data->cs_change)
-				bfin_spi_cs_deactive(drv_data, chip);
-		}
-		/* Schedule next transfer tasklet */
-		tasklet_schedule(&drv_data->pump_transfers);
+	if (!tranf_success) {
+		dev_dbg(&drv_data->pdev->dev,
+			"IO write error!\n");
+		message->state = ERROR_STATE;
+	} else {
+		/* Update total byte transfered */
+		message->actual_length += drv_data->len_in_bytes;
+		/* Move to next transfer of this msg */
+		message->state = bfin_spi_next_transfer(drv_data);
+		if (drv_data->cs_change)
+			bfin_spi_cs_deactive(drv_data, chip);
 	}
+
+	/* Schedule next transfer tasklet */
+	tasklet_schedule(&drv_data->pump_transfers);
 }
 
 /* pop a msg from queue and kick off real transfer */
@@ -1047,6 +1151,7 @@ static int bfin_spi_setup(struct spi_device *spi)
 		chip->cs_chg_udelay = chip_info->cs_chg_udelay;
 		chip->cs_gpio = chip_info->cs_gpio;
 		chip->idle_tx_val = chip_info->idle_tx_val;
+		chip->pio_interrupt = chip_info->pio_interrupt;
 	}
 
 	/* translate common spi framework into our register */
@@ -1096,6 +1201,11 @@ static int bfin_spi_setup(struct spi_device *spi)
 		goto error;
 	}
 
+	if (chip->enable_dma && chip->pio_interrupt) {
+		dev_err(&spi->dev, "enable_dma is set, "
+				"do not set pio_interrupt\n");
+		goto error;
+	}
 	/*
 	 * if any one SPI chip is registered and wants DMA, request the
 	 * DMA channel for it
@@ -1119,6 +1229,18 @@ static int bfin_spi_setup(struct spi_device *spi)
 		dma_disable_irq(drv_data->dma_channel);
 	}
 
+	if (chip->pio_interrupt && !drv_data->irq_requested) {
+		ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler,
+			IRQF_DISABLED, "BFIN_SPI", drv_data);
+		if (ret) {
+			dev_err(&spi->dev, "Unable to register spi IRQ\n");
+			goto error;
+		}
+		drv_data->irq_requested = 1;
+		/* we use write mode, spi irq has to be disabled here */
+		disable_irq(drv_data->spi_irq);
+	}
+
 	if (chip->chip_select_num == 0) {
 		ret = gpio_request(chip->cs_gpio, spi->modalias);
 		if (ret) {
@@ -1328,11 +1450,19 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
 		goto out_error_ioremap;
 	}
 
-	drv_data->dma_channel = platform_get_irq(pdev, 0);
-	if (drv_data->dma_channel < 0) {
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (res == NULL) {
 		dev_err(dev, "No DMA channel specified\n");
 		status = -ENOENT;
-		goto out_error_no_dma_ch;
+		goto out_error_free_io;
+	}
+	drv_data->dma_channel = res->start;
+
+	drv_data->spi_irq = platform_get_irq(pdev, 0);
+	if (drv_data->spi_irq < 0) {
+		dev_err(dev, "No spi pio irq specified\n");
+		status = -ENOENT;
+		goto out_error_free_io;
 	}
 
 	/* Initial and start queue */
@@ -1375,7 +1505,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
 
 out_error_queue_alloc:
 	bfin_spi_destroy_queue(drv_data);
-out_error_no_dma_ch:
+out_error_free_io:
 	iounmap((void *) drv_data->regs_base);
 out_error_ioremap:
 out_error_get_res:
@@ -1407,6 +1537,11 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev)
 			free_dma(drv_data->dma_channel);
 	}
 
+	if (drv_data->irq_requested) {
+		free_irq(drv_data->spi_irq, drv_data);
+		drv_data->irq_requested = 0;
+	}
+
 	/* Disconnect from the SPI framework */
 	spi_unregister_master(drv_data->master);
 
-- 
1.7.3.1

  parent reply	other threads:[~2010-10-17 22:59 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-10-17 22:59 [PATCH 00/28] Blackfin SPI updates for 2.6.36 Mike Frysinger
     [not found] ` <1287356381-31495-1-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
2010-10-17 22:59   ` [PATCH 01/28] Blackfin SPI: fix resources leakage Mike Frysinger
2010-10-17 22:59   ` [PATCH 02/28] Blackfin SPI: work around anomaly 05000119 Mike Frysinger
2010-10-17 22:59   ` [PATCH 03/28] Blackfin SPI: force sane master-mode state at boot Mike Frysinger
2010-10-17 22:59   ` Mike Frysinger [this message]
2010-10-17 22:59   ` [PATCH 05/28] Blackfin SPI: fix CS handling Mike Frysinger
2010-10-17 22:59   ` [PATCH 06/28] Blackfin SPI: drop custom cs_change_per_word support Mike Frysinger
2010-10-17 22:59   ` [PATCH 07/28] Blackfin SPI: punt useless null read/write funcs Mike Frysinger
2010-10-17 22:59   ` [PATCH 08/28] Blackfin SPI: fix up some unused/misleading comments Mike Frysinger
2010-10-17 22:59   ` [PATCH 09/28] Blackfin SPI: convert queue run state to true/false Mike Frysinger
2010-10-17 22:59   ` [PATCH 10/28] Blackfin SPI: convert read/write/duplex funcs to a dedicated ops structure Mike Frysinger
2010-10-17 22:59   ` [PATCH 11/28] Blackfin SPI: convert struct names to something more logical Mike Frysinger
     [not found]     ` <1287356381-31495-12-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
2010-10-18  6:02       ` Grant Likely
     [not found]         ` <20101018060253.GA19399-MrY2KI0G/OVr83L8+7iqerDks+cytr/Z@public.gmane.org>
2010-10-18  6:10           ` Mike Frysinger
     [not found]             ` <AANLkTimJtoRWL0auw1xHthXbkBc6L2b1ph18WZA+_PdY-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18  6:20               ` Grant Likely
     [not found]                 ` <20101018062025.GD19399-MrY2KI0G/OVr83L8+7iqerDks+cytr/Z@public.gmane.org>
2010-10-18  6:28                   ` Mike Frysinger
     [not found]                     ` <AANLkTi=pBYVLCYVtwd3L=O31O4FxOPwQLJQK5WUkdRS9-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18  6:31                       ` Grant Likely
2010-10-17 22:59   ` [PATCH 12/28] Blackfin SPI: drop extra memory we don't need Mike Frysinger
2010-10-17 22:59   ` [PATCH 13/28] Blackfin SPI: use the SPI namespaced bit names Mike Frysinger
2010-10-17 22:59   ` [PATCH 14/28] Blackfin: SPI: expand SPI bitmasks Mike Frysinger
     [not found]     ` <1287356381-31495-15-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
2010-10-18  6:04       ` Grant Likely
     [not found]         ` <20101018060416.GB19399-MrY2KI0G/OVr83L8+7iqerDks+cytr/Z@public.gmane.org>
2010-10-18  6:11           ` Mike Frysinger
2010-10-17 22:59   ` [PATCH 15/28] Blackfin SPI: redo GPIO CS handling Mike Frysinger
2010-10-17 22:59   ` [PATCH 16/28] Blackfin SPI: save/restore state when suspending/resuming Mike Frysinger
2010-10-17 22:59   ` [PATCH 17/28] Blackfin SPI: sync hardware state before reprogramming everything Mike Frysinger
2010-10-17 22:59   ` [PATCH 18/28] Blackfin SPI: use nosync when disabling the IRQ from the IRQ handler Mike Frysinger
2010-10-17 22:59   ` [PATCH 19/28] Blackfin SPI: push all size checks into the transfer function Mike Frysinger
2010-10-17 22:59   ` [PATCH 20/28] Blackfin SPI: reset ctl_reg bits when setup is run again on a device Mike Frysinger
2010-10-17 22:59   ` [PATCH 21/28] Blackfin SPI: combine duplicate SPI_CTL read/write logic Mike Frysinger
2010-10-17 22:59   ` [PATCH 22/28] Blackfin SPI: use dma_disable_irq_nosync() in irq handler Mike Frysinger
2010-10-17 22:59   ` [PATCH 23/28] Blackfin SPI: reject unsupported SPI modes Mike Frysinger
2010-10-17 22:59   ` [PATCH 24/28] Blackfin SPI: fix typo in comment Mike Frysinger
2010-10-17 22:59   ` [PATCH 25/28] Blackfin SPI: cs should be always low when a new transfer begins Mike Frysinger
2010-10-17 22:59   ` [PATCH 26/28] Blackfin SPI: warn when CS is driven by hardware (CPHA=0) Mike Frysinger
2010-10-17 22:59   ` [PATCH 27/28] Blackfin SPI: check per-transfer bits_per_word Mike Frysinger
2010-10-17 22:59   ` [PATCH 28/28] Blackfin SPI: init early Mike Frysinger
     [not found]     ` <1287356381-31495-29-git-send-email-vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
2010-10-18  6:12       ` Grant Likely
     [not found]         ` <20101018061211.GC19399-MrY2KI0G/OVr83L8+7iqerDks+cytr/Z@public.gmane.org>
2010-10-18  6:14           ` Mike Frysinger
     [not found]             ` <AANLkTi=AwuK8-U8+Ezm1JVrvD1Taqwq53EzahA5Ag4Uf-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18  6:21               ` Grant Likely
2010-10-18  6:28   ` [PATCH 00/28] Blackfin SPI updates for 2.6.36 Grant Likely
     [not found]     ` <20101018062834.GG19399-MrY2KI0G/OVr83L8+7iqerDks+cytr/Z@public.gmane.org>
2010-10-18  6:34       ` Mike Frysinger
     [not found]         ` <AANLkTims9Z+XGXMdcAH53OBO=S9aYiszxHczmsTn06Gd-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18  6:37           ` Mike Frysinger
     [not found]             ` <AANLkTim1p0Ukufh1dWGSk8L2yA4xwQzsWmr8EK_AbfLP-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18  6:49               ` Grant Likely
2010-10-18  6:49           ` Mike Frysinger
     [not found]             ` <AANLkTimmVQ0EtkACtXqxO0vkXfN9FoyF9c6OLo3JNPUC-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18  7:04               ` Grant Likely
     [not found]                 ` <AANLkTimE33PngPfSBkQDVR7x6H8fMNQ9z3sXNC9KZPWf-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-10-18 15:27                   ` Grant Likely
2010-10-18  6:46   ` [PATCH 29/28] spi/bfin_spi: namespace local structs Mike Frysinger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1287356381-31495-5-git-send-email-vapier@gentoo.org \
    --to=vapier-abrp7r+bbdudnm+yrofe0a@public.gmane.org \
    --cc=dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
    --cc=grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org \
    --cc=spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
    --cc=uclinux-dist-devel-ZG0+EudsQA8dtHy/vicBwGD2FQJk+8+b@public.gmane.org \
    --cc=yi.li-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.