From 2ce3582aba3568d23bddee886ee5387d00f1399e Mon Sep 17 00:00:00 2001 From: Ruixuan Li Date: Wed, 21 Mar 2018 17:24:25 +0800 Subject: [PATCH] emmc: fix dtb backup mechanism PD#162119: emmc: fix dtb backup mechanism Change-Id: I32ca6ffb2c01bb5f50a5af3f0b2942077da61eb8 Signed-off-by: Ruixuan Li --- drivers/amlogic/mmc/emmc_partitions.c | 166 +++++++++++++++++++++++--- 1 file changed, 151 insertions(+), 15 deletions(-) diff --git a/drivers/amlogic/mmc/emmc_partitions.c b/drivers/amlogic/mmc/emmc_partitions.c index 10552aaf05fb..0f9ab3a5b2ee 100644 --- a/drivers/amlogic/mmc/emmc_partitions.c +++ b/drivers/amlogic/mmc/emmc_partitions.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -48,6 +48,135 @@ struct class *amlmmc_dtb_class; struct mmc_card *card_dtb; struct mmc_partitions_fmt *pt_fmt; +/* + * 2 copies dtb were stored in dtb area. + * each is 256K. + * timestamp&checksum are in the tail. + * |<--------------DTB Area-------------->| + * |<------DTB1------->|<------DTB2------>| + */ + +#define DTB_RESERVE_OFFSET (4 * SZ_1M) +#define DTB_BLK_SIZE (0x200) +#define DTB_BLK_CNT (512) +#define DTB_SIZE (DTB_BLK_CNT * DTB_BLK_SIZE) +#define DTB_COPIES (2) +#define DTB_AREA_BLK_CNT (DTB_BLK_CNT * DTB_COPIES) +/* pertransfer for internal opearations. */ +#define MAX_TRANS_BLK (256) +#define MAX_TRANS_SIZE (MAX_TRANS_BLK * DTB_BLK_SIZE) +#define stamp_after(a, b) ((int)(b) - (int)(a) < 0) +struct aml_dtb_rsv { + u8 data[DTB_BLK_SIZE*DTB_BLK_CNT - 4*sizeof(unsigned int)]; + unsigned int magic; + unsigned int version; + unsigned int timestamp; + unsigned int checksum; +}; + +struct aml_dtb_info { + unsigned int stamp[2]; + u8 valid[2]; +}; + +static struct aml_dtb_info dtb_infos = {{0, 0}, {0, 0} }; +/* dtb read&write operation with backup updates */ +static unsigned int _calc_dtb_checksum(struct aml_dtb_rsv *dtb) +{ + int i = 0; + int size = sizeof(struct aml_dtb_rsv) - sizeof(unsigned int); + unsigned int *buffer; + unsigned int checksum = 0; + + size = size >> 2; + buffer = (unsigned int *) dtb; + while (i < size) + checksum += buffer[i++]; + return checksum; +} + +static int _verify_dtb_checksum(struct aml_dtb_rsv *dtb) +{ + unsigned int checksum; + + checksum = _calc_dtb_checksum(dtb); + pr_info("calc %x, store %x\n", checksum, dtb->checksum); + + return !(checksum == dtb->checksum); +} + + +static int _dtb_read(struct mmc_card *mmc, + int blk, unsigned char *buf) +{ + int ret = 0; + unsigned char *dst = NULL; + int bit = mmc->csd.read_blkbits; + int cnt = CONFIG_DTB_SIZE >> bit; + + dst = (unsigned char *)buf; + mmc_claim_host(mmc->host); + do { + ret = mmc_read_internal(mmc, blk, MAX_TRANS_BLK, dst); + if (ret) { + pr_err("%s: save dtb error", __func__); + ret = -EFAULT; + break; + } + blk += MAX_TRANS_BLK; + cnt -= MAX_TRANS_BLK; + dst = (unsigned char *)buf + MAX_TRANS_SIZE; + } while (cnt != 0); + mmc_release_host(mmc->host); + return ret; +} + +static int _dtb_init(struct mmc_card *mmc) +{ + int ret = 0; + struct aml_dtb_rsv *dtb; + struct aml_dtb_info *info = &dtb_infos; + int cpy = 1, valid = 0; + int bit = mmc->csd.read_blkbits; + int blk; + unsigned int pgcnt; + struct page *page = NULL; + + pgcnt = PAGE_ALIGN(CONFIG_DTB_SIZE) >> PAGE_SHIFT; + + page = dma_alloc_from_contiguous(NULL, + pgcnt, get_order(CONFIG_DTB_SIZE)); + + if (!page) + return -ENOMEM; + dtb = page_address(page); + + /* read dtb2 1st, for compatibility without checksum. */ + while (cpy >= 0) { + blk = ((get_reserve_partition_off_from_tbl() + + DTB_RESERVE_OFFSET) >> bit) + + cpy * DTB_BLK_CNT; + if (_dtb_read(mmc, blk, (unsigned char *)dtb)) { + pr_err("%s: block # %#x ERROR!\n", + __func__, blk); + } else { + ret = _verify_dtb_checksum(dtb); + if (!ret) { + info->stamp[cpy] = dtb->timestamp; + info->valid[cpy] = 1; + } else + pr_err("cpy %d is not valid\n", cpy); + } + valid += info->valid[cpy]; + cpy--; + } + pr_info("total valid %d\n", valid); + + dma_release_from_contiguous(NULL, page, pgcnt); + + return ret; +} + int amlmmc_dtb_write(struct mmc_card *card, unsigned char *buf, int len) { @@ -161,6 +290,8 @@ ssize_t mmc_dtb_read(struct file *file, unsigned char *dtb_ptr = NULL; ssize_t read_size = 0; int ret = 0; + unsigned int pgcnt; + struct page *page = NULL; if (*ppos == CONFIG_DTB_SIZE) return 0; @@ -170,11 +301,13 @@ ssize_t mmc_dtb_read(struct file *file, return -EFAULT; } - dtb_ptr = vmalloc(CONFIG_DTB_SIZE); - if (dtb_ptr == NULL) { - /* pr_err("%s: malloc buf failed", __func__);*/ + pgcnt = PAGE_ALIGN(CONFIG_DTB_SIZE) >> PAGE_SHIFT; + + page = dma_alloc_from_contiguous(NULL, + pgcnt, get_order(CONFIG_DTB_SIZE)); + if (!page) return -ENOMEM; - } + dtb_ptr = page_address(page); mmc_claim_host(card_dtb->host); ret = amlmmc_dtb_read(card_dtb, @@ -191,9 +324,9 @@ ssize_t mmc_dtb_read(struct file *file, read_size = count; ret = copy_to_user(buf, (dtb_ptr + *ppos), read_size); *ppos += read_size; + exit: - mmc_release_host(card_dtb->host); - vfree(dtb_ptr); + dma_release_from_contiguous(NULL, page, pgcnt); return read_size; } @@ -204,6 +337,8 @@ ssize_t mmc_dtb_write(struct file *file, unsigned char *dtb_ptr = NULL; ssize_t write_size = 0; int ret = 0; + unsigned int pgcnt = PAGE_ALIGN(CONFIG_DTB_SIZE) >> PAGE_SHIFT; + struct page *page = NULL; if (*ppos == CONFIG_DTB_SIZE) return 0; @@ -212,12 +347,12 @@ ssize_t mmc_dtb_write(struct file *file, pr_err("%s: out of space!", __func__); return -EFAULT; } - dtb_ptr = vmalloc(CONFIG_DTB_SIZE); - if (dtb_ptr == NULL) { - /* pr_err("%s: malloc buf failed", __func__);*/ + + page = dma_alloc_from_contiguous(NULL, + pgcnt, get_order(CONFIG_DTB_SIZE)); + if (!page) return -ENOMEM; - } - mmc_claim_host(card_dtb->host); + dtb_ptr = page_address(page); if ((*ppos + count) > CONFIG_DTB_SIZE) write_size = CONFIG_DTB_SIZE - *ppos; @@ -236,9 +371,7 @@ ssize_t mmc_dtb_write(struct file *file, *ppos += write_size; exit: - mmc_release_host(card_dtb->host); - /* kfree(dtb_ptr); */ - vfree(dtb_ptr); + dma_release_from_contiguous(NULL, page, pgcnt); return write_size; } @@ -260,6 +393,9 @@ int amlmmc_dtb_init(struct mmc_card *card) card_dtb = card; pr_info("%s: register dtb chardev", __func__); + + _dtb_init(card); + ret = alloc_chrdev_region(&amlmmc_dtb_no, 0, 1, DTB_NAME); if (ret < 0) { pr_err("alloc dtb dev_t no failed");