emmc: fix dtb backup mechanism
authorRuixuan Li <ruixuan.li@amlogic.com>
Wed, 21 Mar 2018 09:24:25 +0000 (17:24 +0800)
committerYixun Lan <yixun.lan@amlogic.com>
Thu, 22 Mar 2018 07:08:36 +0000 (23:08 -0800)
PD#162119: emmc: fix dtb backup mechanism

Change-Id: I32ca6ffb2c01bb5f50a5af3f0b2942077da61eb8
Signed-off-by: Ruixuan Li <ruixuan.li@amlogic.com>
drivers/amlogic/mmc/emmc_partitions.c

index 10552aa..0f9ab3a 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/uaccess.h>
-#include <linux/vmalloc.h>
+#include <linux/dma-contiguous.h>
 
 #include <linux/mmc/emmc_partitions.h>
 #include <linux/amlogic/cpu_version.h>
@@ -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");