#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>
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)
{
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;
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,
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;
}
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;
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;
*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;
}
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");