diff options
Diffstat (limited to 'cpu/arm926ejs/mx28/mmcops.c')
-rw-r--r-- | cpu/arm926ejs/mx28/mmcops.c | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/cpu/arm926ejs/mx28/mmcops.c b/cpu/arm926ejs/mx28/mmcops.c new file mode 100644 index 0000000..eed2046 --- /dev/null +++ b/cpu/arm926ejs/mx28/mmcops.c @@ -0,0 +1,579 @@ +/* + * (C) Copyright 2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <command.h> +#include <exports.h> +#include <mmc.h> + +#if defined(CONFIG_GENERIC_MMC) && defined(CONFIG_CUSTOMIZE_MMCOPS) +#define MMCOPS_DEBUG + +#define MBR_SIGNATURE 0xaa55 +#define MBR_SECTOR_COUNT (CONFIG_ENV_OFFSET >> 9) +/* 1. RSV partition (env and uImage) */ +#define RSVPART_SECTOR_OFFSET (MBR_SECTOR_COUNT) +#define RSVPART_SECTOR_COUNT 0x3ffe /* 8 MB */ +/* 2. FAT partition */ +#define FATPART_FILESYS 0xb +#define FATPART_SECTOR_OFFSET (RSVPART_SECTOR_OFFSET + RSVPART_SECTOR_COUNT) +#define FATPART_SECTOR_COUNT 0x10000 /* 32 MB (minimal) */ +/* 3. EXT partition */ +#define EXTPART_FILESYS 0x83 +#define EXTPART_SECTOR_COUNT 0xc0000 /* 384 MB */ +/* 4. SB partition (uboot.sb or linux.sb) */ +#define SBPART_FILESYS 'S' +#define SBPART_SECTOR_COUNT 0x4000 /* 8 MB */ +#define CB_SIGNATURE 0x00112233 +#define CB_SECTOR_COUNT 2 + +#define MAX_CYLINDERS 1024 +#define MAX_HEADS 256 +#define MAX_SECTORS 63 + +struct partition { + u8 boot_flag; + u8 start_head; + u8 start_sector; + u8 start_cylinder; + u8 file_system; + u8 end_head; + u8 end_sector; + u8 end_cylinder; + u32 sector_offset; + u32 sector_count; +} __attribute__ ((__packed__)); + +struct partition_table { + u8 reserved[446]; + struct partition partitions[4]; + u16 signature; +}; + +struct drive_info { + u32 chip_num; + u32 drive_type; + u32 drive_tag; + u32 sector_offset; + u32 sector_count; +}; + +struct config_block { + u32 signature; + u32 primary_tag; + u32 secondary_tag; + u32 num_copies; + struct drive_info drive_info[2]; +}; + +static int mmc_format(int dev) +{ + int rc = 0; + u8 *buf = 0; + u32 i, cnt, total_sectors; + u32 offset[4]; + struct config_block *cb; + struct partition_table *mbr; + struct mmc *mmc = find_mmc_device(dev); + + /* Warning */ + printf("WARN: Data on card will get lost with format.\n" + "Continue?(y/n)"); + char ch = getc(); + printf("\n"); + if (ch != 'y') { + rc = -1; + goto out; + } + + /* Allocate sector buffer */ + buf = malloc(mmc->read_bl_len); + if (!buf) { + printf("%s[%d]: malloc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + memset(buf, 0, mmc->read_bl_len); + + /* Erase the first sector of each partition */ + cnt = mmc->block_dev.block_read(dev, 0, 1, buf); + if (cnt != 1) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + mbr = (struct partition_table *)buf; + if (mbr->signature == MBR_SIGNATURE) { + /* Get sector offset of each partition */ + for (i = 0; i < 4; i++) + offset[i] = mbr->partitions[i].sector_offset; + /* Erase */ + memset(buf, 0, mmc->read_bl_len); + for (i = 0; i < 4; i++) { + if (offset[i] > 0) { + cnt = mmc->block_dev.block_write(dev, + offset[i], 1, buf); + if (cnt != 1) { + printf("%s[%d]: write mmc error\n", + __func__, __LINE__); + rc = -1; + goto out; + } + } + } + } + + /* Get total sectors */ + total_sectors = mmc->capacity >> 9; + if (RSVPART_SECTOR_COUNT + SBPART_SECTOR_COUNT > total_sectors) { + printf("Card capacity is too low to format\n"); + rc = -1; + goto out; + } + + /* Write config block */ + cb = (struct config_block *)buf; + cb->signature = CB_SIGNATURE; + cb->num_copies = 2; + cb->primary_tag = 0x1; + cb->secondary_tag = 0x2; + cb->drive_info[0].chip_num = 0; + cb->drive_info[0].drive_type = 0; + cb->drive_info[0].drive_tag = 0x1; + cb->drive_info[0].sector_count = SBPART_SECTOR_COUNT - CB_SECTOR_COUNT; + cb->drive_info[0].sector_offset = + total_sectors - cb->drive_info[0].sector_count; + cb->drive_info[1].chip_num = 0; + cb->drive_info[1].drive_type = 0; + cb->drive_info[1].drive_tag = 0x2; + cb->drive_info[1].sector_count = SBPART_SECTOR_COUNT - CB_SECTOR_COUNT; + cb->drive_info[1].sector_offset = + total_sectors - cb->drive_info[1].sector_count; + + cnt = mmc->block_dev.block_write(dev, + total_sectors - SBPART_SECTOR_COUNT, 1, (void *)cb); + if (cnt != 1) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + + /* Prepare for MBR */ + memset(buf, 0, mmc->read_bl_len); + mbr = (struct partition_table *)buf; + + /* RSV partition */ + mbr->partitions[0].sector_offset = RSVPART_SECTOR_OFFSET; + mbr->partitions[0].sector_count = RSVPART_SECTOR_COUNT; + + /* SB partition */ + mbr->partitions[3].file_system = SBPART_FILESYS; + mbr->partitions[3].sector_offset = total_sectors - SBPART_SECTOR_COUNT; + mbr->partitions[3].sector_count = SBPART_SECTOR_COUNT; + + /* EXT partition */ + if (EXTPART_SECTOR_COUNT + SBPART_SECTOR_COUNT + + RSVPART_SECTOR_COUNT > total_sectors) { +#ifdef MMCOPS_DEBUG + printf("No room for EXT partition\n"); +#endif + } else { + mbr->partitions[2].file_system = EXTPART_FILESYS; + mbr->partitions[2].sector_offset = total_sectors - + SBPART_SECTOR_COUNT - EXTPART_SECTOR_COUNT; + mbr->partitions[2].sector_count = EXTPART_SECTOR_COUNT; + } + + /* FAT partition */ + if (FATPART_SECTOR_COUNT + MBR_SECTOR_COUNT + + mbr->partitions[0].sector_count + + mbr->partitions[2].sector_count + + mbr->partitions[3].sector_count > total_sectors) { +#ifdef MMCOPS_DEBUG + printf("No room for FAT partition\n"); +#endif + goto out; + } + mbr->partitions[1].file_system = FATPART_FILESYS; + mbr->partitions[1].sector_offset = FATPART_SECTOR_OFFSET; + mbr->partitions[1].sector_count = total_sectors - MBR_SECTOR_COUNT - + mbr->partitions[0].sector_count - + mbr->partitions[2].sector_count - + mbr->partitions[3].sector_count; + +out: + if (rc == 0) { + /* Write MBR */ + mbr->signature = MBR_SIGNATURE; + cnt = mmc->block_dev.block_write(dev, 0, 1, (void *)mbr); + if (cnt != 1) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + } else + printf("Done.\n"); + } + + if (!buf) + free(buf); + return rc; +} + +static int install_sbimage(int dev, void *addr, u32 size) +{ + int rc = 0; + u8 *buf = 0; + u32 cnt, offset, cb_offset, sectors, not_format = 0; + struct config_block *cb; + struct partition_table *mbr; + struct mmc *mmc = find_mmc_device(dev); + + /* Allocate sector buffer */ + buf = malloc(mmc->read_bl_len); + if (!buf) { + printf("%s[%d]: malloc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + + /* Check partition */ + offset = 0; + cnt = mmc->block_dev.block_read(dev, offset, 1, buf); + if (cnt != 1) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + mbr = (struct partition_table *)buf; + if ((mbr->signature != MBR_SIGNATURE) || + (mbr->partitions[3].file_system != SBPART_FILESYS)) + not_format = 1; + + /* Check config block */ + offset = mbr->partitions[3].sector_offset; + cb_offset = offset; /* Save for later use */ + cnt = mmc->block_dev.block_read(dev, offset, 1, buf); + if (cnt != 1) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + cb = (struct config_block *)buf; + if (cb->signature != CB_SIGNATURE) + not_format = 1; + + /* Not formatted */ + if (not_format) { + printf("Card is not formatted yet\n"); + rc = -1; + goto out; + } + + /* Calculate sectors of image */ + sectors = size / mmc->read_bl_len; + if (size % mmc->read_bl_len) + sectors++; + + /* Write image */ + offset = cb->drive_info[0].sector_offset; + cnt = mmc->block_dev.block_write(dev, offset, sectors, addr); + if (cnt != sectors) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + /* Verify */ + cnt = mmc->block_dev.block_read(dev, offset, sectors, + addr + sectors * mmc->read_bl_len); + if (cnt != sectors) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + if (memcmp(addr, addr + sectors * mmc->read_bl_len, + sectors * mmc->read_bl_len)) { + printf("Verifying sbImage write fails"); + rc = -1; + goto out; + } + + /* Redundant one */ + offset += sectors; + cnt = mmc->block_dev.block_write(dev, offset, sectors, addr); + if (cnt != sectors) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + /* Verify */ + cnt = mmc->block_dev.block_read(dev, offset, sectors, + addr + sectors * mmc->read_bl_len); + if (cnt != sectors) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + if (memcmp(addr, addr + sectors * mmc->read_bl_len, + sectors * mmc->read_bl_len)) { + printf("Verifying redundant sbImage write fails"); + rc = -1; + goto out; + } + + /* Update config block */ + cb->drive_info[0].sector_count = sectors; + cb->drive_info[1].sector_count = sectors; + cb->drive_info[1].sector_offset = offset; + cnt = mmc->block_dev.block_write(dev, cb_offset, 1, (void *)cb); + if (cnt != 1) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + + /* Done */ + printf("Done: %d (%x hex) sectors written at %d (%x hex)\n", + sectors, sectors, offset - sectors, offset - sectors); + +out: + if (!buf) + free(buf); + return rc; +} + +static int install_uimage(int dev, void *addr, u32 size) +{ + int rc = 0; + u8 *buf = 0; + u32 cnt, offset, sectors; + struct partition_table *mbr; + struct mmc *mmc = find_mmc_device(dev); + + /* Calculate sectors of uImage */ + sectors = size / mmc->read_bl_len; + if (size % mmc->read_bl_len) + sectors++; + + /* Allocate sector buffer */ + buf = malloc(mmc->read_bl_len); + if (!buf) { + printf("%s[%d]: malloc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + + /* Check partition */ + offset = 0; + cnt = mmc->block_dev.block_read(dev, offset, 1, buf); + if (cnt != 1) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + mbr = (struct partition_table *)buf; + if (mbr->signature != MBR_SIGNATURE) { + printf("No valid partition table\n"); + rc = -1; + goto out; + } + if (mbr->partitions[0].sector_count < sectors) { + printf("No enough uImage partition room\n"); + rc = -1; + goto out; + } + + /* Write uImage */ + offset = mbr->partitions[0].sector_offset + (CONFIG_ENV_SIZE >> 9); + cnt = mmc->block_dev.block_write(dev, offset, sectors, addr); + if (cnt != sectors) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + /* Verify */ + cnt = mmc->block_dev.block_read(dev, offset, sectors, + addr + sectors * mmc->read_bl_len); + if (cnt != sectors) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + if (memcmp(addr, addr + sectors * mmc->read_bl_len, + sectors * mmc->read_bl_len)) { + printf("Verifying uImage write fails"); + rc = -1; + goto out; + } + + /* Done */ + printf("Done: %d (%x hex) sectors written at %d (%x hex)\n", + sectors, sectors, offset, offset); + +out: + if (!buf) + free(buf); + return rc; +} + +static int install_rootfs(int dev, void *addr, u32 size) +{ + int rc = 0; + u8 *buf = 0; + u32 cnt, offset, sectors; + struct partition_table *mbr; + struct mmc *mmc = find_mmc_device(dev); + + /* Calculate sectors of rootfs */ + sectors = size / mmc->read_bl_len; + if (size % mmc->read_bl_len) + sectors++; + + /* Allocate sector buffer */ + buf = malloc(mmc->read_bl_len); + if (!buf) { + printf("%s[%d]: malloc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + + /* Check partition */ + offset = 0; + cnt = mmc->block_dev.block_read(dev, offset, 1, buf); + if (cnt != 1) { + printf("%s[%d]: read mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + mbr = (struct partition_table *)buf; + if ((mbr->signature != MBR_SIGNATURE) || + (mbr->partitions[2].file_system != EXTPART_FILESYS)) { + printf("No rootfs partition\n"); + rc = -1; + goto out; + } + if (mbr->partitions[2].sector_count < sectors) { + printf("No enough rootfs partition room\n"); + rc = -1; + goto out; + } + + /* Write rootfs */ + offset = mbr->partitions[2].sector_offset; + cnt = mmc->block_dev.block_write(dev, offset, sectors, addr); + if (cnt != sectors) { + printf("%s[%d]: write mmc error\n", __func__, __LINE__); + rc = -1; + goto out; + } + + /* Done */ + printf("Done: %d (%x hex) sectors written at %d (%x hex)\n", + sectors, sectors, offset, offset); + +out: + if (!buf) + free(buf); + return rc; +} + +int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int dev = 0; + struct mmc *mmc; + + if (argc < 2) + goto err_out; + + if (strcmp(argv[1], "read") && strcmp(argv[1], "write") && + strcmp(argv[1], "rescan") && strcmp(argv[1], "format") && + strcmp(argv[1], "install") && strcmp(argv[1], "list")) + goto err_out; + + if (argc == 2) { /* list */ + print_mmc_devices('\n'); + return 0; + } + + /* Find and init mmc */ + dev = simple_strtoul(argv[2], NULL, 10); + mmc = find_mmc_device(dev); + if (!mmc) { + printf("%s[%d]: find mmc error\n", __func__, __LINE__); + return -1; + } + if (mmc_init(mmc)) { + printf("%s[%d]: init mmc error\n", __func__, __LINE__); + return -1; + } + + if (!strcmp(argv[1], "format")) + mmc_format(dev); + if (argc == 3) /* rescan (mmc_init) */ + return 0; + + if (argc != 6) + goto err_out; + + if (!strcmp(argv[1], "read") || !strcmp(argv[1], "write")) { + void *addr = (void *)simple_strtoul(argv[3], NULL, 16); + u32 blk = simple_strtoul(argv[4], NULL, 16); + u32 cnt = simple_strtoul(argv[5], NULL, 16); + u32 n; + + printf("\nMMC %s: dev # %d, block # %d, count %d ... ", + argv[1], dev, blk, cnt); + + if (!strcmp(argv[1], "read")) { + n = mmc->block_dev.block_read(dev, blk, cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); /* FIXME */ + } else /* write */ + n = mmc->block_dev.block_write(dev, blk, cnt, addr); + + printf("%d blocks %s: %s\n", n, argv[1], + (n == cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : -1; + } else if (!strcmp(argv[1], "install")) { + void *addr = (void *)simple_strtoul(argv[3], NULL, 16); + u32 size = simple_strtoul(argv[4], NULL, 16); + + if (!strcmp(argv[5], "sbImage")) + return install_sbimage(dev, addr, size); + else if (!strcmp(argv[5], "uImage")) + return install_uimage(dev, addr, size); + else if (!strcmp(argv[5], "rootfs")) + return install_rootfs(dev, addr, size); + } + +err_out: + printf("Usage:\n%s\n", cmdtp->usage); + return -1; +} + +U_BOOT_CMD( + mmc, 6, 1, do_mmcops, + "MMC sub system", + "mmc read <device num> addr blk# cnt\n" + "mmc write <device num> addr blk# cnt\n" + "mmc rescan <device num>\n" + "mmc format <device num>\n" + "mmc install <device num> addr size sbImage/uImage/rootfs\n" + "mmc list - lists available devices"); +#endif /* (CONFIG_GENERIC_MMC && CONFIG_CUSTOMIZE_MMCOPS) */ |