From 7008284716403237f6bc7d7590b3ed073555bd56 Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Wed, 11 Jan 2012 22:25:11 +0100
Subject: [PATCH 34/34] spi/ath79: make chipselect logic more flexible

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
 arch/mips/ath79/mach-ap121.c                       |    6 ++
 arch/mips/ath79/mach-ap136.c                       |    6 ++
 arch/mips/ath79/mach-ap81.c                        |    6 ++
 arch/mips/ath79/mach-db120.c                       |    6 ++
 arch/mips/ath79/mach-pb44.c                        |    6 ++
 arch/mips/ath79/mach-ubnt-xm.c                     |    6 ++
 .../include/asm/mach-ath79/ath79_spi_platform.h    |    8 ++-
 drivers/spi/spi-ath79.c                            |   67 +++++++++++++-------
 8 files changed, 88 insertions(+), 23 deletions(-)

--- a/arch/mips/ath79/mach-ap121.c
+++ b/arch/mips/ath79/mach-ap121.c
@@ -58,12 +58,18 @@ static struct gpio_keys_button ap121_gpi
 	}
 };
 
+static struct ath79_spi_controller_data ap121_spi0_data = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 0,
+};
+
 static struct spi_board_info ap121_spi_info[] = {
 	{
 		.bus_num	= 0,
 		.chip_select	= 0,
 		.max_speed_hz	= 25000000,
 		.modalias	= "mx25l1606e",
+		.controller_data = &ap121_spi0_data,
 	}
 };
 
--- a/arch/mips/ath79/mach-ap136.c
+++ b/arch/mips/ath79/mach-ap136.c
@@ -98,12 +98,18 @@ static struct gpio_keys_button ap136_gpi
 	},
 };
 
+static struct ath79_spi_controller_data ap136_spi0_data = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 0,
+};
+
 static struct spi_board_info ap136_spi_info[] = {
 	{
 		.bus_num	= 0,
 		.chip_select	= 0,
 		.max_speed_hz	= 25000000,
 		.modalias	= "mx25l6405d",
+		.controller_data = &ap136_spi0_data,
 	}
 };
 
--- a/arch/mips/ath79/mach-ap81.c
+++ b/arch/mips/ath79/mach-ap81.c
@@ -67,12 +67,18 @@ static struct gpio_keys_button ap81_gpio
 	}
 };
 
+static struct ath79_spi_controller_data ap81_spi0_data = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 0,
+};
+
 static struct spi_board_info ap81_spi_info[] = {
 	{
 		.bus_num	= 0,
 		.chip_select	= 0,
 		.max_speed_hz	= 25000000,
 		.modalias	= "m25p64",
+		.controller_data = &ap81_spi0_data,
 	}
 };
 
--- a/arch/mips/ath79/mach-db120.c
+++ b/arch/mips/ath79/mach-db120.c
@@ -76,12 +76,18 @@ static struct gpio_keys_button db120_gpi
 	},
 };
 
+static struct ath79_spi_controller_data db120_spi0_data = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 0,
+};
+
 static struct spi_board_info db120_spi_info[] = {
 	{
 		.bus_num	= 0,
 		.chip_select	= 0,
 		.max_speed_hz	= 25000000,
 		.modalias	= "s25sl064a",
+		.controller_data = &db120_spi0_data,
 	}
 };
 
--- a/arch/mips/ath79/mach-pb44.c
+++ b/arch/mips/ath79/mach-pb44.c
@@ -87,12 +87,18 @@ static struct gpio_keys_button pb44_gpio
 	}
 };
 
+static struct ath79_spi_controller_data pb44_spi0_data = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 0,
+};
+
 static struct spi_board_info pb44_spi_info[] = {
 	{
 		.bus_num	= 0,
 		.chip_select	= 0,
 		.max_speed_hz	= 25000000,
 		.modalias	= "m25p64",
+		.controller_data = &pb44_spi0_data,
 	},
 };
 
--- a/arch/mips/ath79/mach-ubnt-xm.c
+++ b/arch/mips/ath79/mach-ubnt-xm.c
@@ -65,12 +65,18 @@ static struct gpio_keys_button ubnt_xm_g
 	}
 };
 
+static struct ath79_spi_controller_data ubnt_xm_spi0_data = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 0,
+};
+
 static struct spi_board_info ubnt_xm_spi_info[] = {
 	{
 		.bus_num	= 0,
 		.chip_select	= 0,
 		.max_speed_hz	= 25000000,
 		.modalias	= "mx25l6405d",
+		.controller_data = &ubnt_xm_spi0_data,
 	}
 };
 
--- a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
+++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
@@ -16,8 +16,14 @@ struct ath79_spi_platform_data {
 	unsigned	num_chipselect;
 };
 
+enum ath79_spi_cs_type {
+	ATH79_SPI_CS_TYPE_INTERNAL,
+	ATH79_SPI_CS_TYPE_GPIO,
+};
+
 struct ath79_spi_controller_data {
-	unsigned	gpio;
+	enum ath79_spi_cs_type cs_type;
+	unsigned cs_line;
 };
 
 #endif /* _ATH79_SPI_PLATFORM_H */
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -35,6 +35,8 @@
 #define ATH79_SPI_RRW_DELAY_FACTOR	12000
 #define MHZ				(1000 * 1000)
 
