summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2018-12-05 15:06:24 -0500
committerTom Rini <trini@konsulko.com>2018-12-05 15:06:24 -0500
commit9450ab2ba8d720bd9f73bccc0af2e2b5a2c2aaf1 (patch)
treeb0566746ed9a8dd261e61113c19cf0a234a6417b /drivers
parenta77a8fde7bb850178c2e4ad3db354b536114ea32 (diff)
parent08898e8b22d74a4511eadee9b06b11aab43e809c (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/mtd_uboot.c185
-rw-r--r--drivers/mtd/mtdcore.c23
-rw-r--r--drivers/mtd/mtdpart.c12
-rw-r--r--drivers/mtd/nand/raw/pxa3xx_nand.c143
-rw-r--r--drivers/mtd/spi/sf_mtd.c48
-rw-r--r--drivers/mtd/spi/sf_probe.c9
-rw-r--r--drivers/net/e1000_spi.c3
-rw-r--r--drivers/spi/Kconfig27
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/atmel_spi.c5
-rw-r--r--drivers/spi/davinci_spi.c5
-rw-r--r--drivers/spi/fsl_dspi.c5
-rw-r--r--drivers/spi/fsl_espi.c5
-rw-r--r--drivers/spi/lpc32xx_ssp.c9
-rw-r--r--drivers/spi/meson_spifc.c320
-rw-r--r--drivers/spi/mtk_qspi.c359
-rw-r--r--drivers/spi/mxc_spi.c4
-rw-r--r--drivers/spi/mxs_spi.c4
-rw-r--r--drivers/spi/omap3_spi.c5
-rw-r--r--drivers/spi/pl022_spi.c55
-rw-r--r--drivers/spi/sh_qspi.c5
-rw-r--r--drivers/spi/sh_spi.c4
-rw-r--r--drivers/spi/soft_spi_legacy.c7
23 files changed, 1062 insertions, 182 deletions
diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c
index 5ca560c968..d638f700d0 100644
--- a/drivers/mtd/mtd_uboot.c
+++ b/drivers/mtd/mtd_uboot.c
@@ -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,15 +302,23 @@ 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
* return the number of partition structures in the array.
@@ -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
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index fb6c779abb..cb7ca38d07 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -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) {
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 4d2ac8107f..fd8d8e5ea7 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -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.
diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c
index 4c783f1e1e..4d2712df4c 100644
--- a/drivers/mtd/nand/raw/pxa3xx_nand.c
+++ b/drivers/mtd/nand/raw/pxa3xx_nand.c
@@ -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;
diff --git a/drivers/mtd/spi/sf_mtd.c b/drivers/mtd/spi/sf_mtd.c
index 58d7e44399..68c36002be 100644
--- a/drivers/mtd/spi/sf_mtd.c
+++ b/drivers/mtd/spi/sf_mtd.c
@@ -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);
}
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 5a2e932de8..00f8558e70 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -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,
};
diff --git a/drivers/net/e1000_spi.c b/drivers/net/e1000_spi.c
index b38f4df9f3..aecd290d72 100644
--- a/drivers/net/e1000_spi.c
+++ b/drivers/net/e1000_spi.c
@@ -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)
{
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 516188ea88..a7bb5b35c2 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -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
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7242ea7e40..392a925795 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -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
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 1db8bbef2b..cf4de9ee1a 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -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)
{
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c
index 07fa5e3b8a..4d2c106440 100644
--- a/drivers/spi/davinci_spi.c
+++ b/drivers/spi/davinci_spi.c
@@ -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)
{
diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c
index f7ed8fbe08..764c94215e 100644
--- a/drivers/spi/fsl_dspi.c
+++ b/drivers/spi/fsl_dspi.c
@@ -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)))
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index e9941593f5..7444ae1a06 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -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);
diff --git a/drivers/spi/lpc32xx_ssp.c b/drivers/spi/lpc32xx_ssp.c
index ce12eee657..4b09366317 100644
--- a/drivers/spi/lpc32xx_ssp.c
+++ b/drivers/spi/lpc32xx_ssp.c
@@ -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)
diff --git a/drivers/spi/meson_spifc.c b/drivers/spi/meson_spifc.c
new file mode 100644
index 0000000000..3d551694cb
--- /dev/null
+++ b/drivers/spi/meson_spifc.c
@@ -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),
+};
diff --git a/drivers/spi/mtk_qspi.c b/drivers/spi/mtk_qspi.c
new file mode 100644
index 0000000000..b510733e92
--- /dev/null
+++ b/drivers/spi/mtk_qspi.c
@@ -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,
+};
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index 0dccc38b82..b2636909ce 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -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
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 006fe8281c..5065e407f8 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -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 */
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index ecf54bb714..c7fcf050a5 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -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);
diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c
index 86b71d2e21..32bb8c8d21 100644
--- a/drivers/spi/pl022_spi.c
+++ b/drivers/spi/pl022_spi.c
@@ -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,
diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c
index 64dfd748d6..5ae203d8d4 100644
--- a/drivers/spi/sh_qspi.c
+++ b/drivers/spi/sh_qspi.c
@@ -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)
{
diff --git a/drivers/spi/sh_spi.c b/drivers/spi/sh_spi.c
index bc2bd638e6..c58fd0ebc4 100644
--- a/drivers/spi/sh_spi.c
+++ b/drivers/spi/sh_spi.c
@@ -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;
diff --git a/drivers/spi/soft_spi_legacy.c b/drivers/spi/soft_spi_legacy.c
index 0aac0c065d..cc5ab5f991 100644
--- a/drivers/spi/soft_spi_legacy.c
+++ b/drivers/spi/soft_spi_legacy.c
@@ -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)
{