123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- /*
- *
- * (C) COPYRIGHT 2010-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.
- *
- */
- /*
- * Base kernel device APIs
- */
- #include <linux/debugfs.h>
- #include <linux/dma-mapping.h>
- #include <linux/seq_file.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/of_platform.h>
- #include <mali_kbase.h>
- #include <mali_kbase_defs.h>
- #include <mali_kbase_hw.h>
- #include <mali_kbase_config_defaults.h>
- /* NOTE: Magic - 0x45435254 (TRCE in ASCII).
- * Supports tracing feature provided in the base module.
- * Please keep it in sync with the value of base module.
- */
- #define TRACE_BUFFER_HEADER_SPECIAL 0x45435254
- #if KBASE_TRACE_ENABLE
- static const char *kbasep_trace_code_string[] = {
- /* IMPORTANT: USE OF SPECIAL #INCLUDE OF NON-STANDARD HEADER FILE
- * THIS MUST BE USED AT THE START OF THE ARRAY */
- #define KBASE_TRACE_CODE_MAKE_CODE(X) # X
- #include "mali_kbase_trace_defs.h"
- #undef KBASE_TRACE_CODE_MAKE_CODE
- };
- #endif
- #define DEBUG_MESSAGE_SIZE 256
- static int kbasep_trace_init(struct kbase_device *kbdev);
- static void kbasep_trace_term(struct kbase_device *kbdev);
- static void kbasep_trace_hook_wrapper(void *param);
- struct kbase_device *kbase_device_alloc(void)
- {
- return kzalloc(sizeof(struct kbase_device), GFP_KERNEL);
- }
- static int kbase_device_as_init(struct kbase_device *kbdev, int i)
- {
- const char format[] = "mali_mmu%d";
- char name[sizeof(format)];
- const char poke_format[] = "mali_mmu%d_poker";
- char poke_name[sizeof(poke_format)];
- if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8316))
- snprintf(poke_name, sizeof(poke_name), poke_format, i);
- snprintf(name, sizeof(name), format, i);
- kbdev->as[i].number = i;
- kbdev->as[i].fault_addr = 0ULL;
- kbdev->as[i].pf_wq = alloc_workqueue(name, 0, 1);
- if (!kbdev->as[i].pf_wq)
- return -EINVAL;
- mutex_init(&kbdev->as[i].transaction_mutex);
- INIT_WORK(&kbdev->as[i].work_pagefault, page_fault_worker);
- INIT_WORK(&kbdev->as[i].work_busfault, bus_fault_worker);
- if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8316)) {
- struct hrtimer *poke_timer = &kbdev->as[i].poke_timer;
- struct work_struct *poke_work = &kbdev->as[i].poke_work;
- kbdev->as[i].poke_wq = alloc_workqueue(poke_name, 0, 1);
- if (!kbdev->as[i].poke_wq) {
- destroy_workqueue(kbdev->as[i].pf_wq);
- return -EINVAL;
- }
- KBASE_DEBUG_ASSERT(!object_is_on_stack(poke_work));
- INIT_WORK(poke_work, kbasep_as_do_poke);
- hrtimer_init(poke_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- poke_timer->function = kbasep_as_poke_timer_callback;
- kbdev->as[i].poke_refcount = 0;
- kbdev->as[i].poke_state = 0u;
- }
- return 0;
- }
- static void kbase_device_as_term(struct kbase_device *kbdev, int i)
- {
- destroy_workqueue(kbdev->as[i].pf_wq);
- if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8316))
- destroy_workqueue(kbdev->as[i].poke_wq);
- }
- static int kbase_device_all_as_init(struct kbase_device *kbdev)
- {
- int i, err;
- for (i = 0; i < kbdev->nr_hw_address_spaces; i++) {
- err = kbase_device_as_init(kbdev, i);
- if (err)
- goto free_workqs;
- }
- return 0;
- free_workqs:
- for (; i > 0; i--)
- kbase_device_as_term(kbdev, i);
- return err;
- }
- static void kbase_device_all_as_term(struct kbase_device *kbdev)
- {
- int i;
- for (i = 0; i < kbdev->nr_hw_address_spaces; i++)
- kbase_device_as_term(kbdev, i);
- }
- int kbase_device_init(struct kbase_device * const kbdev)
- {
- int err;
- #ifdef CONFIG_ARM64
- struct device_node *np = NULL;
- #endif /* CONFIG_ARM64 */
- spin_lock_init(&kbdev->mmu_mask_change);
- #ifdef CONFIG_ARM64
- kbdev->cci_snoop_enabled = false;
- np = kbdev->dev->of_node;
- if (np != NULL) {
- if (of_property_read_u32(np, "snoop_enable_smc",
- &kbdev->snoop_enable_smc))
- kbdev->snoop_enable_smc = 0;
- if (of_property_read_u32(np, "snoop_disable_smc",
- &kbdev->snoop_disable_smc))
- kbdev->snoop_disable_smc = 0;
- /* Either both or none of the calls should be provided. */
- if (!((kbdev->snoop_disable_smc == 0
- && kbdev->snoop_enable_smc == 0)
- || (kbdev->snoop_disable_smc != 0
- && kbdev->snoop_enable_smc != 0))) {
- WARN_ON(1);
- err = -EINVAL;
- goto fail;
- }
- }
- #endif /* CONFIG_ARM64 */
- /* Get the list of workarounds for issues on the current HW
- * (identified by the GPU_ID register)
- */
- err = kbase_hw_set_issues_mask(kbdev);
- if (err)
- goto fail;
- /* Set the list of features available on the current HW
- * (identified by the GPU_ID register)
- */
- kbase_hw_set_features_mask(kbdev);
- kbase_gpuprops_set_features(kbdev);
- /* On Linux 4.0+, dma coherency is determined from device tree */
- #ifdef CONFIG_ARM64
- set_dma_ops(kbdev->dev, &noncoherent_swiotlb_dma_ops);
- #endif
- /* Workaround a pre-3.13 Linux issue, where dma_mask is NULL when our
- * device structure was created by device-tree
- */
- if (!kbdev->dev->dma_mask)
- kbdev->dev->dma_mask = &kbdev->dev->coherent_dma_mask;
- err = dma_set_mask(kbdev->dev,
- DMA_BIT_MASK(kbdev->gpu_props.mmu.pa_bits));
- if (err)
- goto dma_set_mask_failed;
- err = dma_set_coherent_mask(kbdev->dev,
- DMA_BIT_MASK(kbdev->gpu_props.mmu.pa_bits));
- if (err)
- goto dma_set_mask_failed;
- kbdev->nr_hw_address_spaces = kbdev->gpu_props.num_address_spaces;
- err = kbase_device_all_as_init(kbdev);
- if (err)
- goto as_init_failed;
- spin_lock_init(&kbdev->hwcnt.lock);
- err = kbasep_trace_init(kbdev);
- if (err)
- goto term_as;
- mutex_init(&kbdev->cacheclean_lock);
- #ifdef CONFIG_MALI_TRACE_TIMELINE
- for (i = 0; i < BASE_JM_MAX_NR_SLOTS; ++i)
- kbdev->timeline.slot_atoms_submitted[i] = 0;
- for (i = 0; i <= KBASEP_TIMELINE_PM_EVENT_LAST; ++i)
- atomic_set(&kbdev->timeline.pm_event_uid[i], 0);
- #endif /* CONFIG_MALI_TRACE_TIMELINE */
- kbase_debug_assert_register_hook(&kbasep_trace_hook_wrapper, kbdev);
- atomic_set(&kbdev->ctx_num, 0);
- err = kbase_instr_backend_init(kbdev);
- if (err)
- goto term_trace;
- kbdev->pm.dvfs_period = DEFAULT_PM_DVFS_PERIOD;
- kbdev->reset_timeout_ms = DEFAULT_RESET_TIMEOUT_MS;
- #ifdef CONFIG_MALI_GPU_MMU_AARCH64
- kbdev->mmu_mode = kbase_mmu_mode_get_aarch64();
- #else
- kbdev->mmu_mode = kbase_mmu_mode_get_lpae();
- #endif /* CONFIG_MALI_GPU_MMU_AARCH64 */
- #ifdef CONFIG_MALI_DEBUG
- init_waitqueue_head(&kbdev->driver_inactive_wait);
- #endif /* CONFIG_MALI_DEBUG */
- return 0;
- term_trace:
- kbasep_trace_term(kbdev);
- term_as:
- kbase_device_all_as_term(kbdev);
- as_init_failed:
- dma_set_mask_failed:
- fail:
- return err;
- }
- void kbase_device_term(struct kbase_device *kbdev)
- {
- KBASE_DEBUG_ASSERT(kbdev);
- #if KBASE_TRACE_ENABLE
- kbase_debug_assert_register_hook(NULL, NULL);
- #endif
- kbase_instr_backend_term(kbdev);
- kbasep_trace_term(kbdev);
- kbase_device_all_as_term(kbdev);
- }
- void kbase_device_free(struct kbase_device *kbdev)
- {
- kfree(kbdev);
- }
- int kbase_device_trace_buffer_install(
- struct kbase_context *kctx, u32 *tb, size_t size)
- {
- unsigned long flags;
- KBASE_DEBUG_ASSERT(kctx);
- KBASE_DEBUG_ASSERT(tb);
- /* Interface uses 16-bit value to track last accessed entry. Each entry
- * is composed of two 32-bit words.
- * This limits the size that can be handled without an overflow. */
- if (0xFFFF * (2 * sizeof(u32)) < size)
- return -EINVAL;
- /* set up the header */
- /* magic number in the first 4 bytes */
- tb[0] = TRACE_BUFFER_HEADER_SPECIAL;
- /* Store (write offset = 0, wrap counter = 0, transaction active = no)
- * write offset 0 means never written.
- * Offsets 1 to (wrap_offset - 1) used to store values when trace started
- */
- tb[1] = 0;
- /* install trace buffer */
- spin_lock_irqsave(&kctx->jctx.tb_lock, flags);
- kctx->jctx.tb_wrap_offset = size / 8;
- kctx->jctx.tb = tb;
- spin_unlock_irqrestore(&kctx->jctx.tb_lock, flags);
- return 0;
- }
- void kbase_device_trace_buffer_uninstall(struct kbase_context *kctx)
- {
- unsigned long flags;
- KBASE_DEBUG_ASSERT(kctx);
- spin_lock_irqsave(&kctx->jctx.tb_lock, flags);
- kctx->jctx.tb = NULL;
- kctx->jctx.tb_wrap_offset = 0;
- spin_unlock_irqrestore(&kctx->jctx.tb_lock, flags);
- }
- void kbase_device_trace_register_access(struct kbase_context *kctx, enum kbase_reg_access_type type, u16 reg_offset, u32 reg_value)
- {
- unsigned long flags;
- spin_lock_irqsave(&kctx->jctx.tb_lock, flags);
- if (kctx->jctx.tb) {
- u16 wrap_count;
- u16 write_offset;
- u32 *tb = kctx->jctx.tb;
- u32 header_word;
- header_word = tb[1];
- KBASE_DEBUG_ASSERT(0 == (header_word & 0x1));
- wrap_count = (header_word >> 1) & 0x7FFF;
- write_offset = (header_word >> 16) & 0xFFFF;
- /* mark as transaction in progress */
- tb[1] |= 0x1;
- mb();
- /* calculate new offset */
- write_offset++;
- if (write_offset == kctx->jctx.tb_wrap_offset) {
- /* wrap */
- write_offset = 1;
- wrap_count++;
- wrap_count &= 0x7FFF; /* 15bit wrap counter */
- }
- /* store the trace entry at the selected offset */
- tb[write_offset * 2 + 0] = (reg_offset & ~0x3) | ((type == REG_WRITE) ? 0x1 : 0x0);
- tb[write_offset * 2 + 1] = reg_value;
- mb();
- /* new header word */
- header_word = (write_offset << 16) | (wrap_count << 1) | 0x0; /* transaction complete */
- tb[1] = header_word;
- }
- spin_unlock_irqrestore(&kctx->jctx.tb_lock, flags);
- }
- /*
- * Device trace functions
- */
- #if KBASE_TRACE_ENABLE
- static int kbasep_trace_init(struct kbase_device *kbdev)
- {
- struct kbase_trace *rbuf;
- rbuf = kmalloc_array(KBASE_TRACE_SIZE, sizeof(*rbuf), GFP_KERNEL);
- if (!rbuf)
- return -EINVAL;
- kbdev->trace_rbuf = rbuf;
- spin_lock_init(&kbdev->trace_lock);
- return 0;
- }
- static void kbasep_trace_term(struct kbase_device *kbdev)
- {
- kfree(kbdev->trace_rbuf);
- }
- static void kbasep_trace_format_msg(struct kbase_trace *trace_msg, char *buffer, int len)
- {
- s32 written = 0;
- /* Initial part of message */
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), "%d.%.6d,%d,%d,%s,%p,", (int)trace_msg->timestamp.tv_sec, (int)(trace_msg->timestamp.tv_nsec / 1000), trace_msg->thread_id, trace_msg->cpu, kbasep_trace_code_string[trace_msg->code], trace_msg->ctx), 0);
- if (trace_msg->katom)
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), "atom %d (ud: 0x%llx 0x%llx)", trace_msg->atom_number, trace_msg->atom_udata[0], trace_msg->atom_udata[1]), 0);
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), ",%.8llx,", trace_msg->gpu_addr), 0);
- /* NOTE: Could add function callbacks to handle different message types */
- /* Jobslot present */
- if (trace_msg->flags & KBASE_TRACE_FLAG_JOBSLOT)
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), "%d", trace_msg->jobslot), 0);
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), ","), 0);
- /* Refcount present */
- if (trace_msg->flags & KBASE_TRACE_FLAG_REFCOUNT)
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), "%d", trace_msg->refcount), 0);
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), ","), 0);
- /* Rest of message */
- written += MAX(snprintf(buffer + written, MAX(len - written, 0), "0x%.8lx", trace_msg->info_val), 0);
- }
- static void kbasep_trace_dump_msg(struct kbase_device *kbdev, struct kbase_trace *trace_msg)
- {
- char buffer[DEBUG_MESSAGE_SIZE];
- kbasep_trace_format_msg(trace_msg, buffer, DEBUG_MESSAGE_SIZE);
- dev_dbg(kbdev->dev, "%s", buffer);
- }
- void kbasep_trace_add(struct kbase_device *kbdev, enum kbase_trace_code code, void *ctx, struct kbase_jd_atom *katom, u64 gpu_addr, u8 flags, int refcount, int jobslot, unsigned long info_val)
- {
- unsigned long irqflags;
- struct kbase_trace *trace_msg;
- spin_lock_irqsave(&kbdev->trace_lock, irqflags);
- trace_msg = &kbdev->trace_rbuf[kbdev->trace_next_in];
- /* Fill the message */
- trace_msg->thread_id = task_pid_nr(current);
- trace_msg->cpu = task_cpu(current);
- getnstimeofday(&trace_msg->timestamp);
- trace_msg->code = code;
- trace_msg->ctx = ctx;
- if (katom == NULL) {
- trace_msg->katom = false;
- } else {
- trace_msg->katom = true;
- trace_msg->atom_number = kbase_jd_atom_id(katom->kctx, katom);
- trace_msg->atom_udata[0] = katom->udata.blob[0];
- trace_msg->atom_udata[1] = katom->udata.blob[1];
- }
- trace_msg->gpu_addr = gpu_addr;
- trace_msg->jobslot = jobslot;
- trace_msg->refcount = MIN((unsigned int)refcount, 0xFF);
- trace_msg->info_val = info_val;
- trace_msg->flags = flags;
- /* Update the ringbuffer indices */
- kbdev->trace_next_in = (kbdev->trace_next_in + 1) & KBASE_TRACE_MASK;
- if (kbdev->trace_next_in == kbdev->trace_first_out)
- kbdev->trace_first_out = (kbdev->trace_first_out + 1) & KBASE_TRACE_MASK;
- /* Done */
- spin_unlock_irqrestore(&kbdev->trace_lock, irqflags);
- }
- void kbasep_trace_clear(struct kbase_device *kbdev)
- {
- unsigned long flags;
- spin_lock_irqsave(&kbdev->trace_lock, flags);
- kbdev->trace_first_out = kbdev->trace_next_in;
- spin_unlock_irqrestore(&kbdev->trace_lock, flags);
- }
- void kbasep_trace_dump(struct kbase_device *kbdev)
- {
- unsigned long flags;
- u32 start;
- u32 end;
- dev_dbg(kbdev->dev, "Dumping trace:\nsecs,nthread,cpu,code,ctx,katom,gpu_addr,jobslot,refcount,info_val");
- spin_lock_irqsave(&kbdev->trace_lock, flags);
- start = kbdev->trace_first_out;
- end = kbdev->trace_next_in;
- while (start != end) {
- struct kbase_trace *trace_msg = &kbdev->trace_rbuf[start];
- kbasep_trace_dump_msg(kbdev, trace_msg);
- start = (start + 1) & KBASE_TRACE_MASK;
- }
- dev_dbg(kbdev->dev, "TRACE_END");
- spin_unlock_irqrestore(&kbdev->trace_lock, flags);
- KBASE_TRACE_CLEAR(kbdev);
- }
- static void kbasep_trace_hook_wrapper(void *param)
- {
- struct kbase_device *kbdev = (struct kbase_device *)param;
- kbasep_trace_dump(kbdev);
- }
- void kbasep_trace_debugfs_init(struct kbase_device *kbdev)
- {
- }
- #else /* KBASE_TRACE_ENABLE */
- static int kbasep_trace_init(struct kbase_device *kbdev)
- {
- CSTD_UNUSED(kbdev);
- return 0;
- }
- static void kbasep_trace_term(struct kbase_device *kbdev)
- {
- CSTD_UNUSED(kbdev);
- }
- static void kbasep_trace_hook_wrapper(void *param)
- {
- CSTD_UNUSED(param);
- }
- void kbasep_trace_dump(struct kbase_device *kbdev)
- {
- CSTD_UNUSED(kbdev);
- }
- #endif /* KBASE_TRACE_ENABLE */
|