diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c')
-rw-r--r-- | qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c b/qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c new file mode 100644 index 000000000..0d59c3615 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2006 Ben Warren, Qstreams Networks Inc. + * With help from the common/soft_spi and arch/powerpc/cpu/mpc8260 drivers + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <malloc.h> +#include <spi.h> +#include <asm/mpc8xxx_spi.h> + +#define SPI_EV_NE (0x80000000 >> 22) /* Receiver Not Empty */ +#define SPI_EV_NF (0x80000000 >> 23) /* Transmitter Not Full */ + +#define SPI_MODE_LOOP (0x80000000 >> 1) /* Loopback mode */ +#define SPI_MODE_REV (0x80000000 >> 5) /* Reverse mode - MSB first */ +#define SPI_MODE_MS (0x80000000 >> 6) /* Always master */ +#define SPI_MODE_EN (0x80000000 >> 7) /* Enable interface */ + +#define SPI_TIMEOUT 1000 + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = spi_alloc_slave_base(bus, cs); + if (!slave) + return NULL; + + /* + * TODO: Some of the code in spi_init() should probably move + * here, or into spi_claim_bus() below. + */ + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +void spi_init(void) +{ + volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi; + + /* + * SPI pins on the MPC83xx are not muxed, so all we do is initialize + * some registers + */ + spi->mode = SPI_MODE_REV | SPI_MODE_MS | SPI_MODE_EN; + spi->mode = (spi->mode & 0xfff0ffff) | (1 << 16); /* Use SYSCLK / 8 + (16.67MHz typ.) */ + spi->event = 0xffffffff; /* Clear all SPI events */ + spi->mask = 0x00000000; /* Mask all SPI interrupts */ + spi->com = 0; /* LST bit doesn't do anything, so disregard */ +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi; + unsigned int tmpdout, tmpdin, event; + int numBlks = DIV_ROUND_UP(bitlen, 32); + int tm, isRead = 0; + unsigned char charSize = 32; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + spi->event = 0xffffffff; /* Clear all SPI events */ + + /* handle data in 32-bit chunks */ + while (numBlks--) { + tmpdout = 0; + charSize = (bitlen >= 32 ? 32 : bitlen); + + /* Shift data so it's msb-justified */ + tmpdout = *(u32 *) dout >> (32 - charSize); + + /* The LEN field of the SPMODE register is set as follows: + * + * Bit length setting + * len <= 4 3 + * 4 < len <= 16 len - 1 + * len > 16 0 + */ + + spi->mode &= ~SPI_MODE_EN; + + if (bitlen <= 16) { + if (bitlen <= 4) + spi->mode = (spi->mode & 0xff0fffff) | + (3 << 20); + else + spi->mode = (spi->mode & 0xff0fffff) | + ((bitlen - 1) << 20); + } else { + spi->mode = (spi->mode & 0xff0fffff); + /* Set up the next iteration if sending > 32 bits */ + bitlen -= 32; + dout += 4; + } + + spi->mode |= SPI_MODE_EN; + + spi->tx = tmpdout; /* Write the data out */ + debug("*** spi_xfer: ... %08x written\n", tmpdout); + + /* + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * The NE event must be read and cleared first + */ + for (tm = 0, isRead = 0; tm < SPI_TIMEOUT; ++tm) { + event = spi->event; + if (event & SPI_EV_NE) { + tmpdin = spi->rx; + spi->event |= SPI_EV_NE; + isRead = 1; + + *(u32 *) din = (tmpdin << (32 - charSize)); + if (charSize == 32) { + /* Advance output buffer by 32 bits */ + din += 4; + } + } + /* + * Only bail when we've had both NE and NF events. + * This will cause timeouts on RO devices, so maybe + * in the future put an arbitrary delay after writing + * the device. Arbitrary delays suck, though... + */ + if (isRead && (event & SPI_EV_NF)) + break; + } + if (tm >= SPI_TIMEOUT) + puts("*** spi_xfer: Time out during SPI transfer"); + + debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} |