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");