| /* | 
|  * | 
|  * (C) COPYRIGHT 2012-2016 ARM Limited. All rights reserved. | 
|  * | 
|  * This program is free software and is provided to you under the terms of the | 
|  * GNU General Public License version 2 as published by the Free Software | 
|  * Foundation, and any use by you of this program is subject to the terms | 
|  * of such GNU licence. | 
|  * | 
|  * A copy of the licence is included with the program, and can also be obtained | 
|  * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 
|  * Boston, MA  02110-1301, USA. | 
|  * | 
|  */ | 
|   | 
|   | 
|   | 
|   | 
|   | 
| #include <mali_kbase.h> | 
| #include <mali_kbase_jm.h> | 
| #include <mali_kbase_hwaccess_jm.h> | 
|   | 
| #define CREATE_TRACE_POINTS | 
|   | 
| #ifdef CONFIG_MALI_TRACE_TIMELINE | 
| #include "mali_timeline.h" | 
|   | 
| #include <linux/debugfs.h> | 
| #include <linux/seq_file.h> | 
|   | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_atoms_in_flight); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_atom); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_gpu_slot_active); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_gpu_slot_action); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_gpu_power_active); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_l2_power_active); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_pm_event); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_slot_atom); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_pm_checktrans); | 
| EXPORT_TRACEPOINT_SYMBOL_GPL(mali_timeline_context_active); | 
|   | 
| struct kbase_trace_timeline_desc { | 
|     char *enum_str; | 
|     char *desc; | 
|     char *format; | 
|     char *format_desc; | 
| }; | 
|   | 
| static struct kbase_trace_timeline_desc kbase_trace_timeline_desc_table[] = { | 
|     #define KBASE_TIMELINE_TRACE_CODE(enum_val, desc, format, format_desc) { #enum_val, desc, format, format_desc } | 
|     #include "mali_kbase_trace_timeline_defs.h" | 
|     #undef KBASE_TIMELINE_TRACE_CODE | 
| }; | 
|   | 
| #define KBASE_NR_TRACE_CODES ARRAY_SIZE(kbase_trace_timeline_desc_table) | 
|   | 
| static void *kbasep_trace_timeline_seq_start(struct seq_file *s, loff_t *pos) | 
| { | 
|     if (*pos >= KBASE_NR_TRACE_CODES) | 
|         return NULL; | 
|   | 
|     return &kbase_trace_timeline_desc_table[*pos]; | 
| } | 
|   | 
| static void kbasep_trace_timeline_seq_stop(struct seq_file *s, void *data) | 
| { | 
| } | 
|   | 
| static void *kbasep_trace_timeline_seq_next(struct seq_file *s, void *data, loff_t *pos) | 
| { | 
|     (*pos)++; | 
|   | 
|     if (*pos == KBASE_NR_TRACE_CODES) | 
|         return NULL; | 
|   | 
|     return &kbase_trace_timeline_desc_table[*pos]; | 
| } | 
|   | 
| static int kbasep_trace_timeline_seq_show(struct seq_file *s, void *data) | 
| { | 
|     struct kbase_trace_timeline_desc *trace_desc = data; | 
|   | 
|     seq_printf(s, "%s#%s#%s#%s\n", trace_desc->enum_str, trace_desc->desc, trace_desc->format, trace_desc->format_desc); | 
|     return 0; | 
| } | 
|   | 
|   | 
| static const struct seq_operations kbasep_trace_timeline_seq_ops = { | 
|     .start = kbasep_trace_timeline_seq_start, | 
|     .next = kbasep_trace_timeline_seq_next, | 
|     .stop = kbasep_trace_timeline_seq_stop, | 
|     .show = kbasep_trace_timeline_seq_show, | 
| }; | 
|   | 
| static int kbasep_trace_timeline_debugfs_open(struct inode *inode, struct file *file) | 
| { | 
|     return seq_open(file, &kbasep_trace_timeline_seq_ops); | 
| } | 
|   | 
| static const struct file_operations kbasep_trace_timeline_debugfs_fops = { | 
|     .open = kbasep_trace_timeline_debugfs_open, | 
|     .read = seq_read, | 
|     .llseek = seq_lseek, | 
|     .release = seq_release, | 
| }; | 
|   | 
| #ifdef CONFIG_DEBUG_FS | 
|   | 
| void kbasep_trace_timeline_debugfs_init(struct kbase_device *kbdev) | 
| { | 
|     debugfs_create_file("mali_timeline_defs", | 
|             S_IRUGO, kbdev->mali_debugfs_directory, NULL, | 
|             &kbasep_trace_timeline_debugfs_fops); | 
| } | 
|   | 
| #endif /* CONFIG_DEBUG_FS */ | 
|   | 
| void kbase_timeline_job_slot_submit(struct kbase_device *kbdev, struct kbase_context *kctx, | 
|         struct kbase_jd_atom *katom, int js) | 
| { | 
|     lockdep_assert_held(&kbdev->hwaccess_lock); | 
|   | 
|     if (kbdev->timeline.slot_atoms_submitted[js] > 0) { | 
|         KBASE_TIMELINE_JOB_START_NEXT(kctx, js, 1); | 
|     } else { | 
|         base_atom_id atom_number = kbase_jd_atom_id(kctx, katom); | 
|   | 
|         KBASE_TIMELINE_JOB_START_HEAD(kctx, js, 1); | 
|         KBASE_TIMELINE_JOB_START(kctx, js, atom_number); | 
|     } | 
|     ++kbdev->timeline.slot_atoms_submitted[js]; | 
|   | 
|     KBASE_TIMELINE_ATOMS_SUBMITTED(kctx, js, kbdev->timeline.slot_atoms_submitted[js]); | 
| } | 
|   | 
| void kbase_timeline_job_slot_done(struct kbase_device *kbdev, struct kbase_context *kctx, | 
|         struct kbase_jd_atom *katom, int js, | 
|         kbasep_js_atom_done_code done_code) | 
| { | 
|     lockdep_assert_held(&kbdev->hwaccess_lock); | 
|   | 
|     if (done_code & KBASE_JS_ATOM_DONE_EVICTED_FROM_NEXT) { | 
|         KBASE_TIMELINE_JOB_START_NEXT(kctx, js, 0); | 
|     } else { | 
|         /* Job finished in JS_HEAD */ | 
|         base_atom_id atom_number = kbase_jd_atom_id(kctx, katom); | 
|   | 
|         KBASE_TIMELINE_JOB_START_HEAD(kctx, js, 0); | 
|         KBASE_TIMELINE_JOB_STOP(kctx, js, atom_number); | 
|   | 
|         /* see if we need to trace the job in JS_NEXT moving to JS_HEAD */ | 
|         if (kbase_backend_nr_atoms_submitted(kbdev, js)) { | 
|             struct kbase_jd_atom *next_katom; | 
|             struct kbase_context *next_kctx; | 
|   | 
|             /* Peek the next atom - note that the atom in JS_HEAD will already | 
|              * have been dequeued */ | 
|             next_katom = kbase_backend_inspect_head(kbdev, js); | 
|             WARN_ON(!next_katom); | 
|             next_kctx = next_katom->kctx; | 
|             KBASE_TIMELINE_JOB_START_NEXT(next_kctx, js, 0); | 
|             KBASE_TIMELINE_JOB_START_HEAD(next_kctx, js, 1); | 
|             KBASE_TIMELINE_JOB_START(next_kctx, js, kbase_jd_atom_id(next_kctx, next_katom)); | 
|         } | 
|     } | 
|   | 
|     --kbdev->timeline.slot_atoms_submitted[js]; | 
|   | 
|     KBASE_TIMELINE_ATOMS_SUBMITTED(kctx, js, kbdev->timeline.slot_atoms_submitted[js]); | 
| } | 
|   | 
| void kbase_timeline_pm_send_event(struct kbase_device *kbdev, enum kbase_timeline_pm_event event_sent) | 
| { | 
|     int uid = 0; | 
|     int old_uid; | 
|   | 
|     /* If a producer already exists for the event, try to use their UID (multiple-producers) */ | 
|     uid = atomic_read(&kbdev->timeline.pm_event_uid[event_sent]); | 
|     old_uid = uid; | 
|   | 
|     /* Get a new non-zero UID if we don't have one yet */ | 
|     while (!uid) | 
|         uid = atomic_inc_return(&kbdev->timeline.pm_event_uid_counter); | 
|   | 
|     /* Try to use this UID */ | 
|     if (old_uid != atomic_cmpxchg(&kbdev->timeline.pm_event_uid[event_sent], old_uid, uid)) | 
|         /* If it changed, raced with another producer: we've lost this UID */ | 
|         uid = 0; | 
|   | 
|     KBASE_TIMELINE_PM_SEND_EVENT(kbdev, event_sent, uid); | 
| } | 
|   | 
| void kbase_timeline_pm_check_handle_event(struct kbase_device *kbdev, enum kbase_timeline_pm_event event) | 
| { | 
|     int uid = atomic_read(&kbdev->timeline.pm_event_uid[event]); | 
|   | 
|     if (uid != 0) { | 
|         if (uid != atomic_cmpxchg(&kbdev->timeline.pm_event_uid[event], uid, 0)) | 
|             /* If it changed, raced with another consumer: we've lost this UID */ | 
|             uid = 0; | 
|   | 
|         KBASE_TIMELINE_PM_HANDLE_EVENT(kbdev, event, uid); | 
|     } | 
| } | 
|   | 
| void kbase_timeline_pm_handle_event(struct kbase_device *kbdev, enum kbase_timeline_pm_event event) | 
| { | 
|     int uid = atomic_read(&kbdev->timeline.pm_event_uid[event]); | 
|   | 
|     if (uid != atomic_cmpxchg(&kbdev->timeline.pm_event_uid[event], uid, 0)) | 
|         /* If it changed, raced with another consumer: we've lost this UID */ | 
|         uid = 0; | 
|   | 
|     KBASE_TIMELINE_PM_HANDLE_EVENT(kbdev, event, uid); | 
| } | 
|   | 
| void kbase_timeline_pm_l2_transition_start(struct kbase_device *kbdev) | 
| { | 
|     lockdep_assert_held(&kbdev->hwaccess_lock); | 
|     /* Simply log the start of the transition */ | 
|     kbdev->timeline.l2_transitioning = true; | 
|     KBASE_TIMELINE_POWERING_L2(kbdev); | 
| } | 
|   | 
| void kbase_timeline_pm_l2_transition_done(struct kbase_device *kbdev) | 
| { | 
|     lockdep_assert_held(&kbdev->hwaccess_lock); | 
|     /* Simply log the end of the transition */ | 
|     if (kbdev->timeline.l2_transitioning) { | 
|         kbdev->timeline.l2_transitioning = false; | 
|         KBASE_TIMELINE_POWERED_L2(kbdev); | 
|     } | 
| } | 
|   | 
| #endif /* CONFIG_MALI_TRACE_TIMELINE */ |