Merge branch 'master' of git://git.denx.de/u-boot-spi
- Various MTD fixes from Boris - Zap various unused / legacy paths. - pxa3xx NAND update from Miquel Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
commit
9450ab2ba8
8
README
8
README
@ -1932,14 +1932,6 @@ The following options need to be configured:
|
||||
SPI configuration items (port pins to use, etc). For
|
||||
an example, see include/configs/sacsng.h.
|
||||
|
||||
CONFIG_HARD_SPI
|
||||
|
||||
Enables a hardware SPI driver for general-purpose reads
|
||||
and writes. As with CONFIG_SOFT_SPI, the board configuration
|
||||
must define a list of chip-select function pointers.
|
||||
Currently supported on some MPC8xxx processors. For an
|
||||
example, see include/configs/mpc8349emds.h.
|
||||
|
||||
CONFIG_SYS_SPI_MXC_WAIT
|
||||
Timeout for waiting until spi transfer completed.
|
||||
default: (CONFIG_SYS_HZ/100) /* 10 ms */
|
||||
|
@ -18,13 +18,6 @@
|
||||
#define HWCONFIG_BUFFER_SIZE 256
|
||||
#endif
|
||||
|
||||
/* CONFIG_HARD_SPI triggers SPI bus initialization in PowerPC */
|
||||
#if defined(CONFIG_MPC8XXX_SPI) || defined(CONFIG_FSL_ESPI)
|
||||
# ifndef CONFIG_HARD_SPI
|
||||
# define CONFIG_HARD_SPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define CONFIG_LMB
|
||||
#define CONFIG_SYS_BOOT_RAMDISK_HIGH
|
||||
|
||||
|
@ -273,7 +273,7 @@ void spi_cs_deactivate(struct spi_slave *slave)
|
||||
|
||||
iopd->dat |= SPI_CS_MASK;
|
||||
}
|
||||
#endif /* CONFIG_HARD_SPI */
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_BOARD_SETUP)
|
||||
int ft_board_setup(void *blob, bd_t *bd)
|
||||
|
@ -208,4 +208,4 @@ void spi_cs_deactivate(struct spi_slave *slave)
|
||||
/* deactivate the spi_cs */
|
||||
setbits_be32(&iopd->dat, IDSCPLD_SPI_CS_MASK);
|
||||
}
|
||||
#endif /* CONFIG_HARD_SPI */
|
||||
#endif
|
||||
|
14
cmd/eeprom.c
14
cmd/eeprom.c
@ -66,11 +66,6 @@ __weak int eeprom_write_enable(unsigned dev_addr, int state)
|
||||
|
||||
void eeprom_init(int bus)
|
||||
{
|
||||
/* SPI EEPROM */
|
||||
#if defined(CONFIG_MPC8XX_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C)
|
||||
spi_init_f();
|
||||
#endif
|
||||
|
||||
/* I2C EEPROM */
|
||||
#if defined(CONFIG_SYS_I2C)
|
||||
if (bus >= 0)
|
||||
@ -129,14 +124,6 @@ static int eeprom_rw_block(unsigned offset, uchar *addr, unsigned alen,
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* SPI */
|
||||
#if defined(CONFIG_MPC8XX_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C)
|
||||
if (read)
|
||||
spi_read(addr, alen, buffer, len);
|
||||
else
|
||||
spi_write(addr, alen, buffer, len);
|
||||
#else /* I2C */
|
||||
|
||||
#if defined(CONFIG_DM_I2C) && defined(CONFIG_SYS_I2C_EEPROM_BUS)
|
||||
struct udevice *dev;
|
||||
|
||||
@ -162,7 +149,6 @@ static int eeprom_rw_block(unsigned offset, uchar *addr, unsigned alen,
|
||||
ret = i2c_read(addr[0], offset, alen - 1, buffer, len);
|
||||
else
|
||||
ret = i2c_write(addr[0], offset, alen - 1, buffer, len);
|
||||
#endif
|
||||
#endif /* CONFIG_DM_I2C && CONFIG_SYS_I2C_EEPROM_BUS */
|
||||
if (ret)
|
||||
ret = CMD_RET_FAILURE;
|
||||
|
@ -101,7 +101,6 @@ static int ubi_check(char *name)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int verify_mkvol_req(const struct ubi_device *ubi,
|
||||
const struct ubi_mkvol_req *req)
|
||||
{
|
||||
@ -415,7 +414,7 @@ static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ubi_detach(void)
|
||||
static int ubi_detach(void)
|
||||
{
|
||||
#ifdef CONFIG_CMD_UBIFS
|
||||
/*
|
||||
@ -473,7 +472,6 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
|
||||
if (strcmp(argv[1], "detach") == 0) {
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
@ -481,7 +479,6 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
return ubi_detach();
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(argv[1], "part") == 0) {
|
||||
const char *vid_header_offset = NULL;
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <os.h>
|
||||
#include <post.h>
|
||||
#include <relocate.h>
|
||||
#include <spi.h>
|
||||
#ifdef CONFIG_SPL
|
||||
#include <spl.h>
|
||||
#endif
|
||||
@ -262,16 +261,6 @@ __weak int init_func_vid(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HARD_SPI)
|
||||
static int init_func_spi(void)
|
||||
{
|
||||
puts("SPI: ");
|
||||
spi_init();
|
||||
puts("ready\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int setup_mon_len(void)
|
||||
{
|
||||
#if defined(__ARM__) || defined(__MICROBLAZE__)
|
||||
@ -912,9 +901,6 @@ static const init_fnc_t init_sequence_f[] = {
|
||||
#endif
|
||||
#if defined(CONFIG_VID) && !defined(CONFIG_SPL)
|
||||
init_func_vid,
|
||||
#endif
|
||||
#if defined(CONFIG_HARD_SPI)
|
||||
init_func_spi,
|
||||
#endif
|
||||
announce_dram_init,
|
||||
dram_init, /* configure available RAM banks */
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include <onenand_uboot.h>
|
||||
#include <scsi.h>
|
||||
#include <serial.h>
|
||||
#include <spi.h>
|
||||
#include <stdio_dev.h>
|
||||
#include <timer.h>
|
||||
#include <trace.h>
|
||||
@ -379,20 +378,6 @@ static int initr_flash(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PPC) && !defined(CONFIG_DM_SPI)
|
||||
static int initr_spi(void)
|
||||
{
|
||||
/* MPC8xx does this here */
|
||||
#ifdef CONFIG_MPC8XX_SPI
|
||||
#if !defined(CONFIG_ENV_IS_IN_EEPROM)
|
||||
spi_init_f();
|
||||
#endif
|
||||
spi_init_r();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMD_NAND
|
||||
/* go init the NAND */
|
||||
static int initr_nand(void)
|
||||
@ -744,9 +729,6 @@ static init_fnc_t init_sequence_r[] = {
|
||||
/* initialize higher level parts of CPU like time base and timers */
|
||||
cpu_init_r,
|
||||
#endif
|
||||
#if defined(CONFIG_PPC) && !defined(CONFIG_DM_SPI)
|
||||
initr_spi,
|
||||
#endif
|
||||
#ifdef CONFIG_CMD_NAND
|
||||
initr_nand,
|
||||
#endif
|
||||
|
@ -163,11 +163,6 @@ At this point you should be able to build U-Boot for your board with the
|
||||
empty SPI driver. You still have empty methods in your driver, but we will
|
||||
write these one by one.
|
||||
|
||||
If you have spi_init() functions or the like that are called from your
|
||||
board then the build will fail. Remove these calls and make a note of the
|
||||
init that needs to be done.
|
||||
|
||||
|
||||
7. Set up your platform data structure
|
||||
|
||||
This will hold the information your driver to operate, like its hardware
|
||||
|
@ -13,6 +13,29 @@
|
||||
|
||||
#define MTD_NAME_MAX_LEN 20
|
||||
|
||||
void board_mtdparts_default(const char **mtdids, const char **mtdparts);
|
||||
|
||||
static const char *get_mtdids(void)
|
||||
{
|
||||
__maybe_unused const char *mtdparts = NULL;
|
||||
const char *mtdids = env_get("mtdids");
|
||||
|
||||
if (mtdids)
|
||||
return mtdids;
|
||||
|
||||
#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
|
||||
board_mtdparts_default(&mtdids, &mtdparts);
|
||||
#elif defined(MTDIDS_DEFAULT)
|
||||
mtdids = MTDIDS_DEFAULT;
|
||||
#elif defined(CONFIG_MTDIDS_DEFAULT)
|
||||
mtdids = CONFIG_MTDIDS_DEFAULT;
|
||||
#endif
|
||||
|
||||
if (mtdids)
|
||||
env_set("mtdids", mtdids);
|
||||
|
||||
return mtdids;
|
||||
}
|
||||
|
||||
/**
|
||||
* mtd_search_alternate_name - Search an alternate name for @mtdname thanks to
|
||||
@ -34,7 +57,7 @@ int mtd_search_alternate_name(const char *mtdname, char *altname,
|
||||
const char *mtdids, *equal, *comma, *dev_id, *mtd_id;
|
||||
int dev_id_len, mtd_id_len;
|
||||
|
||||
mtdids = env_get("mtdids");
|
||||
mtdids = get_mtdids();
|
||||
if (!mtdids)
|
||||
return -EINVAL;
|
||||
|
||||
@ -92,30 +115,6 @@ static void mtd_probe_uclass_mtd_devs(void) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MTD_PARTITIONS)
|
||||
extern void board_mtdparts_default(const char **mtdids,
|
||||
const char **mtdparts);
|
||||
|
||||
static const char *get_mtdids(void)
|
||||
{
|
||||
__maybe_unused const char *mtdparts = NULL;
|
||||
const char *mtdids = env_get("mtdids");
|
||||
|
||||
if (mtdids)
|
||||
return mtdids;
|
||||
|
||||
#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
|
||||
board_mtdparts_default(&mtdids, &mtdparts);
|
||||
#elif defined(MTDIDS_DEFAULT)
|
||||
mtdids = MTDIDS_DEFAULT;
|
||||
#elif defined(CONFIG_MTDIDS_DEFAULT)
|
||||
mtdids = CONFIG_MTDIDS_DEFAULT;
|
||||
#endif
|
||||
|
||||
if (mtdids)
|
||||
env_set("mtdids", mtdids);
|
||||
|
||||
return mtdids;
|
||||
}
|
||||
|
||||
#define MTDPARTS_MAXLEN 512
|
||||
|
||||
@ -150,20 +149,74 @@ static const char *get_mtdparts(void)
|
||||
return mtdparts;
|
||||
}
|
||||
|
||||
static int mtd_del_parts(struct mtd_info *mtd, bool quiet)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!mtd_has_partitions(mtd))
|
||||
return 0;
|
||||
|
||||
/* do not delete partitions if they are in use. */
|
||||
if (mtd_partitions_used(mtd)) {
|
||||
if (!quiet)
|
||||
printf("\"%s\" partitions still in use, can't delete them\n",
|
||||
mtd->name);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = del_mtd_partitions(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool mtd_del_all_parts_failed;
|
||||
|
||||
static void mtd_del_all_parts(void)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
int ret = 0;
|
||||
|
||||
mtd_del_all_parts_failed = false;
|
||||
|
||||
/*
|
||||
* It is not safe to remove entries from the mtd_for_each_device loop
|
||||
* as it uses idr indexes and the partitions removal is done in bulk
|
||||
* (all partitions of one device at the same time), so break and
|
||||
* iterate from start each time a new partition is found and deleted.
|
||||
*/
|
||||
do {
|
||||
mtd_for_each_device(mtd) {
|
||||
ret = mtd_del_parts(mtd, false);
|
||||
if (ret > 0)
|
||||
break;
|
||||
else if (ret < 0)
|
||||
mtd_del_all_parts_failed = true;
|
||||
}
|
||||
} while (ret > 0);
|
||||
}
|
||||
|
||||
int mtd_probe_devices(void)
|
||||
{
|
||||
static char *old_mtdparts;
|
||||
static char *old_mtdids;
|
||||
const char *mtdparts = get_mtdparts();
|
||||
const char *mtdids = get_mtdids();
|
||||
bool remaining_partitions = true;
|
||||
const char *mtdparts_next = mtdparts;
|
||||
struct mtd_info *mtd;
|
||||
|
||||
mtd_probe_uclass_mtd_devs();
|
||||
|
||||
/* Check if mtdparts/mtdids changed since last call, otherwise: exit */
|
||||
/*
|
||||
* Check if mtdparts/mtdids changed, if the MTD dev list was updated
|
||||
* or if our previous attempt to delete existing partititions failed.
|
||||
* In any of these cases we want to update the partitions, otherwise,
|
||||
* everything is up-to-date and we can return 0 directly.
|
||||
*/
|
||||
if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) ||
|
||||
(mtdparts && old_mtdparts && mtdids && old_mtdids &&
|
||||
!mtd_dev_list_updated() && !mtd_del_all_parts_failed &&
|
||||
!strcmp(mtdparts, old_mtdparts) &&
|
||||
!strcmp(mtdids, old_mtdids)))
|
||||
return 0;
|
||||
@ -174,55 +227,55 @@ int mtd_probe_devices(void)
|
||||
old_mtdparts = strdup(mtdparts);
|
||||
old_mtdids = strdup(mtdids);
|
||||
|
||||
/* If at least one partition is still in use, do not delete anything */
|
||||
mtd_for_each_device(mtd) {
|
||||
if (mtd->usecount) {
|
||||
printf("Partition \"%s\" already in use, aborting\n",
|
||||
mtd->name);
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Remove all old parts. Note that partition removal can fail in case
|
||||
* one of the partition is still being used by an MTD user, so this
|
||||
* does not guarantee that all old partitions are gone.
|
||||
*/
|
||||
mtd_del_all_parts();
|
||||
|
||||
/*
|
||||
* Everything looks clear, remove all partitions. It is not safe to
|
||||
* remove entries from the mtd_for_each_device loop as it uses idr
|
||||
* indexes and the partitions removal is done in bulk (all partitions of
|
||||
* one device at the same time), so break and iterate from start each
|
||||
* time a new partition is found and deleted.
|
||||
* Call mtd_dev_list_updated() to clear updates generated by our own
|
||||
* parts removal loop.
|
||||
*/
|
||||
while (remaining_partitions) {
|
||||
remaining_partitions = false;
|
||||
mtd_for_each_device(mtd) {
|
||||
if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) {
|
||||
del_mtd_partitions(mtd);
|
||||
remaining_partitions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mtd_dev_list_updated();
|
||||
|
||||
/* If either mtdparts or mtdids is empty, then exit */
|
||||
if (!mtdparts || !mtdids)
|
||||
return 0;
|
||||
|
||||
/* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */
|
||||
if (strstr(mtdparts, "mtdparts="))
|
||||
if (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1))
|
||||
mtdparts += 9;
|
||||
|
||||
/* For each MTD device in mtdparts */
|
||||
while (mtdparts[0] != '\0') {
|
||||
for (; mtdparts[0] != '\0'; mtdparts = mtdparts_next) {
|
||||
char mtd_name[MTD_NAME_MAX_LEN], *colon;
|
||||
struct mtd_partition *parts;
|
||||
int mtd_name_len, nparts;
|
||||
int ret;
|
||||
unsigned int mtd_name_len;
|
||||
int nparts, ret;
|
||||
|
||||
mtdparts_next = strchr(mtdparts, ';');
|
||||
if (!mtdparts_next)
|
||||
mtdparts_next = mtdparts + strlen(mtdparts);
|
||||
else
|
||||
mtdparts_next++;
|
||||
|
||||
colon = strchr(mtdparts, ':');
|
||||
if (colon > mtdparts_next)
|
||||
colon = NULL;
|
||||
|
||||
if (!colon) {
|
||||
printf("Wrong mtdparts: %s\n", mtdparts);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtd_name_len = colon - mtdparts;
|
||||
mtd_name_len = (unsigned int)(colon - mtdparts);
|
||||
if (mtd_name_len + 1 > sizeof(mtd_name)) {
|
||||
printf("MTD name too long: %s\n", mtdparts);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
strncpy(mtd_name, mtdparts, mtd_name_len);
|
||||
mtd_name[mtd_name_len] = '\0';
|
||||
/* Move the pointer forward (including the ':') */
|
||||
@ -249,14 +302,22 @@ int mtd_probe_devices(void)
|
||||
if (ret || IS_ERR_OR_NULL(mtd)) {
|
||||
printf("Could not find a valid device for %s\n",
|
||||
mtd_name);
|
||||
mtdparts = strchr(mtdparts, ';');
|
||||
if (mtdparts)
|
||||
mtdparts++;
|
||||
|
||||
mtdparts = mtdparts_next;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Call mtd_del_parts() again, even if it's already been called
|
||||
* in mtd_del_all_parts(). We need to know if old partitions are
|
||||
* still around (because they are still being used by someone),
|
||||
* and if they are, we shouldn't create new partitions, so just
|
||||
* skip this MTD device and try the next one.
|
||||
*/
|
||||
ret = mtd_del_parts(mtd, true);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Parse the MTD device partitions. It will update the mtdparts
|
||||
* pointer, create an array of parts (that must be freed), and
|
||||
@ -281,6 +342,12 @@ int mtd_probe_devices(void)
|
||||
put_mtd_device(mtd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call mtd_dev_list_updated() to clear updates generated by our own
|
||||
* parts registration loop.
|
||||
*/
|
||||
mtd_dev_list_updated();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
@ -87,14 +87,17 @@ struct idr_layer {
|
||||
|
||||
struct idr {
|
||||
struct idr_layer id[MAX_IDR_ID];
|
||||
bool updated;
|
||||
};
|
||||
|
||||
#define DEFINE_IDR(name) struct idr name;
|
||||
|
||||
void idr_remove(struct idr *idp, int id)
|
||||
{
|
||||
if (idp->id[id].used)
|
||||
if (idp->id[id].used) {
|
||||
idp->id[id].used = 0;
|
||||
idp->updated = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -134,6 +137,7 @@ int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask)
|
||||
if (idl->used == 0) {
|
||||
idl->used = 1;
|
||||
idl->ptr = ptr;
|
||||
idp->updated = true;
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
@ -155,6 +159,16 @@ struct mtd_info *__mtd_next_device(int i)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__mtd_next_device);
|
||||
|
||||
bool mtd_dev_list_updated(void)
|
||||
{
|
||||
if (mtd_idr.updated) {
|
||||
mtd_idr.updated = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static LIST_HEAD(mtd_notifiers);
|
||||
|
||||
@ -514,6 +528,13 @@ int del_mtd_device(struct mtd_info *mtd)
|
||||
struct mtd_notifier *not;
|
||||
#endif
|
||||
|
||||
ret = del_mtd_partitions(mtd);
|
||||
if (ret) {
|
||||
debug("Failed to delete MTD partitions attached to %s (err %d)\n",
|
||||
mtd->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&mtd_table_mutex);
|
||||
|
||||
if (idr_find(&mtd_idr, mtd->index) != mtd) {
|
||||
|
@ -63,6 +63,18 @@ char *kstrdup(const char *s, gfp_t gfp)
|
||||
#define MTD_SIZE_REMAINING (~0LLU)
|
||||
#define MTD_OFFSET_NOT_SPECIFIED (~0LLU)
|
||||
|
||||
bool mtd_partitions_used(struct mtd_info *master)
|
||||
{
|
||||
struct mtd_info *slave;
|
||||
|
||||
list_for_each_entry(slave, &master->partitions, node) {
|
||||
if (slave->usecount)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* mtd_parse_partition - Parse @mtdparts partition definition, fill @partition
|
||||
* with it and update the @mtdparts string pointer.
|
||||
|
@ -195,6 +195,7 @@ struct pxa3xx_nand_info {
|
||||
|
||||
int cs;
|
||||
int use_ecc; /* use HW ECC ? */
|
||||
int force_raw; /* prevent use_ecc to be set */
|
||||
int ecc_bch; /* using BCH ECC? */
|
||||
int use_spare; /* use spare ? */
|
||||
int need_wait;
|
||||
@ -326,14 +327,14 @@ static struct nand_ecclayout ecc_layout_2KB_bch4bit = {
|
||||
static struct nand_ecclayout ecc_layout_2KB_bch8bit = {
|
||||
.eccbytes = 64,
|
||||
.eccpos = {
|
||||
64, 65, 66, 67, 68, 69, 70, 71,
|
||||
72, 73, 74, 75, 76, 77, 78, 79,
|
||||
80, 81, 82, 83, 84, 85, 86, 87,
|
||||
88, 89, 90, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103,
|
||||
104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119,
|
||||
120, 121, 122, 123, 124, 125, 126, 127},
|
||||
32, 33, 34, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63,
|
||||
64, 65, 66, 67, 68, 69, 70, 71,
|
||||
72, 73, 74, 75, 76, 77, 78, 79,
|
||||
80, 81, 82, 83, 84, 85, 86, 87,
|
||||
88, 89, 90, 91, 92, 93, 94, 95},
|
||||
.oobfree = { {1, 4}, {6, 26} }
|
||||
};
|
||||
|
||||
@ -579,7 +580,7 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
|
||||
|
||||
static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
||||
{
|
||||
if (info->ecc_bch) {
|
||||
if (info->ecc_bch && !info->force_raw) {
|
||||
u32 ts;
|
||||
|
||||
/*
|
||||
@ -612,12 +613,22 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
||||
|
||||
static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
{
|
||||
int data_len = info->step_chunk_size;
|
||||
|
||||
/*
|
||||
* In raw mode, include the spare area and the ECC bytes that are not
|
||||
* consumed by the controller in the data section. Do not reorganize
|
||||
* here, do it in the ->read_page_raw() handler instead.
|
||||
*/
|
||||
if (info->force_raw)
|
||||
data_len += info->step_spare_size + info->ecc_size;
|
||||
|
||||
switch (info->state) {
|
||||
case STATE_PIO_WRITING:
|
||||
if (info->step_chunk_size)
|
||||
writesl(info->mmio_base + NDDB,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(info->step_chunk_size, 4));
|
||||
DIV_ROUND_UP(data_len, 4));
|
||||
|
||||
if (info->step_spare_size)
|
||||
writesl(info->mmio_base + NDDB,
|
||||
@ -628,7 +639,10 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
if (info->step_chunk_size)
|
||||
drain_fifo(info,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(info->step_chunk_size, 4));
|
||||
DIV_ROUND_UP(data_len, 4));
|
||||
|
||||
if (info->force_raw)
|
||||
break;
|
||||
|
||||
if (info->step_spare_size)
|
||||
drain_fifo(info,
|
||||
@ -642,7 +656,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
}
|
||||
|
||||
/* Update buffer pointers for multi-page read/write */
|
||||
info->data_buff_pos += info->step_chunk_size;
|
||||
info->data_buff_pos += data_len;
|
||||
info->oob_buff_pos += info->step_spare_size;
|
||||
}
|
||||
|
||||
@ -796,7 +810,8 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
|
||||
case NAND_CMD_READ0:
|
||||
case NAND_CMD_READOOB:
|
||||
case NAND_CMD_PAGEPROG:
|
||||
info->use_ecc = 1;
|
||||
if (!info->force_raw)
|
||||
info->use_ecc = 1;
|
||||
break;
|
||||
case NAND_CMD_PARAM:
|
||||
info->use_spare = 0;
|
||||
@ -866,7 +881,13 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
||||
* which is either naked-read or last-read according to the
|
||||
* state.
|
||||
*/
|
||||
if (mtd->writesize == info->chunk_size) {
|
||||
if (info->force_raw) {
|
||||
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) |
|
||||
NDCB0_LEN_OVRD |
|
||||
NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
||||
info->ndcb3 = info->step_chunk_size +
|
||||
info->step_spare_size + info->ecc_size;
|
||||
} else if (mtd->writesize == info->chunk_size) {
|
||||
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
|
||||
} else if (mtd->writesize > info->chunk_size) {
|
||||
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
|
||||
@ -1216,6 +1237,7 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
|
||||
{
|
||||
struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
|
||||
struct pxa3xx_nand_info *info = host->info_data;
|
||||
int bf;
|
||||
|
||||
chip->read_buf(mtd, buf, mtd->writesize);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
@ -1223,12 +1245,30 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
|
||||
if (info->retcode == ERR_CORERR && info->use_ecc) {
|
||||
mtd->ecc_stats.corrected += info->ecc_err_cnt;
|
||||
|
||||
} else if (info->retcode == ERR_UNCORERR) {
|
||||
} else if (info->retcode == ERR_UNCORERR && info->ecc_bch) {
|
||||
/*
|
||||
* for blank page (all 0xff), HW will calculate its ECC as
|
||||
* 0, which is different from the ECC information within
|
||||
* OOB, ignore such uncorrectable errors
|
||||
* Empty pages will trigger uncorrectable errors. Re-read the
|
||||
* entire page in raw mode and check for bits not being "1".
|
||||
* If there are more than the supported strength, then it means
|
||||
* this is an actual uncorrectable error.
|
||||
*/
|
||||
chip->ecc.read_page_raw(mtd, chip, buf, oob_required, page);
|
||||
bf = nand_check_erased_ecc_chunk(buf, mtd->writesize,
|
||||
chip->oob_poi, mtd->oobsize,
|
||||
NULL, 0, chip->ecc.strength);
|
||||
if (bf < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else if (bf) {
|
||||
mtd->ecc_stats.corrected += bf;
|
||||
info->max_bitflips = max_t(unsigned int,
|
||||
info->max_bitflips, bf);
|
||||
info->retcode = ERR_CORERR;
|
||||
} else {
|
||||
info->retcode = ERR_NONE;
|
||||
}
|
||||
|
||||
} else if (info->retcode == ERR_UNCORERR && !info->ecc_bch) {
|
||||
/* Raw read is not supported with Hamming ECC engine */
|
||||
if (is_buf_blank(buf, mtd->writesize))
|
||||
info->retcode = ERR_NONE;
|
||||
else
|
||||
@ -1238,6 +1278,69 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
|
||||
return info->max_bitflips;
|
||||
}
|
||||
|
||||
static int pxa3xx_nand_read_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct pxa3xx_nand_host *host = chip->priv;
|
||||
struct pxa3xx_nand_info *info = host->info_data;
|
||||
int chunk, ecc_off_buf;
|
||||
|
||||
if (!info->ecc_bch)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
* Set the force_raw boolean, then re-call ->cmdfunc() that will run
|
||||
* pxa3xx_nand_start(), which will actually disable the ECC engine.
|
||||
*/
|
||||
info->force_raw = true;
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||
|
||||
ecc_off_buf = (info->nfullchunks * info->spare_size) +
|
||||
info->last_spare_size;
|
||||
for (chunk = 0; chunk < info->nfullchunks; chunk++) {
|
||||
chip->read_buf(mtd,
|
||||
buf + (chunk * info->chunk_size),
|
||||
info->chunk_size);
|
||||
chip->read_buf(mtd,
|
||||
chip->oob_poi +
|
||||
(chunk * (info->spare_size)),
|
||||
info->spare_size);
|
||||
chip->read_buf(mtd,
|
||||
chip->oob_poi + ecc_off_buf +
|
||||
(chunk * (info->ecc_size)),
|
||||
info->ecc_size - 2);
|
||||
}
|
||||
|
||||
if (info->ntotalchunks > info->nfullchunks) {
|
||||
chip->read_buf(mtd,
|
||||
buf + (info->nfullchunks * info->chunk_size),
|
||||
info->last_chunk_size);
|
||||
chip->read_buf(mtd,
|
||||
chip->oob_poi +
|
||||
(info->nfullchunks * (info->spare_size)),
|
||||
info->last_spare_size);
|
||||
chip->read_buf(mtd,
|
||||
chip->oob_poi + ecc_off_buf +
|
||||
(info->nfullchunks * (info->ecc_size)),
|
||||
info->ecc_size - 2);
|
||||
}
|
||||
|
||||
info->force_raw = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa3xx_nand_read_oob_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, int page)
|
||||
{
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
|
||||
return chip->ecc.read_page_raw(mtd, chip, chip->buffers->databuf, true,
|
||||
page);
|
||||
}
|
||||
|
||||
static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
@ -1488,7 +1591,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
info->chunk_size = 1024;
|
||||
info->spare_size = 0;
|
||||
info->last_chunk_size = 1024;
|
||||
info->last_spare_size = 64;
|
||||
info->last_spare_size = 32;
|
||||
info->ecc_size = 32;
|
||||
ecc->mode = NAND_ECC_HW;
|
||||
ecc->size = info->chunk_size;
|
||||
@ -1669,6 +1772,8 @@ static int alloc_nand_resource(struct pxa3xx_nand_info *info)
|
||||
|
||||
nand_set_controller_data(chip, host);
|
||||
chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
|
||||
chip->ecc.read_page_raw = pxa3xx_nand_read_page_raw;
|
||||
chip->ecc.read_oob_raw = pxa3xx_nand_read_oob_raw;
|
||||
chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
|
||||
chip->controller = &info->controller;
|
||||
chip->waitfunc = pxa3xx_nand_waitfunc;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <spi_flash.h>
|
||||
|
||||
static struct mtd_info sf_mtd_info;
|
||||
static bool sf_mtd_registered;
|
||||
static char sf_mtd_name[8];
|
||||
|
||||
static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
@ -17,6 +18,9 @@ static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
struct spi_flash *flash = mtd->priv;
|
||||
int err;
|
||||
|
||||
if (!flash)
|
||||
return -ENODEV;
|
||||
|
||||
instr->state = MTD_ERASING;
|
||||
|
||||
err = spi_flash_erase(flash, instr->addr, instr->len);
|
||||
@ -38,6 +42,9 @@ static int spi_flash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
struct spi_flash *flash = mtd->priv;
|
||||
int err;
|
||||
|
||||
if (!flash)
|
||||
return -ENODEV;
|
||||
|
||||
err = spi_flash_read(flash, from, len, buf);
|
||||
if (!err)
|
||||
*retlen = len;
|
||||
@ -51,6 +58,9 @@ static int spi_flash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
struct spi_flash *flash = mtd->priv;
|
||||
int err;
|
||||
|
||||
if (!flash)
|
||||
return -ENODEV;
|
||||
|
||||
err = spi_flash_write(flash, to, len, buf);
|
||||
if (!err)
|
||||
*retlen = len;
|
||||
@ -73,6 +83,17 @@ static int spi_flash_mtd_number(void)
|
||||
|
||||
int spi_flash_mtd_register(struct spi_flash *flash)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (sf_mtd_registered) {
|
||||
ret = del_mtd_device(&sf_mtd_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sf_mtd_registered = false;
|
||||
}
|
||||
|
||||
sf_mtd_registered = false;
|
||||
memset(&sf_mtd_info, 0, sizeof(sf_mtd_info));
|
||||
sprintf(sf_mtd_name, "nor%d", spi_flash_mtd_number());
|
||||
|
||||
@ -94,10 +115,33 @@ int spi_flash_mtd_register(struct spi_flash *flash)
|
||||
sf_mtd_info.numeraseregions = 0;
|
||||
sf_mtd_info.erasesize = flash->sector_size;
|
||||
|
||||
return add_mtd_device(&sf_mtd_info);
|
||||
ret = add_mtd_device(&sf_mtd_info);
|
||||
if (!ret)
|
||||
sf_mtd_registered = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void spi_flash_mtd_unregister(void)
|
||||
{
|
||||
del_mtd_device(&sf_mtd_info);
|
||||
int ret;
|
||||
|
||||
if (!sf_mtd_registered)
|
||||
return;
|
||||
|
||||
ret = del_mtd_device(&sf_mtd_info);
|
||||
if (!ret) {
|
||||
sf_mtd_registered = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setting mtd->priv to NULL is the best we can do. Thanks to that,
|
||||
* the MTD layer can still call mtd hooks without risking a
|
||||
* use-after-free bug. Still, things should be fixed to prevent the
|
||||
* spi_flash object from being destroyed when del_mtd_device() fails.
|
||||
*/
|
||||
sf_mtd_info.priv = NULL;
|
||||
printf("Failed to unregister MTD %s and the spi_flash object is going away: you're in deep trouble!",
|
||||
sf_mtd_info.name);
|
||||
}
|
||||
|
@ -144,6 +144,14 @@ static int spi_flash_std_probe(struct udevice *dev)
|
||||
return spi_flash_probe_slave(flash);
|
||||
}
|
||||
|
||||
static int spi_flash_std_remove(struct udevice *dev)
|
||||
{
|
||||
#ifdef CONFIG_SPI_FLASH_MTD
|
||||
spi_flash_mtd_unregister();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_flash_ops spi_flash_std_ops = {
|
||||
.read = spi_flash_std_read,
|
||||
.write = spi_flash_std_write,
|
||||
@ -161,6 +169,7 @@ U_BOOT_DRIVER(spi_flash_std) = {
|
||||
.id = UCLASS_SPI_FLASH,
|
||||
.of_match = spi_flash_std_ids,
|
||||
.probe = spi_flash_std_probe,
|
||||
.remove = spi_flash_std_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct spi_flash),
|
||||
.ops = &spi_flash_std_ops,
|
||||
};
|
||||
|
@ -77,9 +77,6 @@ static inline struct e1000_hw *e1000_hw_from_spi(struct spi_slave *spi)
|
||||
return container_of(spi, struct e1000_hw, spi);
|
||||
}
|
||||
|
||||
/* Not sure why all of these are necessary */
|
||||
void spi_init(void) { /* Nothing to do */ }
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
|
@ -116,6 +116,20 @@ config ICH_SPI
|
||||
access the SPI NOR flash on platforms embedding this Intel
|
||||
ICH IP core.
|
||||
|
||||
config MESON_SPIFC
|
||||
bool "Amlogic Meson SPI Flash Controller driver"
|
||||
depends on ARCH_MESON
|
||||
help
|
||||
Enable the Amlogic Meson SPI Flash Controller SPIFC) driver.
|
||||
This driver can be used to access the SPI NOR flash chips on
|
||||
Amlogic Meson SoCs.
|
||||
|
||||
config MPC8XX_SPI
|
||||
bool "MPC8XX SPI Driver"
|
||||
depends on MPC8xx
|
||||
help
|
||||
Enable support for SPI on MPC8XX
|
||||
|
||||
config MT7621_SPI
|
||||
bool "MediaTek MT7621 SPI driver"
|
||||
depends on ARCH_MT7620
|
||||
@ -124,6 +138,13 @@ config MT7621_SPI
|
||||
the SPI NOR flash on platforms embedding this Ralink / MediaTek
|
||||
SPI core, like MT7621/7628/7688.
|
||||
|
||||
config MTK_QSPI
|
||||
bool "Mediatek QSPI driver"
|
||||
help
|
||||
Enable the Mediatek QSPI driver. This driver can be
|
||||
used to access the SPI NOR flash on platforms embedding this
|
||||
Mediatek QSPI IP core.
|
||||
|
||||
config MVEBU_A3700_SPI
|
||||
bool "Marvell Armada 3700 SPI driver"
|
||||
select CLK_ARMADA_3720
|
||||
@ -328,12 +349,6 @@ config LPC32XX_SSP
|
||||
help
|
||||
Enable support for SPI on LPC32xx
|
||||
|
||||
config MPC8XX_SPI
|
||||
bool "MPC8XX SPI Driver"
|
||||
depends on MPC8xx
|
||||
help
|
||||
Enable support for SPI on MPC8XX
|
||||
|
||||
config MPC8XXX_SPI
|
||||
bool "MPC8XXX SPI Driver"
|
||||
help
|
||||
|
@ -31,8 +31,10 @@ obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o
|
||||
obj-$(CONFIG_ICH_SPI) += ich.o
|
||||
obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
|
||||
obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
|
||||
obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o
|
||||
obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o
|
||||
obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
|
||||
obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o
|
||||
obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o
|
||||
obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
|
||||
obj-$(CONFIG_MXC_SPI) += mxc_spi.o
|
||||
|
@ -34,11 +34,6 @@ static int spi_has_wdrbt(struct atmel_spi_slave *slave)
|
||||
return (ATMEL_SPI_VERSION_REV(ver) >= 0x210);
|
||||
}
|
||||
|
||||
void spi_init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
|
@ -388,11 +388,6 @@ void spi_cs_deactivate(struct spi_slave *slave)
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
|
@ -390,11 +390,6 @@ static int fsl_dspi_cfg_speed(struct fsl_dspi_priv *priv, uint speed)
|
||||
return 0;
|
||||
}
|
||||
#ifndef CONFIG_DM_SPI
|
||||
void spi_init(void)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8)))
|
||||
|
@ -118,11 +118,6 @@ void spi_free_slave(struct spi_slave *slave)
|
||||
free(fsl);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
|
||||
|
@ -47,15 +47,6 @@ static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(
|
||||
return container_of(slave, struct lpc32xx_spi_slave, slave);
|
||||
}
|
||||
|
||||
/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
|
||||
void spi_init(void)
|
||||
{
|
||||
/*
|
||||
* nothing to do: clocking was enabled in lpc32xx_ssp_enable()
|
||||
* and configuration will be done in spi_setup_slave()
|
||||
*/
|
||||
}
|
||||
|
||||
/* the following is called in sequence by do_spi_xfer() */
|
||||
|
||||
struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
|
||||
|
320
drivers/spi/meson_spifc.c
Normal file
320
drivers/spi/meson_spifc.c
Normal file
@ -0,0 +1,320 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
|
||||
* Copyright (C) 2018 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* Amlogic Meson SPI Flash Controller driver
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <spi.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
/* register map */
|
||||
#define REG_CMD 0x00
|
||||
#define REG_ADDR 0x04
|
||||
#define REG_CTRL 0x08
|
||||
#define REG_CTRL1 0x0c
|
||||
#define REG_STATUS 0x10
|
||||
#define REG_CTRL2 0x14
|
||||
#define REG_CLOCK 0x18
|
||||
#define REG_USER 0x1c
|
||||
#define REG_USER1 0x20
|
||||
#define REG_USER2 0x24
|
||||
#define REG_USER3 0x28
|
||||
#define REG_USER4 0x2c
|
||||
#define REG_SLAVE 0x30
|
||||
#define REG_SLAVE1 0x34
|
||||
#define REG_SLAVE2 0x38
|
||||
#define REG_SLAVE3 0x3c
|
||||
#define REG_C0 0x40
|
||||
#define REG_B8 0x60
|
||||
#define REG_MAX 0x7c
|
||||
|
||||
/* register fields */
|
||||
#define CMD_USER BIT(18)
|
||||
#define CTRL_ENABLE_AHB BIT(17)
|
||||
#define CLOCK_SOURCE BIT(31)
|
||||
#define CLOCK_DIV_SHIFT 12
|
||||
#define CLOCK_DIV_MASK (0x3f << CLOCK_DIV_SHIFT)
|
||||
#define CLOCK_CNT_HIGH_SHIFT 6
|
||||
#define CLOCK_CNT_HIGH_MASK (0x3f << CLOCK_CNT_HIGH_SHIFT)
|
||||
#define CLOCK_CNT_LOW_SHIFT 0
|
||||
#define CLOCK_CNT_LOW_MASK (0x3f << CLOCK_CNT_LOW_SHIFT)
|
||||
#define USER_DIN_EN_MS BIT(0)
|
||||
#define USER_CMP_MODE BIT(2)
|
||||
#define USER_CLK_NOT_INV BIT(7)
|
||||
#define USER_UC_DOUT_SEL BIT(27)
|
||||
#define USER_UC_DIN_SEL BIT(28)
|
||||
#define USER_UC_MASK ((BIT(5) - 1) << 27)
|
||||
#define USER1_BN_UC_DOUT_SHIFT 17
|
||||
#define USER1_BN_UC_DOUT_MASK (0xff << 16)
|
||||
#define USER1_BN_UC_DIN_SHIFT 8
|
||||
#define USER1_BN_UC_DIN_MASK (0xff << 8)
|
||||
#define USER4_CS_POL_HIGH BIT(23)
|
||||
#define USER4_IDLE_CLK_HIGH BIT(29)
|
||||
#define USER4_CS_ACT BIT(30)
|
||||
#define SLAVE_TRST_DONE BIT(4)
|
||||
#define SLAVE_OP_MODE BIT(30)
|
||||
#define SLAVE_SW_RST BIT(31)
|
||||
|
||||
#define SPIFC_BUFFER_SIZE 64
|
||||
|
||||
struct meson_spifc_priv {
|
||||
struct regmap *regmap;
|
||||
struct clk clk;
|
||||
};
|
||||
|
||||
/**
|
||||
* meson_spifc_drain_buffer() - copy data from device buffer to memory
|
||||
* @spifc: the Meson SPI device
|
||||
* @buf: the destination buffer
|
||||
* @len: number of bytes to copy
|
||||
*/
|
||||
static void meson_spifc_drain_buffer(struct meson_spifc_priv *spifc,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
u32 data;
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
regmap_read(spifc->regmap, REG_C0 + i, &data);
|
||||
|
||||
if (len - i >= 4) {
|
||||
*((u32 *)buf) = data;
|
||||
buf += 4;
|
||||
} else {
|
||||
memcpy(buf, &data, len - i);
|
||||
break;
|
||||
}
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_spifc_fill_buffer() - copy data from memory to device buffer
|
||||
* @spifc: the Meson SPI device
|
||||
* @buf: the source buffer
|
||||
* @len: number of bytes to copy
|
||||
*/
|
||||
static void meson_spifc_fill_buffer(struct meson_spifc_priv *spifc,
|
||||
const u8 *buf, int len)
|
||||
{
|
||||
u32 data = 0;
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
if (len - i >= 4)
|
||||
data = *(u32 *)buf;
|
||||
else
|
||||
memcpy(&data, buf, len - i);
|
||||
|
||||
regmap_write(spifc->regmap, REG_C0 + i, data);
|
||||
|
||||
buf += 4;
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_spifc_txrx() - transfer a chunk of data
|
||||
* @spifc: the Meson SPI device
|
||||
* @dout: data buffer for TX
|
||||
* @din: data buffer for RX
|
||||
* @offset: offset of the data to transfer
|
||||
* @len: length of the data to transfer
|
||||
* @last_xfer: whether this is the last transfer of the message
|
||||
* @last_chunk: whether this is the last chunk of the transfer
|
||||
* Return: 0 on success, a negative value on error
|
||||
*/
|
||||
static int meson_spifc_txrx(struct meson_spifc_priv *spifc,
|
||||
const u8 *dout, u8 *din, int offset,
|
||||
int len, bool last_xfer, bool last_chunk)
|
||||
{
|
||||
bool keep_cs = true;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
if (dout)
|
||||
meson_spifc_fill_buffer(spifc, dout + offset, len);
|
||||
|
||||
/* enable DOUT stage */
|
||||
regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK,
|
||||
USER_UC_DOUT_SEL);
|
||||
regmap_write(spifc->regmap, REG_USER1,
|
||||
(8 * len - 1) << USER1_BN_UC_DOUT_SHIFT);
|
||||
|
||||
/* enable data input during DOUT */
|
||||
regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS,
|
||||
USER_DIN_EN_MS);
|
||||
|
||||
if (last_chunk && last_xfer)
|
||||
keep_cs = false;
|
||||
|
||||
regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT,
|
||||
keep_cs ? USER4_CS_ACT : 0);
|
||||
|
||||
/* clear transition done bit */
|
||||
regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0);
|
||||
/* start transfer */
|
||||
regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
|
||||
|
||||
/* wait for the current operation to terminate */
|
||||
ret = regmap_read_poll_timeout(spifc->regmap, REG_SLAVE, data,
|
||||
(data & SLAVE_TRST_DONE),
|
||||
0, 5 * CONFIG_SYS_HZ);
|
||||
|
||||
if (!ret && din)
|
||||
meson_spifc_drain_buffer(spifc, din + offset, len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_spifc_xfer() - perform a single transfer
|
||||
* @dev: the SPI controller device
|
||||
* @bitlen: length of the transfer
|
||||
* @dout: data buffer for TX
|
||||
* @din: data buffer for RX
|
||||
* @flags: transfer flags
|
||||
* Return: 0 on success, a negative value on error
|
||||
*/
|
||||
static int meson_spifc_xfer(struct udevice *slave, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct meson_spifc_priv *spifc = dev_get_priv(slave->parent);
|
||||
int blen = bitlen / 8;
|
||||
int len, done = 0, ret = 0;
|
||||
|
||||
if (bitlen % 8)
|
||||
return -EINVAL;
|
||||
|
||||
debug("xfer len %d (%d) dout %p din %p\n", bitlen, blen, dout, din);
|
||||
|
||||
regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0);
|
||||
|
||||
while (done < blen && !ret) {
|
||||
len = min_t(int, blen - done, SPIFC_BUFFER_SIZE);
|
||||
ret = meson_spifc_txrx(spifc, dout, din, done, len,
|
||||
flags & SPI_XFER_END,
|
||||
done + len >= blen);
|
||||
done += len;
|
||||
}
|
||||
|
||||
regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB,
|
||||
CTRL_ENABLE_AHB);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_spifc_set_speed() - program the clock divider
|
||||
* @dev: the SPI controller device
|
||||
* @speed: desired speed in Hz
|
||||
*/
|
||||
static int meson_spifc_set_speed(struct udevice *dev, uint speed)
|
||||
{
|
||||
struct meson_spifc_priv *spifc = dev_get_priv(dev);
|
||||
unsigned long parent, value;
|
||||
int n;
|
||||
|
||||
parent = clk_get_rate(&spifc->clk);
|
||||
n = max_t(int, parent / speed - 1, 1);
|
||||
|
||||
debug("parent %lu, speed %u, n %d\n", parent, speed, n);
|
||||
|
||||
value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
|
||||
value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
|
||||
value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
|
||||
CLOCK_CNT_HIGH_MASK;
|
||||
|
||||
regmap_write(spifc->regmap, REG_CLOCK, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_spifc_set_mode() - setups the SPI bus mode
|
||||
* @dev: the SPI controller device
|
||||
* @mode: desired mode bitfield
|
||||
* Return: 0 on success, -ENODEV on error
|
||||
*/
|
||||
static int meson_spifc_set_mode(struct udevice *dev, uint mode)
|
||||
{
|
||||
struct meson_spifc_priv *spifc = dev_get_priv(dev);
|
||||
|
||||
if (mode & (SPI_CPHA | SPI_RX_QUAD | SPI_RX_DUAL |
|
||||
SPI_TX_QUAD | SPI_TX_DUAL))
|
||||
return -ENODEV;
|
||||
|
||||
regmap_update_bits(spifc->regmap, REG_USER, USER_CLK_NOT_INV,
|
||||
mode & SPI_CPOL ? USER_CLK_NOT_INV : 0);
|
||||
|
||||
regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_POL_HIGH,
|
||||
mode & SPI_CS_HIGH ? USER4_CS_POL_HIGH : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_spifc_hw_init() - reset and initialize the SPI controller
|
||||
* @spifc: the Meson SPI device
|
||||
*/
|
||||
static void meson_spifc_hw_init(struct meson_spifc_priv *spifc)
|
||||
{
|
||||
/* reset device */
|
||||
regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST,
|
||||
SLAVE_SW_RST);
|
||||
/* disable compatible mode */
|
||||
regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0);
|
||||
/* set master mode */
|
||||
regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0);
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops meson_spifc_ops = {
|
||||
.xfer = meson_spifc_xfer,
|
||||
.set_speed = meson_spifc_set_speed,
|
||||
.set_mode = meson_spifc_set_mode,
|
||||
};
|
||||
|
||||
static int meson_spifc_probe(struct udevice *dev)
|
||||
{
|
||||
struct meson_spifc_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
meson_spifc_hw_init(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id meson_spifc_ids[] = {
|
||||
{ .compatible = "amlogic,meson-gxbb-spifc", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(meson_spifc) = {
|
||||
.name = "meson_spifc",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = meson_spifc_ids,
|
||||
.ops = &meson_spifc_ops,
|
||||
.probe = meson_spifc_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct meson_spifc_priv),
|
||||
};
|
359
drivers/spi/mtk_qspi.c
Normal file
359
drivers/spi/mtk_qspi.c
Normal file
@ -0,0 +1,359 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 MediaTek, Inc.
|
||||
* Author : Guochun.Mao@mediatek.com
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
/* Register Offset */
|
||||
struct mtk_qspi_regs {
|
||||
u32 cmd;
|
||||
u32 cnt;
|
||||
u32 rdsr;
|
||||
u32 rdata;
|
||||
u32 radr[3];
|
||||
u32 wdata;
|
||||
u32 prgdata[6];
|
||||
u32 shreg[10];
|
||||
u32 cfg[2];
|
||||
u32 shreg10;
|
||||
u32 mode_mon;
|
||||
u32 status[4];
|
||||
u32 flash_time;
|
||||
u32 flash_cfg;
|
||||
u32 reserved_0[3];
|
||||
u32 sf_time;
|
||||
u32 pp_dw_data;
|
||||
u32 reserved_1;
|
||||
u32 delsel_0[2];
|
||||
u32 intrstus;
|
||||
u32 intren;
|
||||
u32 reserved_2;
|
||||
u32 cfg3;
|
||||
u32 reserved_3;
|
||||
u32 chksum;
|
||||
u32 aaicmd;
|
||||
u32 wrprot;
|
||||
u32 radr3;
|
||||
u32 dual;
|
||||
u32 delsel_1[3];
|
||||
};
|
||||
|
||||
struct mtk_qspi_platdata {
|
||||
fdt_addr_t reg_base;
|
||||
fdt_addr_t mem_base;
|
||||
};
|
||||
|
||||
struct mtk_qspi_priv {
|
||||
struct mtk_qspi_regs *regs;
|
||||
unsigned long *mem_base;
|
||||
u8 op;
|
||||
u8 tx[3]; /* only record max 3 bytes paras, when it's address. */
|
||||
u32 txlen; /* dout buffer length - op code length */
|
||||
u8 *rx;
|
||||
u32 rxlen;
|
||||
};
|
||||
|
||||
#define MTK_QSPI_CMD_POLLINGREG_US 500000
|
||||
#define MTK_QSPI_WRBUF_SIZE 256
|
||||
#define MTK_QSPI_COMMAND_ENABLE 0x30
|
||||
|
||||
/* NOR flash controller commands */
|
||||
#define MTK_QSPI_RD_TRIGGER BIT(0)
|
||||
#define MTK_QSPI_READSTATUS BIT(1)
|
||||
#define MTK_QSPI_PRG_CMD BIT(2)
|
||||
#define MTK_QSPI_WR_TRIGGER BIT(4)
|
||||
#define MTK_QSPI_WRITESTATUS BIT(5)
|
||||
#define MTK_QSPI_AUTOINC BIT(7)
|
||||
|
||||
#define MTK_QSPI_MAX_RX_TX_SHIFT 0x6
|
||||
#define MTK_QSPI_MAX_SHIFT 0x8
|
||||
|
||||
#define MTK_QSPI_WR_BUF_ENABLE 0x1
|
||||
#define MTK_QSPI_WR_BUF_DISABLE 0x0
|
||||
|
||||
static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd)
|
||||
{
|
||||
u8 tmp;
|
||||
u8 val = cmd & ~MTK_QSPI_AUTOINC;
|
||||
|
||||
writeb(cmd, &priv->regs->cmd);
|
||||
|
||||
return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp),
|
||||
MTK_QSPI_CMD_POLLINGREG_US);
|
||||
}
|
||||
|
||||
static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv)
|
||||
{
|
||||
int len = 1 + priv->txlen + priv->rxlen;
|
||||
int i, ret, idx;
|
||||
|
||||
if (len > MTK_QSPI_MAX_SHIFT)
|
||||
return -ERR_INVAL;
|
||||
|
||||
writeb(len * 8, &priv->regs->cnt);
|
||||
|
||||
/* start at PRGDATA5, go down to PRGDATA0 */
|
||||
idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1;
|
||||
|
||||
/* opcode */
|
||||
writeb(priv->op, &priv->regs->prgdata[idx]);
|
||||
idx--;
|
||||
|
||||
/* program TX data */
|
||||
for (i = 0; i < priv->txlen; i++, idx--)
|
||||
writeb(priv->tx[i], &priv->regs->prgdata[idx]);
|
||||
|
||||
/* clear out rest of TX registers */
|
||||
while (idx >= 0) {
|
||||
writeb(0, &priv->regs->prgdata[idx]);
|
||||
idx--;
|
||||
}
|
||||
|
||||
ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* restart at first RX byte */
|
||||
idx = priv->rxlen - 1;
|
||||
|
||||
/* read out RX data */
|
||||
for (i = 0; i < priv->rxlen; i++, idx--)
|
||||
priv->rx[i] = readb(&priv->regs->shreg[idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_read(struct mtk_qspi_priv *priv,
|
||||
u32 addr, u8 *buf, u32 len)
|
||||
{
|
||||
memcpy(buf, (u8 *)priv->mem_base + addr, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
writeb(addr & 0xff, &priv->regs->radr[i]);
|
||||
addr >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv,
|
||||
u32 addr, u32 length, const u8 *data)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
mtk_qspi_set_addr(priv, addr);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
writeb(*data++, &priv->regs->wdata);
|
||||
ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr,
|
||||
const u8 *buf)
|
||||
{
|
||||
int i, data;
|
||||
|
||||
mtk_qspi_set_addr(priv, addr);
|
||||
|
||||
for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) {
|
||||
data = buf[i + 3] << 24 | buf[i + 2] << 16 |
|
||||
buf[i + 1] << 8 | buf[i];
|
||||
writel(data, &priv->regs->pp_dw_data);
|
||||
}
|
||||
|
||||
return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
|
||||
}
|
||||
|
||||
static int mtk_qspi_write(struct mtk_qspi_priv *priv,
|
||||
u32 addr, const u8 *buf, u32 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* setting pre-fetch buffer for page program */
|
||||
writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]);
|
||||
while (len >= MTK_QSPI_WRBUF_SIZE) {
|
||||
ret = mtk_qspi_write_buffer(priv, addr, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len -= MTK_QSPI_WRBUF_SIZE;
|
||||
addr += MTK_QSPI_WRBUF_SIZE;
|
||||
buf += MTK_QSPI_WRBUF_SIZE;
|
||||
}
|
||||
/* disable pre-fetch buffer for page program */
|
||||
writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]);
|
||||
|
||||
if (len)
|
||||
return mtk_qspi_write_single_byte(priv, addr, len, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_release_bus(struct udevice *dev)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
u32 bytes = DIV_ROUND_UP(bitlen, 8);
|
||||
u32 addr;
|
||||
|
||||
if (!bytes)
|
||||
return -ERR_INVAL;
|
||||
|
||||
if (dout) {
|
||||
if (flags & SPI_XFER_BEGIN) {
|
||||
/* parse op code and potential paras first */
|
||||
priv->op = *(u8 *)dout;
|
||||
if (bytes > 1)
|
||||
memcpy(priv->tx, (u8 *)dout + 1,
|
||||
bytes <= 4 ? bytes - 1 : 3);
|
||||
priv->txlen = bytes - 1;
|
||||
}
|
||||
|
||||
if (flags == SPI_XFER_ONCE) {
|
||||
/* operations without receiving or sending data.
|
||||
* for example: erase, write flash register or write
|
||||
* enable...
|
||||
*/
|
||||
priv->rx = NULL;
|
||||
priv->rxlen = 0;
|
||||
return mtk_qspi_tx_rx(priv);
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END) {
|
||||
/* here, dout should be data to be written.
|
||||
* and priv->tx should be filled 3Bytes address.
|
||||
*/
|
||||
addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
|
||||
priv->tx[2];
|
||||
return mtk_qspi_write(priv, addr, (u8 *)dout, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
if (din) {
|
||||
if (priv->txlen >= 3) {
|
||||
/* if run to here, priv->tx[] should be the address
|
||||
* where read data from,
|
||||
* and, din is the buf to receive data.
|
||||
*/
|
||||
addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
|
||||
priv->tx[2];
|
||||
return mtk_qspi_read(priv, addr, (u8 *)din, bytes);
|
||||
}
|
||||
|
||||
/* should be reading flash's register */
|
||||
priv->rx = (u8 *)din;
|
||||
priv->rxlen = bytes;
|
||||
return mtk_qspi_tx_rx(priv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct mtk_qspi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
return mtk_qspi_transfer(priv, bitlen, dout, din, flags);
|
||||
}
|
||||
|
||||
static int mtk_qspi_set_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_set_mode(struct udevice *bus, uint mode)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct resource res_reg, res_mem;
|
||||
struct mtk_qspi_platdata *plat = bus->platdata;
|
||||
int ret;
|
||||
|
||||
ret = dev_read_resource_byname(bus, "reg_base", &res_reg);
|
||||
if (ret) {
|
||||
debug("can't get reg_base resource(ret = %d)\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dev_read_resource_byname(bus, "mem_base", &res_mem);
|
||||
if (ret) {
|
||||
debug("can't get map_base resource(ret = %d)\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
plat->mem_base = res_mem.start;
|
||||
plat->reg_base = res_reg.start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_probe(struct udevice *bus)
|
||||
{
|
||||
struct mtk_qspi_platdata *plat = dev_get_platdata(bus);
|
||||
struct mtk_qspi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->regs = (struct mtk_qspi_regs *)plat->reg_base;
|
||||
priv->mem_base = (unsigned long *)plat->mem_base;
|
||||
|
||||
writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops mtk_qspi_ops = {
|
||||
.claim_bus = mtk_qspi_claim_bus,
|
||||
.release_bus = mtk_qspi_release_bus,
|
||||
.xfer = mtk_qspi_xfer,
|
||||
.set_speed = mtk_qspi_set_speed,
|
||||
.set_mode = mtk_qspi_set_mode,
|
||||
};
|
||||
|
||||
static const struct udevice_id mtk_qspi_ids[] = {
|
||||
{ .compatible = "mediatek,mt7629-qspi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mtk_qspi) = {
|
||||
.name = "mtk_qspi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = mtk_qspi_ids,
|
||||
.ops = &mtk_qspi_ops,
|
||||
.ofdata_to_platdata = mtk_qspi_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct mtk_qspi_priv),
|
||||
.probe = mtk_qspi_probe,
|
||||
};
|
@ -400,10 +400,6 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
return mxc_spi_xfer_internal(mxcs, bitlen, dout, din, flags);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Some SPI devices require active chip-select over multiple
|
||||
* transactions, we achieve this using a GPIO. Still, the SPI
|
||||
|
@ -39,10 +39,6 @@ static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
|
||||
return container_of(slave, struct mxs_spi_slave, slave);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
/* MXS SPI: 4 ports and 3 chip selects maximum */
|
||||
|
@ -461,11 +461,6 @@ static inline struct omap3_spi_priv *to_omap3_spi(struct spi_slave *slave)
|
||||
return container_of(slave, struct omap3_spi_priv, slave);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct omap3_spi_priv *priv = to_omap3_spi(slave);
|
||||
|
@ -9,16 +9,11 @@
|
||||
* Driver for ARM PL022 SPI Controller.
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/platform_data/pl022_spi.h>
|
||||
#include <fdtdec.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bug.h>
|
||||
#include <dm/platform_data/spi_pl022.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <spi.h>
|
||||
|
||||
#define SSP_CR0 0x000
|
||||
@ -72,11 +67,7 @@
|
||||
|
||||
struct pl022_spi_slave {
|
||||
void *base;
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
struct clk clk;
|
||||
#else
|
||||
unsigned int freq;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@ -96,30 +87,13 @@ static int pl022_is_supported(struct pl022_spi_slave *ps)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
static int pl022_spi_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct pl022_spi_pdata *plat = bus->platdata;
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int node = dev_of_offset(bus);
|
||||
|
||||
plat->addr = fdtdec_get_addr_size(fdt, node, "reg", &plat->size);
|
||||
|
||||
return clk_get_by_index(bus, 0, &plat->clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pl022_spi_probe(struct udevice *bus)
|
||||
{
|
||||
struct pl022_spi_pdata *plat = dev_get_platdata(bus);
|
||||
struct pl022_spi_slave *ps = dev_get_priv(bus);
|
||||
|
||||
ps->base = ioremap(plat->addr, plat->size);
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
ps->clk = plat->clk;
|
||||
#else
|
||||
ps->freq = plat->freq;
|
||||
#endif
|
||||
|
||||
/* Check the PL022 version */
|
||||
if (!pl022_is_supported(ps))
|
||||
@ -240,11 +214,7 @@ static int pl022_spi_set_speed(struct udevice *bus, uint speed)
|
||||
u16 scr = SSP_SCR_MIN, cr0 = 0, cpsr = SSP_CPSR_MIN, best_scr = scr,
|
||||
best_cpsr = cpsr;
|
||||
u32 min, max, best_freq = 0, tmp;
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
u32 rate = clk_get_rate(&ps->clk);
|
||||
#else
|
||||
u32 rate = ps->freq;
|
||||
#endif
|
||||
bool found = false;
|
||||
|
||||
max = spi_rate(rate, SSP_CPSR_MIN, SSP_SCR_MIN);
|
||||
@ -316,6 +286,25 @@ static const struct dm_spi_ops pl022_spi_ops = {
|
||||
};
|
||||
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
static int pl022_spi_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct pl022_spi_pdata *plat = bus->platdata;
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int node = dev_of_offset(bus);
|
||||
struct clk clkdev;
|
||||
int ret;
|
||||
|
||||
plat->addr = fdtdec_get_addr_size(fdt, node, "reg", &plat->size);
|
||||
|
||||
ret = clk_get_by_index(bus, 0, &clkdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
plat->freq = clk_get_rate(&clkdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id pl022_spi_ids[] = {
|
||||
{ .compatible = "arm,pl022-spi" },
|
||||
{ }
|
||||
@ -327,11 +316,9 @@ U_BOOT_DRIVER(pl022_spi) = {
|
||||
.id = UCLASS_SPI,
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
.of_match = pl022_spi_ids,
|
||||
#endif
|
||||
.ops = &pl022_spi_ops,
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
.ofdata_to_platdata = pl022_spi_ofdata_to_platdata,
|
||||
#endif
|
||||
.ops = &pl022_spi_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct pl022_spi_pdata),
|
||||
.priv_auto_alloc_size = sizeof(struct pl022_spi_slave),
|
||||
.probe = pl022_spi_probe,
|
||||
|
@ -247,11 +247,6 @@ void spi_cs_deactivate(struct spi_slave *slave)
|
||||
sh_qspi_cs_deactivate(ss);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
|
@ -66,10 +66,6 @@ static int write_fifo_empty_wait(struct sh_spi *ss)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void sh_spi_set_cs(struct sh_spi *ss, unsigned int cs)
|
||||
{
|
||||
unsigned long val = 0;
|
||||
|
@ -36,13 +36,6 @@ static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave)
|
||||
/* Public Functions */
|
||||
/*=====================================================================*/
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
void spi_init (void)
|
||||
{
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
|
@ -126,8 +126,6 @@ int atmel_df_pow2(int argc, char * const argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
spi_init();
|
||||
|
||||
while (1) {
|
||||
struct spi_slave *slave;
|
||||
char *line, *p;
|
||||
|
@ -50,11 +50,9 @@
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_CMD_SPI) || defined(CONFIG_DM_SPI)
|
||||
EXPORT_FUNC(dummy, void, spi_init, void)
|
||||
EXPORT_FUNC(dummy, void, spi_setup_slave, void)
|
||||
EXPORT_FUNC(dummy, void, spi_free_slave, void)
|
||||
#else
|
||||
EXPORT_FUNC(spi_init, void, spi_init, void)
|
||||
EXPORT_FUNC(spi_setup_slave, struct spi_slave *, spi_setup_slave,
|
||||
unsigned int, unsigned int, unsigned int, unsigned int)
|
||||
EXPORT_FUNC(spi_free_slave, void, spi_free_slave, struct spi_slave *)
|
||||
|
@ -287,13 +287,6 @@ int eeprom_write (unsigned dev_addr, unsigned offset, uchar *buffer, unsigned c
|
||||
# define CONFIG_SYS_DEF_EEPROM_ADDR CONFIG_SYS_I2C_EEPROM_ADDR
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MPC8XX_SPI)
|
||||
extern void spi_init_f (void);
|
||||
extern void spi_init_r (void);
|
||||
extern ssize_t spi_read (uchar *, int, uchar *, int);
|
||||
extern ssize_t spi_write (uchar *, int, uchar *, int);
|
||||
#endif
|
||||
|
||||
/* $(BOARD)/$(BOARD).c */
|
||||
int board_early_init_f (void);
|
||||
int board_fix_fdt (void *rw_fdt_blob); /* manipulate the U-Boot fdt before its relocation */
|
||||
|
@ -102,7 +102,6 @@
|
||||
|
||||
/* DSPI and Serial Flash */
|
||||
#define CONFIG_CF_DSPI
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_SYS_SBFHDR_SIZE 0x7
|
||||
#ifdef CONFIG_CMD_SPI
|
||||
# define CONFIG_SYS_DSPI_CS2
|
||||
|
@ -151,7 +151,6 @@
|
||||
/* DSPI and Serial Flash */
|
||||
#define CONFIG_CF_DSPI
|
||||
#define CONFIG_SERIAL_FLASH
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_SYS_SBFHDR_SIZE 0x7
|
||||
#ifdef CONFIG_CMD_SPI
|
||||
|
||||
|
@ -116,7 +116,6 @@
|
||||
/* DSPI and Serial Flash */
|
||||
#define CONFIG_CF_DSPI
|
||||
#define CONFIG_SERIAL_FLASH
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_SYS_SBFHDR_SIZE 0x7
|
||||
#ifdef CONFIG_CMD_SPI
|
||||
|
||||
|
@ -142,7 +142,6 @@
|
||||
|
||||
/* DSPI and Serial Flash */
|
||||
#define CONFIG_CF_DSPI
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_SYS_SBFHDR_SIZE 0x13
|
||||
#ifdef CONFIG_CMD_SPI
|
||||
|
||||
|
@ -370,11 +370,6 @@
|
||||
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1
|
||||
#define CONFIG_SYS_EEPROM_BUS_NUM 1
|
||||
|
||||
/*
|
||||
* eSPI - Enhanced SPI
|
||||
*/
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
#if defined(CONFIG_SPI_FLASH)
|
||||
#define CONFIG_SF_DEFAULT_SPEED 10000000
|
||||
#define CONFIG_SF_DEFAULT_MODE 0
|
||||
|
@ -386,12 +386,6 @@
|
||||
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1
|
||||
#define CONFIG_SYS_EEPROM_BUS_NUM 1
|
||||
|
||||
/*
|
||||
* eSPI - Enhanced SPI
|
||||
*/
|
||||
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
#define CONFIG_SF_DEFAULT_SPEED 10000000
|
||||
#define CONFIG_SF_DEFAULT_MODE 0
|
||||
|
||||
|
@ -287,11 +287,6 @@
|
||||
#define CONFIG_SYS_I2C_NCT72_ADDR 0x4C
|
||||
#define CONFIG_SYS_I2C_IDT6V49205B 0x69
|
||||
|
||||
/*
|
||||
* eSPI - Enhanced SPI
|
||||
*/
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
#define CONFIG_SF_DEFAULT_SPEED 10000000
|
||||
#define CONFIG_SF_DEFAULT_MODE SPI_MODE_0
|
||||
|
||||
|
@ -182,10 +182,6 @@
|
||||
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2
|
||||
|
||||
#ifndef CONFIG_TRAILBLAZER
|
||||
/*
|
||||
* eSPI - Enhanced SPI
|
||||
*/
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
#define CONFIG_SF_DEFAULT_SPEED 10000000
|
||||
#define CONFIG_SF_DEFAULT_MODE 0
|
||||
|
@ -159,7 +159,6 @@
|
||||
*/
|
||||
#define CONFIG_TSEC1
|
||||
#define CONFIG_TSEC2
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
/*
|
||||
* NOR FLASH setup
|
||||
@ -273,15 +272,6 @@
|
||||
#define CONFIG_RTC_PCF8563
|
||||
#define CONFIG_SYS_I2C_RTC_ADDR 0x51
|
||||
|
||||
/*
|
||||
* SPI setup
|
||||
*/
|
||||
#ifdef CONFIG_HARD_SPI
|
||||
#define CONFIG_SYS_GPIO1_PRELIM
|
||||
#define CONFIG_SYS_GPIO1_DIR 0x00000001
|
||||
#define CONFIG_SYS_GPIO1_DAT 0x00000001
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Ethernet setup
|
||||
*/
|
||||
|
@ -43,7 +43,6 @@
|
||||
#define CONFIG_MXC_UART
|
||||
#define CONFIG_MXC_UART_BASE UART1_BASE
|
||||
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_DEFAULT_SPI_BUS 1
|
||||
#define CONFIG_DEFAULT_SPI_MODE (SPI_MODE_0 | SPI_CS_HIGH)
|
||||
|
||||
|
@ -143,7 +143,6 @@
|
||||
|
||||
/* SPI */
|
||||
#ifdef CONFIG_CMD_SPI
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_SPI_HALF_DUPLEX
|
||||
#endif
|
||||
|
||||
|
@ -576,11 +576,6 @@
|
||||
#define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3
|
||||
#define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
|
||||
|
||||
/*
|
||||
* eSPI - Enhanced SPI
|
||||
*/
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
#if defined(CONFIG_SPI_FLASH)
|
||||
#define CONFIG_SF_DEFAULT_SPEED 10000000
|
||||
#define CONFIG_SF_DEFAULT_MODE 0
|
||||
|
@ -214,11 +214,6 @@ extern unsigned long get_board_sys_clk(unsigned long dummy);
|
||||
#define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3
|
||||
#define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
|
||||
|
||||
/*
|
||||
* eSPI - Enhanced SPI
|
||||
*/
|
||||
#define CONFIG_HARD_SPI
|
||||
|
||||
#if defined(CONFIG_PCI)
|
||||
/*
|
||||
* General PCI
|
||||
|
@ -66,7 +66,6 @@
|
||||
#define CONFIG_CF_DSPI
|
||||
#define CONFIG_SF_DEFAULT_SPEED 50000000
|
||||
#define CONFIG_SERIAL_FLASH
|
||||
#define CONFIG_HARD_SPI
|
||||
#define CONFIG_ENV_SPI_BUS 0
|
||||
#define CONFIG_ENV_SPI_CS 1
|
||||
|
||||
|
@ -42,11 +42,6 @@
|
||||
#define CONFIG_MXC_UART
|
||||
#define CONFIG_MXC_UART_BASE UART1_BASE
|
||||
|
||||
/*
|
||||
* SPI Configs
|
||||
* */
|
||||
#define CONFIG_HARD_SPI /* puts SPI: ready */
|
||||
|
||||
/*
|
||||
* MMC Configs
|
||||
* */
|
||||
|
@ -7,22 +7,15 @@
|
||||
* in ofdata_to_platdata.
|
||||
*/
|
||||
|
||||
#ifndef __PL022_SPI_H__
|
||||
#define __PL022_SPI_H__
|
||||
#ifndef __spi_pl022_h
|
||||
#define __spi_pl022_h
|
||||
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
#include <clk.h>
|
||||
#endif
|
||||
#include <fdtdec.h>
|
||||
|
||||
struct pl022_spi_pdata {
|
||||
fdt_addr_t addr;
|
||||
fdt_size_t size;
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
struct clk clk;
|
||||
#else
|
||||
unsigned int freq;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif /* __spi_pl022_h */
|
@ -366,6 +366,8 @@ static inline bool mtd_has_partitions(const struct mtd_info *mtd)
|
||||
return !list_empty(&mtd->partitions);
|
||||
}
|
||||
|
||||
bool mtd_partitions_used(struct mtd_info *master);
|
||||
|
||||
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobecc);
|
||||
int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
|
||||
@ -562,8 +564,23 @@ unsigned mtd_mmap_capabilities(struct mtd_info *mtd);
|
||||
/* drivers/mtd/mtdcore.h */
|
||||
int add_mtd_device(struct mtd_info *mtd);
|
||||
int del_mtd_device(struct mtd_info *mtd);
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
|
||||
int del_mtd_partitions(struct mtd_info *);
|
||||
#else
|
||||
static inline int add_mtd_partitions(struct mtd_info *mtd,
|
||||
const struct mtd_partition *parts,
|
||||
int nparts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int del_mtd_partitions(struct mtd_info *mtd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct mtd_info *__mtd_next_device(int i);
|
||||
#define mtd_for_each_device(mtd) \
|
||||
@ -581,6 +598,7 @@ int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
|
||||
void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
|
||||
const uint64_t length, uint64_t *len_incl_bad,
|
||||
int *truncated);
|
||||
bool mtd_dev_list_updated(void);
|
||||
|
||||
/* drivers/mtd/mtd_uboot.c */
|
||||
int mtd_search_alternate_name(const char *mtdname, char *altname,
|
||||
|
@ -239,6 +239,44 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
|
||||
#define regmap_get(map, type, member, valp) \
|
||||
regmap_range_get(map, 0, type, member, valp)
|
||||
|
||||
/**
|
||||
* regmap_read_poll_timeout - Poll until a condition is met or a timeout occurs
|
||||
*
|
||||
* @map: Regmap to read from
|
||||
* @addr: Offset to poll
|
||||
* @val: Unsigned integer variable to read the value into
|
||||
* @cond: Break condition (usually involving @val)
|
||||
* @sleep_us: Maximum time to sleep between reads in us (0 tight-loops).
|
||||
* @timeout_ms: Timeout in ms, 0 means never timeout
|
||||
*
|
||||
* Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_read
|
||||
* error return value in case of a error read. In the two former cases,
|
||||
* the last read value at @addr is stored in @val. Must not be called
|
||||
* from atomic context if sleep_us or timeout_us are used.
|
||||
*
|
||||
* This is modelled after the regmap_read_poll_timeout macros in linux but
|
||||
* with millisecond timeout.
|
||||
*/
|
||||
#define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_ms) \
|
||||
({ \
|
||||
unsigned long __start = get_timer(0); \
|
||||
int __ret; \
|
||||
for (;;) { \
|
||||
__ret = regmap_read((map), (addr), &(val)); \
|
||||
if (__ret) \
|
||||
break; \
|
||||
if (cond) \
|
||||
break; \
|
||||
if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \
|
||||
__ret = regmap_read((map), (addr), &(val)); \
|
||||
break; \
|
||||
} \
|
||||
if ((sleep_us)) \
|
||||
udelay((sleep_us)); \
|
||||
} \
|
||||
__ret ?: ((cond) ? 0 : -ETIMEDOUT); \
|
||||
})
|
||||
|
||||
/**
|
||||
* regmap_update_bits() - Perform a read/modify/write using a mask
|
||||
*
|
||||
|
@ -117,13 +117,6 @@ struct spi_slave {
|
||||
#define SPI_XFER_MMAP_END BIT(3) /* Memory Mapped End */
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialization, must be called once on start up.
|
||||
*
|
||||
* TODO: I don't think we really need this.
|
||||
*/
|
||||
void spi_init(void);
|
||||
|
||||
/**
|
||||
* spi_do_alloc_slave - Allocate a new SPI slave (internal)
|
||||
*
|
||||
|
@ -750,7 +750,6 @@ CONFIG_G_DNL_UMS_PRODUCT_NUM
|
||||
CONFIG_G_DNL_UMS_VENDOR_NUM
|
||||
CONFIG_H264_FREQ
|
||||
CONFIG_H8300
|
||||
CONFIG_HARD_SPI
|
||||
CONFIG_HAS_ETH0
|
||||
CONFIG_HAS_ETH1
|
||||
CONFIG_HAS_ETH2
|
||||
|
@ -144,3 +144,29 @@ static int dm_test_regmap_getset(struct unit_test_state *uts)
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_regmap_getset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Read polling test */
|
||||
static int dm_test_regmap_poll(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct regmap *map;
|
||||
uint reg;
|
||||
unsigned long start;
|
||||
|
||||
ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
|
||||
map = syscon_get_regmap(dev);
|
||||
ut_assertok_ptr(map);
|
||||
|
||||
start = get_timer(0);
|
||||
|
||||
ut_asserteq(-ETIMEDOUT,
|
||||
regmap_read_poll_timeout(map, 0, reg,
|
||||
(reg == 0xcacafafa),
|
||||
1, 5 * CONFIG_SYS_HZ));
|
||||
|
||||
ut_assert(get_timer(start) > (5 * CONFIG_SYS_HZ));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_regmap_poll, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
Loading…
Reference in New Issue
Block a user