mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-26 21:54:37 +08:00
Merge branch 'spi' of git://git.denx.de/u-boot-x86
This commit is contained in:
commit
8b906a9f0b
@ -164,13 +164,13 @@ init_fnc_t *init_sequence_r[] = {
|
||||
#ifndef CONFIG_SYS_NO_FLASH
|
||||
flash_init_r,
|
||||
#endif
|
||||
#ifdef CONFIG_SPI
|
||||
init_func_spi;
|
||||
#endif
|
||||
env_relocate_r,
|
||||
#ifdef CONFIG_PCI
|
||||
pci_init_r,
|
||||
#endif
|
||||
#ifdef CONFIG_SPI
|
||||
init_func_spi,
|
||||
#endif
|
||||
env_relocate_r,
|
||||
stdio_init,
|
||||
jumptable_init_r,
|
||||
console_init_r,
|
||||
|
@ -21,4 +21,15 @@
|
||||
|
||||
chosen { };
|
||||
memory { device_type = "memory"; reg = <0 0>; };
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "intel,ich9";
|
||||
spi-flash@0 {
|
||||
reg = <0>;
|
||||
compatible = "winbond,w25q64", "spi-flash";
|
||||
memory-map = <0xff800000 0x00800000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -369,8 +369,8 @@ static void spi_test_next_stage(struct test_info *test)
|
||||
* @param vbuf Verification buffer
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int spi_flash_test(struct spi_flash *flash, char *buf, ulong len,
|
||||
ulong offset, char *vbuf)
|
||||
static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
|
||||
ulong offset, uint8_t *vbuf)
|
||||
{
|
||||
struct test_info test;
|
||||
int i;
|
||||
@ -431,9 +431,9 @@ static int do_spi_flash_test(int argc, char * const argv[])
|
||||
{
|
||||
unsigned long offset;
|
||||
unsigned long len;
|
||||
char *buf = (char *)CONFIG_SYS_TEXT_BASE;
|
||||
uint8_t *buf = (uint8_t *)CONFIG_SYS_TEXT_BASE;
|
||||
char *endp;
|
||||
char *vbuf;
|
||||
uint8_t *vbuf;
|
||||
int ret;
|
||||
|
||||
offset = simple_strtoul(argv[1], &endp, 16);
|
||||
|
@ -480,15 +480,13 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
asf = malloc(sizeof(struct atmel_spi_flash));
|
||||
asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name);
|
||||
if (!asf) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
asf->params = params;
|
||||
asf->flash.spi = spi;
|
||||
asf->flash.name = params->name;
|
||||
|
||||
/* Assuming power-of-two page size initially. */
|
||||
page_size = 1 << params->l2_page_size;
|
||||
@ -513,7 +511,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
|
||||
asf->flash.erase = dataflash_erase_at45;
|
||||
page_size += 1 << (params->l2_page_size - 5);
|
||||
} else {
|
||||
asf->flash.read = spi_flash_cmd_read_fast;
|
||||
asf->flash.write = dataflash_write_p2;
|
||||
asf->flash.erase = dataflash_erase_p2;
|
||||
}
|
||||
@ -524,9 +521,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
|
||||
|
||||
case DF_FAMILY_AT26F:
|
||||
case DF_FAMILY_AT26DF:
|
||||
asf->flash.read = spi_flash_cmd_read_fast;
|
||||
asf->flash.write = spi_flash_cmd_write_multi;
|
||||
asf->flash.erase = spi_flash_cmd_erase;
|
||||
asf->flash.page_size = page_size;
|
||||
asf->flash.sector_size = 4096;
|
||||
/* clear SPRL# bit for locked flash */
|
||||
|
@ -46,18 +46,12 @@ struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash = malloc(sizeof(*flash));
|
||||
flash = spi_flash_alloc_base(spi, params->name);
|
||||
if (!flash) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash->spi = spi;
|
||||
flash->name = params->name;
|
||||
|
||||
flash->write = spi_flash_cmd_write_multi;
|
||||
flash->erase = spi_flash_cmd_erase;
|
||||
flash->read = spi_flash_cmd_read_fast;
|
||||
flash->page_size = 256;
|
||||
flash->sector_size = 256 * 16 * 16;
|
||||
flash->size = 256 * 16
|
||||
|
@ -97,18 +97,12 @@ struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash = malloc(sizeof(*flash));
|
||||
flash = spi_flash_alloc_base(spi, params->name);
|
||||
if (!flash) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash->spi = spi;
|
||||
flash->name = params->name;
|
||||
|
||||
flash->write = spi_flash_cmd_write_multi;
|
||||
flash->erase = spi_flash_cmd_erase;
|
||||
flash->read = spi_flash_cmd_read_fast;
|
||||
flash->page_size = 256;
|
||||
flash->sector_size = 256 * 16 * 16;
|
||||
flash->size = flash->sector_size * params->nr_blocks;
|
||||
|
@ -284,15 +284,13 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
sn = malloc(sizeof(*sn));
|
||||
sn = spi_flash_alloc(struct ramtron_spi_fram, spi, params->name);
|
||||
if (!sn) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sn->params = params;
|
||||
sn->flash.spi = spi;
|
||||
sn->flash.name = params->name;
|
||||
|
||||
sn->flash.write = ramtron_write;
|
||||
sn->flash.read = ramtron_read;
|
||||
|
@ -128,18 +128,12 @@ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash = malloc(sizeof(*flash));
|
||||
flash = spi_flash_alloc_base(spi, params->name);
|
||||
if (!flash) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash->spi = spi;
|
||||
flash->name = params->name;
|
||||
|
||||
flash->write = spi_flash_cmd_write_multi;
|
||||
flash->erase = spi_flash_cmd_erase;
|
||||
flash->read = spi_flash_cmd_read_fast;
|
||||
flash->page_size = 256;
|
||||
flash->sector_size = 256 * params->pages_per_sector;
|
||||
flash->size = flash->sector_size * params->nr_sectors;
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
@ -15,6 +16,8 @@
|
||||
|
||||
#include "spi_flash_internal.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static void spi_flash_addr(u32 addr, u8 *cmd)
|
||||
{
|
||||
/* cmd[0] is actual command */
|
||||
@ -87,6 +90,9 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
|
||||
for (actual = 0; actual < len; actual += chunk_len) {
|
||||
chunk_len = min(len - actual, page_size - byte_addr);
|
||||
|
||||
if (flash->spi->max_write_size)
|
||||
chunk_len = min(chunk_len, flash->spi->max_write_size);
|
||||
|
||||
cmd[1] = page_addr >> 8;
|
||||
cmd[2] = page_addr;
|
||||
cmd[3] = byte_addr;
|
||||
@ -111,8 +117,11 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
page_addr++;
|
||||
byte_addr = 0;
|
||||
byte_addr += chunk_len;
|
||||
if (byte_addr == page_size) {
|
||||
page_addr++;
|
||||
byte_addr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
debug("SF: program %s %zu bytes @ %#x\n",
|
||||
@ -140,6 +149,10 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
|
||||
{
|
||||
u8 cmd[5];
|
||||
|
||||
/* Handle memory-mapped SPI */
|
||||
if (flash->memory_map)
|
||||
memcpy(data, flash->memory_map + offset, len);
|
||||
|
||||
cmd[0] = CMD_READ_ARRAY_FAST;
|
||||
spi_flash_addr(offset, cmd);
|
||||
cmd[4] = 0x00;
|
||||
@ -269,6 +282,34 @@ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
|
||||
{
|
||||
fdt_addr_t addr;
|
||||
fdt_size_t size;
|
||||
int node;
|
||||
|
||||
/* If there is no node, do nothing */
|
||||
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
|
||||
if (node < 0)
|
||||
return 0;
|
||||
|
||||
addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
|
||||
if (addr == FDT_ADDR_T_NONE) {
|
||||
debug("%s: Cannot decode address\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flash->size != size) {
|
||||
debug("%s: Memory map must cover entire device\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
flash->memory_map = (void *)addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_OF_CONTROL */
|
||||
|
||||
/*
|
||||
* The following table holds all device probe functions
|
||||
*
|
||||
@ -385,9 +426,18 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
|
||||
goto err_manufacturer_probe;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {
|
||||
debug("SF: FDT decode error\n");
|
||||
goto err_manufacturer_probe;
|
||||
}
|
||||
#endif
|
||||
printf("SF: Detected %s with page size ", flash->name);
|
||||
print_size(flash->sector_size, ", total ");
|
||||
print_size(flash->size, "\n");
|
||||
print_size(flash->size, "");
|
||||
if (flash->memory_map)
|
||||
printf(", mapped at %p", flash->memory_map);
|
||||
puts("\n");
|
||||
|
||||
spi_release_bus(spi);
|
||||
|
||||
@ -401,6 +451,31 @@ err_claim_bus:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
|
||||
const char *name)
|
||||
{
|
||||
struct spi_flash *flash;
|
||||
void *ptr;
|
||||
|
||||
ptr = malloc(size);
|
||||
if (!ptr) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(ptr, '\0', size);
|
||||
flash = (struct spi_flash *)(ptr + offset);
|
||||
|
||||
/* Set up some basic fields - caller will sort out sizes */
|
||||
flash->spi = spi;
|
||||
flash->name = name;
|
||||
|
||||
flash->read = spi_flash_cmd_read_fast;
|
||||
flash->write = spi_flash_cmd_write_multi;
|
||||
flash->erase = spi_flash_cmd_erase;
|
||||
|
||||
return flash;
|
||||
}
|
||||
|
||||
void spi_flash_free(struct spi_flash *flash)
|
||||
{
|
||||
spi_free_slave(flash->spi);
|
||||
|
@ -203,22 +203,16 @@ spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stm = malloc(sizeof(*stm));
|
||||
stm = spi_flash_alloc(struct sst_spi_flash, spi, params->name);
|
||||
if (!stm) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stm->params = params;
|
||||
stm->flash.spi = spi;
|
||||
stm->flash.name = params->name;
|
||||
|
||||
if (stm->params->flags & SST_FEAT_WP)
|
||||
stm->flash.write = sst_write_wp;
|
||||
else
|
||||
stm->flash.write = spi_flash_cmd_write_multi;
|
||||
stm->flash.erase = spi_flash_cmd_erase;
|
||||
stm->flash.read = spi_flash_cmd_read_fast;
|
||||
stm->flash.page_size = 256;
|
||||
stm->flash.sector_size = 4096;
|
||||
stm->flash.size = stm->flash.sector_size * params->nr_sectors;
|
||||
|
@ -176,18 +176,12 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash = malloc(sizeof(*flash));
|
||||
flash = spi_flash_alloc_base(spi, params->name);
|
||||
if (!flash) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash->spi = spi;
|
||||
flash->name = params->name;
|
||||
|
||||
flash->write = spi_flash_cmd_write_multi;
|
||||
flash->erase = spi_flash_cmd_erase;
|
||||
flash->read = spi_flash_cmd_read_fast;
|
||||
flash->page_size = 256;
|
||||
flash->sector_size = 256 * params->pages_per_sector;
|
||||
flash->size = flash->sector_size * params->nr_sectors;
|
||||
|
@ -92,18 +92,12 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash = malloc(sizeof(*flash));
|
||||
flash = spi_flash_alloc_base(spi, params->name);
|
||||
if (!flash) {
|
||||
debug("SF: Failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash->spi = spi;
|
||||
flash->name = params->name;
|
||||
|
||||
flash->write = spi_flash_cmd_write_multi;
|
||||
flash->erase = spi_flash_cmd_erase;
|
||||
flash->read = spi_flash_cmd_read_fast;
|
||||
flash->page_size = 256;
|
||||
flash->sector_size = 4096;
|
||||
flash->size = 4096 * 16 * params->nr_blocks;
|
||||
|
@ -25,6 +25,9 @@ include $(TOPDIR)/config.mk
|
||||
|
||||
LIB := $(obj)libspi.o
|
||||
|
||||
# There are many options which enable SPI, so make this library available
|
||||
COBJS-y += spi.o
|
||||
|
||||
COBJS-$(CONFIG_ALTERA_SPI) += altera_spi.o
|
||||
COBJS-$(CONFIG_ANDES_SPI) += andes_spi.o
|
||||
COBJS-$(CONFIG_ARMADA100_SPI) += armada100_spi.o
|
||||
@ -36,6 +39,7 @@ COBJS-$(CONFIG_CF_SPI) += cf_spi.o
|
||||
COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
|
||||
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
|
||||
COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
|
||||
COBJS-$(CONFIG_ICH_SPI) += ich.o
|
||||
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
|
||||
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
|
||||
COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
|
||||
|
@ -83,12 +83,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
altspi = malloc(sizeof(*altspi));
|
||||
altspi = spi_alloc_slave(struct altera_spi_slave, bus, cs);
|
||||
if (!altspi)
|
||||
return NULL;
|
||||
|
||||
altspi->slave.bus = bus;
|
||||
altspi->slave.cs = cs;
|
||||
altspi->base = altera_spi_base_list[bus];
|
||||
debug("%s: bus:%i cs:%i base:%lx\n", __func__,
|
||||
bus, cs, altspi->base);
|
||||
|
@ -53,12 +53,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
ds = malloc(sizeof(*ds));
|
||||
ds = spi_alloc_slave(struct andes_spi_slave, bus, cs);
|
||||
if (!ds)
|
||||
return NULL;
|
||||
|
||||
ds->slave.bus = bus;
|
||||
ds->slave.cs = cs;
|
||||
ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE;
|
||||
|
||||
/*
|
||||
|
@ -120,12 +120,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
{
|
||||
struct armd_spi_slave *pss;
|
||||
|
||||
pss = malloc(sizeof(*pss));
|
||||
pss = spi_alloc_slave(struct armd_spi_slave, bus, cs);
|
||||
if (!pss)
|
||||
return NULL;
|
||||
|
||||
pss->slave.bus = bus;
|
||||
pss->slave.cs = cs;
|
||||
pss->spi_reg = (struct ssp_reg *)SSP_REG_BASE(CONFIG_SYS_SSP_PORT);
|
||||
|
||||
pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) | SSCR0_SSE;
|
||||
|
@ -84,12 +84,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (mode & SPI_CPOL)
|
||||
csrx |= ATMEL_SPI_CSRx_CPOL;
|
||||
|
||||
as = malloc(sizeof(struct atmel_spi_slave));
|
||||
as = spi_alloc_slave(struct atmel_spi_slave, bus, cs);
|
||||
if (!as)
|
||||
return NULL;
|
||||
|
||||
as->slave.bus = bus;
|
||||
as->slave.cs = cs;
|
||||
as->regs = regs;
|
||||
as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
|
||||
#if defined(CONFIG_AT91SAM9X5) || defined(CONFIG_AT91SAM9M10G45)
|
||||
|
@ -182,12 +182,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
default: return NULL;
|
||||
}
|
||||
|
||||
bss = malloc(sizeof(*bss));
|
||||
bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs);
|
||||
if (!bss)
|
||||
return NULL;
|
||||
|
||||
bss->slave.bus = bus;
|
||||
bss->slave.cs = cs;
|
||||
bss->mmr_base = (void *)mmr_base;
|
||||
bss->ctl = SPE | MSTR | TDBR_CORE;
|
||||
if (mode & SPI_CPHA) bss->ctl |= CPHA;
|
||||
|
@ -178,12 +178,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bss = malloc(sizeof(*bss));
|
||||
bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs);
|
||||
if (!bss)
|
||||
return NULL;
|
||||
|
||||
bss->slave.bus = bus;
|
||||
bss->slave.cs = cs;
|
||||
bss->regs = (struct bfin_spi_regs *)reg_base;
|
||||
bss->control = SPI_CTL_EN | SPI_CTL_MSTR;
|
||||
if (mode & SPI_CPHA)
|
||||
|
@ -120,13 +120,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
dev = malloc(sizeof(struct cf_qspi_slave));
|
||||
dev = spi_alloc_slave(struct cf_qspi_slave, bus, cs);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
/* Initialize to known value */
|
||||
dev->slave.bus = bus;
|
||||
dev->slave.cs = cs;
|
||||
dev->regs = (qspi_t *)MMAP_QSPI;
|
||||
dev->qmr = 0;
|
||||
dev->qwr = 0;
|
||||
|
@ -330,12 +330,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
cfslave = malloc(sizeof(struct cf_spi_slave));
|
||||
cfslave = spi_alloc_slave(struct cf_spi_slave, bus, cs);
|
||||
if (!cfslave)
|
||||
return NULL;
|
||||
|
||||
cfslave->slave.bus = bus;
|
||||
cfslave->slave.cs = cs;
|
||||
cfslave->baudrate = max_hz;
|
||||
|
||||
/* specific setup */
|
||||
|
@ -44,12 +44,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
ds = malloc(sizeof(*ds));
|
||||
ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs);
|
||||
if (!ds)
|
||||
return NULL;
|
||||
|
||||
ds->slave.bus = bus;
|
||||
ds->slave.cs = cs;
|
||||
ds->regs = (struct davinci_spi_regs *)CONFIG_SYS_SPI_BASE;
|
||||
ds->freq = max_hz;
|
||||
|
||||
|
@ -89,15 +89,13 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spi_slave = malloc(sizeof(*spi_slave));
|
||||
spi_slave = spi_alloc_slave(struct exynos_spi_slave, busnum, cs);
|
||||
if (!spi_slave) {
|
||||
debug("%s: Could not allocate spi_slave\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bus = &spi_bus[busnum];
|
||||
spi_slave->slave.bus = busnum;
|
||||
spi_slave->slave.cs = cs;
|
||||
spi_slave->regs = bus->regs;
|
||||
spi_slave->mode = mode;
|
||||
spi_slave->periph_id = bus->periph_id;
|
||||
|
@ -79,12 +79,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
fsl = malloc(sizeof(struct fsl_spi_slave));
|
||||
fsl = spi_alloc_slave(struct fsl_spi_slave, bus, cs);
|
||||
if (!fsl)
|
||||
return NULL;
|
||||
|
||||
fsl->slave.bus = bus;
|
||||
fsl->slave.cs = cs;
|
||||
fsl->mode = mode;
|
||||
fsl->max_transfer_length = ESPI_MAX_DATA_TRANSFER_LEN;
|
||||
|
||||
|
754
drivers/spi/ich.c
Normal file
754
drivers/spi/ich.c
Normal file
@ -0,0 +1,754 @@
|
||||
/*
|
||||
* Copyright (c) 2011-12 The Chromium OS Authors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
* This file is derived from the flashrom project.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <pci.h>
|
||||
#include <pci_ids.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "ich.h"
|
||||
|
||||
#define SPI_OPCODE_WREN 0x06
|
||||
#define SPI_OPCODE_FAST_READ 0x0b
|
||||
|
||||
struct ich_ctlr {
|
||||
pci_dev_t dev; /* PCI device number */
|
||||
int ich_version; /* Controller version, 7 or 9 */
|
||||
int ichspi_lock;
|
||||
int locked;
|
||||
uint8_t *opmenu;
|
||||
int menubytes;
|
||||
void *base; /* Base of register set */
|
||||
uint16_t *preop;
|
||||
uint16_t *optype;
|
||||
uint32_t *addr;
|
||||
uint8_t *data;
|
||||
unsigned databytes;
|
||||
uint8_t *status;
|
||||
uint16_t *control;
|
||||
uint32_t *bbar;
|
||||
uint32_t *pr; /* only for ich9 */
|
||||
uint8_t *speed; /* pointer to speed control */
|
||||
ulong max_speed; /* Maximum bus speed in MHz */
|
||||
};
|
||||
|
||||
struct ich_ctlr ctlr;
|
||||
|
||||
static inline struct ich_spi_slave *to_ich_spi(struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct ich_spi_slave, slave);
|
||||
}
|
||||
|
||||
static unsigned int ich_reg(const void *addr)
|
||||
{
|
||||
return (unsigned)(addr - ctlr.base) & 0xffff;
|
||||
}
|
||||
|
||||
static u8 ich_readb(const void *addr)
|
||||
{
|
||||
u8 value = readb(addr);
|
||||
|
||||
debug("read %2.2x from %4.4x\n", value, ich_reg(addr));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static u16 ich_readw(const void *addr)
|
||||
{
|
||||
u16 value = readw(addr);
|
||||
|
||||
debug("read %4.4x from %4.4x\n", value, ich_reg(addr));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 ich_readl(const void *addr)
|
||||
{
|
||||
u32 value = readl(addr);
|
||||
|
||||
debug("read %8.8x from %4.4x\n", value, ich_reg(addr));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void ich_writeb(u8 value, void *addr)
|
||||
{
|
||||
writeb(value, addr);
|
||||
debug("wrote %2.2x to %4.4x\n", value, ich_reg(addr));
|
||||
}
|
||||
|
||||
static void ich_writew(u16 value, void *addr)
|
||||
{
|
||||
writew(value, addr);
|
||||
debug("wrote %4.4x to %4.4x\n", value, ich_reg(addr));
|
||||
}
|
||||
|
||||
static void ich_writel(u32 value, void *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
debug("wrote %8.8x to %4.4x\n", value, ich_reg(addr));
|
||||
}
|
||||
|
||||
static void write_reg(const void *value, void *dest, uint32_t size)
|
||||
{
|
||||
memcpy_toio(dest, value, size);
|
||||
}
|
||||
|
||||
static void read_reg(const void *src, void *value, uint32_t size)
|
||||
{
|
||||
memcpy_fromio(value, src, size);
|
||||
}
|
||||
|
||||
static void ich_set_bbar(struct ich_ctlr *ctlr, uint32_t minaddr)
|
||||
{
|
||||
const uint32_t bbar_mask = 0x00ffff00;
|
||||
uint32_t ichspi_bbar;
|
||||
|
||||
minaddr &= bbar_mask;
|
||||
ichspi_bbar = ich_readl(ctlr->bbar) & ~bbar_mask;
|
||||
ichspi_bbar |= minaddr;
|
||||
ich_writel(ichspi_bbar, ctlr->bbar);
|
||||
}
|
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
puts("spi_cs_is_valid used but not implemented\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
struct ich_spi_slave *ich;
|
||||
|
||||
ich = spi_alloc_slave(struct ich_spi_slave, bus, cs);
|
||||
if (!ich) {
|
||||
puts("ICH SPI: Out of memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Yes this controller can only write a small number of bytes at
|
||||
* once! The limit is typically 64 bytes.
|
||||
*/
|
||||
ich->slave.max_write_size = ctlr.databytes;
|
||||
ich->speed = max_hz;
|
||||
|
||||
return &ich->slave;
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct ich_spi_slave *ich = to_ich_spi(slave);
|
||||
|
||||
free(ich);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this device ID matches one of supported Intel PCH devices.
|
||||
*
|
||||
* Return the ICH version if there is a match, or zero otherwise.
|
||||
*/
|
||||
static int get_ich_version(uint16_t device_id)
|
||||
{
|
||||
if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC)
|
||||
return 7;
|
||||
|
||||
if ((device_id >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN &&
|
||||
device_id <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX) ||
|
||||
(device_id >= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN &&
|
||||
device_id <= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX))
|
||||
return 9;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @return 1 if the SPI flash supports the 33MHz speed */
|
||||
static int ich9_can_do_33mhz(pci_dev_t dev)
|
||||
{
|
||||
u32 fdod, speed;
|
||||
|
||||
/* Observe SPI Descriptor Component Section 0 */
|
||||
pci_write_config_dword(dev, 0xb0, 0x1000);
|
||||
|
||||
/* Extract the Write/Erase SPI Frequency from descriptor */
|
||||
pci_read_config_dword(dev, 0xb4, &fdod);
|
||||
|
||||
/* Bits 23:21 have the fast read clock frequency, 0=20MHz, 1=33MHz */
|
||||
speed = (fdod >> 21) & 7;
|
||||
|
||||
return speed == 1;
|
||||
}
|
||||
|
||||
static int ich_find_spi_controller(pci_dev_t *devp, int *ich_versionp)
|
||||
{
|
||||
int last_bus = pci_last_busno();
|
||||
int bus;
|
||||
|
||||
if (last_bus == -1) {
|
||||
debug("No PCI busses?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (bus = 0; bus <= last_bus; bus++) {
|
||||
uint16_t vendor_id, device_id;
|
||||
uint32_t ids;
|
||||
pci_dev_t dev;
|
||||
|
||||
dev = PCI_BDF(bus, 31, 0);
|
||||
pci_read_config_dword(dev, 0, &ids);
|
||||
vendor_id = ids;
|
||||
device_id = ids >> 16;
|
||||
|
||||
if (vendor_id == PCI_VENDOR_ID_INTEL) {
|
||||
*devp = dev;
|
||||
*ich_versionp = get_ich_version(device_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
debug("ICH SPI: No ICH found.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ich_init_controller(struct ich_ctlr *ctlr)
|
||||
{
|
||||
uint8_t *rcrb; /* Root Complex Register Block */
|
||||
uint32_t rcba; /* Root Complex Base Address */
|
||||
|
||||
pci_read_config_dword(ctlr->dev, 0xf0, &rcba);
|
||||
/* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable. */
|
||||
rcrb = (uint8_t *)(rcba & 0xffffc000);
|
||||
if (ctlr->ich_version == 7) {
|
||||
struct ich7_spi_regs *ich7_spi;
|
||||
|
||||
ich7_spi = (struct ich7_spi_regs *)(rcrb + 0x3020);
|
||||
ctlr->ichspi_lock = ich_readw(&ich7_spi->spis) & SPIS_LOCK;
|
||||
ctlr->opmenu = ich7_spi->opmenu;
|
||||
ctlr->menubytes = sizeof(ich7_spi->opmenu);
|
||||
ctlr->optype = &ich7_spi->optype;
|
||||
ctlr->addr = &ich7_spi->spia;
|
||||
ctlr->data = (uint8_t *)ich7_spi->spid;
|
||||
ctlr->databytes = sizeof(ich7_spi->spid);
|
||||
ctlr->status = (uint8_t *)&ich7_spi->spis;
|
||||
ctlr->control = &ich7_spi->spic;
|
||||
ctlr->bbar = &ich7_spi->bbar;
|
||||
ctlr->preop = &ich7_spi->preop;
|
||||
ctlr->base = ich7_spi;
|
||||
} else if (ctlr->ich_version == 9) {
|
||||
struct ich9_spi_regs *ich9_spi;
|
||||
|
||||
ich9_spi = (struct ich9_spi_regs *)(rcrb + 0x3800);
|
||||
ctlr->ichspi_lock = ich_readw(&ich9_spi->hsfs) & HSFS_FLOCKDN;
|
||||
ctlr->opmenu = ich9_spi->opmenu;
|
||||
ctlr->menubytes = sizeof(ich9_spi->opmenu);
|
||||
ctlr->optype = &ich9_spi->optype;
|
||||
ctlr->addr = &ich9_spi->faddr;
|
||||
ctlr->data = (uint8_t *)ich9_spi->fdata;
|
||||
ctlr->databytes = sizeof(ich9_spi->fdata);
|
||||
ctlr->status = &ich9_spi->ssfs;
|
||||
ctlr->control = (uint16_t *)ich9_spi->ssfc;
|
||||
ctlr->speed = ich9_spi->ssfc + 2;
|
||||
ctlr->bbar = &ich9_spi->bbar;
|
||||
ctlr->preop = &ich9_spi->preop;
|
||||
ctlr->pr = &ich9_spi->pr[0];
|
||||
ctlr->base = ich9_spi;
|
||||
} else {
|
||||
debug("ICH SPI: Unrecognized ICH version %d.\n",
|
||||
ctlr->ich_version);
|
||||
return -1;
|
||||
}
|
||||
debug("ICH SPI: Version %d detected\n", ctlr->ich_version);
|
||||
|
||||
/* Work out the maximum speed we can support */
|
||||
ctlr->max_speed = 20000000;
|
||||
if (ctlr->ich_version == 9 && ich9_can_do_33mhz(ctlr->dev))
|
||||
ctlr->max_speed = 33000000;
|
||||
|
||||
ich_set_bbar(ctlr, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
uint8_t bios_cntl;
|
||||
|
||||
if (ich_find_spi_controller(&ctlr.dev, &ctlr.ich_version)) {
|
||||
printf("ICH SPI: Cannot find device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ich_init_controller(&ctlr)) {
|
||||
printf("ICH SPI: Cannot setup controller\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the BIOS write protect so write commands are allowed. On
|
||||
* v9, deassert SMM BIOS Write Protect Disable.
|
||||
*/
|
||||
pci_read_config_byte(ctlr.dev, 0xdc, &bios_cntl);
|
||||
if (ctlr.ich_version == 9)
|
||||
bios_cntl &= ~(1 << 5);
|
||||
pci_write_config_byte(ctlr.dev, 0xdc, bios_cntl | 0x1);
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
/* Handled by ICH automatically. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
/* Handled by ICH automatically. */
|
||||
}
|
||||
|
||||
void spi_cs_activate(struct spi_slave *slave)
|
||||
{
|
||||
/* Handled by ICH automatically. */
|
||||
}
|
||||
|
||||
void spi_cs_deactivate(struct spi_slave *slave)
|
||||
{
|
||||
/* Handled by ICH automatically. */
|
||||
}
|
||||
|
||||
static inline void spi_use_out(struct spi_trans *trans, unsigned bytes)
|
||||
{
|
||||
trans->out += bytes;
|
||||
trans->bytesout -= bytes;
|
||||
}
|
||||
|
||||
static inline void spi_use_in(struct spi_trans *trans, unsigned bytes)
|
||||
{
|
||||
trans->in += bytes;
|
||||
trans->bytesin -= bytes;
|
||||
}
|
||||
|
||||
static void spi_setup_type(struct spi_trans *trans, int data_bytes)
|
||||
{
|
||||
trans->type = 0xFF;
|
||||
|
||||
/* Try to guess spi type from read/write sizes. */
|
||||
if (trans->bytesin == 0) {
|
||||
if (trans->bytesout + data_bytes > 4)
|
||||
/*
|
||||
* If bytesin = 0 and bytesout > 4, we presume this is
|
||||
* a write data operation, which is accompanied by an
|
||||
* address.
|
||||
*/
|
||||
trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS;
|
||||
else
|
||||
trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (trans->bytesout == 1) { /* and bytesin is > 0 */
|
||||
trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (trans->bytesout == 4) /* and bytesin is > 0 */
|
||||
trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
|
||||
|
||||
/* Fast read command is called with 5 bytes instead of 4 */
|
||||
if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) {
|
||||
trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
|
||||
--trans->bytesout;
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_setup_opcode(struct spi_trans *trans)
|
||||
{
|
||||
uint16_t optypes;
|
||||
uint8_t opmenu[ctlr.menubytes];
|
||||
|
||||
trans->opcode = trans->out[0];
|
||||
spi_use_out(trans, 1);
|
||||
if (!ctlr.ichspi_lock) {
|
||||
/* The lock is off, so just use index 0. */
|
||||
ich_writeb(trans->opcode, ctlr.opmenu);
|
||||
optypes = ich_readw(ctlr.optype);
|
||||
optypes = (optypes & 0xfffc) | (trans->type & 0x3);
|
||||
ich_writew(optypes, ctlr.optype);
|
||||
return 0;
|
||||
} else {
|
||||
/* The lock is on. See if what we need is on the menu. */
|
||||
uint8_t optype;
|
||||
uint16_t opcode_index;
|
||||
|
||||
/* Write Enable is handled as atomic prefix */
|
||||
if (trans->opcode == SPI_OPCODE_WREN)
|
||||
return 0;
|
||||
|
||||
read_reg(ctlr.opmenu, opmenu, sizeof(opmenu));
|
||||
for (opcode_index = 0; opcode_index < ctlr.menubytes;
|
||||
opcode_index++) {
|
||||
if (opmenu[opcode_index] == trans->opcode)
|
||||
break;
|
||||
}
|
||||
|
||||
if (opcode_index == ctlr.menubytes) {
|
||||
printf("ICH SPI: Opcode %x not found\n",
|
||||
trans->opcode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
optypes = ich_readw(ctlr.optype);
|
||||
optype = (optypes >> (opcode_index * 2)) & 0x3;
|
||||
if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS &&
|
||||
optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS &&
|
||||
trans->bytesout >= 3) {
|
||||
/* We guessed wrong earlier. Fix it up. */
|
||||
trans->type = optype;
|
||||
}
|
||||
if (optype != trans->type) {
|
||||
printf("ICH SPI: Transaction doesn't fit type %d\n",
|
||||
optype);
|
||||
return -1;
|
||||
}
|
||||
return opcode_index;
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_setup_offset(struct spi_trans *trans)
|
||||
{
|
||||
/* Separate the SPI address and data. */
|
||||
switch (trans->type) {
|
||||
case SPI_OPCODE_TYPE_READ_NO_ADDRESS:
|
||||
case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS:
|
||||
return 0;
|
||||
case SPI_OPCODE_TYPE_READ_WITH_ADDRESS:
|
||||
case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS:
|
||||
trans->offset = ((uint32_t)trans->out[0] << 16) |
|
||||
((uint32_t)trans->out[1] << 8) |
|
||||
((uint32_t)trans->out[2] << 0);
|
||||
spi_use_out(trans, 3);
|
||||
return 1;
|
||||
default:
|
||||
printf("Unrecognized SPI transaction type %#x\n", trans->type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for up to 6s til status register bit(s) turn 1 (in case wait_til_set
|
||||
* below is True) or 0. In case the wait was for the bit(s) to set - write
|
||||
* those bits back, which would cause resetting them.
|
||||
*
|
||||
* Return the last read status value on success or -1 on failure.
|
||||
*/
|
||||
static int ich_status_poll(u16 bitmask, int wait_til_set)
|
||||
{
|
||||
int timeout = 600000; /* This will result in 6s */
|
||||
u16 status = 0;
|
||||
|
||||
while (timeout--) {
|
||||
status = ich_readw(ctlr.status);
|
||||
if (wait_til_set ^ ((status & bitmask) == 0)) {
|
||||
if (wait_til_set)
|
||||
ich_writew((status & bitmask), ctlr.status);
|
||||
return status;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
printf("ICH SPI: SCIP timeout, read %x, expected %x\n",
|
||||
status, bitmask);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
int spi_xfer(struct spi_slave *slave, const void *dout,
|
||||
unsigned int bitsout, void *din, unsigned int bitsin)
|
||||
*/
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
void *din, unsigned long flags)
|
||||
{
|
||||
struct ich_spi_slave *ich = to_ich_spi(slave);
|
||||
uint16_t control;
|
||||
int16_t opcode_index;
|
||||
int with_address;
|
||||
int status;
|
||||
int bytes = bitlen / 8;
|
||||
struct spi_trans *trans = &ich->trans;
|
||||
unsigned type = flags & (SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
int using_cmd = 0;
|
||||
/* Align read transactions to 64-byte boundaries */
|
||||
char buff[ctlr.databytes];
|
||||
|
||||
/* Ee don't support writing partial bytes. */
|
||||
if (bitlen % 8) {
|
||||
debug("ICH SPI: Accessing partial bytes not supported\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* An empty end transaction can be ignored */
|
||||
if (type == SPI_XFER_END && !dout && !din)
|
||||
return 0;
|
||||
|
||||
if (type & SPI_XFER_BEGIN)
|
||||
memset(trans, '\0', sizeof(*trans));
|
||||
|
||||
/* Dp we need to come back later to finish it? */
|
||||
if (dout && type == SPI_XFER_BEGIN) {
|
||||
if (bytes > ICH_MAX_CMD_LEN) {
|
||||
debug("ICH SPI: Command length limit exceeded\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(trans->cmd, dout, bytes);
|
||||
trans->cmd_len = bytes;
|
||||
debug("ICH SPI: Saved %d bytes\n", bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We process a 'middle' spi_xfer() call, which has no
|
||||
* SPI_XFER_BEGIN/END, as an independent transaction as if it had
|
||||
* an end. We therefore repeat the command. This is because ICH
|
||||
* seems to have no support for this, or because interest (in digging
|
||||
* out the details and creating a special case in the code) is low.
|
||||
*/
|
||||
if (trans->cmd_len) {
|
||||
trans->out = trans->cmd;
|
||||
trans->bytesout = trans->cmd_len;
|
||||
using_cmd = 1;
|
||||
debug("ICH SPI: Using %d bytes\n", trans->cmd_len);
|
||||
} else {
|
||||
trans->out = dout;
|
||||
trans->bytesout = dout ? bytes : 0;
|
||||
}
|
||||
|
||||
trans->in = din;
|
||||
trans->bytesin = din ? bytes : 0;
|
||||
|
||||
/* There has to always at least be an opcode. */
|
||||
if (!trans->bytesout) {
|
||||
debug("ICH SPI: No opcode for transfer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ich_status_poll(SPIS_SCIP, 0) == -1)
|
||||
return -1;
|
||||
|
||||
ich_writew(SPIS_CDS | SPIS_FCERR, ctlr.status);
|
||||
|
||||
spi_setup_type(trans, using_cmd ? bytes : 0);
|
||||
opcode_index = spi_setup_opcode(trans);
|
||||
if (opcode_index < 0)
|
||||
return -1;
|
||||
with_address = spi_setup_offset(trans);
|
||||
if (with_address < 0)
|
||||
return -1;
|
||||
|
||||
if (trans->opcode == SPI_OPCODE_WREN) {
|
||||
/*
|
||||
* Treat Write Enable as Atomic Pre-Op if possible
|
||||
* in order to prevent the Management Engine from
|
||||
* issuing a transaction between WREN and DATA.
|
||||
*/
|
||||
if (!ctlr.ichspi_lock)
|
||||
ich_writew(trans->opcode, ctlr.preop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctlr.speed && ctlr.max_speed >= 33000000) {
|
||||
int byte;
|
||||
|
||||
byte = ich_readb(ctlr.speed);
|
||||
if (ich->speed >= 33000000)
|
||||
byte |= SSFC_SCF_33MHZ;
|
||||
else
|
||||
byte &= ~SSFC_SCF_33MHZ;
|
||||
ich_writeb(byte, ctlr.speed);
|
||||
}
|
||||
|
||||
/* See if we have used up the command data */
|
||||
if (using_cmd && dout && bytes) {
|
||||
trans->out = dout;
|
||||
trans->bytesout = bytes;
|
||||
debug("ICH SPI: Moving to data, %d bytes\n", bytes);
|
||||
}
|
||||
|
||||
/* Preset control fields */
|
||||
control = ich_readw(ctlr.control);
|
||||
control &= ~SSFC_RESERVED;
|
||||
control = SPIC_SCGO | ((opcode_index & 0x07) << 4);
|
||||
|
||||
/* Issue atomic preop cycle if needed */
|
||||
if (ich_readw(ctlr.preop))
|
||||
control |= SPIC_ACS;
|
||||
|
||||
if (!trans->bytesout && !trans->bytesin) {
|
||||
/* SPI addresses are 24 bit only */
|
||||
if (with_address)
|
||||
ich_writel(trans->offset & 0x00FFFFFF, ctlr.addr);
|
||||
|
||||
/*
|
||||
* This is a 'no data' command (like Write Enable), its
|
||||
* bitesout size was 1, decremented to zero while executing
|
||||
* spi_setup_opcode() above. Tell the chip to send the
|
||||
* command.
|
||||
*/
|
||||
ich_writew(control, ctlr.control);
|
||||
|
||||
/* wait for the result */
|
||||
status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1);
|
||||
if (status == -1)
|
||||
return -1;
|
||||
|
||||
if (status & SPIS_FCERR) {
|
||||
debug("ICH SPI: Command transaction error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this is a write command atempting to transfer more bytes
|
||||
* than the controller can handle. Iterations for writes are not
|
||||
* supported here because each SPI write command needs to be preceded
|
||||
* and followed by other SPI commands, and this sequence is controlled
|
||||
* by the SPI chip driver.
|
||||
*/
|
||||
if (trans->bytesout > ctlr.databytes) {
|
||||
debug("ICH SPI: Too much to write. This should be prevented by the driver's max_write_size?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read or write up to databytes bytes at a time until everything has
|
||||
* been sent.
|
||||
*/
|
||||
while (trans->bytesout || trans->bytesin) {
|
||||
uint32_t data_length;
|
||||
uint32_t aligned_offset;
|
||||
uint32_t diff;
|
||||
|
||||
aligned_offset = trans->offset & ~(ctlr.databytes - 1);
|
||||
diff = trans->offset - aligned_offset;
|
||||
|
||||
/* SPI addresses are 24 bit only */
|
||||
ich_writel(aligned_offset & 0x00FFFFFF, ctlr.addr);
|
||||
|
||||
if (trans->bytesout)
|
||||
data_length = min(trans->bytesout, ctlr.databytes);
|
||||
else
|
||||
data_length = min(trans->bytesin, ctlr.databytes);
|
||||
|
||||
/* Program data into FDATA0 to N */
|
||||
if (trans->bytesout) {
|
||||
write_reg(trans->out, ctlr.data, data_length);
|
||||
spi_use_out(trans, data_length);
|
||||
if (with_address)
|
||||
trans->offset += data_length;
|
||||
}
|
||||
|
||||
/* Add proper control fields' values */
|
||||
control &= ~((ctlr.databytes - 1) << 8);
|
||||
control |= SPIC_DS;
|
||||
control |= (data_length - 1) << 8;
|
||||
|
||||
/* write it */
|
||||
ich_writew(control, ctlr.control);
|
||||
|
||||
/* Wait for Cycle Done Status or Flash Cycle Error. */
|
||||
status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1);
|
||||
if (status == -1)
|
||||
return -1;
|
||||
|
||||
if (status & SPIS_FCERR) {
|
||||
debug("ICH SPI: Data transaction error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (trans->bytesin) {
|
||||
if (diff) {
|
||||
data_length -= diff;
|
||||
read_reg(ctlr.data, buff, ctlr.databytes);
|
||||
memcpy(trans->in, buff + diff, data_length);
|
||||
} else {
|
||||
read_reg(ctlr.data, trans->in, data_length);
|
||||
}
|
||||
spi_use_in(trans, data_length);
|
||||
if (with_address)
|
||||
trans->offset += data_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear atomic preop now that xfer is done */
|
||||
ich_writew(0, ctlr.preop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This uses the SPI controller from the Intel Cougar Point and Panther Point
|
||||
* PCH to write-protect portions of the SPI flash until reboot. The changes
|
||||
* don't actually take effect until the HSFS[FLOCKDN] bit is set, but that's
|
||||
* done elsewhere.
|
||||
*/
|
||||
int spi_write_protect_region(uint32_t lower_limit, uint32_t length, int hint)
|
||||
{
|
||||
uint32_t tmplong;
|
||||
uint32_t upper_limit;
|
||||
|
||||
if (!ctlr.pr) {
|
||||
printf("%s: operation not supported on this chipset\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length == 0 ||
|
||||
lower_limit > (0xFFFFFFFFUL - length) + 1 ||
|
||||
hint < 0 || hint > 4) {
|
||||
printf("%s(0x%x, 0x%x, %d): invalid args\n", __func__,
|
||||
lower_limit, length, hint);
|
||||
return -1;
|
||||
}
|
||||
|
||||
upper_limit = lower_limit + length - 1;
|
||||
|
||||
/*
|
||||
* Determine bits to write, as follows:
|
||||
* 31 Write-protection enable (includes erase operation)
|
||||
* 30:29 reserved
|
||||
* 28:16 Upper Limit (FLA address bits 24:12, with 11:0 == 0xfff)
|
||||
* 15 Read-protection enable
|
||||
* 14:13 reserved
|
||||
* 12:0 Lower Limit (FLA address bits 24:12, with 11:0 == 0x000)
|
||||
*/
|
||||
tmplong = 0x80000000 |
|
||||
((upper_limit & 0x01fff000) << 4) |
|
||||
((lower_limit & 0x01fff000) >> 12);
|
||||
|
||||
printf("%s: writing 0x%08x to %p\n", __func__, tmplong,
|
||||
&ctlr.pr[hint]);
|
||||
ctlr.pr[hint] = tmplong;
|
||||
|
||||
return 0;
|
||||
}
|
143
drivers/spi/ich.h
Normal file
143
drivers/spi/ich.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
*
|
||||
* See file CREDITS for list of people who contributed to this
|
||||
* project.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
* This file is derived from the flashrom project.
|
||||
*/
|
||||
|
||||
struct ich7_spi_regs {
|
||||
uint16_t spis;
|
||||
uint16_t spic;
|
||||
uint32_t spia;
|
||||
uint64_t spid[8];
|
||||
uint64_t _pad;
|
||||
uint32_t bbar;
|
||||
uint16_t preop;
|
||||
uint16_t optype;
|
||||
uint8_t opmenu[8];
|
||||
} __packed;
|
||||
|
||||
struct ich9_spi_regs {
|
||||
uint32_t bfpr; /* 0x00 */
|
||||
uint16_t hsfs;
|
||||
uint16_t hsfc;
|
||||
uint32_t faddr;
|
||||
uint32_t _reserved0;
|
||||
uint32_t fdata[16]; /* 0x10 */
|
||||
uint32_t frap; /* 0x50 */
|
||||
uint32_t freg[5];
|
||||
uint32_t _reserved1[3];
|
||||
uint32_t pr[5]; /* 0x74 */
|
||||
uint32_t _reserved2[2];
|
||||
uint8_t ssfs; /* 0x90 */
|
||||
uint8_t ssfc[3];
|
||||
uint16_t preop; /* 0x94 */
|
||||
uint16_t optype;
|
||||
uint8_t opmenu[8]; /* 0x98 */
|
||||
uint32_t bbar;
|
||||
uint8_t _reserved3[12];
|
||||
uint32_t fdoc;
|
||||
uint32_t fdod;
|
||||
uint8_t _reserved4[8];
|
||||
uint32_t afc;
|
||||
uint32_t lvscc;
|
||||
uint32_t uvscc;
|
||||
uint8_t _reserved5[4];
|
||||
uint32_t fpb;
|
||||
uint8_t _reserved6[28];
|
||||
uint32_t srdl;
|
||||
uint32_t srdc;
|
||||
uint32_t srd;
|
||||
} __packed;
|
||||
|
||||
enum {
|
||||
SPIS_SCIP = 0x0001,
|
||||
SPIS_GRANT = 0x0002,
|
||||
SPIS_CDS = 0x0004,
|
||||
SPIS_FCERR = 0x0008,
|
||||
SSFS_AEL = 0x0010,
|
||||
SPIS_LOCK = 0x8000,
|
||||
SPIS_RESERVED_MASK = 0x7ff0,
|
||||
SSFS_RESERVED_MASK = 0x7fe2
|
||||
};
|
||||
|
||||
enum {
|
||||
SPIC_SCGO = 0x000002,
|
||||
SPIC_ACS = 0x000004,
|
||||
SPIC_SPOP = 0x000008,
|
||||
SPIC_DBC = 0x003f00,
|
||||
SPIC_DS = 0x004000,
|
||||
SPIC_SME = 0x008000,
|
||||
SSFC_SCF_MASK = 0x070000,
|
||||
SSFC_RESERVED = 0xf80000,
|
||||
|
||||
/* Mask for speed byte, biuts 23:16 of SSFC */
|
||||
SSFC_SCF_33MHZ = 0x01,
|
||||
};
|
||||
|
||||
enum {
|
||||
HSFS_FDONE = 0x0001,
|
||||
HSFS_FCERR = 0x0002,
|
||||
HSFS_AEL = 0x0004,
|
||||
HSFS_BERASE_MASK = 0x0018,
|
||||
HSFS_BERASE_SHIFT = 3,
|
||||
HSFS_SCIP = 0x0020,
|
||||
HSFS_FDOPSS = 0x2000,
|
||||
HSFS_FDV = 0x4000,
|
||||
HSFS_FLOCKDN = 0x8000
|
||||
};
|
||||
|
||||
enum {
|
||||
HSFC_FGO = 0x0001,
|
||||
HSFC_FCYCLE_MASK = 0x0006,
|
||||
HSFC_FCYCLE_SHIFT = 1,
|
||||
HSFC_FDBC_MASK = 0x3f00,
|
||||
HSFC_FDBC_SHIFT = 8,
|
||||
HSFC_FSMIE = 0x8000
|
||||
};
|
||||
|
||||
enum {
|
||||
SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0,
|
||||
SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1,
|
||||
SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2,
|
||||
SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
ICH_MAX_CMD_LEN = 5,
|
||||
};
|
||||
|
||||
struct spi_trans {
|
||||
uint8_t cmd[ICH_MAX_CMD_LEN];
|
||||
int cmd_len;
|
||||
const uint8_t *out;
|
||||
uint32_t bytesout;
|
||||
uint8_t *in;
|
||||
uint32_t bytesin;
|
||||
uint8_t type;
|
||||
uint8_t opcode;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct ich_spi_slave {
|
||||
struct spi_slave slave;
|
||||
struct spi_trans trans; /* current transaction in progress */
|
||||
int speed; /* SPI speed in Hz */
|
||||
};
|
@ -49,13 +49,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
slave = malloc(sizeof(struct spi_slave));
|
||||
slave = spi_alloc_slave_base(bus, cs);
|
||||
if (!slave)
|
||||
return NULL;
|
||||
|
||||
slave->bus = bus;
|
||||
slave->cs = cs;
|
||||
|
||||
writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl);
|
||||
|
||||
/* calculate spi clock prescaller using max_hz */
|
||||
|
@ -48,13 +48,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
{
|
||||
struct spi_slave *slave;
|
||||
|
||||
slave = malloc(sizeof(struct spi_slave));
|
||||
slave = spi_alloc_slave_base(bus, cs);
|
||||
if (!slave)
|
||||
return NULL;
|
||||
|
||||
slave->bus = bus;
|
||||
slave->cs = cs;
|
||||
|
||||
return slave;
|
||||
}
|
||||
|
||||
|
@ -45,13 +45,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
slave = malloc(sizeof(struct spi_slave));
|
||||
slave = spi_alloc_slave_base(bus, cs);
|
||||
if (!slave)
|
||||
return NULL;
|
||||
|
||||
slave->bus = bus;
|
||||
slave->cs = cs;
|
||||
|
||||
/*
|
||||
* TODO: Some of the code in spi_init() should probably move
|
||||
* here, or into spi_claim_bus() below.
|
||||
|
@ -408,7 +408,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (bus >= ARRAY_SIZE(spi_bases))
|
||||
return NULL;
|
||||
|
||||
mxcs = calloc(sizeof(struct mxc_spi_slave), 1);
|
||||
mxcs = spi_alloc_slave(struct mxc_spi_slave, bus, cs);
|
||||
if (!mxcs) {
|
||||
puts("mxc_spi: SPI Slave not allocated !\n");
|
||||
return NULL;
|
||||
@ -424,8 +424,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
|
||||
cs = ret;
|
||||
|
||||
mxcs->slave.bus = bus;
|
||||
mxcs->slave.cs = cs;
|
||||
mxcs->base = spi_bases[bus];
|
||||
|
||||
ret = spi_cfg_mxc(mxcs, cs, max_hz, mode);
|
||||
|
@ -77,15 +77,13 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1);
|
||||
mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
|
||||
if (!mxs_slave)
|
||||
return NULL;
|
||||
|
||||
if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
|
||||
goto err_init;
|
||||
|
||||
mxs_slave->slave.bus = bus;
|
||||
mxs_slave->slave.cs = cs;
|
||||
mxs_slave->max_khz = max_hz / 1000;
|
||||
mxs_slave->mode = mode;
|
||||
mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
|
||||
|
@ -90,13 +90,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi"))
|
||||
return NULL;
|
||||
|
||||
tiny_spi = malloc(sizeof(*tiny_spi));
|
||||
tiny_spi = spi_alloc_slave(struct tiny_spi_slave, bus, cs);
|
||||
if (!tiny_spi)
|
||||
return NULL;
|
||||
memset(tiny_spi, 0, sizeof(*tiny_spi));
|
||||
|
||||
tiny_spi->slave.bus = bus;
|
||||
tiny_spi->slave.cs = cs;
|
||||
tiny_spi->host = &tiny_spi_host_list[bus];
|
||||
tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA);
|
||||
tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0;
|
||||
|
@ -80,12 +80,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
struct omap3_spi_slave *ds;
|
||||
|
||||
ds = malloc(sizeof(struct omap3_spi_slave));
|
||||
if (!ds) {
|
||||
printf("SPI error: malloc of SPI structure failed\n");
|
||||
return NULL;
|
||||
}
|
||||
struct mcspi *regs;
|
||||
|
||||
/*
|
||||
* OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
|
||||
@ -98,21 +93,21 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
|
||||
switch (bus) {
|
||||
case 0:
|
||||
ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
|
||||
regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
|
||||
break;
|
||||
#ifdef OMAP3_MCSPI2_BASE
|
||||
case 1:
|
||||
ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
|
||||
regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
|
||||
break;
|
||||
#endif
|
||||
#ifdef OMAP3_MCSPI3_BASE
|
||||
case 2:
|
||||
ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
|
||||
regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
|
||||
break;
|
||||
#endif
|
||||
#ifdef OMAP3_MCSPI4_BASE
|
||||
case 3:
|
||||
ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
|
||||
regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -120,7 +115,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
Supported busses 0 - 3\n", bus);
|
||||
return NULL;
|
||||
}
|
||||
ds->slave.bus = bus;
|
||||
|
||||
if (((bus == 0) && (cs > 3)) ||
|
||||
((bus == 1) && (cs > 1)) ||
|
||||
@ -130,19 +124,26 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
on bus %i\n", cs, bus);
|
||||
return NULL;
|
||||
}
|
||||
ds->slave.cs = cs;
|
||||
|
||||
if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
|
||||
printf("SPI error: unsupported frequency %i Hz. \
|
||||
Max frequency is 48 Mhz\n", max_hz);
|
||||
return NULL;
|
||||
}
|
||||
ds->freq = max_hz;
|
||||
|
||||
if (mode > SPI_MODE_3) {
|
||||
printf("SPI error: unsupported SPI mode %i\n", mode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ds = spi_alloc_slave(struct omap3_spi_slave, bus, cs);
|
||||
if (!ds) {
|
||||
printf("SPI error: malloc of SPI structure failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ds->regs = regs;
|
||||
ds->freq = max_hz;
|
||||
ds->mode = mode;
|
||||
|
||||
return &ds->slave;
|
||||
|
@ -103,12 +103,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
ss = malloc(sizeof(struct spi_slave));
|
||||
ss = spi_alloc_slave(struct sh_spi, bus, cs);
|
||||
if (!ss)
|
||||
return NULL;
|
||||
|
||||
ss->slave.bus = bus;
|
||||
ss->slave.cs = cs;
|
||||
ss->regs = (struct sh_spi_regs *)CONFIG_SH_SPI_BASE;
|
||||
|
||||
/* SPI sycle stop */
|
||||
|
@ -73,12 +73,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
ss = malloc(sizeof(struct soft_spi_slave));
|
||||
ss = spi_alloc_slave(struct soft_spi_slave, bus, cs);
|
||||
if (!ss)
|
||||
return NULL;
|
||||
|
||||
ss->slave.bus = bus;
|
||||
ss->slave.cs = cs;
|
||||
ss->mode = mode;
|
||||
|
||||
/* TODO: Use max_hz to limit the SCK rate */
|
||||
|
39
drivers/spi/spi.c
Normal file
39
drivers/spi/spi.c
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
|
||||
void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
|
||||
unsigned int cs)
|
||||
{
|
||||
struct spi_slave *slave;
|
||||
void *ptr;
|
||||
|
||||
ptr = malloc(size);
|
||||
if (ptr) {
|
||||
memset(ptr, '\0', size);
|
||||
slave = (struct spi_slave *)(ptr + offset);
|
||||
slave->bus = bus;
|
||||
slave->cs = cs;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
@ -81,13 +81,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spi = malloc(sizeof(struct tegra_spi_slave));
|
||||
spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs);
|
||||
if (!spi) {
|
||||
printf("SPI error: malloc of SPI structure failed\n");
|
||||
return NULL;
|
||||
}
|
||||
spi->slave.bus = bus;
|
||||
spi->slave.cs = cs;
|
||||
spi->ctrl = &spi_ctrls[bus];
|
||||
if (!spi->ctrl) {
|
||||
printf("SPI error: could not find controller for bus %d\n",
|
||||
|
@ -81,13 +81,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spi = malloc(sizeof(struct tegra_spi_slave));
|
||||
spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs);
|
||||
if (!spi) {
|
||||
printf("SPI error: malloc of SPI structure failed\n");
|
||||
return NULL;
|
||||
}
|
||||
spi->slave.bus = bus;
|
||||
spi->slave.cs = cs;
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
int node = fdtdec_next_compatible(gd->fdt_blob, 0,
|
||||
COMPAT_NVIDIA_TEGRA20_SFLASH);
|
||||
|
@ -85,14 +85,12 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xilspi = malloc(sizeof(*xilspi));
|
||||
xilspi = spi_alloc_slave(struct xilinx_spi_slave, bus, cs);
|
||||
if (!xilspi) {
|
||||
printf("XILSPI error: %s: malloc of SPI structure failed\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
xilspi->slave.bus = bus;
|
||||
xilspi->slave.cs = cs;
|
||||
xilspi->regs = (struct xilinx_spi_reg *)xilinx_spi_base_list[bus];
|
||||
xilspi->freq = max_hz;
|
||||
xilspi->mode = mode;
|
||||
|
@ -179,6 +179,8 @@
|
||||
#define CONFIG_CMD_SAVEENV
|
||||
#define CONFIG_CMD_SETGETDCR
|
||||
#define CONFIG_CMD_SOURCE
|
||||
#define CONFIG_CMD_TIME
|
||||
#define CONFIG_CMD_GETTIME
|
||||
#define CONFIG_CMD_XIMG
|
||||
#define CONFIG_CMD_SCSI
|
||||
|
||||
@ -257,10 +259,16 @@
|
||||
/*-----------------------------------------------------------------------
|
||||
* FLASH configuration
|
||||
*/
|
||||
#define CONFIG_ICH_SPI
|
||||
#define CONFIG_SPI_FLASH
|
||||
#define CONFIG_SPI_FLASH_MACRONIX
|
||||
#define CONFIG_SPI_FLASH_WINBOND
|
||||
#define CONFIG_SPI_FLASH_GIGADEVICE
|
||||
#define CONFIG_SYS_NO_FLASH
|
||||
#undef CONFIG_FLASH_CFI_DRIVER
|
||||
#define CONFIG_SYS_MAX_FLASH_SECT 1
|
||||
#define CONFIG_SYS_MAX_FLASH_BANKS 1
|
||||
#define CONFIG_CMD_SF
|
||||
#define CONFIG_CMD_SF_TEST
|
||||
#define CONFIG_CMD_SPI
|
||||
#define CONFIG_SPI
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Environment configuration
|
||||
|
@ -38,11 +38,13 @@
|
||||
*/
|
||||
#ifdef CONFIG_PHYS_64BIT
|
||||
typedef u64 fdt_addr_t;
|
||||
typedef u64 fdt_size_t;
|
||||
#define FDT_ADDR_T_NONE (-1ULL)
|
||||
#define fdt_addr_to_cpu(reg) be64_to_cpu(reg)
|
||||
#define fdt_size_to_cpu(reg) be64_to_cpu(reg)
|
||||
#else
|
||||
typedef u32 fdt_addr_t;
|
||||
typedef u32 fdt_size_t;
|
||||
#define FDT_ADDR_T_NONE (-1U)
|
||||
#define fdt_addr_to_cpu(reg) be32_to_cpu(reg)
|
||||
#define fdt_size_to_cpu(reg) be32_to_cpu(reg)
|
||||
@ -84,6 +86,7 @@ enum fdt_compat_id {
|
||||
COMPAT_SAMSUNG_EXYNOS_EHCI, /* Exynos EHCI controller */
|
||||
COMPAT_SAMSUNG_EXYNOS_USB_PHY, /* Exynos phy controller for usb2.0 */
|
||||
COMPAT_MAXIM_MAX77686_PMIC, /* MAX77686 PMIC */
|
||||
COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */
|
||||
|
||||
COMPAT_COUNT,
|
||||
};
|
||||
@ -199,6 +202,19 @@ int fdtdec_next_compatible_subnode(const void *blob, int node,
|
||||
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
||||
const char *prop_name);
|
||||
|
||||
/**
|
||||
* Look up an address property in a node and return it as an address.
|
||||
* The property must hold one address with a length. This is only tested
|
||||
* on 32-bit machines.
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param node node to examine
|
||||
* @param prop_name name of property to find
|
||||
* @return address, if found, or FDT_ADDR_T_NONE if not
|
||||
*/
|
||||
fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
|
||||
const char *prop_name, fdt_size_t *sizep);
|
||||
|
||||
/**
|
||||
* Look up a 32-bit integer property in a node and return it. The property
|
||||
* must have at least 4 bytes of data. The value of the first cell is
|
||||
|
@ -49,10 +49,13 @@
|
||||
*
|
||||
* bus: ID of the bus that the slave is attached to.
|
||||
* cs: ID of the chip select connected to the slave.
|
||||
* max_write_size: If non-zero, the maximum number of bytes which can
|
||||
* be written at once, excluding command bytes.
|
||||
*/
|
||||
struct spi_slave {
|
||||
unsigned int bus;
|
||||
unsigned int cs;
|
||||
unsigned int max_write_size;
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
@ -62,6 +65,47 @@ struct spi_slave {
|
||||
*/
|
||||
void spi_init(void);
|
||||
|
||||
/**
|
||||
* spi_do_alloc_slave - Allocate a new SPI slave (internal)
|
||||
*
|
||||
* Allocate and zero all fields in the spi slave, and set the bus/chip
|
||||
* select. Use the helper macro spi_alloc_slave() to call this.
|
||||
*
|
||||
* @offset: Offset of struct spi_slave within slave structure
|
||||
* @size: Size of slave structure
|
||||
* @bus: Bus ID of the slave chip.
|
||||
* @cs: Chip select ID of the slave chip on the specified bus.
|
||||
*/
|
||||
void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
|
||||
unsigned int cs);
|
||||
|
||||
/**
|
||||
* spi_alloc_slave - Allocate a new SPI slave
|
||||
*
|
||||
* Allocate and zero all fields in the spi slave, and set the bus/chip
|
||||
* select.
|
||||
*
|
||||
* @_struct: Name of structure to allocate (e.g. struct tegra_spi). This
|
||||
* structure must contain a member 'struct spi_slave *slave'.
|
||||
* @bus: Bus ID of the slave chip.
|
||||
* @cs: Chip select ID of the slave chip on the specified bus.
|
||||
*/
|
||||
#define spi_alloc_slave(_struct, bus, cs) \
|
||||
spi_do_alloc_slave(offsetof(_struct, slave), \
|
||||
sizeof(_struct), bus, cs)
|
||||
|
||||
/**
|
||||
* spi_alloc_slave_base - Allocate a new SPI slave with no private data
|
||||
*
|
||||
* Allocate and zero all fields in the spi slave, and set the bus/chip
|
||||
* select.
|
||||
*
|
||||
* @bus: Bus ID of the slave chip.
|
||||
* @cs: Chip select ID of the slave chip on the specified bus.
|
||||
*/
|
||||
#define spi_alloc_slave_base(bus, cs) \
|
||||
spi_do_alloc_slave(0, sizeof(struct spi_slave), bus, cs)
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Set up communications parameters for a SPI slave.
|
||||
*
|
||||
|
@ -39,6 +39,7 @@ struct spi_flash {
|
||||
/* Erase (sector) size */
|
||||
u32 sector_size;
|
||||
|
||||
void *memory_map; /* Address of read-only SPI flash access */
|
||||
int (*read)(struct spi_flash *flash, u32 offset,
|
||||
size_t len, void *buf);
|
||||
int (*write)(struct spi_flash *flash, u32 offset,
|
||||
@ -47,6 +48,44 @@ struct spi_flash {
|
||||
size_t len);
|
||||
};
|
||||
|
||||
/**
|
||||
* spi_flash_do_alloc - Allocate a new spi flash structure
|
||||
*
|
||||
* The structure is allocated and cleared with default values for
|
||||
* read, write and erase, which the caller can modify. The caller must set
|
||||
* up size, page_size and sector_size.
|
||||
*
|
||||
* Use the helper macro spi_flash_alloc() to call this.
|
||||
*
|
||||
* @offset: Offset of struct spi_slave within slave structure
|
||||
* @size: Size of slave structure
|
||||
* @spi: SPI slave
|
||||
* @name: Name of SPI flash device
|
||||
*/
|
||||
void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* spi_flash_alloc - Allocate a new SPI flash structure
|
||||
*
|
||||
* @_struct: Name of structure to allocate (e.g. struct ramtron_spi_fram). This
|
||||
* structure must contain a member 'struct spi_flash *flash'.
|
||||
* @spi: SPI slave
|
||||
* @name: Name of SPI flash device
|
||||
*/
|
||||
#define spi_flash_alloc(_struct, spi, name) \
|
||||
spi_flash_do_alloc(offsetof(_struct, flash), sizeof(_struct), \
|
||||
spi, name)
|
||||
|
||||
/**
|
||||
* spi_flash_alloc_base - Allocate a new SPI flash structure with no private data
|
||||
*
|
||||
* @spi: SPI slave
|
||||
* @name: Name of SPI flash device
|
||||
*/
|
||||
#define spi_flash_alloc_base(spi, name) \
|
||||
spi_flash_do_alloc(0, sizeof(struct spi_flash), spi, name)
|
||||
|
||||
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int spi_mode);
|
||||
void spi_flash_free(struct spi_flash *flash);
|
||||
|
26
lib/fdtdec.c
26
lib/fdtdec.c
@ -59,6 +59,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
|
||||
COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"),
|
||||
COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"),
|
||||
COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
|
||||
COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
|
||||
};
|
||||
|
||||
const char *fdtdec_get_compatible(enum fdt_compat_id id)
|
||||
@ -68,25 +69,40 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)
|
||||
return compat_names[id];
|
||||
}
|
||||
|
||||
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
||||
const char *prop_name)
|
||||
fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
|
||||
const char *prop_name, fdt_size_t *sizep)
|
||||
{
|
||||
const fdt_addr_t *cell;
|
||||
int len;
|
||||
|
||||
debug("%s: %s: ", __func__, prop_name);
|
||||
cell = fdt_getprop(blob, node, prop_name, &len);
|
||||
if (cell && (len == sizeof(fdt_addr_t) ||
|
||||
len == sizeof(fdt_addr_t) * 2)) {
|
||||
if (cell && ((!sizep && len == sizeof(fdt_addr_t)) ||
|
||||
len == sizeof(fdt_addr_t) * 2)) {
|
||||
fdt_addr_t addr = fdt_addr_to_cpu(*cell);
|
||||
if (sizep) {
|
||||
const fdt_size_t *size;
|
||||
|
||||
debug("%p\n", (void *)addr);
|
||||
size = (fdt_size_t *)((char *)cell +
|
||||
sizeof(fdt_addr_t));
|
||||
*sizep = fdt_size_to_cpu(*size);
|
||||
debug("addr=%p, size=%p\n", (void *)addr,
|
||||
(void *)*sizep);
|
||||
} else {
|
||||
debug("%p\n", (void *)addr);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
debug("(not found)\n");
|
||||
return FDT_ADDR_T_NONE;
|
||||
}
|
||||
|
||||
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
||||
const char *prop_name)
|
||||
{
|
||||
return fdtdec_get_addr_size(blob, node, prop_name, NULL);
|
||||
}
|
||||
|
||||
s32 fdtdec_get_int(const void *blob, int node, const char *prop_name,
|
||||
s32 default_val)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user