Merge branch 'master' of git://git.denx.de/u-boot-spi
This commit is contained in:
commit
66ddf42b0e
@ -17,6 +17,8 @@
|
||||
#define ZYNQ_SDHCI_BASEADDR1 0xE0101000
|
||||
#define ZYNQ_I2C_BASEADDR0 0xE0004000
|
||||
#define ZYNQ_I2C_BASEADDR1 0xE0005000
|
||||
#define ZYNQ_SPI_BASEADDR0 0xE0006000
|
||||
#define ZYNQ_SPI_BASEADDR1 0xE0007000
|
||||
|
||||
/* Reflect slcr offsets */
|
||||
struct slcr_regs {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <stdio_dev.h>
|
||||
#include <serial.h>
|
||||
#include <net.h>
|
||||
#include <spi.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/microblaze_intc.h>
|
||||
@ -147,6 +148,10 @@ void board_init_f(ulong not_used)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPI
|
||||
spi_init();
|
||||
#endif
|
||||
|
||||
/* relocate environment function pointers etc. */
|
||||
env_relocate();
|
||||
|
||||
|
@ -151,16 +151,17 @@ static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
|
||||
size_t len, const char *buf, char *cmp_buf, size_t *skipped)
|
||||
{
|
||||
debug("offset=%#x, sector_size=%#x, len=%#zx\n",
|
||||
offset, flash->sector_size, len);
|
||||
offset, flash->sector_size, len);
|
||||
if (spi_flash_read(flash, offset, len, cmp_buf))
|
||||
return "read";
|
||||
if (memcmp(cmp_buf, buf, len) == 0) {
|
||||
debug("Skip region %x size %zx: no change\n",
|
||||
offset, len);
|
||||
offset, len);
|
||||
*skipped += len;
|
||||
return NULL;
|
||||
}
|
||||
if (spi_flash_erase(flash, offset, len))
|
||||
/* Erase the entire sector */
|
||||
if (spi_flash_erase(flash, offset, flash->sector_size))
|
||||
return "erase";
|
||||
if (spi_flash_write(flash, offset, len, buf))
|
||||
return "write";
|
||||
@ -200,7 +201,7 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
|
||||
todo = min(end - buf, flash->sector_size);
|
||||
if (get_timer(last_update) > 100) {
|
||||
printf(" \rUpdating, %zu%% %lu B/s",
|
||||
100 - (end - buf) / scale,
|
||||
100 - (end - buf) / scale,
|
||||
bytes_per_second(buf - start_buf,
|
||||
start_time));
|
||||
last_update = get_timer(0);
|
||||
@ -220,9 +221,9 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
|
||||
|
||||
delta = get_timer(start_time);
|
||||
printf("%zu bytes written, %zu bytes skipped", len - skipped,
|
||||
skipped);
|
||||
skipped);
|
||||
printf(" in %ld.%lds, speed %ld B/s\n",
|
||||
delta / 1000, delta % 1000, bytes_per_second(len, start_time));
|
||||
delta / 1000, delta % 1000, bytes_per_second(len, start_time));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -252,7 +253,7 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
|
||||
/* Consistency checking */
|
||||
if (offset + len > flash->size) {
|
||||
printf("ERROR: attempting %s past flash size (%#x)\n",
|
||||
argv[0], flash->size);
|
||||
argv[0], flash->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -262,9 +263,9 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[0], "update") == 0)
|
||||
if (strcmp(argv[0], "update") == 0) {
|
||||
ret = spi_flash_update(flash, offset, len, buf);
|
||||
else if (strncmp(argv[0], "read", 4) == 0 ||
|
||||
} else if (strncmp(argv[0], "read", 4) == 0 ||
|
||||
strncmp(argv[0], "write", 5) == 0) {
|
||||
int read;
|
||||
|
||||
@ -275,7 +276,7 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
|
||||
ret = spi_flash_write(flash, offset, len, buf);
|
||||
|
||||
printf("SF: %zu bytes @ %#x %s: %s\n", (size_t)len, (u32)offset,
|
||||
read ? "Read" : "Written", ret ? "ERROR" : "OK");
|
||||
read ? "Read" : "Written", ret ? "ERROR" : "OK");
|
||||
}
|
||||
|
||||
unmap_physmem(buf, len);
|
||||
@ -304,13 +305,13 @@ static int do_spi_flash_erase(int argc, char * const argv[])
|
||||
/* Consistency checking */
|
||||
if (offset + len > flash->size) {
|
||||
printf("ERROR: attempting %s past flash size (%#x)\n",
|
||||
argv[0], flash->size);
|
||||
argv[0], flash->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = spi_flash_erase(flash, offset, len);
|
||||
printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)len, (u32)offset,
|
||||
ret ? "ERROR" : "OK");
|
||||
ret ? "ERROR" : "OK");
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
@ -470,7 +471,8 @@ static int do_spi_flash_test(int argc, char * const argv[])
|
||||
}
|
||||
#endif /* CONFIG_CMD_SF_TEST */
|
||||
|
||||
static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
char * const argv[])
|
||||
{
|
||||
const char *cmd;
|
||||
int ret;
|
||||
@ -526,7 +528,7 @@ U_BOOT_CMD(
|
||||
"SPI flash sub-system",
|
||||
"probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n"
|
||||
" and chip select\n"
|
||||
"sf read addr offset len - read `len' bytes starting at\n"
|
||||
"sf read addr offset len - read `len' bytes starting at\n"
|
||||
" `offset' to memory at `addr'\n"
|
||||
"sf write addr offset len - write `len' bytes from memory\n"
|
||||
" at `addr' to flash at `offset'\n"
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* (C) Copyright 2008 Atmel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <environment.h>
|
||||
|
@ -252,7 +252,7 @@ static int dataflash_write_p2(struct spi_flash *flash,
|
||||
}
|
||||
|
||||
debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n",
|
||||
len, offset);
|
||||
len, offset);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
@ -325,7 +325,7 @@ static int dataflash_write_at45(struct spi_flash *flash,
|
||||
}
|
||||
|
||||
debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n",
|
||||
len, offset);
|
||||
len, offset);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
@ -387,7 +387,7 @@ static int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len)
|
||||
}
|
||||
|
||||
debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n",
|
||||
len, offset);
|
||||
len, offset);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
@ -450,7 +450,7 @@ static int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len)
|
||||
}
|
||||
|
||||
debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n",
|
||||
len, offset);
|
||||
len, offset);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
@ -476,7 +476,7 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
|
||||
|
||||
if (i == ARRAY_SIZE(atmel_spi_flash_table)) {
|
||||
debug("SF: Unsupported DataFlash ID %02x\n",
|
||||
idcode[1]);
|
||||
idcode[1]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,7 @@ struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
|
||||
|
||||
flash->page_size = 256;
|
||||
flash->sector_size = 256 * 16 * 16;
|
||||
flash->size = 256 * 16
|
||||
* params->nr_sectors;
|
||||
flash->size = 256 * 16 * params->nr_sectors;
|
||||
|
||||
return flash;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode)
|
||||
|
||||
if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) {
|
||||
debug("SF: Unsupported Gigadevice ID %02x%02x\n",
|
||||
idcode[1], idcode[2]);
|
||||
idcode[1], idcode[2]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -230,7 +230,8 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
|
||||
/* JEDEC conformant RAMTRON id */
|
||||
for (i = 0; i < ARRAY_SIZE(ramtron_spi_fram_table); i++) {
|
||||
params = &ramtron_spi_fram_table[i];
|
||||
if (idcode[1] == params->id1 && idcode[2] == params->id2)
|
||||
if (idcode[1] == params->id1 &&
|
||||
idcode[2] == params->id2)
|
||||
goto found;
|
||||
}
|
||||
break;
|
||||
@ -251,7 +252,8 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
|
||||
/* now find the device */
|
||||
for (i = 0; i < ARRAY_SIZE(ramtron_spi_fram_table); i++) {
|
||||
params = &ramtron_spi_fram_table[i];
|
||||
if (!strcmp(params->name, CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC))
|
||||
if (!strcmp(params->name,
|
||||
CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC))
|
||||
goto found;
|
||||
}
|
||||
debug("SF: Unsupported non-JEDEC RAMTRON device "
|
||||
@ -264,7 +266,7 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
|
||||
|
||||
/* arriving here means no method has found a device we can handle */
|
||||
debug("SF/ramtron: unsupported device id0=%02x id1=%02x id2=%02x\n",
|
||||
idcode[0], idcode[1], idcode[2]);
|
||||
idcode[0], idcode[1], idcode[2]);
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
|
@ -6,7 +6,7 @@
|
||||
* TsiChung Liew (Tsi-Chung.Liew@freescale.com),
|
||||
* and Jason McMullan (mcmullan@netapp.com)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
@ -122,7 +122,8 @@ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
|
||||
debug("SF: Unsupported SPANSION ID %04x %04x\n", jedec, ext_jedec);
|
||||
debug("SF: Unsupported SPANSION ID %04x %04x\n",
|
||||
jedec, ext_jedec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -40,12 +40,13 @@ static int spi_flash_read_write(struct spi_slave *spi,
|
||||
ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
|
||||
if (ret) {
|
||||
debug("SF: Failed to send command (%zu bytes): %d\n",
|
||||
cmd_len, ret);
|
||||
cmd_len, ret);
|
||||
} else if (data_len != 0) {
|
||||
ret = spi_xfer(spi, data_len * 8, data_out, data_in, SPI_XFER_END);
|
||||
ret = spi_xfer(spi, data_len * 8, data_out, data_in,
|
||||
SPI_XFER_END);
|
||||
if (ret)
|
||||
debug("SF: Failed to transfer %zu bytes of data: %d\n",
|
||||
data_len, ret);
|
||||
data_len, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -86,7 +87,7 @@ int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
|
||||
ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
|
||||
if (ret) {
|
||||
debug("SF: fail to read %s status register\n",
|
||||
cmd == CMD_READ_STATUS ? "read" : "flag");
|
||||
cmd == CMD_READ_STATUS ? "read" : "flag");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -144,7 +145,7 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
|
||||
ret = spi_flash_cmd_wait_ready(flash, timeout);
|
||||
if (ret < 0) {
|
||||
debug("SF: write %s timed out\n",
|
||||
timeout == SPI_FLASH_PROG_TIMEOUT ?
|
||||
timeout == SPI_FLASH_PROG_TIMEOUT ?
|
||||
"program" : "page erase");
|
||||
return ret;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ void spl_spi_load_image(void)
|
||||
|
||||
/* Load u-boot, mkimage header is 64 bytes. */
|
||||
spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40,
|
||||
(void *) header);
|
||||
(void *)header);
|
||||
spl_parse_image_header(header);
|
||||
spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS,
|
||||
spl_image.size, (void *)spl_image.load_addr);
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "spi_flash_internal.h"
|
||||
|
||||
#define CMD_SST_BP 0x02 /* Byte Program */
|
||||
#define CMD_SST_AAI_WP 0xAD /* Auto Address Increment Word Program */
|
||||
#define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */
|
||||
|
||||
#define SST_SR_WIP (1 << 0) /* Write-in-Progress */
|
||||
#define SST_SR_WEL (1 << 1) /* Write enable */
|
||||
@ -50,47 +50,61 @@ static const struct sst_spi_flash_params sst_spi_flash_table[] = {
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 128,
|
||||
.name = "SST25VF040B",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x8e,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 256,
|
||||
.name = "SST25VF080B",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x41,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 512,
|
||||
.name = "SST25VF016B",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x4a,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 1024,
|
||||
.name = "SST25VF032B",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x4b,
|
||||
.flags = SST_FEAT_MBP,
|
||||
.nr_sectors = 2048,
|
||||
.name = "SST25VF064C",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x01,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 16,
|
||||
.name = "SST25WF512",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x02,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 32,
|
||||
.name = "SST25WF010",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x03,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 64,
|
||||
.name = "SST25WF020",
|
||||
},{
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x04,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 128,
|
||||
.name = "SST25WF040",
|
||||
},
|
||||
{
|
||||
.idcode1 = 0x05,
|
||||
.flags = SST_FEAT_WP,
|
||||
.nr_sectors = 256,
|
||||
.name = "SST25WF080",
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
@ -105,7 +119,7 @@ sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
|
||||
};
|
||||
|
||||
debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
|
||||
spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset);
|
||||
spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset);
|
||||
|
||||
ret = spi_flash_cmd_write_enable(flash);
|
||||
if (ret)
|
||||
@ -152,11 +166,11 @@ sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
|
||||
|
||||
for (; actual < len - 1; actual += 2) {
|
||||
debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
|
||||
spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, cmd[0],
|
||||
offset);
|
||||
spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual,
|
||||
cmd[0], offset);
|
||||
|
||||
ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len,
|
||||
buf + actual, 2);
|
||||
buf + actual, 2);
|
||||
if (ret) {
|
||||
debug("SF: sst word program failed\n");
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
|
||||
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
@ -18,7 +18,7 @@
|
||||
#include "spi_flash_internal.h"
|
||||
|
||||
/* M25Pxx-specific commands */
|
||||
#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
|
||||
#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
|
||||
|
||||
struct stmicro_spi_flash_params {
|
||||
u16 id;
|
||||
@ -150,7 +150,7 @@ static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
|
||||
},
|
||||
};
|
||||
|
||||
struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
|
||||
struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode)
|
||||
{
|
||||
const struct stmicro_spi_flash_params *params;
|
||||
struct spi_flash *flash;
|
||||
@ -166,17 +166,17 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
|
||||
idcode[0] = 0x20;
|
||||
idcode[1] = 0x20;
|
||||
idcode[2] = idcode[3] + 1;
|
||||
} else
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
id = ((idcode[1] << 8) | idcode[2]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
|
||||
params = &stmicro_spi_flash_table[i];
|
||||
if (params->id == id) {
|
||||
if (params->id == id)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
|
||||
|
@ -123,7 +123,7 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
|
||||
|
||||
if (i == ARRAY_SIZE(winbond_spi_flash_table)) {
|
||||
debug("SF: Unsupported Winbond ID %02x%02x\n",
|
||||
idcode[1], idcode[2]);
|
||||
idcode[1], idcode[2]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
|
||||
COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
|
||||
COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
|
||||
COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o
|
||||
COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
|
||||
|
||||
COBJS := $(COBJS-y)
|
||||
SRCS := $(COBJS:.o=.c)
|
||||
|
@ -221,15 +221,13 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out,
|
||||
slave->bus, slave->cs, *(uint *) dout,
|
||||
dout, *(uint *) din, din, len);
|
||||
|
||||
num_chunks = data_len / max_tran_len +
|
||||
(data_len % max_tran_len ? 1 : 0);
|
||||
num_chunks = DIV_ROUND_UP(data_len, max_tran_len);
|
||||
while (num_chunks--) {
|
||||
if (data_in)
|
||||
din = buffer + rx_offset;
|
||||
dout = buffer;
|
||||
tran_len = min(data_len , max_tran_len);
|
||||
num_blks = (tran_len + cmd_len) / 4 +
|
||||
((tran_len + cmd_len) % 4 ? 1 : 0);
|
||||
num_blks = DIV_ROUND_UP(tran_len + cmd_len, 4);
|
||||
num_bytes = (tran_len + cmd_len) % 4;
|
||||
fsl->data_len = tran_len + cmd_len;
|
||||
spi_cs_activate(slave);
|
||||
|
@ -77,7 +77,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
{
|
||||
volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi;
|
||||
unsigned int tmpdout, tmpdin, event;
|
||||
int numBlks = bitlen / 32 + (bitlen % 32 ? 1 : 0);
|
||||
int numBlks = DIV_ROUND_UP(bitlen, 32);
|
||||
int tm, isRead = 0;
|
||||
unsigned char charSize = 32;
|
||||
|
||||
|
280
drivers/spi/zynq_spi.c
Normal file
280
drivers/spi/zynq_spi.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* (C) Copyright 2013 Inc.
|
||||
*
|
||||
* Xilinx Zynq PS SPI controller driver (master mode only)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
/* zynq spi register bit masks ZYNQ_SPI_<REG>_<BIT>_MASK */
|
||||
#define ZYNQ_SPI_CR_MSA_MASK (1 << 15) /* Manual start enb */
|
||||
#define ZYNQ_SPI_CR_MCS_MASK (1 << 14) /* Manual chip select */
|
||||
#define ZYNQ_SPI_CR_CS_MASK (0xF << 10) /* Chip select */
|
||||
#define ZYNQ_SPI_CR_BRD_MASK (0x7 << 3) /* Baud rate div */
|
||||
#define ZYNQ_SPI_CR_CPHA_MASK (1 << 2) /* Clock phase */
|
||||
#define ZYNQ_SPI_CR_CPOL_MASK (1 << 1) /* Clock polarity */
|
||||
#define ZYNQ_SPI_CR_MSTREN_MASK (1 << 0) /* Mode select */
|
||||
#define ZYNQ_SPI_IXR_RXNEMPTY_MASK (1 << 4) /* RX_FIFO_not_empty */
|
||||
#define ZYNQ_SPI_IXR_TXOW_MASK (1 << 2) /* TX_FIFO_not_full */
|
||||
#define ZYNQ_SPI_IXR_ALL_MASK 0x7F /* All IXR bits */
|
||||
#define ZYNQ_SPI_ENR_SPI_EN_MASK (1 << 0) /* SPI Enable */
|
||||
|
||||
#define ZYNQ_SPI_FIFO_DEPTH 128
|
||||
#ifndef CONFIG_SYS_ZYNQ_SPI_WAIT
|
||||
#define CONFIG_SYS_ZYNQ_SPI_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */
|
||||
#endif
|
||||
|
||||
/* zynq spi register set */
|
||||
struct zynq_spi_regs {
|
||||
u32 cr; /* 0x00 */
|
||||
u32 isr; /* 0x04 */
|
||||
u32 ier; /* 0x08 */
|
||||
u32 idr; /* 0x0C */
|
||||
u32 imr; /* 0x10 */
|
||||
u32 enr; /* 0x14 */
|
||||
u32 dr; /* 0x18 */
|
||||
u32 txdr; /* 0x1C */
|
||||
u32 rxdr; /* 0x20 */
|
||||
};
|
||||
|
||||
/* zynq spi slave */
|
||||
struct zynq_spi_slave {
|
||||
struct spi_slave slave;
|
||||
struct zynq_spi_regs *base;
|
||||
u8 mode;
|
||||
u8 fifo_depth;
|
||||
u32 speed_hz;
|
||||
u32 input_hz;
|
||||
u32 req_hz;
|
||||
};
|
||||
|
||||
static inline struct zynq_spi_slave *to_zynq_spi_slave(struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct zynq_spi_slave, slave);
|
||||
}
|
||||
|
||||
static inline struct zynq_spi_regs *get_zynq_spi_base(int dev)
|
||||
{
|
||||
if (dev)
|
||||
return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR1;
|
||||
else
|
||||
return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR0;
|
||||
}
|
||||
|
||||
static void zynq_spi_init_hw(struct zynq_spi_slave *zslave)
|
||||
{
|
||||
u32 confr;
|
||||
|
||||
/* Disable SPI */
|
||||
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr);
|
||||
|
||||
/* Disable Interrupts */
|
||||
writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->idr);
|
||||
|
||||
/* Clear RX FIFO */
|
||||
while (readl(&zslave->base->isr) &
|
||||
ZYNQ_SPI_IXR_RXNEMPTY_MASK)
|
||||
readl(&zslave->base->rxdr);
|
||||
|
||||
/* Clear Interrupts */
|
||||
writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->isr);
|
||||
|
||||
/* Manual slave select and Auto start */
|
||||
confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK |
|
||||
ZYNQ_SPI_CR_MSTREN_MASK;
|
||||
confr &= ~ZYNQ_SPI_CR_MSA_MASK;
|
||||
writel(confr, &zslave->base->cr);
|
||||
|
||||
/* Enable SPI */
|
||||
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr);
|
||||
}
|
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
/* 2 bus with 3 chipselect */
|
||||
return bus < 2 && cs < 3;
|
||||
}
|
||||
|
||||
void spi_cs_activate(struct spi_slave *slave)
|
||||
{
|
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave);
|
||||
u32 cr;
|
||||
|
||||
debug("spi_cs_activate: 0x%08x\n", (u32)slave);
|
||||
|
||||
clrbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK);
|
||||
cr = readl(&zslave->base->cr);
|
||||
/*
|
||||
* CS cal logic: CS[13:10]
|
||||
* xxx0 - cs0
|
||||
* xx01 - cs1
|
||||
* x011 - cs2
|
||||
*/
|
||||
cr |= (~(0x1 << slave->cs) << 10) & ZYNQ_SPI_CR_CS_MASK;
|
||||
writel(cr, &zslave->base->cr);
|
||||
}
|
||||
|
||||
void spi_cs_deactivate(struct spi_slave *slave)
|
||||
{
|
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave);
|
||||
|
||||
debug("spi_cs_deactivate: 0x%08x\n", (u32)slave);
|
||||
|
||||
setbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK);
|
||||
}
|
||||
|
||||
void spi_init()
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
struct zynq_spi_slave *zslave;
|
||||
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
zslave = spi_alloc_slave(struct zynq_spi_slave, bus, cs);
|
||||
if (!zslave) {
|
||||
printf("SPI_error: Fail to allocate zynq_spi_slave\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
zslave->base = get_zynq_spi_base(bus);
|
||||
zslave->mode = mode;
|
||||
zslave->fifo_depth = ZYNQ_SPI_FIFO_DEPTH;
|
||||
zslave->input_hz = 166666700;
|
||||
zslave->speed_hz = zslave->input_hz / 2;
|
||||
zslave->req_hz = max_hz;
|
||||
|
||||
/* init the zynq spi hw */
|
||||
zynq_spi_init_hw(zslave);
|
||||
|
||||
return &zslave->slave;
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave);
|
||||
|
||||
debug("spi_free_slave: 0x%08x\n", (u32)slave);
|
||||
free(zslave);
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave);
|
||||
u32 confr = 0;
|
||||
u8 baud_rate_val = 0;
|
||||
|
||||
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr);
|
||||
|
||||
/* Set the SPI Clock phase and polarities */
|
||||
confr = readl(&zslave->base->cr);
|
||||
confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK);
|
||||
if (zslave->mode & SPI_CPHA)
|
||||
confr |= ZYNQ_SPI_CR_CPHA_MASK;
|
||||
if (zslave->mode & SPI_CPOL)
|
||||
confr |= ZYNQ_SPI_CR_CPOL_MASK;
|
||||
|
||||
/* Set the clock frequency */
|
||||
if (zslave->req_hz == 0) {
|
||||
/* Set baudrate x8, if the req_hz is 0 */
|
||||
baud_rate_val = 0x2;
|
||||
} else if (zslave->speed_hz != zslave->req_hz) {
|
||||
while ((baud_rate_val < 8) &&
|
||||
((zslave->input_hz /
|
||||
(2 << baud_rate_val)) > zslave->req_hz))
|
||||
baud_rate_val++;
|
||||
zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val);
|
||||
}
|
||||
confr &= ~ZYNQ_SPI_CR_BRD_MASK;
|
||||
confr |= (baud_rate_val << 3);
|
||||
writel(confr, &zslave->base->cr);
|
||||
|
||||
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave);
|
||||
|
||||
debug("spi_release_bus: 0x%08x\n", (u32)slave);
|
||||
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr);
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
void *din, unsigned long flags)
|
||||
{
|
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave);
|
||||
u32 len = bitlen / 8;
|
||||
u32 tx_len = len, rx_len = len, tx_tvl;
|
||||
const u8 *tx_buf = dout;
|
||||
u8 *rx_buf = din, buf;
|
||||
u32 ts, status;
|
||||
|
||||
debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
|
||||
slave->bus, slave->cs, bitlen, len, flags);
|
||||
|
||||
if (bitlen == 0)
|
||||
return -1;
|
||||
|
||||
if (bitlen % 8) {
|
||||
debug("spi_xfer: Non byte aligned SPI transfer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_BEGIN)
|
||||
spi_cs_activate(slave);
|
||||
|
||||
while (rx_len > 0) {
|
||||
/* Write the data into TX FIFO - tx threshold is fifo_depth */
|
||||
tx_tvl = 0;
|
||||
while ((tx_tvl < zslave->fifo_depth) && tx_len) {
|
||||
if (tx_buf)
|
||||
buf = *tx_buf++;
|
||||
else
|
||||
buf = 0;
|
||||
writel(buf, &zslave->base->txdr);
|
||||
tx_len--;
|
||||
tx_tvl++;
|
||||
}
|
||||
|
||||
/* Check TX FIFO completion */
|
||||
ts = get_timer(0);
|
||||
status = readl(&zslave->base->isr);
|
||||
while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) {
|
||||
if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) {
|
||||
printf("spi_xfer: Timeout! TX FIFO not full\n");
|
||||
return -1;
|
||||
}
|
||||
status = readl(&zslave->base->isr);
|
||||
}
|
||||
|
||||
/* Read the data from RX FIFO */
|
||||
status = readl(&zslave->base->isr);
|
||||
while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) {
|
||||
buf = readl(&zslave->base->rxdr);
|
||||
if (rx_buf)
|
||||
*rx_buf++ = buf;
|
||||
status = readl(&zslave->base->isr);
|
||||
rx_len--;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(slave);
|
||||
|
||||
return 0;
|
||||
}
|
@ -72,6 +72,15 @@
|
||||
# define CONFIG_CPU_V6 /* Required by CONFIG_ARM_DCC */
|
||||
#endif
|
||||
|
||||
#define CONFIG_ZYNQ_SPI
|
||||
|
||||
/* SPI */
|
||||
#ifdef CONFIG_ZYNQ_SPI
|
||||
# define CONFIG_SPI_FLASH
|
||||
# define CONFIG_SPI_FLASH_SST
|
||||
# define CONFIG_CMD_SF
|
||||
#endif
|
||||
|
||||
/* Enable the PL to be downloaded */
|
||||
#define CONFIG_FPGA
|
||||
#define CONFIG_FPGA_XILINX
|
||||
|
Loading…
Reference in New Issue
Block a user