1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3 * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
4 */
5
6 #include <linux/delay.h>
7 #include <linux/gpio.h>
8 #include <linux/miscdevice.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/of_gpio.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
14 #include <linux/spi/spi.h>
15
16 #define SPI_OBJ_MAX_XFER_SIZE 0x1040
17 #define SPI_OBJ_APP_RAM_SIZE 0x10000
18
19 #define SPI_OBJ_CTRL_MSG_SIZE 0x8
20 #define SPI_OBJ_CTRL_CMD_INIT 0x99
21 #define SPI_OBJ_CTRL_CMD_READ 0x3A
22 #define SPI_OBJ_CTRL_CMD_WRITE 0x4B
23 #define SPI_OBJ_CTRL_CMD_DUPLEX 0x5C
24
25 struct spi_obj_ctrl {
26 u16 cmd;
27 u16 addr;
28 u32 data;
29 };
30
31 struct spidev_rkslv_data {
32 struct device *dev;
33 struct spi_device *spi;
34 char *ctrlbuf;
35 char *appmem;
36 char *tempbuf;
37 bool verbose;
38 struct task_struct *tsk;
39 bool tsk_run;
40 struct miscdevice misc_dev;
41 };
42
43 static u32 bit_per_word = 8;
44
spidev_slv_write(struct spidev_rkslv_data * spidev,const void * txbuf,size_t n)45 static int spidev_slv_write(struct spidev_rkslv_data *spidev, const void *txbuf, size_t n)
46 {
47 int ret = -1;
48 struct spi_device *spi = spidev->spi;
49 struct spi_transfer t = {
50 .tx_buf = txbuf,
51 .len = n,
52 .bits_per_word = bit_per_word,
53 };
54 struct spi_message m;
55
56 spi_message_init(&m);
57 spi_message_add_tail(&t, &m);
58 ret = spi_sync(spi, &m);
59
60 return ret;
61 }
62
spidev_slv_read(struct spidev_rkslv_data * spidev,void * rxbuf,size_t n)63 static int spidev_slv_read(struct spidev_rkslv_data *spidev, void *rxbuf, size_t n)
64 {
65 int ret = -1;
66 struct spi_device *spi = spidev->spi;
67 struct spi_transfer t = {
68 .rx_buf = rxbuf,
69 .len = n,
70 .bits_per_word = bit_per_word,
71 };
72 struct spi_message m;
73
74 spi_message_init(&m);
75 spi_message_add_tail(&t, &m);
76 ret = spi_sync(spi, &m);
77
78 return ret;
79 }
80
spidev_slv_write_and_read(struct spidev_rkslv_data * spidev,const void * tx_buf,void * rx_buf,size_t len)81 static int spidev_slv_write_and_read(struct spidev_rkslv_data *spidev, const void *tx_buf,
82 void *rx_buf, size_t len)
83 {
84 struct spi_device *spi = spidev->spi;
85 struct spi_transfer t = {
86 .tx_buf = tx_buf,
87 .rx_buf = rx_buf,
88 .len = len,
89 };
90 struct spi_message m;
91
92 spi_message_init(&m);
93 spi_message_add_tail(&t, &m);
94 return spi_sync(spi, &m);
95 }
96
spidev_rkslv_misc_write(struct file * filp,const char __user * buf,size_t n,loff_t * offset)97 static ssize_t spidev_rkslv_misc_write(struct file *filp, const char __user *buf,
98 size_t n, loff_t *offset)
99 {
100 struct spidev_rkslv_data *spidev;
101 struct spi_device *spi;
102 int argc = 0;
103 char tmp[64];
104 char *argv[16];
105 char *cmd, *data;
106
107 if (n >= 64)
108 return -EINVAL;
109
110 spidev = filp->private_data;
111
112 if (!spidev)
113 return -ESHUTDOWN;
114
115 spi = spidev->spi;
116 memset(tmp, 0, sizeof(tmp));
117 if (copy_from_user(tmp, buf, n))
118 return -EFAULT;
119 cmd = tmp;
120 data = tmp;
121
122 while (data < (tmp + n)) {
123 data = strstr(data, " ");
124 if (!data)
125 break;
126 *data = 0;
127 argv[argc] = ++data;
128 argc++;
129 if (argc >= 16)
130 break;
131 }
132
133 tmp[n - 1] = 0;
134
135 if (!strcmp(cmd, "verbose")) {
136 int val;
137
138 if (argc < 1)
139 return -EINVAL;
140
141 if (kstrtoint(argv[0], 0, &val))
142 return -EINVAL;
143
144 if (val == 1)
145 spidev->verbose = true;
146 else
147 spidev->verbose = false;
148 } else if (!strcmp(cmd, "appmem")) {
149 int addr, len;
150
151 if (argc < 2)
152 return -EINVAL;
153
154 if (kstrtoint(argv[0], 0, &addr))
155 return -EINVAL;
156 if (kstrtoint(argv[1], 0, &len))
157 return -EINVAL;
158
159 if (!len) {
160 dev_err(&spi->dev, "param invalid,%s %s\n", argv[0], argv[1]);
161 return -EINVAL;
162 }
163
164 if (addr + len > SPI_OBJ_APP_RAM_SIZE) {
165 dev_err(&spi->dev, "appmem print out of size\n");
166 return -EINVAL;
167 }
168
169 print_hex_dump(KERN_ERR, "APPMEM: ",
170 DUMP_PREFIX_OFFSET,
171 16,
172 1,
173 spidev->appmem + addr,
174 len,
175 1);
176 } else {
177 dev_err(&spi->dev, "unknown command\n");
178 }
179
180 return n;
181 }
182
spidev_rkslv_misc_open(struct inode * inode,struct file * filp)183 static int spidev_rkslv_misc_open(struct inode *inode, struct file *filp)
184 {
185 struct miscdevice *miscdev = filp->private_data;
186 struct spidev_rkslv_data *spidev;
187
188 spidev = container_of(miscdev, struct spidev_rkslv_data, misc_dev);
189 filp->private_data = spidev;
190
191 return 0;
192 }
193
194 static const struct file_operations spidev_rkslv_misc_fops = {
195 .write = spidev_rkslv_misc_write,
196 .open = spidev_rkslv_misc_open,
197 };
198
spidev_rkslv_xfer(struct spidev_rkslv_data * spidev)199 static int spidev_rkslv_xfer(struct spidev_rkslv_data *spidev)
200 {
201 char *ctrlbuf = spidev->ctrlbuf, *appmem = spidev->appmem, *tempbuf = spidev->tempbuf;
202 struct spi_obj_ctrl *ctrl;
203 struct spi_device *spi = spidev->spi;
204 u32 len;
205 int ret;
206
207 memset(spidev->ctrlbuf, 0, SPI_OBJ_CTRL_MSG_SIZE);
208 ret = spidev_slv_read(spidev, spidev->ctrlbuf, SPI_OBJ_CTRL_MSG_SIZE);
209 if (ret) {
210 dev_err(&spi->dev, "%s ctrl\n", __func__);
211 return -EIO;
212 }
213
214 ctrl = (struct spi_obj_ctrl *)ctrlbuf;
215 if (spidev->verbose)
216 dev_err(&spi->dev, "ctrl cmd=%x addr=0x%x data=0x%x\n",
217 ctrl->cmd, ctrl->addr, ctrl->data);
218
219 switch (ctrl->cmd) {
220 case SPI_OBJ_CTRL_CMD_INIT:
221 return 0;
222 case SPI_OBJ_CTRL_CMD_READ:
223 len = ctrl->data;
224 ret = spidev_slv_write(spidev, appmem + ctrl->addr, len);
225 if (ret) {
226 dev_err(&spi->dev, "%s cmd=%x addr=0x%x data=0x%x\n",
227 __func__, ctrl->cmd, ctrl->addr, ctrl->data);
228 return -EIO;
229 }
230 break;
231 case SPI_OBJ_CTRL_CMD_WRITE:
232 len = ctrl->data;
233 ret = spidev_slv_read(spidev, appmem + ctrl->addr, len);
234 if (ret) {
235 dev_err(&spi->dev, "%s cmd=%x addr=0x%x data=0x%x\n",
236 __func__, ctrl->cmd, ctrl->addr, ctrl->data);
237 return -EIO;
238 }
239 if (spidev->verbose) {
240 print_hex_dump(KERN_ERR, "s-r: ",
241 DUMP_PREFIX_OFFSET,
242 16,
243 1,
244 appmem + ctrl->addr,
245 len,
246 1);
247 }
248 break;
249 case SPI_OBJ_CTRL_CMD_DUPLEX:
250 len = ctrl->data;
251 ret = spidev_slv_write_and_read(spidev, appmem + ctrl->addr, tempbuf, len);
252 if (ret) {
253 dev_err(&spi->dev, "%s cmd=%x addr=0x%x data=0x%x\n",
254 __func__, ctrl->cmd, ctrl->addr, ctrl->data);
255 return -EIO;
256 }
257 if (spidev->verbose) {
258 print_hex_dump(KERN_ERR, "s-d-t: ",
259 DUMP_PREFIX_OFFSET,
260 16,
261 1,
262 appmem + ctrl->addr,
263 len,
264 1);
265 print_hex_dump(KERN_ERR, "s-d-r: ",
266 DUMP_PREFIX_OFFSET,
267 16,
268 1,
269 tempbuf,
270 len,
271 1);
272 }
273 memcpy(appmem + ctrl->addr, tempbuf, len);
274 break;
275 default:
276 if (spidev->verbose)
277 dev_err(&spi->dev, "%s unknown\n", __func__);
278 return 0;
279 }
280
281 if (spidev->verbose)
282 dev_err(&spi->dev, "xfer len=0x%x\n", ctrl->data);
283
284 return 0;
285 }
286
spidev_rkslv_ctrl_receiver_thread(void * p)287 static int spidev_rkslv_ctrl_receiver_thread(void *p)
288 {
289 struct spidev_rkslv_data *spidev = (struct spidev_rkslv_data *)p;
290
291 while (spidev->tsk_run)
292 spidev_rkslv_xfer(spidev);
293
294 return 0;
295 }
296
spidev_rkslv_probe(struct spi_device * spi)297 static int spidev_rkslv_probe(struct spi_device *spi)
298 {
299 struct spidev_rkslv_data *spidev = NULL;
300 int ret;
301
302 if (!spi)
303 return -ENOMEM;
304
305 spidev = devm_kzalloc(&spi->dev, sizeof(struct spidev_rkslv_data), GFP_KERNEL);
306 if (!spidev)
307 return -ENOMEM;
308
309 spidev->ctrlbuf = devm_kzalloc(&spi->dev, SPI_OBJ_MAX_XFER_SIZE, GFP_KERNEL);
310 if (!spidev->ctrlbuf)
311 return -ENOMEM;
312
313 spidev->appmem = devm_kzalloc(&spi->dev, SPI_OBJ_APP_RAM_SIZE, GFP_KERNEL | GFP_DMA);
314 if (!spidev->appmem)
315 return -ENOMEM;
316
317 spidev->tempbuf = devm_kzalloc(&spi->dev, SPI_OBJ_MAX_XFER_SIZE, GFP_KERNEL);
318 if (!spidev->tempbuf)
319 return -ENOMEM;
320
321 spidev->spi = spi;
322 spidev->dev = &spi->dev;
323 dev_set_drvdata(&spi->dev, spidev);
324
325 dev_err(&spi->dev, "mode=%d, max_speed_hz=%d\n", spi->mode, spi->max_speed_hz);
326
327 spidev->misc_dev.minor = MISC_DYNAMIC_MINOR;
328 spidev->misc_dev.name = "spidev_rkslv_misc";
329 spidev->misc_dev.fops = &spidev_rkslv_misc_fops;
330 spidev->misc_dev.parent = &spi->dev;
331 ret = misc_register(&spidev->misc_dev);
332 if (ret) {
333 dev_err(&spi->dev, "fail to register misc device\n");
334 return ret;
335 }
336
337 spidev->tsk_run = true;
338 spidev->tsk = kthread_run(spidev_rkslv_ctrl_receiver_thread, spidev, "spidev-rkslv");
339 if (IS_ERR(spidev->tsk)) {
340 dev_err(&spi->dev, "start spidev-rkslv thread failed\n");
341 return PTR_ERR(spidev->tsk);
342 }
343
344 return 0;
345 }
346
spidev_rkslv_remove(struct spi_device * spi)347 static int spidev_rkslv_remove(struct spi_device *spi)
348 {
349 struct spidev_rkslv_data *spidev = dev_get_drvdata(&spi->dev);
350
351 spidev->tsk_run = false;
352 spi_slave_abort(spi);
353 kthread_stop(spidev->tsk);
354 misc_deregister(&spidev->misc_dev);
355
356 return 0;
357 }
358
359 #ifdef CONFIG_OF
360 static const struct of_device_id spidev_rkslv_dt_match[] = {
361 { .compatible = "rockchip,spi-obj-slave", },
362 {},
363 };
364 MODULE_DEVICE_TABLE(of, spidev_rkslv_dt_match);
365
366 #endif /* CONFIG_OF */
367
368 static struct spi_driver spidev_rkmst_driver = {
369 .driver = {
370 .name = "spidev_rkslv",
371 .owner = THIS_MODULE,
372 .of_match_table = of_match_ptr(spidev_rkslv_dt_match),
373 },
374 .probe = spidev_rkslv_probe,
375 .remove = spidev_rkslv_remove,
376 };
377 module_spi_driver(spidev_rkmst_driver);
378
379 MODULE_AUTHOR("Jon Lin <jon.lin@rock-chips.com>");
380 MODULE_DESCRIPTION("ROCKCHIP SPI Object Slave Driver");
381 MODULE_LICENSE("GPL");
382 MODULE_ALIAS("spi:spidev_rkslv");
383