xref: /OK3568_Linux_fs/kernel/drivers/soc/rockchip/ram_vendor_storage.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 
3 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd */
4 
5 #include <linux/kernel.h>
6 #include <linux/debugfs.h>
7 #include <linux/dma-mapping.h>
8 #include <linux/fs.h>
9 #include <linux/file.h>
10 #include <linux/list.h>
11 #include <linux/io.h>
12 #include <linux/mempolicy.h>
13 #include <linux/miscdevice.h>
14 #include <linux/mm.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/of_address.h>
18 #include <linux/of_device.h>
19 #include <linux/of_platform.h>
20 #include <linux/of_reserved_mem.h>
21 #include <linux/platform_device.h>
22 #include <linux/sched.h>
23 #include <linux/soc/rockchip/rk_vendor_storage.h>
24 #include <linux/uaccess.h>
25 #include <misc/rkflash_vendor_storage.h>
26 
27 static struct vendor_info *g_vendor;
28 
ram_vendor_read(u32 id,void * pbuf,u32 size)29 static int ram_vendor_read(u32 id, void *pbuf, u32 size)
30 {
31 	u32 i;
32 
33 	if (!g_vendor)
34 		return -ENOMEM;
35 
36 	for (i = 0; i < g_vendor->item_num; i++) {
37 		if (g_vendor->item[i].id == id) {
38 			if (size > g_vendor->item[i].size)
39 				size = g_vendor->item[i].size;
40 			memcpy(pbuf, &g_vendor->data[g_vendor->item[i].offset], size);
41 			return size;
42 		}
43 	}
44 
45 	return (-1);
46 }
47 
ram_vendor_storage_open(struct inode * inode,struct file * file)48 static int ram_vendor_storage_open(struct inode *inode, struct file *file)
49 {
50 	return 0;
51 }
52 
ram_vendor_storage_release(struct inode * inode,struct file * file)53 static int ram_vendor_storage_release(struct inode *inode, struct file *file)
54 {
55 	return 0;
56 }
57 
ram_vendor_storage_ioctl(struct file * file,unsigned int cmd,unsigned long arg)58 static long ram_vendor_storage_ioctl(struct file *file, unsigned int cmd,
59 				 unsigned long arg)
60 {
61 	long ret = -1;
62 	int size;
63 	struct RK_VENDOR_REQ *v_req;
64 	u32 *page_buf;
65 
66 	page_buf = kmalloc(4096, GFP_KERNEL);
67 	if (!page_buf)
68 		return -ENOMEM;
69 
70 	v_req = (struct RK_VENDOR_REQ *)page_buf;
71 
72 	switch (cmd) {
73 	case VENDOR_READ_IO:
74 	{
75 		if (copy_from_user(page_buf, (void __user *)arg, 8)) {
76 			ret = -EFAULT;
77 			break;
78 		}
79 		if (v_req->tag == VENDOR_REQ_TAG && v_req->len <= 4096 - 8) {
80 			size = ram_vendor_read(v_req->id, v_req->data, v_req->len);
81 			if (size != -1) {
82 				v_req->len = size;
83 				ret = 0;
84 				if (copy_to_user((void __user *)arg,
85 						 page_buf,
86 						 v_req->len + 8))
87 					ret = -EFAULT;
88 			}
89 		}
90 	} break;
91 
92 	case VENDOR_WRITE_IO:
93 	default:
94 		ret = -EINVAL;
95 		goto exit;
96 	}
97 exit:
98 	kfree(page_buf);
99 	return ret;
100 }
101 
102 static const struct file_operations vendor_storage_fops = {
103 	.open = ram_vendor_storage_open,
104 	.compat_ioctl = ram_vendor_storage_ioctl,
105 	.unlocked_ioctl = ram_vendor_storage_ioctl,
106 	.release = ram_vendor_storage_release,
107 };
108 
109 static struct miscdevice vender_storage_dev = {
110 	.minor = MISC_DYNAMIC_MINOR,
111 	.name  = "vendor_storage",
112 	.fops  = &vendor_storage_fops,
113 };
114 
ram_vendor_stroage_map(phys_addr_t start,size_t len)115 static void *ram_vendor_stroage_map(phys_addr_t start, size_t len)
116 {
117 	int i;
118 	void *vaddr;
119 	pgprot_t pgprot = PAGE_KERNEL;
120 	phys_addr_t phys;
121 	int npages = PAGE_ALIGN(len) / PAGE_SIZE;
122 	struct page **p = vmalloc(sizeof(struct page *) * npages);
123 
124 	if (!p)
125 		return NULL;
126 
127 	phys = start;
128 	for (i = 0; i < npages; i++) {
129 		p[i] = phys_to_page(phys);
130 		phys += PAGE_SIZE;
131 	}
132 
133 	vaddr = vmap(p, npages, VM_MAP, pgprot);
134 	vfree(p);
135 
136 	return vaddr;
137 }
138 
ram_vendor_storage_probe(struct platform_device * pdev)139 static int ram_vendor_storage_probe(struct platform_device *pdev)
140 {
141 	struct device_node *np = pdev->dev.of_node;
142 	struct device_node *node;
143 	struct resource res;
144 	int ret;
145 	phys_addr_t size, start;
146 
147 	if (g_vendor)
148 		return -EINVAL;
149 
150 	node = of_parse_phandle(np, "memory-region", 0);
151 	if (!node)
152 		return -ENOMEM;
153 
154 	ret = of_address_to_resource(node, 0, &res);
155 	if (ret)
156 		return ret;
157 
158 	ret = -EINVAL;
159 
160 	size = resource_size(&res);
161 	start = res.start;
162 	if (size != VENDOR_PART_SIZE << 9 || (start & (PAGE_SIZE - 1)))
163 		goto un_reserved;
164 
165 	g_vendor = ram_vendor_stroage_map(start, size);
166 	if (IS_ERR(g_vendor))
167 		goto un_reserved;
168 
169 	if (g_vendor->tag != VENDOR_HEAD_TAG)
170 		goto un_remap;
171 
172 	misc_register(&vender_storage_dev);
173 	rk_vendor_register(ram_vendor_read, NULL);
174 
175 	return 0;
176 
177 un_remap:
178 	vunmap(g_vendor);
179 un_reserved:
180 #ifndef MODULE
181 	free_reserved_area(phys_to_virt(start), phys_to_virt(start) + size, -1, "memory-region");
182 #endif
183 	g_vendor = NULL;
184 
185 	return ret;
186 }
187 
ram_vendor_storage_remove(struct platform_device * pdev)188 static int ram_vendor_storage_remove(struct platform_device *pdev)
189 {
190 	if (g_vendor) {
191 		misc_deregister(&vender_storage_dev);
192 		vunmap(g_vendor);
193 		g_vendor = NULL;
194 	}
195 
196 	return 0;
197 }
198 
199 static const struct of_device_id dt_match[] = {
200 	{ .compatible = "rockchip,ram-vendor-storage" },
201 	{}
202 };
203 
204 static struct platform_driver vendor_storage_driver = {
205 	.probe		= ram_vendor_storage_probe,
206 	.remove		= ram_vendor_storage_remove,
207 	.driver		= {
208 		.name		= "vendor-storage",
209 		.of_match_table	= dt_match,
210 	},
211 };
212 
213 module_platform_driver(vendor_storage_driver);
214 MODULE_LICENSE("GPL");
215