// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ /* *============================================================= * Include files *============================================================= */ /* system includes */ #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_OF) #include #include #include #include #include #include #endif #include "apusys_core.h" #include "apusys_power.h" //#include "apusys_power_cust.h" #include "apusys_debug_api.h" #include "mnoc_drv.h" #include "mnoc_qos.h" #include "mnoc_qos_sys.h" #include "mnoc_dbg.h" #include "mnoc_pmu.h" #include "mnoc_util.h" #include "mnoc_api.h" #ifdef MNOC_TAG_TP #include "mnoc_tag.h" #endif DEFINE_SPINLOCK(mnoc_spinlock); void __iomem *mnoc_base; void __iomem *mnoc_int_base; void __iomem *mnoc_apu_conn_base; void __iomem *mnoc_slp_prot_base1; void __iomem *mnoc_slp_prot_base2; bool mnoc_reg_valid; int mnoc_log_level; struct mutex mnoc_pwr_mtx; bool mnoc_pwr_is_on; #if MNOC_INT_ENABLE unsigned int mnoc_irq_number; bool is_first_isr_after_pwr_on; static struct work_struct mnoc_isr_work; struct mnoc_plat_drv mnoc_drv; struct apusys_core_info *mnoc_core_info; phys_addr_t get_apu_iommu_tfrp(unsigned int id) { return mnoc_drv.get_apu_iommu_tfrp(id); } EXPORT_SYMBOL(get_apu_iommu_tfrp); void mnoc_set_mni_pre_ultra(int dev_type, int dev_core, bool endis) { mnoc_drv.set_mni_pre_ultra(dev_type, dev_core, endis); } void mnoc_set_lt_guardian_pre_ultra(int dev_type, int dev_core, bool endis) { mnoc_drv.set_lt_guardian_pre_ultra(dev_type, dev_core, endis); } static void mnoc_isr_work_func(struct work_struct *work) { LOG_DEBUG("+\n"); #if MNOC_AEE_WARN_ENABLE mutex_lock(&mnoc_pwr_mtx); if (mnoc_pwr_is_on) apusys_reg_dump("apusys-mnoc", false); mutex_unlock(&mnoc_pwr_mtx); mnoc_aee_warn("MNOC", "MNOC Exception"); #endif mnoc_drv.print_int_sta(NULL); LOG_DEBUG("-\n"); } #endif static void mnoc_apusys_top_after_pwr_on(void *para) { unsigned long flags; LOG_DEBUG("+\n"); mnoc_pmu_reg_init(); infra2apu_sram_en(); mnoc_drv.hw_reinit(); apu_qos_on(); spin_lock_irqsave(&mnoc_spinlock, flags); #if MNOC_INT_ENABLE is_first_isr_after_pwr_on = true; #endif mnoc_reg_valid = true; spin_unlock_irqrestore(&mnoc_spinlock, flags); mutex_lock(&mnoc_pwr_mtx); mnoc_pwr_is_on = true; mutex_unlock(&mnoc_pwr_mtx); mnoc_drv.int_endis(true); LOG_DEBUG("-\n"); } static void mnoc_apusys_top_before_pwr_off(void *para) { unsigned long flags; LOG_DEBUG("+\n"); mnoc_drv.int_endis(false); spin_lock_irqsave(&mnoc_spinlock, flags); mnoc_reg_valid = false; spin_unlock_irqrestore(&mnoc_spinlock, flags); infra2apu_sram_dis(); apu_qos_off(); mutex_lock(&mnoc_pwr_mtx); mnoc_pwr_is_on = false; mutex_unlock(&mnoc_pwr_mtx); LOG_DEBUG("-\n"); } #if MNOC_INT_ENABLE /* * GIC SPI IRQ 406 is shared, need to return IRQ_NONE * if not triggered by mnoc */ static irqreturn_t mnoc_isr(int irq, void *dev_id) { unsigned long flags; int mnoc_irq_triggered = 0; LOG_DEBUG("+\n"); spin_lock_irqsave(&mnoc_spinlock, flags); /* prevent register access if apusys power off */ if (!mnoc_reg_valid) { spin_unlock_irqrestore(&mnoc_spinlock, flags); LOG_DEBUG("ISR can't access mnoc reg when APUSYS off\n"); return IRQ_NONE; } mnoc_irq_triggered = mnoc_drv.chk_int_status(); spin_unlock_irqrestore(&mnoc_spinlock, flags); if (mnoc_irq_triggered != 0) { LOG_DEBUG("INT triggered by mnoc\n"); if (mnoc_irq_triggered == 2) LOG_DEBUG("mnoc timeout\n"); if (mnoc_irq_triggered == 3 && is_first_isr_after_pwr_on) { LOG_ERR("check int status case 3 !!\n"); is_first_isr_after_pwr_on = false; mutex_lock(&mnoc_pwr_mtx); if (mnoc_pwr_is_on) apusys_reg_dump("apusys-mnoc", false); mutex_unlock(&mnoc_pwr_mtx); mnoc_aee_warn("MNOC", "MNOC Exception"); } /* Prevent overwhelming interrupts paralyzing system */ if (mnoc_irq_triggered == 1 && is_first_isr_after_pwr_on) { is_first_isr_after_pwr_on = false; schedule_work(&mnoc_isr_work); } return IRQ_HANDLED; } LOG_DEBUG("INT NOT triggered by mnoc\n"); LOG_DEBUG("-\n"); return IRQ_NONE; } #endif static int mnoc_probe(struct platform_device *pdev) { int ret = 0; struct device_node *node; struct apu_mnoc *p_mnoc = NULL; mnoc_reg_valid = false; mnoc_log_level = 0; LOG_DEBUG("+\n"); p_mnoc = kmalloc(sizeof(struct apu_mnoc), GFP_KERNEL); if (!p_mnoc) return -ENOMEM; p_mnoc->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, p_mnoc); #if MNOC_APU_PWR_CHK if (!apusys_power_check()) return -1; #endif mnoc_drv = *(struct mnoc_plat_drv *)of_device_get_match_data(&pdev->dev); mutex_init(&mnoc_pwr_mtx); mnoc_pwr_is_on = false; create_debugfs(mnoc_core_info->dbg_root); mnoc_qos_create_sys(&pdev->dev); spin_lock_init(&mnoc_spinlock); apu_qos_counter_init(&pdev->dev); mnoc_pmu_init(); mnoc_drv.init(); node = pdev->dev.of_node; if (!node) { LOG_ERR("get apusys_mnoc device node err\n"); return -ENODEV; } /* Setup IO addresses */ mnoc_base = of_iomap(node, 0); mnoc_int_base = of_iomap(node, 1); mnoc_apu_conn_base = of_iomap(node, 2); mnoc_slp_prot_base1 = of_iomap(node, 3); mnoc_slp_prot_base2 = of_iomap(node, 4); #if MNOC_INT_ENABLE INIT_WORK(&mnoc_isr_work, &mnoc_isr_work_func); mnoc_irq_number = irq_of_parse_and_map(node, 0); LOG_DEBUG("mnoc_irq_number = %d\n", mnoc_irq_number); /* set mnoc IRQ(GIC SPI pin shared with axi-reviser/devapc) */ ret = request_irq(mnoc_irq_number, mnoc_isr, //IRQF_TRIGGER_HIGH | IRQF_SHARED, irq_get_trigger_type(mnoc_irq_number) | IRQF_SHARED, APUSYS_MNOC_DEV_NAME, &pdev->dev); if (ret) LOG_ERR("IRQ register failed (%d)\n", ret); else LOG_DEBUG("Set IRQ OK.\n"); #endif ret = apu_power_callback_device_register(MNOC, mnoc_apusys_top_after_pwr_on, mnoc_apusys_top_before_pwr_off); if (ret) { LOG_ERR("apu_power_callback_device_register return error(%d)\n", ret); return -EINVAL; } #ifdef MNOC_TAG_TP mnoc_init_drv_tags(); #endif LOG_DEBUG("-\n"); return ret; } static int mnoc_suspend(struct platform_device *pdev, pm_message_t state) { /* apu_qos_suspend(); */ /* mnoc_pmu_suspend(); */ return 0; } static int mnoc_resume(struct platform_device *pdev) { /* apu_qos_resume(); */ /* mnoc_pmu_resume(); */ return 0; } static int mnoc_remove(struct platform_device *pdev) { struct device_node *node = NULL; struct apu_mnoc *p_mnoc = NULL; LOG_DEBUG("+\n"); #if MNOC_APU_PWR_CHK if (!apusys_power_check()) return 0; #endif apu_power_callback_device_unregister(MNOC); remove_debugfs(); mnoc_qos_remove_sys(&pdev->dev); apu_qos_counter_destroy(&pdev->dev); mnoc_pmu_exit(); node = pdev->dev.of_node; if (!node) { LOG_ERR("get apusys_mnoc device node err\n"); return -ENODEV; } #if MNOC_INT_ENABLE free_irq(mnoc_irq_number, &pdev->dev); cancel_work_sync(&mnoc_isr_work); #endif iounmap(mnoc_base); iounmap(mnoc_int_base); iounmap(mnoc_apu_conn_base); iounmap(mnoc_slp_prot_base1); iounmap(mnoc_slp_prot_base2); p_mnoc = dev_get_drvdata(&pdev->dev); kfree(p_mnoc); #ifdef MNOC_TAG_TP mnoc_exit_drv_tags(); #endif LOG_DEBUG("-\n"); return 0; } static struct platform_driver mnoc_driver = { .probe = mnoc_probe, .remove = mnoc_remove, .suspend = mnoc_suspend, .resume = mnoc_resume, .driver = { .name = APUSYS_MNOC_DEV_NAME, .owner = THIS_MODULE, }, }; int mnoc_init(struct apusys_core_info *info) { int ret = 0; LOG_DEBUG("mnoc driver init start\n"); mnoc_core_info = info; memset(&mnoc_drv, 0, sizeof(struct mnoc_plat_drv)); mnoc_driver.driver.of_match_table = mnoc_util_get_device_id(); ret = mnoc_rv_setup(info); if (ret) { LOG_ERR("failed to set mnoc rv\n"); goto out; } if (platform_driver_register(&mnoc_driver)) { LOG_ERR("failed to register %s driver\n", APUSYS_MNOC_DEV_NAME); return -ENODEV; } out: return ret; } void mnoc_exit(void) { LOG_DEBUG("de-initialization\n"); platform_driver_unregister(&mnoc_driver); mnoc_rv_clear(); }