[PATCH] spi: pxa2xx_spi introduce chip select gpio to simplify the common cases | |
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] | |
Most SPI peripherals use GPIOs as their chip select, introduce .gpio_cs
for this.
Signed-off-by: Eric Miao <eric.miao@xxxxxxxxxxx>
---
arch/arm/mach-pxa/include/mach/pxa2xx_spi.h | 1 +
drivers/spi/pxa2xx_spi.c | 88 +++++++++++++++++++++++----
2 files changed, 76 insertions(+), 13 deletions(-)
diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx_spi.h
b/arch/arm/mach-pxa/include/mach/pxa2xx_spi.h
index 2206cb6..b87cecd 100644
--- a/arch/arm/mach-pxa/include/mach/pxa2xx_spi.h
+++ b/arch/arm/mach-pxa/include/mach/pxa2xx_spi.h
@@ -38,6 +38,7 @@ struct pxa2xx_spi_chip {
u8 dma_burst_size;
u32 timeout;
u8 enable_loopback;
+ int gpio_cs;
void (*cs_control)(u32 command);
};
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index 34c7c98..e29b31a 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -28,6 +28,7 @@
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -164,6 +165,8 @@ struct chip_data {
u8 enable_dma;
u8 bits_per_word;
u32 speed_hz;
+ int gpio_cs;
+ unsigned gpio_cs_inverted : 1;
int (*write)(struct driver_data *drv_data);
int (*read)(struct driver_data *drv_data);
void (*cs_control)(u32 command);
@@ -171,6 +174,32 @@ struct chip_data {
static void pump_messages(struct work_struct *work);
+static void cs_assert(struct driver_data *drv_data)
+{
+ struct chip_data *chip = drv_data->cur_chip;
+
+ if (chip->cs_control) {
+ chip->cs_control(PXA2XX_CS_ASSERT);
+ return;
+ }
+
+ if (chip->gpio_cs != -1)
+ gpio_set_value(chip->gpio_cs, chip->gpio_cs_inverted);
+}
+
+static void cs_deassert(struct driver_data *drv_data)
+{
+ struct chip_data *chip = drv_data->cur_chip;
+
+ if (chip->cs_control) {
+ chip->cs_control(PXA2XX_CS_DEASSERT);
+ return;
+ }
+
+ if (chip->gpio_cs != -1)
+ gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted);
+}
+
static int flush(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
@@ -187,10 +216,6 @@ static int flush(struct driver_data *drv_data)
return limit;
}
-static void null_cs_control(u32 command)
-{
-}
-
static int null_writer(struct driver_data *drv_data)
{
void __iomem *reg = drv_data->ioaddr;
@@ -398,7 +423,6 @@ static void giveback(struct driver_data *drv_data)
msg = drv_data->cur_msg;
drv_data->cur_msg = NULL;
drv_data->cur_transfer = NULL;
- drv_data->cur_chip = NULL;
queue_work(drv_data->workqueue, &drv_data->pump_messages);
spin_unlock_irqrestore(&drv_data->lock, flags);
@@ -407,7 +431,9 @@ static void giveback(struct driver_data *drv_data)
transfer_list);
if (!last_transfer->cs_change)
- drv_data->cs_control(PXA2XX_CS_DEASSERT);
+ cs_deassert(drv_data);
+
+ drv_data->cur_chip = NULL;
msg->state = NULL;
if (msg->complete)
@@ -493,7 +519,7 @@ static void dma_transfer_complete(struct
driver_data *drv_data)
/* Release chip select if requested, transfer delays are
* handled in pump_transfers */
if (drv_data->cs_change)
- drv_data->cs_control(PXA2XX_CS_DEASSERT);
+ cs_deassert(drv_data);
/* Move to next transfer */
msg->state = next_transfer(drv_data);
@@ -605,7 +631,7 @@ static void int_transfer_complete(struct
driver_data *drv_data)
/* Release chip select if requested, transfer delays are
* handled in pump_transfers */
if (drv_data->cs_change)
- drv_data->cs_control(PXA2XX_CS_DEASSERT);
+ cs_deassert(drv_data);
/* Move to next transfer */
drv_data->cur_msg->state = next_transfer(drv_data);
@@ -868,7 +894,6 @@ static void pump_transfers(unsigned long data)
}
drv_data->n_bytes = chip->n_bytes;
drv_data->dma_width = chip->dma_width;
- drv_data->cs_control = chip->cs_control;
drv_data->tx = (void *)transfer->tx_buf;
drv_data->tx_end = drv_data->tx + transfer->len;
drv_data->rx = transfer->rx_buf;
@@ -1020,7 +1045,7 @@ static void pump_transfers(unsigned long data)
* this driver uses struct pxa2xx_spi_chip.cs_control to
* specify a CS handling function, and it ignores most
* struct spi_device.mode[s], including SPI_CS_HIGH */
- drv_data->cs_control(PXA2XX_CS_ASSERT);
+ cs_assert(drv_data);
/* after chip select, release the data by enabling service
* requests and interrupts, without changing any mode bits */
@@ -1098,6 +1123,40 @@ static int transfer(struct spi_device *spi,
struct spi_message *msg)
/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA)
+static int setup_cs(struct spi_device *spi, struct chip_data *chip,
+ struct pxa2xx_spi_chip *chip_info)
+{
+ int err = 0;
+
+ /* NOTE: setup() can be called multiple times, possibly with
+ * different chip_info, previously requested GPIO shall be
+ * released, stumped :(
+ */
+ if (chip->gpio_cs != -1)
+ gpio_free(chip->gpio_cs);
+
+ if (chip_info->cs_control) {
+ chip->cs_control = chip_info->cs_control;
+ return 0;
+ }
+
+ if (chip_info->gpio_cs != -1) {
+ err = gpio_request(chip_info->gpio_cs, "SPI_CS");
+ if (err) {
+ dev_err(&spi->dev, "failed to request CS GPIO%d\n",
+ chip_info->gpio_cs);
+ return err;
+ }
+
+ chip->gpio_cs = chip_info->gpio_cs;
+ chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH;
+
+ gpio_direction_output(chip->gpio_cs, !chip->gpio_cs_inverted);
+ }
+
+ return err;
+}
+
static int setup(struct spi_device *spi)
{
struct pxa2xx_spi_chip *chip_info = NULL;
@@ -1141,7 +1200,7 @@ static int setup(struct spi_device *spi)
return -ENOMEM;
}
- chip->cs_control = null_cs_control;
+ chip->gpio_cs = -1;
chip->enable_dma = 0;
chip->timeout = 1000;
chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1);
@@ -1156,8 +1215,8 @@ static int setup(struct spi_device *spi)
/* chip_info isn't always needed */
chip->cr1 = 0;
if (chip_info) {
- if (chip_info->cs_control)
- chip->cs_control = chip_info->cs_control;
+ if (setup_cs(spi, chip, chip_info))
+ return -EINVAL;
chip->timeout = chip_info->timeout;
@@ -1245,6 +1304,9 @@ static void cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
+ if (chip->gpio_cs != -1)
+ gpio_free(chip->gpio_cs);
+
kfree(chip);
}
--
1.5.4.3
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
[Site Home] [Linux Arm] [Fedora ARM] [Gcc Help] [Git] [DCCP] [IETF Announce] [Security] [PDAs] [Linux] [Linux Book List] [Linux MIPS] [Yosemite Campsites] [Photos]
![]() |
|