343 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
 | |
| 
 | |
| /* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd */
 | |
| 
 | |
| #include <linux/fs.h>
 | |
| #include <linux/kthread.h>
 | |
| #include <linux/miscdevice.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/soc/rockchip/rk_vendor_storage.h>
 | |
| #include <linux/uaccess.h>
 | |
| #include <misc/rkflash_vendor_storage.h>
 | |
| 
 | |
| #include "flash_vendor_storage.h"
 | |
| 
 | |
| #define FLASH_VENDOR_TEST	0
 | |
| #define DRM_DEBUG		1
 | |
| 
 | |
| #if DRM_DEBUG
 | |
| #define DLOG(fmt, args...)	pr_info(fmt, ##args)
 | |
| #else
 | |
| #define DLOG(x...)
 | |
| #endif
 | |
| 
 | |
| #define FLASH_VENDOR_PART_START		8
 | |
| #define FLASH_VENDOR_PART_NUM		4
 | |
| #define FLASH_VENDOR_TAG		VENDOR_HEAD_TAG
 | |
| 
 | |
| static int (*_flash_read)(u32 sec, u32 n_sec, void *p_data);
 | |
| static int (*_flash_write)(u32 sec, u32 n_sec, void *p_data);
 | |
| static struct flash_vendor_info *g_vendor;
 | |
| 
 | |
| int flash_vendor_dev_ops_register(int (*read)(u32 sec,
 | |
| 					      u32 n_sec,
 | |
| 					      void *p_data),
 | |
| 				  int (*write)(u32 sec,
 | |
| 					       u32 n_sec,
 | |
| 					       void *p_data))
 | |
