diff options
Diffstat (limited to 'drivers')
52 files changed, 3862 insertions, 2587 deletions
diff --git a/drivers/block/sym53c8xx.c b/drivers/block/sym53c8xx.c index c538e37..6f1ac85 100644 --- a/drivers/block/sym53c8xx.c +++ b/drivers/block/sym53c8xx.c @@ -248,7 +248,7 @@ void scsi_print_error (ccb * pccb) /****************************************************************************** * sets-up the SCSI controller - * the base memory address is retrived via the pci_read_config_dword + * the base memory address is retrieved via the pci_read_config_dword */ void scsi_low_level_init(int busdevfunc) { diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index fca370a..de9e44e 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -12,6 +12,7 @@ LIB = $(obj)libdfu.o COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o COBJS-$(CONFIG_DFU_NAND) += dfu_nand.o +COBJS-$(CONFIG_DFU_RAM) += dfu_ram.o SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index d73d510..56b21c7 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -41,6 +41,29 @@ static int dfu_find_alt_num(const char *s) return ++i; } +int dfu_init_env_entities(char *interface, int dev) +{ + const char *str_env; + char *env_bkp; + int ret; + + str_env = getenv("dfu_alt_info"); + if (!str_env) { + error("\"dfu_alt_info\" env variable not defined!\n"); + return -EINVAL; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, interface, dev); + if (ret) { + error("DFU entities configuration failed!\n"); + return ret; + } + + free(env_bkp); + return 0; +} + static unsigned char *dfu_buf; static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; @@ -153,8 +176,8 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) /* we should be in buffer now (if not then size too large) */ if ((dfu->i_buf + size) > dfu->i_buf_end) { - printf("%s: Wrong size! [%d] [%d] - %d\n", - __func__, dfu->i_blk_seq_num, blk_seq_num, size); + error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, + size, dfu->i_buf_end); return -1; } @@ -325,6 +348,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, } else if (strcmp(interface, "nand") == 0) { if (dfu_fill_entity_nand(dfu, s)) return -1; + } else if (strcmp(interface, "ram") == 0) { + if (dfu_fill_entity_ram(dfu, s)) + return -1; } else { printf("%s: Device %s not (yet) supported!\n", __func__, interface); @@ -374,14 +400,14 @@ int dfu_config_entities(char *env, char *interface, int num) const char *dfu_get_dev_type(enum dfu_device_type t) { - const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" }; + const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" }; return dev_t[t]; } const char *dfu_get_layout(enum dfu_layout l) { const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", - "EXT3", "EXT4" }; + "EXT3", "EXT4", "RAM_ADDR" }; return dfu_layout[l]; } diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 0871a77..f942758 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -13,16 +13,11 @@ #include <div64.h> #include <dfu.h> -enum dfu_mmc_op { - DFU_OP_READ = 1, - DFU_OP_WRITE, -}; - static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; static long dfu_file_buf_len; -static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, +static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) { char cmd_buf[DFU_CMD_BUF_SIZE]; @@ -65,7 +60,7 @@ static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) return 0; } -static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, +static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, void *buf, long *len) { char cmd_buf[DFU_CMD_BUF_SIZE]; diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c index 0ec12cf..edbf5a9 100644 --- a/drivers/dfu/dfu_nand.c +++ b/drivers/dfu/dfu_nand.c @@ -19,12 +19,7 @@ #include <jffs2/load_kernel.h> #include <nand.h> -enum dfu_nand_op { - DFU_OP_READ = 1, - DFU_OP_WRITE, -}; - -static int nand_block_op(enum dfu_nand_op op, struct dfu_entity *dfu, +static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) { loff_t start, lim; diff --git a/drivers/dfu/dfu_ram.c b/drivers/dfu/dfu_ram.c new file mode 100644 index 0000000..335a8e1 --- /dev/null +++ b/drivers/dfu/dfu_ram.c @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2013 + * Afzal Mohammed <afzal.mohd.ma@gmail.com> + * + * Reference: dfu_mmc.c + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <errno.h> +#include <dfu.h> + +static int dfu_transfer_medium_ram(enum dfu_op op, struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + if (dfu->layout != DFU_RAM_ADDR) { + error("unsupported layout: %s\n", dfu_get_layout(dfu->layout)); + return -EINVAL; + } + + if (offset > dfu->data.ram.size) { + error("request exceeds allowed area\n"); + return -EINVAL; + } + + if (op == DFU_OP_WRITE) + memcpy(dfu->data.ram.start + offset, buf, *len); + else + memcpy(buf, dfu->data.ram.start + offset, *len); + + return 0; +} + +static int dfu_write_medium_ram(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + return dfu_transfer_medium_ram(DFU_OP_WRITE, dfu, offset, buf, len); +} + +static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + if (!*len) { + *len = dfu->data.ram.size; + return 0; + } + + return dfu_transfer_medium_ram(DFU_OP_READ, dfu, offset, buf, len); +} + +int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s) +{ + char *st; + + dfu->dev_type = DFU_DEV_RAM; + st = strsep(&s, " "); + if (strcmp(st, "ram")) { + error("unsupported device: %s\n", st); + return -ENODEV; + } + + dfu->layout = DFU_RAM_ADDR; + dfu->data.ram.start = (void *)simple_strtoul(s, &s, 16); + s++; + dfu->data.ram.size = simple_strtoul(s, &s, 16); + + dfu->write_medium = dfu_write_medium_ram; + dfu->read_medium = dfu_read_medium_ram; + + dfu->inited = 0; + + return 0; +} diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index bedf833..06280d1 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -34,6 +34,8 @@ COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o COBJS-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o ifdef CONFIG_SPL_BUILD COBJS-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o +else +COBJS-$(CONFIG_GENERIC_MMC) += mmc_write.o endif COBJS := $(COBJS-y) diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a82ee17..9a803a0 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -41,12 +41,11 @@ static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, } static void dwmci_prepare_data(struct dwmci_host *host, - struct mmc_data *data) + struct mmc_data *data, struct dwmci_idmac *cur_idmac) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; ulong data_start, data_end, start_addr; - ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, data->blocks); blk_cnt = data->blocks; @@ -73,7 +72,7 @@ static void dwmci_prepare_data(struct dwmci_host *host, dwmci_set_idma_desc(cur_idmac, flags, cnt, start_addr + (i * PAGE_SIZE)); - if(blk_cnt < 8) + if (blk_cnt <= 8) break; blk_cnt -= 8; cur_idmac++; @@ -111,6 +110,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, + data ? DIV_ROUND_UP(data->blocks, 8) : 0); int flags = 0, i; unsigned int timeout = 100000; u32 retry = 10000; @@ -127,7 +128,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); if (data) - dwmci_prepare_data(host, data); + dwmci_prepare_data(host, data, cur_idmac); dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5502675..84dae4d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -15,6 +15,7 @@ #include <malloc.h> #include <linux/list.h> #include <div64.h> +#include "mmc_private.h" /* Set block count limit because of 16 bit register limit on some hardware*/ #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT @@ -52,14 +53,10 @@ int __board_mmc_getcd(struct mmc *mmc) { int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, alias("__board_mmc_getcd"))); -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - struct mmc_data backup; int ret; - memset(&backup, 0, sizeof(backup)); - #ifdef CONFIG_MMC_TRACE int i; u8 *ptr; @@ -114,7 +111,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; } -static int mmc_send_status(struct mmc *mmc, int timeout) +int mmc_send_status(struct mmc *mmc, int timeout) { struct mmc_cmd cmd; int err, retries = 5; @@ -135,8 +132,10 @@ static int mmc_send_status(struct mmc *mmc, int timeout) MMC_STATE_PRG) break; else if (cmd.response[0] & MMC_STATUS_MASK) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Status Error: 0x%08X\n", cmd.response[0]); +#endif return COMM_ERR; } } else if (--retries < 0) @@ -151,14 +150,16 @@ static int mmc_send_status(struct mmc *mmc, int timeout) printf("CURR STATE:%d\n", status); #endif if (timeout <= 0) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Timeout waiting card ready\n"); +#endif return TIMEOUT; } return 0; } -static int mmc_set_blocklen(struct mmc *mmc, int len) +int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; @@ -181,179 +182,13 @@ struct mmc *find_mmc_device(int dev_num) return m; } +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC Device %d not found\n", dev_num); +#endif return NULL; } -static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) -{ - struct mmc_cmd cmd; - ulong end; - int err, start_cmd, end_cmd; - - if (mmc->high_capacity) - end = start + blkcnt - 1; - else { - end = (start + blkcnt - 1) * mmc->write_bl_len; - start *= mmc->write_bl_len; - } - - if (IS_SD(mmc)) { - start_cmd = SD_CMD_ERASE_WR_BLK_START; - end_cmd = SD_CMD_ERASE_WR_BLK_END; - } else { - start_cmd = MMC_CMD_ERASE_GROUP_START; - end_cmd = MMC_CMD_ERASE_GROUP_END; - } - - cmd.cmdidx = start_cmd; - cmd.cmdarg = start; - cmd.resp_type = MMC_RSP_R1; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - cmd.cmdidx = end_cmd; - cmd.cmdarg = end; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - cmd.cmdidx = MMC_CMD_ERASE; - cmd.cmdarg = SECURE_ERASE; - cmd.resp_type = MMC_RSP_R1b; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - return 0; - -err_out: - puts("mmc erase failed\n"); - return err; -} - -static unsigned long -mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) -{ - int err = 0; - struct mmc *mmc = find_mmc_device(dev_num); - lbaint_t blk = 0, blk_r = 0; - int timeout = 1000; - - if (!mmc) - return -1; - - if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) - printf("\n\nCaution! Your devices Erase group is 0x%x\n" - "The erase range would be change to " - "0x" LBAF "~0x" LBAF "\n\n", - mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), - ((start + blkcnt + mmc->erase_grp_size) - & ~(mmc->erase_grp_size - 1)) - 1); - - while (blk < blkcnt) { - blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? - mmc->erase_grp_size : (blkcnt - blk); - err = mmc_erase_t(mmc, start + blk, blk_r); - if (err) - break; - - blk += blk_r; - - /* Waiting for the ready status */ - if (mmc_send_status(mmc, timeout)) - return 0; - } - - return blk; -} - -static ulong -mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*src) -{ - struct mmc_cmd cmd; - struct mmc_data data; - int timeout = 1000; - - if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", - start + blkcnt, mmc->block_dev.lba); - return 0; - } - - if (blkcnt == 0) - return 0; - else if (blkcnt == 1) - cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; - else - cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; - - if (mmc->high_capacity) - cmd.cmdarg = start; - else - cmd.cmdarg = start * mmc->write_bl_len; - - cmd.resp_type = MMC_RSP_R1; - - data.src = src; - data.blocks = blkcnt; - data.blocksize = mmc->write_bl_len; - data.flags = MMC_DATA_WRITE; - - if (mmc_send_cmd(mmc, &cmd, &data)) { - printf("mmc write failed\n"); - return 0; - } - - /* SPI multiblock writes terminate using a special - * token, not a STOP_TRANSMISSION request. - */ - if (!mmc_host_is_spi(mmc) && blkcnt > 1) { - cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; - cmd.cmdarg = 0; - cmd.resp_type = MMC_RSP_R1b; - if (mmc_send_cmd(mmc, &cmd, NULL)) { - printf("mmc fail to send stop cmd\n"); - return 0; - } - } - - /* Waiting for the ready status */ - if (mmc_send_status(mmc, timeout)) - return 0; - - return blkcnt; -} - -static ulong -mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void*src) -{ - lbaint_t cur, blocks_todo = blkcnt; - - struct mmc *mmc = find_mmc_device(dev_num); - if (!mmc) - return 0; - - if (mmc_set_blocklen(mmc, mmc->write_bl_len)) - return 0; - - do { - cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; - if(mmc_write_blocks(mmc, start, cur, src) != cur) - return 0; - blocks_todo -= cur; - start += cur; - src += cur * mmc->write_bl_len; - } while (blocks_todo > 0); - - return blkcnt; -} - static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { @@ -385,7 +220,9 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("mmc fail to send stop cmd\n"); +#endif return 0; } } @@ -405,8 +242,10 @@ static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) return 0; if ((start + blkcnt) > mmc->block_dev.lba) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); +#endif return 0; } @@ -1268,6 +1107,7 @@ static int mmc_startup(struct mmc *mmc) mmc->block_dev.blksz = mmc->read_bl_len; mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz); mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), (mmc->cid[3] >> 16) & 0xffff); @@ -1277,6 +1117,11 @@ static int mmc_startup(struct mmc *mmc) (mmc->cid[2] >> 24) & 0xff); sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, (mmc->cid[2] >> 16) & 0xf); +#else + mmc->block_dev.vendor[0] = 0; + mmc->block_dev.product[0] = 0; + mmc->block_dev.revision[0] = 0; +#endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) init_part(&mmc->block_dev); #endif @@ -1343,7 +1188,9 @@ int mmc_start_init(struct mmc *mmc) if (mmc_getcd(mmc) == 0) { mmc->has_init = 0; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC: no card present\n"); +#endif return NO_CARD_ERR; } @@ -1378,7 +1225,9 @@ int mmc_start_init(struct mmc *mmc) err = mmc_send_op_cond(mmc); if (err && err != IN_PROGRESS) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Card did not respond to voltage select!\n"); +#endif return UNUSABLE_ERR; } } @@ -1434,6 +1283,8 @@ static int __def_mmc_init(bd_t *bis) int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + void print_mmc_devices(char separator) { struct mmc *m; @@ -1451,6 +1302,10 @@ void print_mmc_devices(char separator) printf("\n"); } +#else +void print_mmc_devices(char separator) { } +#endif + int get_mmc_num(void) { return cur_dev_num; diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h new file mode 100644 index 0000000..16dcf9f --- /dev/null +++ b/drivers/mmc/mmc_private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008,2010 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based (loosely) on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MMC_PRIVATE_H_ +#define _MMC_PRIVATE_H_ + +#include <mmc.h> + +extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data); +extern int mmc_send_status(struct mmc *mmc, int timeout); +extern int mmc_set_blocklen(struct mmc *mmc, int len); + +#ifndef CONFIG_SPL_BUILD + +extern unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt); + +extern ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src); + +#else /* CONFIG_SPL_BUILD */ + +/* SPL will never write or erase, declare dummies to reduce code size. */ + +static inline unsigned long mmc_berase(int dev_num, lbaint_t start, + lbaint_t blkcnt) +{ + return 0; +} + +static inline ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src) +{ + return 0; +} + +#endif /* CONFIG_SPL_BUILD */ + +#endif /* _MMC_PRIVATE_H_ */ diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c new file mode 100644 index 0000000..aa2fdef --- /dev/null +++ b/drivers/mmc/mmc_write.c @@ -0,0 +1,179 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <part.h> +#include "mmc_private.h" + +static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) +{ + struct mmc_cmd cmd; + ulong end; + int err, start_cmd, end_cmd; + + if (mmc->high_capacity) { + end = start + blkcnt - 1; + } else { + end = (start + blkcnt - 1) * mmc->write_bl_len; + start *= mmc->write_bl_len; + } + + if (IS_SD(mmc)) { + start_cmd = SD_CMD_ERASE_WR_BLK_START; + end_cmd = SD_CMD_ERASE_WR_BLK_END; + } else { + start_cmd = MMC_CMD_ERASE_GROUP_START; + end_cmd = MMC_CMD_ERASE_GROUP_END; + } + + cmd.cmdidx = start_cmd; + cmd.cmdarg = start; + cmd.resp_type = MMC_RSP_R1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = end_cmd; + cmd.cmdarg = end; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = MMC_CMD_ERASE; + cmd.cmdarg = SECURE_ERASE; + cmd.resp_type = MMC_RSP_R1b; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + return 0; + +err_out: + puts("mmc erase failed\n"); + return err; +} + +unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + int timeout = 1000; + + if (!mmc) + return -1; + + if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) + printf("\n\nCaution! Your devices Erase group is 0x%x\n" + "The erase range would be change to " + "0x" LBAF "~0x" LBAF "\n\n", + mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), + ((start + blkcnt + mmc->erase_grp_size) + & ~(mmc->erase_grp_size - 1)) - 1); + + while (blk < blkcnt) { + blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? + mmc->erase_grp_size : (blkcnt - blk); + err = mmc_erase_t(mmc, start + blk, blk_r); + if (err) + break; + + blk += blk_r; + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + } + + return blk; +} + +static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, + lbaint_t blkcnt, const void *src) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int timeout = 1000; + + if ((start + blkcnt) > mmc->block_dev.lba) { + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, mmc->block_dev.lba); + return 0; + } + + if (blkcnt == 0) + return 0; + else if (blkcnt == 1) + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * mmc->write_bl_len; + + cmd.resp_type = MMC_RSP_R1; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = mmc->write_bl_len; + data.flags = MMC_DATA_WRITE; + + if (mmc_send_cmd(mmc, &cmd, &data)) { + printf("mmc write failed\n"); + return 0; + } + + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + if (mmc_send_cmd(mmc, &cmd, NULL)) { + printf("mmc fail to send stop cmd\n"); + return 0; + } + } + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + + return blkcnt; +} + +ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) +{ + lbaint_t cur, blocks_todo = blkcnt; + + struct mmc *mmc = find_mmc_device(dev_num); + if (!mmc) + return 0; + + if (mmc_set_blocklen(mmc, mmc->write_bl_len)) + return 0; + + do { + cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; + if (mmc_write_blocks(mmc, start, cur, src) != cur) + return 0; + blocks_todo -= cur; + start += cur; + src += cur * mmc->write_bl_len; + } while (blocks_todo > 0); + + return blkcnt; +} diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 975b2c5..d3a8b53 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -288,6 +288,30 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) mmc_reg_out(&mmc_base->sysctl, bit, bit); + /* + * CMD(DAT) lines reset procedures are slightly different + * for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx). + * According to OMAP3 TRM: + * Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it + * returns to 0x0. + * According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset + * procedure steps must be as follows: + * 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in + * MMCHS_SYSCTL register (SD_SYSCTL for AM335x). + * 2. Poll the SRC(SRD) bit until it is set to 0x1. + * 3. Wait until the SRC (SRD) bit returns to 0x0 + * (reset procedure is completed). + */ +#if defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_AM33XX) + if (!(readl(&mmc_base->sysctl) & bit)) { + start = get_timer(0); + while (!(readl(&mmc_base->sysctl) & bit)) { + if (get_timer(0) - start > MAX_RETRY_MS) + return; + } + } +#endif start = get_timer(0); while ((readl(&mmc_base->sysctl) & bit) != 0) { if (get_timer(0) - start > MAX_RETRY_MS) { @@ -376,6 +400,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } writel(cmd->cmdarg, &mmc_base->arg); + udelay(20); /* To fix "No status update" error on eMMC */ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); start = get_timer(0); @@ -480,7 +505,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int count; /* - * Start Polled Read + * Start Polled Write */ count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; count /= 4; @@ -586,6 +611,8 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, { struct mmc *mmc = &hsmmc_dev[dev_index]; struct omap_hsmmc_data *priv_data = &hsmmc_dev_data[dev_index]; + uint host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | + MMC_MODE_HC; sprintf(mmc->name, "OMAP SD/MMC"); mmc->send_cmd = mmc_send_cmd; @@ -600,11 +627,20 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, #ifdef OMAP_HSMMC2_BASE case 1: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; +#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT) + /* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif break; #endif #ifdef OMAP_HSMMC3_BASE case 2: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; +#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT) + /* Enable 8-bit interface for eMMC on DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif break; #endif default: @@ -620,8 +656,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, mmc->getwp = omap_mmc_getwp; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | - MMC_MODE_HC) & ~host_caps_mask; + mmc->host_caps = host_caps_val & ~host_caps_mask; mmc->f_min = 400000; diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 7f89403..40ff873 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -72,7 +72,7 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | - SDHCI_QUIRK_WAIT_SEND_CMD; + SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; host->version = sdhci_readw(host, SDHCI_HOST_VERSION); @@ -81,6 +81,8 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->index = index; host->host_caps = MMC_MODE_HC; + if (bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; return add_sdhci(host, 52000000, 400000); } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4261991..dfb2eee 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -68,10 +68,9 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int stat, rdy, mask, timeout, block = 0; #ifdef CONFIG_MMC_SDMA unsigned char ctrl; - ctrl = sdhci_readl(host, SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; - ctrl |= SDHCI_CTRL_SDMA; - sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); #endif timeout = 1000000; @@ -254,7 +253,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (clock == 0) return 0; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* Version 3.00 divisors must be a multiple of 2. */ if (mmc->f_max <= clock) div = 1; @@ -347,10 +346,11 @@ void sdhci_set_ios(struct mmc *mmc) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (mmc->bus_width == 8) { ctrl &= ~SDHCI_CTRL_4BITBUS; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || + (host->quirks & SDHCI_QUIRK_USE_WIDE8)) ctrl |= SDHCI_CTRL_8BITBUS; } else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ctrl &= ~SDHCI_CTRL_8BITBUS; if (mmc->bus_width == 4) ctrl |= SDHCI_CTRL_4BITBUS; @@ -437,7 +437,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (max_clk) mmc->f_max = max_clk; else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; else @@ -452,7 +452,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (min_clk) mmc->f_min = min_clk; else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300; else mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200; @@ -470,7 +470,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->voltages |= host->voltages; mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { if (caps & SDHCI_CAN_DO_8BIT) mmc->host_caps |= MMC_MODE_8BIT; } diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 191138a..86ffc59 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -14,16 +14,11 @@ COBJS-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o COBJS-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o endif -COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o -COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o -COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o -COBJS-$(CONFIG_SPI_FLASH_GIGADEVICE) += gigadevice.o -COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o -COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o -COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o -COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o -COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o -COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o +ifdef CONFIG_CMD_SF +COBJS-y += sf.o +endif +COBJS-$(CONFIG_SPI_FLASH) += sf_probe.o sf_ops.o +COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o COBJS := $(COBJS-y) diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c deleted file mode 100644 index f34df43..0000000 --- a/drivers/mtd/spi/atmel.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Atmel SPI DataFlash support - * - * Copyright (C) 2008 Atmel Corporation - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -/* AT45-specific commands */ -#define CMD_AT45_READ_STATUS 0xd7 -#define CMD_AT45_ERASE_PAGE 0x81 -#define CMD_AT45_LOAD_PROG_BUF1 0x82 -#define CMD_AT45_LOAD_BUF1 0x84 -#define CMD_AT45_LOAD_PROG_BUF2 0x85 -#define CMD_AT45_LOAD_BUF2 0x87 -#define CMD_AT45_PROG_BUF1 0x88 -#define CMD_AT45_PROG_BUF2 0x89 - -/* AT45 status register bits */ -#define AT45_STATUS_P2_PAGE_SIZE (1 << 0) -#define AT45_STATUS_READY (1 << 7) - -/* DataFlash family IDs, as obtained from the second idcode byte */ -#define DF_FAMILY_AT26F 0 -#define DF_FAMILY_AT45 1 -#define DF_FAMILY_AT26DF 2 /* AT25DF and AT26DF */ - -struct atmel_spi_flash_params { - u8 idcode1; - /* Log2 of page size in power-of-two mode */ - u8 l2_page_size; - u8 pages_per_block; - u8 blocks_per_sector; - u8 nr_sectors; - const char *name; -}; - -/* spi_flash needs to be first so upper layers can free() it */ -struct atmel_spi_flash { - struct spi_flash flash; - const struct atmel_spi_flash_params *params; -}; - -static inline struct atmel_spi_flash * -to_atmel_spi_flash(struct spi_flash *flash) -{ - return container_of(flash, struct atmel_spi_flash, flash); -} - -static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { - { - .idcode1 = 0x22, - .l2_page_size = 8, - .pages_per_block = 8, - .blocks_per_sector = 16, - .nr_sectors = 4, - .name = "AT45DB011D", - }, - { - .idcode1 = 0x23, - .l2_page_size = 8, - .pages_per_block = 8, - .blocks_per_sector = 16, - .nr_sectors = 8, - .name = "AT45DB021D", - }, - { - .idcode1 = 0x24, - .l2_page_size = 8, - .pages_per_block = 8, - .blocks_per_sector = 32, - .nr_sectors = 8, - .name = "AT45DB041D", - }, - { - .idcode1 = 0x25, - .l2_page_size = 8, - .pages_per_block = 8, - .blocks_per_sector = 32, - .nr_sectors = 16, - .name = "AT45DB081D", - }, - { - .idcode1 = 0x26, - .l2_page_size = 9, - .pages_per_block = 8, - .blocks_per_sector = 32, - .nr_sectors = 16, - .name = "AT45DB161D", - }, - { - .idcode1 = 0x27, - .l2_page_size = 9, - .pages_per_block = 8, - .blocks_per_sector = 64, - .nr_sectors = 64, - .name = "AT45DB321D", - }, - { - .idcode1 = 0x28, - .l2_page_size = 10, - .pages_per_block = 8, - .blocks_per_sector = 32, - .nr_sectors = 32, - .name = "AT45DB642D", - }, - { - .idcode1 = 0x47, - .l2_page_size = 8, - .pages_per_block = 16, - .blocks_per_sector = 16, - .nr_sectors = 64, - .name = "AT25DF321", - }, -}; - -static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout) -{ - struct spi_slave *spi = flash->spi; - unsigned long timebase; - int ret; - u8 cmd = CMD_AT45_READ_STATUS; - u8 status; - - timebase = get_timer(0); - - ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); - if (ret) - return -1; - - do { - ret = spi_xfer(spi, 8, NULL, &status, 0); - if (ret) - return -1; - - if (status & AT45_STATUS_READY) - break; - } while (get_timer(timebase) < timeout); - - /* Deactivate CS */ - spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); - - if (status & AT45_STATUS_READY) - return 0; - - /* Timed out */ - return -1; -} - -/* - * Assemble the address part of a command for AT45 devices in - * non-power-of-two page size mode. - */ -static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset) -{ - unsigned long page_addr; - unsigned long byte_addr; - unsigned long page_size; - unsigned int page_shift; - - /* - * The "extra" space per page is the power-of-two page size - * divided by 32. - */ - page_shift = asf->params->l2_page_size; - page_size = (1 << page_shift) + (1 << (page_shift - 5)); - page_shift++; - page_addr = offset / page_size; - byte_addr = offset % page_size; - - cmd[0] = page_addr >> (16 - page_shift); - cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); - cmd[2] = byte_addr; -} - -static int dataflash_read_fast_at45(struct spi_flash *flash, - u32 offset, size_t len, void *buf) -{ - struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); - u8 cmd[5]; - - cmd[0] = CMD_READ_ARRAY_FAST; - at45_build_address(asf, cmd + 1, offset); - cmd[4] = 0x00; - - return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); -} - -/* - * TODO: the two write funcs (_p2/_at45) should get unified ... - */ -static int dataflash_write_p2(struct spi_flash *flash, - u32 offset, size_t len, const void *buf) -{ - struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); - unsigned long page_size; - u32 addr = offset; - size_t chunk_len; - size_t actual; - int ret; - u8 cmd[4]; - - /* - * TODO: This function currently uses only page buffer #1. We can - * speed this up by using both buffers and loading one buffer while - * the other is being programmed into main memory. - */ - - page_size = (1 << asf->params->l2_page_size); - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); - return ret; - } - - for (actual = 0; actual < len; actual += chunk_len) { - chunk_len = min(len - actual, page_size - (addr % page_size)); - - /* Use the same address bits for both commands */ - cmd[0] = CMD_AT45_LOAD_BUF1; - cmd[1] = addr >> 16; - cmd[2] = addr >> 8; - cmd[3] = addr; - - ret = spi_flash_cmd_write(flash->spi, cmd, 4, - buf + actual, chunk_len); - if (ret < 0) { - debug("SF: Loading AT45 buffer failed\n"); - goto out; - } - - cmd[0] = CMD_AT45_PROG_BUF1; - ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); - if (ret < 0) { - debug("SF: AT45 page programming failed\n"); - goto out; - } - - ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); - if (ret < 0) { - debug("SF: AT45 page programming timed out\n"); - goto out; - } - - addr += chunk_len; - } - - debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", - len, offset); - ret = 0; - -out: - spi_release_bus(flash->spi); - return ret; -} - -static int dataflash_write_at45(struct spi_flash *flash, - u32 offset, size_t len, const void *buf) -{ - struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); - unsigned long page_addr; - unsigned long byte_addr; - unsigned long page_size; - unsigned int page_shift; - size_t chunk_len; - size_t actual; - int ret; - u8 cmd[4]; - - /* - * TODO: This function currently uses only page buffer #1. We can - * speed this up by using both buffers and loading one buffer while - * the other is being programmed into main memory. - */ - - page_shift = asf->params->l2_page_size; - page_size = (1 << page_shift) + (1 << (page_shift - 5)); - page_shift++; - page_addr = offset / page_size; - byte_addr = offset % page_size; - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); - return ret; - } - - for (actual = 0; actual < len; actual += chunk_len) { - chunk_len = min(len - actual, page_size - byte_addr); - - /* Use the same address bits for both commands */ - cmd[0] = CMD_AT45_LOAD_BUF1; - cmd[1] = page_addr >> (16 - page_shift); - cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); - cmd[3] = byte_addr; - - ret = spi_flash_cmd_write(flash->spi, cmd, 4, - buf + actual, chunk_len); - if (ret < 0) { - debug("SF: Loading AT45 buffer failed\n"); - goto out; - } - - cmd[0] = CMD_AT45_PROG_BUF1; - ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); - if (ret < 0) { - debug("SF: AT45 page programming failed\n"); - goto out; - } - - ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); - if (ret < 0) { - debug("SF: AT45 page programming timed out\n"); - goto out; - } - - page_addr++; - byte_addr = 0; - } - - debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", - len, offset); - ret = 0; - -out: - spi_release_bus(flash->spi); - return ret; -} - -/* - * TODO: the two erase funcs (_p2/_at45) should get unified ... - */ -static int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len) -{ - struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); - unsigned long page_size; - - size_t actual; - int ret; - u8 cmd[4]; - - /* - * TODO: This function currently uses page erase only. We can - * probably speed things up by using block and/or sector erase - * when possible. - */ - - page_size = (1 << asf->params->l2_page_size); - - if (offset % page_size || len % page_size) { - debug("SF: Erase offset/length not multiple of page size\n"); - return -1; - } - - cmd[0] = CMD_AT45_ERASE_PAGE; - cmd[3] = 0x00; - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); - return ret; - } - - for (actual = 0; actual < len; actual += page_size) { - cmd[1] = offset >> 16; - cmd[2] = offset >> 8; - - ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); - if (ret < 0) { - debug("SF: AT45 page erase failed\n"); - goto out; - } - - ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); - if (ret < 0) { - debug("SF: AT45 page erase timed out\n"); - goto out; - } - - offset += page_size; - } - - debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", - len, offset); - ret = 0; - -out: - spi_release_bus(flash->spi); - return ret; -} - -static int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) -{ - struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); - unsigned long page_addr; - unsigned long page_size; - unsigned int page_shift; - size_t actual; - int ret; - u8 cmd[4]; - - /* - * TODO: This function currently uses page erase only. We can - * probably speed things up by using block and/or sector erase - * when possible. - */ - - page_shift = asf->params->l2_page_size; - page_size = (1 << page_shift) + (1 << (page_shift - 5)); - page_shift++; - page_addr = offset / page_size; - - if (offset % page_size || len % page_size) { - debug("SF: Erase offset/length not multiple of page size\n"); - return -1; - } - - cmd[0] = CMD_AT45_ERASE_PAGE; - cmd[3] = 0x00; - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); - return ret; - } - - for (actual = 0; actual < len; actual += page_size) { - cmd[1] = page_addr >> (16 - page_shift); - cmd[2] = page_addr << (page_shift - 8); - - ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); - if (ret < 0) { - debug("SF: AT45 page erase failed\n"); - goto out; - } - - ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); - if (ret < 0) { - debug("SF: AT45 page erase timed out\n"); - goto out; - } - - page_addr++; - } - - debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", - len, offset); - ret = 0; - -out: - spi_release_bus(flash->spi); - return ret; -} - -struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) -{ - const struct atmel_spi_flash_params *params; - unsigned page_size; - unsigned int family; - struct atmel_spi_flash *asf; - unsigned int i; - int ret; - u8 status; - - for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) { - params = &atmel_spi_flash_table[i]; - if (params->idcode1 == idcode[1]) - break; - } - - if (i == ARRAY_SIZE(atmel_spi_flash_table)) { - debug("SF: Unsupported DataFlash ID %02x\n", - idcode[1]); - return NULL; - } - - asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name); - if (!asf) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - asf->params = params; - - /* Assuming power-of-two page size initially. */ - page_size = 1 << params->l2_page_size; - - family = idcode[1] >> 5; - - switch (family) { - case DF_FAMILY_AT45: - /* - * AT45 chips have configurable page size. The status - * register indicates which configuration is active. - */ - ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1); - if (ret) - goto err; - - debug("SF: AT45 status register: %02x\n", status); - - if (!(status & AT45_STATUS_P2_PAGE_SIZE)) { - asf->flash.read = dataflash_read_fast_at45; - asf->flash.write = dataflash_write_at45; - asf->flash.erase = dataflash_erase_at45; - page_size += 1 << (params->l2_page_size - 5); - } else { - asf->flash.write = dataflash_write_p2; - asf->flash.erase = dataflash_erase_p2; - } - - asf->flash.page_size = page_size; - asf->flash.sector_size = page_size; - break; - - case DF_FAMILY_AT26F: - case DF_FAMILY_AT26DF: - asf->flash.page_size = page_size; - asf->flash.sector_size = 4096; - /* clear SPRL# bit for locked flash */ - spi_flash_cmd_write_status(&asf->flash, 0); - break; - - default: - debug("SF: Unsupported DataFlash family %u\n", family); - goto err; - } - - asf->flash.size = page_size * params->pages_per_block - * params->blocks_per_sector - * params->nr_sectors; - - return &asf->flash; - -err: - free(asf); - return NULL; -} diff --git a/drivers/mtd/spi/eon.c b/drivers/mtd/spi/eon.c deleted file mode 100644 index 25cfc12..0000000 --- a/drivers/mtd/spi/eon.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * (C) Copyright 2010, ucRobotics Inc. - * Author: Chong Huang <chuang@ucrobotics.com> - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct eon_spi_flash_params { - u8 idcode1; - u16 nr_sectors; - const char *name; -}; - -static const struct eon_spi_flash_params eon_spi_flash_table[] = { - { - .idcode1 = 0x16, - .nr_sectors = 1024, - .name = "EN25Q32B", - }, - { - .idcode1 = 0x18, - .nr_sectors = 4096, - .name = "EN25Q128", - }, -}; - -struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode) -{ - const struct eon_spi_flash_params *params; - struct spi_flash *flash; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) { - params = &eon_spi_flash_table[i]; - if (params->idcode1 == idcode[2]) - break; - } - - if (i == ARRAY_SIZE(eon_spi_flash_table)) { - debug("SF: Unsupported EON ID %02x\n", idcode[1]); - return NULL; - } - - flash = spi_flash_alloc_base(spi, params->name); - if (!flash) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - flash->page_size = 256; - flash->sector_size = 256 * 16 * 16; - flash->size = 256 * 16 * params->nr_sectors; - - return flash; -} diff --git a/drivers/mtd/spi/gigadevice.c b/drivers/mtd/spi/gigadevice.c deleted file mode 100644 index b42581a..0000000 --- a/drivers/mtd/spi/gigadevice.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Gigadevice SPI flash driver - * Copyright 2013, Samsung Electronics Co., Ltd. - * Author: Banajit Goswami <banajit.g@samsung.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct gigadevice_spi_flash_params { - uint16_t id; - uint16_t nr_blocks; - const char *name; -}; - -static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = { - { - .id = 0x6016, - .nr_blocks = 64, - .name = "GD25LQ", - }, - { - .id = 0x4017, - .nr_blocks = 128, - .name = "GD25Q64B", - }, -}; - -struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode) -{ - const struct gigadevice_spi_flash_params *params; - struct spi_flash *flash; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) { - params = &gigadevice_spi_flash_table[i]; - if (params->id == ((idcode[1] << 8) | idcode[2])) - break; - } - - if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) { - debug("SF: Unsupported Gigadevice ID %02x%02x\n", - idcode[1], idcode[2]); - return NULL; - } - - flash = spi_flash_alloc_base(spi, params->name); - if (!flash) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - /* page_size */ - flash->page_size = 256; - /* sector_size = page_size * pages_per_sector */ - flash->sector_size = flash->page_size * 16; - /* size = sector_size * sector_per_block * number of blocks */ - flash->size = flash->sector_size * 16 * params->nr_blocks; - - return flash; -} diff --git a/drivers/mtd/spi/macronix.c b/drivers/mtd/spi/macronix.c deleted file mode 100644 index 70435eb..0000000 --- a/drivers/mtd/spi/macronix.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2009(C) Marvell International Ltd. and its affiliates - * Prafulla Wadaskar <prafulla@marvell.com> - * - * Based on drivers/mtd/spi/stmicro.c - * - * Copyright 2008, Network Appliance Inc. - * Jason McMullan <mcmullan@netapp.com> - * - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct macronix_spi_flash_params { - u16 idcode; - u16 nr_blocks; - const char *name; -}; - -static const struct macronix_spi_flash_params macronix_spi_flash_table[] = { - { - .idcode = 0x2013, - .nr_blocks = 8, - .name = "MX25L4005", - }, - { - .idcode = 0x2014, - .nr_blocks = 16, - .name = "MX25L8005", - }, - { - .idcode = 0x2015, - .nr_blocks = 32, - .name = "MX25L1605D", - }, - { - .idcode = 0x2016, - .nr_blocks = 64, - .name = "MX25L3205D", - }, - { - .idcode = 0x2017, - .nr_blocks = 128, - .name = "MX25L6405D", - }, - { - .idcode = 0x2018, - .nr_blocks = 256, - .name = "MX25L12805D", - }, - { - .idcode = 0x2618, - .nr_blocks = 256, - .name = "MX25L12855E", - }, -}; - -struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode) -{ - const struct macronix_spi_flash_params *params; - struct spi_flash *flash; - unsigned int i; - u16 id = idcode[2] | idcode[1] << 8; - - for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) { - params = ¯onix_spi_flash_table[i]; - if (params->idcode == id) - break; - } - - if (i == ARRAY_SIZE(macronix_spi_flash_table)) { - debug("SF: Unsupported Macronix ID %04x\n", id); - return NULL; - } - - flash = spi_flash_alloc_base(spi, params->name); - if (!flash) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - flash->page_size = 256; - flash->sector_size = 256 * 16 * 16; - flash->size = flash->sector_size * params->nr_blocks; - - /* Clear BP# bits for read-only flash */ - spi_flash_cmd_write_status(flash, 0); - - return flash; -} diff --git a/drivers/mtd/spi/ramtron.c b/drivers/mtd/spi/ramtron.c index 38f9d69..d50da37 100644 --- a/drivers/mtd/spi/ramtron.c +++ b/drivers/mtd/spi/ramtron.c @@ -36,7 +36,7 @@ #include <common.h> #include <malloc.h> #include <spi_flash.h> -#include "spi_flash_internal.h" +#include "sf_internal.h" /* * Properties of supported FRAMs @@ -214,7 +214,8 @@ static int ramtron_erase(struct spi_flash *flash, u32 offset, size_t len) * nore: we are called here with idcode pointing to the first non-0x7f byte * already! */ -struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode) +static struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, + u8 *idcode) { const struct ramtron_spi_fram_params *params; struct ramtron_spi_fram *sn; @@ -270,7 +271,7 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode) return NULL; found: - sn = spi_flash_alloc(struct ramtron_spi_fram, spi, params->name); + sn = malloc(sizeof(*sn)); if (!sn) { debug("SF: Failed to allocate memory\n"); return NULL; @@ -285,3 +286,118 @@ found: return &sn->flash; } + +/* + * The following table holds all device probe functions + * (All flashes are removed and implemented a common probe at + * spi_flash_probe.c) + * + * shift: number of continuation bytes before the ID + * idcode: the expected IDCODE or 0xff for non JEDEC devices + * probe: the function to call + * + * Non JEDEC devices should be ordered in the table such that + * the probe functions with best detection algorithms come first. + * + * Several matching entries are permitted, they will be tried + * in sequence until a probe function returns non NULL. + * + * IDCODE_CONT_LEN may be redefined if a device needs to declare a + * larger "shift" value. IDCODE_PART_LEN generally shouldn't be + * changed. This is the max number of bytes probe functions may + * examine when looking up part-specific identification info. + * + * Probe functions will be given the idcode buffer starting at their + * manu id byte (the "idcode" in the table below). In other words, + * all of the continuation bytes will be skipped (the "shift" below). + */ +#define IDCODE_CONT_LEN 0 +#define IDCODE_PART_LEN 5 +static const struct { + const u8 shift; + const u8 idcode; + struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode); +} flashes[] = { + /* Keep it sorted by define name */ +#ifdef CONFIG_SPI_FRAM_RAMTRON + { 6, 0xc2, spi_fram_probe_ramtron, }, +# undef IDCODE_CONT_LEN +# define IDCODE_CONT_LEN 6 +#endif +#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC + { 0, 0xff, spi_fram_probe_ramtron, }, +#endif +}; +#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + struct spi_flash *flash = NULL; + int ret, i, shift; + u8 idcode[IDCODE_LEN], *idp; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + printf("SF: Failed to set up slave\n"); + return NULL; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); + if (ret) + goto err_read_id; + +#ifdef DEBUG + printf("SF: Got idcodes\n"); + print_buffer(0, idcode, 1, sizeof(idcode), 0); +#endif + + /* count the number of continuation bytes */ + for (shift = 0, idp = idcode; + shift < IDCODE_CONT_LEN && *idp == 0x7f; + ++shift, ++idp) + continue; + + /* search the table for matches in shift and id */ + for (i = 0; i < ARRAY_SIZE(flashes); ++i) + if (flashes[i].shift == shift && flashes[i].idcode == *idp) { + /* we have a match, call probe */ + flash = flashes[i].probe(spi, idp); + if (flash) + break; + } + + if (!flash) { + printf("SF: Unsupported manufacturer %02x\n", *idp); + goto err_manufacturer_probe; + } + + printf("SF: Detected %s with total size ", flash->name); + print_size(flash->size, ""); + puts("\n"); + + spi_release_bus(spi); + + return flash; + +err_manufacturer_probe: +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c new file mode 100644 index 0000000..ddbdda0 --- /dev/null +++ b/drivers/mtd/spi/sf.c @@ -0,0 +1,54 @@ +/* + * SPI flash interface + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <spi.h> + +static int spi_flash_read_write(struct spi_slave *spi, + const u8 *cmd, size_t cmd_len, + const u8 *data_out, u8 *data_in, + size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data_out, data_in, + SPI_XFER_END); + if (ret) + debug("SF: Failed to transfer %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); +} + +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) +{ + return spi_flash_cmd_read(spi, &cmd, 1, response, len); +} + +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); +} diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/sf_internal.h index af1afa9..12d02f9 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -2,42 +2,43 @@ * SPI flash internal definitions * * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * Licensed under the GPL-2 or later. */ -/* Common parameters -- kind of high, but they should only occur when there - * is a problem (and well your system already is broken), so err on the side - * of caution in case we're dealing with slower SPI buses and/or processors. - */ -#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) -#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) -#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) +#ifndef _SPI_FLASH_INTERNAL_H_ +#define _SPI_FLASH_INTERNAL_H_ -/* Common commands */ -#define CMD_READ_ID 0x9f +#define SPI_FLASH_16MB_BOUN 0x1000000 -#define CMD_READ_ARRAY_SLOW 0x03 -#define CMD_READ_ARRAY_FAST 0x0b +/* SECT flags */ +#define SECT_4K (1 << 1) +#define SECT_32K (1 << 2) +#define E_FSR (1 << 3) + +/* Erase commands */ +#define CMD_ERASE_4K 0x20 +#define CMD_ERASE_32K 0x52 +#define CMD_ERASE_CHIP 0xc7 +#define CMD_ERASE_64K 0xd8 +/* Write commands */ #define CMD_WRITE_STATUS 0x01 #define CMD_PAGE_PROGRAM 0x02 #define CMD_WRITE_DISABLE 0x04 #define CMD_READ_STATUS 0x05 -#define CMD_FLAG_STATUS 0x70 #define CMD_WRITE_ENABLE 0x06 -#define CMD_ERASE_4K 0x20 -#define CMD_ERASE_32K 0x52 -#define CMD_ERASE_64K 0xd8 -#define CMD_ERASE_CHIP 0xc7 - -#define SPI_FLASH_16MB_BOUN 0x1000000 +#define CMD_READ_CONFIG 0x35 +#define CMD_FLAG_STATUS 0x70 -/* Manufacture ID's */ -#define SPI_FLASH_SPANSION_IDCODE0 0x01 -#define SPI_FLASH_STMICRO_IDCODE0 0x20 -#define SPI_FLASH_WINBOND_IDCODE0 0xef +/* Read commands */ +#define CMD_READ_ARRAY_SLOW 0x03 +#define CMD_READ_ARRAY_FAST 0x0b +#define CMD_READ_ID 0x9f -#ifdef CONFIG_SPI_FLASH_BAR /* Bank addr access commands */ +#ifdef CONFIG_SPI_FLASH_BAR # define CMD_BANKADDR_BRWR 0x17 # define CMD_BANKADDR_BRRD 0x16 # define CMD_EXTNADDR_WREAR 0xC5 @@ -48,6 +49,21 @@ #define STATUS_WIP 0x01 #define STATUS_PEC 0x80 +/* Flash timeout values */ +#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) +#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) + +/* SST specific */ +#ifdef CONFIG_SPI_FLASH_SST +# define SST_WP 0x01 /* Supports AAI word program */ +# define CMD_SST_BP 0x02 /* Byte Program */ +# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ + +int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf); +#endif + /* Send a single-byte command to the device and read the response */ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); @@ -58,9 +74,6 @@ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, void *data, size_t data_len); -int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, - size_t len, void *data); - /* * Send a multi-byte command to the device followed by (optional) * data. Used for programming the flash array, etc. @@ -68,46 +81,34 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, const void *data, size_t data_len); -/* - * Write the requested data out breaking it up into multiple write - * commands as needed per the write size. - */ -int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, - size_t len, const void *buf); -/* - * Enable writing on the SPI flash. - */ +/* Flash erase(sectors) operation, support all possible erase commands */ +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len); + +/* Program the status register */ +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); + +/* Set quad enbale bit */ +int spi_flash_set_qeb(struct spi_flash *flash); + +/* Enable writing on the SPI flash */ static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) { return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0); } -/* - * Disable writing on the SPI flash. - */ +/* Disable writing on the SPI flash */ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) { return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0); } -/* Program the status register. */ -int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); - -#ifdef CONFIG_SPI_FLASH_BAR -/* Program the bank address register */ -int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel); - -/* Configure the BAR - discover the bank cmds */ -int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0); -#endif - /* - * Same as spi_flash_cmd_read() except it also claims/releases the SPI - * bus. Used as common part of the ->read() operation. + * Send the read status command to the device and wait for the wip + * (write-in-progress) bit to clear itself. */ -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len); +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); + /* * Used for spi_flash write operation * - SPI claim @@ -120,21 +121,22 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, size_t cmd_len, const void *buf, size_t buf_len); /* - * Send the read status command to the device and wait for the wip - * (write-in-progress) bit to clear itself. + * Flash write operation, support all possible write commands. + * Write the requested data out breaking it up into multiple write + * commands as needed per the write size. */ -int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, + size_t len, const void *buf); + +/* + * Same as spi_flash_cmd_read() except it also claims/releases the SPI + * bus. Used as common part of the ->read() operation. + */ +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* Flash read operation, support all possible read commands */ +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, + size_t len, void *data); -/* Erase sectors. */ -int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len); - -/* Manufacturer-specific probe functions */ -struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode); +#endif /* _SPI_FLASH_INTERNAL_H_ */ diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c new file mode 100644 index 0000000..2396e22 --- /dev/null +++ b/drivers/mtd/spi/sf_ops.c @@ -0,0 +1,405 @@ +/* + * SPI flash operations + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <spi.h> +#include <spi_flash.h> +#include <watchdog.h> + +#include "sf_internal.h" + +static void spi_flash_addr(u32 addr, u8 *cmd) +{ + /* cmd[0] is actual command */ + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr >> 0; +} + +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) +{ + u8 cmd; + int ret; + + cmd = CMD_WRITE_STATUS; + ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); + if (ret < 0) { + debug("SF: fail to write status register\n"); + return ret; + } + + return 0; +} + +#ifdef CONFIG_SPI_FLASH_BAR +static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) +{ + u8 cmd; + int ret; + + if (flash->bank_curr == bank_sel) { + debug("SF: not require to enable bank%d\n", bank_sel); + return 0; + } + + cmd = flash->bank_write_cmd; + ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); + if (ret < 0) { + debug("SF: fail to write bank register\n"); + return ret; + } + flash->bank_curr = bank_sel; + + return 0; +} +#endif + +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 check_status = 0x0; + u8 poll_bit = STATUS_WIP; + u8 cmd = flash->poll_cmd; + + if (cmd == CMD_FLAG_STATUS) { + poll_bit = STATUS_PEC; + check_status = poll_bit; + } + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: fail to read %s status register\n", + cmd == CMD_READ_STATUS ? "read" : "flag"); + return ret; + } + + timebase = get_timer(0); + do { + WATCHDOG_RESET(); + + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & poll_bit) == check_status) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & poll_bit) == check_status) + return 0; + + /* Timed out */ + debug("SF: time out!\n"); + return -1; +} + +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, const void *buf, size_t buf_len) +{ + struct spi_slave *spi = flash->spi; + unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; + int ret; + + if (buf == NULL) + timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + debug("SF: enabling write failed\n"); + return ret; + } + + ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); + if (ret < 0) { + debug("SF: write cmd failed\n"); + return ret; + } + + ret = spi_flash_cmd_wait_ready(flash, timeout); + if (ret < 0) { + debug("SF: write %s timed out\n", + timeout == SPI_FLASH_PROG_TIMEOUT ? + "program" : "page erase"); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) +{ + u32 erase_size; + u8 cmd[4]; + int ret = -1; + + erase_size = flash->erase_size; + if (offset % erase_size || len % erase_size) { + debug("SF: Erase offset/length not multiple of erase size\n"); + return -1; + } + + cmd[0] = flash->erase_cmd; + while (len) { +#ifdef CONFIG_SPI_FLASH_BAR + u8 bank_sel; + + bank_sel = offset / SPI_FLASH_16MB_BOUN; + + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } +#endif + spi_flash_addr(offset, cmd); + + debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], + cmd[2], cmd[3], offset); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); + if (ret < 0) { + debug("SF: erase failed\n"); + break; + } + + offset += erase_size; + len -= erase_size; + } + + return ret; +} + +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + unsigned long byte_addr, page_size; + size_t chunk_len, actual; + u8 cmd[4]; + int ret = -1; + + page_size = flash->page_size; + + cmd[0] = CMD_PAGE_PROGRAM; + for (actual = 0; actual < len; actual += chunk_len) { +#ifdef CONFIG_SPI_FLASH_BAR + u8 bank_sel; + + bank_sel = offset / SPI_FLASH_16MB_BOUN; + + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } +#endif + byte_addr = offset % page_size; + chunk_len = min(len - actual, page_size - byte_addr); + + if (flash->spi->max_write_size) + chunk_len = min(chunk_len, flash->spi->max_write_size); + + spi_flash_addr(offset, cmd); + + debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: write failed\n"); + break; + } + + offset += chunk_len; + } + + return ret; +} + +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); + if (ret < 0) { + debug("SF: read cmd failed\n"); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, + size_t len, void *data) +{ + u8 cmd[5], bank_sel = 0; + u32 remain_len, read_len; + int ret = -1; + + /* Handle memory-mapped SPI */ + if (flash->memory_map) { + spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP); + memcpy(data, flash->memory_map + offset, len); + spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); + return 0; + } + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[4] = 0x00; + + while (len) { +#ifdef CONFIG_SPI_FLASH_BAR + bank_sel = offset / SPI_FLASH_16MB_BOUN; + + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } +#endif + remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); + if (len < remain_len) + read_len = len; + else + read_len = remain_len; + + spi_flash_addr(offset, cmd); + + ret = spi_flash_read_common(flash, cmd, sizeof(cmd), + data, read_len); + if (ret < 0) { + debug("SF: read failed\n"); + break; + } + + offset += read_len; + len -= read_len; + data += read_len; + } + + return ret; +} + +#ifdef CONFIG_SPI_FLASH_SST +static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) +{ + int ret; + u8 cmd[4] = { + CMD_SST_BP, + offset >> 16, + offset >> 8, + offset, + }; + + debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", + spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); + + ret = spi_flash_cmd_write_enable(flash); + if (ret) + return ret; + + ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); + if (ret) + return ret; + + return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +} + +int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf) +{ + size_t actual, cmd_len; + int ret; + u8 cmd[4]; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + /* If the data is not word aligned, write out leading single byte */ + actual = offset % 2; + if (actual) { + ret = sst_byte_write(flash, offset, buf); + if (ret) + goto done; + } + offset += actual; + + ret = spi_flash_cmd_write_enable(flash); + if (ret) + goto done; + + cmd_len = 4; + cmd[0] = CMD_SST_AAI_WP; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + + for (; actual < len - 1; actual += 2) { + debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", + spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, + cmd[0], offset); + + ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, + buf + actual, 2); + if (ret) { + debug("SF: sst word program failed\n"); + break; + } + + ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + break; + + cmd_len = 1; + offset += 2; + } + + if (!ret) + ret = spi_flash_cmd_write_disable(flash); + + /* If there is a single trailing byte, write it out */ + if (!ret && actual != len) + ret = sst_byte_write(flash, offset, buf + actual); + + done: + debug("SF: sst: program %s %zu bytes @ 0x%zx\n", + ret ? "failure" : "success", len, offset - actual); + + spi_release_bus(flash->spi); + return ret; +} +#endif diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c new file mode 100644 index 0000000..4251b1b --- /dev/null +++ b/drivers/mtd/spi/sf_probe.c @@ -0,0 +1,363 @@ +/* + * SPI flash probing + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <fdtdec.h> +#include <malloc.h> +#include <spi.h> +#include <spi_flash.h> + +#include "sf_internal.h" + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct spi_flash_params - SPI/QSPI flash device params structure + * + * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) + * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id]) + * @ext_jedec: Device ext_jedec ID + * @sector_size: Sector size of this device + * @nr_sectors: No.of sectors on this device + * @flags: Importent param, for flash specific behaviour + */ +struct spi_flash_params { + const char *name; + u32 jedec; + u16 ext_jedec; + u32 sector_size; + u32 nr_sectors; + u16 flags; +}; + +static const struct spi_flash_params spi_flash_params_table[] = { +#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ + {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, SECT_4K}, + {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, SECT_4K}, + {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, SECT_4K}, + {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, SECT_4K}, + {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, SECT_4K}, + {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, SECT_4K}, + {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_EON /* EON */ + {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0}, + {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, SECT_4K}, + {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0}, + {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0}, +#endif +#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ + {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, SECT_4K}, + {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ + {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0}, + {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0}, + {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0}, + {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0}, + {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0}, + {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, 0}, + {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, 0}, + {"MX25L51235F", 0xc2201A, 0x0, 64 * 1024, 1024, 0}, + {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, 0}, +#endif +#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ + {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0}, + {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0}, + {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0}, + {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0}, + {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, 0}, + {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, 0}, + {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, 0}, + {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, 0}, + {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, 0}, + {"S25FL256S_256K", 0x010219, 0x4d00, 64 * 1024, 512, 0}, + {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, 0}, + {"S25FL512S_256K", 0x010220, 0x4d00, 64 * 1024, 1024, 0}, + {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, 0}, +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ + {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0}, + {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0}, + {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0}, + {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0}, + {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0}, + {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0}, + {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0}, + {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0}, + {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, SECT_4K}, + {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, SECT_4K}, + {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, SECT_4K}, + {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, SECT_4K}, + {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, SECT_4K}, + {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, SECT_4K}, + {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, SECT_4K}, + {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, SECT_4K}, + {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K}, + {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K}, + {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K}, + {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_SST /* SST */ + {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WP}, + {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WP}, + {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WP}, + {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WP}, + {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, SECT_4K}, + {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WP}, + {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WP}, + {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WP}, + {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WP}, + {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WP}, +#endif +#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ + {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0}, + {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0}, + {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0}, + {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, SECT_4K}, + {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, SECT_4K}, + {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, SECT_4K}, + {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, SECT_4K}, + {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, SECT_4K}, + {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, SECT_4K}, + {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, SECT_4K}, + {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, SECT_4K}, + {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, SECT_4K}, + {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, SECT_4K}, + {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, SECT_4K}, + {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, SECT_4K}, + {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, SECT_4K}, + {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, SECT_4K}, + {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, SECT_4K}, +#endif + /* + * Note: + * Below paired flash devices has similar spi_flash_params params. + * (S25FL129P_64K, S25FL128S_64K) + * (W25Q80BL, W25Q80BV) + * (W25Q16CL, W25Q16DV) + * (W25Q32BV, W25Q32FV_SPI) + * (W25Q64CV, W25Q64FV_SPI) + * (W25Q128BV, W25Q128FV_SPI) + * (W25Q32DW, W25Q32FV_QPI) + * (W25Q64DW, W25Q64FV_QPI) + * (W25Q128FW, W25Q128FV_QPI) + */ +}; + +static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, + u8 *idcode) +{ + const struct spi_flash_params *params; + struct spi_flash *flash; + int i; + u16 jedec = idcode[1] << 8 | idcode[2]; + u16 ext_jedec = idcode[3] << 8 | idcode[4]; + + /* Get the flash id (jedec = manuf_id + dev_id, ext_jedec) */ + for (i = 0; i < ARRAY_SIZE(spi_flash_params_table); i++) { + params = &spi_flash_params_table[i]; + if ((params->jedec >> 16) == idcode[0]) { + if ((params->jedec & 0xFFFF) == jedec) { + if (params->ext_jedec == 0) + break; + else if (params->ext_jedec == ext_jedec) + break; + } + } + } + + if (i == ARRAY_SIZE(spi_flash_params_table)) { + printf("SF: Unsupported flash IDs: "); + printf("manuf %02x, jedec %04x, ext_jedec %04x\n", + idcode[0], jedec, ext_jedec); + return NULL; + } + + flash = malloc(sizeof(*flash)); + if (!flash) { + debug("SF: Failed to allocate spi_flash\n"); + return NULL; + } + memset(flash, '\0', sizeof(*flash)); + + flash->spi = spi; + flash->name = params->name; + flash->memory_map = spi->memory_map; + + /* Assign spi_flash ops */ + flash->write = spi_flash_cmd_write_ops; +#ifdef CONFIG_SPI_FLASH_SST + if (params->flags & SST_WP) + flash->write = sst_write_wp; +#endif + flash->erase = spi_flash_cmd_erase_ops; + flash->read = spi_flash_cmd_read_ops; + + /* Compute the flash size */ + flash->page_size = (ext_jedec == 0x4d00) ? 512 : 256; + flash->sector_size = params->sector_size; + flash->size = flash->sector_size * params->nr_sectors; + + /* Compute erase sector and command */ + if (params->flags & SECT_4K) { + flash->erase_cmd = CMD_ERASE_4K; + flash->erase_size = 4096; + } else if (params->flags & SECT_32K) { + flash->erase_cmd = CMD_ERASE_32K; + flash->erase_size = 32768; + } else { + flash->erase_cmd = CMD_ERASE_64K; + flash->erase_size = flash->sector_size; + } + + /* Poll cmd seclection */ + flash->poll_cmd = CMD_READ_STATUS; +#ifdef CONFIG_SPI_FLASH_STMICRO + if (params->flags & E_FSR) + flash->poll_cmd = CMD_FLAG_STATUS; +#endif + + /* Configure the BAR - discover bank cmds and read current bank */ +#ifdef CONFIG_SPI_FLASH_BAR + u8 curr_bank = 0; + if (flash->size > SPI_FLASH_16MB_BOUN) { + flash->bank_read_cmd = (idcode[0] == 0x01) ? + CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR; + flash->bank_write_cmd = (idcode[0] == 0x01) ? + CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR; + + if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1, + &curr_bank, 1)) { + debug("SF: fail to read bank addr register\n"); + return NULL; + } + flash->bank_curr = curr_bank; + } else { + flash->bank_curr = curr_bank; + } +#endif + + /* Flash powers up read-only, so clear BP# bits */ +#if defined(CONFIG_SPI_FLASH_ATMEL) || \ + defined(CONFIG_SPI_FLASH_MACRONIX) || \ + defined(CONFIG_SPI_FLASH_SST) + spi_flash_cmd_write_status(flash, 0); +#endif + + return flash; +} + +#ifdef CONFIG_OF_CONTROL +int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) +{ + fdt_addr_t addr; + fdt_size_t size; + int node; + + /* If there is no node, do nothing */ + node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); + if (node < 0) + return 0; + + addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: Cannot decode address\n", __func__); + return 0; + } + + if (flash->size != size) { + debug("%s: Memory map must cover entire device\n", __func__); + return -1; + } + flash->memory_map = (void *)addr; + + return 0; +} +#endif /* CONFIG_OF_CONTROL */ + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + struct spi_flash *flash = NULL; + u8 idcode[5]; + int ret; + + /* Setup spi_slave */ + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + printf("SF: Failed to set up slave\n"); + return NULL; + } + + /* Claim spi bus */ + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); + if (ret) { + printf("SF: Failed to get idcodes\n"); + goto err_read_id; + } + +#ifdef DEBUG + printf("SF: Got idcodes\n"); + print_buffer(0, idcode, 1, sizeof(idcode), 0); +#endif + + /* Validate params from spi_flash_params table */ + flash = spi_flash_validate_params(spi, idcode); + if (!flash) + goto err_read_id; + +#ifdef CONFIG_OF_CONTROL + if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { + debug("SF: FDT decode error\n"); + goto err_read_id; + } +#endif +#ifndef CONFIG_SPL_BUILD + printf("SF: Detected %s with page size ", flash->name); + print_size(flash->page_size, ", erase size "); + print_size(flash->erase_size, ", total "); + print_size(flash->size, ""); + if (flash->memory_map) + printf(", mapped at %p", flash->memory_map); + puts("\n"); +#endif +#ifndef CONFIG_SPI_FLASH_BAR + if (flash->size > SPI_FLASH_16MB_BOUN) { + puts("SF: Warning - Only lower 16MiB accessible,"); + puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); + } +#endif + + /* Release spi bus */ + spi_release_bus(spi); + + return flash; + +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c deleted file mode 100644 index fa7ac8c..0000000 --- a/drivers/mtd/spi/spansion.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2009 Freescale Semiconductor, Inc. - * - * Author: Mingkai Hu (Mingkai.hu@freescale.com) - * Based on stmicro.c by Wolfgang Denk (wd@denx.de), - * TsiChung Liew (Tsi-Chung.Liew@freescale.com), - * and Jason McMullan (mcmullan@netapp.com) - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct spansion_spi_flash_params { - u16 idcode1; - u16 idcode2; - u16 pages_per_sector; - u16 nr_sectors; - const char *name; -}; - -static const struct spansion_spi_flash_params spansion_spi_flash_table[] = { - { - .idcode1 = 0x0213, - .idcode2 = 0, - .pages_per_sector = 256, - .nr_sectors = 16, - .name = "S25FL008A", - }, - { - .idcode1 = 0x0214, - .idcode2 = 0, - .pages_per_sector = 256, - .nr_sectors = 32, - .name = "S25FL016A", - }, - { - .idcode1 = 0x0215, - .idcode2 = 0, - .pages_per_sector = 256, - .nr_sectors = 64, - .name = "S25FL032A", - }, - { - .idcode1 = 0x0216, - .idcode2 = 0, - .pages_per_sector = 256, - .nr_sectors = 128, - .name = "S25FL064A", - }, - { - .idcode1 = 0x2018, - .idcode2 = 0x0301, - .pages_per_sector = 256, - .nr_sectors = 256, - .name = "S25FL128P_64K", - }, - { - .idcode1 = 0x2018, - .idcode2 = 0x0300, - .pages_per_sector = 1024, - .nr_sectors = 64, - .name = "S25FL128P_256K", - }, - { - .idcode1 = 0x0215, - .idcode2 = 0x4d00, - .pages_per_sector = 256, - .nr_sectors = 64, - .name = "S25FL032P", - }, - { - .idcode1 = 0x0216, - .idcode2 = 0x4d00, - .pages_per_sector = 256, - .nr_sectors = 128, - .name = "S25FL064P", - }, - { - .idcode1 = 0x2018, - .idcode2 = 0x4d01, - .pages_per_sector = 256, - .nr_sectors = 256, - .name = "S25FL129P_64K/S25FL128S_64K", - }, - { - .idcode1 = 0x0219, - .idcode2 = 0x4d01, - .pages_per_sector = 256, - .nr_sectors = 512, - .name = "S25FL256S_64K", - }, - { - .idcode1 = 0x0220, - .idcode2 = 0x4d01, - .pages_per_sector = 256, - .nr_sectors = 1024, - .name = "S25FL512S_64K", - }, -}; - -struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) -{ - const struct spansion_spi_flash_params *params; - struct spi_flash *flash; - unsigned int i; - unsigned short jedec, ext_jedec; - - jedec = idcode[1] << 8 | idcode[2]; - ext_jedec = idcode[3] << 8 | idcode[4]; - - for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) { - params = &spansion_spi_flash_table[i]; - if (params->idcode1 == jedec) { - if (params->idcode2 == ext_jedec) - break; - } - } - - if (i == ARRAY_SIZE(spansion_spi_flash_table)) { - debug("SF: Unsupported SPANSION ID %04x %04x\n", - jedec, ext_jedec); - return NULL; - } - - flash = spi_flash_alloc_base(spi, params->name); - if (!flash) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - flash->page_size = 256; - flash->sector_size = 256 * params->pages_per_sector; - flash->size = flash->sector_size * params->nr_sectors; - - return flash; -} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c deleted file mode 100644 index 5d5055f..0000000 --- a/drivers/mtd/spi/spi_flash.c +++ /dev/null @@ -1,615 +0,0 @@ -/* - * SPI flash interface - * - * Copyright (C) 2008 Atmel Corporation - * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <fdtdec.h> -#include <malloc.h> -#include <spi.h> -#include <spi_flash.h> -#include <watchdog.h> - -#include "spi_flash_internal.h" - -DECLARE_GLOBAL_DATA_PTR; - -static void spi_flash_addr(u32 addr, u8 *cmd) -{ - /* cmd[0] is actual command */ - cmd[1] = addr >> 16; - cmd[2] = addr >> 8; - cmd[3] = addr >> 0; -} - -static int spi_flash_read_write(struct spi_slave *spi, - const u8 *cmd, size_t cmd_len, - const u8 *data_out, u8 *data_in, - size_t data_len) -{ - unsigned long flags = SPI_XFER_BEGIN; - int ret; - - if (data_len == 0) - flags |= SPI_XFER_END; - - ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); - if (ret) { - debug("SF: Failed to send command (%zu bytes): %d\n", - cmd_len, ret); - } else if (data_len != 0) { - ret = spi_xfer(spi, data_len * 8, data_out, data_in, - SPI_XFER_END); - if (ret) - debug("SF: Failed to transfer %zu bytes of data: %d\n", - data_len, ret); - } - - return ret; -} - -int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) -{ - return spi_flash_cmd_read(spi, &cmd, 1, response, len); -} - -int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len) -{ - return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); -} - -int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, - const void *data, size_t data_len) -{ - return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); -} - -int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) -{ - struct spi_slave *spi = flash->spi; - unsigned long timebase; - int ret; - u8 status; - u8 check_status = 0x0; - u8 poll_bit = STATUS_WIP; - u8 cmd = flash->poll_cmd; - - if (cmd == CMD_FLAG_STATUS) { - poll_bit = STATUS_PEC; - check_status = poll_bit; - } - - ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); - if (ret) { - debug("SF: fail to read %s status register\n", - cmd == CMD_READ_STATUS ? "read" : "flag"); - return ret; - } - - timebase = get_timer(0); - do { - WATCHDOG_RESET(); - - ret = spi_xfer(spi, 8, NULL, &status, 0); - if (ret) - return -1; - - if ((status & poll_bit) == check_status) - break; - - } while (get_timer(timebase) < timeout); - - spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); - - if ((status & poll_bit) == check_status) - return 0; - - /* Timed out */ - debug("SF: time out!\n"); - return -1; -} - -int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, const void *buf, size_t buf_len) -{ - struct spi_slave *spi = flash->spi; - unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; - int ret; - - if (buf == NULL) - timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: unable to claim SPI bus\n"); - return ret; - } - - ret = spi_flash_cmd_write_enable(flash); - if (ret < 0) { - debug("SF: enabling write failed\n"); - return ret; - } - - ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); - if (ret < 0) { - debug("SF: write cmd failed\n"); - return ret; - } - - ret = spi_flash_cmd_wait_ready(flash, timeout); - if (ret < 0) { - debug("SF: write %s timed out\n", - timeout == SPI_FLASH_PROG_TIMEOUT ? - "program" : "page erase"); - return ret; - } - - spi_release_bus(spi); - - return ret; -} - -int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) -{ - u32 erase_size; - u8 cmd[4]; - int ret = -1; - - erase_size = flash->sector_size; - if (offset % erase_size || len % erase_size) { - debug("SF: Erase offset/length not multiple of erase size\n"); - return -1; - } - - if (erase_size == 4096) - cmd[0] = CMD_ERASE_4K; - else - cmd[0] = CMD_ERASE_64K; - - while (len) { -#ifdef CONFIG_SPI_FLASH_BAR - u8 bank_sel; - - bank_sel = offset / SPI_FLASH_16MB_BOUN; - - ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); - if (ret) { - debug("SF: fail to set bank%d\n", bank_sel); - return ret; - } -#endif - spi_flash_addr(offset, cmd); - - debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], - cmd[2], cmd[3], offset); - - ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); - if (ret < 0) { - debug("SF: erase failed\n"); - break; - } - - offset += erase_size; - len -= erase_size; - } - - return ret; -} - -int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, - size_t len, const void *buf) -{ - unsigned long byte_addr, page_size; - size_t chunk_len, actual; - u8 cmd[4]; - int ret = -1; - - page_size = flash->page_size; - - cmd[0] = CMD_PAGE_PROGRAM; - for (actual = 0; actual < len; actual += chunk_len) { -#ifdef CONFIG_SPI_FLASH_BAR - u8 bank_sel; - - bank_sel = offset / SPI_FLASH_16MB_BOUN; - - ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); - if (ret) { - debug("SF: fail to set bank%d\n", bank_sel); - return ret; - } -#endif - byte_addr = offset % page_size; - chunk_len = min(len - actual, page_size - byte_addr); - - if (flash->spi->max_write_size) - chunk_len = min(chunk_len, flash->spi->max_write_size); - - spi_flash_addr(offset, cmd); - - debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", - buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); - - ret = spi_flash_write_common(flash, cmd, sizeof(cmd), - buf + actual, chunk_len); - if (ret < 0) { - debug("SF: write failed\n"); - break; - } - - offset += chunk_len; - } - - return ret; -} - -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len) -{ - struct spi_slave *spi = flash->spi; - int ret; - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: unable to claim SPI bus\n"); - return ret; - } - - ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); - if (ret < 0) { - debug("SF: read cmd failed\n"); - return ret; - } - - spi_release_bus(spi); - - return ret; -} - -int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, - size_t len, void *data) -{ - u8 cmd[5], bank_sel = 0; - u32 remain_len, read_len; - int ret = -1; - - /* Handle memory-mapped SPI */ - if (flash->memory_map) { - memcpy(data, flash->memory_map + offset, len); - return 0; - } - - cmd[0] = CMD_READ_ARRAY_FAST; - cmd[4] = 0x00; - - while (len) { -#ifdef CONFIG_SPI_FLASH_BAR - bank_sel = offset / SPI_FLASH_16MB_BOUN; - - ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); - if (ret) { - debug("SF: fail to set bank%d\n", bank_sel); - return ret; - } -#endif - remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); - if (len < remain_len) - read_len = len; - else - read_len = remain_len; - - spi_flash_addr(offset, cmd); - - ret = spi_flash_read_common(flash, cmd, sizeof(cmd), - data, read_len); - if (ret < 0) { - debug("SF: read failed\n"); - break; - } - - offset += read_len; - len -= read_len; - data += read_len; - } - - return ret; -} - -int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) -{ - u8 cmd; - int ret; - - cmd = CMD_WRITE_STATUS; - ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); - if (ret < 0) { - debug("SF: fail to write status register\n"); - return ret; - } - - return 0; -} - -#ifdef CONFIG_SPI_FLASH_BAR -int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) -{ - u8 cmd; - int ret; - - if (flash->bank_curr == bank_sel) { - debug("SF: not require to enable bank%d\n", bank_sel); - return 0; - } - - cmd = flash->bank_write_cmd; - ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); - if (ret < 0) { - debug("SF: fail to write bank register\n"); - return ret; - } - flash->bank_curr = bank_sel; - - return 0; -} - -int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0) -{ - u8 cmd; - u8 curr_bank = 0; - - /* discover bank cmds */ - switch (idcode0) { - case SPI_FLASH_SPANSION_IDCODE0: - flash->bank_read_cmd = CMD_BANKADDR_BRRD; - flash->bank_write_cmd = CMD_BANKADDR_BRWR; - break; - case SPI_FLASH_STMICRO_IDCODE0: - case SPI_FLASH_WINBOND_IDCODE0: - flash->bank_read_cmd = CMD_EXTNADDR_RDEAR; - flash->bank_write_cmd = CMD_EXTNADDR_WREAR; - break; - default: - printf("SF: Unsupported bank commands %02x\n", idcode0); - return -1; - } - - /* read the bank reg - on which bank the flash is in currently */ - cmd = flash->bank_read_cmd; - if (flash->size > SPI_FLASH_16MB_BOUN) { - if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) { - debug("SF: fail to read bank addr register\n"); - return -1; - } - flash->bank_curr = curr_bank; - } else { - flash->bank_curr = curr_bank; - } - - return 0; -} -#endif - -#ifdef CONFIG_OF_CONTROL -int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) -{ - fdt_addr_t addr; - fdt_size_t size; - int node; - - /* If there is no node, do nothing */ - node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); - if (node < 0) - return 0; - - addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); - if (addr == FDT_ADDR_T_NONE) { - debug("%s: Cannot decode address\n", __func__); - return 0; - } - - if (flash->size != size) { - debug("%s: Memory map must cover entire device\n", __func__); - return -1; - } - flash->memory_map = (void *)addr; - - return 0; -} -#endif /* CONFIG_OF_CONTROL */ - -/* - * The following table holds all device probe functions - * - * shift: number of continuation bytes before the ID - * idcode: the expected IDCODE or 0xff for non JEDEC devices - * probe: the function to call - * - * Non JEDEC devices should be ordered in the table such that - * the probe functions with best detection algorithms come first. - * - * Several matching entries are permitted, they will be tried - * in sequence until a probe function returns non NULL. - * - * IDCODE_CONT_LEN may be redefined if a device needs to declare a - * larger "shift" value. IDCODE_PART_LEN generally shouldn't be - * changed. This is the max number of bytes probe functions may - * examine when looking up part-specific identification info. - * - * Probe functions will be given the idcode buffer starting at their - * manu id byte (the "idcode" in the table below). In other words, - * all of the continuation bytes will be skipped (the "shift" below). - */ -#define IDCODE_CONT_LEN 0 -#define IDCODE_PART_LEN 5 -static const struct { - const u8 shift; - const u8 idcode; - struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode); -} flashes[] = { - /* Keep it sorted by define name */ -#ifdef CONFIG_SPI_FLASH_ATMEL - { 0, 0x1f, spi_flash_probe_atmel, }, -#endif -#ifdef CONFIG_SPI_FLASH_EON - { 0, 0x1c, spi_flash_probe_eon, }, -#endif -#ifdef CONFIG_SPI_FLASH_GIGADEVICE - { 0, 0xc8, spi_flash_probe_gigadevice, }, -#endif -#ifdef CONFIG_SPI_FLASH_MACRONIX - { 0, 0xc2, spi_flash_probe_macronix, }, -#endif -#ifdef CONFIG_SPI_FLASH_SPANSION - { 0, 0x01, spi_flash_probe_spansion, }, -#endif -#ifdef CONFIG_SPI_FLASH_SST - { 0, 0xbf, spi_flash_probe_sst, }, -#endif -#ifdef CONFIG_SPI_FLASH_STMICRO - { 0, 0x20, spi_flash_probe_stmicro, }, -#endif -#ifdef CONFIG_SPI_FLASH_WINBOND - { 0, 0xef, spi_flash_probe_winbond, }, -#endif -#ifdef CONFIG_SPI_FRAM_RAMTRON - { 6, 0xc2, spi_fram_probe_ramtron, }, -# undef IDCODE_CONT_LEN -# define IDCODE_CONT_LEN 6 -#endif - /* Keep it sorted by best detection */ -#ifdef CONFIG_SPI_FLASH_STMICRO - { 0, 0xff, spi_flash_probe_stmicro, }, -#endif -#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC - { 0, 0xff, spi_fram_probe_ramtron, }, -#endif -}; -#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) - -struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int spi_mode) -{ - struct spi_slave *spi; - struct spi_flash *flash = NULL; - int ret, i, shift; - u8 idcode[IDCODE_LEN], *idp; - - spi = spi_setup_slave(bus, cs, max_hz, spi_mode); - if (!spi) { - printf("SF: Failed to set up slave\n"); - return NULL; - } - - ret = spi_claim_bus(spi); - if (ret) { - debug("SF: Failed to claim SPI bus: %d\n", ret); - goto err_claim_bus; - } - - /* Read the ID codes */ - ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); - if (ret) - goto err_read_id; - -#ifdef DEBUG - printf("SF: Got idcodes\n"); - print_buffer(0, idcode, 1, sizeof(idcode), 0); -#endif - - /* count the number of continuation bytes */ - for (shift = 0, idp = idcode; - shift < IDCODE_CONT_LEN && *idp == 0x7f; - ++shift, ++idp) - continue; - - /* search the table for matches in shift and id */ - for (i = 0; i < ARRAY_SIZE(flashes); ++i) - if (flashes[i].shift == shift && flashes[i].idcode == *idp) { - /* we have a match, call probe */ - flash = flashes[i].probe(spi, idp); - if (flash) - break; - } - - if (!flash) { - printf("SF: Unsupported manufacturer %02x\n", *idp); - goto err_manufacturer_probe; - } - -#ifdef CONFIG_SPI_FLASH_BAR - /* Configure the BAR - disover bank cmds and read current bank */ - ret = spi_flash_bank_config(flash, *idp); - if (ret < 0) - goto err_manufacturer_probe; -#endif - -#ifdef CONFIG_OF_CONTROL - if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { - debug("SF: FDT decode error\n"); - goto err_manufacturer_probe; - } -#endif -#ifndef CONFIG_SPL_BUILD - printf("SF: Detected %s with page size ", flash->name); - print_size(flash->sector_size, ", total "); - print_size(flash->size, ""); - if (flash->memory_map) - printf(", mapped at %p", flash->memory_map); - puts("\n"); -#endif -#ifndef CONFIG_SPI_FLASH_BAR - if (flash->size > SPI_FLASH_16MB_BOUN) { - puts("SF: Warning - Only lower 16MiB accessible,"); - puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); - } -#endif - - spi_release_bus(spi); - - return flash; - -err_manufacturer_probe: -err_read_id: - spi_release_bus(spi); -err_claim_bus: - spi_free_slave(spi); - return NULL; -} - -void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi, - const char *name) -{ - struct spi_flash *flash; - void *ptr; - - ptr = malloc(size); - if (!ptr) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - memset(ptr, '\0', size); - flash = (struct spi_flash *)(ptr + offset); - - /* Set up some basic fields - caller will sort out sizes */ - flash->spi = spi; - flash->name = name; - flash->poll_cmd = CMD_READ_STATUS; - - flash->read = spi_flash_cmd_read_fast; - flash->write = spi_flash_cmd_write_multi; - flash->erase = spi_flash_cmd_erase; - - return flash; -} - -void spi_flash_free(struct spi_flash *flash) -{ - spi_free_slave(flash->spi); - free(flash); -} diff --git a/drivers/mtd/spi/sst.c b/drivers/mtd/spi/sst.c deleted file mode 100644 index 256867c..0000000 --- a/drivers/mtd/spi/sst.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Driver for SST serial flashes - * - * (C) Copyright 2000-2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * Copyright 2008, Network Appliance Inc. - * Jason McMullan <mcmullan@netapp.com> - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * Copyright (c) 2008-2009 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -#define CMD_SST_BP 0x02 /* Byte Program */ -#define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ - -#define SST_SR_WIP (1 << 0) /* Write-in-Progress */ -#define SST_SR_WEL (1 << 1) /* Write enable */ -#define SST_SR_BP0 (1 << 2) /* Block Protection 0 */ -#define SST_SR_BP1 (1 << 3) /* Block Protection 1 */ -#define SST_SR_BP2 (1 << 4) /* Block Protection 2 */ -#define SST_SR_AAI (1 << 6) /* Addressing mode */ -#define SST_SR_BPL (1 << 7) /* BP bits lock */ - -#define SST_FEAT_WP (1 << 0) /* Supports AAI word program */ -#define SST_FEAT_MBP (1 << 1) /* Supports multibyte program */ - -struct sst_spi_flash_params { - u8 idcode1; - u8 flags; - u16 nr_sectors; - const char *name; -}; - -struct sst_spi_flash { - struct spi_flash flash; - const struct sst_spi_flash_params *params; -}; - -static const struct sst_spi_flash_params sst_spi_flash_table[] = { - { - .idcode1 = 0x8d, - .flags = SST_FEAT_WP, - .nr_sectors = 128, - .name = "SST25VF040B", - }, - { - .idcode1 = 0x8e, - .flags = SST_FEAT_WP, - .nr_sectors = 256, - .name = "SST25VF080B", - }, - { - .idcode1 = 0x41, - .flags = SST_FEAT_WP, - .nr_sectors = 512, - .name = "SST25VF016B", - }, - { - .idcode1 = 0x4a, - .flags = SST_FEAT_WP, - .nr_sectors = 1024, - .name = "SST25VF032B", - }, - { - .idcode1 = 0x4b, - .flags = SST_FEAT_MBP, - .nr_sectors = 2048, - .name = "SST25VF064C", - }, - { - .idcode1 = 0x01, - .flags = SST_FEAT_WP, - .nr_sectors = 16, - .name = "SST25WF512", - }, - { - .idcode1 = 0x02, - .flags = SST_FEAT_WP, - .nr_sectors = 32, - .name = "SST25WF010", - }, - { - .idcode1 = 0x03, - .flags = SST_FEAT_WP, - .nr_sectors = 64, - .name = "SST25WF020", - }, - { - .idcode1 = 0x04, - .flags = SST_FEAT_WP, - .nr_sectors = 128, - .name = "SST25WF040", - }, - { - .idcode1 = 0x05, - .flags = SST_FEAT_WP, - .nr_sectors = 256, - .name = "SST25WF080", - }, -}; - -static int -sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) -{ - int ret; - u8 cmd[4] = { - CMD_SST_BP, - offset >> 16, - offset >> 8, - offset, - }; - - debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", - spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); - - ret = spi_flash_cmd_write_enable(flash); - if (ret) - return ret; - - ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); - if (ret) - return ret; - - return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -} - -static int -sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf) -{ - size_t actual, cmd_len; - int ret; - u8 cmd[4]; - - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); - return ret; - } - - /* If the data is not word aligned, write out leading single byte */ - actual = offset % 2; - if (actual) { - ret = sst_byte_write(flash, offset, buf); - if (ret) - goto done; - } - offset += actual; - - ret = spi_flash_cmd_write_enable(flash); - if (ret) - goto done; - - cmd_len = 4; - cmd[0] = CMD_SST_AAI_WP; - cmd[1] = offset >> 16; - cmd[2] = offset >> 8; - cmd[3] = offset; - - for (; actual < len - 1; actual += 2) { - debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", - spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, - cmd[0], offset); - - ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, - buf + actual, 2); - if (ret) { - debug("SF: sst word program failed\n"); - break; - } - - ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); - if (ret) - break; - - cmd_len = 1; - offset += 2; - } - - if (!ret) - ret = spi_flash_cmd_write_disable(flash); - - /* If there is a single trailing byte, write it out */ - if (!ret && actual != len) - ret = sst_byte_write(flash, offset, buf + actual); - - done: - debug("SF: sst: program %s %zu bytes @ 0x%zx\n", - ret ? "failure" : "success", len, offset - actual); - - spi_release_bus(flash->spi); - return ret; -} - -struct spi_flash * -spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode) -{ - const struct sst_spi_flash_params *params; - struct sst_spi_flash *stm; - size_t i; - - for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) { - params = &sst_spi_flash_table[i]; - if (params->idcode1 == idcode[2]) - break; - } - - if (i == ARRAY_SIZE(sst_spi_flash_table)) { - debug("SF: Unsupported SST ID %02x\n", idcode[1]); - return NULL; - } - - stm = spi_flash_alloc(struct sst_spi_flash, spi, params->name); - if (!stm) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - stm->params = params; - - if (stm->params->flags & SST_FEAT_WP) - stm->flash.write = sst_write_wp; - stm->flash.page_size = 256; - stm->flash.sector_size = 4096; - stm->flash.size = stm->flash.sector_size * params->nr_sectors; - - /* Flash powers up read-only, so clear BP# bits */ - spi_flash_cmd_write_status(&stm->flash, 0); - - return &stm->flash; -} diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c deleted file mode 100644 index c5fa64e..0000000 --- a/drivers/mtd/spi/stmicro.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * (C) Copyright 2000-2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * Copyright 2008, Network Appliance Inc. - * Jason McMullan <mcmullan@netapp.com> - * - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -/* M25Pxx-specific commands */ -#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */ - -struct stmicro_spi_flash_params { - u16 id; - u16 pages_per_sector; - u16 nr_sectors; - const char *name; -}; - -static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = { - { - .id = 0x2011, - .pages_per_sector = 128, - .nr_sectors = 4, - .name = "M25P10", - }, - { - .id = 0x2015, - .pages_per_sector = 256, - .nr_sectors = 32, - .name = "M25P16", - }, - { - .id = 0x2012, - .pages_per_sector = 256, - .nr_sectors = 4, - .name = "M25P20", - }, - { - .id = 0x2016, - .pages_per_sector = 256, - .nr_sectors = 64, - .name = "M25P32", - }, - { - .id = 0x2013, - .pages_per_sector = 256, - .nr_sectors = 8, - .name = "M25P40", - }, - { - .id = 0x2017, - .pages_per_sector = 256, - .nr_sectors = 128, - .name = "M25P64", - }, - { - .id = 0x2014, - .pages_per_sector = 256, - .nr_sectors = 16, - .name = "M25P80", - }, - { - .id = 0x2018, - .pages_per_sector = 1024, - .nr_sectors = 64, - .name = "M25P128", - }, - { - .id = 0xba16, - .pages_per_sector = 256, - .nr_sectors = 64, - .name = "N25Q32", - }, - { - .id = 0xbb16, - .pages_per_sector = 256, - .nr_sectors = 64, - .name = "N25Q32A", - }, - { - .id = 0xba17, - .pages_per_sector = 256, - .nr_sectors = 128, - .name = "N25Q064", - }, - { - .id = 0xbb17, - .pages_per_sector = 256, - .nr_sectors = 128, - .name = "N25Q64A", - }, - { - .id = 0xba18, - .pages_per_sector = 256, - .nr_sectors = 256, - .name = "N25Q128", - }, - { - .id = 0xbb18, - .pages_per_sector = 256, - .nr_sectors = 256, - .name = "N25Q128A", - }, - { - .id = 0xba19, - .pages_per_sector = 256, - .nr_sectors = 512, - .name = "N25Q256", - }, - { - .id = 0xbb19, - .pages_per_sector = 256, - .nr_sectors = 512, - .name = "N25Q256A", - }, - { - .id = 0xba20, - .pages_per_sector = 256, - .nr_sectors = 1024, - .name = "N25Q512", - }, - { - .id = 0xbb20, - .pages_per_sector = 256, - .nr_sectors = 1024, - .name = "N25Q512A", - }, - { - .id = 0xba21, - .pages_per_sector = 256, - .nr_sectors = 2048, - .name = "N25Q1024", - }, - { - .id = 0xbb21, - .pages_per_sector = 256, - .nr_sectors = 2048, - .name = "N25Q1024A", - }, -}; - -struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode) -{ - const struct stmicro_spi_flash_params *params; - struct spi_flash *flash; - unsigned int i; - u16 id; - - if (idcode[0] == 0xff) { - i = spi_flash_cmd(spi, CMD_M25PXX_RES, - idcode, 4); - if (i) - return NULL; - if ((idcode[3] & 0xf0) == 0x10) { - idcode[0] = 0x20; - idcode[1] = 0x20; - idcode[2] = idcode[3] + 1; - } else { - return NULL; - } - } - - id = ((idcode[1] << 8) | idcode[2]); - - for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) { - params = &stmicro_spi_flash_table[i]; - if (params->id == id) - break; - } - - if (i == ARRAY_SIZE(stmicro_spi_flash_table)) { - debug("SF: Unsupported STMicro ID %04x\n", id); - return NULL; - } - - flash = spi_flash_alloc_base(spi, params->name); - if (!flash) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - flash->page_size = 256; - flash->sector_size = 256 * params->pages_per_sector; - flash->size = flash->sector_size * params->nr_sectors; - - /* for >= 512MiB flashes, use flag status instead of read_status */ - if (flash->size >= 0x4000000) - flash->poll_cmd = CMD_FLAG_STATUS; - - return flash; -} diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c deleted file mode 100644 index b31911a..0000000 --- a/drivers/mtd/spi/winbond.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2008, Network Appliance Inc. - * Author: Jason McMullan <mcmullan <at> netapp.com> - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct winbond_spi_flash_params { - uint16_t id; - uint16_t nr_blocks; - const char *name; -}; - -static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { - { - .id = 0x2014, - .nr_blocks = 16, - .name = "W25P80", - }, - { - .id = 0x2015, - .nr_blocks = 32, - .name = "W25P16", - }, - { - .id = 0x2016, - .nr_blocks = 64, - .name = "W25P32", - }, - { - .id = 0x3013, - .nr_blocks = 8, - .name = "W25X40", - }, - { - .id = 0x3015, - .nr_blocks = 32, - .name = "W25X16", - }, - { - .id = 0x3016, - .nr_blocks = 64, - .name = "W25X32", - }, - { - .id = 0x3017, - .nr_blocks = 128, - .name = "W25X64", - }, - { - .id = 0x4014, - .nr_blocks = 16, - .name = "W25Q80BL/W25Q80BV", - }, - { - .id = 0x4015, - .nr_blocks = 32, - .name = "W25Q16CL/W25Q16DV", - }, - { - .id = 0x4016, - .nr_blocks = 64, - .name = "W25Q32BV/W25Q32FV_SPI", - }, - { - .id = 0x4017, - .nr_blocks = 128, - .name = "W25Q64CV/W25Q64FV_SPI", - }, - { - .id = 0x4018, - .nr_blocks = 256, - .name = "W25Q128BV/W25Q128FV_SPI", - }, - { - .id = 0x4019, - .nr_blocks = 512, - .name = "W25Q256", - }, - { - .id = 0x5014, - .nr_blocks = 16, - .name = "W25Q80BW", - }, - { - .id = 0x6015, - .nr_blocks = 32, - .name = "W25Q16DW", - }, - { - .id = 0x6016, - .nr_blocks = 64, - .name = "W25Q32DW/W25Q32FV_QPI", - }, - { - .id = 0x6017, - .nr_blocks = 128, - .name = "W25Q64DW/W25Q64FV_QPI", - }, - { - .id = 0x6018, - .nr_blocks = 256, - .name = "W25Q128FW/W25Q128FV_QPI", - }, -}; - -struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode) -{ - const struct winbond_spi_flash_params *params; - struct spi_flash *flash; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) { - params = &winbond_spi_flash_table[i]; - if (params->id == ((idcode[1] << 8) | idcode[2])) - break; - } - - if (i == ARRAY_SIZE(winbond_spi_flash_table)) { - debug("SF: Unsupported Winbond ID %02x%02x\n", - idcode[1], idcode[2]); - return NULL; - } - - flash = spi_flash_alloc_base(spi, params->name); - if (!flash) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - flash->page_size = 256; - flash->sector_size = (idcode[1] == 0x20) ? 65536 : 4096; - flash->size = 4096 * 16 * params->nr_blocks; - - return flash; -} diff --git a/drivers/net/4xx_enet.c b/drivers/net/4xx_enet.c index c98867d..381ec42 100644 --- a/drivers/net/4xx_enet.c +++ b/drivers/net/4xx_enet.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifier: GPL-2.0 ibm-pibs + * SPDX-License-Identifier: GPL-2.0 IBM-pibs */ /*-----------------------------------------------------------------------------+ * diff --git a/drivers/net/npe/miiphy.c b/drivers/net/npe/miiphy.c index 07fcb60..002fb81 100644 --- a/drivers/net/npe/miiphy.c +++ b/drivers/net/npe/miiphy.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifier: GPL-2.0 ibm-pibs + * SPDX-License-Identifier: GPL-2.0 IBM-pibs */ /*-----------------------------------------------------------------------------+ | diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 5936f9b..60ed92d 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -7,7 +7,7 @@ * Copyright 2010-2011 Freescale Semiconductor, Inc. * author Andy Fleming * - * Some code get from linux kenrel + * Some code copied from linux kernel * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org> */ #include <miiphy.h> diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index c4ed8ba..86ba6b5 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -297,7 +297,7 @@ void pciauto_config_init(struct pci_controller *hose) { int i; - hose->pci_io = hose->pci_mem = NULL; + hose->pci_io = hose->pci_mem = hose->pci_prefetch = NULL; for (i = 0; i < hose->region_count; i++) { switch(hose->regions[i].flags) { diff --git a/drivers/power/power_core.c b/drivers/power/power_core.c index d79971b..29ccc83 100644 --- a/drivers/power/power_core.c +++ b/drivers/power/power_core.c @@ -184,18 +184,21 @@ int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc < 4) return CMD_RET_USAGE; + if (!p->pbat) { + printf("%s is not a battery\n", p->name); + return CMD_RET_FAILURE; + } + if (strcmp(argv[3], "state") == 0) p->fg->fg_battery_check(p->pbat->fg, p); if (strcmp(argv[3], "charge") == 0) { - if (p->pbat) { - printf("BAT: %s charging (ctrl+c to break)\n", - p->name); - if (p->low_power_mode) - p->low_power_mode(); - if (p->pbat->battery_charge) - p->pbat->battery_charge(p); - } + printf("BAT: %s charging (ctrl+c to break)\n", + p->name); + if (p->low_power_mode) + p->low_power_mode(); + if (p->pbat->battery_charge) + p->pbat->battery_charge(p); } return CMD_RET_SUCCESS; diff --git a/drivers/sound/max98095.c b/drivers/sound/max98095.c index d69db58..febf419 100644 --- a/drivers/sound/max98095.c +++ b/drivers/sound/max98095.c @@ -52,7 +52,7 @@ int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, static int max98095_i2c_write(unsigned int reg, unsigned char data) { debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n", - __func__, reg, data); + __func__, reg, data); return i2c_write(g_max98095_i2c_dev_addr, reg, 1, &data, 1); } @@ -71,7 +71,7 @@ static unsigned int max98095_i2c_read(unsigned int reg, unsigned char *data) ret = i2c_read(g_max98095_i2c_dev_addr, reg, 1, data, 1); if (ret != 0) { debug("%s: Error while reading register %#04x\n", - __func__, reg); + __func__, reg); return -1; } @@ -138,43 +138,57 @@ static int rate_value(int rate, u8 *value) * @return -1 for error and 0 Success. */ static int max98095_hw_params(struct max98095_priv *max98095, - unsigned int rate, unsigned int bits_per_sample) + enum en_max_audio_interface aif_id, + unsigned int rate, unsigned int bits_per_sample) { u8 regval; int error; + unsigned short M98095_DAI_CLKMODE; + unsigned short M98095_DAI_FORMAT; + unsigned short M98095_DAI_FILTERS; + + if (aif_id == AIF1) { + M98095_DAI_CLKMODE = M98095_027_DAI1_CLKMODE; + M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT; + M98095_DAI_FILTERS = M98095_02E_DAI1_FILTERS; + } else { + M98095_DAI_CLKMODE = M98095_031_DAI2_CLKMODE; + M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT; + M98095_DAI_FILTERS = M98095_038_DAI2_FILTERS; + } switch (bits_per_sample) { case 16: - error = max98095_update_bits(M98095_034_DAI2_FORMAT, - M98095_DAI_WS, 0); + error = max98095_update_bits(M98095_DAI_FORMAT, + M98095_DAI_WS, 0); break; case 24: - error = max98095_update_bits(M98095_034_DAI2_FORMAT, - M98095_DAI_WS, M98095_DAI_WS); + error = max98095_update_bits(M98095_DAI_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); break; default: debug("%s: Illegal bits per sample %d.\n", - __func__, bits_per_sample); + __func__, bits_per_sample); return -1; } if (rate_value(rate, ®val)) { debug("%s: Failed to set sample rate to %d.\n", - __func__, rate); + __func__, rate); return -1; } max98095->rate = rate; - error |= max98095_update_bits(M98095_031_DAI2_CLKMODE, - M98095_CLKMODE_MASK, regval); + error |= max98095_update_bits(M98095_DAI_CLKMODE, + M98095_CLKMODE_MASK, regval); /* Update sample rate mode */ if (rate < 50000) - error |= max98095_update_bits(M98095_038_DAI2_FILTERS, - M98095_DAI_DHF, 0); + error |= max98095_update_bits(M98095_DAI_FILTERS, + M98095_DAI_DHF, 0); else - error |= max98095_update_bits(M98095_038_DAI2_FILTERS, - M98095_DAI_DHF, M98095_DAI_DHF); + error |= max98095_update_bits(M98095_DAI_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); if (error < 0) { debug("%s: Error setting hardware params.\n", __func__); @@ -193,7 +207,7 @@ static int max98095_hw_params(struct max98095_priv *max98095, * @return -1 for error and 0 success. */ static int max98095_set_sysclk(struct max98095_priv *max98095, - unsigned int freq) + unsigned int freq) { int error = 0; @@ -235,22 +249,39 @@ static int max98095_set_sysclk(struct max98095_priv *max98095, * * @return -1 for error and 0 Success. */ -static int max98095_set_fmt(struct max98095_priv *max98095, int fmt) +static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, + enum en_max_audio_interface aif_id) { u8 regval = 0; int error = 0; + unsigned short M98095_DAI_CLKCFG_HI; + unsigned short M98095_DAI_CLKCFG_LO; + unsigned short M98095_DAI_FORMAT; + unsigned short M98095_DAI_CLOCK; if (fmt == max98095->fmt) return 0; max98095->fmt = fmt; + if (aif_id == AIF1) { + M98095_DAI_CLKCFG_HI = M98095_028_DAI1_CLKCFG_HI; + M98095_DAI_CLKCFG_LO = M98095_029_DAI1_CLKCFG_LO; + M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT; + M98095_DAI_CLOCK = M98095_02B_DAI1_CLOCK; + } else { + M98095_DAI_CLKCFG_HI = M98095_032_DAI2_CLKCFG_HI; + M98095_DAI_CLKCFG_LO = M98095_033_DAI2_CLKCFG_LO; + M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT; + M98095_DAI_CLOCK = M98095_035_DAI2_CLOCK; + } + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* Slave mode PLL */ - error |= max98095_i2c_write(M98095_032_DAI2_CLKCFG_HI, + error |= max98095_i2c_write(M98095_DAI_CLKCFG_HI, 0x80); - error |= max98095_i2c_write(M98095_033_DAI2_CLKCFG_LO, + error |= max98095_i2c_write(M98095_DAI_CLKCFG_LO, 0x00); break; case SND_SOC_DAIFMT_CBM_CFM: @@ -292,12 +323,13 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt) return -1; } - error |= max98095_update_bits(M98095_034_DAI2_FORMAT, - M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | - M98095_DAI_WCI, regval); + error |= max98095_update_bits(M98095_DAI_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | + M98095_DAI_BCI | M98095_DAI_WCI, + regval); - error |= max98095_i2c_write(M98095_035_DAI2_CLOCK, - M98095_DAI_BSEL64); + error |= max98095_i2c_write(M98095_DAI_CLOCK, + M98095_DAI_BSEL64); if (error < 0) { debug("%s: Error setting i2s format.\n", __func__); @@ -354,7 +386,8 @@ static int max98095_reset(void) * * @returns -1 for error and 0 Success. */ -static int max98095_device_init(struct max98095_priv *max98095) +static int max98095_device_init(struct max98095_priv *max98095, + enum en_max_audio_interface aif_id) { unsigned char id; int error = 0; @@ -374,7 +407,7 @@ static int max98095_device_init(struct max98095_priv *max98095) error = max98095_i2c_read(M98095_0FF_REV_ID, &id); if (error < 0) { debug("%s: Failure reading hardware revision: %d\n", - __func__, id); + __func__, id); goto err_access; } debug("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A'); @@ -385,26 +418,28 @@ static int max98095_device_init(struct max98095_priv *max98095) * initialize registers to hardware default configuring audio * interface2 to DAC */ - error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, - M98095_DAI2M_TO_DACL|M98095_DAI2M_TO_DACR); + if (aif_id == AIF1) + error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + M98095_DAI1L_TO_DACL | + M98095_DAI1R_TO_DACR); + else + error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + M98095_DAI2M_TO_DACL | + M98095_DAI2M_TO_DACR); error |= max98095_i2c_write(M98095_092_PWR_EN_OUT, - M98095_SPK_SPREADSPECTRUM); - error |= max98095_i2c_write(M98095_045_CFG_DSP, M98095_DSPNORMAL); + M98095_SPK_SPREADSPECTRUM); error |= max98095_i2c_write(M98095_04E_CFG_HP, M98095_HPNORMAL); - - error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, - M98095_S1NORMAL|M98095_SDATA); - - error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, - M98095_S2NORMAL|M98095_SDATA); - - error |= max98095_i2c_write(M98095_040_DAI3_IOCFG, - M98095_S3NORMAL|M98095_SDATA); + if (aif_id == AIF1) + error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, + M98095_S1NORMAL | M98095_SDATA); + else + error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, + M98095_S2NORMAL | M98095_SDATA); /* take the codec out of the shut down */ error |= max98095_update_bits(M98095_097_PWR_SYS, M98095_SHDNRUN, - M98095_SHDNRUN); + M98095_SHDNRUN); /* route DACL and DACR output to HO and Spekers */ error |= max98095_i2c_write(M98095_050_MIX_SPK_LEFT, 0x01); /* DACL */ error |= max98095_i2c_write(M98095_051_MIX_SPK_RIGHT, 0x01);/* DACR */ @@ -422,7 +457,10 @@ static int max98095_device_init(struct max98095_priv *max98095) /* Enable DAIs */ error |= max98095_i2c_write(M98095_093_BIAS_CTRL, 0x30); - error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07); + if (aif_id == AIF1) + error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x01); + else + error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07); err_access: if (error < 0) @@ -432,8 +470,9 @@ err_access: } static int max98095_do_init(struct sound_codec_info *pcodec_info, - int sampling_rate, int mclk_freq, - int bits_per_sample) + enum en_max_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample) { int ret = 0; @@ -443,15 +482,15 @@ static int max98095_do_init(struct sound_codec_info *pcodec_info, /* shift the device address by 1 for 7 bit addressing */ g_max98095_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; - if (pcodec_info->codec_type == CODEC_MAX_98095) + if (pcodec_info->codec_type == CODEC_MAX_98095) { g_max98095_info.devtype = MAX98095; - else { + } else { debug("%s: Codec id [%d] not defined\n", __func__, - pcodec_info->codec_type); + pcodec_info->codec_type); return -1; } - ret = max98095_device_init(&g_max98095_info); + ret = max98095_device_init(&g_max98095_info, aif_id); if (ret < 0) { debug("%s: max98095 codec chip init failed\n", __func__); return ret; @@ -463,14 +502,15 @@ static int max98095_do_init(struct sound_codec_info *pcodec_info, return ret; } - ret = max98095_hw_params(&g_max98095_info, sampling_rate, - bits_per_sample); + ret = max98095_hw_params(&g_max98095_info, aif_id, sampling_rate, + bits_per_sample); if (ret == 0) { ret = max98095_set_fmt(&g_max98095_info, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + aif_id); } return ret; @@ -529,8 +569,9 @@ static int get_max98095_codec_values(struct sound_codec_info *pcodec_info, } /* max98095 Device Initialisation */ -int max98095_init(const void *blob, int sampling_rate, int mclk_freq, - int bits_per_sample) +int max98095_init(const void *blob, enum en_max_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample) { int ret; int old_bus = i2c_get_bus_num(); @@ -538,12 +579,12 @@ int max98095_init(const void *blob, int sampling_rate, int mclk_freq, if (get_max98095_codec_values(pcodec_info, blob) < 0) { debug("FDT Codec values failed\n"); - return -1; + return -1; } i2c_set_bus_num(pcodec_info->i2c_bus); - ret = max98095_do_init(pcodec_info, sampling_rate, mclk_freq, - bits_per_sample); + ret = max98095_do_init(pcodec_info, aif_id, sampling_rate, mclk_freq, + bits_per_sample); i2c_set_bus_num(old_bus); return ret; diff --git a/drivers/sound/max98095.h b/drivers/sound/max98095.h index ae5eb14..44b1e3a 100644 --- a/drivers/sound/max98095.h +++ b/drivers/sound/max98095.h @@ -11,6 +11,12 @@ #ifndef _MAX98095_H #define _MAX98095_H +/* Available audio interface ports in wm8994 codec */ +enum en_max_audio_interface { + AIF1 = 1, + AIF2, +}; + /* * MAX98095 Registers Definition */ @@ -305,7 +311,7 @@ * * @returns -1 for error and 0 Success. */ -int max98095_init(const void *blob, int sampling_rate, int mclk_freq, - int bits_per_sample); +int max98095_init(const void *blob, enum en_max_audio_interface aif_id, + int sampling_rate, int mclk_freq, int bits_per_sample); #endif diff --git a/drivers/sound/samsung-i2s.c b/drivers/sound/samsung-i2s.c index 49921e5..47f155f 100644 --- a/drivers/sound/samsung-i2s.c +++ b/drivers/sound/samsung-i2s.c @@ -65,9 +65,7 @@ static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) if (on) { con |= CON_ACTIVE; con &= ~CON_TXCH_PAUSE; - } else { - con |= CON_TXCH_PAUSE; con &= ~CON_ACTIVE; } @@ -172,7 +170,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) break; default: debug("%s: Invalid format priority [0x%x]\n", __func__, - (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); + (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); return -1; } @@ -191,7 +189,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) break; default: debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, - (fmt & SND_SOC_DAIFMT_INV_MASK)); + (fmt & SND_SOC_DAIFMT_INV_MASK)); return -1; } @@ -209,7 +207,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) break; default: debug("%s: Invalid master selection [0x%x]\n", __func__, - (fmt & SND_SOC_DAIFMT_MASTER_MASK)); + (fmt & SND_SOC_DAIFMT_MASTER_MASK)); return -1; } @@ -250,7 +248,7 @@ int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) break; default: debug("%s: Invalid sample size input [0x%x]\n", - __func__, blc); + __func__, blc); return -1; } writel(mod, &i2s_reg->mod); @@ -301,27 +299,58 @@ int i2s_tx_init(struct i2stx_info *pi2s_tx) int ret; struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + if (pi2s_tx->id == 0) { + /* Initialize GPIO for I2S-0 */ + exynos_pinmux_config(PERIPH_ID_I2S0, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4); + } else if (pi2s_tx->id == 1) { + /* Initialize GPIO for I2S-1 */ + exynos_pinmux_config(PERIPH_ID_I2S1, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->audio_pll_clk); + } else { + debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); + return -1; + } - /* Initialize GPIO for I2s */ - exynos_pinmux_config(PERIPH_ID_I2S1, 0); - - /* Set EPLL Clock */ - ret = set_epll_clk(pi2s_tx->audio_pll_clk); if (ret != 0) { - debug("%s: epll clock set rate falied\n", __func__); + debug("%s: epll clock set rate failed\n", __func__); return -1; } - /* Select Clk Source for Audio1 */ - set_i2s_clk_source(); + /* Select Clk Source for Audio 0 or 1 */ + ret = set_i2s_clk_source(pi2s_tx->id); + if (ret == -1) { + debug("%s: unsupported clock for i2s-%d\n", __func__, + pi2s_tx->id); + return -1; + } + + if (pi2s_tx->id == 0) { + /*Reset the i2s module */ + writel(CON_RESET, &i2s_reg->con); - /* Set Prescaler to get MCLK */ - set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, - (pi2s_tx->samplingrate * (pi2s_tx->rfs))); + writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod); + /* set i2s prescaler */ + writel(PSREN | PSVAL, &i2s_reg->psr); + } else { + /* Set Prescaler to get MCLK */ + ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->id); + } + if (ret == -1) { + debug("%s: unsupported prescalar for i2s-%d\n", __func__, + pi2s_tx->id); + return -1; + } /* Configure I2s format */ ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM)); + SND_SOC_DAIFMT_CBM_CFM)); if (ret == 0) { i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c index 6fcc75d..9b8ce5a 100644 --- a/drivers/sound/sound.c +++ b/drivers/sound/sound.c @@ -36,8 +36,7 @@ static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob) int error = 0; int base; - node = fdtdec_next_compatible(blob, 0, - COMPAT_SAMSUNG_EXYNOS5_SOUND); + node = fdt_path_offset(blob, "i2s"); if (node <= 0) { debug("EXYNOS_SOUND: No node for sound in device tree\n"); return -1; @@ -80,6 +79,11 @@ static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob) node, "samsung,i2s-bit-clk-framesize", -1); error |= i2s->bfs; debug("bfs = %d\n", i2s->bfs); + + i2s->id = fdtdec_get_int(blob, node, "samsung,i2s-id", -1); + error |= i2s->id; + debug("id = %d\n", i2s->id); + if (error == -1) { debug("fail to get sound i2s node properties\n"); return -1; @@ -92,6 +96,7 @@ static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob) i2s->channels = I2S_CHANNELS; i2s->rfs = I2S_RFS; i2s->bfs = I2S_BFS; + i2s->id = 0; #endif return 0; } @@ -111,7 +116,7 @@ static int codec_init(const void *blob, struct i2stx_info *pi2s_tx) int node; /* Get the node from FDT for sound */ - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_SOUND); + node = fdt_path_offset(blob, "i2s"); if (node <= 0) { debug("EXYNOS_SOUND: No node for sound in device tree\n"); debug("node = %d\n", node); @@ -130,14 +135,15 @@ static int codec_init(const void *blob, struct i2stx_info *pi2s_tx) #endif if (!strcmp(codectype, "wm8994")) { /* Check the codec type and initialise the same */ - ret = wm8994_init(blob, WM8994_AIF2, - pi2s_tx->samplingrate, - (pi2s_tx->samplingrate * (pi2s_tx->rfs)), - pi2s_tx->bitspersample, pi2s_tx->channels); + ret = wm8994_init(blob, pi2s_tx->id + 1, + pi2s_tx->samplingrate, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->bitspersample, pi2s_tx->channels); } else if (!strcmp(codectype, "max98095")) { - ret = max98095_init(blob, pi2s_tx->samplingrate, - (pi2s_tx->samplingrate * (pi2s_tx->rfs)), - pi2s_tx->bitspersample); + ret = max98095_init(blob, pi2s_tx->id + 1, + pi2s_tx->samplingrate, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->bitspersample); } else { debug("%s: Unknown codec type %s\n", __func__, codectype); return -1; @@ -230,7 +236,7 @@ int sound_play(uint32_t msec, uint32_t frequency) } sound_prepare_buffer((unsigned short *)data, - data_size / sizeof(unsigned short), frequency); + data_size / sizeof(unsigned short), frequency); while (msec >= 1000) { ret = i2s_transfer_tx_data(&g_i2stx_pri, data, diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c index 37e354c..f8e9a6e 100644 --- a/drivers/sound/wm8994.c +++ b/drivers/sound/wm8994.c @@ -432,12 +432,12 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) int ret; /* AIF(1/0) register adress offset calculated */ - if (aif) + if (aif-1) offset = 4; else offset = 0; - switch (wm8994->sysclk[aif]) { + switch (wm8994->sysclk[aif-1]) { case WM8994_SYSCLK_MCLK1: reg1 |= SEL_MCLK1; rate = wm8994->mclk[0]; @@ -460,7 +460,7 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) default: debug("%s: Invalid input clock selection [%d]\n", - __func__, wm8994->sysclk[aif]); + __func__, wm8994->sysclk[aif-1]); return -1; } @@ -470,13 +470,18 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) reg1 |= WM8994_AIF1CLK_DIV; } - wm8994->aifclk[aif] = rate; + wm8994->aifclk[aif-1] = rate; ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset, WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, reg1); - ret |= wm8994_update_bits(WM8994_CLOCKING_1, + if (aif == WM8994_AIF1) + ret |= wm8994_update_bits(WM8994_CLOCKING_1, + WM8994_AIF1DSPCLK_ENA_MASK | WM8994_SYSDSPCLK_ENA_MASK, + WM8994_AIF1DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); + else if (aif == WM8994_AIF2) + ret |= wm8994_update_bits(WM8994_CLOCKING_1, WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK | WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); @@ -536,7 +541,7 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, break; if (i == ARRAY_SIZE(opclk_divs)) { debug("%s frequency divisor not found\n", - __func__); + __func__); return -1; } ret = wm8994_update_bits(WM8994_CLOCKING_2, @@ -554,7 +559,7 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, return -1; } - ret |= configure_aif_clock(wm8994, aif_id - 1); + ret |= configure_aif_clock(wm8994, aif_id); if (ret < 0) { debug("%s: codec register access error\n", __func__); @@ -608,13 +613,46 @@ static int wm8994_init_volume_aif2_dac1(void) } /* + * Initializes Volume for AIF1 to HP path + * + * @returns -1 for error and 0 Success. + * + */ +static int wm8994_init_volume_aif1_dac1(void) +{ + int ret = 0; + + /* Unmute AIF1DAC */ + ret |= wm8994_i2c_write(WM8994_AIF1_DAC_FILTERS_1, 0x0000); + + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + /* Head Phone Volume */ + ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* * Intialise wm8994 codec device * * @param wm8994 wm8994 information * * @returns -1 for error and 0 Success. */ -static int wm8994_device_init(struct wm8994_priv *wm8994) +static int wm8994_device_init(struct wm8994_priv *wm8994, + enum en_audio_interface aif_id) { const char *devname; unsigned short reg_data; @@ -661,13 +699,30 @@ static int wm8994_device_init(struct wm8994_priv *wm8994) ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); - /* Power enable for AIF2 and DAC1 */ - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, - WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | - WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, - WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA | - WM8994_DAC1R_ENA); - + if (aif_id == WM8994_AIF1) { + ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_2, + WM8994_TSHUT_ENA | WM8994_MIXINL_ENA | + WM8994_MIXINR_ENA | WM8994_IN2L_ENA | + WM8994_IN2R_ENA); + + ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_4, + WM8994_ADCL_ENA | WM8994_ADCR_ENA | + WM8994_AIF1ADC1R_ENA | + WM8994_AIF1ADC1L_ENA); + + /* Power enable for AIF1 and DAC1 */ + ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_5, + WM8994_AIF1DACL_ENA | + WM8994_AIF1DACR_ENA | + WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); + } else if (aif_id == WM8994_AIF2) { + /* Power enable for AIF2 and DAC1 */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | + WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, + WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | + WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); + } /* Head Phone Initialisation */ ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, @@ -695,35 +750,49 @@ static int wm8994_device_init(struct wm8994_priv *wm8994) ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2, WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); - /* Routing AIF2 to DAC1 */ - ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, - WM8994_AIF2DACL_TO_DAC1L_MASK, - WM8994_AIF2DACL_TO_DAC1L); - - ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, - WM8994_AIF2DACR_TO_DAC1R_MASK, - WM8994_AIF2DACR_TO_DAC1R); - - /* GPIO Settings for AIF2 */ - /* B CLK */ - ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | - WM8994_GPIO_FUNCTION_MASK , - WM8994_GPIO_DIR_OUTPUT | - WM8994_GPIO_FUNCTION_I2S_CLK); - - /* LR CLK */ - ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | - WM8994_GPIO_FUNCTION_MASK, - WM8994_GPIO_DIR_OUTPUT | - WM8994_GPIO_FUNCTION_I2S_CLK); - - /* DATA */ - ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | - WM8994_GPIO_FUNCTION_MASK, - WM8994_GPIO_DIR_OUTPUT | - WM8994_GPIO_FUNCTION_I2S_CLK); - - ret |= wm8994_init_volume_aif2_dac1(); + if (aif_id == WM8994_AIF1) { + /* Routing AIF1 to DAC1 */ + ret |= wm8994_i2c_write(WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF1DAC1L_TO_DAC1L); + + ret |= wm8994_i2c_write(WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF1DAC1R_TO_DAC1R); + + /* GPIO Settings for AIF1 */ + ret |= wm8994_i2c_write(WM8994_GPIO_1, WM8994_GPIO_DIR_OUTPUT + | WM8994_GPIO_FUNCTION_I2S_CLK + | WM8994_GPIO_INPUT_DEBOUNCE); + + ret |= wm8994_init_volume_aif1_dac1(); + } else if (aif_id == WM8994_AIF2) { + /* Routing AIF2 to DAC1 */ + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF2DACL_TO_DAC1L_MASK, + WM8994_AIF2DACL_TO_DAC1L); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF2DACR_TO_DAC1R_MASK, + WM8994_AIF2DACR_TO_DAC1R); + + /* GPIO Settings for AIF2 */ + /* B CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK , + WM8994_GPIO_DIR_OUTPUT); + + /* LR CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); + + /* DATA */ + ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); + + ret |= wm8994_init_volume_aif2_dac1(); + } + if (ret < 0) goto err; @@ -795,7 +864,7 @@ static int get_codec_values(struct sound_codec_info *pcodec_info, return 0; } -/*wm8994 Device Initialisation */ +/* WM8994 Device Initialisation */ int wm8994_init(const void *blob, enum en_audio_interface aif_id, int sampling_rate, int mclk_freq, int bits_per_sample, unsigned int channels) @@ -813,15 +882,15 @@ int wm8994_init(const void *blob, enum en_audio_interface aif_id, g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr; wm8994_i2c_init(pcodec_info->i2c_bus); - if (pcodec_info->codec_type == CODEC_WM_8994) + if (pcodec_info->codec_type == CODEC_WM_8994) { g_wm8994_info.type = WM8994; - else { + } else { debug("%s: Codec id [%d] not defined\n", __func__, - pcodec_info->codec_type); + pcodec_info->codec_type); return -1; } - ret = wm8994_device_init(&g_wm8994_info); + ret = wm8994_device_init(&g_wm8994_info, aif_id); if (ret < 0) { debug("%s: wm8994 codec chip init failed\n", __func__); return ret; diff --git a/drivers/sound/wm8994_registers.h b/drivers/sound/wm8994_registers.h index 1e987c2..0aba2fd 100644 --- a/drivers/sound/wm8994_registers.h +++ b/drivers/sound/wm8994_registers.h @@ -13,6 +13,7 @@ #define WM8994_SOFTWARE_RESET 0x00 #define WM8994_POWER_MANAGEMENT_1 0x01 #define WM8994_POWER_MANAGEMENT_2 0x02 +#define WM8994_POWER_MANAGEMENT_4 0x04 #define WM8994_POWER_MANAGEMENT_5 0x05 #define WM8994_LEFT_OUTPUT_VOLUME 0x1C #define WM8994_RIGHT_OUTPUT_VOLUME 0x1D @@ -38,6 +39,7 @@ #define WM8994_AIF2_CONTROL_2 0x311 #define WM8994_AIF2_MASTER_SLAVE 0x312 #define WM8994_AIF2_BCLK 0x313 +#define WM8994_AIF1_DAC_FILTERS_1 0x420 #define WM8994_AIF2_DAC_LEFT_VOLUME 0x502 #define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503 #define WM8994_AIF2_DAC_FILTERS_1 0x520 @@ -45,6 +47,7 @@ #define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602 #define WM8994_DAC1_LEFT_VOLUME 0x610 #define WM8994_DAC1_RIGHT_VOLUME 0x611 +#define WM8994_GPIO_1 0x700 #define WM8994_GPIO_3 0x702 #define WM8994_GPIO_4 0x703 #define WM8994_GPIO_5 0x704 @@ -82,6 +85,20 @@ /* OPCLK_ENA */ #define WM8994_OPCLK_ENA 0x0800 +#define WM8994_TSHUT_ENA 0x4000 +#define WM8994_MIXINL_ENA 0x0200 +#define WM8994_MIXINR_ENA 0x0100 +#define WM8994_IN2L_ENA 0x0080 +#define WM8994_IN2R_ENA 0x0020 + +/* + * R5 (0x04) - Power Management (4) + */ +#define WM8994_ADCL_ENA 0x0001 +#define WM8994_ADCR_ENA 0x0002 +#define WM8994_AIF1ADC1R_ENA 0x0100 +#define WM8994_AIF1ADC1L_ENA 0x0200 + /* * R5 (0x05) - Power Management (5) */ @@ -91,6 +108,12 @@ /* AIF2DACR_ENA */ #define WM8994_AIF2DACR_ENA 0x1000 #define WM8994_AIF2DACR_ENA_MASK 0x1000 +/* AIF1DACL_ENA */ +#define WM8994_AIF1DACL_ENA 0x0200 +#define WM8994_AIF1DACL_ENA_MASK 0x0200 +/* AIF1DACR_ENA */ +#define WM8994_AIF1DACR_ENA 0x0100 +#define WM8994_AIF1DACR_ENA_MASK 0x0100 /* DAC1L_ENA */ #define WM8994_DAC1L_ENA 0x0002 #define WM8994_DAC1L_ENA_MASK 0x0002 @@ -170,6 +193,9 @@ /* * R520 (0x208) - Clocking (1) */ +/* AIF1DSPCLK_ENA */ +#define WM8994_AIF1DSPCLK_ENA 0x0008 +#define WM8994_AIF1DSPCLK_ENA_MASK 0x0008 /* AIF2DSPCLK_ENA */ #define WM8994_AIF2DSPCLK_ENA 0x0004 #define WM8994_AIF2DSPCLK_ENA_MASK 0x0004 @@ -254,6 +280,8 @@ /* AIF2DACL_TO_DAC1L */ #define WM8994_AIF2DACL_TO_DAC1L 0x0004 #define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004 +/* AIF1DAC1L_TO_DAC1L */ +#define WM8994_AIF1DAC1L_TO_DAC1L 0x0001 /* * R1538 (0x602) - DAC1 Right Mixer Routing @@ -261,6 +289,8 @@ /* AIF2DACR_TO_DAC1R */ #define WM8994_AIF2DACR_TO_DAC1R 0x0004 #define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004 +/* AIF1DAC1R_TO_DAC1R */ +#define WM8994_AIF1DAC1R_TO_DAC1R 0x0001 /* * R1552 (0x610) - DAC1 Left Volume @@ -285,11 +315,12 @@ * GPIO */ /* OUTPUT PIN */ -#define WM8994_GPIO_DIR_OUTPUT 0x8000 +#define WM8994_GPIO_DIR_OUTPUT 0x8000 /* GPIO PIN MASK */ -#define WM8994_GPIO_DIR_MASK 0xFFE0 +#define WM8994_GPIO_DIR_MASK 0xFFE0 /* I2S CLK */ -#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0000 +#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0001 +#define WM8994_GPIO_INPUT_DEBOUNCE 0x0100 /* GPn FN */ -#define WM8994_GPIO_FUNCTION_MASK 0x001F +#define WM8994_GPIO_FUNCTION_MASK 0x001F #endif diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 91d24ce..e5941b0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -38,6 +38,7 @@ COBJS-$(CONFIG_FDT_SPI) += fdt_spi.o COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o +COBJS-$(CONFIG_TI_QSPI) += ti_qspi.o COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index efc8b1e..699c57e 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -26,6 +26,7 @@ struct spi_bus { struct exynos_spi *regs; int inited; /* 1 if this bus is ready for use */ int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ }; /* A list of spi buses that we know about */ @@ -40,6 +41,8 @@ struct exynos_spi_slave { enum periph_id periph_id; /* Peripheral ID for this device */ unsigned int fifo_size; int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ }; static struct spi_bus *spi_get_bus(unsigned dev_index) @@ -85,6 +88,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, } bus = &spi_bus[busnum]; + spi_slave->bus = bus; spi_slave->regs = bus->regs; spi_slave->mode = mode; spi_slave->periph_id = bus->periph_id; @@ -95,6 +99,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, spi_slave->fifo_size = 256; spi_slave->skip_preamble = 0; + spi_slave->last_transaction_us = timer_get_us(); spi_slave->freq = bus->frequency; if (max_hz) @@ -199,12 +204,29 @@ static void spi_get_fifo_levels(struct exynos_spi *regs, * * @param regs SPI peripheral registers * @param count Number of bytes to transfer + * @param step Number of bytes to transfer in each packet (1 or 4) */ -static void spi_request_bytes(struct exynos_spi *regs, int count) +static void spi_request_bytes(struct exynos_spi *regs, int count, int step) { + /* For word address we need to swap bytes */ + if (step == 4) { + setbits_le32(®s->mode_cfg, + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); + count /= 4; + setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | + SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | + SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); + } else { + /* Select byte access and clear the swap configuration */ + clrbits_le32(®s->mode_cfg, + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); + writel(0, ®s->swap_cfg); + } + assert(count && count < (1 << 16)); setbits_le32(®s->ch_cfg, SPI_CH_RST); clrbits_le32(®s->ch_cfg, SPI_CH_RST); + writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); } @@ -219,6 +241,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, int toread; unsigned start = get_timer(0); int stopping; + int step; out_bytes = in_bytes = todo; @@ -226,10 +249,19 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, !(spi_slave->mode & SPI_SLAVE); /* + * Try to transfer words if we can. This helps read performance at + * SPI clock speeds above about 20MHz. + */ + step = 1; + if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && + !spi_slave->skip_preamble) + step = 4; + + /* * If there's something to send, do a software reset and set a * transaction size. */ - spi_request_bytes(regs, todo); + spi_request_bytes(regs, todo, step); /* * Bytes are transmitted/received in pairs. Wait to receive all the @@ -242,24 +274,42 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, /* Keep the fifos full/empty. */ spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); - if (tx_lvl < spi_slave->fifo_size && out_bytes) { - temp = txp ? *txp++ : 0xff; + + /* + * Don't completely fill the txfifo, since we don't want our + * rxfifo to overflow, and it may already contain data. + */ + while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) { + if (!txp) + temp = -1; + else if (step == 4) + temp = *(uint32_t *)txp; + else + temp = *txp; writel(temp, ®s->tx_data); - out_bytes--; + out_bytes -= step; + if (txp) + txp += step; + tx_lvl += step; } - if (rx_lvl > 0) { - temp = readl(®s->rx_data); - if (spi_slave->skip_preamble) { - if (temp == SPI_PREAMBLE_END_BYTE) { - spi_slave->skip_preamble = 0; - stopping = 0; + if (rx_lvl >= step) { + while (rx_lvl >= step) { + temp = readl(®s->rx_data); + if (spi_slave->skip_preamble) { + if (temp == SPI_PREAMBLE_END_BYTE) { + spi_slave->skip_preamble = 0; + stopping = 0; + } + } else { + if (rxp || stopping) { + *rxp = temp; + rxp += step; + } + in_bytes -= step; } - } else { - if (rxp || stopping) - *rxp++ = temp; - in_bytes--; + toread -= step; + rx_lvl -= step; } - toread--; } else if (!toread) { /* * We have run out of input data, but haven't read @@ -271,7 +321,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, out_bytes = in_bytes; toread = in_bytes; txp = NULL; - spi_request_bytes(regs, toread); + spi_request_bytes(regs, toread, step); } if (spi_slave->skip_preamble && get_timer(start) > 100) { printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", @@ -315,10 +365,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, if ((flags & SPI_XFER_BEGIN)) spi_cs_activate(slave); - /* Exynos SPI limits each transfer to 65535 bytes */ + /* + * Exynos SPI limits each transfer to 65535 transfers. To keep + * things simple, allow a maximum of 65532 bytes. We could allow + * more in word mode, but the performance difference is small. + */ bytelen = bitlen / 8; for (upto = 0; !ret && upto < bytelen; upto += todo) { - todo = min(bytelen - upto, (1 << 16) - 1); + todo = min(bytelen - upto, (1 << 16) - 4); ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags); if (ret) break; @@ -359,9 +413,22 @@ void spi_cs_activate(struct spi_slave *slave) { struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); debug("Activate CS, bus %d\n", spi_slave->slave.bus); spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; + + /* Remember time of this transaction so we can honour the bus delay */ + if (spi_slave->bus->deactivate_delay_us) + spi_slave->last_transaction_us = timer_get_us(); } /** @@ -411,6 +478,8 @@ static int spi_get_config(const void *blob, int node, struct spi_bus *bus) /* Use 500KHz as a suitable default */ bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", 500000); + bus->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); return 0; } diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 3cf7142..2b9f395 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -56,8 +56,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct mxs_spi_slave *mxs_slave; - struct mxs_ssp_regs *ssp_regs; - int reg; if (!spi_cs_is_valid(bus, cs)) { printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs); @@ -74,13 +72,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, mxs_slave->max_khz = max_hz / 1000; mxs_slave->mode = mode; mxs_slave->regs = mxs_ssp_regs_by_bus(bus); - ssp_regs = mxs_slave->regs; - reg = readl(&ssp_regs->hw_ssp_ctrl0); - reg &= ~(MXS_SSP_CHIPSELECT_MASK); - reg |= cs << MXS_SSP_CHIPSELECT_SHIFT; - - writel(reg, &ssp_regs->hw_ssp_ctrl0); return &mxs_slave->slave; err_init: @@ -102,7 +94,9 @@ int spi_claim_bus(struct spi_slave *slave) mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); - writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); + writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) | + SSP_CTRL0_BUS_WIDTH_ONE_BIT, + &ssp_regs->hw_ssp_ctrl0); reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS; reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c new file mode 100644 index 0000000..5a5b482 --- /dev/null +++ b/drivers/spi/ti_qspi.c @@ -0,0 +1,311 @@ +/* + * TI QSPI driver + * + * Copyright (C) 2013, Texas Instruments, Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/omap.h> +#include <malloc.h> +#include <spi.h> + +/* ti qpsi register bit masks */ +#define QSPI_TIMEOUT 2000000 +#define QSPI_FCLK 192000000 +/* clock control */ +#define QSPI_CLK_EN (1 << 31) +#define QSPI_CLK_DIV_MAX 0xffff +/* command */ +#define QSPI_EN_CS(n) (n << 28) +#define QSPI_WLEN(n) ((n-1) << 19) +#define QSPI_3_PIN (1 << 18) +#define QSPI_RD_SNGL (1 << 16) +#define QSPI_WR_SNGL (2 << 16) +#define QSPI_INVAL (4 << 16) +#define QSPI_RD_QUAD (7 << 16) +/* device control */ +#define QSPI_DD(m, n) (m << (3 + n*8)) +#define QSPI_CKPHA(n) (1 << (2 + n*8)) +#define QSPI_CSPOL(n) (1 << (1 + n*8)) +#define QSPI_CKPOL(n) (1 << (n*8)) +/* status */ +#define QSPI_WC (1 << 1) +#define QSPI_BUSY (1 << 0) +#define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY) +#define QSPI_XFER_DONE QSPI_WC +#define MM_SWITCH 0x01 +#define MEM_CS 0x100 +#define MEM_CS_UNSELECT 0xfffff0ff +#define MMAP_START_ADDR 0x5c000000 +#define CORE_CTRL_IO 0x4a002558 + +#define QSPI_CMD_READ (0x3 << 0) +#define QSPI_CMD_READ_QUAD (0x6b << 0) +#define QSPI_CMD_READ_FAST (0x0b << 0) +#define QSPI_SETUP0_NUM_A_BYTES (0x2 << 8) +#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10) +#define QSPI_SETUP0_NUM_D_BYTES_8_BITS (0x1 << 10) +#define QSPI_SETUP0_READ_NORMAL (0x0 << 12) +#define QSPI_SETUP0_READ_QUAD (0x3 << 12) +#define QSPI_CMD_WRITE (0x2 << 16) +#define QSPI_NUM_DUMMY_BITS (0x0 << 24) + +/* ti qspi register set */ +struct ti_qspi_regs { + u32 pid; + u32 pad0[3]; + u32 sysconfig; + u32 pad1[3]; + u32 int_stat_raw; + u32 int_stat_en; + u32 int_en_set; + u32 int_en_ctlr; + u32 intc_eoi; + u32 pad2[3]; + u32 clk_ctrl; + u32 dc; + u32 cmd; + u32 status; + u32 data; + u32 setup0; + u32 setup1; + u32 setup2; + u32 setup3; + u32 memswitch; + u32 data1; + u32 data2; + u32 data3; +}; + +/* ti qspi slave */ +struct ti_qspi_slave { + struct spi_slave slave; + struct ti_qspi_regs *base; + unsigned int mode; + u32 cmd; + u32 dc; +}; + +static inline struct ti_qspi_slave *to_ti_qspi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct ti_qspi_slave, slave); +} + +static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave) +{ + struct spi_slave *slave = &qslave->slave; + u32 memval = 0; + + slave->memory_map = (void *)MMAP_START_ADDR; + + memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | + QSPI_SETUP0_NUM_D_BYTES_NO_BITS | + QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE | + QSPI_NUM_DUMMY_BITS; + + writel(memval, &qslave->base->setup0); +} + +static void ti_spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + uint clk_div; + + debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); + + if (!hz) + clk_div = 0; + else + clk_div = (QSPI_FCLK / hz) - 1; + + /* disable SCLK */ + writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN, + &qslave->base->clk_ctrl); + + /* assign clk_div values */ + if (clk_div < 0) + clk_div = 0; + else if (clk_div > QSPI_CLK_DIV_MAX) + clk_div = QSPI_CLK_DIV_MAX; + + /* enable SCLK */ + writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* CS handled in xfer */ + return; +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + + debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); + + writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd); +} + +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) +{ + struct ti_qspi_slave *qslave; + + qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs); + if (!qslave) { + printf("SPI_error: Fail to allocate ti_qspi_slave\n"); + return NULL; + } + + qslave->base = (struct ti_qspi_regs *)QSPI_BASE; + qslave->mode = mode; + + ti_spi_set_speed(&qslave->slave, max_hz); + +#ifdef CONFIG_TI_SPI_MMAP + ti_spi_setup_spi_register(qslave); +#endif + + return &qslave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + free(qslave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + + debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs); + + qslave->dc = 0; + if (qslave->mode & SPI_CPHA) + qslave->dc |= QSPI_CKPHA(slave->cs); + if (qslave->mode & SPI_CPOL) + qslave->dc |= QSPI_CKPOL(slave->cs); + if (qslave->mode & SPI_CS_HIGH) + qslave->dc |= QSPI_CSPOL(slave->cs); + + writel(qslave->dc, &qslave->base->dc); + writel(0, &qslave->base->cmd); + writel(0, &qslave->base->data); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + + debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs); + + writel(0, &qslave->base->dc); + writel(0, &qslave->base->cmd); + writel(0, &qslave->base->data); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + uint words = bitlen >> 3; /* fixed 8-bit word length */ + const uchar *txp = dout; + uchar *rxp = din; + uint status; + int timeout, val; + + debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n", + slave->bus, slave->cs, bitlen, words, flags); + + /* Setup mmap flags */ + if (flags & SPI_XFER_MMAP) { + writel(MM_SWITCH, &qslave->base->memswitch); + val = readl(CORE_CTRL_IO); + val |= MEM_CS; + writel(val, CORE_CTRL_IO); + return 0; + } else if (flags & SPI_XFER_MMAP_END) { + writel(~MM_SWITCH, &qslave->base->memswitch); + val = readl(CORE_CTRL_IO); + val &= MEM_CS_UNSELECT; + writel(val, CORE_CTRL_IO); + return 0; + } + + if (bitlen == 0) + return -1; + + if (bitlen % 8) { + debug("spi_xfer: Non byte aligned SPI transfer\n"); + return -1; + } + + /* Setup command reg */ + qslave->cmd = 0; + qslave->cmd |= QSPI_WLEN(8); + qslave->cmd |= QSPI_EN_CS(slave->cs); + if (flags & SPI_3WIRE) + qslave->cmd |= QSPI_3_PIN; + qslave->cmd |= 0xfff; + + while (words--) { + if (txp) { + debug("tx cmd %08x dc %08x data %02x\n", + qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp); + writel(*txp++, &qslave->base->data); + writel(qslave->cmd | QSPI_WR_SNGL, + &qslave->base->cmd); + status = readl(&qslave->base->status); + timeout = QSPI_TIMEOUT; + while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { + if (--timeout < 0) { + printf("spi_xfer: TX timeout!\n"); + return -1; + } + status = readl(&qslave->base->status); + } + debug("tx done, status %08x\n", status); + } + if (rxp) { + qslave->cmd |= QSPI_RD_SNGL; + debug("rx cmd %08x dc %08x\n", + qslave->cmd, qslave->dc); + writel(qslave->cmd, &qslave->base->cmd); + status = readl(&qslave->base->status); + timeout = QSPI_TIMEOUT; + while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { + if (--timeout < 0) { + printf("spi_xfer: RX timeout!\n"); + return -1; + } + status = readl(&qslave->base->status); + } + *rxp++ = readl(&qslave->base->data); + debug("rx done, status %08x, read %02x\n", + status, *(rxp-1)); + } + } + + /* Terminate frame */ + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4c2a39a..1590c4a 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,10 +18,12 @@ endif # new USB gadget layer dependencies ifdef CONFIG_USB_GADGET +COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o +COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c new file mode 100644 index 0000000..c99208d --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -0,0 +1,1306 @@ +/* + * Driver for the Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.c] + * + * Copyright (C) 2005-2013 Atmel Corporation + * Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/hardware.h> +#include <linux/list.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/atmel_usba_udc.h> +#include <malloc.h> +#include <usb/lin_gadget_compat.h> + +#include "atmel_usba_udc.h" + +static int vbus_is_present(struct usba_udc *udc) +{ + /* No Vbus detection: Assume always present */ + return 1; +} + +static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) +{ + unsigned int transaction_len; + + transaction_len = req->req.length - req->req.actual; + req->last_transaction = 1; + if (transaction_len > ep->ep.maxpacket) { + transaction_len = ep->ep.maxpacket; + req->last_transaction = 0; + } else if (transaction_len == ep->ep.maxpacket && req->req.zero) { + req->last_transaction = 0; + } + + DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n", + ep->ep.name, req, transaction_len, + req->last_transaction ? ", done" : ""); + + memcpy(ep->fifo, req->req.buf + req->req.actual, transaction_len); + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + req->req.actual += transaction_len; +} + +static void submit_request(struct usba_ep *ep, struct usba_request *req) +{ + DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d), dma: %d\n", + ep->ep.name, req, req->req.length, req->using_dma); + + req->req.actual = 0; + req->submitted = 1; + + next_fifo_transaction(ep, req); + if (req->last_transaction) { + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); + } else { + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); + } +} + +static void submit_next_request(struct usba_ep *ep) +{ + struct usba_request *req; + + if (list_empty(&ep->queue)) { + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY); + return; + } + + req = list_entry(ep->queue.next, struct usba_request, queue); + if (!req->submitted) + submit_request(ep, req); +} + +static void send_status(struct usba_udc *udc, struct usba_ep *ep) +{ + ep->state = STATUS_STAGE_IN; + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +} + +static void receive_data(struct usba_ep *ep) +{ + struct usba_udc *udc = ep->udc; + struct usba_request *req; + unsigned long status; + unsigned int bytecount, nr_busy; + int is_complete = 0; + + status = usba_ep_readl(ep, STA); + nr_busy = USBA_BFEXT(BUSY_BANKS, status); + + DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy); + + while (nr_busy > 0) { + if (list_empty(&ep->queue)) { + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + break; + } + req = list_entry(ep->queue.next, + struct usba_request, queue); + + bytecount = USBA_BFEXT(BYTE_COUNT, status); + + if (status & USBA_SHORT_PACKET) + is_complete = 1; + if (req->req.actual + bytecount >= req->req.length) { + is_complete = 1; + bytecount = req->req.length - req->req.actual; + } + + memcpy(req->req.buf + req->req.actual, ep->fifo, bytecount); + req->req.actual += bytecount; + + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + + if (is_complete) { + DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name); + req->req.status = 0; + list_del_init(&req->queue); + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + spin_lock(&udc->lock); + req->req.complete(&ep->ep, &req->req); + spin_unlock(&udc->lock); + } + + status = usba_ep_readl(ep, STA); + nr_busy = USBA_BFEXT(BUSY_BANKS, status); + + if (is_complete && ep_is_control(ep)) { + send_status(udc, ep); + break; + } + } +} + +static void +request_complete(struct usba_ep *ep, struct usba_request *req, int status) +{ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + + DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n", + ep->ep.name, req, req->req.status, req->req.actual); + + req->req.complete(&ep->ep, &req->req); +} + +static void +request_complete_list(struct usba_ep *ep, struct list_head *list, int status) +{ + struct usba_request *req, *tmp_req; + + list_for_each_entry_safe(req, tmp_req, list, queue) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } +} + +static int +usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + unsigned long flags, ept_cfg, maxpacket; + unsigned int nr_trans; + + DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); + + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + + if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) + != ep->index) || + ep->index == 0 || + desc->bDescriptorType != USB_DT_ENDPOINT || + maxpacket == 0 || + maxpacket > ep->fifo_size) { + DBG(DBG_ERR, "ep_enable: Invalid argument"); + return -EINVAL; + } + + ep->is_isoc = 0; + ep->is_in = 0; + + if (maxpacket <= 8) + ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8); + else + /* LSB is bit 1, not 0 */ + ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3); + + DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", + ep->ep.name, ept_cfg, maxpacket); + + if (usb_endpoint_dir_in(desc)) { + ep->is_in = 1; + ept_cfg |= USBA_EPT_DIR_IN; + } + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL); + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); + break; + case USB_ENDPOINT_XFER_ISOC: + if (!ep->can_isoc) { + DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n", + ep->ep.name); + return -EINVAL; + } + + /* + * Bits 11:12 specify number of _additional_ + * transactions per microframe. + */ + nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; + if (nr_trans > 3) + return -EINVAL; + + ep->is_isoc = 1; + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO); + + /* + * Do triple-buffering on high-bandwidth iso endpoints. + */ + if (nr_trans > 1 && ep->nr_banks == 3) + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE); + else + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE); + ept_cfg |= USBA_BF(NB_TRANS, nr_trans); + break; + case USB_ENDPOINT_XFER_BULK: + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK); + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); + break; + case USB_ENDPOINT_XFER_INT: + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT); + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); + break; + } + + spin_lock_irqsave(&ep->udc->lock, flags); + + ep->desc = desc; + ep->ep.maxpacket = maxpacket; + + usba_ep_writel(ep, CFG, ept_cfg); + usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + + usba_writel(udc, INT_ENB, + (usba_readl(udc, INT_ENB) + | USBA_BF(EPT_INT, 1 << ep->index))); + + spin_unlock_irqrestore(&udc->lock, flags); + + DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index, + (unsigned long)usba_ep_readl(ep, CFG)); + DBG(DBG_HW, "INT_ENB after init: %#08lx\n", + (unsigned long)usba_readl(udc, INT_ENB)); + + return 0; +} + +static int usba_ep_disable(struct usb_ep *_ep) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + LIST_HEAD(req_list); + unsigned long flags; + + DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name); + + spin_lock_irqsave(&udc->lock, flags); + + if (!ep->desc) { + spin_unlock_irqrestore(&udc->lock, flags); + /* REVISIT because this driver disables endpoints in + * reset_all_endpoints() before calling disconnect(), + * most gadget drivers would trigger this non-error ... + */ + if (udc->gadget.speed != USB_SPEED_UNKNOWN) + DBG(DBG_ERR, "ep_disable: %s not enabled\n", + ep->ep.name); + return -EINVAL; + } + ep->desc = NULL; + + list_splice_init(&ep->queue, &req_list); + usba_ep_writel(ep, CFG, 0); + usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE); + usba_writel(udc, INT_ENB, + usba_readl(udc, INT_ENB) & + ~USBA_BF(EPT_INT, 1 << ep->index)); + + request_complete_list(ep, &req_list, -ESHUTDOWN); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static struct usb_request * +usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct usba_request *req; + + DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags); + + req = malloc(sizeof(struct usba_request)); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void +usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct usba_request *req = to_usba_req(_req); + + DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req); + + free(req); +} + +static int +usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct usba_request *req = to_usba_req(_req); + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + unsigned long flags; + int ret; + + DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n", + ep->ep.name, req, _req->length); + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || + !ep->desc) + return -ESHUTDOWN; + + req->submitted = 0; + req->using_dma = 0; + req->last_transaction = 0; + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* May have received a reset since last time we checked */ + ret = -ESHUTDOWN; + spin_lock_irqsave(&udc->lock, flags); + if (ep->desc) { + list_add_tail(&req->queue, &ep->queue); + + if ((!ep_is_control(ep) && ep->is_in) || + (ep_is_control(ep) && (ep->state == DATA_STAGE_IN || + ep->state == STATUS_STAGE_IN))) + usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); + else + usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); + + ret = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_request *req = to_usba_req(_req); + + DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n", + ep->ep.name, req); + + /* + * Errors should stop the queue from advancing until the + * completion function returns. + */ + list_del_init(&req->queue); + + request_complete(ep, req, -ECONNRESET); + + /* Process the next request if any */ + submit_next_request(ep); + + return 0; +} + +static int usba_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct usba_ep *ep = to_usba_ep(_ep); + unsigned long flags; + int ret = 0; + + DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name, + value ? "set" : "clear"); + + if (!ep->desc) { + DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n", + ep->ep.name); + return -ENODEV; + } + + if (ep->is_isoc) { + DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n", + ep->ep.name); + return -ENOTTY; + } + + spin_lock_irqsave(&udc->lock, flags); + + /* + * We can't halt IN endpoints while there are still data to be + * transferred + */ + if (!list_empty(&ep->queue) || + ((value && ep->is_in && (usba_ep_readl(ep, STA) & + USBA_BF(BUSY_BANKS, -1L))))) { + ret = -EAGAIN; + } else { + if (value) + usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); + else + usba_ep_writel(ep, CLR_STA, + USBA_FORCE_STALL | USBA_TOGGLE_CLR); + usba_ep_readl(ep, STA); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int usba_ep_fifo_status(struct usb_ep *_ep) +{ + struct usba_ep *ep = to_usba_ep(_ep); + + return USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); +} + +static void usba_ep_fifo_flush(struct usb_ep *_ep) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + + usba_writel(udc, EPT_RST, 1 << ep->index); +} + +static const struct usb_ep_ops usba_ep_ops = { + .enable = usba_ep_enable, + .disable = usba_ep_disable, + .alloc_request = usba_ep_alloc_request, + .free_request = usba_ep_free_request, + .queue = usba_ep_queue, + .dequeue = usba_ep_dequeue, + .set_halt = usba_ep_set_halt, + .fifo_status = usba_ep_fifo_status, + .fifo_flush = usba_ep_fifo_flush, +}; + +static int usba_udc_get_frame(struct usb_gadget *gadget) +{ + struct usba_udc *udc = to_usba_udc(gadget); + + return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM)); +} + +static int usba_udc_wakeup(struct usb_gadget *gadget) +{ + struct usba_udc *udc = to_usba_udc(gadget); + unsigned long flags; + u32 ctrl; + int ret = -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { + ctrl = usba_readl(udc, CTRL); + usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP); + ret = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int +usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct usba_udc *udc = to_usba_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + if (is_selfpowered) + udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; + else + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops usba_udc_ops = { + .get_frame = usba_udc_get_frame, + .wakeup = usba_udc_wakeup, + .set_selfpowered = usba_udc_set_selfpowered, +}; + +static struct usb_endpoint_descriptor usba_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(64), + /* FIXME: I have no idea what to put here */ + .bInterval = 1, +}; + +/* + * Called with interrupts disabled and udc->lock held. + */ +static void reset_all_endpoints(struct usba_udc *udc) +{ + struct usba_ep *ep; + struct usba_request *req, *tmp_req; + + usba_writel(udc, EPT_RST, ~0UL); + + ep = to_usba_ep(udc->gadget.ep0); + list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) { + list_del_init(&req->queue); + request_complete(ep, req, -ECONNRESET); + } + + /* NOTE: normally, the next call to the gadget driver is in + * charge of disabling endpoints... usually disconnect(). + * The exception would be entering a high speed test mode. + * + * FIXME remove this code ... and retest thoroughly. + */ + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) { + spin_unlock(&udc->lock); + usba_ep_disable(&ep->ep); + spin_lock(&udc->lock); + } + } +} + +static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex) +{ + struct usba_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return to_usba_ep(udc->gadget.ep0); + + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) + == (wIndex & USB_ENDPOINT_NUMBER_MASK)) + return ep; + } + + return NULL; +} + +/* Called with interrupts disabled and udc->lock held */ +static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep) +{ + usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); + ep->state = WAIT_FOR_SETUP; +} + +static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep) +{ + if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL) + return 1; + return 0; +} + +static inline void set_address(struct usba_udc *udc, unsigned int addr) +{ + u32 regval; + + DBG(DBG_BUS, "setting address %u...\n", addr); + regval = usba_readl(udc, CTRL); + regval = USBA_BFINS(DEV_ADDR, addr, regval); + usba_writel(udc, CTRL, regval); +} + +static int do_test_mode(struct usba_udc *udc) +{ + static const char test_packet_buffer[] = { + /* JKJKJKJK * 9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK * 8 */ + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + /* JJKKJJKK * 8 */ + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + /* JJJJJJJKKKKKKK * 8 */ + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* JJJJJJJK * 8 */ + 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, + /* {JKKKKKKK * 10}, JK */ + 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E + }; + struct usba_ep *ep; + int test_mode; + + test_mode = udc->test_mode; + + /* Start from a clean slate */ + reset_all_endpoints(udc); + + switch (test_mode) { + case 0x0100: + /* Test_J */ + usba_writel(udc, TST, USBA_TST_J_MODE); + DBG(DBG_ALL, "Entering Test_J mode...\n"); + break; + case 0x0200: + /* Test_K */ + usba_writel(udc, TST, USBA_TST_K_MODE); + DBG(DBG_ALL, "Entering Test_K mode...\n"); + break; + case 0x0300: + /* + * Test_SE0_NAK: Force high-speed mode and set up ep0 + * for Bulk IN transfers + */ + ep = &udc->usba_ep[0]; + usba_writel(udc, TST, + USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH)); + usba_ep_writel(ep, CFG, + USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) + | USBA_EPT_DIR_IN + | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) + | USBA_BF(BK_NUMBER, 1)); + if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { + set_protocol_stall(udc, ep); + DBG(DBG_ALL, "Test_SE0_NAK: ep0 not mapped\n"); + } else { + usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + DBG(DBG_ALL, "Entering Test_SE0_NAK mode...\n"); + } + break; + case 0x0400: + /* Test_Packet */ + ep = &udc->usba_ep[0]; + usba_ep_writel(ep, CFG, + USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) + | USBA_EPT_DIR_IN + | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) + | USBA_BF(BK_NUMBER, 1)); + if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { + set_protocol_stall(udc, ep); + DBG(DBG_ALL, "Test_Packet: ep0 not mapped\n"); + } else { + usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + usba_writel(udc, TST, USBA_TST_PKT_MODE); + memcpy(ep->fifo, test_packet_buffer, + sizeof(test_packet_buffer)); + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + DBG(DBG_ALL, "Entering Test_Packet mode...\n"); + } + break; + default: + DBG(DBG_ERR, "Invalid test mode: 0x%04x\n", test_mode); + return -EINVAL; + } + + return 0; +} + +/* Avoid overly long expressions */ +static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq) +{ + if (crq->wValue == cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) + return true; + return false; +} + +static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq) +{ + if (crq->wValue == cpu_to_le16(USB_DEVICE_TEST_MODE)) + return true; + return false; +} + +static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq) +{ + if (crq->wValue == cpu_to_le16(USB_ENDPOINT_HALT)) + return true; + return false; +} + +static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, + struct usb_ctrlrequest *crq) +{ + int retval = 0; + + switch (crq->bRequest) { + case USB_REQ_GET_STATUS: { + u16 status; + + if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) { + status = cpu_to_le16(udc->devstatus); + } else if (crq->bRequestType + == (USB_DIR_IN | USB_RECIP_INTERFACE)) { + status = cpu_to_le16(0); + } else if (crq->bRequestType + == (USB_DIR_IN | USB_RECIP_ENDPOINT)) { + struct usba_ep *target; + + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + status = 0; + if (is_stalled(udc, target)) + status |= cpu_to_le16(1); + } else { + goto delegate; + } + + /* Write directly to the FIFO. No queueing is done. */ + if (crq->wLength != cpu_to_le16(sizeof(status))) + goto stall; + ep->state = DATA_STAGE_IN; + __raw_writew(status, ep->fifo); + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + break; + } + + case USB_REQ_CLEAR_FEATURE: { + if (crq->bRequestType == USB_RECIP_DEVICE) { + if (feature_is_dev_remote_wakeup(crq)) + udc->devstatus + &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); + else + /* Can't CLEAR_FEATURE TEST_MODE */ + goto stall; + } else if (crq->bRequestType == USB_RECIP_ENDPOINT) { + struct usba_ep *target; + + if (crq->wLength != cpu_to_le16(0) || + !feature_is_ep_halt(crq)) + goto stall; + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL); + if (target->index != 0) + usba_ep_writel(target, CLR_STA, + USBA_TOGGLE_CLR); + } else { + goto delegate; + } + + send_status(udc, ep); + break; + } + + case USB_REQ_SET_FEATURE: { + if (crq->bRequestType == USB_RECIP_DEVICE) { + if (feature_is_dev_test_mode(crq)) { + send_status(udc, ep); + ep->state = STATUS_STAGE_TEST; + udc->test_mode = le16_to_cpu(crq->wIndex); + return 0; + } else if (feature_is_dev_remote_wakeup(crq)) { + udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP; + } else { + goto stall; + } + } else if (crq->bRequestType == USB_RECIP_ENDPOINT) { + struct usba_ep *target; + + if (crq->wLength != cpu_to_le16(0) || + !feature_is_ep_halt(crq)) + goto stall; + + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + usba_ep_writel(target, SET_STA, USBA_FORCE_STALL); + } else { + goto delegate; + } + + send_status(udc, ep); + break; + } + + case USB_REQ_SET_ADDRESS: + if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) + goto delegate; + + set_address(udc, le16_to_cpu(crq->wValue)); + send_status(udc, ep); + ep->state = STATUS_STAGE_ADDR; + break; + + default: +delegate: + spin_unlock(&udc->lock); + retval = udc->driver->setup(&udc->gadget, crq); + spin_lock(&udc->lock); + } + + return retval; + +stall: + DBG(DBG_ALL, "%s: Invalid setup request: %02x.%02x v%04x i%04x l%d\n", + ep->ep.name, crq->bRequestType, crq->bRequest, + le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex), + le16_to_cpu(crq->wLength)); + set_protocol_stall(udc, ep); + + return -1; +} + +static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep) +{ + struct usba_request *req; + u32 epstatus; + u32 epctrl; + +restart: + epstatus = usba_ep_readl(ep, STA); + epctrl = usba_ep_readl(ep, CTL); + + DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n", + ep->ep.name, ep->state, epstatus, epctrl); + + req = NULL; + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct usba_request, queue); + + if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { + if (req->submitted) + next_fifo_transaction(ep, req); + else + submit_request(ep, req); + + if (req->last_transaction) { + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); + } + goto restart; + } + if ((epstatus & epctrl) & USBA_TX_COMPLETE) { + usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE); + + switch (ep->state) { + case DATA_STAGE_IN: + usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = STATUS_STAGE_OUT; + break; + case STATUS_STAGE_ADDR: + /* Activate our new address */ + usba_writel(udc, CTRL, (usba_readl(udc, CTRL) + | USBA_FADDR_EN)); + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = WAIT_FOR_SETUP; + break; + case STATUS_STAGE_IN: + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, 0); + submit_next_request(ep); + } + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = WAIT_FOR_SETUP; + break; + case STATUS_STAGE_TEST: + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = WAIT_FOR_SETUP; + if (do_test_mode(udc)) + set_protocol_stall(udc, ep); + break; + default: + DBG(DBG_ALL, "%s: TXCOMP: Invalid endpoint state %d\n", + ep->ep.name, ep->state); + set_protocol_stall(udc, ep); + break; + } + + goto restart; + } + if ((epstatus & epctrl) & USBA_RX_BK_RDY) { + switch (ep->state) { + case STATUS_STAGE_OUT: + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, 0); + } + ep->state = WAIT_FOR_SETUP; + break; + + case DATA_STAGE_OUT: + receive_data(ep); + break; + + default: + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + DBG(DBG_ALL, "%s: RXRDY: Invalid endpoint state %d\n", + ep->ep.name, ep->state); + set_protocol_stall(udc, ep); + break; + } + + goto restart; + } + if (epstatus & USBA_RX_SETUP) { + union { + struct usb_ctrlrequest crq; + unsigned long data[2]; + } crq; + unsigned int pkt_len; + int ret; + + if (ep->state != WAIT_FOR_SETUP) { + /* + * Didn't expect a SETUP packet at this + * point. Clean up any pending requests (which + * may be successful). + */ + int status = -EPROTO; + + /* + * RXRDY and TXCOMP are dropped when SETUP + * packets arrive. Just pretend we received + * the status packet. + */ + if (ep->state == STATUS_STAGE_OUT || + ep->state == STATUS_STAGE_IN) { + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + status = 0; + } + + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } + } + + pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); + DBG(DBG_HW, "Packet length: %u\n", pkt_len); + if (pkt_len != sizeof(crq)) { + DBG(DBG_ALL, "udc: Invalid length %u (expected %zu)\n", + pkt_len, sizeof(crq)); + set_protocol_stall(udc, ep); + return; + } + + DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo); + memcpy(crq.data, ep->fifo, sizeof(crq)); + + /* Free up one bank in the FIFO so that we can + * generate or receive a reply right away. */ + usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP); + + if (crq.crq.bRequestType & USB_DIR_IN) { + /* + * The USB 2.0 spec states that "if wLength is + * zero, there is no data transfer phase." + * However, testusb #14 seems to actually + * expect a data phase even if wLength = 0... + */ + ep->state = DATA_STAGE_IN; + } else { + if (crq.crq.wLength != cpu_to_le16(0)) + ep->state = DATA_STAGE_OUT; + else + ep->state = STATUS_STAGE_IN; + } + + ret = -1; + if (ep->index == 0) { + ret = handle_ep0_setup(udc, ep, &crq.crq); + } else { + spin_unlock(&udc->lock); + ret = udc->driver->setup(&udc->gadget, &crq.crq); + spin_lock(&udc->lock); + } + + DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n", + crq.crq.bRequestType, crq.crq.bRequest, + le16_to_cpu(crq.crq.wLength), ep->state, ret); + + if (ret < 0) { + /* Let the host know that we failed */ + set_protocol_stall(udc, ep); + } + } +} + +static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) +{ + struct usba_request *req; + u32 epstatus; + u32 epctrl; + + epstatus = usba_ep_readl(ep, STA); + epctrl = usba_ep_readl(ep, CTL); + + DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus); + + while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { + DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name); + + if (list_empty(&ep->queue)) { + DBG(DBG_INT, "ep_irq: queue empty\n"); + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); + return; + } + + req = list_entry(ep->queue.next, struct usba_request, queue); + + if (req->submitted) + next_fifo_transaction(ep, req); + else + submit_request(ep, req); + + if (req->last_transaction) { + list_del_init(&req->queue); + submit_next_request(ep); + request_complete(ep, req, 0); + } + + epstatus = usba_ep_readl(ep, STA); + epctrl = usba_ep_readl(ep, CTL); + } + + if ((epstatus & epctrl) & USBA_RX_BK_RDY) { + DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); + receive_data(ep); + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + } +} + +static int usba_udc_irq(struct usba_udc *udc) +{ + u32 status, ep_status; + + spin_lock(&udc->lock); + + status = usba_readl(udc, INT_STA); + DBG(DBG_INT, "irq, status=%#08x\n", status); + + if (status & USBA_DET_SUSPEND) { + usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); + DBG(DBG_BUS, "Suspend detected\n"); + if (udc->gadget.speed != USB_SPEED_UNKNOWN && + udc->driver && udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } + + if (status & USBA_WAKE_UP) { + usba_writel(udc, INT_CLR, USBA_WAKE_UP); + DBG(DBG_BUS, "Wake Up CPU detected\n"); + } + + if (status & USBA_END_OF_RESUME) { + usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); + DBG(DBG_BUS, "Resume detected\n"); + if (udc->gadget.speed != USB_SPEED_UNKNOWN && + udc->driver && udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + + ep_status = USBA_BFEXT(EPT_INT, status); + if (ep_status) { + int i; + + for (i = 0; i < USBA_NR_ENDPOINTS; i++) + if (ep_status & (1 << i)) { + if (ep_is_control(&udc->usba_ep[i])) + usba_control_irq(udc, &udc->usba_ep[i]); + else + usba_ep_irq(udc, &udc->usba_ep[i]); + } + } + + if (status & USBA_END_OF_RESET) { + struct usba_ep *ep0; + + usba_writel(udc, INT_CLR, USBA_END_OF_RESET); + reset_all_endpoints(udc); + + if (udc->gadget.speed != USB_SPEED_UNKNOWN && + udc->driver->disconnect) { + udc->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + + if (status & USBA_HIGH_SPEED) + udc->gadget.speed = USB_SPEED_HIGH; + else + udc->gadget.speed = USB_SPEED_FULL; + + ep0 = &udc->usba_ep[0]; + ep0->desc = &usba_ep0_desc; + ep0->state = WAIT_FOR_SETUP; + usba_ep_writel(ep0, CFG, + (USBA_BF(EPT_SIZE, EP0_EPT_SIZE) + | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL) + | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); + usba_ep_writel(ep0, CTL_ENB, + USBA_EPT_ENABLE | USBA_RX_SETUP); + usba_writel(udc, INT_ENB, + (usba_readl(udc, INT_ENB) + | USBA_BF(EPT_INT, 1) + | USBA_DET_SUSPEND + | USBA_END_OF_RESUME)); + + /* + * Unclear why we hit this irregularly, e.g. in usbtest, + * but it's clearly harmless... + */ + if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) + DBG(DBG_ALL, "ODD: EP0 configuration is invalid!\n"); + } + + spin_unlock(&udc->lock); + + return 0; +} + +static int atmel_usba_start(struct usba_udc *udc) +{ + udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; + + udc->vbus_prev = 0; + + /* If Vbus is present, enable the controller and wait for reset */ + if (vbus_is_present(udc) && udc->vbus_prev == 0) { + usba_writel(udc, CTRL, USBA_ENABLE_MASK); + usba_writel(udc, INT_ENB, USBA_END_OF_RESET); + } + + return 0; +} + +static int atmel_usba_stop(struct usba_udc *udc) +{ + udc->gadget.speed = USB_SPEED_UNKNOWN; + reset_all_endpoints(udc); + + /* This will also disable the DP pullup */ + usba_writel(udc, CTRL, USBA_DISABLE_MASK); + + return 0; +} + +static struct usba_udc controller = { + .regs = (unsigned *)ATMEL_BASE_UDPHS, + .fifo = (unsigned *)ATMEL_BASE_UDPHS_FIFO, + .gadget = { + .ops = &usba_udc_ops, + .ep_list = LIST_HEAD_INIT(controller.gadget.ep_list), + .speed = USB_SPEED_HIGH, + .is_dualspeed = 1, + .name = "atmel_usba_udc", + }, +}; + +int usb_gadget_handle_interrupts(void) +{ + struct usba_udc *udc = &controller; + + return usba_udc_irq(udc); +} + + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct usba_udc *udc = &controller; + int ret; + + if (!driver || !driver->bind || !driver->setup) { + printf("bad paramter\n"); + return -EINVAL; + } + + if (udc->driver) { + printf("UDC already has a gadget driver\n"); + return -EBUSY; + } + + atmel_usba_start(udc); + + udc->driver = driver; + + ret = driver->bind(&udc->gadget); + if (ret) { + error("driver->bind() returned %d\n", ret); + udc->driver = NULL; + } + + return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usba_udc *udc = &controller; + + if (!driver || !driver->unbind || !driver->disconnect) { + error("bad paramter\n"); + return -EINVAL; + } + + driver->disconnect(&udc->gadget); + driver->unbind(&udc->gadget); + udc->driver = NULL; + + atmel_usba_stop(udc); + + return 0; +} + +static struct usba_ep *usba_udc_pdata(struct usba_platform_data *pdata, + struct usba_udc *udc) +{ + struct usba_ep *eps; + int i; + + eps = malloc(sizeof(struct usba_ep) * pdata->num_ep); + if (!eps) { + error("failed to alloc eps\n"); + return NULL; + } + + udc->gadget.ep0 = &eps[0].ep; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&eps[0].ep.ep_list); + + for (i = 0; i < pdata->num_ep; i++) { + struct usba_ep *ep = &eps[i]; + + ep->ep_regs = udc->regs + USBA_EPT_BASE(i); + ep->dma_regs = udc->regs + USBA_DMA_BASE(i); + ep->fifo = udc->fifo + USBA_FIFO_BASE(i); + ep->ep.ops = &usba_ep_ops; + ep->ep.name = pdata->ep[i].name; + ep->ep.maxpacket = pdata->ep[i].fifo_size; + ep->fifo_size = ep->ep.maxpacket; + ep->udc = udc; + INIT_LIST_HEAD(&ep->queue); + ep->nr_banks = pdata->ep[i].nr_banks; + ep->index = pdata->ep[i].index; + ep->can_dma = pdata->ep[i].can_dma; + ep->can_isoc = pdata->ep[i].can_isoc; + if (i) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + }; + + return eps; +} + +int usba_udc_probe(struct usba_platform_data *pdata) +{ + struct usba_udc *udc; + + udc = &controller; + + udc->usba_ep = usba_udc_pdata(pdata, udc); + + return 0; +} diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h new file mode 100644 index 0000000..92e462d --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -0,0 +1,326 @@ +/* + * Register definition for Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.h] + * + * Copyright (C) 2005-2013 Atmel Corporation + * Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LINUX_USB_GADGET_USBA_UDC_H__ +#define __LINUX_USB_GADGET_USBA_UDC_H__ + +/* USB register offsets */ +#define USBA_CTRL 0x0000 +#define USBA_FNUM 0x0004 +#define USBA_INT_ENB 0x0010 +#define USBA_INT_STA 0x0014 +#define USBA_INT_CLR 0x0018 +#define USBA_EPT_RST 0x001c +#define USBA_TST 0x00e0 + +/* USB endpoint register offsets */ +#define USBA_EPT_CFG 0x0000 +#define USBA_EPT_CTL_ENB 0x0004 +#define USBA_EPT_CTL_DIS 0x0008 +#define USBA_EPT_CTL 0x000c +#define USBA_EPT_SET_STA 0x0014 +#define USBA_EPT_CLR_STA 0x0018 +#define USBA_EPT_STA 0x001c + +/* USB DMA register offsets */ +#define USBA_DMA_NXT_DSC 0x0000 +#define USBA_DMA_ADDRESS 0x0004 +#define USBA_DMA_CONTROL 0x0008 +#define USBA_DMA_STATUS 0x000c + +/* Bitfields in CTRL */ +#define USBA_DEV_ADDR_OFFSET 0 +#define USBA_DEV_ADDR_SIZE 7 +#define USBA_FADDR_EN (1 << 7) +#define USBA_EN_USBA (1 << 8) +#define USBA_DETACH (1 << 9) +#define USBA_REMOTE_WAKE_UP (1 << 10) +#define USBA_PULLD_DIS (1 << 11) + +#if defined(CONFIG_AVR32) +#define USBA_ENABLE_MASK USBA_EN_USBA +#define USBA_DISABLE_MASK 0 +#elif defined(CONFIG_AT91FAMILY) +#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS) +#define USBA_DISABLE_MASK USBA_DETACH +#endif /* CONFIG_ARCH_AT91 */ + +/* Bitfields in FNUM */ +#define USBA_MICRO_FRAME_NUM_OFFSET 0 +#define USBA_MICRO_FRAME_NUM_SIZE 3 +#define USBA_FRAME_NUMBER_OFFSET 3 +#define USBA_FRAME_NUMBER_SIZE 11 +#define USBA_FRAME_NUM_ERROR (1 << 31) + +/* Bitfields in INT_ENB/INT_STA/INT_CLR */ +#define USBA_HIGH_SPEED (1 << 0) +#define USBA_DET_SUSPEND (1 << 1) +#define USBA_MICRO_SOF (1 << 2) +#define USBA_SOF (1 << 3) +#define USBA_END_OF_RESET (1 << 4) +#define USBA_WAKE_UP (1 << 5) +#define USBA_END_OF_RESUME (1 << 6) +#define USBA_UPSTREAM_RESUME (1 << 7) +#define USBA_EPT_INT_OFFSET 8 +#define USBA_EPT_INT_SIZE 16 +#define USBA_DMA_INT_OFFSET 24 +#define USBA_DMA_INT_SIZE 8 + +/* Bitfields in EPT_RST */ +#define USBA_RST_OFFSET 0 +#define USBA_RST_SIZE 16 + +/* Bitfields in USBA_TST */ +#define USBA_SPEED_CFG_OFFSET 0 +#define USBA_SPEED_CFG_SIZE 2 +#define USBA_TST_J_MODE (1 << 2) +#define USBA_TST_K_MODE (1 << 3) +#define USBA_TST_PKT_MODE (1 << 4) +#define USBA_OPMODE2 (1 << 5) + +/* Bitfields in EPT_CFG */ +#define USBA_EPT_SIZE_OFFSET 0 +#define USBA_EPT_SIZE_SIZE 3 +#define USBA_EPT_DIR_IN (1 << 3) +#define USBA_EPT_TYPE_OFFSET 4 +#define USBA_EPT_TYPE_SIZE 2 +#define USBA_BK_NUMBER_OFFSET 6 +#define USBA_BK_NUMBER_SIZE 2 +#define USBA_NB_TRANS_OFFSET 8 +#define USBA_NB_TRANS_SIZE 2 +#define USBA_EPT_MAPPED (1 << 31) + +/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */ +#define USBA_EPT_ENABLE (1 << 0) +#define USBA_AUTO_VALID (1 << 1) +#define USBA_INTDIS_DMA (1 << 3) +#define USBA_NYET_DIS (1 << 4) +#define USBA_DATAX_RX (1 << 6) +#define USBA_MDATA_RX (1 << 7) +/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */ +#define USBA_BUSY_BANK_IE (1 << 18) + +/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */ +#define USBA_FORCE_STALL (1 << 5) +#define USBA_TOGGLE_CLR (1 << 6) +#define USBA_TOGGLE_SEQ_OFFSET 6 +#define USBA_TOGGLE_SEQ_SIZE 2 +#define USBA_ERR_OVFLW (1 << 8) +#define USBA_RX_BK_RDY (1 << 9) +#define USBA_KILL_BANK (1 << 9) +#define USBA_TX_COMPLETE (1 << 10) +#define USBA_TX_PK_RDY (1 << 11) +#define USBA_ISO_ERR_TRANS (1 << 11) +#define USBA_RX_SETUP (1 << 12) +#define USBA_ISO_ERR_FLOW (1 << 12) +#define USBA_STALL_SENT (1 << 13) +#define USBA_ISO_ERR_CRC (1 << 13) +#define USBA_ISO_ERR_NBTRANS (1 << 13) +#define USBA_NAK_IN (1 << 14) +#define USBA_ISO_ERR_FLUSH (1 << 14) +#define USBA_NAK_OUT (1 << 15) +#define USBA_CURRENT_BANK_OFFSET 16 +#define USBA_CURRENT_BANK_SIZE 2 +#define USBA_BUSY_BANKS_OFFSET 18 +#define USBA_BUSY_BANKS_SIZE 2 +#define USBA_BYTE_COUNT_OFFSET 20 +#define USBA_BYTE_COUNT_SIZE 11 +#define USBA_SHORT_PACKET (1 << 31) + +/* Bitfields in DMA_CONTROL */ +#define USBA_DMA_CH_EN (1 << 0) +#define USBA_DMA_LINK (1 << 1) +#define USBA_DMA_END_TR_EN (1 << 2) +#define USBA_DMA_END_BUF_EN (1 << 3) +#define USBA_DMA_END_TR_IE (1 << 4) +#define USBA_DMA_END_BUF_IE (1 << 5) +#define USBA_DMA_DESC_LOAD_IE (1 << 6) +#define USBA_DMA_BURST_LOCK (1 << 7) +#define USBA_DMA_BUF_LEN_OFFSET 16 +#define USBA_DMA_BUF_LEN_SIZE 16 + +/* Bitfields in DMA_STATUS */ +#define USBA_DMA_CH_ACTIVE (1 << 1) +#define USBA_DMA_END_TR_ST (1 << 4) +#define USBA_DMA_END_BUF_ST (1 << 5) +#define USBA_DMA_DESC_LOAD_ST (1 << 6) + +/* Constants for SPEED_CFG */ +#define USBA_SPEED_CFG_NORMAL 0 +#define USBA_SPEED_CFG_FORCE_HIGH 2 +#define USBA_SPEED_CFG_FORCE_FULL 3 + +/* Constants for EPT_SIZE */ +#define USBA_EPT_SIZE_8 0 +#define USBA_EPT_SIZE_16 1 +#define USBA_EPT_SIZE_32 2 +#define USBA_EPT_SIZE_64 3 +#define USBA_EPT_SIZE_128 4 +#define USBA_EPT_SIZE_256 5 +#define USBA_EPT_SIZE_512 6 +#define USBA_EPT_SIZE_1024 7 + +/* Constants for EPT_TYPE */ +#define USBA_EPT_TYPE_CONTROL 0 +#define USBA_EPT_TYPE_ISO 1 +#define USBA_EPT_TYPE_BULK 2 +#define USBA_EPT_TYPE_INT 3 + +/* Constants for BK_NUMBER */ +#define USBA_BK_NUMBER_ZERO 0 +#define USBA_BK_NUMBER_ONE 1 +#define USBA_BK_NUMBER_DOUBLE 2 +#define USBA_BK_NUMBER_TRIPLE 3 + +/* Bit manipulation macros */ +#define USBA_BF(name, value) \ + (((value) & ((1 << USBA_##name##_SIZE) - 1)) \ + << USBA_##name##_OFFSET) +#define USBA_BFEXT(name, value) \ + (((value) >> USBA_##name##_OFFSET) \ + & ((1 << USBA_##name##_SIZE) - 1)) +#define USBA_BFINS(name, value, old) \ + (((old) & ~(((1 << USBA_##name##_SIZE) - 1) \ + << USBA_##name##_OFFSET)) \ + | USBA_BF(name, value)) + +/* Register access macros */ +#define usba_readl(udc, reg) \ + __raw_readl((udc)->regs + USBA_##reg) +#define usba_writel(udc, reg, value) \ + __raw_writel((value), (udc)->regs + USBA_##reg) +#define usba_ep_readl(ep, reg) \ + __raw_readl((ep)->ep_regs + USBA_EPT_##reg) +#define usba_ep_writel(ep, reg, value) \ + __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg) +#define usba_dma_readl(ep, reg) \ + __raw_readl((ep)->dma_regs + USBA_DMA_##reg) +#define usba_dma_writel(ep, reg, value) \ + __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + +/* Calculate base address for a given endpoint or DMA controller */ +#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) +#define USBA_DMA_BASE(x) (0x300 + (x) * 0x10) +#define USBA_FIFO_BASE(x) ((x) << 16) + +/* Synth parameters */ +#define USBA_NR_ENDPOINTS 7 + +#define EP0_FIFO_SIZE 64 +#define EP0_EPT_SIZE USBA_EPT_SIZE_64 +#define EP0_NR_BANKS 1 + +#define DBG_ERR 0x0001 /* report all error returns */ +#define DBG_HW 0x0002 /* debug hardware initialization */ +#define DBG_GADGET 0x0004 /* calls to/from gadget driver */ +#define DBG_INT 0x0008 /* interrupts */ +#define DBG_BUS 0x0010 /* report changes in bus state */ +#define DBG_QUEUE 0x0020 /* debug request queue processing */ +#define DBG_FIFO 0x0040 /* debug FIFO contents */ +#define DBG_DMA 0x0080 /* debug DMA handling */ +#define DBG_REQ 0x0100 /* print out queued request length */ +#define DBG_ALL 0xffff +#define DBG_NONE 0x0000 + +#define DEBUG_LEVEL (DBG_ERR) + +#define DBG(level, fmt, ...) \ + do { \ + if ((level) & DEBUG_LEVEL) \ + debug("udc: " fmt, ## __VA_ARGS__); \ + } while (0) + +enum usba_ctrl_state { + WAIT_FOR_SETUP, + DATA_STAGE_IN, + DATA_STAGE_OUT, + STATUS_STAGE_IN, + STATUS_STAGE_OUT, + STATUS_STAGE_ADDR, + STATUS_STAGE_TEST, +}; + +struct usba_dma_desc { + dma_addr_t next; + dma_addr_t addr; + u32 ctrl; +}; + +struct usba_ep { + int state; + void *ep_regs; + void *dma_regs; + void *fifo; + struct usb_ep ep; + struct usba_udc *udc; + + struct list_head queue; + + u16 fifo_size; + u8 nr_banks; + u8 index; + unsigned int can_dma:1; + unsigned int can_isoc:1; + unsigned int is_isoc:1; + unsigned int is_in:1; + + const struct usb_endpoint_descriptor *desc; +}; + +struct usba_request { + struct usb_request req; + struct list_head queue; + + u32 ctrl; + + unsigned int submitted:1; + unsigned int last_transaction:1; + unsigned int using_dma:1; + unsigned int mapped:1; +}; + +struct usba_udc { + void *regs; + void *fifo; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct platform_device *pdev; + int irq; + int vbus_pin; + int vbus_pin_inverted; + int num_ep; + struct usba_ep *usba_ep; + + u16 devstatus; + + u16 test_mode; + int vbus_prev; +}; + +static inline struct usba_ep *to_usba_ep(struct usb_ep *ep) +{ + return container_of(ep, struct usba_ep, ep); +} + +static inline struct usba_request *to_usba_req(struct usb_request *req) +{ + return container_of(req, struct usba_request, req); +} + +static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct usba_udc, gadget); +} + +#define ep_is_control(ep) ((ep)->index == 0) +#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE) + +#endif /* __LINUX_USB_GADGET_USBA_UDC_H */ diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index f563afe..014a679 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -10,6 +10,7 @@ */ #include <common.h> +#include <asm/unaligned.h> #include <asm/errno.h> #include <linux/list.h> #include <linux/string.h> @@ -86,7 +87,8 @@ int usb_gadget_config_buf( /* config descriptor first */ if (length < USB_DT_CONFIG_SIZE || !desc) return -EINVAL; - *cp = *config; + /* config need not be aligned */ + memcpy(cp, config, sizeof(*cp)); /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, @@ -100,7 +102,7 @@ int usb_gadget_config_buf( /* patch up the config descriptor */ cp->bLength = USB_DT_CONFIG_SIZE; cp->bDescriptorType = USB_DT_CONFIG; - cp->wTotalLength = cpu_to_le16(len); + put_unaligned_le16(len, &cp->wTotalLength); cp->bmAttributes |= USB_CONFIG_ATT_ONE; return len; } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 579893c..700d5fb 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -849,9 +849,10 @@ static struct usb_gadget_strings stringtab = { }; /*============================================================================*/ -static u8 control_req[USB_BUFSIZ]; +DEFINE_CACHE_ALIGN_BUFFER(u8, control_req, USB_BUFSIZ); + #if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) -static u8 status_req[STATUS_BYTECOUNT] __attribute__ ((aligned(4))); +DEFINE_CACHE_ALIGN_BUFFER(u8, status_req, STATUS_BYTECOUNT); #endif diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h index 34a4dde..cc2c455 100644 --- a/drivers/usb/gadget/f_dfu.h +++ b/drivers/usb/gadget/f_dfu.h @@ -82,7 +82,4 @@ struct dfu_function_descriptor { __le16 wTransferSize; __le16 bcdDFUVersion; } __packed; - -/* configuration-specific linkup */ -int dfu_add(struct usb_configuration *c); #endif /* __F_DFU_H_ */ diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index a3e05a8..40868c0 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -7,7 +7,6 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include <errno.h> #include <common.h> #include <malloc.h> @@ -15,11 +14,11 @@ #include <part.h> #include <g_dnl.h> -#include "f_dfu.h" +#include <usb_mass_storage.h> +#include <dfu.h> #include "gadget_chips.h" #include "composite.c" -#include "f_mass_storage.c" /* * One needs to define the following: diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fdad739..3ae04c0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -36,6 +36,12 @@ #define CONFIG_USB_MAX_CONTROLLER_COUNT 1 #endif +/* + * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt. + * Let's time out after 8 to have a little safety margin on top of that. + */ +#define HCHALT_TIMEOUT (8 * 1000) + static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; #define ALIGN_END_ADDR(type, ptr, size) \ @@ -190,6 +196,36 @@ out: return ret; } +static int ehci_shutdown(struct ehci_ctrl *ctrl) +{ + int i, ret = 0; + uint32_t cmd, reg; + + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); + cmd &= ~(CMD_PSE | CMD_ASE); + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0, + 100 * 1000); + + if (!ret) { + for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) { + reg = ehci_readl(&ctrl->hcor->or_portsc[i]); + reg |= EHCI_PS_SUSP; + ehci_writel(&ctrl->hcor->or_portsc[i], reg); + } + + cmd &= ~CMD_RUN; + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT, + HCHALT_TIMEOUT); + } + + if (ret) + puts("EHCI failed to shut down host controller.\n"); + + return ret; +} + static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) { uint32_t delta, next; @@ -808,6 +844,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, } break; case USB_PORT_FEAT_TEST: + ehci_shutdown(ctrl); reg &= ~(0xf << 16); reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16; ehci_writel(status_reg, reg); @@ -878,6 +915,7 @@ unknown: int usb_lowlevel_stop(int index) { + ehci_shutdown(&ehcic[index]); return ehci_hcd_stop(index); } |