Merge branch 'for-next/spi'
This commit is contained in:
commit
0bf6b41cfa
|
@ -210,7 +210,12 @@ void at91_add_device_spi(int spi_id, struct at91_spi_platform_data *pdata)
|
|||
cs_pin = pdata->chipselect[i];
|
||||
|
||||
/* enable chip-select pin */
|
||||
if (cs_pin > 0)
|
||||
if (!gpio_is_valid(cs_pin))
|
||||
continue;
|
||||
|
||||
if (cs_pin == AT91_PIN_PA3)
|
||||
at91_set_A_periph(cs_pin, 0);
|
||||
else
|
||||
at91_set_gpio_output(cs_pin, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -607,6 +607,12 @@ config CMD_I2C
|
|||
include i2c_probe, i2c_read and i2c_write commands to communicate
|
||||
on i2c bus.
|
||||
|
||||
config CMD_SPI
|
||||
bool
|
||||
depends on SPI
|
||||
prompt "spi command"
|
||||
help
|
||||
|
||||
config CMD_LED
|
||||
bool
|
||||
depends on LED
|
||||
|
|
|
@ -55,6 +55,7 @@ obj-$(CONFIG_USB_GADGET_SERIAL) += usbserial.o
|
|||
obj-$(CONFIG_CMD_GPIO) += gpio.o
|
||||
obj-$(CONFIG_CMD_UNCOMPRESS) += uncompress.o
|
||||
obj-$(CONFIG_CMD_I2C) += i2c.o
|
||||
obj-$(CONFIG_CMD_SPI) += spi.o
|
||||
obj-$(CONFIG_CMD_UBI) += ubi.o
|
||||
obj-$(CONFIG_CMD_MENU) += menu.o
|
||||
obj-$(CONFIG_CMD_PASSWD) += passwd.o
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
|
||||
*
|
||||
* Under GPLv2 only
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <getopt.h>
|
||||
#include <spi/spi.h>
|
||||
|
||||
static int do_spi(int argc, char *argv[])
|
||||
{
|
||||
struct spi_device spi;
|
||||
int bus = 0;
|
||||
int read = 0;
|
||||
int verbose = 0;
|
||||
int opt, count, i, ret;
|
||||
int byte_per_word;
|
||||
|
||||
u8 *tx_buf, *rx_buf;
|
||||
|
||||
memset(&spi, 0, sizeof(struct spi_device));
|
||||
|
||||
spi.max_speed_hz = 1 * 1000 * 1000;
|
||||
spi.bits_per_word = 8;
|
||||
|
||||
while ((opt = getopt(argc, argv, "b:c:r:m:f:w:v")) > 0) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
bus = simple_strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'r':
|
||||
read = simple_strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'c':
|
||||
spi.chip_select = simple_strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'm':
|
||||
spi.mode = simple_strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'f':
|
||||
spi.max_speed_hz = simple_strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'w':
|
||||
spi.bits_per_word = simple_strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
default:
|
||||
return COMMAND_ERROR_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
count = argc - optind;
|
||||
|
||||
if ((!read && !count) || !spi.max_speed_hz || !spi.bits_per_word)
|
||||
return COMMAND_ERROR_USAGE;
|
||||
|
||||
|
||||
spi.master = spi_get_master(bus);
|
||||
if (!spi.master) {
|
||||
printf("spi bus %d not found\n", bus);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (spi.chip_select > spi.master->num_chipselect) {
|
||||
printf("spi chip select (%d)> master num chipselect (%d)\n",
|
||||
spi.chip_select, spi.master->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = spi.master->setup(&spi);
|
||||
if (ret) {
|
||||
printf("can not setup the master (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tx_buf = xmalloc(count);
|
||||
rx_buf = xmalloc(count);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
tx_buf[i] = (u8) simple_strtol(argv[optind + i], NULL, 16);
|
||||
|
||||
ret = spi_write_then_read(&spi, tx_buf, count, rx_buf, read);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
byte_per_word = max(spi.bits_per_word / 8, 1);
|
||||
if (verbose) {
|
||||
printf("device config\n");
|
||||
printf(" bus_num = %d\n", spi.master->bus_num);
|
||||
printf(" max_speed_hz = %d\n", spi.max_speed_hz);
|
||||
printf(" chip_select = %d\n", spi.chip_select);
|
||||
printf(" mode = 0x%x\n", spi.mode);
|
||||
printf(" bits_per_word = %d\n", spi.bits_per_word);
|
||||
printf("\n");
|
||||
|
||||
printf("wrote %i bytes\n", count);
|
||||
memory_display(tx_buf, 0, count, byte_per_word);
|
||||
|
||||
printf("read %i bytes\n", read);
|
||||
}
|
||||
|
||||
memory_display(rx_buf, 0, read, byte_per_word);
|
||||
|
||||
out:
|
||||
free(rx_buf);
|
||||
free(tx_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const __maybe_unused char cmd_spi_help[] =
|
||||
"Usage: spi [OPTION] [data to write 0xXX]\n"
|
||||
"write/read spi device.\n"
|
||||
" -b <bus_num> spi bus number (default = 0)\n"
|
||||
" -r <count> to read\n"
|
||||
" -c <cs> chip select (default = 0)\n"
|
||||
" -m <mode> spi mode (default = 0)\n"
|
||||
" -f <hz> max_speed_hz (default = 1MHz)\n"
|
||||
" -w <bit> bits_per_word (default = 8)\n"
|
||||
" -v verbose\n";
|
||||
|
||||
BAREBOX_CMD_START(spi)
|
||||
.cmd = do_spi,
|
||||
.usage = "write/read spi device",
|
||||
BAREBOX_CMD_HELP(cmd_spi_help)
|
||||
BAREBOX_CMD_END
|
|
@ -66,6 +66,106 @@ static inline bool atmel_spi_is_v2(void)
|
|||
return !cpu_is_at91rm9200();
|
||||
}
|
||||
|
||||
/*
|
||||
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
|
||||
* they assume that spi slave device state will not change on deselect, so
|
||||
* that automagic deselection is OK. ("NPCSx rises if no data is to be
|
||||
* transmitted") Not so! Workaround uses nCSx pins as GPIOs; or newer
|
||||
* controllers have CSAAT and friends.
|
||||
*
|
||||
* Since the CSAAT functionality is a bit weird on newer controllers as
|
||||
* well, we use GPIO to control nCSx pins on all controllers, updating
|
||||
* MR.PCS to avoid confusing the controller. Using GPIOs also lets us
|
||||
* support active-high chipselects despite the controller's belief that
|
||||
* only active-low devices/systems exists.
|
||||
*
|
||||
* However, at91rm9200 has a second erratum whereby nCS0 doesn't work
|
||||
* right when driven with GPIO. ("Mode Fault does not allow more than one
|
||||
* Master on Chip Select 0.") No workaround exists for that ... so for
|
||||
* nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH,
|
||||
* and (c) will trigger that first erratum in some cases.
|
||||
*
|
||||
* TODO: Test if the atmel_spi_is_v2() branch below works on
|
||||
* AT91RM9200 if we use some other register than CSR0. However, don't
|
||||
* do this unconditionally since AP7000 has an errata where the BITS
|
||||
* field in CSR0 overrides all other CSRs.
|
||||
*/
|
||||
|
||||
static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
|
||||
{
|
||||
struct spi_master *master = &as->master;
|
||||
int npcs_pin;
|
||||
unsigned active = spi->mode & SPI_CS_HIGH;
|
||||
u32 mr, csr;
|
||||
|
||||
BUG_ON(spi->chip_select >= master->num_chipselect);
|
||||
npcs_pin = as->cs_pins[spi->chip_select];
|
||||
|
||||
csr = (u32)spi->controller_data;
|
||||
|
||||
if (atmel_spi_is_v2()) {
|
||||
/*
|
||||
* Always use CSR0. This ensures that the clock
|
||||
* switches to the correct idle polarity before we
|
||||
* toggle the CS.
|
||||
*/
|
||||
spi_writel(as, CSR0, csr);
|
||||
spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS)
|
||||
| SPI_BIT(MSTR));
|
||||
mr = spi_readl(as, MR);
|
||||
gpio_set_value(npcs_pin, active);
|
||||
} else {
|
||||
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
|
||||
int i;
|
||||
u32 csr;
|
||||
|
||||
/* Make sure clock polarity is correct */
|
||||
for (i = 0; i < spi->master->num_chipselect; i++) {
|
||||
csr = spi_readl(as, CSR0 + 4 * i);
|
||||
if ((csr ^ cpol) & SPI_BIT(CPOL))
|
||||
spi_writel(as, CSR0 + 4 * i,
|
||||
csr ^ SPI_BIT(CPOL));
|
||||
}
|
||||
|
||||
mr = spi_readl(as, MR);
|
||||
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
|
||||
if (npcs_pin != AT91_PIN_PA3)
|
||||
gpio_set_value(npcs_pin, active);
|
||||
spi_writel(as, MR, mr);
|
||||
}
|
||||
|
||||
dev_dbg(&spi->dev, "activate %u%s, mr %08x\n",
|
||||
npcs_pin, active ? " (high)" : "",
|
||||
mr);
|
||||
}
|
||||
|
||||
static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
|
||||
{
|
||||
struct spi_master *master = &as->master;
|
||||
int npcs_pin;
|
||||
unsigned active = spi->mode & SPI_CS_HIGH;
|
||||
u32 mr;
|
||||
|
||||
BUG_ON(spi->chip_select >= master->num_chipselect);
|
||||
npcs_pin = as->cs_pins[spi->chip_select];
|
||||
|
||||
/* only deactivate *this* device; sometimes transfers to
|
||||
* another device may be active when this routine is called.
|
||||
*/
|
||||
mr = spi_readl(as, MR);
|
||||
if (~SPI_BFEXT(PCS, mr) & (1 << spi->chip_select)) {
|
||||
mr = SPI_BFINS(PCS, 0xf, mr);
|
||||
spi_writel(as, MR, mr);
|
||||
}
|
||||
|
||||
dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x\n",
|
||||
npcs_pin, active ? " (low)" : "",
|
||||
mr);
|
||||
|
||||
if (atmel_spi_is_v2() || npcs_pin != AT91_PIN_PA3)
|
||||
gpio_set_value(npcs_pin, !active);
|
||||
}
|
||||
|
||||
static int atmel_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
|
@ -75,10 +175,18 @@ static int atmel_spi_setup(struct spi_device *spi)
|
|||
unsigned int bits = spi->bits_per_word;
|
||||
unsigned long bus_hz;
|
||||
|
||||
if (spi->controller_data) {
|
||||
csr = (u32)spi->controller_data;
|
||||
spi_writel(as, CSR0, csr);
|
||||
return 0;
|
||||
if (spi->chip_select > spi->master->num_chipselect) {
|
||||
dev_dbg(&spi->dev,
|
||||
"setup: invalid chipselect %u (%u defined)\n",
|
||||
spi->chip_select, spi->master->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bits < 8 || bits > 16) {
|
||||
dev_dbg(&spi->dev,
|
||||
"setup: invalid bits_per_word %u (8 to 16)\n",
|
||||
bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(master->dev, "%s mode 0x%08x bits_per_word: %d speed: %d\n",
|
||||
|
@ -131,30 +239,16 @@ static int atmel_spi_setup(struct spi_device *spi)
|
|||
"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
|
||||
bus_hz / scbr, bits, spi->mode, spi->chip_select, csr);
|
||||
|
||||
spi_writel(as, CSR0, csr);
|
||||
spi->controller_data = (void *)csr;
|
||||
|
||||
/*
|
||||
* store the csr-setting when bits are defined. This happens usually
|
||||
* after the specific spi_device driver has been probed.
|
||||
*/
|
||||
if (bits > 0)
|
||||
spi->controller_data = (void *)csr;
|
||||
cs_deactivate(as, spi);
|
||||
|
||||
if (!atmel_spi_is_v2())
|
||||
spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_spi_chipselect(struct spi_device *spi, struct atmel_spi *as, int on)
|
||||
{
|
||||
struct spi_master *master = &as->master;
|
||||
int cs_pin;
|
||||
int val = ((spi->mode & SPI_CS_HIGH) != 0) == on;
|
||||
|
||||
BUG_ON(spi->chip_select >= master->num_chipselect);
|
||||
cs_pin = as->cs_pins[spi->chip_select];
|
||||
|
||||
gpio_direction_output(cs_pin, val);
|
||||
}
|
||||
|
||||
static int atmel_spi_xchg(struct atmel_spi *as, u32 tx_val)
|
||||
{
|
||||
uint64_t start;
|
||||
|
@ -178,20 +272,57 @@ static int atmel_spi_xchg(struct atmel_spi *as, u32 tx_val)
|
|||
return spi_readl(as, RDR) & 0xffff;
|
||||
}
|
||||
|
||||
static int atmel_spi_do_xfer(struct spi_device *spi, struct atmel_spi *as,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
unsigned int bits = spi->bits_per_word;
|
||||
u32 tx_val;
|
||||
int i = 0, rx_val;
|
||||
|
||||
if (bits <= 8) {
|
||||
const u8 *txbuf = t->tx_buf;
|
||||
u8 *rxbuf = t->rx_buf;
|
||||
|
||||
while (i < t->len) {
|
||||
tx_val = txbuf ? txbuf[i] : 0;
|
||||
|
||||
rx_val = atmel_spi_xchg(as, tx_val);
|
||||
if (rx_val < 0)
|
||||
return rx_val;
|
||||
|
||||
if (rxbuf)
|
||||
rxbuf[i] = rx_val;
|
||||
i++;
|
||||
}
|
||||
} else if (bits <= 16) {
|
||||
const u16 *txbuf = t->tx_buf;
|
||||
u16 *rxbuf = t->rx_buf;
|
||||
|
||||
while (i < t->len >> 1) {
|
||||
tx_val = txbuf ? txbuf[i] : 0;
|
||||
|
||||
rx_val = atmel_spi_xchg(as, tx_val);
|
||||
if (rx_val < 0)
|
||||
return rx_val;
|
||||
|
||||
if (rxbuf)
|
||||
rxbuf[i] = rx_val;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return t->len;
|
||||
}
|
||||
|
||||
static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
|
||||
{
|
||||
int ret;
|
||||
struct spi_master *master = spi->master;
|
||||
struct atmel_spi *as = to_atmel_spi(master);
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned int bits = spi->bits_per_word;
|
||||
unsigned int cs_change;
|
||||
|
||||
mesg->actual_length = 0;
|
||||
ret = master->setup(spi);
|
||||
if (ret < 0) {
|
||||
dev_dbg(master->dev, "transfer: master setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(master->dev, " csr0: %08x\n", spi_readl(as, CSR0));
|
||||
|
||||
|
@ -202,50 +333,38 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
|
|||
t, t->len, t->tx_buf, t->rx_buf);
|
||||
}
|
||||
#endif
|
||||
atmel_spi_chipselect(spi, as, 1);
|
||||
|
||||
cs_activate(as, spi);
|
||||
|
||||
cs_change = 0;
|
||||
|
||||
list_for_each_entry(t, &mesg->transfers, transfer_list) {
|
||||
u32 tx_val;
|
||||
int i = 0, rx_val;
|
||||
|
||||
mesg->actual_length += t->len;
|
||||
if (bits <= 8) {
|
||||
const u8 *txbuf = t->tx_buf;
|
||||
u8 *rxbuf = t->rx_buf;
|
||||
|
||||
while (i < t->len) {
|
||||
tx_val = txbuf ? txbuf[i] : 0;
|
||||
|
||||
rx_val = atmel_spi_xchg(as, tx_val);
|
||||
if (rx_val < 0) {
|
||||
ret = rx_val;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rxbuf)
|
||||
rxbuf[i] = rx_val;
|
||||
i++;
|
||||
}
|
||||
} else if (bits <= 16) {
|
||||
const u16 *txbuf = t->tx_buf;
|
||||
u16 *rxbuf = t->rx_buf;
|
||||
|
||||
while (i < t->len >> 1) {
|
||||
tx_val = txbuf ? txbuf[i] : 0;
|
||||
|
||||
rx_val = atmel_spi_xchg(as, tx_val);
|
||||
if (rx_val < 0) {
|
||||
ret = rx_val;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rxbuf)
|
||||
rxbuf[i] = rx_val;
|
||||
i++;
|
||||
}
|
||||
if (cs_change) {
|
||||
udelay(1);
|
||||
cs_deactivate(as, spi);
|
||||
udelay(1);
|
||||
cs_activate(as, spi);
|
||||
}
|
||||
|
||||
cs_change = t->cs_change;
|
||||
|
||||
ret = atmel_spi_do_xfer(spi, as, t);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
mesg->actual_length += ret;
|
||||
|
||||
if (cs_change)
|
||||
cs_activate(as, spi);
|
||||
}
|
||||
out:
|
||||
atmel_spi_chipselect(spi, as, 0);
|
||||
|
||||
if (!cs_change)
|
||||
cs_deactivate(as, spi);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
cs_deactivate(as, spi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -265,6 +384,7 @@ static int atmel_spi_probe(struct device_d *dev)
|
|||
|
||||
master = &as->master;
|
||||
master->dev = dev;
|
||||
master->bus_num = dev->id;
|
||||
|
||||
as->clk = clk_get(dev, "spi_clk");
|
||||
if (IS_ERR(as->clk)) {
|
||||
|
|
|
@ -327,10 +327,10 @@ static int omap3_spi_setup(struct spi_device *spi)
|
|||
{
|
||||
struct spi_master *master = spi->master;
|
||||
|
||||
if (((master->bus_num == 0) && (spi->chip_select > 3)) ||
|
||||
((master->bus_num == 1) && (spi->chip_select > 1)) ||
|
||||
if (((master->bus_num == 1) && (spi->chip_select > 3)) ||
|
||||
((master->bus_num == 2) && (spi->chip_select > 1)) ||
|
||||
((master->bus_num == 3) && (spi->chip_select > 0))) {
|
||||
((master->bus_num == 3) && (spi->chip_select > 1)) ||
|
||||
((master->bus_num == 4) && (spi->chip_select > 0))) {
|
||||
printf("SPI error: unsupported chip select %i \
|
||||
on bus %i\n", spi->chip_select, master->bus_num);
|
||||
return -EINVAL;
|
||||
|
@ -363,10 +363,10 @@ static int omap3_spi_probe(struct device_d *dev)
|
|||
/*
|
||||
* OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
|
||||
* with different number of chip selects (CS, channels):
|
||||
* McSPI1 has 4 CS (bus 0, cs 0 - 3)
|
||||
* McSPI2 has 2 CS (bus 1, cs 0 - 1)
|
||||
* McSPI3 has 2 CS (bus 2, cs 0 - 1)
|
||||
* McSPI4 has 1 CS (bus 3, cs 0)
|
||||
* McSPI1 has 4 CS (bus 1, cs 0 - 3)
|
||||
* McSPI2 has 2 CS (bus 2, cs 0 - 1)
|
||||
* McSPI3 has 2 CS (bus 3, cs 0 - 1)
|
||||
* McSPI4 has 1 CS (bus 4, cs 0)
|
||||
*
|
||||
* The board code has to make sure that it does not use
|
||||
* invalid buses or chip selects.
|
||||
|
|
|
@ -219,6 +219,18 @@ int spi_register_master(struct spi_master *master)
|
|||
}
|
||||
EXPORT_SYMBOL(spi_register_master);
|
||||
|
||||
struct spi_master *spi_get_master(int bus)
|
||||
{
|
||||
struct spi_master* m;
|
||||
|
||||
list_for_each_entry(m, &spi_master_list, list) {
|
||||
if (m->bus_num == bus)
|
||||
return m;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int spi_sync(struct spi_device *spi, struct spi_message *message)
|
||||
{
|
||||
return spi->master->transfer(spi, message);
|
||||
|
|
|
@ -432,6 +432,8 @@ static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
|
|||
|
||||
extern struct bus_type spi_bus;
|
||||
|
||||
struct spi_master *spi_get_master(int bus);
|
||||
|
||||
static inline int spi_register_driver(struct driver_d *drv)
|
||||
{
|
||||
drv->bus = &spi_bus;
|
||||
|
|
Loading…
Reference in New Issue