| {
 | |
| 	if (!_flash_read) {
 | |
| 		_flash_read = read;
 | |
| 		_flash_write = write;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static u32 flash_vendor_init(void)
 | |
| {
 | |
| 	u32 i, max_ver, max_index;
 | |
| 
 | |
| 	if (!_flash_read)
 | |
| 		return -EPERM;
 | |
| 
 | |
| 	g_vendor = kmalloc(sizeof(*g_vendor), GFP_KERNEL | GFP_DMA);
 | |
| 	if (!g_vendor)
 | |
| 		return 0;
 | |
| 
 | |
| 	max_ver = 0;
 | |
| 	max_index = 0;
 | |
| 	for (i = 0; i < FLASH_VENDOR_PART_NUM; i++) {
 | |
| 		_flash_read(FLASH_VENDOR_PART_START +
 | |
| 				FLASH_VENDOR_PART_SIZE * i,
 | |
| 				FLASH_VENDOR_PART_SIZE,
 | |
| 				g_vendor);
 | |
| 		if (g_vendor->tag == FLASH_VENDOR_TAG &&
 | |
| 		    g_vendor->version == g_vendor->version2) {
 | |
| 			if (max_ver < g_vendor->version) {
 | |
| 				max_index = i;
 | |
| 				max_ver = g_vendor->version;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	/* DLOG("max_ver = %d\n",max_ver); */
 | |
| 	if (max_ver) {
 | |
| 		_flash_read(FLASH_VENDOR_PART_START +
 | |
| 				FLASH_VENDOR_PART_SIZE * max_index,
 | |
| 				FLASH_VENDOR_PART_SIZE,
 | |
| 		g_vendor);
 | |
| 	} else {
 | |
| 		memset(g_vendor, 0, sizeof(*g_vendor));
 | |
| 		g_vendor->version = 1;
 | |
| 		g_vendor->tag = FLASH_VENDOR_TAG;
 | |
| 		g_vendor->version2 = g_vendor->version;
 | |
| 		g_vendor->free_offset = 0;
 | |
| 		g_vendor->free_size = sizeof(g_vendor->data);
 | |
| 	}
 | |
| 	/* rknand_print_hex("vendor:", g_vendor, 4, 1024); */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int flash_vendor_read(u32 id, void *pbuf, u32 size)
 | |
| {
 | |
| 	u32 i;
 | |
| 
 | |
| 	if (!g_vendor)
 | |
| 		return -1;
 | |
| 
 | |
| 	for (i = 0; i < g_vendor->item_num; i++) {
 | |
| 		if (g_vendor->item[i].id == id) {
 | |
| 			if (size > g_vendor->item[i].size)
 | |
| 				size = g_vendor->item[i].size;
 | |
| 			memcpy(pbuf,
 | |
| 			       &g_vendor->data[g_vendor->item[i].offset],
 | |
| 			       size);
 | |
| 			return size;
 | |
| 		}
 | |
| 	}
 | |
| 	return (-1);
 | |
| }
 | |
| 
 | |
| static int flash_vendor_write(u32 id, void *pbuf, u32 size)
 | |
| {
 | |
| 	u32 i, j, next_index, align_size, alloc_size, item_num;
 | |
| 	u32 offset, next_size;
 | |
| 	u8 *p_data;
 | |
| 	struct vendor_item *item;
 | |
| 	struct vendor_item *next_item;
 | |
| 
 | |
| 	if (!g_vendor)
 | |
| 		return -1;
 | |
| 
 | |
| 	p_data = g_vendor->data;
 | |
| 	item_num = g_vendor->item_num;
 | |
| 	align_size = ALIGN(size, 0x40); /* align to 64 bytes*/
 | |
| 	next_index = g_vendor->next_index;
 | |
| 	for (i = 0; i < item_num; i++) {
 | |
| 		item = &g_vendor->item[i];
 | |
| 		if (item->id == id) {
 | |
| 			alloc_size = ALIGN(item->size, 0x40);
 | |
| 			if (size > alloc_size) {
 | |
| 				if (g_vendor->free_size < align_size)
 | |
| 					return -1;
 | |
| 				offset = item->offset;
 | |
| 				for (j = i; j < item_num - 1; j++) {
 | |
| 					item = &g_vendor->item[j];
 | |
| 					next_item = &g_vendor->item[j + 1];
 | |
| 					item->id = next_item->id;
 | |
| 					item->size = next_item->size;
 | |
| 					item->offset = offset;
 | |
| 					next_size = ALIGN(next_item->size,
 | |
| 							  0x40);
 | |
| 					memcpy(&p_data[offset],
 | |
| 					       &p_data[next_item->offset],
 | |
| 					       next_size);
 | |
| 					offset += next_size;
 | |
| 				}
 | |
| 				item = &g_vendor->item[j];
 | |
| 				item->id = id;
 | |
| 				item->offset = offset;
 | |
| 				item->size = size;
 | |
| 				memcpy(&p_data[item->offset], pbuf, size);
 | |
| 				g_vendor->free_offset = offset + align_size;
 | |
| 				g_vendor->free_size -= (align_size -
 | |
| 							alloc_size);
 | |
| 			} else {
 | |
| 				memcpy(&p_data[item->offset],
 | |
| 				       pbuf,
 | |
| 				       size);
 | |
| 				g_vendor->item[i].size = size;
 | |
| 			}
 | |
| 			g_vendor->version++;
 | |
| 			g_vendor->version2 = g_vendor->version;
 | |
| 			g_vendor->next_index++;
 | |
| 			if (g_vendor->next_index >= FLASH_VENDOR_PART_NUM)
 | |
| 				g_vendor->next_index = 0;
 | |
| 			_flash_write(FLASH_VENDOR_PART_START +
 | |
| 					FLASH_VENDOR_PART_SIZE * next_index,
 | |
| 					FLASH_VENDOR_PART_SIZE,
 | |
| 					g_vendor);
 | |
| 			return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (g_vendor->free_size >= align_size) {
 | |
| 		item = &g_vendor->item[g_vendor->item_num];
 | |
| 		item->id = id;
 | |
| 		item->offset = g_vendor->free_offset;
 | |
| 		item->size = align_size;
 | |
| 		item->size = size;
 | |
| 		g_vendor->free_offset += align_size;
 | |
| 		g_vendor->free_size -= align_size;
 | |
| 		memcpy(&g_vendor->data[item->offset], pbuf, size);
 | |
| 		g_vendor->item_num++;
 | |
| 		g_vendor->version++;
 | |
| 		g_vendor->next_index++;
 | |
| 		g_vendor->version2 = g_vendor->version;
 | |
| 		if (g_vendor->next_index >= FLASH_VENDOR_PART_NUM)
 | |
| 			g_vendor->next_index = 0;
 | |
| 		_flash_write(FLASH_VENDOR_PART_START +
 | |
| 				FLASH_VENDOR_PART_SIZE * next_index,
 | |
| 				FLASH_VENDOR_PART_SIZE,
 | |
| 			g_vendor);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return(-1);
 | |
| }
 | |
| 
 | |
| #if (FLASH_VENDOR_TEST)
 | |
| static void print_hex(char *s, void *buf, int width, int len)
 | |
| {
 | |
| 	print_hex_dump(KERN_WARNING, s, DUMP_PREFIX_OFFSET,
 | |
| 		       16, width, buf, len * width, 0);
 | |
| }
 | |
| 
 | |
| static void flash_vendor_test(void)
 | |
| {
 | |
| 	u32 i;
 | |
| 	u8 test_buf[512];
 | |
| 
 | |
| 	memset(test_buf, 0, 512);
 | |
| 	for (i = 0; i < 62; i++) {
 | |
| 		memset(test_buf, i, i + 1);
 | |
| 		flash_vendor_write(i, test_buf, i + 1);
 | |
| 	}
 | |
| 	memset(test_buf, 0, 512);
 | |
| 	for (i = 0; i < 62; i++) {
 | |
| 		flash_vendor_read(i, test_buf, i + 1);
 | |
| 		DLOG("id = %d ,size = %d\n", i, i + 1);
 | |
| 		print_hex("data:", test_buf, 1, i + 1);
 | |
| 	}
 | |
| 	flash_vendor_init();
 | |
| 	memset(test_buf, 0, 512);
 | |
| 	for (i = 0; i < 62; i++) {
 | |
| 		flash_vendor_read(i, test_buf, i + 1);
 | |
| 		DLOG("id = %d ,size = %d\n", i, i + 1);
 | |
| 		print_hex("data:", test_buf, 1, i + 1);
 | |
| 	}
 | |
| 	while (1)
 | |
| 		;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static long vendor_storage_ioctl(struct file *file,
 | |
| 				 unsigned int cmd,
 | |
| 				 unsigned long arg)
 | |
| {
 | |
| 	long ret = -EINVAL;
 | |
| 	int size;
 | |
| 	u32 *temp_buf;
 | |
| 	struct RK_VENDOR_REQ *req;
 | |
| 
 | |
| 	req = kmalloc(sizeof(*req), GFP_KERNEL);
 | |
| 	if (!req)
 | |
| 		return ret;
 | |
| 
 | |
| 	temp_buf = (u32 *)req;
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 	case VENDOR_READ_IO:
 | |
| 	{
 | |
| 		if (copy_from_user(temp_buf,
 | |
| 				   (void __user *)arg,
 | |
| 				   sizeof(*req))) {
 | |
| 			DLOG("copy_from_user error\n");
 | |
| 			ret = -EFAULT;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (req->tag == VENDOR_REQ_TAG) {
 | |
| 			size = flash_vendor_read(req->id,
 | |
| 						 req->data,
 | |
| 						 req->len);
 | |
| 			if (size > 0) {
 | |
| 				req->len = size;
 | |
| 				ret = 0;
 | |
| 				if (copy_to_user((void __user *)arg,
 | |
| 						 temp_buf,
 | |
| 						 sizeof(*req)))
 | |
| 					ret = -EFAULT;
 | |
| 			}
 | |
| 		}
 | |
| 	} break;
 | |
| 	case VENDOR_WRITE_IO:
 | |
| 	{
 | |
| 		if (copy_from_user(temp_buf,
 | |
| 				   (void __user *)arg,
 | |
| 				   sizeof(struct RK_VENDOR_REQ))) {
 | |
| 			DLOG("copy_from_user error\n");
 | |
| 			ret = -EFAULT;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (req->tag == VENDOR_REQ_TAG)
 | |
| 			ret = flash_vendor_write(req->id,
 | |
| 						 req->data,
 | |
| 						 req->len);
 | |
| 	} break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	kfree(temp_buf);
 | |
| 	DLOG("flash_vendor_ioctl cmd=%x ret = %lx\n", cmd, ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static const struct file_operations vendor_storage_fops = {
 | |
| 	.compat_ioctl	= vendor_storage_ioctl,
 | |
| 	.unlocked_ioctl = vendor_storage_ioctl,
 | |
| };
 | |
| 
 | |
| static struct miscdevice vender_storage_dev = {
 | |
| 	.minor = MISC_DYNAMIC_MINOR,
 | |
| 	.name  = "vendor_storage",
 | |
| 	.fops  = &vendor_storage_fops,
 | |
| };
 | |
| 
 | |
| static int vendor_init_thread(void *arg)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	pr_info("flash %s!\n", __func__);
 | |
| 	ret = flash_vendor_init();
 | |
| 	if (!ret) {
 | |
| 		ret = misc_register(&vender_storage_dev);
 | |
| 		#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE
 | |
| 		rk_vendor_register(flash_vendor_read, flash_vendor_write);
 | |
| 		#endif
 | |
| 	}
 | |
| 	pr_info("flash vendor storage:20170308 ret = %d\n", ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int __init vendor_storage_init(void)
 | |
| {
 | |
| 	kthread_run(vendor_init_thread, (void *)NULL, "vendor_storage_init");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static __exit void vendor_storage_deinit(void)
 | |
| {
 | |
| 	if (g_vendor) {
 | |
| 		misc_deregister(&vender_storage_dev);
 | |
| 		kfree(g_vendor);
 | |
| 		g_vendor = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| device_initcall_sync(vendor_storage_init);
 | |
| module_exit(vendor_storage_deinit);
 | |
| MODULE_LICENSE("GPL");
 |