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