From: chris@techworks.ie (Christian Gagneraud)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH RFC 1/3] AT91: SPI: Add CS decode support
Date: Tue, 31 May 2011 17:04:21 +0100 [thread overview]
Message-ID: <1306857863-13424-2-git-send-email-chris@techworks.ie> (raw)
In-Reply-To: <1306857863-13424-1-git-send-email-chris@techworks.ie>
With CS decode, up to 15 Chip Select signals can be generated with the
four lines using an external 4- to 16-bit decoder.
Signed-off-by: Christian Gagneraud <chris@techworks.ie>
---
drivers/spi/atmel_spi.c | 137 +++++++++++++++++++++++++++++++---------------
1 files changed, 92 insertions(+), 45 deletions(-)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 08711e9..df0dec9 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -50,12 +50,8 @@ struct atmel_spi {
void *buffer;
dma_addr_t buffer_dma;
-};
-/* Controller-specific per-slave state */
-struct atmel_spi_device {
- unsigned int npcs_pin;
- u32 csr;
+ struct atmel_spi_data *board_data;
};
#define BUFFER_SIZE PAGE_SIZE
@@ -105,25 +101,34 @@ static bool atmel_spi_is_v2(void)
static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
{
- struct atmel_spi_device *asd = spi->controller_state;
+ u32 csr;
unsigned active = spi->mode & SPI_CS_HIGH;
u32 mr;
+ int i;
+ unsigned cs_decoding = as->board_data->flags & ATMEL_SPI_CS_DEC;
if (atmel_spi_is_v2()) {
+ csr = (u32)spi->controller_state;
/*
* Always use CSR0. This ensures that the clock
* switches to the correct idle polarity before we
* toggle the CS.
*/
- spi_writel(as, CSR0, asd->csr);
+ spi_writel(as, CSR0, csr);
spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS)
| SPI_BIT(MSTR));
mr = spi_readl(as, MR);
- gpio_set_value(asd->npcs_pin, active);
+ if (cs_decoding) {
+ for (i = 0; i < as->board_data->num_cs_pin; i++) {
+ unsigned gpio = as->board_data->cs_pins[i].gpio;
+ unsigned val = 1 & (spi->chip_select>>i);
+ gpio_set_value(gpio, val);
+ }
+ } else
+ gpio_set_value(as->board_data->cs_pins[spi->chip_select].gpio,
+ active);
} else {
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
- int i;
- u32 csr;
/* Make sure clock polarity is correct */
for (i = 0; i < spi->master->num_chipselect; i++) {
@@ -136,36 +141,43 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
mr = spi_readl(as, MR);
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
if (spi->chip_select != 0)
- gpio_set_value(asd->npcs_pin, active);
+ gpio_set_value(as->board_data->cs_pins[spi->chip_select].gpio, active);
spi_writel(as, MR, mr);
}
dev_dbg(&spi->dev, "activate %u%s, mr %08x\n",
- asd->npcs_pin, active ? " (high)" : "",
+ spi->chip_select, active ? " (high)" : "",
mr);
}
static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
{
- struct atmel_spi_device *asd = spi->controller_state;
unsigned active = spi->mode & SPI_CS_HIGH;
u32 mr;
+ unsigned cs_decoding = as->board_data->flags & ATMEL_SPI_CS_DEC;
+ int i;
/* only deactivate *this* device; sometimes transfers to
* another device may be active when this routine is called.
*/
mr = spi_readl(as, MR);
- if (~SPI_BFEXT(PCS, mr) & (1 << spi->chip_select)) {
+ if ((!cs_decoding && (~SPI_BFEXT(PCS, mr) & (1 << spi->chip_select))) ||
+ (cs_decoding && (~SPI_BFEXT(PCS, mr) & spi->chip_select))) {
mr = SPI_BFINS(PCS, 0xf, mr);
spi_writel(as, MR, mr);
}
dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x\n",
- asd->npcs_pin, active ? " (low)" : "",
+ spi->chip_select, active ? " (low)" : "",
mr);
- if (atmel_spi_is_v2() || spi->chip_select != 0)
- gpio_set_value(asd->npcs_pin, !active);
+ if (atmel_spi_is_v2() || spi->chip_select != 0) {
+ if (cs_decoding) {
+ for (i = 0; i < as->board_data->num_cs_pin; i++)
+ gpio_set_value(as->board_data->cs_pins[i].gpio, 1);
+ } else
+ gpio_set_value(as->board_data->cs_pins[spi->chip_select].gpio, !active);
+ }
}
static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
@@ -538,14 +550,14 @@ atmel_spi_interrupt(int irq, void *dev_id)
static int atmel_spi_setup(struct spi_device *spi)
{
struct atmel_spi *as;
- struct atmel_spi_device *asd;
u32 scbr, csr;
unsigned int bits = spi->bits_per_word;
unsigned long bus_hz;
- unsigned int npcs_pin;
int ret;
+ unsigned cs_decoding;
as = spi_master_get_devdata(spi->master);
+ cs_decoding = as->board_data->flags & ATMEL_SPI_CS_DEC;
if (as->stopping)
return -ESHUTDOWN;
@@ -571,6 +583,15 @@ static int atmel_spi_setup(struct spi_device *spi)
dev_dbg(&spi->dev, "setup: can't be active-high\n");
return -EINVAL;
}
+ if (cs_decoding && (spi->mode & SPI_CS_HIGH)) {
+ dev_dbg(&spi->dev, "setup: can't be active-high while using CS decoding\n");
+ return -EINVAL;
+ }
+ if (cs_decoding && !atmel_spi_is_v2()) {
+ dev_dbg(&spi->dev, "setup: CS decoding not supported on this controller\n");
+ return -EINVAL;
+ }
+
/* v1 chips start out at half the peripheral bus speed. */
bus_hz = clk_get_rate(as->clk);
@@ -614,22 +635,15 @@ static int atmel_spi_setup(struct spi_device *spi)
csr |= SPI_BF(DLYBCT, 0);
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
- npcs_pin = (unsigned int)spi->controller_data;
- asd = spi->controller_state;
- if (!asd) {
- asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL);
- if (!asd)
- return -ENOMEM;
-
- ret = gpio_request(npcs_pin, dev_name(&spi->dev));
- if (ret) {
- kfree(asd);
- return ret;
+ if (!spi->controller_state) {
+ /* Request the GPIO only if not using CS decoding */
+ if (!cs_decoding) {
+ u32 gpio = as->board_data->cs_pins[spi->chip_select].gpio;
+ ret = gpio_request(gpio, dev_name(&spi->dev));
+ if (ret)
+ return ret;
+ gpio_direction_output(gpio, !(spi->mode & SPI_CS_HIGH));
}
-
- asd->npcs_pin = npcs_pin;
- spi->controller_state = asd;
- gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
} else {
unsigned long flags;
@@ -640,7 +654,7 @@ static int atmel_spi_setup(struct spi_device *spi)
spin_unlock_irqrestore(&as->lock, flags);
}
- asd->csr = csr;
+ spi->controller_state = (void *)csr;
dev_dbg(&spi->dev,
"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
@@ -659,7 +673,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
unsigned long flags;
struct device *controller = spi->master->dev.parent;
u8 bits;
- struct atmel_spi_device *asd;
+ u32 csr = (u32)spi->controller_state;
as = spi_master_get_devdata(spi->master);
@@ -679,8 +693,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
}
if (xfer->bits_per_word) {
- asd = spi->controller_state;
- bits = (asd->csr >> 4) & 0xf;
+ bits = (csr >> 4) & 0xf;
if (bits != xfer->bits_per_word - 8) {
dev_dbg(&spi->dev, "you can't yet change "
"bits_per_word in transfers\n");
@@ -733,11 +746,9 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
static void atmel_spi_cleanup(struct spi_device *spi)
{
struct atmel_spi *as = spi_master_get_devdata(spi->master);
- struct atmel_spi_device *asd = spi->controller_state;
- unsigned gpio = (unsigned) spi->controller_data;
unsigned long flags;
- if (!asd)
+ if (!spi->controller_state)
return;
spin_lock_irqsave(&as->lock, flags);
@@ -748,8 +759,6 @@ static void atmel_spi_cleanup(struct spi_device *spi)
spin_unlock_irqrestore(&as->lock, flags);
spi->controller_state = NULL;
- gpio_free(gpio);
- kfree(asd);
}
/*-------------------------------------------------------------------------*/
@@ -762,6 +771,8 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
int ret;
struct spi_master *master;
struct atmel_spi *as;
+ struct atmel_spi_data *board_data;
+ int i;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
@@ -784,8 +795,19 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ /* Get board specific data */
+ ret = -EINVAL;
+ board_data = platform_get_drvdata(pdev);
+ if (!board_data)
+ goto out_free;
+
master->bus_num = pdev->id;
- master->num_chipselect = 4;
+ if (board_data->flags & ATMEL_SPI_CS_DEC) {
+ master->num_chipselect = (0x01 << board_data->num_cs_pin) - 1;
+ dev_dbg(&pdev->dev, "Chip select decoding enabled (%d:%d)\n",
+ board_data->num_cs_pin, 0x01 << board_data->num_cs_pin);
+ } else
+ master->num_chipselect = board_data->num_cs_pin;
master->setup = atmel_spi_setup;
master->transfer = atmel_spi_transfer;
master->cleanup = atmel_spi_cleanup;
@@ -793,6 +815,23 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
as = spi_master_get_devdata(master);
+ /* Keep revelant board info in our structure */
+ as->board_data = board_data;
+
+ /* Request and init the CS pins here only when using CS
+ * decoding. When not using CS decoding this is done on a per
+ * SPI device basis (see atmel_spi_setup()).
+ */
+ if (board_data->flags & ATMEL_SPI_CS_DEC) {
+ ret = gpio_request_array(board_data->cs_pins,
+ board_data->num_cs_pin);
+ if (ret != 0)
+ goto out_free;
+ /* CS decoding is always active low */
+ for (i = 0; i < board_data->num_cs_pin; i++)
+ gpio_direction_output(board_data->cs_pins[i].gpio, 1);
+ }
+
/*
* Scratch buffer is used for throwaway rx and tx data.
* It's coherent to minimize dcache pollution.
@@ -800,7 +839,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
&as->buffer_dma, GFP_KERNEL);
if (!as->buffer)
- goto out_free;
+ goto out_free_pins;
spin_lock_init(&as->lock);
INIT_LIST_HEAD(&as->queue);
@@ -823,6 +862,8 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
spi_writel(as, CR, SPI_BIT(SPIEN));
+ if (as->board_data->flags & ATMEL_SPI_CS_DEC)
+ spi_writel(as, MR, SPI_BIT(PCSDEC));
/* go! */
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
@@ -844,6 +885,9 @@ out_unmap_regs:
out_free_buffer:
dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
as->buffer_dma);
+out_free_pins:
+ if (board_data->flags & ATMEL_SPI_CS_DEC)
+ gpio_free_array(board_data->cs_pins, board_data->num_cs_pin);
out_free:
clk_put(clk);
spi_master_put(master);
@@ -876,6 +920,9 @@ static int __exit atmel_spi_remove(struct platform_device *pdev)
dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
as->buffer_dma);
+ if (as->board_data->flags & ATMEL_SPI_CS_DEC)
+ gpio_free_array(as->board_data->cs_pins,
+ as->board_data->num_cs_pin);
clk_disable(as->clk);
clk_put(as->clk);
free_irq(as->irq, master);
--
1.7.4.1
next prev parent reply other threads:[~2011-05-31 16:04 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-05-31 16:04 [PATCH RFC 0/3] AT91: SPI: Add peripheral chip select decoding Christian Gagneraud
2011-05-31 16:04 ` Christian Gagneraud [this message]
2011-05-31 16:04 ` [PATCH RFC 2/3] AT91: SPI: Split SPI controller device and SPI device Christian Gagneraud
2011-05-31 16:04 ` [PATCH RFC 3/3] AT91: SPI: Add example of platform specific CS/GPIO usage Christian Gagneraud
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=1306857863-13424-2-git-send-email-chris@techworks.ie \
--to=chris@techworks.ie \
--cc=linux-arm-kernel@lists.infradead.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).