Line 1... |
Line 1... |
/*
|
/*
|
* Opencore tiny_spi driver
|
* Opencore tiny_spi driver
|
*
|
*
|
|
* http://opencores.org/project,tiny_spi
|
|
*
|
* based on bfin_spi.c
|
* based on bfin_spi.c
|
* Copyright (c) 2005-2008 Analog Devices Inc.
|
* Copyright (c) 2005-2008 Analog Devices Inc.
|
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
|
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
|
*
|
*
|
* Licensed under the GPL-2 or later.
|
* Licensed under the GPL-2 or later.
|
*/
|
*/
|
|
|
#include <common.h>
|
#include <common.h>
|
#include <asm/io.h>
|
#include <asm/io.h>
|
#include <malloc.h>
|
#include <malloc.h>
|
#include <spi.h>
|
#include <spi.h>
|
#include <asm/gpio.h>
|
#include <asm/gpio.h>
|
|
|
#define TINY_SPI_RXDATA 0
|
#define TINY_SPI_RXDATA 0
|
#define TINY_SPI_TXDATA 4
|
#define TINY_SPI_TXDATA 4
|
#define TINY_SPI_STATUS 8
|
#define TINY_SPI_STATUS 8
|
#define TINY_SPI_CONTROL 12
|
#define TINY_SPI_CONTROL 12
|
#define TINY_SPI_BAUD 16
|
#define TINY_SPI_BAUD 16
|
Line 24... |
Line 28... |
struct tiny_spi_host {
|
struct tiny_spi_host {
|
ulong base;
|
ulong base;
|
uint freq;
|
uint freq;
|
uint baudwidth;
|
uint baudwidth;
|
};
|
};
|
static struct tiny_spi_host tiny_spi_host_list[] = CONFIG_SYS_TINY_SPI_LIST;
|
static const struct tiny_spi_host tiny_spi_host_list[] =
|
|
CONFIG_SYS_TINY_SPI_LIST;
|
|
|
struct tiny_spi_slave {
|
struct tiny_spi_slave {
|
struct spi_slave slave;
|
struct spi_slave slave;
|
struct tiny_spi_host *host;
|
const struct tiny_spi_host *host;
|
uint mode;
|
uint mode;
|
uint baud;
|
uint baud;
|
uint flg;
|
uint flg;
|
};
|
};
|
#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave)
|
#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave)
|
|
|
__attribute__((weak))
|
|
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
{
|
{
|
return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs);
|
return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs);
|
}
|
}
|
|
|
__attribute__((weak))
|
|
void spi_cs_activate(struct spi_slave *slave)
|
void spi_cs_activate(struct spi_slave *slave)
|
{
|
{
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
unsigned int cs = slave->cs;
|
unsigned int cs = slave->cs;
|
gpio_set_value(cs, tiny_spi->flg);
|
gpio_set_value(cs, tiny_spi->flg);
|
debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
|
debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
|
}
|
}
|
|
|
__attribute__((weak))
|
|
void spi_cs_deactivate(struct spi_slave *slave)
|
void spi_cs_deactivate(struct spi_slave *slave)
|
{
|
{
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
unsigned int cs = slave->cs;
|
unsigned int cs = slave->cs;
|
gpio_set_value(cs, !tiny_spi->flg);
|
gpio_set_value(cs, !tiny_spi->flg);
|
Line 62... |
Line 64... |
}
|
}
|
|
|
void spi_set_speed(struct spi_slave *slave, uint hz)
|
void spi_set_speed(struct spi_slave *slave, uint hz)
|
{
|
{
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
struct tiny_spi_host *host = tiny_spi->host;
|
const struct tiny_spi_host *host = tiny_spi->host;
|
tiny_spi->baud = DIV_ROUND_UP(host->freq, hz * 2) - 1;
|
tiny_spi->baud = min(DIV_ROUND_UP(host->freq, hz * 2),
|
if (tiny_spi->baud > (1 << host->baudwidth) - 1)
|
(1 << host->baudwidth)) - 1;
|
tiny_spi->baud =(1 << host->baudwidth) - 1;
|
|
debug("%s: speed %u actual %u\n", __func__, hz,
|
debug("%s: speed %u actual %u\n", __func__, hz,
|
host->freq / ((tiny_spi->baud + 1) * 2));
|
host->freq / ((tiny_spi->baud + 1) * 2));
|
}
|
}
|
|
|
void spi_init(void)
|
void spi_init(void)
|
Line 109... |
Line 110... |
}
|
}
|
|
|
int spi_claim_bus(struct spi_slave *slave)
|
int spi_claim_bus(struct spi_slave *slave)
|
{
|
{
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
|
struct tiny_spi_host *host = tiny_spi->host;
|
const struct tiny_spi_host *host = tiny_spi->host;
|
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
|
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
|
gpio_direction_output(slave->cs, !tiny_spi->flg);
|
gpio_direction_output(slave->cs, !tiny_spi->flg);
|
writel(tiny_spi->mode, host->base + TINY_SPI_CONTROL);
|
writel(tiny_spi->mode, host->base + TINY_SPI_CONTROL);
|
writel(tiny_spi->baud, host->base + TINY_SPI_BAUD);
|
writel(tiny_spi->baud, host->base + TINY_SPI_BAUD);
|
return 0;
|
return 0;
|
Line 129... |
Line 130... |
#endif
|
#endif
|
|
|
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
void *din, unsigned long flags)
|
void *din, unsigned long flags)
|
{
|
{
|
struct tiny_spi_host *host = to_tiny_spi_slave(slave)->host;
|
const struct tiny_spi_host *host = to_tiny_spi_slave(slave)->host;
|
const u8 *txp = dout;
|
const u8 *txp = dout;
|
u8 *rxp = din;
|
u8 *rxp = din;
|
uint bytes = bitlen / 8;
|
uint bytes = bitlen / 8;
|
uint i;
|
uint i;
|
|
|
Line 157... |
Line 158... |
if (bytes > 1) {
|
if (bytes > 1) {
|
writeb(*txp++, host->base + TINY_SPI_TXDATA);
|
writeb(*txp++, host->base + TINY_SPI_TXDATA);
|
for (i = 2; i < bytes; i++) {
|
for (i = 2; i < bytes; i++) {
|
u8 rx, tx = *txp++;
|
u8 rx, tx = *txp++;
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXR));
|
TINY_SPI_STATUS_TXR))
|
|
;
|
rx = readb(host->base + TINY_SPI_TXDATA);
|
rx = readb(host->base + TINY_SPI_TXDATA);
|
writeb(tx, host->base + TINY_SPI_TXDATA);
|
writeb(tx, host->base + TINY_SPI_TXDATA);
|
*rxp++ = rx;
|
*rxp++ = rx;
|
}
|
}
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXR));
|
TINY_SPI_STATUS_TXR))
|
|
;
|
*rxp++ = readb(host->base + TINY_SPI_TXDATA);
|
*rxp++ = readb(host->base + TINY_SPI_TXDATA);
|
}
|
}
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXE));
|
TINY_SPI_STATUS_TXE))
|
|
;
|
*rxp++ = readb(host->base + TINY_SPI_RXDATA);
|
*rxp++ = readb(host->base + TINY_SPI_RXDATA);
|
} else if (rxp) {
|
} else if (rxp) {
|
writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA);
|
writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA);
|
if (bytes > 1) {
|
if (bytes > 1) {
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
host->base + TINY_SPI_TXDATA);
|
host->base + TINY_SPI_TXDATA);
|
for (i = 2; i < bytes; i++) {
|
for (i = 2; i < bytes; i++) {
|
u8 rx;
|
u8 rx;
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXR));
|
TINY_SPI_STATUS_TXR))
|
|
;
|
rx = readb(host->base + TINY_SPI_TXDATA);
|
rx = readb(host->base + TINY_SPI_TXDATA);
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
host->base + TINY_SPI_TXDATA);
|
host->base + TINY_SPI_TXDATA);
|
*rxp++ = rx;
|
*rxp++ = rx;
|
}
|
}
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXR));
|
TINY_SPI_STATUS_TXR))
|
|
;
|
*rxp++ = readb(host->base + TINY_SPI_TXDATA);
|
*rxp++ = readb(host->base + TINY_SPI_TXDATA);
|
}
|
}
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXE));
|
TINY_SPI_STATUS_TXE))
|
|
;
|
*rxp++ = readb(host->base + TINY_SPI_RXDATA);
|
*rxp++ = readb(host->base + TINY_SPI_RXDATA);
|
} else if (txp) {
|
} else if (txp) {
|
writeb(*txp++, host->base + TINY_SPI_TXDATA);
|
writeb(*txp++, host->base + TINY_SPI_TXDATA);
|
if (bytes > 1) {
|
if (bytes > 1) {
|
writeb(*txp++, host->base + TINY_SPI_TXDATA);
|
writeb(*txp++, host->base + TINY_SPI_TXDATA);
|
for (i = 2; i < bytes; i++) {
|
for (i = 2; i < bytes; i++) {
|
u8 tx = *txp++;
|
u8 tx = *txp++;
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXR));
|
TINY_SPI_STATUS_TXR))
|
|
;
|
writeb(tx, host->base + TINY_SPI_TXDATA);
|
writeb(tx, host->base + TINY_SPI_TXDATA);
|
}
|
}
|
}
|
}
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXE));
|
TINY_SPI_STATUS_TXE))
|
|
;
|
} else {
|
} else {
|
writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA);
|
writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA);
|
if (bytes > 1) {
|
if (bytes > 1) {
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
host->base + TINY_SPI_TXDATA);
|
host->base + TINY_SPI_TXDATA);
|
for (i = 2; i < bytes; i++) {
|
for (i = 2; i < bytes; i++) {
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXR));
|
TINY_SPI_STATUS_TXR))
|
|
;
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
writeb(CONFIG_TINY_SPI_IDLE_VAL,
|
host->base + TINY_SPI_TXDATA);
|
host->base + TINY_SPI_TXDATA);
|
}
|
}
|
}
|
}
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
while (!(readb(host->base + TINY_SPI_STATUS) &
|
TINY_SPI_STATUS_TXE));
|
TINY_SPI_STATUS_TXE))
|
|
;
|
}
|
}
|
|
|
done:
|
done:
|
if (flags & SPI_XFER_END)
|
if (flags & SPI_XFER_END)
|
spi_cs_deactivate(slave);
|
spi_cs_deactivate(slave);
|