+#define ATH79_SPI_CS_LINE_MAX		2
+
 struct ath79_spi {
 	struct spi_bitbang	bitbang;
 	u32			ioc_base;
@@ -69,6 +71,7 @@ static void ath79_spi_chipselect(struct
 {
 	struct ath79_spi *sp = ath79_spidev_to_sp(spi);
 	int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active;
+	struct ath79_spi_controller_data *cdata = spi->controller_data;
 
 	if (is_active) {
 		/* set initial clock polarity */
@@ -80,20 +83,24 @@ static void ath79_spi_chipselect(struct
 		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
 	}
 
-	if (spi->chip_select) {
-		struct ath79_spi_controller_data *cdata = spi->controller_data;
-
-		/* SPI is normally active-low */
-		gpio_set_value(cdata->gpio, cs_high);
-	} else {
+	switch (cdata->cs_type) {
+	case ATH79_SPI_CS_TYPE_INTERNAL:
 		if (cs_high)
-			sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+			sp->ioc_base |= AR71XX_SPI_IOC_CS(cdata->cs_line);
 		else
-			sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+			sp->ioc_base &= ~AR71XX_SPI_IOC_CS(cdata->cs_line);
 
 		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
-	}
+		break;
 
+	case ATH79_SPI_CS_TYPE_GPIO:
+		/* SPI is normally active-low */
+		if (gpio_cansleep(cdata->cs_line))
+			gpio_set_value_cansleep(cdata->cs_line, cs_high);
+		else
+			gpio_set_value(cdata->cs_line, cs_high);
+		break;
+	}
 }
 
 static void ath79_spi_enable(struct ath79_spi *sp)
@@ -120,24 +127,30 @@ static void ath79_spi_disable(struct ath
 static int ath79_spi_setup_cs(struct spi_device *spi)
 {
 	struct ath79_spi_controller_data *cdata;
+	unsigned long flags;
 	int status;
 
 	cdata = spi->controller_data;
-	if (spi->chip_select && !cdata)
+	if (!cdata)
 		return -EINVAL;
 
 	status = 0;
-	if (spi->chip_select) {
-		unsigned long flags;
+	switch (cdata->cs_type) {
+	case ATH79_SPI_CS_TYPE_INTERNAL:
+		if (cdata->cs_line > ATH79_SPI_CS_LINE_MAX)
+			status = -EINVAL;
+		break;
 
+	case ATH79_SPI_CS_TYPE_GPIO:
 		flags = GPIOF_DIR_OUT;
 		if (spi->mode & SPI_CS_HIGH)
 			flags |= GPIOF_INIT_HIGH;
 		else
 			flags |= GPIOF_INIT_LOW;
 
-		status = gpio_request_one(cdata->gpio, flags,
+		status = gpio_request_one(cdata->cs_line, flags,
 					  dev_name(&spi->dev));
+		break;
 	}
 
 	return status;
@@ -145,9 +158,19 @@ static int ath79_spi_setup_cs(struct spi
 
 static void ath79_spi_cleanup_cs(struct spi_device *spi)
 {
-	if (spi->chip_select) {
-		struct ath79_spi_controller_data *cdata = spi->controller_data;
-		gpio_free(cdata->gpio);
+	struct ath79_spi_controller_data *cdata;
+
+	cdata = spi->controller_data;
+	if (!cdata)
+		return;
+
+	switch (cdata->cs_type) {
+	case ATH79_SPI_CS_TYPE_INTERNAL:
+		/* nothing to do */
+		break;
+	case ATH79_SPI_CS_TYPE_GPIO:
+		gpio_free(cdata->cs_line);
+		break;
 	}
 }
 
@@ -155,6 +178,9 @@ static int ath79_spi_setup(struct spi_de
 {
 	int status = 0;
 
+	if (spi->controller_data == NULL)
+		return -EINVAL;
+
 	if (spi->bits_per_word > 32)
 		return -EINVAL;
 
@@ -215,6 +241,10 @@ static int ath79_spi_probe(struct platfo
 	unsigned long rate;
 	int ret;
 
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -EINVAL;
+
 	master = spi_alloc_master(&pdev->dev, sizeof(*sp));
 	if (master == NULL) {
 		dev_err(&pdev->dev, "failed to allocate spi master\n");
@@ -224,14 +254,10 @@ static int ath79_spi_probe(struct platfo
 	sp = spi_master_get_devdata(master);
 	platform_set_drvdata(pdev, sp);
 
-	pdata = pdev->dev.platform_data;
-
 	master->setup = ath79_spi_setup;
 	master->cleanup = ath79_spi_cleanup;
-	if (pdata) {
-		master->bus_num = pdata->bus_num;
-		master->num_chipselect = pdata->num_chipselect;
-	}
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->num_chipselect;
 
 	sp->bitbang.master = spi_master_get(master);
 	sp->bitbang.chipselect = ath79_spi_chipselect;