blob: 6a1334c0fe9e6e98efa3644ef0753051a199c478 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * Marvell sulog driver
3 *
4 * Tzviel Lemberger <tzviel@marvell.com>
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 * (c) 2010
10 *
11 */
12
13#include <linux/kernel.h>
14#include <linux/slab.h>
15#include <linux/module.h>
16#include <linux/device.h>
17#include <linux/interrupt.h>
18#include <linux/irq.h>
19#include <linux/miscdevice.h>
20#include <linux/errno.h>
21#include <linux/fs.h>
22#include <linux/of.h>
23#include <linux/platform_device.h>
24#include <linux/sched.h>
25#include <linux/wait.h>
26#include <linux/io.h>
27#include <linux/uaccess.h>
28#include <linux/vmalloc.h>
29#include <linux/workqueue.h>
30#include <asm/io.h>
31
32
33#include <linux/debugfs.h>
34#include <linux/usb.h>
35#include <linux/timer.h>
36
37/* TODO: find a proper solution to the 2 functions included from pxa182x_serial_debug.h */
38extern struct debug_serial_direct_callbacks *pxa_register_debug_serial_streaming(void);
39extern void pxa_unregister_debug_serial_streaming(void);
40extern struct debug_serial_direct_callbacks *pxa_register_diag_serial_streaming(void);
41extern void pxa_unregister_diag_serial_streaming(void);
42
43//#include "pxa182x_serial_debug.h"
44
45/*
46 * NOTE: send_buf_func called from an atomic context (timer callback).
47 * You are responsible to check wether the send_buf_func can be called
48 * from atomic context.
49 */
50static int (*send_buf_func)(const unsigned char *buf, int count);
51
52/* Sulog macros */
53#define PR_ERR(fmt, ...) \
54 pr_err("misc sulog: %s(%d): " \
55 fmt, __func__, __LINE__ , ##__VA_ARGS__)
56
57#define DEV_ERR(dev, fmt, arg...) \
58 pr_err("%s %s: %s(%d): " fmt, dev_driver_string(dev), \
59 dev_name(dev), __func__, __LINE__, ## arg)
60#define DEV_INFO(dev, fmt, arg...) \
61 pr_info("%s %s: %s(%d): " fmt, dev_driver_string(dev), \
62 dev_name(dev), __func__, __LINE__, ## arg)
63/*#define SULOG_DBG 1*/
64#ifdef SULOG_DBG
65#define DEV_DBG(dev, fmt, arg...) \
66 pr_debug("%s %s: %s(%d): " fmt, dev_driver_string(dev), \
67 dev_name(dev), __func__, __LINE__, ## arg)
68#else
69#define DEV_DBG(dev, fmt, arg...)
70#endif
71
72#define SULOG_ENABLE 1
73#define SULOG_ENABLE2 2 /* sulog thru diag port */
74#define SULOG_DISABLE 0
75/*Default AP DDR memory allocation */
76#define AP_DDR_DEFAULT_SIZE 0x7f000
77#define NUM_OF_BUFFERS 2
78/*Default high water mark level */
79#define AP_HIGHWATERMARK 0x1000
80/*Comm maximum DDR size*/
81#define CP_MAX_DDR_SIZE 0x100000
82
83/*Register offsets*/
84#define RIPC_STATUS_REG 0x0
85#define RIPC_APPS_INT_REG 0x4
86#define RIPC_SG_INT_REG 0x8
87#define RIPC_GB_INT_REG 0xC
88#define RIPC_APPS_INFO_REG 0x14
89#define RIPC_SG_INFO_REG 0x18
90#define RIPC_GB_INFO_REG 0x1C
91/*Two base addresses for RIPC - registers and Clk*/
92#define RIPC_BASE_ADDRESSES 0x2
93#define RIPC_APPS_CLK_REG 0x0
94
95#define RIPC_CLR_INTRRUPT_BIT 0x1
96
97#define TARGET_SD 0x0
98#define TARGET_USB 0x1
99
100/* Used to ioremap large enough space
101 in case of address outside of expected CP memory */
102#define COMM_DSP_MAX_BUFFER_SIZE 0x200000
103/* Used to test if requseted buffer size is not too large */
104#define COMM_DSP_DOUBLE_BUFFER_SIZE 0x100000
105
106/*Static variables*/
107static struct sulog_struct *sulog_str;
108static int sulog_thru_diag;
109volatile static int glb_cnt;
110/*General Structures*/
111struct sulog_resource {
112 unsigned int *ripc_base_address;
113 unsigned char *comm_strt_addr;
114 unsigned int comm_addr_offst;
115 unsigned char *comm_base_addr;
116 unsigned int ripc_size;
117 int sulog_irq_resource;
118 char sulog_irq_name[20];
119 struct resource *resource;
120 unsigned int remapped;
121};
122
123struct sulog_app_mem {
124 unsigned int *sulog_app_ptr;
125 unsigned int *sulog_app_buf[NUM_OF_BUFFERS];
126 unsigned int sulog_allocbuf_size;
127 unsigned int app_buff_size;
128 unsigned int high_watermark;
129 unsigned int sulog_target_type;
130};
131
132struct sulog_statistic {
133 unsigned int irq_count;
134 unsigned int tot_data_sum;
135 unsigned int packets_pass;
136 unsigned int packets_dropped;
137};
138
139struct sulog_struct {
140 struct device *dev;
141 struct sulog_resource res[RIPC_BASE_ADDRESSES];
142 struct sulog_app_mem app_mem;
143 unsigned int sulog_driver_status;
144 struct tasklet_struct sulog_copy_tasklet;
145 struct workqueue_struct *sulog_workqueue_ptr;
146 struct work_struct sulog_workstruct;
147 struct sulog_statistic sulog_statistic;
148 wait_queue_head_t read_queue;
149};
150
151/*Function declerations*/
152static void sulog_comm_map(struct work_struct *work);
153static void sulog_copy_tasklet(unsigned long data);
154static int sulog_driver_enable(void);
155static int sulog_driver_disable(void);
156static int sulog_change_status(unsigned int input);
157static ssize_t sulog_driver_show(struct device *dev,
158 struct device_attribute *attr,
159 char *buf);
160static ssize_t sulog_driver_store(struct device *dev,
161 struct device_attribute *attr,
162 const char *buff, size_t count);
163static ssize_t sulog_watermark_show(struct device *dev,
164 struct device_attribute *attr,
165 char *buf);
166static ssize_t sulog_watermark_store(struct device *dev,
167 struct device_attribute *attr,
168 const char *buff, size_t count);
169static ssize_t sulog_buff_size_show(struct device *dev,
170 struct device_attribute *attr,
171 char *buf);
172static ssize_t sulog_buff_size_store(struct device *dev,
173 struct device_attribute *attr,
174 const char *buff, size_t count);
175static ssize_t sulog_target_type_show(struct device *dev,
176 struct device_attribute *attr,
177 char *buf);
178static ssize_t sulog_target_type_store(struct device *dev,
179 struct device_attribute *attr,
180 const char *buff, size_t count);
181static ssize_t sulog_statistics_show(struct device *dev,
182 struct device_attribute *attr,
183 char *buf);
184static ssize_t sulog_statistics_store(struct device *dev,
185 struct device_attribute *attr,
186 const char *buff, size_t count);
187
188static irqreturn_t sulog_irq_handler(int this_irq, void *dev_id);
189static int sulog_open(struct inode *inode, struct file *file);
190static ssize_t sulog_read(struct file *filp, char __user *buf,
191 size_t count, loff_t *unused);
192
193/*Static structures*/
194static const struct file_operations sulog_fops = {
195 .owner = THIS_MODULE,
196 .open = sulog_open,
197 .read = sulog_read,
198};
199
200static struct miscdevice sulog_miscdev = {
201 .minor = MISC_DYNAMIC_MINOR,
202 .name = "sulog_driver",
203 .fops = &sulog_fops,
204};
205
206static DEVICE_ATTR(status, 0644, sulog_driver_show, sulog_driver_store);
207static DEVICE_ATTR(watermark, 0644, sulog_watermark_show,
208 sulog_watermark_store);
209static DEVICE_ATTR(buff_size, 0644, sulog_buff_size_show,
210 sulog_buff_size_store);
211static DEVICE_ATTR(target_type, 0644, sulog_target_type_show,
212 sulog_target_type_store);
213static DEVICE_ATTR(statistics, 0644, sulog_statistics_show,
214 sulog_statistics_store);
215
216static const struct device_attribute *dev_attr_arr[] = {
217 &dev_attr_status,
218 &dev_attr_watermark,
219 &dev_attr_buff_size,
220 &dev_attr_target_type,
221 &dev_attr_statistics,
222};
223
224/*Static functions*/
225static void sulog_comm_map(struct work_struct *work)
226{
227 /* The workqueue is responsible to deliver the tasklet with a valid address to work with.
228 * if physical address is inside kernel memory - translate it to a virtual address and continue.
229 * if physical address is outside of kernel memory - remap it first. */
230 if (sulog_str->res[0].comm_strt_addr && sulog_str->res[0].remapped)
231 iounmap(sulog_str->res[0].comm_strt_addr);
232
233 if (pfn_valid(__phys_to_pfn((unsigned int)sulog_str->res[0].comm_base_addr))) {
234 /* Physical address inside kernel memory */
235 sulog_str->res[0].remapped = 0;
236 sulog_str->res[0].comm_strt_addr =
237 phys_to_virt((unsigned int)sulog_str->res[0].comm_base_addr);
238 } else {
239 /* Physical address outside of kernel memory */
240 sulog_str->res[0].remapped = 1;
241 sulog_str->res[0].comm_strt_addr =
242 ioremap((unsigned int)sulog_str->res[0].comm_base_addr,
243 COMM_DSP_MAX_BUFFER_SIZE);
244 }
245
246 DEV_DBG(sulog_str->dev, "Comm memory remapped, triggering tasklet\n");
247 tasklet_schedule(&sulog_str->sulog_copy_tasklet);
248 return;
249}
250
251static void sulog_copy_tasklet(unsigned long data)
252{
253 unsigned int data_size;
254 unsigned char *resource_p;
255
256
257 DEV_DBG(sulog_str->dev,
258 "Start copying information from SHMEM to Ap DDR\n");
259 data_size = (unsigned int) sulog_str->res[0].ripc_size;
260 resource_p = (sulog_str->res[0].comm_strt_addr +
261 sulog_str->res[0].comm_addr_offst);
262
263 if (sulog_str->app_mem.sulog_target_type == TARGET_USB) {
264 /*Output is usb: Copying data from SHMEM to kernel memory */
265 if (!send_buf_func(resource_p, data_size))
266 sulog_str->sulog_statistic.packets_pass++;
267 else
268 sulog_str->sulog_statistic.packets_dropped++;
269 } else { /* TARGET SD */
270 /* Output is sdcard: Copying data from SHMEM to AP DDR*/
271 if (unlikely
272 ((sulog_str->app_mem.app_buff_size + data_size) >
273 sulog_str->app_mem.sulog_allocbuf_size)) {
274 /*We will exceed the maximum allocation size */
275 DEV_INFO(sulog_str->dev,
276 "Data exceed maximum size, possible data lost\n");
277 /*Allocate all remaining space for copy */
278 data_size =
279 sulog_str->app_mem.sulog_allocbuf_size -
280 sulog_str->app_mem.app_buff_size;
281 /* We missed some data. mark as lost packet */
282 sulog_str->sulog_statistic.packets_dropped++;
283 }
284 memcpy((unsigned char *)
285 ((unsigned int)sulog_str->app_mem.sulog_app_ptr +
286 (unsigned int)sulog_str->app_mem.app_buff_size),
287 resource_p,
288 data_size);
289 sulog_str->app_mem.app_buff_size += data_size;
290 if (sulog_str->app_mem.app_buff_size >
291 sulog_str->app_mem.high_watermark) {
292 /*Allow user space to access the data */
293 disable_irq(sulog_str->res[0].sulog_irq_resource);
294 DEV_DBG(sulog_str->dev, "waking up read to user space\n\n");
295 wake_up(&sulog_str->read_queue);
296 }
297 }
298 return;
299}
300
301static int sulog_driver_enable(void)
302{
303 int i;
304
305 struct debug_serial_direct_callbacks *callback_ptr;
306 if (sulog_str->app_mem.sulog_target_type == TARGET_SD) {
307 /*We'll allocate double buffer*/
308 for (i = 0; i < NUM_OF_BUFFERS; i++) {
309 sulog_str->app_mem.sulog_app_buf[i] =
310 vmalloc((unsigned long)
311 sulog_str->app_mem.sulog_allocbuf_size);
312 if (sulog_str->app_mem.sulog_app_buf[i] == NULL) {
313 PR_ERR("Failed to allocate memory!\n");
314 return -ENOMEM;
315 }
316 }
317 sulog_str->app_mem.sulog_app_ptr =
318 sulog_str->app_mem.sulog_app_buf[glb_cnt];
319 }
320
321 /*Set RIPC clk to 1 - JIRA NEZHA3-90*/
322 __raw_writel(0x1, sulog_str->res[1].ripc_base_address);
323 if (sulog_str->app_mem.sulog_target_type == TARGET_USB) {
324 if (sulog_thru_diag) {
325 pr_info("Sulog is sent through diag port!\n");
326 callback_ptr = pxa_register_diag_serial_streaming();
327 } else {
328 callback_ptr = pxa_register_debug_serial_streaming();
329 }
330
331 if (!callback_ptr) {
332 pr_err("callback function pointer is %p\n", callback_ptr);
333 return 0;
334 }
335 send_buf_func = callback_ptr->send_buff_cb;
336 if (!send_buf_func)
337 return 0;
338 }
339
340 /* only enable tasklet when it's not enabled */
341 if (atomic_read(&sulog_str->sulog_copy_tasklet.count))
342 tasklet_enable(&sulog_str->sulog_copy_tasklet);
343
344 /*Init statistic values*/
345 sulog_str->sulog_statistic.irq_count = 0;
346 sulog_str->sulog_statistic.packets_dropped = 0;
347 sulog_str->sulog_statistic.packets_pass = 0;
348 sulog_str->sulog_statistic.tot_data_sum = 0;
349 /*Enable IRQ */
350 if (sulog_thru_diag)
351 sulog_str->sulog_driver_status = 2;
352 else
353 sulog_str->sulog_driver_status = 1;
354 enable_irq(sulog_str->res[0].sulog_irq_resource);
355 return 0;
356}
357
358static int sulog_driver_disable(void)
359{
360 int i;
361 struct irq_desc *desc;
362 /* orignal: Disable IRQ */
363 /* new: don't disable irq as ripc0 wakeup irq share
364 * the same irq line with sulog ripc notification irq
365 */
366 sulog_str->sulog_driver_status = 0;
367 desc = irq_to_desc(sulog_str->res[0].sulog_irq_resource);
368 if (!(desc && desc->action && desc->action->next)) {
369 DEV_INFO(sulog_str->dev, "disable sulog irq\n");
370 disable_irq(sulog_str->res[0].sulog_irq_resource);
371 }
372
373 /* disable tasklet when it's enabled */
374 if (atomic_read(&sulog_str->sulog_copy_tasklet.count) == 0)
375 tasklet_disable(&sulog_str->sulog_copy_tasklet);
376
377 if (sulog_str->app_mem.sulog_target_type == TARGET_USB) {
378 if (sulog_thru_diag) {
379 pr_info("Disable Sulog through diag port!\n");
380 pxa_unregister_diag_serial_streaming();
381 } else {
382 pxa_unregister_debug_serial_streaming();
383 }
384 }
385 /*Set trace counter back to inital value */
386 DEV_DBG(sulog_str->dev, "disable data copy tasklet\n");
387
388 if (sulog_str->app_mem.sulog_target_type == TARGET_SD) {
389 /* free 2 buffers */
390 for (i = 0; i < NUM_OF_BUFFERS; i++)
391 vfree(sulog_str->app_mem.sulog_app_buf[i]);
392 }
393
394 /* clr pending ripc event if any */
395 if (__raw_readl((unsigned int *)
396 ((unsigned int)sulog_str->res[0].ripc_base_address +
397 RIPC_APPS_INT_REG)) & 1) {
398 printk_ratelimited("%s: clr sulog APPS INT\n", __func__);
399 /*Clear LSB to mark interrupt as handled*/
400 __raw_writel(0, (unsigned int *)
401 ((unsigned int)sulog_str->res[0].ripc_base_address +
402 RIPC_APPS_INT_REG));
403 }
404
405 DEV_INFO(sulog_str->dev, "Sulog driver disabled\n");
406 return 0;
407}
408
409static int sulog_change_status(unsigned int input)
410{
411 int ret_val = 0;
412
413 DEV_INFO(sulog_str->dev, "Changing sulog driver status. Selected status 0x%x\n", input);
414 switch (input) {
415 case (SULOG_ENABLE2):
416 sulog_thru_diag = 1;
417 /* fall through */
418 case (SULOG_ENABLE):
419 ret_val = sulog_driver_enable();
420 break;
421 case (SULOG_DISABLE):
422 ret_val = sulog_driver_disable();
423 sulog_thru_diag = 0;
424 break;
425 default:
426 /*User request is not valid */
427 DEV_INFO(sulog_str->dev,
428 "Input value is not valid: %d\n", input);
429 ret_val = -ESRCH;
430 }
431 return ret_val;
432}
433
434static ssize_t sulog_driver_show(struct device *dev,
435 struct device_attribute *attr,
436 char *buf)
437{
438 return sprintf(buf, "Sulog driver status is 0x%x\n",
439 sulog_str->sulog_driver_status);
440}
441
442static ssize_t sulog_driver_store(struct device *dev,
443 struct device_attribute *attr,
444 const char *buff, size_t count)
445{
446 unsigned int input, ret_val;
447
448 if (sscanf(buff, "%x", &input) < 1) {
449 PR_ERR("Storing information failed\n");
450 return count;
451 }
452
453 if (input != SULOG_ENABLE2)
454 input = (input == 0) ? 0 : 1;
455
456 if (sulog_str->sulog_driver_status == input)
457 return count;
458 ret_val = sulog_change_status(input);
459 if (ret_val)
460 PR_ERR("Sulog driver failed with return code 0x%x\n",
461 ret_val);
462 else
463 DEV_DBG(sulog_str->dev, "Sulog driver status is 0x%x\n",
464 ret_val);
465 return count;
466}
467
468static ssize_t sulog_watermark_show(struct device *dev,
469 struct device_attribute *attr,
470 char *buf)
471{
472 return sprintf(buf,
473 "Sulog Hightwater mark buffer size is 0x%x\n",
474 sulog_str->app_mem.high_watermark);
475}
476
477static ssize_t sulog_watermark_store(struct device *dev,
478 struct device_attribute *attr,
479 const char *buff, size_t count)
480{
481 unsigned int input;
482
483 if (sscanf(buff, "0x%x", &input) < 1) {
484 PR_ERR("Reading watermark size from user failed\n");
485 return count;
486 }
487
488 if (sulog_str->app_mem.sulog_allocbuf_size < input)
489 PR_ERR("Requested Watermark level is higher then memory allocation, update it too\n");
490 sulog_str->app_mem.high_watermark = input;
491 DEV_DBG(sulog_str->dev,
492 "High watermark was set to 0x%x\n", input);
493
494 return count;
495}
496
497static ssize_t sulog_buff_size_show(struct device *dev,
498 struct device_attribute *attr,
499 char *buf)
500{
501 return sprintf(buf, "Sulog App buffer size is 0x%08x\n",
502 sulog_str->app_mem.sulog_allocbuf_size);
503}
504
505static ssize_t sulog_buff_size_store(struct device *dev,
506 struct device_attribute *attr,
507 const char *buff, size_t count)
508{
509 unsigned int input;
510
511 if (sulog_str->sulog_driver_status) {
512 PR_ERR("Cannot change memory allocation while driver is running\n");
513 return count;
514 }
515 if (sscanf(buff, "0x%x", &input) < 1) {
516 PR_ERR("Reading buffer size input from user failed\n");
517 return count;
518 }
519 if (sulog_str->app_mem.high_watermark > input)
520 PR_ERR("Current High watermark is larger then requested memory allocation, update it too\n");
521 sulog_str->app_mem.sulog_allocbuf_size = input;
522 DEV_DBG(sulog_str->dev,
523 "App memory allocation was set to 0x%x\n", input);
524
525 return count;
526}
527
528static ssize_t sulog_target_type_show(struct device *dev,
529 struct device_attribute *attr,
530 char *buf)
531{
532 if (sulog_str->app_mem.sulog_target_type == TARGET_SD)
533 return sprintf(buf, "Sulog target type is SD\n");
534 else
535 return sprintf(buf, "Sulog target type is USB\n");
536}
537
538static ssize_t sulog_target_type_store(struct device *dev,
539 struct device_attribute *attr,
540 const char *buff, size_t count)
541{
542 unsigned int input;
543
544 if (sscanf(buff, "%x", &input) < 1) {
545 PR_ERR("Storing information failed\n");
546 return count;
547 }
548
549 if (sulog_str->sulog_driver_status) {
550 PR_ERR("Cannot change target type while driver is running\n");
551 return count;
552 }
553
554 if (input != TARGET_SD && input != TARGET_USB) {
555 PR_ERR("Wrong input value.\n\
556 0 - Output to SDcard\n\
557 1 - Output to usb\n");
558 return count;
559 }
560
561 sulog_str->app_mem.sulog_target_type = input;
562 if (sulog_str->app_mem.sulog_target_type == TARGET_SD)
563 DEV_INFO(sulog_str->dev, "Sulog target type is SD\n");
564 else
565 DEV_INFO(sulog_str->dev, "Sulog target type is USB\n");
566 return count;
567}
568
569static ssize_t sulog_statistics_show(struct device *dev,
570 struct device_attribute *attr,
571 char *buf)
572{
573 return sprintf(buf, "Sulog driver statistics:\n\
574 Number of interrupts received - %d\n\
575 Amount of data passed in bytes 0x%x\n\
576 Number of packates passed - %d\n\
577 Number of packates dropped - %d\n", \
578 sulog_str->sulog_statistic.irq_count, \
579 sulog_str->sulog_statistic.tot_data_sum, \
580 sulog_str->sulog_statistic.packets_pass, \
581 sulog_str->sulog_statistic.packets_dropped);
582}
583
584static ssize_t sulog_statistics_store(struct device *dev,
585 struct device_attribute *attr,
586 const char *buff, size_t count)
587{
588 return 0;
589}
590static int sulog_open(struct inode *inode, struct file *file)
591{
592 DEV_DBG(sulog_str->dev, "sulog open command triggered\n");
593 return 0;
594}
595
596static ssize_t sulog_read(struct file *filp, char __user *buf,
597 size_t count, loff_t *unused)
598{
599 /*Todo - check there is enough space for write*/
600 int ret_val;
601 size_t data_count;
602 ret_val = wait_event_interruptible(sulog_str->read_queue,
603 (sulog_str->app_mem.app_buff_size >
604 sulog_str->app_mem.high_watermark));
605 if (ret_val == -ERESTARTSYS)
606 return 0;
607
608 if (likely(count >= sulog_str->app_mem.app_buff_size)) {
609 data_count = sulog_str->app_mem.app_buff_size;
610 } else {
611 data_count = count;
612 DEV_INFO(sulog_str->dev, "User space didn't allocate enough read space, possible data lost\n");
613 }
614 /*Moving to other apps buffer*/
615 if (glb_cnt == 0) {
616 sulog_str->app_mem.sulog_app_ptr =
617 sulog_str->app_mem.sulog_app_buf[glb_cnt+1];
618 } else {
619 sulog_str->app_mem.sulog_app_ptr =
620 sulog_str->app_mem.sulog_app_buf[glb_cnt-1];
621 }
622
623 /*AP memory is cyclic*/
624 sulog_str->app_mem.app_buff_size = 0;
625
626 enable_irq(sulog_str->res[0].sulog_irq_resource);
627
628 ret_val = copy_to_user(buf,
629 sulog_str->app_mem.sulog_app_buf[glb_cnt], data_count);
630
631 /*Moving to other apps buffer*/
632 glb_cnt = ((glb_cnt+1) % 2);
633
634 if (unlikely(ret_val)) {
635 DEV_ERR(sulog_str->dev, "copy to user failed\n\n");
636 sulog_str->sulog_statistic.packets_dropped++;
637 return -EFAULT;
638 } else {
639 sulog_str->sulog_statistic.packets_pass++;
640 }
641
642 return data_count;
643}
644
645static irqreturn_t sulog_irq_handler(int this_irq, void *dev_id)
646{
647 unsigned char *tmp_start_addr;
648 /*In order to prevent race conditions */
649 if (unlikely(!sulog_str->sulog_driver_status)) {
650 if (__raw_readl((unsigned int *)
651 ((unsigned int)sulog_str->res[0].ripc_base_address +
652 RIPC_APPS_INT_REG)) & 1) {
653 printk_ratelimited(KERN_DEBUG "%s: clr sulog APPS INT\n", __func__);
654 /*Clear LSB to mark interrupt as handled*/
655 __raw_writel(0, (unsigned int *)
656 ((unsigned int)sulog_str->res[0].ripc_base_address +
657 RIPC_APPS_INT_REG));
658 }
659 return IRQ_HANDLED;
660 }
661 if (unlikely(!(__raw_readl((unsigned int *)
662 (((unsigned int)sulog_str->
663 res[0].ripc_base_address +
664 RIPC_APPS_INFO_REG)))))) {
665 /*Clear LSB to mark interrupt as handled*/
666 __raw_writel(0, (unsigned int *)
667 ((unsigned int)sulog_str->res[0].ripc_base_address +
668 RIPC_APPS_INT_REG));
669 return IRQ_HANDLED;
670 }
671
672 sulog_str->res[0].ripc_size =
673 __raw_readl((unsigned int *)
674 ((unsigned int)sulog_str->res[0].ripc_base_address +
675 RIPC_APPS_INT_REG));
676 sulog_str->res[0].ripc_size = (sulog_str->res[0].ripc_size >> 1) * 4;
677 /* Check if given size is reasonable */
678 BUG_ON(sulog_str->res[0].ripc_size > COMM_DSP_DOUBLE_BUFFER_SIZE);
679
680 tmp_start_addr = (unsigned char *)__raw_readl((unsigned int *)
681 (((unsigned int)sulog_str->
682 res[0].ripc_base_address +
683 RIPC_APPS_INFO_REG)));
684
685 /* Check if first run or if start address is less than base*/
686 if (unlikely((!sulog_str->res[0].comm_strt_addr)) ||
687 (tmp_start_addr < sulog_str->res[0].comm_base_addr)) {
688 /* First run - go to workqueue to ioremap the whole comm buffer, then continue
689 or if bug, base address id less than last round, got to remap again*/
690 sulog_str->res[0].comm_base_addr = tmp_start_addr;
691 queue_work(sulog_str->sulog_workqueue_ptr,
692 &sulog_str->sulog_workstruct);
693 } else {
694 /* Not first run - get offset from buffer and continue */
695 sulog_str->res[0].comm_addr_offst =
696 tmp_start_addr - sulog_str->res[0].comm_base_addr;
697 if (likely((sulog_str->res[0].comm_addr_offst >= 0) && (sulog_str->res[0].ripc_size > 0))) {
698 tasklet_schedule(&sulog_str->sulog_copy_tasklet);
699 sulog_str->sulog_statistic.tot_data_sum +=
700 sulog_str->res[0].ripc_size;
701 } else
702 /*We shouldn't reach here, only happen if Current address
703 is smaller then base address*/
704 sulog_str->sulog_statistic.packets_dropped++;
705 }
706
707 /*Clear LSB to mark interrupt as handled*/
708 __raw_writel(0, (unsigned int *)
709 ((unsigned int)sulog_str->res[0].ripc_base_address +
710 RIPC_APPS_INT_REG));
711
712 sulog_str->sulog_statistic.irq_count++;
713
714 return IRQ_HANDLED;
715}
716
717static int sulog_probe(struct platform_device *pdev)
718{
719 int ret_val, irq, fs_cnt, i = 0;
720 unsigned long flags;
721
722 sulog_str = kzalloc(sizeof(struct sulog_struct), GFP_KERNEL);
723 if (sulog_str == NULL) {
724 PR_ERR("Failed to allocate memory!\n");
725 ret_val = -ENOMEM;
726 goto err_mem;
727 }
728 ret_val = misc_register(&sulog_miscdev);
729 if (ret_val != 0) {
730 PR_ERR("misc_register failed\n");
731 goto err_misc_register;
732 }
733 sulog_str->dev = sulog_miscdev.this_device;
734 /* Submit resourses */
735 for (i = 0; i < RIPC_BASE_ADDRESSES; i++) {
736 sulog_str->res[i].resource = platform_get_resource(
737 pdev, IORESOURCE_MEM, i);
738 if (sulog_str->res[i].resource == NULL) {
739 PR_ERR("Failed to receive driver resources\n");
740 ret_val = -ENOMEM;
741 goto err_malloc;
742 }
743 #if 0
744 if (!request_mem_region(
745 sulog_str->res[i].resource->start,
746 resource_size(sulog_str->res[i].resource),
747 sulog_str->res[i].resource->name)) {
748 ret_val = -ENOMEM;
749 goto mem_reg;
750 }
751 #endif
752 sulog_str->res[i].ripc_base_address = ioremap(
753 sulog_str->res[i].resource->start,
754 resource_size(sulog_str->res[i].resource));
755
756 if (sulog_str->res[i].ripc_base_address == NULL) {
757 DEV_ERR(sulog_str->dev,
758 "failed to get platform register values!\n");
759 ret_val = -ENXIO;
760 goto err_malloc;
761 }
762 }
763
764 /*Set default values */
765 sulog_str->app_mem.sulog_allocbuf_size = AP_DDR_DEFAULT_SIZE;
766 sulog_str->app_mem.high_watermark = AP_HIGHWATERMARK;
767 sulog_str->app_mem.app_buff_size = 0;
768 sulog_str->sulog_driver_status = 0;
769 sulog_str->app_mem.sulog_target_type = TARGET_USB;
770 platform_set_drvdata(pdev, sulog_str);
771 /*Submit IRQ */
772 irq = platform_get_irq(pdev, 0);
773 if (irq < 0) {
774 DEV_ERR(sulog_str->dev, "failed to get irq!\n");
775 ret_val = -ENODEV;
776 goto err_malloc;
777 }
778 local_irq_save(flags);
779 ret_val = request_irq(irq, sulog_irq_handler,
780 /* IRQF_DISABLED | */IRQF_SHARED, pdev->name, sulog_str);
781 if (ret_val) {
782 DEV_ERR(sulog_str->dev, "failed to request irq!\n");
783 local_irq_restore(flags);
784 goto err_malloc;
785 }
786 disable_irq(irq);
787 local_irq_restore(flags);
788 /*Todo-add irq name*/
789 sulog_str->res[0].sulog_irq_resource = irq;
790 /*Todo - register on DVFM */
791 /* Create sysfs files */
792 for (fs_cnt = 0; fs_cnt < ARRAY_SIZE(dev_attr_arr); fs_cnt++) {
793 ret_val = device_create_file(sulog_str->dev,
794 dev_attr_arr[fs_cnt]);
795 if (ret_val < 0) {
796 DEV_ERR(sulog_str->dev, "failed to create sysfs!\n");
797 goto err_sysfs;
798 }
799 }
800 init_waitqueue_head(&sulog_str->read_queue);
801 /*Init workqueue*/
802 sulog_str->sulog_workqueue_ptr = create_workqueue("sulog_workqueue");
803 if (!sulog_str->sulog_workqueue_ptr) {
804 DEV_ERR(sulog_str->dev, "create workqueue error\n");
805 ret_val = -ENOMEM;
806 goto err_sysfs;
807 }
808 INIT_WORK(&sulog_str->sulog_workstruct, sulog_comm_map);
809 tasklet_init(&sulog_str->sulog_copy_tasklet,
810 sulog_copy_tasklet, 0);
811 DEV_INFO(sulog_str->dev, "sulog_driver_init finished successfully\n");
812 return 0;
813err_sysfs:
814 for (fs_cnt--; fs_cnt >= 0; fs_cnt--)
815 device_remove_file(sulog_str->dev,
816 dev_attr_arr[fs_cnt]);
817mem_reg:
818 for (; i >= 0 ; i--)
819 release_mem_region(sulog_str->res[i].resource->start,
820 resource_size(sulog_str->res[i].resource));
821err_malloc:
822 kfree(sulog_str);
823 if (i-- > 0)
824 goto mem_reg;
825err_mem:
826 misc_deregister(&sulog_miscdev);
827err_misc_register:
828 PR_ERR("dbg_macro probe error %d\n", ret_val);
829 return ret_val;
830}
831
832static int sulog_remove(struct platform_device *pdev)
833{
834 int i, fs_cnt = ARRAY_SIZE(dev_attr_arr);
835
836 disable_irq(sulog_str->res[0].sulog_irq_resource);
837 destroy_workqueue(sulog_str->sulog_workqueue_ptr);
838 if (sulog_str->res[0].comm_strt_addr && sulog_str->res[0].remapped)
839 iounmap(sulog_str->res[0].comm_strt_addr);
840 /* free 2 buffers */
841 for (i = 0; i < NUM_OF_BUFFERS; i++)
842 vfree(sulog_str->app_mem.sulog_app_buf[i]);
843 for (fs_cnt--; fs_cnt >= 0; fs_cnt--)
844 device_remove_file(sulog_str->dev,
845 dev_attr_arr[fs_cnt]);
846 for (i = 0; i < RIPC_BASE_ADDRESSES; i++) {
847 release_mem_region(sulog_str->res[i].resource->start,
848 resource_size(sulog_str->res[i].resource));
849 }
850 kfree(sulog_str);
851 misc_deregister(&sulog_miscdev);
852 sulog_miscdev.minor = MISC_DYNAMIC_MINOR;
853 DEV_INFO(sulog_str->dev,
854 "Sulog remove completed\n");
855 return 0;
856}
857
858#ifdef CONFIG_OF
859static const struct of_device_id sulog_of_match[] = {
860 {.compatible = "mrvl,mmp-sulog",},
861 {},
862};
863
864MODULE_DEVICE_TABLE(of, sulog_of_match);
865#endif
866static struct platform_driver sulog_driver = {
867 .probe = sulog_probe,
868 .remove = sulog_remove,
869 .driver = {
870 .name = "mmp-sulog",
871 .owner = THIS_MODULE,
872#ifdef CONFIG_OF
873 .of_match_table = sulog_of_match,
874#endif
875 },
876};
877
878static int __init sulog_init(void)
879{
880 return platform_driver_register(&sulog_driver);
881}
882
883static void __exit sulog_exit(void)
884{
885 platform_driver_unregister(&sulog_driver);
886}
887
888module_init(sulog_init);
889module_exit(sulog_exit);
890
891MODULE_AUTHOR("Tzviel Lemberger <tzviel@marvell.com>");
892MODULE_DESCRIPTION("Super log driver");
893MODULE_LICENSE("GPL");