// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ /* *============================================================= * Include files *============================================================= */ /* system includes */ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OF #include #include #include #include #include #include #endif #include "apusys_power.h" #include "apusys_power_cust.h" #include "apusys_dbg.h" #include "mnoc_drv.h" #include "mnoc_hw.h" #include "mnoc_qos.h" #include "mnoc_qos_sys.h" #include "mnoc_dbg.h" #include "mnoc_pmu.h" #include "mnoc_option.h" 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; 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(); mutex_unlock(&mnoc_pwr_mtx); mnoc_aee_warn("MNOC", "MNOC Exception"); #endif 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_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_int_endis(true); LOG_DEBUG("-\n"); } static void mnoc_apusys_top_before_pwr_off(void *para) { unsigned long flags; LOG_DEBUG("+\n"); mnoc_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_check_int_status(); spin_unlock_irqrestore(&mnoc_spinlock, flags); if (mnoc_irq_triggered != 0) { LOG_DEBUG("INT triggered by mnoc\n"); /* 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; #if MNOC_INT_ENABLE struct device *dev = &pdev->dev; #endif struct device_node *node, *sub_node; struct platform_device *sub_pdev; struct apu_mnoc *p_mnoc = NULL; mnoc_reg_valid = false; mnoc_log_level = 0; LOG_DEBUG("+\n"); p_mnoc = kmalloc(sizeof(*p_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 0; #endif /* make sure apusys_power driver initiallized before * calling apu_power_callback_device_register */ sub_node = of_find_compatible_node(NULL, NULL, "mediatek,apusys_power"); if (!sub_node) { LOG_ERR("DT,mediatek,apusys_power not found\n"); return -EINVAL; } sub_pdev = of_find_device_by_node(sub_node); if (!sub_pdev || !sub_pdev->dev.driver) { LOG_DEBUG("Waiting for %s\n", sub_node->full_name); return -EPROBE_DEFER; } mutex_init(&mnoc_pwr_mtx); mnoc_pwr_is_on = false; create_debugfs(); mnoc_qos_create_sys(&pdev->dev); spin_lock_init(&mnoc_spinlock); apu_qos_counter_init(&pdev->dev); mnoc_pmu_init(); mnoc_hw_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, APUSYS_MNOC_DEV_NAME, 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; } 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) { #if MNOC_INT_ENABLE struct device *dev = &pdev->dev; #endif 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(); mnoc_hw_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, 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); LOG_DEBUG("-\n"); return 0; } static const struct of_device_id apusys_mnoc_of_match[] = { {.compatible = "mediatek,apusys_mnoc",}, {/* end of list */}, }; 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, .of_match_table = apusys_mnoc_of_match, }, }; static int __init mnoc_init(void) { LOG_DEBUG("driver init start\n"); if (platform_driver_register(&mnoc_driver)) { LOG_ERR("failed to register %s driver", APUSYS_MNOC_DEV_NAME); return -ENODEV; } return 0; } static void __exit mnoc_exit(void) { LOG_DEBUG("de-initialization\n"); platform_driver_unregister(&mnoc_driver); } module_init(mnoc_init); module_exit(mnoc_exit); MODULE_DESCRIPTION("MTK APUSYS MNoC Driver"); MODULE_AUTHOR("SPT1/SS5"); MODULE_LICENSE("GPL");