diff options
author | Tom Rini <trini@konsulko.com> | 2018-12-05 15:06:24 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-12-05 15:06:24 -0500 |
commit | 9450ab2ba8d720bd9f73bccc0af2e2b5a2c2aaf1 (patch) | |
tree | b0566746ed9a8dd261e61113c19cf0a234a6417b /drivers | |
parent | a77a8fde7bb850178c2e4ad3db354b536114ea32 (diff) | |
parent | 08898e8b22d74a4511eadee9b06b11aab43e809c (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.c | 185 | ||||
-rw-r--r-- | drivers/mtd/mtdcore.c | 23 | ||||
-rw-r--r-- | drivers/mtd/mtdpart.c | 12 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/pxa3xx_nand.c | 143 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_mtd.c | 48 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_probe.c | 9 | ||||
-rw-r--r-- | drivers/net/e1000_spi.c | 3 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 27 | ||||
-rw-r--r-- | drivers/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.c | 5 | ||||
-rw-r--r-- | drivers/spi/davinci_spi.c | 5 | ||||
-rw-r--r-- | drivers/spi/fsl_dspi.c | 5 | ||||
-rw-r--r-- | drivers/spi/fsl_espi.c | 5 | ||||
-rw-r--r-- | drivers/spi/lpc32xx_ssp.c | 9 | ||||
-rw-r--r-- | drivers/spi/meson_spifc.c | 320 | ||||
-rw-r--r-- | drivers/spi/mtk_qspi.c | 359 | ||||
-rw-r--r-- | drivers/spi/mxc_spi.c | 4 | ||||
-rw-r--r-- | drivers/spi/mxs_spi.c | 4 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.c | 5 | ||||
-rw-r--r-- | drivers/spi/pl022_spi.c | 55 | ||||
-rw-r--r-- | drivers/spi/sh_qspi.c | 5 | ||||
-rw-r--r-- | drivers/spi/sh_spi.c | 4 | ||||
-rw-r--r-- | drivers/spi/soft_spi_legacy.c | 7 |
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) { |