From 91949a2d4a96195ccd37322fafe8d16c68dda86e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:51 +0100 Subject: spi: rspi: Remove unused mesg parameter from {send,receive}_pio() Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 28987d9fcfe5..ccd5cf201d04 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -228,11 +228,8 @@ static u16 rspi_read16(const struct rspi_data *rspi, u16 offset) struct spi_ops { int (*set_config_register)(const struct rspi_data *rspi, int access_size); - int (*send_pio)(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t); - int (*receive_pio)(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t); - + int (*send_pio)(struct rspi_data *rspi, struct spi_transfer *t); + int (*receive_pio)(struct rspi_data *rspi, struct spi_transfer *t); }; /* @@ -358,8 +355,7 @@ static void rspi_negate_ssl(const struct rspi_data *rspi) rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); } -static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static int rspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) { int remain = t->len; const u8 *data = t->tx_buf; @@ -384,8 +380,7 @@ static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, return 0; } -static int qspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static int qspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) { int remain = t->len; const u8 *data = t->tx_buf; @@ -418,7 +413,7 @@ static int qspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, return 0; } -#define send_pio(spi, mesg, t) spi->ops->send_pio(spi, mesg, t) +#define send_pio(spi, t) spi->ops->send_pio(spi, t) static void rspi_dma_complete(void *arg) { @@ -551,8 +546,7 @@ static void rspi_receive_init(const struct rspi_data *rspi) RSPI_SPSR); } -static int rspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static int rspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) { int remain = t->len; u8 *data; @@ -598,8 +592,7 @@ static void qspi_receive_init(const struct rspi_data *rspi) rspi_write8(rspi, 0x00, QSPI_SPBFCR); } -static int qspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static int qspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) { int remain = t->len; u8 *data; @@ -630,7 +623,7 @@ static int qspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, return 0; } -#define receive_pio(spi, mesg, t) spi->ops->receive_pio(spi, mesg, t) +#define receive_pio(spi, t) spi->ops->receive_pio(spi, t) static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { @@ -771,7 +764,7 @@ static void rspi_work(struct work_struct *work) if (rspi_is_dma(rspi, t)) ret = rspi_send_dma(rspi, t); else - ret = send_pio(rspi, mesg, t); + ret = send_pio(rspi, t); if (ret < 0) goto error; } @@ -779,7 +772,7 @@ static void rspi_work(struct work_struct *work) if (rspi_is_dma(rspi, t)) ret = rspi_receive_dma(rspi, t); else - ret = receive_pio(rspi, mesg, t); + ret = receive_pio(rspi, t); if (ret < 0) goto error; } -- cgit v1.2.3 From 79d2349542f38663c3096f389115b1f131d6e564 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:52 +0100 Subject: spi: rspi: Use core message handling Let the generic SPI core handle SPI messages, calling into our rspi_transfer_one() method. rspi_assert_ssl() and rspi_negate_ssl() are absorbed into rspi_prepare_message() and rspi_unprepare_message(), as they actually enable/disable the whole SPI function, instead of just (de)asserting the chip select line. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 111 +++++++++++++++---------------------------------- 1 file changed, 34 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index ccd5cf201d04..0e4d169c90d7 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -25,8 +25,6 @@ #include #include #include -#include -#include #include #include #include @@ -181,10 +179,7 @@ struct rspi_data { void __iomem *addr; u32 max_speed_hz; struct spi_master *master; - struct list_head queue; - struct work_struct ws; wait_queue_head_t wait; - spinlock_t lock; struct clk *clk; u8 spsr; u16 spcmd; @@ -345,16 +340,6 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask, return 0; } -static void rspi_assert_ssl(const struct rspi_data *rspi) -{ - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR); -} - -static void rspi_negate_ssl(const struct rspi_data *rspi) -{ - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); -} - static int rspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) { int remain = t->len; @@ -739,56 +724,27 @@ static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) return 0; } -static void rspi_work(struct work_struct *work) +static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) { - struct rspi_data *rspi = container_of(work, struct rspi_data, ws); - struct spi_message *mesg; - struct spi_transfer *t; - unsigned long flags; - int ret; - - while (1) { - spin_lock_irqsave(&rspi->lock, flags); - if (list_empty(&rspi->queue)) { - spin_unlock_irqrestore(&rspi->lock, flags); - break; - } - mesg = list_entry(rspi->queue.next, struct spi_message, queue); - list_del_init(&mesg->queue); - spin_unlock_irqrestore(&rspi->lock, flags); - - rspi_assert_ssl(rspi); - - list_for_each_entry(t, &mesg->transfers, transfer_list) { - if (t->tx_buf) { - if (rspi_is_dma(rspi, t)) - ret = rspi_send_dma(rspi, t); - else - ret = send_pio(rspi, t); - if (ret < 0) - goto error; - } - if (t->rx_buf) { - if (rspi_is_dma(rspi, t)) - ret = rspi_receive_dma(rspi, t); - else - ret = receive_pio(rspi, t); - if (ret < 0) - goto error; - } - mesg->actual_length += t->len; - } - rspi_negate_ssl(rspi); + struct rspi_data *rspi = spi_master_get_devdata(master); + int ret = 0; - mesg->status = 0; - mesg->complete(mesg->context); + if (xfer->tx_buf) { + if (rspi_is_dma(rspi, xfer)) + ret = rspi_send_dma(rspi, xfer); + else + ret = send_pio(rspi, xfer); + if (ret < 0) + return ret; } - - return; - -error: - mesg->status = ret; - mesg->complete(mesg->context); + if (xfer->rx_buf) { + if (rspi_is_dma(rspi, xfer)) + ret = rspi_receive_dma(rspi, xfer); + else + ret = receive_pio(rspi, xfer); + } + return ret; } static int rspi_setup(struct spi_device *spi) @@ -808,24 +764,26 @@ static int rspi_setup(struct spi_device *spi) return 0; } -static int rspi_transfer(struct spi_device *spi, struct spi_message *mesg) +static void rspi_cleanup(struct spi_device *spi) { - struct rspi_data *rspi = spi_master_get_devdata(spi->master); - unsigned long flags; - - mesg->actual_length = 0; - mesg->status = -EINPROGRESS; +} - spin_lock_irqsave(&rspi->lock, flags); - list_add_tail(&mesg->queue, &rspi->queue); - schedule_work(&rspi->ws); - spin_unlock_irqrestore(&rspi->lock, flags); +static int rspi_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR); return 0; } -static void rspi_cleanup(struct spi_device *spi) +static int rspi_unprepare_message(struct spi_master *master, + struct spi_message *message) { + struct rspi_data *rspi = spi_master_get_devdata(master); + + rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); + return 0; } static irqreturn_t rspi_irq(int irq, void *_sr) @@ -972,9 +930,6 @@ static int rspi_probe(struct platform_device *pdev) } clk_enable(rspi->clk); - INIT_LIST_HEAD(&rspi->queue); - spin_lock_init(&rspi->lock); - INIT_WORK(&rspi->ws, rspi_work); init_waitqueue_head(&rspi->wait); if (rspi_pd && rspi_pd->num_chipselect) @@ -984,8 +939,10 @@ static int rspi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->setup = rspi_setup; - master->transfer = rspi_transfer; + master->transfer_one = rspi_transfer_one; master->cleanup = rspi_cleanup; + master->prepare_message = rspi_prepare_message; + master->unprepare_message = rspi_unprepare_message; master->mode_bits = SPI_CPHA | SPI_CPOL; ret = devm_request_irq(&pdev->dev, irq, rspi_irq, 0, -- cgit v1.2.3 From 74da76865d57161cadf8f324281f23ed3eb5db9c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:53 +0100 Subject: spi: rspi: Abstract 8/16-bit Data Register access Add rspi_{write,read}_data(), to abstract 8-bit (QSPI, and RSPI on RZ/A1H) versus 16-bit (RSPI) Data Register access. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 56 +++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 0e4d169c90d7..a0bb3c28ae91 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -192,6 +192,7 @@ struct rspi_data { unsigned dma_width_16bit:1; unsigned dma_callbacked:1; + unsigned byte_access:1; }; static void rspi_write8(const struct rspi_data *rspi, u8 data, u16 offset) @@ -219,10 +220,25 @@ static u16 rspi_read16(const struct rspi_data *rspi, u16 offset) return ioread16(rspi->addr + offset); } +static void rspi_write_data(const struct rspi_data *rspi, u16 data) +{ + if (rspi->byte_access) + rspi_write8(rspi, data, RSPI_SPDR); + else /* 16 bit */ + rspi_write16(rspi, data, RSPI_SPDR); +} + +static u16 rspi_read_data(const struct rspi_data *rspi) +{ + if (rspi->byte_access) + return rspi_read8(rspi, RSPI_SPDR); + else /* 16 bit */ + return rspi_read16(rspi, RSPI_SPDR); +} + /* optional functions */ struct spi_ops { - int (*set_config_register)(const struct rspi_data *rspi, - int access_size); + int (*set_config_register)(struct rspi_data *rspi, int access_size); int (*send_pio)(struct rspi_data *rspi, struct spi_transfer *t); int (*receive_pio)(struct rspi_data *rspi, struct spi_transfer *t); }; @@ -230,8 +246,7 @@ struct spi_ops { /* * functions for RSPI */ -static int rspi_set_config_register(const struct rspi_data *rspi, - int access_size) +static int rspi_set_config_register(struct rspi_data *rspi, int access_size) { int spbr; @@ -242,8 +257,9 @@ static int rspi_set_config_register(const struct rspi_data *rspi, spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); - /* Sets number of frames to be used: 1 frame */ - rspi_write8(rspi, 0x00, RSPI_SPDCR); + /* Disable dummy transmission, set 16-bit word access, 1 frame */ + rspi_write8(rspi, 0, RSPI_SPDCR); + rspi->byte_access = 0; /* Sets RSPCK, SSL, next-access delay value */ rspi_write8(rspi, 0x00, RSPI_SPCKD); @@ -266,8 +282,7 @@ static int rspi_set_config_register(const struct rspi_data *rspi, /* * functions for QSPI */ -static int qspi_set_config_register(const struct rspi_data *rspi, - int access_size) +static int qspi_set_config_register(struct rspi_data *rspi, int access_size) { u16 spcmd; int spbr; @@ -279,8 +294,9 @@ static int qspi_set_config_register(const struct rspi_data *rspi, spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz); rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); - /* Sets number of frames to be used: 1 frame */ - rspi_write8(rspi, 0x00, RSPI_SPDCR); + /* Disable dummy transmission, set byte access */ + rspi_write8(rspi, 0, RSPI_SPDCR); + rspi->byte_access = 1; /* Sets RSPCK, SSL, next-access delay value */ rspi_write8(rspi, 0x00, RSPI_SPCKD); @@ -354,7 +370,7 @@ static int rspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) return -ETIMEDOUT; } - rspi_write16(rspi, *data, RSPI_SPDR); + rspi_write_data(rspi, *data); data++; remain--; } @@ -380,14 +396,14 @@ static int qspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) "%s: tx empty timeout\n", __func__); return -ETIMEDOUT; } - rspi_write8(rspi, *data++, RSPI_SPDR); + rspi_write_data(rspi, *data++); if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { dev_err(&rspi->master->dev, "%s: receive timeout\n", __func__); return -ETIMEDOUT; } - rspi_read8(rspi, RSPI_SPDR); + rspi_read_data(rspi); remain--; } @@ -525,7 +541,7 @@ static void rspi_receive_init(const struct rspi_data *rspi) spsr = rspi_read8(rspi, RSPI_SPSR); if (spsr & SPSR_SPRF) - rspi_read16(rspi, RSPI_SPDR); /* dummy read */ + rspi_read_data(rspi); /* dummy read */ if (spsr & SPSR_OVRF) rspi_write8(rspi, rspi_read8(rspi, RSPI_SPSR) & ~SPSR_OVRF, RSPI_SPSR); @@ -549,15 +565,14 @@ static int rspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) return -ETIMEDOUT; } /* dummy write for generate clock */ - rspi_write16(rspi, DUMMY_DATA, RSPI_SPDR); + rspi_write_data(rspi, DUMMY_DATA); if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { dev_err(&rspi->master->dev, "%s: receive timeout\n", __func__); return -ETIMEDOUT; } - /* SPDR allows 16 or 32-bit access only */ - *data = (u8)rspi_read16(rspi, RSPI_SPDR); + *data = rspi_read_data(rspi); data++; remain--; @@ -572,7 +587,7 @@ static void qspi_receive_init(const struct rspi_data *rspi) spsr = rspi_read8(rspi, RSPI_SPSR); if (spsr & SPSR_SPRF) - rspi_read8(rspi, RSPI_SPDR); /* dummy read */ + rspi_read_data(rspi); /* dummy read */ rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR); rspi_write8(rspi, 0x00, QSPI_SPBFCR); } @@ -593,15 +608,14 @@ static int qspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) return -ETIMEDOUT; } /* dummy write for generate clock */ - rspi_write8(rspi, DUMMY_DATA, RSPI_SPDR); + rspi_write_data(rspi, DUMMY_DATA); if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { dev_err(&rspi->master->dev, "%s: receive timeout\n", __func__); return -ETIMEDOUT; } - /* SPDR allows 8, 16 or 32-bit access */ - *data++ = rspi_read8(rspi, RSPI_SPDR); + *data++ = rspi_read_data(rspi); remain--; } -- cgit v1.2.3 From 35301c996046243ca6e41d490dea2823f045614c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:54 +0100 Subject: spi: rspi: Add rspi_data_{out,in,out_in}() helpers Add helpers rspi_data_{out,in,out_in}() to write, read, or write and read data to/from the Data Register, taking care of waiting until data or space is available in the buffers. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 117 +++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index a0bb3c28ae91..4b27513e7204 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -356,22 +356,51 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask, return 0; } +static int rspi_data_out(struct rspi_data *rspi, u8 data) +{ + if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { + dev_err(&rspi->master->dev, "transmit timeout\n"); + return -ETIMEDOUT; + } + rspi_write_data(rspi, data); + return 0; +} + +static int rspi_data_in(struct rspi_data *rspi) +{ + u8 data; + + if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { + dev_err(&rspi->master->dev, "receive timeout\n"); + return -ETIMEDOUT; + } + data = rspi_read_data(rspi); + return data; +} + +static int rspi_data_out_in(struct rspi_data *rspi, u8 data) +{ + int ret; + + ret = rspi_data_out(rspi, data); + if (ret < 0) + return ret; + + return rspi_data_in(rspi); +} + static int rspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) { - int remain = t->len; + int remain = t->len, ret; const u8 *data = t->tx_buf; + while (remain > 0) { rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR); - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } - - rspi_write_data(rspi, *data); - data++; + ret = rspi_data_out(rspi, *data++); + if (ret < 0) + return ret; remain--; } @@ -383,28 +412,17 @@ static int rspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) static int qspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) { - int remain = t->len; + int remain = t->len, ret; const u8 *data = t->tx_buf; rspi_write8(rspi, SPBFCR_TXRST, QSPI_SPBFCR); rspi_write8(rspi, 0x00, QSPI_SPBFCR); while (remain > 0) { - - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } - rspi_write_data(rspi, *data++); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { - dev_err(&rspi->master->dev, - "%s: receive timeout\n", __func__); - return -ETIMEDOUT; - } - rspi_read_data(rspi); - + /* dummy read */ + ret = rspi_data_out_in(rspi, *data++); + if (ret < 0) + return ret; remain--; } @@ -549,32 +567,20 @@ static void rspi_receive_init(const struct rspi_data *rspi) static int rspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) { - int remain = t->len; - u8 *data; + int remain = t->len, ret; + u8 *data = t->rx_buf; rspi_receive_init(rspi); - data = t->rx_buf; while (remain > 0) { rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, RSPI_SPCR); - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } - /* dummy write for generate clock */ - rspi_write_data(rspi, DUMMY_DATA); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { - dev_err(&rspi->master->dev, - "%s: receive timeout\n", __func__); - return -ETIMEDOUT; - } - *data = rspi_read_data(rspi); - - data++; + /* dummy write data for generate clock */ + ret = rspi_data_out_in(rspi, DUMMY_DATA); + if (ret < 0) + return ret; + *data++ = ret; remain--; } @@ -594,28 +600,17 @@ static void qspi_receive_init(const struct rspi_data *rspi) static int qspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) { - int remain = t->len; - u8 *data; + int remain = t->len, ret; + u8 *data = t->rx_buf; qspi_receive_init(rspi); - data = t->rx_buf; while (remain > 0) { - - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } /* dummy write for generate clock */ - rspi_write_data(rspi, DUMMY_DATA); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { - dev_err(&rspi->master->dev, - "%s: receive timeout\n", __func__); - return -ETIMEDOUT; - } - *data++ = rspi_read_data(rspi); + ret = rspi_data_out_in(rspi, DUMMY_DATA); + if (ret < 0) + return ret; + *data++ = ret; remain--; } -- cgit v1.2.3 From eb557f75269e82dd26a79be536eca223ddc3eaf7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:55 +0100 Subject: spi: rspi: Abstract transfer_one() for RSPI and QSPI Split off qspi_transfer_one() (which doesn't support DMA yet) from rspi_transfer_one(). Replace the abstraction of send_pio()/receive_pio() by the abstracrion of transfer_one(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 4b27513e7204..d837c5029308 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -239,8 +239,8 @@ static u16 rspi_read_data(const struct rspi_data *rspi) /* optional functions */ struct spi_ops { int (*set_config_register)(struct rspi_data *rspi, int access_size); - int (*send_pio)(struct rspi_data *rspi, struct spi_transfer *t); - int (*receive_pio)(struct rspi_data *rspi, struct spi_transfer *t); + int (*transfer_one)(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer); }; /* @@ -432,8 +432,6 @@ static int qspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) return 0; } -#define send_pio(spi, t) spi->ops->send_pio(spi, t) - static void rspi_dma_complete(void *arg) { struct rspi_data *rspi = arg; @@ -617,8 +615,6 @@ static int qspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) return 0; } -#define receive_pio(spi, t) spi->ops->receive_pio(spi, t) - static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { struct scatterlist sg, sg_dummy; @@ -743,7 +739,7 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, if (rspi_is_dma(rspi, xfer)) ret = rspi_send_dma(rspi, xfer); else - ret = send_pio(rspi, xfer); + ret = rspi_send_pio(rspi, xfer); if (ret < 0) return ret; } @@ -751,8 +747,24 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, if (rspi_is_dma(rspi, xfer)) ret = rspi_receive_dma(rspi, xfer); else - ret = receive_pio(rspi, xfer); + ret = rspi_receive_pio(rspi, xfer); + } + return ret; +} + +static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + int ret = 0; + + if (xfer->tx_buf) { + ret = qspi_send_pio(rspi, xfer); + if (ret < 0) + return ret; } + if (xfer->rx_buf) + ret = qspi_receive_pio(rspi, xfer); return ret; } @@ -948,7 +960,7 @@ static int rspi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->setup = rspi_setup; - master->transfer_one = rspi_transfer_one; + master->transfer_one = ops->transfer_one; master->cleanup = rspi_cleanup; master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; @@ -990,14 +1002,12 @@ error1: static struct spi_ops rspi_ops = { .set_config_register = rspi_set_config_register, - .send_pio = rspi_send_pio, - .receive_pio = rspi_receive_pio, + .transfer_one = rspi_transfer_one, }; static struct spi_ops qspi_ops = { .set_config_register = qspi_set_config_register, - .send_pio = qspi_send_pio, - .receive_pio = qspi_receive_pio, + .transfer_one = qspi_transfer_one, }; static struct platform_device_id spi_driver_ids[] = { -- cgit v1.2.3 From 8449fd76deb9ac67a15a6fb8ead7bb4595d019d2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:56 +0100 Subject: spi: rspi: Merge rspi_send_pio() and rspi_receive_pio() rspi_send_pio() and rspi_receive_pio() are very similar: - the former only sends data, using TX Only Mode, - the latter sends and receives full duplex data to/from the hardware, but uses dummy transmit data. Merge them into rspi_transfer_out_in(), now supporting full duplex if needed. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 101 ++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d837c5029308..cc90136d02c8 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -389,27 +389,6 @@ static int rspi_data_out_in(struct rspi_data *rspi, u8 data) return rspi_data_in(rspi); } -static int rspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) -{ - int remain = t->len, ret; - const u8 *data = t->tx_buf; - - while (remain > 0) { - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, - RSPI_SPCR); - - ret = rspi_data_out(rspi, *data++); - if (ret < 0) - return ret; - remain--; - } - - /* Waiting for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); - - return 0; -} - static int qspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) { int remain = t->len, ret; @@ -563,28 +542,6 @@ static void rspi_receive_init(const struct rspi_data *rspi) RSPI_SPSR); } -static int rspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) -{ - int remain = t->len, ret; - u8 *data = t->rx_buf; - - rspi_receive_init(rspi); - - while (remain > 0) { - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, - RSPI_SPCR); - - /* dummy write data for generate clock */ - ret = rspi_data_out_in(rspi, DUMMY_DATA); - if (ret < 0) - return ret; - *data++ = ret; - remain--; - } - - return 0; -} - static void qspi_receive_init(const struct rspi_data *rspi) { u8 spsr; @@ -729,27 +686,61 @@ static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) return 0; } +static int rspi_transfer_out_in(struct rspi_data *rspi, + struct spi_transfer *xfer) +{ + int remain = xfer->len, ret; + const u8 *tx_buf = xfer->tx_buf; + u8 *rx_buf = xfer->rx_buf; + u8 spcr, data; + + rspi_receive_init(rspi); + + spcr = rspi_read8(rspi, RSPI_SPCR); + if (rx_buf) + spcr &= ~SPCR_TXMD; + else + spcr |= SPCR_TXMD; + rspi_write8(rspi, spcr, RSPI_SPCR); + + while (remain > 0) { + data = tx_buf ? *tx_buf++ : DUMMY_DATA; + ret = rspi_data_out(rspi, data); + if (ret < 0) + return ret; + if (rx_buf) { + ret = rspi_data_in(rspi); + if (ret < 0) + return ret; + *rx_buf++ = ret; + } + remain--; + } + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct rspi_data *rspi = spi_master_get_devdata(master); - int ret = 0; + int ret; + + if (!rspi_is_dma(rspi, xfer)) + return rspi_transfer_out_in(rspi, xfer); if (xfer->tx_buf) { - if (rspi_is_dma(rspi, xfer)) - ret = rspi_send_dma(rspi, xfer); - else - ret = rspi_send_pio(rspi, xfer); + ret = rspi_send_dma(rspi, xfer); if (ret < 0) return ret; } - if (xfer->rx_buf) { - if (rspi_is_dma(rspi, xfer)) - ret = rspi_receive_dma(rspi, xfer); - else - ret = rspi_receive_pio(rspi, xfer); - } - return ret; + if (xfer->rx_buf) + return rspi_receive_dma(rspi, xfer); + + return 0; } static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, -- cgit v1.2.3 From 340a15e6f0d6cd436c55693f7328a1de02fcdb96 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:57 +0100 Subject: spi: rspi: Merge qspi_send_pio() and qspi_receive_pio() qspi_send_pio() and qspi_receive_pio() are very similar: they both send and receive full duplex data to/from the hardware, but ignore the data stream in the unused direction. Merge them into qspi_transfer_out_in(), now supporting real full duplex. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 79 ++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index cc90136d02c8..5d39cd3eba62 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -389,28 +389,6 @@ static int rspi_data_out_in(struct rspi_data *rspi, u8 data) return rspi_data_in(rspi); } -static int qspi_send_pio(struct rspi_data *rspi, struct spi_transfer *t) -{ - int remain = t->len, ret; - const u8 *data = t->tx_buf; - - rspi_write8(rspi, SPBFCR_TXRST, QSPI_SPBFCR); - rspi_write8(rspi, 0x00, QSPI_SPBFCR); - - while (remain > 0) { - /* dummy read */ - ret = rspi_data_out_in(rspi, *data++); - if (ret < 0) - return ret; - remain--; - } - - /* Waiting for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); - - return 0; -} - static void rspi_dma_complete(void *arg) { struct rspi_data *rspi = arg; @@ -550,26 +528,7 @@ static void qspi_receive_init(const struct rspi_data *rspi) if (spsr & SPSR_SPRF) rspi_read_data(rspi); /* dummy read */ rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR); - rspi_write8(rspi, 0x00, QSPI_SPBFCR); -} - -static int qspi_receive_pio(struct rspi_data *rspi, struct spi_transfer *t) -{ - int remain = t->len, ret; - u8 *data = t->rx_buf; - - qspi_receive_init(rspi); - - while (remain > 0) { - /* dummy write for generate clock */ - ret = rspi_data_out_in(rspi, DUMMY_DATA); - if (ret < 0) - return ret; - *data++ = ret; - remain--; - } - - return 0; + rspi_write8(rspi, 0, QSPI_SPBFCR); } static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) @@ -743,20 +702,38 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, return 0; } -static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, - struct spi_transfer *xfer) +static int qspi_transfer_out_in(struct rspi_data *rspi, + struct spi_transfer *xfer) { - struct rspi_data *rspi = spi_master_get_devdata(master); - int ret = 0; + int remain = xfer->len, ret; + const u8 *tx_buf = xfer->tx_buf; + u8 *rx_buf = xfer->rx_buf; + u8 data; - if (xfer->tx_buf) { - ret = qspi_send_pio(rspi, xfer); + qspi_receive_init(rspi); + + while (remain > 0) { + data = tx_buf ? *tx_buf++ : DUMMY_DATA; + ret = rspi_data_out_in(rspi, data); if (ret < 0) return ret; + if (rx_buf) + *rx_buf++ = ret; + remain--; } - if (xfer->rx_buf) - ret = qspi_receive_pio(rspi, xfer); - return ret; + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + + return qspi_transfer_out_in(rspi, xfer); } static int rspi_setup(struct spi_device *spi) -- cgit v1.2.3 From 9372220678cd4c62992f7637b2ee36b47fa58d37 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:58 +0100 Subject: spi: rspi: Add support for more than one interrupt Add support for multiple interrupts, based on the SDK reference code. This is needed for RZ/A1H, which supports 3 interrupts. When using multiple interrupts, they must be called "rx" (SPRI) and "tx" (SPTI). The error interrupt (SPEI) is not used, as it matters for slave mode only. When using a single interrupt, it may be called "mux". If it cannot be found, the first interrupt in the device's resources will be used. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 106 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 5d39cd3eba62..d2ade5e09f58 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1,7 +1,7 @@ /* * SH RSPI driver * - * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012, 2013 Renesas Solutions Corp. * * Based on spi-sh.c: * Copyright (C) 2011 Renesas Solutions Corp. @@ -183,12 +183,12 @@ struct rspi_data { struct clk *clk; u8 spsr; u16 spcmd; + int rx_irq, tx_irq; const struct spi_ops *ops; /* for dmaengine */ struct dma_chan *chan_tx; struct dma_chan *chan_rx; - int irq; unsigned dma_width_16bit:1; unsigned dma_callbacked:1; @@ -440,7 +440,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) struct scatterlist sg; const void *buf = NULL; struct dma_async_tx_descriptor *desc; - unsigned len; + unsigned int len; int ret = 0; if (rspi->dma_width_16bit) { @@ -478,7 +478,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be * called. So, this driver disables the IRQ while DMA transfer. */ - disable_irq(rspi->irq); + disable_irq(rspi->tx_irq); rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR); rspi_enable_irq(rspi, SPCR_SPTIE); @@ -497,7 +497,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) ret = -ETIMEDOUT; rspi_disable_irq(rspi, SPCR_SPTIE); - enable_irq(rspi->irq); + enable_irq(rspi->tx_irq); end: rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE); @@ -536,7 +536,7 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) struct scatterlist sg, sg_dummy; void *dummy = NULL, *rx_buf = NULL; struct dma_async_tx_descriptor *desc, *desc_dummy; - unsigned len; + unsigned int len; int ret = 0; if (rspi->dma_width_16bit) { @@ -594,7 +594,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be * called. So, this driver disables the IRQ while DMA transfer. */ - disable_irq(rspi->irq); + disable_irq(rspi->tx_irq); + if (rspi->rx_irq != rspi->tx_irq) + disable_irq(rspi->rx_irq); rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, RSPI_SPCR); rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); @@ -617,7 +619,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) ret = -ETIMEDOUT; rspi_disable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); - enable_irq(rspi->irq); + enable_irq(rspi->tx_irq); + if (rspi->rx_irq != rspi->tx_irq) + enable_irq(rspi->rx_irq); end: rspi_dma_unmap_sg(&sg, rspi->chan_rx, DMA_FROM_DEVICE); @@ -775,7 +779,7 @@ static int rspi_unprepare_message(struct spi_master *master, return 0; } -static irqreturn_t rspi_irq(int irq, void *_sr) +static irqreturn_t rspi_irq_mux(int irq, void *_sr) { struct rspi_data *rspi = _sr; u8 spsr; @@ -797,6 +801,36 @@ static irqreturn_t rspi_irq(int irq, void *_sr) return ret; } +static irqreturn_t rspi_irq_rx(int irq, void *_sr) +{ + struct rspi_data *rspi = _sr; + u8 spsr; + + rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR); + if (spsr & SPSR_SPRF) { + rspi_disable_irq(rspi, SPCR_SPRIE); + wake_up(&rspi->wait); + return IRQ_HANDLED; + } + + return 0; +} + +static irqreturn_t rspi_irq_tx(int irq, void *_sr) +{ + struct rspi_data *rspi = _sr; + u8 spsr; + + rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR); + if (spsr & SPSR_SPTEF) { + rspi_disable_irq(rspi, SPCR_SPTIE); + wake_up(&rspi->wait); + return IRQ_HANDLED; + } + + return 0; +} + static int rspi_request_dma(struct rspi_data *rspi, struct platform_device *pdev) { @@ -868,12 +902,25 @@ static int rspi_remove(struct platform_device *pdev) return 0; } +static int rspi_request_irq(struct device *dev, unsigned int irq, + irq_handler_t handler, const char *suffix, + void *dev_id) +{ + const char *base = dev_name(dev); + size_t len = strlen(base) + strlen(suffix) + 2; + char *name = devm_kzalloc(dev, len, GFP_KERNEL); + if (!name) + return -ENOMEM; + snprintf(name, len, "%s:%s", base, suffix); + return devm_request_irq(dev, irq, handler, 0, name, dev_id); +} + static int rspi_probe(struct platform_device *pdev) { struct resource *res; struct spi_master *master; struct rspi_data *rspi; - int ret, irq; + int ret; char clk_name[16]; const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); const struct spi_ops *ops; @@ -886,12 +933,6 @@ static int rspi_probe(struct platform_device *pdev) return -ENODEV; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "platform_get_irq error\n"); - return -ENODEV; - } - master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data)); if (master == NULL) { dev_err(&pdev->dev, "spi_alloc_master error.\n"); @@ -934,14 +975,41 @@ static int rspi_probe(struct platform_device *pdev) master->unprepare_message = rspi_unprepare_message; master->mode_bits = SPI_CPHA | SPI_CPOL; - ret = devm_request_irq(&pdev->dev, irq, rspi_irq, 0, - dev_name(&pdev->dev), rspi); + ret = platform_get_irq_byname(pdev, "rx"); + if (ret < 0) { + ret = platform_get_irq_byname(pdev, "mux"); + if (ret < 0) + ret = platform_get_irq(pdev, 0); + if (ret >= 0) + rspi->rx_irq = rspi->tx_irq = ret; + } else { + rspi->rx_irq = ret; + ret = platform_get_irq_byname(pdev, "tx"); + if (ret >= 0) + rspi->tx_irq = ret; + } + if (ret < 0) { + dev_err(&pdev->dev, "platform_get_irq error\n"); + goto error2; + } + + if (rspi->rx_irq == rspi->tx_irq) { + /* Single multiplexed interrupt */ + ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_mux, + "mux", rspi); + } else { + /* Multi-interrupt mode, only SPRI and SPTI are used */ + ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_rx, + "rx", rspi); + if (!ret) + ret = rspi_request_irq(&pdev->dev, rspi->tx_irq, + rspi_irq_tx, "tx", rspi); + } if (ret < 0) { dev_err(&pdev->dev, "request_irq error\n"); goto error2; } - rspi->irq = irq; ret = rspi_request_dma(rspi, pdev); if (ret < 0) { dev_err(&pdev->dev, "rspi_request_dma failed.\n"); -- cgit v1.2.3 From 862d357f84f009fdcba22be8d6a2f82ff80ab740 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:43:59 +0100 Subject: spi: rspi: Add support for RSPI on RZ/A1H Add support for the RSPI variant in the RZ/A1H (r7s72100) SoC. Main differences with RSPI on SH are: - Lack of TX only mode, hence we always have to use full duplex transfers, - The Data Register must be accessed used 8-bit operations. RSPI on RZ is matched using the new "rspi-rz" platform device name. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 101 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d2ade5e09f58..0c7556978d2e 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -47,7 +47,7 @@ #define RSPI_SPCKD 0x0c /* Clock Delay Register */ #define RSPI_SSLND 0x0d /* Slave Select Negation Delay Register */ #define RSPI_SPND 0x0e /* Next-Access Delay Register */ -#define RSPI_SPCR2 0x0f /* Control Register 2 */ +#define RSPI_SPCR2 0x0f /* Control Register 2 (SH only) */ #define RSPI_SPCMD0 0x10 /* Command Register 0 */ #define RSPI_SPCMD1 0x12 /* Command Register 1 */ #define RSPI_SPCMD2 0x14 /* Command Register 2 */ @@ -56,10 +56,12 @@ #define RSPI_SPCMD5 0x1a /* Command Register 5 */ #define RSPI_SPCMD6 0x1c /* Command Register 6 */ #define RSPI_SPCMD7 0x1e /* Command Register 7 */ + +/* RSPI on RZ only */ #define RSPI_SPBFCR 0x20 /* Buffer Control Register */ #define RSPI_SPBFDR 0x22 /* Buffer Data Count Setting Register */ -/*qspi only */ +/* QSPI only */ #define QSPI_SPBFCR 0x18 /* Buffer Control Register */ #define QSPI_SPBDCR 0x1a /* Buffer Data Count Register */ #define QSPI_SPBMUL0 0x1c /* Transfer Data Length Multiplier Setting Register 0 */ @@ -102,7 +104,7 @@ #define SPSR_PERF 0x08 /* Parity Error Flag */ #define SPSR_MODF 0x04 /* Mode Fault Error Flag */ #define SPSR_IDLNF 0x02 /* RSPI Idle Flag */ -#define SPSR_OVRF 0x01 /* Overrun Error Flag */ +#define SPSR_OVRF 0x01 /* Overrun Error Flag (RSPI only) */ /* SPSCR - Sequence Control Register */ #define SPSCR_SPSLN_MASK 0x07 /* Sequence Length Specification */ @@ -119,13 +121,13 @@ #define SPDCR_SPLWORD SPDCR_SPLW1 #define SPDCR_SPLBYTE SPDCR_SPLW0 #define SPDCR_SPLW 0x20 /* Access Width Specification (SH) */ -#define SPDCR_SPRDTD 0x10 /* Receive Transmit Data Select */ +#define SPDCR_SPRDTD 0x10 /* Receive Transmit Data Select (SH) */ #define SPDCR_SLSEL1 0x08 #define SPDCR_SLSEL0 0x04 -#define SPDCR_SLSEL_MASK 0x0c /* SSL1 Output Select */ +#define SPDCR_SLSEL_MASK 0x0c /* SSL1 Output Select (SH) */ #define SPDCR_SPFC1 0x02 #define SPDCR_SPFC0 0x01 -#define SPDCR_SPFC_MASK 0x03 /* Frame Count Setting (1-4) */ +#define SPDCR_SPFC_MASK 0x03 /* Frame Count Setting (1-4) (SH) */ /* SPCKD - Clock Delay Register */ #define SPCKD_SCKDL_MASK 0x07 /* Clock Delay Setting (1-8) */ @@ -168,8 +170,8 @@ #define SPCMD_CPHA 0x0001 /* Clock Phase Setting */ /* SPBFCR - Buffer Control Register */ -#define SPBFCR_TXRST 0x80 /* Transmit Buffer Data Reset (qspi only) */ -#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset (qspi only) */ +#define SPBFCR_TXRST 0x80 /* Transmit Buffer Data Reset */ +#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset */ #define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */ #define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */ @@ -244,7 +246,7 @@ struct spi_ops { }; /* - * functions for RSPI + * functions for RSPI on legacy SH */ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) { @@ -279,6 +281,39 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) return 0; } +/* + * functions for RSPI on RZ + */ +static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) +{ + int spbr; + + /* Sets output mode */ + rspi_write8(rspi, 0x00, RSPI_SPPCR); + + /* Sets transfer bit rate */ + spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + + /* Disable dummy transmission, set byte access */ + rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR); + rspi->byte_access = 1; + + /* Sets RSPCK, SSL, next-access delay value */ + rspi_write8(rspi, 0x00, RSPI_SPCKD); + rspi_write8(rspi, 0x00, RSPI_SSLND); + rspi_write8(rspi, 0x00, RSPI_SPND); + + /* Sets SPCMD */ + rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size); + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); + + /* Sets RSPI mode */ + rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); + + return 0; +} + /* * functions for QSPI */ @@ -520,6 +555,13 @@ static void rspi_receive_init(const struct rspi_data *rspi) RSPI_SPSR); } +static void rspi_rz_receive_init(const struct rspi_data *rspi) +{ + rspi_receive_init(rspi); + rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, RSPI_SPBFCR); + rspi_write8(rspi, 0, RSPI_SPBFCR); +} + static void qspi_receive_init(const struct rspi_data *rspi) { u8 spsr; @@ -706,6 +748,41 @@ static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, return 0; } +static int rspi_rz_transfer_out_in(struct rspi_data *rspi, + struct spi_transfer *xfer) +{ + int remain = xfer->len, ret; + const u8 *tx_buf = xfer->tx_buf; + u8 *rx_buf = xfer->rx_buf; + u8 data; + + rspi_rz_receive_init(rspi); + + while (remain > 0) { + data = tx_buf ? *tx_buf++ : DUMMY_DATA; + ret = rspi_data_out_in(rspi, data); + if (ret < 0) + return ret; + if (rx_buf) + *rx_buf++ = ret; + remain--; + } + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +static int rspi_rz_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + + return rspi_rz_transfer_out_in(rspi, xfer); +} + static int qspi_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { @@ -1041,6 +1118,11 @@ static struct spi_ops rspi_ops = { .transfer_one = rspi_transfer_one, }; +static struct spi_ops rspi_rz_ops = { + .set_config_register = rspi_rz_set_config_register, + .transfer_one = rspi_rz_transfer_one, +}; + static struct spi_ops qspi_ops = { .set_config_register = qspi_set_config_register, .transfer_one = qspi_transfer_one, @@ -1048,6 +1130,7 @@ static struct spi_ops qspi_ops = { static struct platform_device_id spi_driver_ids[] = { { "rspi", (kernel_ulong_t)&rspi_ops }, + { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, { "qspi", (kernel_ulong_t)&qspi_ops }, {}, }; -- cgit v1.2.3 From 06a7a3cff042a36fb7e6af71039a17c6d1a6d90f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:44:00 +0100 Subject: spi: rspi: Add support for loopback mode Add support for specifying loopback mode. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 0c7556978d2e..28db8770aaf3 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -183,8 +183,9 @@ struct rspi_data { struct spi_master *master; wait_queue_head_t wait; struct clk *clk; - u8 spsr; u16 spcmd; + u8 spsr; + u8 sppcr; int rx_irq, tx_irq; const struct spi_ops *ops; @@ -252,8 +253,8 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) { int spbr; - /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ - rspi_write8(rspi, 0x00, RSPI_SPPCR); + /* Sets output mode, MOSI signal, and (optionally) loopback */ + rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; @@ -288,8 +289,8 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) { int spbr; - /* Sets output mode */ - rspi_write8(rspi, 0x00, RSPI_SPPCR); + /* Sets output mode, MOSI signal, and (optionally) loopback */ + rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; @@ -322,8 +323,8 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) u16 spcmd; int spbr; - /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ - rspi_write8(rspi, 0x00, RSPI_SPPCR); + /* Sets output mode, MOSI signal, and (optionally) loopback */ + rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz); @@ -829,6 +830,11 @@ static int rspi_setup(struct spi_device *spi) if (spi->mode & SPI_CPHA) rspi->spcmd |= SPCMD_CPHA; + /* CMOS output mode and MOSI signal from previous transfer */ + rspi->sppcr = 0; + if (spi->mode & SPI_LOOP) + rspi->sppcr |= SPPCR_SPLP; + set_config_register(rspi, 8); return 0; @@ -1050,7 +1056,7 @@ static int rspi_probe(struct platform_device *pdev) master->cleanup = rspi_cleanup; master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; - master->mode_bits = SPI_CPHA | SPI_CPOL; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP; ret = platform_get_irq_byname(pdev, "rx"); if (ret < 0) { -- cgit v1.2.3 From 17fe0d9a28fe742c467f800625459cf7bcb44b3b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:44:01 +0100 Subject: spi: rspi: Convert to clk_prepare_enable/disable_unprepare Get the driver ready for the migration to the common clock framework by calling clk_prepare_enable() and clk_disable_unprepare(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 28db8770aaf3..a648b40c271b 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -980,7 +980,7 @@ static int rspi_remove(struct platform_device *pdev) struct rspi_data *rspi = platform_get_drvdata(pdev); rspi_release_dma(rspi); - clk_disable(rspi->clk); + clk_disable_unprepare(rspi->clk); return 0; } @@ -1041,7 +1041,12 @@ static int rspi_probe(struct platform_device *pdev) ret = PTR_ERR(rspi->clk); goto error1; } - clk_enable(rspi->clk); + + ret = clk_prepare_enable(rspi->clk); + if (ret < 0) { + dev_err(&pdev->dev, "unable to prepare/enable clock\n"); + goto error1; + } init_waitqueue_head(&rspi->wait); @@ -1112,7 +1117,7 @@ static int rspi_probe(struct platform_device *pdev) error3: rspi_release_dma(rspi); error2: - clk_disable(rspi->clk); + clk_disable_unprepare(rspi->clk); error1: spi_master_put(master); -- cgit v1.2.3 From 29f397b739ceef90c8b848f6579cbacd088e896e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 24 Jan 2014 09:44:02 +0100 Subject: spi: rspi: Use NULL as the clock ID There's only one RSPI/QSPI clock, so we can use NULL as the clock ID Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index a648b40c271b..d79a7ed9b92e 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1004,7 +1004,6 @@ static int rspi_probe(struct platform_device *pdev) struct spi_master *master; struct rspi_data *rspi; int ret; - char clk_name[16]; const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); const struct spi_ops *ops; const struct platform_device_id *id_entry = pdev->id_entry; @@ -1034,8 +1033,7 @@ static int rspi_probe(struct platform_device *pdev) goto error1; } - snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id); - rspi->clk = devm_clk_get(&pdev->dev, clk_name); + rspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rspi->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); ret = PTR_ERR(rspi->clk); -- cgit v1.2.3 From 426ef76dd8a394a0e04d096941cd9acb49539a3e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 28 Jan 2014 10:21:38 +0100 Subject: spi: rspi: Add DT support Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-rspi.txt | 59 ++++++++++++ drivers/spi/spi-rspi.c | 106 +++++++++++++++------ 2 files changed, 136 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/spi/spi-rspi.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt new file mode 100644 index 000000000000..95f9b21d239f --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-rspi.txt @@ -0,0 +1,59 @@ +Device tree configuration for Renesas RSPI/QSPI driver + +Required properties: +- compatible : For Renesas Serial Peripheral Interface on legacy SH: + "renesas,rspi-", "renesas,rspi" as fallback. + For Renesas Serial Peripheral Interface on RZ/A1H: + "renesas,rspi-", "renesas,rspi-rz" as fallback. + For Quad Serial Peripheral Interface on R-Car Gen2: + "renesas,qspi-", "renesas,qspi" as fallback. + Examples of valid soctypes are "sh7757" (SH), + "r7s72100" (RZ/A1H), "r8a7790" (R-Car H2), and + "r8a7791" (R-Car M2). +- reg : Address start and address range size of the device +- interrupts : A list of interrupt-specifiers, one for each entry in + interrupt-names. + If interrupt-names is not present, an interrupt specifier + for a single muxed interrupt. +- interrupt-names : A list of interrupt names. Should contain (if present): + - "error" for SPEI, + - "rx" for SPRI, + - "tx" to SPTI, + - "mux" for a single muxed interrupt. +- interrupt-parent : The phandle for the interrupt controller that + services interrupts for this device. +- num-cs : Number of chip selects. Some RSPI cores have more than 1. +- #address-cells : Must be <1> +- #size-cells : Must be <0> + +Optional properties: +- clocks: : Must contain a reference to the functional clock. + +Pinctrl properties might be needed, too. See +Documentation/devicetree/bindings/pinctrl/renesas,*. + +Examples: + + spi0: spi@e800c800 { + compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz"; + reg = <0xe800c800 0x24>; + interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>, + <0 239 IRQ_TYPE_LEVEL_HIGH>, + <0 240 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "error", "rx", "tx"; + interrupt-parent = <&gic>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + spi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7791", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupt-parent = <&gic>; + interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d79a7ed9b92e..e56fcb5f7f99 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -985,6 +986,56 @@ static int rspi_remove(struct platform_device *pdev) return 0; } +static const struct spi_ops rspi_ops = { + .set_config_register = rspi_set_config_register, + .transfer_one = rspi_transfer_one, +}; + +static const struct spi_ops rspi_rz_ops = { + .set_config_register = rspi_rz_set_config_register, + .transfer_one = rspi_rz_transfer_one, +}; + +static const struct spi_ops qspi_ops = { + .set_config_register = qspi_set_config_register, + .transfer_one = qspi_transfer_one, +}; + +#ifdef CONFIG_OF +static const struct of_device_id rspi_of_match[] = { + /* RSPI on legacy SH */ + { .compatible = "renesas,rspi", .data = &rspi_ops }, + /* RSPI on RZ/A1H */ + { .compatible = "renesas,rspi-rz", .data = &rspi_rz_ops }, + /* QSPI on R-Car Gen2 */ + { .compatible = "renesas,qspi", .data = &qspi_ops }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, rspi_of_match); + +static int rspi_parse_dt(struct device *dev, struct spi_master *master) +{ + u32 num_cs; + int error; + + /* Parse DT properties */ + error = of_property_read_u32(dev->of_node, "num-cs", &num_cs); + if (error) { + dev_err(dev, "of_property_read_u32 num-cs failed %d\n", error); + return error; + } + + master->num_chipselect = num_cs; + return 0; +} +#else +static inline int rspi_parse_dt(struct device *dev, struct spi_master *master) +{ + return -EINVAL; +} +#endif /* CONFIG_OF */ + static int rspi_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, const char *suffix, void *dev_id) @@ -1004,16 +1055,9 @@ static int rspi_probe(struct platform_device *pdev) struct spi_master *master; struct rspi_data *rspi; int ret; - const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); + const struct of_device_id *of_id; + const struct rspi_plat_data *rspi_pd; const struct spi_ops *ops; - const struct platform_device_id *id_entry = pdev->id_entry; - - ops = (struct spi_ops *)id_entry->driver_data; - /* ops parameter check */ - if (!ops->set_config_register) { - dev_err(&pdev->dev, "there is no set_config_register\n"); - return -ENODEV; - } master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data)); if (master == NULL) { @@ -1021,6 +1065,28 @@ static int rspi_probe(struct platform_device *pdev) return -ENOMEM; } + of_id = of_match_device(rspi_of_match, &pdev->dev); + if (of_id) { + ops = of_id->data; + ret = rspi_parse_dt(&pdev->dev, master); + if (ret) + goto error1; + } else { + ops = (struct spi_ops *)pdev->id_entry->driver_data; + rspi_pd = dev_get_platdata(&pdev->dev); + if (rspi_pd && rspi_pd->num_chipselect) + master->num_chipselect = rspi_pd->num_chipselect; + else + master->num_chipselect = 2; /* default */ + }; + + /* ops parameter check */ + if (!ops->set_config_register) { + dev_err(&pdev->dev, "there is no set_config_register\n"); + ret = -ENODEV; + goto error1; + } + rspi = spi_master_get_devdata(master); platform_set_drvdata(pdev, rspi); rspi->ops = ops; @@ -1048,11 +1114,6 @@ static int rspi_probe(struct platform_device *pdev) init_waitqueue_head(&rspi->wait); - if (rspi_pd && rspi_pd->num_chipselect) - master->num_chipselect = rspi_pd->num_chipselect; - else - master->num_chipselect = 2; /* default */ - master->bus_num = pdev->id; master->setup = rspi_setup; master->transfer_one = ops->transfer_one; @@ -1060,6 +1121,7 @@ static int rspi_probe(struct platform_device *pdev) master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP; + master->dev.of_node = pdev->dev.of_node; ret = platform_get_irq_byname(pdev, "rx"); if (ret < 0) { @@ -1122,21 +1184,6 @@ error1: return ret; } -static struct spi_ops rspi_ops = { - .set_config_register = rspi_set_config_register, - .transfer_one = rspi_transfer_one, -}; - -static struct spi_ops rspi_rz_ops = { - .set_config_register = rspi_rz_set_config_register, - .transfer_one = rspi_rz_transfer_one, -}; - -static struct spi_ops qspi_ops = { - .set_config_register = qspi_set_config_register, - .transfer_one = qspi_transfer_one, -}; - static struct platform_device_id spi_driver_ids[] = { { "rspi", (kernel_ulong_t)&rspi_ops }, { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, @@ -1153,6 +1200,7 @@ static struct platform_driver rspi_driver = { .driver = { .name = "renesas_spi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rspi_of_match), }, }; module_platform_driver(rspi_driver); -- cgit v1.2.3 From 880c6d114fd79a6973708744c78c7f55da6aea4c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Jan 2014 09:43:50 +0100 Subject: spi: rspi: Add support for Quad and Dual SPI Transfers on QSPI Add support for Quad and Dual SPI Transfers on the Renesas Quad Serial Peripheral Interface, as found in R-Car Gen2 SoCs like R-Car H2 (r8a7790) and R-Car M2 (r8a7791): - Add unidirectional transfer methods for Quad/Dual SPI Transfers. - Program the sequencer to handle SPI messages with multiple transfer modes when Quad or Dual transfers are enabled for an SPI slave. Up to 4 transfer modes per SPI message are supported by the hardware. - Advertise the availability of Quad and Dual SPI modes on QSPI. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 162 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index e56fcb5f7f99..34ad4bca8a41 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -2,6 +2,7 @@ * SH RSPI driver * * Copyright (C) 2012, 2013 Renesas Solutions Corp. + * Copyright (C) 2014 Glider bvba * * Based on spi-sh.c: * Copyright (C) 2011 Renesas Solutions Corp. @@ -57,6 +58,10 @@ #define RSPI_SPCMD5 0x1a /* Command Register 5 */ #define RSPI_SPCMD6 0x1c /* Command Register 6 */ #define RSPI_SPCMD7 0x1e /* Command Register 7 */ +#define RSPI_SPCMD(i) (RSPI_SPCMD0 + (i) * 2) +#define RSPI_NUM_SPCMD 8 +#define RSPI_RZ_NUM_SPCMD 4 +#define QSPI_NUM_SPCMD 4 /* RSPI on RZ only */ #define RSPI_SPBFCR 0x20 /* Buffer Control Register */ @@ -69,6 +74,7 @@ #define QSPI_SPBMUL1 0x20 /* Transfer Data Length Multiplier Setting Register 1 */ #define QSPI_SPBMUL2 0x24 /* Transfer Data Length Multiplier Setting Register 2 */ #define QSPI_SPBMUL3 0x28 /* Transfer Data Length Multiplier Setting Register 3 */ +#define QSPI_SPBMUL(i) (QSPI_SPBMUL0 + (i) * 4) /* SPCR - Control Register */ #define SPCR_SPRIE 0x80 /* Receive Interrupt Enable */ @@ -152,7 +158,7 @@ #define SPCMD_LSBF 0x1000 /* LSB First */ #define SPCMD_SPB_MASK 0x0f00 /* Data Length Setting */ #define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK) -#define SPCMD_SPB_8BIT 0x0000 /* qspi only */ +#define SPCMD_SPB_8BIT 0x0000 /* QSPI only */ #define SPCMD_SPB_16BIT 0x0100 #define SPCMD_SPB_20BIT 0x0000 #define SPCMD_SPB_24BIT 0x0100 @@ -245,6 +251,7 @@ struct spi_ops { int (*set_config_register)(struct rspi_data *rspi, int access_size); int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer); + u16 mode_bits; }; /* @@ -274,8 +281,8 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, 0x00, RSPI_SPCR2); /* Sets SPCMD */ - rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | rspi->spcmd, - RSPI_SPCMD0); + rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size); + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); /* Sets RSPI mode */ rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); @@ -321,7 +328,6 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) */ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) { - u16 spcmd; int spbr; /* Sets output mode, MOSI signal, and (optionally) loopback */ @@ -342,13 +348,13 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) /* Data Length Setting */ if (access_size == 8) - spcmd = SPCMD_SPB_8BIT; + rspi->spcmd |= SPCMD_SPB_8BIT; else if (access_size == 16) - spcmd = SPCMD_SPB_16BIT; + rspi->spcmd |= SPCMD_SPB_16BIT; else - spcmd = SPCMD_SPB_32BIT; + rspi->spcmd |= SPCMD_SPB_32BIT; - spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | rspi->spcmd | SPCMD_SPNDEN; + rspi->spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | SPCMD_SPNDEN; /* Resets transfer data length */ rspi_write32(rspi, 0, QSPI_SPBMUL0); @@ -359,9 +365,9 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, 0x00, QSPI_SPBFCR); /* Sets SPCMD */ - rspi_write16(rspi, spcmd, RSPI_SPCMD0); + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); - /* Enables SPI function in a master mode */ + /* Enables SPI function in master mode */ rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR); return 0; @@ -811,12 +817,55 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, return 0; } +static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) +{ + const u8 *buf = xfer->tx_buf; + unsigned int i; + int ret; + + for (i = 0; i < xfer->len; i++) { + ret = rspi_data_out(rspi, *buf++); + if (ret < 0) + return ret; + } + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer) +{ + u8 *buf = xfer->rx_buf; + unsigned int i; + int ret; + + for (i = 0; i < xfer->len; i++) { + ret = rspi_data_in(rspi); + if (ret < 0) + return ret; + *buf++ = ret; + } + + return 0; +} + static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct rspi_data *rspi = spi_master_get_devdata(master); - return qspi_transfer_out_in(rspi, xfer); + if (xfer->tx_buf && xfer->tx_nbits > SPI_NBITS_SINGLE) { + /* Quad or Dual SPI Write */ + return qspi_transfer_out(rspi, xfer); + } else if (xfer->rx_buf && xfer->rx_nbits > SPI_NBITS_SINGLE) { + /* Quad or Dual SPI Read */ + return qspi_transfer_in(rspi, xfer); + } else { + /* Single SPI Transfer */ + return qspi_transfer_out_in(rspi, xfer); + } } static int rspi_setup(struct spi_device *spi) @@ -845,21 +894,101 @@ static void rspi_cleanup(struct spi_device *spi) { } +static u16 qspi_transfer_mode(const struct spi_transfer *xfer) +{ + if (xfer->tx_buf) + switch (xfer->tx_nbits) { + case SPI_NBITS_QUAD: + return SPCMD_SPIMOD_QUAD; + case SPI_NBITS_DUAL: + return SPCMD_SPIMOD_DUAL; + default: + return 0; + } + if (xfer->rx_buf) + switch (xfer->rx_nbits) { + case SPI_NBITS_QUAD: + return SPCMD_SPIMOD_QUAD | SPCMD_SPRW; + case SPI_NBITS_DUAL: + return SPCMD_SPIMOD_DUAL | SPCMD_SPRW; + default: + return 0; + } + + return 0; +} + +static int qspi_setup_sequencer(struct rspi_data *rspi, + const struct spi_message *msg) +{ + const struct spi_transfer *xfer; + unsigned int i = 0, len = 0; + u16 current_mode = 0xffff, mode; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + mode = qspi_transfer_mode(xfer); + if (mode == current_mode) { + len += xfer->len; + continue; + } + + /* Transfer mode change */ + if (i) { + /* Set transfer data length of previous transfer */ + rspi_write32(rspi, len, QSPI_SPBMUL(i - 1)); + } + + if (i >= QSPI_NUM_SPCMD) { + dev_err(&msg->spi->dev, + "Too many different transfer modes"); + return -EINVAL; + } + + /* Program transfer mode for this transfer */ + rspi_write16(rspi, rspi->spcmd | mode, RSPI_SPCMD(i)); + current_mode = mode; + len = xfer->len; + i++; + } + if (i) { + /* Set final transfer data length and sequence length */ + rspi_write32(rspi, len, QSPI_SPBMUL(i - 1)); + rspi_write8(rspi, i - 1, RSPI_SPSCR); + } + + return 0; +} + static int rspi_prepare_message(struct spi_master *master, - struct spi_message *message) + struct spi_message *msg) { struct rspi_data *rspi = spi_master_get_devdata(master); + int ret; + if (msg->spi->mode & + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)) { + /* Setup sequencer for messages with multiple transfer modes */ + ret = qspi_setup_sequencer(rspi, msg); + if (ret < 0) + return ret; + } + + /* Enable SPI function in master mode */ rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR); return 0; } static int rspi_unprepare_message(struct spi_master *master, - struct spi_message *message) + struct spi_message *msg) { struct rspi_data *rspi = spi_master_get_devdata(master); + /* Disable SPI function */ rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); + + /* Reset sequencer for Single SPI Transfers */ + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); + rspi_write8(rspi, 0, RSPI_SPSCR); return 0; } @@ -989,16 +1118,21 @@ static int rspi_remove(struct platform_device *pdev) static const struct spi_ops rspi_ops = { .set_config_register = rspi_set_config_register, .transfer_one = rspi_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, }; static const struct spi_ops rspi_rz_ops = { .set_config_register = rspi_rz_set_config_register, .transfer_one = rspi_rz_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, }; static const struct spi_ops qspi_ops = { .set_config_register = qspi_set_config_register, .transfer_one = qspi_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | + SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD, }; #ifdef CONFIG_OF @@ -1120,7 +1254,7 @@ static int rspi_probe(struct platform_device *pdev) master->cleanup = rspi_cleanup; master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; - master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP; + master->mode_bits = ops->mode_bits; master->dev.of_node = pdev->dev.of_node; ret = platform_get_irq_byname(pdev, "rx"); -- cgit v1.2.3 From 3e7dde425dbcc7965398e92909d898c4ebc9fdb8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 22 Jan 2014 18:41:36 +0000 Subject: spi/pl022: Unprepare clocks while suspended When the driver was converted to clk_prepare() the suspend path didn't have any changes made so the clock remains prepared throughout the runtime of the driver. Unprepare it when suspended so that any savings that can be made as a result are made. Signed-off-by: Mark Brown Acked-by: Linus Walleij --- drivers/spi/spi-pl022.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 2789b452e711..4ec49eae6141 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2296,7 +2296,7 @@ pl022_remove(struct amba_device *adev) */ static void pl022_suspend_resources(struct pl022 *pl022, bool runtime) { - clk_disable(pl022->clk); + clk_disable_unprepare(pl022->clk); if (runtime) pinctrl_pm_select_idle_state(&pl022->adev->dev); @@ -2312,7 +2312,7 @@ static void pl022_resume_resources(struct pl022 *pl022, bool runtime) /* Then let's idle the pins until the next transfer happens */ pinctrl_pm_select_idle_state(&pl022->adev->dev); - clk_enable(pl022->clk); + clk_prepare_enable(pl022->clk); } #endif -- cgit v1.2.3 From 64b67defe4eb4de2d2df8acd5584a9e28fa727d3 Mon Sep 17 00:00:00 2001 From: "Shimoda, Yoshihiro" Date: Mon, 3 Feb 2014 10:43:46 +0900 Subject: spi: rspi: fix build error when CONFIG_OF is not set This patch fixes an issue that the following build error happens when the CONFIG_OF is not set: drivers/spi/spi-rspi.c: In function 'rspi_probe': drivers/spi/spi-rspi.c:1203:26: error: 'rspi_of_match' undeclared (first use in this function) Signed-off-by: Yoshihiro Shimoda Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 34ad4bca8a41..e5cfc3d77b8c 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1164,6 +1164,7 @@ static int rspi_parse_dt(struct device *dev, struct spi_master *master) return 0; } #else +#define rspi_of_match NULL static inline int rspi_parse_dt(struct device *dev, struct spi_master *master) { return -EINVAL; -- cgit v1.2.3 From 2bd16e3e23d9df41592c6b257c59b6860a9cc3ea Mon Sep 17 00:00:00 2001 From: Stefan Sørensen Date: Sun, 2 Feb 2014 16:24:12 +0100 Subject: spi: omap2-mcspi: Do not configure the controller on each transfer unless needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spi_transfer->speed_hz and spi_transfer->bits_per_word used to only be set when not using the default settings but are not set on every transfer, causing omap2_mcspi_setup_transfer to be called on each transfer. This patch changes the check to only call omap2_mcspi_setup_transfer if the settings needs to be changed. Signed-off-by: Stefan Sørensen Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index a72127f08e39..965539b0dbfb 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1057,12 +1057,15 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) status = -EINVAL; break; } - if (par_override || t->speed_hz || t->bits_per_word) { + if (par_override || + (t->speed_hz != spi->max_speed_hz) || + (t->bits_per_word != spi->bits_per_word)) { par_override = 1; status = omap2_mcspi_setup_transfer(spi, t); if (status < 0) break; - if (!t->speed_hz && !t->bits_per_word) + if (t->speed_hz == spi->max_speed_hz && + t->bits_per_word == spi->bits_per_word) par_override = 0; } if (cd && cd->cs_per_word) { -- cgit v1.2.3 From faee9b05f69f22e4d65fb2c24f82eea9c6fe4062 Mon Sep 17 00:00:00 2001 From: Stefan Sørensen Date: Sun, 2 Feb 2014 16:24:25 +0100 Subject: spi: omap2-mcspi: Support divide-by-n clock dividers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently a divide-by-2^n clock is used, causing a very coarse clock selection, i.e. a 10MHz device will need to use a 6MHz clock. The McSPI can also use a divide-by-n clock, this patch adds support for selecting that when possible. Signed-off-by: Stefan Sørensen Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 965539b0dbfb..8f0afdb067b8 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -45,6 +45,7 @@ #include #define OMAP2_MCSPI_MAX_FREQ 48000000 +#define OMAP2_MCSPI_MAX_DIVIDER 4096 #define OMAP2_MCSPI_MAX_FIFODEPTH 64 #define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF #define SPI_AUTOSUSPEND_TIMEOUT 2000 @@ -89,6 +90,7 @@ #define OMAP2_MCSPI_CHCONF_FORCE BIT(20) #define OMAP2_MCSPI_CHCONF_FFET BIT(27) #define OMAP2_MCSPI_CHCONF_FFER BIT(28) +#define OMAP2_MCSPI_CHCONF_CLKG BIT(29) #define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1) @@ -96,6 +98,7 @@ #define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3) #define OMAP2_MCSPI_CHCTRL_EN BIT(0) +#define OMAP2_MCSPI_CHCTRL_EXTCLK_MASK (0xff << 8) #define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) @@ -149,7 +152,7 @@ struct omap2_mcspi_cs { int word_len; struct list_head node; /* Context save and restore shadow register */ - u32 chconf0; + u32 chconf0, chctrl0; }; static inline void mcspi_write_reg(struct spi_master *master, @@ -230,10 +233,16 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) { + struct omap2_mcspi_cs *cs = spi->controller_state; u32 l; - l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); + l = cs->chctrl0; + if (enable) + l |= OMAP2_MCSPI_CHCTRL_EN; + else + l &= ~OMAP2_MCSPI_CHCTRL_EN; + cs->chctrl0 = l; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0); /* Flash post-writes */ mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); } @@ -840,7 +849,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; struct spi_master *spi_cntrl; - u32 l = 0, div = 0; + u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0; u8 word_len = spi->bits_per_word; u32 speed_hz = spi->max_speed_hz; @@ -856,7 +865,17 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, speed_hz = t->speed_hz; speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ); - div = omap2_mcspi_calc_divisor(speed_hz); + if (speed_hz < (OMAP2_MCSPI_MAX_FREQ / OMAP2_MCSPI_MAX_DIVIDER)) { + clkd = omap2_mcspi_calc_divisor(speed_hz); + speed_hz = OMAP2_MCSPI_MAX_FREQ >> clkd; + clkg = 0; + } else { + div = (OMAP2_MCSPI_MAX_FREQ + speed_hz - 1) / speed_hz; + speed_hz = OMAP2_MCSPI_MAX_FREQ / div; + clkd = (div - 1) & 0xf; + extclk = (div - 1) >> 4; + clkg = OMAP2_MCSPI_CHCONF_CLKG; + } l = mcspi_cached_chconf0(spi); @@ -885,7 +904,16 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* set clock divisor */ l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; - l |= div << 2; + l |= clkd << 2; + + /* set clock granularity */ + l &= ~OMAP2_MCSPI_CHCONF_CLKG; + l |= clkg; + if (clkg) { + cs->chctrl0 &= ~OMAP2_MCSPI_CHCTRL_EXTCLK_MASK; + cs->chctrl0 |= extclk << 8; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0); + } /* set SPI mode 0..3 */ if (spi->mode & SPI_CPOL) @@ -900,7 +928,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, mcspi_write_chconf0(spi, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", - OMAP2_MCSPI_MAX_FREQ >> div, + speed_hz, (spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPOL) ? "inverted" : "normal"); @@ -972,6 +1000,7 @@ static int omap2_mcspi_setup(struct spi_device *spi) cs->base = mcspi->base + spi->chip_select * 0x14; cs->phys = mcspi->phys + spi->chip_select * 0x14; cs->chconf0 = 0; + cs->chctrl0 = 0; spi->controller_state = cs; /* Link this to context save list */ list_add_tail(&cs->node, &ctx->cs); -- cgit v1.2.3 From 5dd1ad23af689591d70be06ee6efcc57d1ec2d16 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 4 Feb 2014 11:06:24 +0100 Subject: spi: rspi: Only enable interrupts when there's a need to wait rspi_wait_for_interrupt() unconditionally enables interrupts, even when the wait condition is already satisfied. This causes a high interrupt load (2 interrupts/byte for full-duplex Single SPI transfers, 1 interrupt/byte for RSPI with TX Only mode, or QSPI in unidirectional Dual or Quad Transfer mode). Change this to return immediately when the wait condition is satisfied. This dramatically reduces the interrupt load, especially in high-speed Quad Transfer mode, and increases transfer speed, as no interrupts need to be handled when there's space available in the output FIFO, or data available in the input FIFO. Benchmark results for QSPI on r8a7791 while reading 1 MiB from 30 MHz SPI FLASH on the Koelsch development board: Before: Single SPI Dual SPI Quad SPI Interrupts: 2096856 1048592 1048594 Mbps: 0.9 1.6 1.6 After: Single SPI Dual SPI Quad SPI Interrupts: 1048569 21295 8 Mbps: 0.7 10.8 12.9 I don't know why Single SPI slowed down a bit. I've also verified functionality for RSPI-RZ on r7s72100, but don't have benchmark results as there's no SPI FLASH connected to RSPI on the Genmai development board. Unlike RSPI and QSPI, RSPI-RZ has separate interrupts for RX and TX, which shows that Single SPI transfers now generate (mostly) RX interrupts, as expected. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index e5cfc3d77b8c..04528888a53f 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -391,6 +391,9 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask, int ret; rspi->spsr = rspi_read8(rspi, RSPI_SPSR); + if (rspi->spsr & wait_mask) + return 0; + rspi_enable_irq(rspi, enable_bit); ret = wait_event_timeout(rspi->wait, rspi->spsr & wait_mask, HZ); if (ret == 0 && !(rspi->spsr & wait_mask)) -- cgit v1.2.3 From 6cac167b826eced25a2756b996a93c6f7833c49b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2014 16:58:50 +0100 Subject: spi: pl022: Simplify clock handling Make use of clk_prepare_enable and clk_disable_unprepare to simplify code. No functional change. Signed-off-by: Ulf Hansson Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 4ec49eae6141..5f7594bc2086 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2183,13 +2183,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) goto err_no_clk; } - status = clk_prepare(pl022->clk); - if (status) { - dev_err(&adev->dev, "could not prepare SSP/SPI bus clock\n"); - goto err_clk_prep; - } - - status = clk_enable(pl022->clk); + status = clk_prepare_enable(pl022->clk); if (status) { dev_err(&adev->dev, "could not enable SSP/SPI bus clock\n"); goto err_no_clk_en; @@ -2250,10 +2244,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) if (platform_info->enable_dma) pl022_dma_remove(pl022); err_no_irq: - clk_disable(pl022->clk); + clk_disable_unprepare(pl022->clk); err_no_clk_en: - clk_unprepare(pl022->clk); - err_clk_prep: err_no_clk: err_no_ioremap: amba_release_regions(adev); @@ -2281,8 +2273,7 @@ pl022_remove(struct amba_device *adev) if (pl022->master_info->enable_dma) pl022_dma_remove(pl022); - clk_disable(pl022->clk); - clk_unprepare(pl022->clk); + clk_disable_unprepare(pl022->clk); amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); return 0; -- cgit v1.2.3 From 8535736c27cc4e25b9948d857138315f45b6fa34 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2014 16:58:51 +0100 Subject: spi: pl022: Remove redundant pinctrl to default state in probe The driver core is now taking care of putting our pins into default state at probe. Thus we can remove the redundant call for it in probe. Signed-off-by: Ulf Hansson Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 5f7594bc2086..74a072924b65 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2109,8 +2109,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), GFP_KERNEL); - pinctrl_pm_select_default_state(dev); - /* * Bus Number Which has been Assigned to this SSP controller * on this board -- cgit v1.2.3 From 1647138df66c695eb16f4a6bce7f088f817ab26d Mon Sep 17 00:00:00 2001 From: Christian Engelmayer Date: Wed, 5 Feb 2014 22:23:01 +0100 Subject: spi: orion: Remove unused pointer in orion_spi_write_read() Remove unused devdata pointer 'orion_spi' in function orion_spi_write_read(). Detected by Coverity: CID 1077860. Signed-off-by: Christian Engelmayer Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 7f2121fe2622..75871d444ff4 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -260,11 +260,9 @@ orion_spi_write_read_16bit(struct spi_device *spi, static unsigned int orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) { - struct orion_spi *orion_spi; unsigned int count; int word_len; - orion_spi = spi_master_get_devdata(spi->master); word_len = spi->bits_per_word; count = xfer->len; -- cgit v1.2.3 From b52a37f517d62c92d7d5bffb0fab0b025a51b9eb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 4 Feb 2014 11:05:50 +0800 Subject: spi: orion: Let spi core handle checking transfer speed Since commit a2fd4f9fa3b9 'spi: Support transfer speed checking in the core', the SPI core validates the desired speed of a given transfer against the minimum and maximum speeds supported by the controller. So we can remove the same code in this driver and let spi core handle checking the desired speed of a given transfer. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 39 ++------------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 75871d444ff4..20f174ee06ec 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -43,8 +43,6 @@ struct orion_spi { struct spi_master *master; void __iomem *base; - unsigned int max_speed; - unsigned int min_speed; struct clk *clk; }; @@ -319,16 +317,6 @@ static int orion_spi_transfer_one_message(struct spi_master *master, goto msg_done; } - if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - orion_spi->min_speed, t->speed_hz); - status = -EIO; - goto msg_done; - } - if (par_override || t->speed_hz || t->bits_per_word) { par_override = 1; status = orion_spi_setup_transfer(spi, t); @@ -373,28 +361,6 @@ static int orion_spi_reset(struct orion_spi *orion_spi) return 0; } -static int orion_spi_setup(struct spi_device *spi) -{ - struct orion_spi *orion_spi; - - orion_spi = spi_master_get_devdata(spi->master); - - if ((spi->max_speed_hz == 0) - || (spi->max_speed_hz > orion_spi->max_speed)) - spi->max_speed_hz = orion_spi->max_speed; - - if (spi->max_speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, "setup: requested speed too low %d Hz\n", - spi->max_speed_hz); - return -EINVAL; - } - - /* - * baudrate & width will be set orion_spi_setup_transfer - */ - return 0; -} - static int orion_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -423,7 +389,6 @@ static int orion_spi_probe(struct platform_device *pdev) /* we support only mode 0, and no options */ master->mode_bits = SPI_CPHA | SPI_CPOL; - master->setup = orion_spi_setup; master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; @@ -441,8 +406,8 @@ static int orion_spi_probe(struct platform_device *pdev) clk_prepare(spi->clk); clk_enable(spi->clk); tclk_hz = clk_get_rate(spi->clk); - spi->max_speed = DIV_ROUND_UP(tclk_hz, 4); - spi->min_speed = DIV_ROUND_UP(tclk_hz, 30); + master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4); + master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi->base = devm_ioremap_resource(&pdev->dev, r); -- cgit v1.2.3 From d655bbbd5beead3643f6769adaa1f30bcddc3fe2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 8 Feb 2014 22:46:02 +0800 Subject: spi: omap-100k: Remove unused MOD_REG_BIT macro Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap-100k.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 0d32054bfc0d..657184a4644c 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -99,13 +99,6 @@ struct omap1_spi100k_cs { int word_len; }; -#define MOD_REG_BIT(val, mask, set) do { \ - if (set) \ - val |= mask; \ - else \ - val &= ~mask; \ -} while (0) - static void spi100k_enable_clock(struct spi_master *master) { unsigned int val; -- cgit v1.2.3 From 77c398f58c1fe57224c17f6d4360c7f74cc0e418 Mon Sep 17 00:00:00 2001 From: Christian Engelmayer Date: Sat, 8 Feb 2014 19:02:23 +0100 Subject: spi: omap-100k: Remove unused pointer in omap1_spi100k_txrx_pio() Remove unused devdata pointer 'spi100k' in function omap1_spi100k_txrx_pio(). Detected by Coverity: CID 1077869. Signed-off-by: Christian Engelmayer Reviewed-by: Jingoo Han Acked-by: Michal Nazarewicz Signed-off-by: Mark Brown --- drivers/spi/spi-omap-100k.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 657184a4644c..afc91c4d8461 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -197,12 +197,10 @@ static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable) static unsigned omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) { - struct omap1_spi100k *spi100k; struct omap1_spi100k_cs *cs = spi->controller_state; unsigned int count, c; int word_len; - spi100k = spi_master_get_devdata(spi->master); count = xfer->len; c = count; word_len = cs->word_len; -- cgit v1.2.3 From 79c2f49a2dfed00e51b41427ef89c5e67263a8c3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 9 Feb 2014 11:35:59 +0800 Subject: spi: omap-100k: Remove unused fields from struct omap1_spi100k Both *master and state are not really used, remove them. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap-100k.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index afc91c4d8461..3b8b36a144b4 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -83,15 +83,11 @@ #define SPI_SHUTDOWN 1 struct omap1_spi100k { - struct spi_master *master; struct clk *ick; struct clk *fck; /* Virtual base address of the controller */ void __iomem *base; - - /* State of the SPI */ - unsigned int state; }; struct omap1_spi100k_cs { @@ -425,7 +421,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); spi100k = spi_master_get_devdata(master); - spi100k->master = master; /* * The memory region base address is taken as the platform_data. @@ -452,8 +447,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev) if (status < 0) goto err; - spi100k->state = SPI_RUNNING; - return status; err: -- cgit v1.2.3 From f6ab395bfeb0ba8dd0397c24b76af00ed016f286 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sat, 8 Feb 2014 22:01:21 +0100 Subject: spi: omap2: use SUPERH, not SH Commit 0079aae0f1e6 ("spi: omap2: Add build dependencies for writel_relaxed()") added an optional Kconfig dependency on SH. That Kconfig symbol doesn't exist. Apparently SUPERH was intended. Use that. Signed-off-by: Paul Bolle Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ba9310bc9acb..205dbfe97e55 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -307,7 +307,7 @@ config SPI_OMAP_UWIRE config SPI_OMAP24XX tristate "McSPI driver for OMAP" - depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SH + depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SUPERH depends on ARCH_OMAP2PLUS || COMPILE_TEST help SPI master controller for OMAP24XX and later Multichannel SPI -- cgit v1.2.3 From 495b335885f822444b66be3b81edcd4835f379c5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 11 Feb 2014 20:51:36 +0800 Subject: spi: orion: Convert to let spi core validate xfer->bits_per_word Set bits_per_word_mask so spi core will reject transfers that attempt to use an unsupported bits_per_word value. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 20f174ee06ec..caaa53fc68cc 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -73,23 +73,6 @@ orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask) writel(val, reg_addr); } -static int orion_spi_set_transfer_size(struct orion_spi *orion_spi, int size) -{ - if (size == 16) { - orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG, - ORION_SPI_IF_8_16_BIT_MODE); - } else if (size == 8) { - orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG, - ORION_SPI_IF_8_16_BIT_MODE); - } else { - pr_debug("Bad bits per word value %d (only 8 or 16 are allowed).\n", - size); - return -EINVAL; - } - - return 0; -} - static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) { u32 tclk_hz; @@ -168,7 +151,14 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) if (rc) return rc; - return orion_spi_set_transfer_size(orion_spi, bits_per_word); + if (bits_per_word == 16) + orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG, + ORION_SPI_IF_8_16_BIT_MODE); + else + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG, + ORION_SPI_IF_8_16_BIT_MODE); + + return 0; } static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable) @@ -391,6 +381,7 @@ static int orion_spi_probe(struct platform_device *pdev) master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); platform_set_drvdata(pdev, master); -- cgit v1.2.3 From 08850fa94831d374f4d827a85088f6087836dfc8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Feb 2014 20:38:16 +0800 Subject: spi: s3c24xx: Convert to let spi core validate bits_per_word Set bits_per_word_mask so spi core will reject transfers that attempt to use an unsupported bits_per_word value. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 746424aa5353..f1ffbfa6acef 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -123,25 +123,15 @@ static int s3c24xx_spi_update_state(struct spi_device *spi, { struct s3c24xx_spi *hw = to_hw(spi); struct s3c24xx_spi_devstate *cs = spi->controller_state; - unsigned int bpw; unsigned int hz; unsigned int div; unsigned long clk; - bpw = t ? t->bits_per_word : spi->bits_per_word; hz = t ? t->speed_hz : spi->max_speed_hz; - if (!bpw) - bpw = 8; - if (!hz) hz = spi->max_speed_hz; - if (bpw != 8) { - dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); - return -EINVAL; - } - if (spi->mode != cs->mode) { u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK; @@ -544,6 +534,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) master->num_chipselect = hw->pdata->num_cs; master->bus_num = pdata->bus_num; + master->bits_per_word_mask = SPI_BPW_MASK(8); /* setup the state for the bitbang driver */ -- cgit v1.2.3 From cd60080fd6200bd2e5ca9bf5135d32c06a8e6f3b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 18 Feb 2014 22:01:36 +0800 Subject: spi: omap2-mcspi: Remove list_empty checking in omap2_mcspi_transfer_one_message This checking is done in __spi_validate(). Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 8f0afdb067b8..d90edaaa7f69 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1208,9 +1208,6 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, m->actual_length = 0; m->status = 0; - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers)) - return -EINVAL; list_for_each_entry(t, &m->transfers, transfer_list) { const void *tx_buf = t->tx_buf; void *rx_buf = t->rx_buf; -- cgit v1.2.3 From aca0924b4819e813bdae2c595bf736ec2b593585 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 18 Feb 2014 22:02:47 +0800 Subject: spi: omap2-mcspi: Convert to let spi core validate transfer speed Set master->max_speed_hz and master->min_speed_hz then spi core will handle checking transfer speed. So we can remove the same checking in this driver. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d90edaaa7f69..48d218e5fa01 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1213,8 +1213,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, void *rx_buf = t->rx_buf; unsigned len = t->len; - if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ - || (len && !(rx_buf || tx_buf))) { + if ((len && !(rx_buf || tx_buf))) { dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -1223,12 +1222,6 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, t->bits_per_word); return -EINVAL; } - if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) { - dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n", - t->speed_hz, - OMAP2_MCSPI_MAX_FREQ >> 15); - return -EINVAL; - } if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; @@ -1340,6 +1333,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->transfer_one_message = omap2_mcspi_transfer_one_message; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; + master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; + master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15; platform_set_drvdata(pdev, master); -- cgit v1.2.3 From 64ff247a978facc437d40f0c9b754675846a98f0 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Thu, 13 Feb 2014 18:21:38 +0200 Subject: spi: Add Qualcomm QUP SPI controller support Qualcomm Universal Peripheral (QUP) core is an AHB slave that provides a common data path (an output FIFO and an input FIFO) for serial peripheral interface (SPI) mini-core. SPI in master mode supports up to 50MHz, up to four chip selects, programmable data path from 4 bits to 32 bits and numerous protocol variants. Cc: Alok Chauhan Cc: Gilad Avidov Cc: Kiran Gunda Cc: Sagar Dharia Cc: dsneddon@codeaurora.org Signed-off-by: Ivan T. Ivanov Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 13 + drivers/spi/Makefile | 1 + drivers/spi/spi-qup.c | 837 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 851 insertions(+) create mode 100644 drivers/spi/spi-qup.c (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ba9310bc9acb..86c254d76406 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -381,6 +381,19 @@ config SPI_RSPI help SPI driver for Renesas RSPI blocks. +config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" + depends on ARCH_MSM_DT + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) + for serial peripheral interface (SPI) mini-core. SPI in master + mode supports up to 50MHz, up to four chip selects, programmable + data path from 4 bits to 32 bits and numerous protocol variants. + + This driver can also be built as a module. If so, the module + will be called spi_qup. + config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" depends on ARCH_S3C24XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 95af48d2d360..e598147b06ef 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o +obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c new file mode 100644 index 000000000000..b0bcc09425a7 --- /dev/null +++ b/drivers/spi/spi-qup.c @@ -0,0 +1,837 @@ +/* + * Copyright (c) 2008-2014, The Linux foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License rev 2 and + * only rev 2 as published by the free Software foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QUP_CONFIG 0x0000 +#define QUP_STATE 0x0004 +#define QUP_IO_M_MODES 0x0008 +#define QUP_SW_RESET 0x000c +#define QUP_OPERATIONAL 0x0018 +#define QUP_ERROR_FLAGS 0x001c +#define QUP_ERROR_FLAGS_EN 0x0020 +#define QUP_OPERATIONAL_MASK 0x0028 +#define QUP_HW_VERSION 0x0030 +#define QUP_MX_OUTPUT_CNT 0x0100 +#define QUP_OUTPUT_FIFO 0x0110 +#define QUP_MX_WRITE_CNT 0x0150 +#define QUP_MX_INPUT_CNT 0x0200 +#define QUP_MX_READ_CNT 0x0208 +#define QUP_INPUT_FIFO 0x0218 + +#define SPI_CONFIG 0x0300 +#define SPI_IO_CONTROL 0x0304 +#define SPI_ERROR_FLAGS 0x0308 +#define SPI_ERROR_FLAGS_EN 0x030c + +/* QUP_CONFIG fields */ +#define QUP_CONFIG_SPI_MODE (1 << 8) +#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13) +#define QUP_CONFIG_NO_INPUT BIT(7) +#define QUP_CONFIG_NO_OUTPUT BIT(6) +#define QUP_CONFIG_N 0x001f + +/* QUP_STATE fields */ +#define QUP_STATE_VALID BIT(2) +#define QUP_STATE_RESET 0 +#define QUP_STATE_RUN 1 +#define QUP_STATE_PAUSE 3 +#define QUP_STATE_MASK 3 +#define QUP_STATE_CLEAR 2 + +#define QUP_HW_VERSION_2_1_1 0x20010001 + +/* QUP_IO_M_MODES fields */ +#define QUP_IO_M_PACK_EN BIT(15) +#define QUP_IO_M_UNPACK_EN BIT(14) +#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12 +#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10 +#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT) +#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT) + +#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0) +#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2) +#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5) +#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7) + +#define QUP_IO_M_MODE_FIFO 0 +#define QUP_IO_M_MODE_BLOCK 1 +#define QUP_IO_M_MODE_DMOV 2 +#define QUP_IO_M_MODE_BAM 3 + +/* QUP_OPERATIONAL fields */ +#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) +#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) +#define QUP_OP_IN_SERVICE_FLAG BIT(9) +#define QUP_OP_OUT_SERVICE_FLAG BIT(8) +#define QUP_OP_IN_FIFO_FULL BIT(7) +#define QUP_OP_OUT_FIFO_FULL BIT(6) +#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5) +#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4) + +/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */ +#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5) +#define QUP_ERROR_INPUT_UNDER_RUN BIT(4) +#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3) +#define QUP_ERROR_INPUT_OVER_RUN BIT(2) + +/* SPI_CONFIG fields */ +#define SPI_CONFIG_HS_MODE BIT(10) +#define SPI_CONFIG_INPUT_FIRST BIT(9) +#define SPI_CONFIG_LOOPBACK BIT(8) + +/* SPI_IO_CONTROL fields */ +#define SPI_IO_C_FORCE_CS BIT(11) +#define SPI_IO_C_CLK_IDLE_HIGH BIT(10) +#define SPI_IO_C_MX_CS_MODE BIT(8) +#define SPI_IO_C_CS_N_POLARITY_0 BIT(4) +#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2) +#define SPI_IO_C_CS_SELECT_MASK 0x000c +#define SPI_IO_C_TRISTATE_CS BIT(1) +#define SPI_IO_C_NO_TRI_STATE BIT(0) + +/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */ +#define SPI_ERROR_CLK_OVER_RUN BIT(1) +#define SPI_ERROR_CLK_UNDER_RUN BIT(0) + +#define SPI_NUM_CHIPSELECTS 4 + +/* high speed mode is when bus rate is greater then 26MHz */ +#define SPI_HS_MIN_RATE 26000000 +#define SPI_MAX_RATE 50000000 + +#define SPI_DELAY_THRESHOLD 1 +#define SPI_DELAY_RETRY 10 + +struct spi_qup_device { + int select; + u16 mode; +}; + +struct spi_qup { + void __iomem *base; + struct device *dev; + struct clk *cclk; /* core clock */ + struct clk *iclk; /* interface clock */ + int irq; + u32 max_speed_hz; + spinlock_t lock; + + int in_fifo_sz; + int out_fifo_sz; + int in_blk_sz; + int out_blk_sz; + + struct spi_transfer *xfer; + struct completion done; + int error; + int w_size; /* bytes per SPI word */ + int tx_bytes; + int rx_bytes; +}; + + +static inline bool spi_qup_is_valid_state(struct spi_qup *controller) +{ + u32 opstate = readl_relaxed(controller->base + QUP_STATE); + + return opstate & QUP_STATE_VALID; +} + +static int spi_qup_set_state(struct spi_qup *controller, u32 state) +{ + unsigned long loop; + u32 cur_state; + + loop = 0; + while (!spi_qup_is_valid_state(controller)) { + + usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); + + if (++loop > SPI_DELAY_RETRY) + return -EIO; + } + + if (loop) + dev_dbg(controller->dev, "invalid state for %ld,us %d\n", + loop, state); + + cur_state = readl_relaxed(controller->base + QUP_STATE); + /* + * Per spec: for PAUSE_STATE to RESET_STATE, two writes + * of (b10) are required + */ + if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) && + (state == QUP_STATE_RESET)) { + writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); + writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); + } else { + cur_state &= ~QUP_STATE_MASK; + cur_state |= state; + writel_relaxed(cur_state, controller->base + QUP_STATE); + } + + loop = 0; + while (!spi_qup_is_valid_state(controller)) { + + usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); + + if (++loop > SPI_DELAY_RETRY) + return -EIO; + } + + return 0; +} + + +static void spi_qup_fifo_read(struct spi_qup *controller, + struct spi_transfer *xfer) +{ + u8 *rx_buf = xfer->rx_buf; + u32 word, state; + int idx, shift, w_size; + + w_size = controller->w_size; + + while (controller->rx_bytes < xfer->len) { + + state = readl_relaxed(controller->base + QUP_OPERATIONAL); + if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) + break; + + word = readl_relaxed(controller->base + QUP_INPUT_FIFO); + + if (!rx_buf) { + controller->rx_bytes += w_size; + continue; + } + + for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { + /* + * The data format depends on bytes per SPI word: + * 4 bytes: 0x12345678 + * 2 bytes: 0x00001234 + * 1 byte : 0x00000012 + */ + shift = BITS_PER_BYTE; + shift *= (w_size - idx - 1); + rx_buf[controller->rx_bytes] = word >> shift; + } + } +} + +static void spi_qup_fifo_write(struct spi_qup *controller, + struct spi_transfer *xfer) +{ + const u8 *tx_buf = xfer->tx_buf; + u32 word, state, data; + int idx, w_size; + + w_size = controller->w_size; + + while (controller->tx_bytes < xfer->len) { + + state = readl_relaxed(controller->base + QUP_OPERATIONAL); + if (state & QUP_OP_OUT_FIFO_FULL) + break; + + word = 0; + for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { + + if (!tx_buf) { + controller->tx_bytes += w_size; + break; + } + + data = tx_buf[controller->tx_bytes]; + word |= data << (BITS_PER_BYTE * (3 - idx)); + } + + writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); + } +} + +static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) +{ + struct spi_qup *controller = dev_id; + struct spi_transfer *xfer; + u32 opflags, qup_err, spi_err; + unsigned long flags; + int error = 0; + + spin_lock_irqsave(&controller->lock, flags); + xfer = controller->xfer; + controller->xfer = NULL; + spin_unlock_irqrestore(&controller->lock, flags); + + qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); + spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS); + opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); + + writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); + writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + + if (!xfer) { + dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", + qup_err, spi_err, opflags); + return IRQ_HANDLED; + } + + if (qup_err) { + if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN) + dev_warn(controller->dev, "OUTPUT_OVER_RUN\n"); + if (qup_err & QUP_ERROR_INPUT_UNDER_RUN) + dev_warn(controller->dev, "INPUT_UNDER_RUN\n"); + if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN) + dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n"); + if (qup_err & QUP_ERROR_INPUT_OVER_RUN) + dev_warn(controller->dev, "INPUT_OVER_RUN\n"); + + error = -EIO; + } + + if (spi_err) { + if (spi_err & SPI_ERROR_CLK_OVER_RUN) + dev_warn(controller->dev, "CLK_OVER_RUN\n"); + if (spi_err & SPI_ERROR_CLK_UNDER_RUN) + dev_warn(controller->dev, "CLK_UNDER_RUN\n"); + + error = -EIO; + } + + if (opflags & QUP_OP_IN_SERVICE_FLAG) + spi_qup_fifo_read(controller, xfer); + + if (opflags & QUP_OP_OUT_SERVICE_FLAG) + spi_qup_fifo_write(controller, xfer); + + spin_lock_irqsave(&controller->lock, flags); + controller->error = error; + controller->xfer = xfer; + spin_unlock_irqrestore(&controller->lock, flags); + + if (controller->rx_bytes == xfer->len || error) + complete(&controller->done); + + return IRQ_HANDLED; +} + + +/* set clock freq ... bits per word */ +static int spi_qup_io_config(struct spi_qup *controller, + struct spi_qup_device *chip, + struct spi_transfer *xfer) +{ + u32 config, iomode, mode; + int ret, n_words, w_size; + + if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + dev_err(controller->dev, "too big size for loopback %d > %d\n", + xfer->len, controller->in_fifo_sz); + return -EIO; + } + + ret = clk_set_rate(controller->cclk, xfer->speed_hz); + if (ret) { + dev_err(controller->dev, "fail to set frequency %d", + xfer->speed_hz); + return -EIO; + } + + if (spi_qup_set_state(controller, QUP_STATE_RESET)) { + dev_err(controller->dev, "cannot set RESET state\n"); + return -EIO; + } + + w_size = 4; + if (xfer->bits_per_word <= 8) + w_size = 1; + else if (xfer->bits_per_word <= 16) + w_size = 2; + + n_words = xfer->len / w_size; + controller->w_size = w_size; + + if (n_words <= controller->in_fifo_sz) { + mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); + } else { + mode = QUP_IO_M_MODE_BLOCK; + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); + /* must be zero for BLOCK and BAM */ + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + } + + iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); + /* Set input and output transfer mode */ + iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); + iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); + iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); + iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); + + writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); + + config = readl_relaxed(controller->base + SPI_CONFIG); + + if (chip->mode & SPI_LOOP) + config |= SPI_CONFIG_LOOPBACK; + else + config &= ~SPI_CONFIG_LOOPBACK; + + if (chip->mode & SPI_CPHA) + config &= ~SPI_CONFIG_INPUT_FIRST; + else + config |= SPI_CONFIG_INPUT_FIRST; + + /* + * HS_MODE improves signal stability for spi-clk high rates, + * but is invalid in loop back mode. + */ + if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP)) + config |= SPI_CONFIG_HS_MODE; + else + config &= ~SPI_CONFIG_HS_MODE; + + writel_relaxed(config, controller->base + SPI_CONFIG); + + config = readl_relaxed(controller->base + QUP_CONFIG); + config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); + config |= xfer->bits_per_word - 1; + config |= QUP_CONFIG_SPI_MODE; + writel_relaxed(config, controller->base + QUP_CONFIG); + + writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); + return 0; +} + +static void spi_qup_set_cs(struct spi_device *spi, bool enable) +{ + struct spi_qup *controller = spi_master_get_devdata(spi->master); + struct spi_qup_device *chip = spi_get_ctldata(spi); + + u32 iocontol, mask; + + iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL); + + /* Disable auto CS toggle and use manual */ + iocontol &= ~SPI_IO_C_MX_CS_MODE; + iocontol |= SPI_IO_C_FORCE_CS; + + iocontol &= ~SPI_IO_C_CS_SELECT_MASK; + iocontol |= SPI_IO_C_CS_SELECT(chip->select); + + mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select; + + if (enable) + iocontol |= mask; + else + iocontol &= ~mask; + + writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL); +} + +static int spi_qup_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct spi_qup *controller = spi_master_get_devdata(master); + struct spi_qup_device *chip = spi_get_ctldata(spi); + unsigned long timeout, flags; + int ret = -EIO; + + ret = spi_qup_io_config(controller, chip, xfer); + if (ret) + return ret; + + timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC); + timeout = DIV_ROUND_UP(xfer->len * 8, timeout); + timeout = 100 * msecs_to_jiffies(timeout); + + reinit_completion(&controller->done); + + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = xfer; + controller->error = 0; + controller->rx_bytes = 0; + controller->tx_bytes = 0; + spin_unlock_irqrestore(&controller->lock, flags); + + if (spi_qup_set_state(controller, QUP_STATE_RUN)) { + dev_warn(controller->dev, "cannot set RUN state\n"); + goto exit; + } + + if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { + dev_warn(controller->dev, "cannot set PAUSE state\n"); + goto exit; + } + + spi_qup_fifo_write(controller, xfer); + + if (spi_qup_set_state(controller, QUP_STATE_RUN)) { + dev_warn(controller->dev, "cannot set EXECUTE state\n"); + goto exit; + } + + if (!wait_for_completion_timeout(&controller->done, timeout)) + ret = -ETIMEDOUT; +exit: + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = NULL; + if (!ret) + ret = controller->error; + spin_unlock_irqrestore(&controller->lock, flags); + return ret; +} + +static int spi_qup_setup(struct spi_device *spi) +{ + struct spi_qup *controller = spi_master_get_devdata(spi->master); + struct spi_qup_device *chip = spi_get_ctldata(spi); + + if (spi->chip_select >= spi->master->num_chipselect) { + dev_err(controller->dev, "invalid chip_select %d\n", + spi->chip_select); + return -EINVAL; + } + + if (spi->max_speed_hz > controller->max_speed_hz) { + dev_err(controller->dev, "invalid max_speed_hz %d\n", + spi->max_speed_hz); + return -EINVAL; + } + + if (!chip) { + /* First setup */ + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(controller->dev, "no memory for chip data\n"); + return -ENOMEM; + } + + chip->mode = spi->mode; + chip->select = spi->chip_select; + spi_set_ctldata(spi, chip); + } + + return 0; +} + +static void spi_qup_cleanup(struct spi_device *spi) +{ + struct spi_qup_device *chip = spi_get_ctldata(spi); + + if (!chip) + return; + + spi_set_ctldata(spi, NULL); + kfree(chip); +} + +static int spi_qup_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct clk *iclk, *cclk; + struct spi_qup *controller; + struct resource *res; + struct device *dev; + void __iomem *base; + u32 data, max_freq, iomode; + int ret, irq, size; + + dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + irq = platform_get_irq(pdev, 0); + + if (irq < 0) + return irq; + + cclk = devm_clk_get(dev, "core"); + if (IS_ERR(cclk)) + return PTR_ERR(cclk); + + iclk = devm_clk_get(dev, "iface"); + if (IS_ERR(iclk)) + return PTR_ERR(iclk); + + /* This is optional parameter */ + if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq)) + max_freq = SPI_MAX_RATE; + + if (!max_freq || max_freq > SPI_MAX_RATE) { + dev_err(dev, "invalid clock frequency %d\n", max_freq); + return -ENXIO; + } + + ret = clk_prepare_enable(cclk); + if (ret) { + dev_err(dev, "cannot enable core clock\n"); + return ret; + } + + ret = clk_prepare_enable(iclk); + if (ret) { + clk_disable_unprepare(cclk); + dev_err(dev, "cannot enable iface clock\n"); + return ret; + } + + data = readl_relaxed(base + QUP_HW_VERSION); + + if (data < QUP_HW_VERSION_2_1_1) { + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + dev_err(dev, "v.%08x is not supported\n", data); + return -ENXIO; + } + + master = spi_alloc_master(dev, sizeof(struct spi_qup)); + if (!master) { + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + dev_err(dev, "cannot allocate master\n"); + return -ENOMEM; + } + + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->setup = spi_qup_setup; + master->cleanup = spi_qup_cleanup; + master->set_cs = spi_qup_set_cs; + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + + platform_set_drvdata(pdev, master); + + controller = spi_master_get_devdata(master); + + controller->dev = dev; + controller->base = base; + controller->iclk = iclk; + controller->cclk = cclk; + controller->irq = irq; + controller->max_speed_hz = max_freq; + + spin_lock_init(&controller->lock); + init_completion(&controller->done); + + iomode = readl_relaxed(base + QUP_IO_M_MODES); + + size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); + if (size) + controller->out_blk_sz = size * 16; + else + controller->out_blk_sz = 4; + + size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode); + if (size) + controller->in_blk_sz = size * 16; + else + controller->in_blk_sz = 4; + + size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode); + controller->out_fifo_sz = controller->out_blk_sz * (2 << size); + + size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); + controller->in_fifo_sz = controller->in_blk_sz * (2 << size); + + dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", + data, controller->in_blk_sz, controller->in_fifo_sz, + controller->out_blk_sz, controller->out_fifo_sz); + + writel_relaxed(1, base + QUP_SW_RESET); + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) { + dev_err(dev, "cannot set RESET state\n"); + goto error; + } + + writel_relaxed(0, base + QUP_OPERATIONAL); + writel_relaxed(0, base + QUP_IO_M_MODES); + writel_relaxed(0, base + QUP_OPERATIONAL_MASK); + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, + base + SPI_ERROR_FLAGS_EN); + + writel_relaxed(0, base + SPI_CONFIG); + writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); + + ret = devm_request_irq(dev, irq, spi_qup_qup_irq, + IRQF_TRIGGER_HIGH, pdev->name, controller); + if (ret) + goto error; + + ret = devm_spi_register_master(dev, master); + if (ret) + goto error; + + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; + +error: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + spi_master_put(master); + return ret; +} + +#ifdef CONFIG_PM_RUNTIME +static int spi_qup_pm_suspend_runtime(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + u32 config; + + /* Enable clocks auto gaiting */ + config = readl(controller->base + QUP_CONFIG); + config |= QUP_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; +} + +static int spi_qup_pm_resume_runtime(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + u32 config; + + /* Disable clocks auto gaiting */ + config = readl_relaxed(controller->base + QUP_CONFIG); + config &= ~QUP_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_SLEEP +static int spi_qup_suspend(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + return ret; + + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + return 0; +} + +static int spi_qup_resume(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(controller->iclk); + if (ret) + return ret; + + ret = clk_prepare_enable(controller->cclk); + if (ret) + return ret; + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + return ret; + + return spi_master_resume(master); +} +#endif /* CONFIG_PM_SLEEP */ + +static int spi_qup_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_qup *controller = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + return ret; + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + return ret; + + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + spi_master_put(master); + return 0; +} + +static struct of_device_id spi_qup_dt_match[] = { + { .compatible = "qcom,spi-qup-v2.1.1", }, + { .compatible = "qcom,spi-qup-v2.2.1", }, + { } +}; +MODULE_DEVICE_TABLE(of, spi_qup_dt_match); + +static const struct dev_pm_ops spi_qup_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume) + SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime, + spi_qup_pm_resume_runtime, + NULL) +}; + +static struct platform_driver spi_qup_driver = { + .driver = { + .name = "spi_qup", + .owner = THIS_MODULE, + .pm = &spi_qup_dev_pm_ops, + .of_match_table = spi_qup_dt_match, + }, + .probe = spi_qup_probe, + .remove = spi_qup_remove, +}; +module_platform_driver(spi_qup_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.4"); +MODULE_ALIAS("platform:spi_qup"); -- cgit v1.2.3 From ab98fcba962a57cee9fdb97aff2b25248c93cea5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Feb 2014 17:29:17 +0100 Subject: spi: rspi: Remove empty rspi_cleanup() If spi_master.cleanup() is not needed, it can be left unimplemented. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 04528888a53f..4a1f978c3381 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -893,10 +893,6 @@ static int rspi_setup(struct spi_device *spi) return 0; } -static void rspi_cleanup(struct spi_device *spi) -{ -} - static u16 qspi_transfer_mode(const struct spi_transfer *xfer) { if (xfer->tx_buf) @@ -1255,7 +1251,6 @@ static int rspi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->setup = rspi_setup; master->transfer_one = ops->transfer_one; - master->cleanup = rspi_cleanup; master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; master->mode_bits = ops->mode_bits; -- cgit v1.2.3 From ba824d4971691a7f1f66429e378a08a95fbb5b79 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Feb 2014 17:29:18 +0100 Subject: spi: rspi: Fix loopback mode for Dual/Quad SPI Transfers While normal Dual and Quad SPI Transfers are unidirectional, we must do a bidirectional transfer if loopback mode is enabled, else rx_buf is not filled. With spidev it seemed to work, as spidev uses the same buffer for tranmission and reception. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 4a1f978c3381..92bec7e91046 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -859,7 +859,9 @@ static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, { struct rspi_data *rspi = spi_master_get_devdata(master); - if (xfer->tx_buf && xfer->tx_nbits > SPI_NBITS_SINGLE) { + if (spi->mode & SPI_LOOP) { + return qspi_transfer_out_in(rspi, xfer); + } else if (xfer->tx_buf && xfer->tx_nbits > SPI_NBITS_SINGLE) { /* Quad or Dual SPI Write */ return qspi_transfer_out(rspi, xfer); } else if (xfer->rx_buf && xfer->rx_nbits > SPI_NBITS_SINGLE) { -- cgit v1.2.3 From 81b045fe72f957ad24ab97f999ea8ca14359d941 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 21 Feb 2014 09:33:16 +0800 Subject: spi: qup: Remove spi_master_put in spi_qup_remove This driver uses devm_spi_register_master() so don't explicitly call spi_master_put() in spi_qup_remove(). Signed-off-by: Axel Lin Acked-by: Ivan T. Ivanov Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index b0bcc09425a7..5edc56f5c887 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -802,7 +802,6 @@ static int spi_qup_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - spi_master_put(master); return 0; } -- cgit v1.2.3 From cb64ca54076243b1d88b0a8430504d0a4cf3a5fa Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 21 Feb 2014 09:34:16 +0800 Subject: spi: qup: Convert ot let spi core handle checking transfer speed Set master->max_speed_hz then spi core will handle checking transfer speed. So we can remove the same checking in this driver. Also remove checking spi->chip_select in spi_qup_setup(), the checking is done by spi core. Signed-off-by: Axel Lin Acked-by: Ivan T. Ivanov Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 5edc56f5c887..dec339d51fc8 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -134,7 +134,6 @@ struct spi_qup { struct clk *cclk; /* core clock */ struct clk *iclk; /* interface clock */ int irq; - u32 max_speed_hz; spinlock_t lock; int in_fifo_sz; @@ -517,18 +516,6 @@ static int spi_qup_setup(struct spi_device *spi) struct spi_qup *controller = spi_master_get_devdata(spi->master); struct spi_qup_device *chip = spi_get_ctldata(spi); - if (spi->chip_select >= spi->master->num_chipselect) { - dev_err(controller->dev, "invalid chip_select %d\n", - spi->chip_select); - return -EINVAL; - } - - if (spi->max_speed_hz > controller->max_speed_hz) { - dev_err(controller->dev, "invalid max_speed_hz %d\n", - spi->max_speed_hz); - return -EINVAL; - } - if (!chip) { /* First setup */ chip = kzalloc(sizeof(*chip), GFP_KERNEL); @@ -629,6 +616,7 @@ static int spi_qup_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; master->num_chipselect = SPI_NUM_CHIPSELECTS; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->max_speed_hz = max_freq; master->setup = spi_qup_setup; master->cleanup = spi_qup_cleanup; master->set_cs = spi_qup_set_cs; @@ -645,7 +633,6 @@ static int spi_qup_probe(struct platform_device *pdev) controller->iclk = iclk; controller->cclk = cclk; controller->irq = irq; - controller->max_speed_hz = max_freq; spin_lock_init(&controller->lock); init_completion(&controller->done); -- cgit v1.2.3 From f0ceb114aec78cc585b8e7cb3d536f8a3e386bf1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 23 Feb 2014 13:27:16 +0800 Subject: spi: qup: Fix build error due to a typo Fix below build error when CONFIG_PM_RUNTIME=y: C [M] drivers/spi/spi-qup.o drivers/spi/spi-qup.c: In function 'spi_qup_pm_suspend_runtime': drivers/spi/spi-qup.c:712:12: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function) drivers/spi/spi-qup.c:712:12: note: each undeclared identifier is reported only once for each function it appears in drivers/spi/spi-qup.c: In function 'spi_qup_pm_resume_runtime': drivers/spi/spi-qup.c:725:13: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function) make[2]: *** [drivers/spi/spi-qup.o] Error 1 make[1]: *** [drivers/spi] Error 2 make: *** [drivers] Error 2 Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index dec339d51fc8..886edb4f4cb0 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -709,7 +709,7 @@ static int spi_qup_pm_suspend_runtime(struct device *device) /* Enable clocks auto gaiting */ config = readl(controller->base + QUP_CONFIG); - config |= QUP_CLOCK_AUTO_GATE; + config |= QUP_CONFIG_CLOCK_AUTO_GATE; writel_relaxed(config, controller->base + QUP_CONFIG); return 0; } @@ -722,7 +722,7 @@ static int spi_qup_pm_resume_runtime(struct device *device) /* Disable clocks auto gaiting */ config = readl_relaxed(controller->base + QUP_CONFIG); - config &= ~QUP_CLOCK_AUTO_GATE; + config &= ~QUP_CONFIG_CLOCK_AUTO_GATE; writel_relaxed(config, controller->base + QUP_CONFIG); return 0; } -- cgit v1.2.3 From 702d3cf9d05dbb2dc145bc97aeff6c4d61bec70f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 23 Feb 2014 13:28:33 +0800 Subject: spi: qup: Enable driver compilation with COMPILE_TEST This helps increasing build testing coverage. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 86c254d76406..f0351d88085e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -383,7 +383,7 @@ config SPI_RSPI config SPI_QUP tristate "Qualcomm SPI controller with QUP interface" - depends on ARCH_MSM_DT + depends on ARCH_MSM_DT || COMPILE_TEST help Qualcomm Universal Peripheral (QUP) core is an AHB slave that provides a common data path (an output FIFO and an input FIFO) -- cgit v1.2.3 From 80faf90f9734a06eb4da2e3b92e698b96f469c2c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 24 Feb 2014 12:07:51 +0800 Subject: spi: qup: Depend on ARM && COMPILE_TEST to avoid build error This driver uses writel_relaxed() which does not exist in x86, ppc, etc. Make it depend on ARM && COMPILE_TEST to avoid below build error: CC [M] drivers/spi/spi-qup.o drivers/spi/spi-qup.c: In function 'spi_qup_set_state': drivers/spi/spi-qup.c:180:3: error: implicit declaration of function 'writel_relaxed' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors make[2]: *** [drivers/spi/spi-qup.o] Error 1 make[1]: *** [drivers/spi] Error 2 make: *** [drivers] Error 2 Reported-by: Stephen Rothwell Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f0351d88085e..082acbdef1bd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -383,7 +383,7 @@ config SPI_RSPI config SPI_QUP tristate "Qualcomm SPI controller with QUP interface" - depends on ARCH_MSM_DT || COMPILE_TEST + depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) help Qualcomm Universal Peripheral (QUP) core is an AHB slave that provides a common data path (an output FIFO and an input FIFO) -- cgit v1.2.3 From 83f091440db9ba3f739522cee0c627ecfe1cc8ef Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 24 Feb 2014 13:48:12 +0800 Subject: spi: qup: Remove module version The module version is unlikely to be updated, use kernel version should be enough. Signed-off-by: Axel Lin Acked-by: Ivan T. Ivanov Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 886edb4f4cb0..203f0d4f049f 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -819,5 +819,4 @@ static struct platform_driver spi_qup_driver = { module_platform_driver(spi_qup_driver); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.4"); MODULE_ALIAS("platform:spi_qup"); -- cgit v1.2.3 From 00cce74d081e86ee0660cbe41699cbc523a55605 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 24 Feb 2014 23:07:36 +0800 Subject: spi: qup: Get rid of using struct spi_qup_device Current code uses struct spi_qup_device to store spi->mode and spi->chip_select settings. We can get these settings in spi_qup_transfer_one and spi_qup_set_cs without using struct spi_qup_device. Refactor the code a bit to remove spi_qup_setup(), spi_qup_cleanup(), and struct spi_qup_device. Signed-off-by: Axel Lin Tested-by: Ivan T. Ivanov Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 61 ++++++++------------------------------------------- 1 file changed, 9 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 203f0d4f049f..b032e8885e24 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -123,11 +123,6 @@ #define SPI_DELAY_THRESHOLD 1 #define SPI_DELAY_RETRY 10 -struct spi_qup_device { - int select; - u16 mode; -}; - struct spi_qup { void __iomem *base; struct device *dev; @@ -338,14 +333,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) /* set clock freq ... bits per word */ -static int spi_qup_io_config(struct spi_qup *controller, - struct spi_qup_device *chip, - struct spi_transfer *xfer) +static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) { + struct spi_qup *controller = spi_master_get_devdata(spi->master); u32 config, iomode, mode; int ret, n_words, w_size; - if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { dev_err(controller->dev, "too big size for loopback %d > %d\n", xfer->len, controller->in_fifo_sz); return -EIO; @@ -399,12 +393,12 @@ static int spi_qup_io_config(struct spi_qup *controller, config = readl_relaxed(controller->base + SPI_CONFIG); - if (chip->mode & SPI_LOOP) + if (spi->mode & SPI_LOOP) config |= SPI_CONFIG_LOOPBACK; else config &= ~SPI_CONFIG_LOOPBACK; - if (chip->mode & SPI_CPHA) + if (spi->mode & SPI_CPHA) config &= ~SPI_CONFIG_INPUT_FIRST; else config |= SPI_CONFIG_INPUT_FIRST; @@ -413,7 +407,7 @@ static int spi_qup_io_config(struct spi_qup *controller, * HS_MODE improves signal stability for spi-clk high rates, * but is invalid in loop back mode. */ - if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP)) + if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP)) config |= SPI_CONFIG_HS_MODE; else config &= ~SPI_CONFIG_HS_MODE; @@ -433,7 +427,6 @@ static int spi_qup_io_config(struct spi_qup *controller, static void spi_qup_set_cs(struct spi_device *spi, bool enable) { struct spi_qup *controller = spi_master_get_devdata(spi->master); - struct spi_qup_device *chip = spi_get_ctldata(spi); u32 iocontol, mask; @@ -444,9 +437,9 @@ static void spi_qup_set_cs(struct spi_device *spi, bool enable) iocontol |= SPI_IO_C_FORCE_CS; iocontol &= ~SPI_IO_C_CS_SELECT_MASK; - iocontol |= SPI_IO_C_CS_SELECT(chip->select); + iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select); - mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select; + mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; if (enable) iocontol |= mask; @@ -461,11 +454,10 @@ static int spi_qup_transfer_one(struct spi_master *master, struct spi_transfer *xfer) { struct spi_qup *controller = spi_master_get_devdata(master); - struct spi_qup_device *chip = spi_get_ctldata(spi); unsigned long timeout, flags; int ret = -EIO; - ret = spi_qup_io_config(controller, chip, xfer); + ret = spi_qup_io_config(spi, xfer); if (ret) return ret; @@ -511,38 +503,6 @@ exit: return ret; } -static int spi_qup_setup(struct spi_device *spi) -{ - struct spi_qup *controller = spi_master_get_devdata(spi->master); - struct spi_qup_device *chip = spi_get_ctldata(spi); - - if (!chip) { - /* First setup */ - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) { - dev_err(controller->dev, "no memory for chip data\n"); - return -ENOMEM; - } - - chip->mode = spi->mode; - chip->select = spi->chip_select; - spi_set_ctldata(spi, chip); - } - - return 0; -} - -static void spi_qup_cleanup(struct spi_device *spi) -{ - struct spi_qup_device *chip = spi_get_ctldata(spi); - - if (!chip) - return; - - spi_set_ctldata(spi, NULL); - kfree(chip); -} - static int spi_qup_probe(struct platform_device *pdev) { struct spi_master *master; @@ -561,7 +521,6 @@ static int spi_qup_probe(struct platform_device *pdev) return PTR_ERR(base); irq = platform_get_irq(pdev, 0); - if (irq < 0) return irq; @@ -617,8 +576,6 @@ static int spi_qup_probe(struct platform_device *pdev) master->num_chipselect = SPI_NUM_CHIPSELECTS; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->max_speed_hz = max_freq; - master->setup = spi_qup_setup; - master->cleanup = spi_qup_cleanup; master->set_cs = spi_qup_set_cs; master->transfer_one = spi_qup_transfer_one; master->dev.of_node = pdev->dev.of_node; -- cgit v1.2.3 From cd2ac0c0cdc5b255489a2cfd388b2d3eda37e7a7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 25 Feb 2014 19:16:57 +0800 Subject: spi: orion: Let spi core handle validating transfer length spi core will handle validating transfer length since commit 4d94bd21b333 "spi: core: Validate length of the transfers in message". So remove the same checking in this driver. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index caaa53fc68cc..12ecb0762ccb 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -296,17 +296,6 @@ static int orion_spi_transfer_one_message(struct spi_master *master, goto msg_done; list_for_each_entry(t, &m->transfers, transfer_list) { - /* make sure buffer length is even when working in 16 - * bit mode*/ - if ((t->bits_per_word == 16) && (t->len & 1)) { - dev_err(&spi->dev, - "message rejected : " - "odd data length %d while in 16 bit mode\n", - t->len); - status = -EIO; - goto msg_done; - } - if (par_override || t->speed_hz || t->bits_per_word) { par_override = 1; status = orion_spi_setup_transfer(spi, t); -- cgit v1.2.3 From 5f59df79837bb809f3945613aba5519cd9755a53 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Sat, 1 Mar 2014 11:56:04 +0100 Subject: PM / runtime: Fetch runtime PM callbacks using a macro While fetching the proper runtime PM callback, we walk the hierarchy of device's power domains, subsystems and drivers. This is common for rpm_suspend(), rpm_idle() and rpm_resume(). Let's clean up the code by using a macro that handles this. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 78 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 72e00e66ecc5..ac495b1357fa 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -13,6 +13,42 @@ #include #include "power.h" +#define RPM_GET_CALLBACK(dev, cb) \ +({ \ + int (*__rpm_cb)(struct device *__d); \ + \ + if (dev->pm_domain) \ + __rpm_cb = dev->pm_domain->ops.cb; \ + else if (dev->type && dev->type->pm) \ + __rpm_cb = dev->type->pm->cb; \ + else if (dev->class && dev->class->pm) \ + __rpm_cb = dev->class->pm->cb; \ + else if (dev->bus && dev->bus->pm) \ + __rpm_cb = dev->bus->pm->cb; \ + else \ + __rpm_cb = NULL; \ + \ + if (!__rpm_cb && dev->driver && dev->driver->pm) \ + __rpm_cb = dev->driver->pm->cb; \ + \ + __rpm_cb; \ +}) + +static int (*rpm_get_suspend_cb(struct device *dev))(struct device *) +{ + return RPM_GET_CALLBACK(dev, runtime_suspend); +} + +static int (*rpm_get_resume_cb(struct device *dev))(struct device *) +{ + return RPM_GET_CALLBACK(dev, runtime_resume); +} + +static int (*rpm_get_idle_cb(struct device *dev))(struct device *) +{ + return RPM_GET_CALLBACK(dev, runtime_idle); +} + static int rpm_resume(struct device *dev, int rpmflags); static int rpm_suspend(struct device *dev, int rpmflags); @@ -310,19 +346,7 @@ static int rpm_idle(struct device *dev, int rpmflags) dev->power.idle_notification = true; - if (dev->pm_domain) - callback = dev->pm_domain->ops.runtime_idle; - else if (dev->type && dev->type->pm) - callback = dev->type->pm->runtime_idle; - else if (dev->class && dev->class->pm) - callback = dev->class->pm->runtime_idle; - else if (dev->bus && dev->bus->pm) - callback = dev->bus->pm->runtime_idle; - else - callback = NULL; - - if (!callback && dev->driver && dev->driver->pm) - callback = dev->driver->pm->runtime_idle; + callback = rpm_get_idle_cb(dev); if (callback) retval = __rpm_callback(callback, dev); @@ -492,19 +516,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) __update_runtime_status(dev, RPM_SUSPENDING); - if (dev->pm_domain) - callback = dev->pm_domain->ops.runtime_suspend; - else if (dev->type && dev->type->pm) - callback = dev->type->pm->runtime_suspend; - else if (dev->class && dev->class->pm) - callback = dev->class->pm->runtime_suspend; - else if (dev->bus && dev->bus->pm) - callback = dev->bus->pm->runtime_suspend; - else - callback = NULL; - - if (!callback && dev->driver && dev->driver->pm) - callback = dev->driver->pm->runtime_suspend; + callback = rpm_get_suspend_cb(dev); retval = rpm_callback(callback, dev); if (retval) @@ -724,19 +736,7 @@ static int rpm_resume(struct device *dev, int rpmflags) __update_runtime_status(dev, RPM_RESUMING); - if (dev->pm_domain) - callback = dev->pm_domain->ops.runtime_resume; - else if (dev->type && dev->type->pm) - callback = dev->type->pm->runtime_resume; - else if (dev->class && dev->class->pm) - callback = dev->class->pm->runtime_resume; - else if (dev->bus && dev->bus->pm) - callback = dev->bus->pm->runtime_resume; - else - callback = NULL; - - if (!callback && dev->driver && dev->driver->pm) - callback = dev->driver->pm->runtime_resume; + callback = rpm_get_resume_cb(dev); retval = rpm_callback(callback, dev); if (retval) { -- cgit v1.2.3 From 37f204164dfb0186a0caf20bc3e3120080bcd788 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Sat, 1 Mar 2014 11:56:05 +0100 Subject: PM: Add pm_runtime_suspend|resume_force functions This patch provides two new runtime PM helper functions which intend to be used from system suspend/resume callbacks, to make sure devices are put into low power state during system suspend and brought back to full power at system resume. The prerequisite is to have all levels of a device's runtime PM callbacks to be defined through the SET_PM_RUNTIME_PM_OPS macro, which means these are available for CONFIG_PM. By using the new runtime PM helper functions especially the two scenarios below will be addressed. 1) The PM core prevents .runtime_suspend callbacks from being invoked during system suspend. That means even for a runtime PM centric subsystem and driver, the device needs to be put into low power state from a system suspend callback. Otherwise it may very well be left in full power state (runtime resumed) while the system is suspended. By using the new helper functions, we make sure to walk the hierarchy of a device's power domain, subsystem and driver. 2) Subsystems and drivers need to cope with all the combinations of CONFIG_PM_SLEEP and CONFIG_PM_RUNTIME. The two new helper functions smothly addresses this. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/Makefile | 3 +- drivers/base/power/runtime.c | 84 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_runtime.h | 4 +++ 3 files changed, 89 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 2e58ebb1f6c0..1cb8544598d5 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,6 +1,5 @@ -obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o +obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o -obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ac495b1357fa..4776cf528d08 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -44,6 +44,7 @@ static int (*rpm_get_resume_cb(struct device *dev))(struct device *) return RPM_GET_CALLBACK(dev, runtime_resume); } +#ifdef CONFIG_PM_RUNTIME static int (*rpm_get_idle_cb(struct device *dev))(struct device *) { return RPM_GET_CALLBACK(dev, runtime_idle); @@ -1401,3 +1402,86 @@ void pm_runtime_remove(struct device *dev) if (dev->power.irq_safe && dev->parent) pm_runtime_put(dev->parent); } +#endif + +/** + * pm_runtime_force_suspend - Force a device into suspend state if needed. + * @dev: Device to suspend. + * + * Disable runtime PM so we safely can check the device's runtime PM status and + * if it is active, invoke it's .runtime_suspend callback to bring it into + * suspend state. Keep runtime PM disabled to preserve the state unless we + * encounter errors. + * + * Typically this function may be invoked from a system suspend callback to make + * sure the device is put into low power state. + */ +int pm_runtime_force_suspend(struct device *dev) +{ + int (*callback)(struct device *); + int ret = 0; + + pm_runtime_disable(dev); + + /* + * Note that pm_runtime_status_suspended() returns false while + * !CONFIG_PM_RUNTIME, which means the device will be put into low + * power state. + */ + if (pm_runtime_status_suspended(dev)) + return 0; + + callback = rpm_get_suspend_cb(dev); + + if (!callback) { + ret = -ENOSYS; + goto err; + } + + ret = callback(dev); + if (ret) + goto err; + + pm_runtime_set_suspended(dev); + return 0; +err: + pm_runtime_enable(dev); + return ret; +} +EXPORT_SYMBOL_GPL(pm_runtime_force_suspend); + +/** + * pm_runtime_force_resume - Force a device into resume state. + * @dev: Device to resume. + * + * Prior invoking this function we expect the user to have brought the device + * into low power state by a call to pm_runtime_force_suspend(). Here we reverse + * those actions and brings the device into full power. We update the runtime PM + * status and re-enables runtime PM. + * + * Typically this function may be invoked from a system resume callback to make + * sure the device is put into full power state. + */ +int pm_runtime_force_resume(struct device *dev) +{ + int (*callback)(struct device *); + int ret = 0; + + callback = rpm_get_resume_cb(dev); + + if (!callback) { + ret = -ENOSYS; + goto out; + } + + ret = callback(dev); + if (ret) + goto out; + + pm_runtime_set_active(dev); + pm_runtime_mark_last_busy(dev); +out: + pm_runtime_enable(dev); + return ret; +} +EXPORT_SYMBOL_GPL(pm_runtime_force_resume); diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 16c9a62fa1c0..2a5897a4afbc 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -26,9 +26,13 @@ #ifdef CONFIG_PM extern int pm_generic_runtime_suspend(struct device *dev); extern int pm_generic_runtime_resume(struct device *dev); +extern int pm_runtime_force_suspend(struct device *dev); +extern int pm_runtime_force_resume(struct device *dev); #else static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } +static inline int pm_runtime_force_suspend(struct device *dev) { return 0; } +static inline int pm_runtime_force_resume(struct device *dev) { return 0; } #endif #ifdef CONFIG_PM_RUNTIME -- cgit v1.2.3 From 790fc55a77bc041d613da087b3e03bdc59edcfa5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 15 Feb 2014 14:26:21 +0800 Subject: spi: omap-uwire: Convert to use bits_per_word_mask Set bits_per_word_mask so spi core will reject transfers that attempt to use an unsupported bits_per_word value. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap-uwire.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 462ddd458217..95cc6948a2ba 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -99,7 +99,6 @@ struct uwire_spi { }; struct uwire_state { - unsigned bits_per_word; unsigned div1_idx; }; @@ -210,9 +209,8 @@ static void uwire_chipselect(struct spi_device *spi, int value) static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) { - struct uwire_state *ust = spi->controller_state; unsigned len = t->len; - unsigned bits = ust->bits_per_word; + unsigned bits = t->bits_per_word ? : spi->bits_per_word; unsigned bytes; u16 val, w; int status = 0; @@ -322,7 +320,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t) struct uwire_state *ust = spi->controller_state; struct uwire_spi *uwire; unsigned flags = 0; - unsigned bits; unsigned hz; unsigned long rate; int div1_idx; @@ -332,17 +329,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t) uwire = spi_master_get_devdata(spi->master); - bits = spi->bits_per_word; - if (t != NULL && t->bits_per_word) - bits = t->bits_per_word; - - if (bits > 16) { - pr_debug("%s: wordsize %d?\n", dev_name(&spi->dev), bits); - status = -ENODEV; - goto done; - } - ust->bits_per_word = bits; - /* mode 0..3, clock inverted separately; * standard nCS signaling; * don't treat DI=high as "not ready" @@ -509,7 +495,7 @@ static int uwire_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); master->flags = SPI_MASTER_HALF_DUPLEX; master->bus_num = 2; /* "official" */ -- cgit v1.2.3 From 31804f638ef213f78f4a5ee896c4226ba55ab415 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 26 Feb 2014 10:27:10 +0900 Subject: spi: omap-100k: fix spacing coding style issue Fix the following checkpatch issues. ERROR: space prohibited after that open parenthesis '(' ERROR: space required before the open parenthesis '(' ERROR: trailing statements should be on next line ERROR: space required after that ',' (ctx:VxV) ERROR: spaces required around that '-=' (ctx:VxV) WARNING: sizeof *cs should be sizeof(*cs) Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-omap-100k.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 3b8b36a144b4..c9e228242643 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -128,7 +128,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data) } spi100k_enable_clock(master); - writew( data , spi100k->base + SPI_TX_MSB); + writew(data , spi100k->base + SPI_TX_MSB); writew(SPI_CTRL_SEN(0) | SPI_CTRL_WORD_SIZE(len) | @@ -136,7 +136,8 @@ static void spi100k_write_data(struct spi_master *master, int len, int data) spi100k->base + SPI_CTRL); /* Wait for bit ack send change */ - while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE); + while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE) + ; udelay(1000); spi100k_disable_clock(master); @@ -144,7 +145,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data) static int spi100k_read_data(struct spi_master *master, int len) { - int dataH,dataL; + int dataH, dataL; struct omap1_spi100k *spi100k = spi_master_get_devdata(master); /* Always do at least 16 bits */ @@ -157,7 +158,8 @@ static int spi100k_read_data(struct spi_master *master, int len) SPI_CTRL_RD, spi100k->base + SPI_CTRL); - while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD); + while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD) + ; udelay(1000); dataL = readw(spi100k->base + SPI_RX_LSB); @@ -208,12 +210,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { - c-=1; + c -= 1; if (xfer->tx_buf != NULL) spi100k_write_data(spi->master, word_len, *tx++); if (xfer->rx_buf != NULL) *rx++ = spi100k_read_data(spi->master, word_len); - } while(c); + } while (c); } else if (word_len <= 16) { u16 *rx; const u16 *tx; @@ -221,12 +223,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { - c-=2; + c -= 2; if (xfer->tx_buf != NULL) - spi100k_write_data(spi->master,word_len, *tx++); + spi100k_write_data(spi->master, word_len, *tx++); if (xfer->rx_buf != NULL) - *rx++ = spi100k_read_data(spi->master,word_len); - } while(c); + *rx++ = spi100k_read_data(spi->master, word_len); + } while (c); } else if (word_len <= 32) { u32 *rx; const u32 *tx; @@ -234,12 +236,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { - c-=4; + c -= 4; if (xfer->tx_buf != NULL) - spi100k_write_data(spi->master,word_len, *tx); + spi100k_write_data(spi->master, word_len, *tx); if (xfer->rx_buf != NULL) - *rx = spi100k_read_data(spi->master,word_len); - } while(c); + *rx = spi100k_read_data(spi->master, word_len); + } while (c); } return count - c; } @@ -281,7 +283,7 @@ static int omap1_spi100k_setup(struct spi_device *spi) spi100k = spi_master_get_devdata(spi->master); if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; cs->base = spi100k->base + spi->chip_select * 0x14; @@ -398,14 +400,14 @@ static int omap1_spi100k_probe(struct platform_device *pdev) if (!pdev->id) return -EINVAL; - master = spi_alloc_master(&pdev->dev, sizeof *spi100k); + master = spi_alloc_master(&pdev->dev, sizeof(*spi100k)); if (master == NULL) { dev_dbg(&pdev->dev, "master allocation failed\n"); return -ENOMEM; } if (pdev->id != -1) - master->bus_num = pdev->id; + master->bus_num = pdev->id; master->setup = omap1_spi100k_setup; master->transfer_one_message = omap1_spi100k_transfer_one_message; -- cgit v1.2.3 From 736198b0486c8d5032456de0813515d954df978a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Sat, 1 Mar 2014 11:56:06 +0100 Subject: spi: pl022: Let runtime PM callbacks be available for CONFIG_PM Convert to the SET_PM_RUNTIME_PM macro while defining the runtime PM callbacks. This means the callbacks becomes available for both CONFIG_PM_SLEEP and CONFIG_PM_RUNTIME, which is needed to handle the combinations of these scenarios. Signed-off-by: Ulf Hansson Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 74a072924b65..6dfcabf74728 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2277,7 +2277,7 @@ pl022_remove(struct amba_device *adev) return 0; } -#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME) +#ifdef CONFIG_PM /* * These two functions are used from both suspend/resume and * the runtime counterparts to handle external resources like @@ -2343,7 +2343,7 @@ static int pl022_resume(struct device *dev) } #endif /* CONFIG_PM */ -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); @@ -2363,7 +2363,7 @@ static int pl022_runtime_resume(struct device *dev) static const struct dev_pm_ops pl022_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume) - SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL) + SET_PM_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL) }; static struct vendor_data vendor_arm = { -- cgit v1.2.3 From 84a5dc41f64faadb79cde600b130c8cbd7e21977 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Sat, 1 Mar 2014 11:56:07 +0100 Subject: spi: pl022: Don't ignore power domain and amba bus at system suspend Previously only the resources controlled by the driver were put into low power state at system suspend. Both the amba bus and a potential power domain were ignored. Moreover, while putting the device into low power state we first brought it back to full power, but for no particular reason. To handle both issues above, use pm_runtime_force_suspend|resume() from the system suspend|resume callbacks. Signed-off-by: Ulf Hansson Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 54 +++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 6dfcabf74728..d37e840944d6 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2277,35 +2277,7 @@ pl022_remove(struct amba_device *adev) return 0; } -#ifdef CONFIG_PM -/* - * These two functions are used from both suspend/resume and - * the runtime counterparts to handle external resources like - * clocks, pins and regulators when going to sleep. - */ -static void pl022_suspend_resources(struct pl022 *pl022, bool runtime) -{ - clk_disable_unprepare(pl022->clk); - - if (runtime) - pinctrl_pm_select_idle_state(&pl022->adev->dev); - else - pinctrl_pm_select_sleep_state(&pl022->adev->dev); -} - -static void pl022_resume_resources(struct pl022 *pl022, bool runtime) -{ - /* First go to the default state */ - pinctrl_pm_select_default_state(&pl022->adev->dev); - if (!runtime) - /* Then let's idle the pins until the next transfer happens */ - pinctrl_pm_select_idle_state(&pl022->adev->dev); - - clk_prepare_enable(pl022->clk); -} -#endif - -#ifdef CONFIG_SUSPEND +#ifdef CONFIG_PM_SLEEP static int pl022_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); @@ -2317,8 +2289,13 @@ static int pl022_suspend(struct device *dev) return ret; } - pm_runtime_get_sync(dev); - pl022_suspend_resources(pl022, false); + ret = pm_runtime_force_suspend(dev); + if (ret) { + spi_master_resume(pl022->master); + return ret; + } + + pinctrl_pm_select_sleep_state(dev); dev_dbg(dev, "suspended\n"); return 0; @@ -2329,8 +2306,9 @@ static int pl022_resume(struct device *dev) struct pl022 *pl022 = dev_get_drvdata(dev); int ret; - pl022_resume_resources(pl022, false); - pm_runtime_put(dev); + ret = pm_runtime_force_resume(dev); + if (ret) + dev_err(dev, "problem resuming\n"); /* Start the queue running */ ret = spi_master_resume(pl022->master); @@ -2341,14 +2319,16 @@ static int pl022_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM */ +#endif #ifdef CONFIG_PM static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - pl022_suspend_resources(pl022, true); + clk_disable_unprepare(pl022->clk); + pinctrl_pm_select_idle_state(dev); + return 0; } @@ -2356,7 +2336,9 @@ static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - pl022_resume_resources(pl022, true); + pinctrl_pm_select_default_state(dev); + clk_prepare_enable(pl022->clk); + return 0; } #endif -- cgit v1.2.3 From 380603712072145785f19040b9791f4f6cde414e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 5 Mar 2014 15:17:23 +0800 Subject: spi: s3c24xx: Add missing spi_master_{resume,suspend} calls to PM callbacks This is required since commit 2025172e3280 "spi/bitbang: Use core message pump". spi-bitbang now uses core message pump, so it needs to call spi_master_suspend/ spi_master_resume to start/stop the queue while suspend/resume. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index f1ffbfa6acef..b7f5c349a3c0 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -634,6 +634,11 @@ static int s3c24xx_spi_remove(struct platform_device *dev) static int s3c24xx_spi_suspend(struct device *dev) { struct s3c24xx_spi *hw = dev_get_drvdata(dev); + int ret; + + ret = spi_master_suspend(hw->master); + if (ret) + return ret; if (hw->pdata && hw->pdata->gpio_setup) hw->pdata->gpio_setup(hw->pdata, 0); @@ -647,7 +652,7 @@ static int s3c24xx_spi_resume(struct device *dev) struct s3c24xx_spi *hw = dev_get_drvdata(dev); s3c24xx_spi_initialsetup(hw); - return 0; + return spi_master_resume(hw->master); } static const struct dev_pm_ops s3c24xx_spi_pmops = { -- cgit v1.2.3 From 490c97747d5dc77dfb5826e2823b41d8b2ef7ecc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Mar 2014 10:59:12 +0100 Subject: spi: rspi: Add runtime PM support, using spi core auto_runtime_pm Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 92bec7e91046..1fb0ad213324 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -1111,7 +1112,7 @@ static int rspi_remove(struct platform_device *pdev) struct rspi_data *rspi = platform_get_drvdata(pdev); rspi_release_dma(rspi); - clk_disable_unprepare(rspi->clk); + pm_runtime_disable(&pdev->dev); return 0; } @@ -1242,16 +1243,13 @@ static int rspi_probe(struct platform_device *pdev) goto error1; } - ret = clk_prepare_enable(rspi->clk); - if (ret < 0) { - dev_err(&pdev->dev, "unable to prepare/enable clock\n"); - goto error1; - } + pm_runtime_enable(&pdev->dev); init_waitqueue_head(&rspi->wait); master->bus_num = pdev->id; master->setup = rspi_setup; + master->auto_runtime_pm = true; master->transfer_one = ops->transfer_one; master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; @@ -1312,7 +1310,7 @@ static int rspi_probe(struct platform_device *pdev) error3: rspi_release_dma(rspi); error2: - clk_disable_unprepare(rspi->clk); + pm_runtime_disable(&pdev->dev); error1: spi_master_put(master); -- cgit v1.2.3 From d7a7f6ec14fb07ed9c1167db760bc535b742b382 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 26 Mar 2014 19:22:08 +0800 Subject: spi: omap-uwire: Remove full duplex check This driver sets the SPI_MASTER_HALF_DUPLEX flag, so the spi core will check transfers to ensure they are not full duplex. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap-uwire.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 95cc6948a2ba..f5176bbfa6f1 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -218,10 +218,6 @@ static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) if (!t->tx_buf && !t->rx_buf) return 0; - /* Microwire doesn't read and write concurrently */ - if (t->tx_buf && t->rx_buf) - return -EPERM; - w = spi->chip_select << 10; w |= CS_CMD; -- cgit v1.2.3 From cbab80464f7470432b4b8061333c572a8b9a713f Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 27 Mar 2014 11:07:59 -0500 Subject: spi: omap-uwire: add missing iounmap This fixes the following coccicheck warning: linux-2.6/drivers/spi/spi-omap-uwire.c:485:2-8: ERROR: missing iounmap; ioremap on line 471 and execution via conditional on line 481 Signed-off-by: Nishanth Menon Signed-off-by: Mark Brown --- drivers/spi/spi-omap-uwire.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index f5176bbfa6f1..31ce92ff738b 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -478,6 +478,7 @@ static int uwire_probe(struct platform_device *pdev) status = PTR_ERR(uwire->ck); dev_dbg(&pdev->dev, "no functional clock?\n"); spi_master_put(master); + iounmap(uwire_base); return status; } clk_enable(uwire->ck); -- cgit v1.2.3 From d1c18caace25aa8b6fcfe4dc78e96a031f1eab2d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 29 Mar 2014 15:03:37 +0800 Subject: spi: omap-100k: Fix memory leak The memory allocated for cs is not freed anywhere. Convert to use devm_kzalloc to fix the memory leak. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-omap-100k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index c9e228242643..e7ffcded4e14 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -283,7 +283,7 @@ static int omap1_spi100k_setup(struct spi_device *spi) spi100k = spi_master_get_devdata(spi->master); if (!cs) { - cs = kzalloc(sizeof(*cs), GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; cs->base = spi100k->base + spi->chip_select * 0x14; -- cgit v1.2.3