xref: /OK3568_Linux_fs/kernel/drivers/spi/spidev-rkslv.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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