summaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi.c45
-rw-r--r--drivers/spi/spi_mpc83xx.c30
-rw-r--r--drivers/spi/spi_s3c24xx.c2
-rw-r--r--drivers/spi/spidev.c13
4 files changed, 67 insertions, 23 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b05de30b5d9b..e84d21597943 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -200,6 +200,8 @@ static DEFINE_MUTEX(board_lock);
* platforms may not be able to use spi_register_board_info though, and
* this is exported so that for example a USB or parport based adapter
* driver could add devices (which it would learn about out-of-band).
+ *
+ * Returns the new device, or NULL.
*/
struct spi_device *spi_new_device(struct spi_master *master,
struct spi_board_info *chip)
@@ -208,7 +210,20 @@ struct spi_device *spi_new_device(struct spi_master *master,
struct device *dev = master->cdev.dev;
int status;
- /* NOTE: caller did any chip->bus_num checks necessary */
+ /* NOTE: caller did any chip->bus_num checks necessary.
+ *
+ * Also, unless we change the return value convention to use
+ * error-or-pointer (not NULL-or-pointer), troubleshootability
+ * suggests syslogged diagnostics are best here (ugh).
+ */
+
+ /* Chipselects are numbered 0..max; validate. */
+ if (chip->chip_select >= master->num_chipselect) {
+ dev_err(dev, "cs%d > max %d\n",
+ chip->chip_select,
+ master->num_chipselect);
+ return NULL;
+ }
if (!spi_master_get(master))
return NULL;
@@ -236,10 +251,10 @@ struct spi_device *spi_new_device(struct spi_master *master,
proxy->controller_state = NULL;
proxy->dev.release = spidev_release;
- /* drivers may modify this default i/o setup */
+ /* drivers may modify this initial i/o setup */
status = master->setup(proxy);
if (status < 0) {
- dev_dbg(dev, "can't %s %s, status %d\n",
+ dev_err(dev, "can't %s %s, status %d\n",
"setup", proxy->dev.bus_id, status);
goto fail;
}
@@ -249,7 +264,7 @@ struct spi_device *spi_new_device(struct spi_master *master,
*/
status = device_register(&proxy->dev);
if (status < 0) {
- dev_dbg(dev, "can't %s %s, status %d\n",
+ dev_err(dev, "can't %s %s, status %d\n",
"add", proxy->dev.bus_id, status);
goto fail;
}
@@ -306,7 +321,6 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
- struct device *dev = master->cdev.dev;
mutex_lock(&board_lock);
list_for_each_entry(bi, &board_list, list) {
@@ -316,17 +330,9 @@ static void scan_boardinfo(struct spi_master *master)
for (n = bi->n_board_info; n > 0; n--, chip++) {
if (chip->bus_num != master->bus_num)
continue;
- /* some controllers only have one chip, so they
- * might not use chipselects. otherwise, the
- * chipselects are numbered 0..max.
+ /* NOTE: this relies on spi_new_device to
+ * issue diagnostics when given bogus inputs
*/
- if (chip->chip_select >= master->num_chipselect
- && master->num_chipselect) {
- dev_dbg(dev, "cs%d > max %d\n",
- chip->chip_select,
- master->num_chipselect);
- continue;
- }
(void) spi_new_device(master, chip);
}
}
@@ -419,8 +425,17 @@ int spi_register_master(struct spi_master *master)
if (!dev)
return -ENODEV;
+ /* even if it's just one always-selected device, there must
+ * be at least one chipselect
+ */
+ if (master->num_chipselect == 0)
+ return -EINVAL;
+
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
+ /* FIXME switch to an IDR based scheme, something like
+ * I2C now uses, so we can't run out of "dynamic" IDs
+ */
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c
index 3295cfcc9f20..0c16a2b39b41 100644
--- a/drivers/spi/spi_mpc83xx.c
+++ b/drivers/spi/spi_mpc83xx.c
@@ -39,6 +39,7 @@ struct mpc83xx_spi_reg {
};
/* SPI Controller mode register definitions */
+#define SPMODE_LOOP (1 << 30)
#define SPMODE_CI_INACTIVEHIGH (1 << 29)
#define SPMODE_CP_BEGIN_EDGECLK (1 << 28)
#define SPMODE_DIV16 (1 << 27)
@@ -153,12 +154,18 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value)
len = len - 1;
/* mask out bits we are going to set */
- regval &= ~0x38ff0000;
+ regval &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
+ | SPMODE_LEN(0xF) | SPMODE_DIV16
+ | SPMODE_PM(0xF) | SPMODE_REV | SPMODE_LOOP);
if (spi->mode & SPI_CPHA)
regval |= SPMODE_CP_BEGIN_EDGECLK;
if (spi->mode & SPI_CPOL)
regval |= SPMODE_CI_INACTIVEHIGH;
+ if (!(spi->mode & SPI_LSB_FIRST))
+ regval |= SPMODE_REV;
+ if (spi->mode & SPI_LOOP)
+ regval |= SPMODE_LOOP;
regval |= SPMODE_LEN(len);
@@ -176,6 +183,8 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value)
regval |= SPMODE_PM(pm);
}
+ /* Turn off SPI unit prior changing mode */
+ mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
if (mpc83xx_spi->activate_cs)
mpc83xx_spi->activate_cs(spi->chip_select, pol);
@@ -231,6 +240,14 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
} else
return -EINVAL;
+ if (mpc83xx_spi->qe_mode && spi->mode & SPI_LSB_FIRST) {
+ mpc83xx_spi->tx_shift = 0;
+ if (bits_per_word <= 8)
+ mpc83xx_spi->rx_shift = 8;
+ else
+ mpc83xx_spi->rx_shift = 0;
+ }
+
/* nsecs = (clock period)/2 */
if (!hz)
hz = spi->max_speed_hz;
@@ -245,17 +262,22 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
- /* Mask out bits_per_wordgth */
- regval &= 0xff0fffff;
+ /* mask out bits we are going to set */
+ regval &= ~(SPMODE_LEN(0xF) | SPMODE_REV);
regval |= SPMODE_LEN(bits_per_word);
+ if (!(spi->mode & SPI_LSB_FIRST))
+ regval |= SPMODE_REV;
+ /* Turn off SPI unit prior changing mode */
+ mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
return 0;
}
/* the spi->mode bits understood by this driver: */
-#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
+ | SPI_LSB_FIRST | SPI_LOOP)
static int mpc83xx_spi_setup(struct spi_device *spi)
{
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 7071ff8da63e..5cf48123e0ef 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -28,7 +28,7 @@
#include <asm/hardware.h>
#include <asm/arch/regs-gpio.h>
-#include <asm/arch/regs-spi.h>
+#include <asm/plat-s3c24xx/regs-spi.h>
#include <asm/arch/spi.h>
struct s3c24xx_spi {
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 38b60ad0eda0..630f781aeb19 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -55,9 +55,16 @@
static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG];
-/* Bit masks for spi_device.mode management */
-#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL)
-
+/* Bit masks for spi_device.mode management. Note that incorrect
+ * settings for CS_HIGH and 3WIRE can cause *lots* of trouble for other
+ * devices on a shared bus: CS_HIGH, because this device will be
+ * active when it shouldn't be; 3WIRE, because when active it won't
+ * behave as it should.
+ *
+ * REVISIT should changing those two modes be privileged?
+ */
+#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
+ | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP)
struct spidev_data {
struct device dev;
OpenPOWER on IntegriCloud