// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include /* min() */ #include #include /* copy_to_user() */ #include /* TASK_INTERRUPTIBLE/signal_pending/schedule */ #include #include #include #include /* ioremap() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "memory-amms.h" #define AMMS_PENDING_DRDI_FREE_BIT (1<<0) #ifdef CONFIG_OF static const struct of_device_id amms_of_ids[] = { { .compatible = "mediatek,amms", }, {} }; #endif static bool amms_static_free; int free_reserved_memory(phys_addr_t start_phys, phys_addr_t end_phys) { phys_addr_t pos; unsigned long pages = 0; if (end_phys <= start_phys) { pr_notice("%s end_phys is smaller than start_phys start_phys:0x%pa end_phys:0x%pa\n" , __func__, &start_phys, &end_phys); return -1; } for (pos = start_phys; pos < end_phys; pos += PAGE_SIZE, pages++) free_reserved_page(phys_to_page(pos)); if (pages) pr_info("Freeing modem memory: %ldK from phys %llx\n", pages << (PAGE_SHIFT - 10), (unsigned long long)start_phys); return 0; } EXPORT_SYMBOL(free_reserved_memory); static irqreturn_t amms_legacy_handler(int irq, void *data) { phys_addr_t addr = 0, length = 0; struct arm_smccc_res res; if (!amms_static_free) { arm_smccc_smc(MTK_SIP_KERNEL_AMMS_GET_FREE_ADDR, 0, 0, 0, 0, 0, 0, 0, &res); addr = res.a0; arm_smccc_smc( MTK_SIP_KERNEL_AMMS_GET_FREE_LENGTH, 0, 0, 0, 0, 0, 0, 0, &res); length = res.a0; if (pfn_valid(__phys_to_pfn(addr)) && pfn_valid(__phys_to_pfn( addr + length - 1))) { pr_info("%s:addr=%pa length=%pa\n", __func__, &addr, &length); free_reserved_memory(addr, addr+length); amms_static_free = true; } else { pr_info("AMMS: error addr and length is not set properly\n"); pr_info("can not free_reserved_memory\n"); } } else { pr_info("amms: static memory already free\n"); } return IRQ_HANDLED; } static irqreturn_t amms_handler(int irq, void *data) { phys_addr_t addr = 0, length = 0; unsigned long long pending; struct arm_smccc_res res; arm_smccc_smc(MTK_SIP_KERNEL_AMMS_GET_PENDING, 0, 0, 0, 0, 0, 0, 0, &res); pending = res.a0; pr_info("%s:pending = 0x%llx\n", __func__, pending); pr_info("%s:pending = %lld\n", __func__, (long long)pending); if (((long long)pending) != AMMS_PENDING_DRDI_FREE_BIT) { pr_info("%s:Not support pending\n", __func__); return amms_legacy_handler(irq, data); } if (pending & AMMS_PENDING_DRDI_FREE_BIT) { pr_info("%s:Support pending\n", __func__); /*below part is for staic memory free */ if (!amms_static_free) { arm_smccc_smc(MTK_SIP_KERNEL_AMMS_GET_FREE_ADDR, 0, 0, 0, 0, 0, 0, 0, &res); addr = res.a0; arm_smccc_smc( MTK_SIP_KERNEL_AMMS_GET_FREE_LENGTH, 0, 0, 0, 0, 0, 0, 0, &res); length = res.a0; if (pfn_valid(__phys_to_pfn(addr)) && pfn_valid(__phys_to_pfn( addr + length - 1))) { pr_info("%s:addr=%pa length=%pa\n", __func__, &addr, &length); free_reserved_memory(addr, addr+length); amms_static_free = true; arm_smccc_smc(MTK_SIP_KERNEL_AMMS_ACK_PENDING, AMMS_PENDING_DRDI_FREE_BIT, 0, 0, 0, 0, 0, 0, &res); } else { pr_info("AMMS: error addr and length is not set properly\n"); pr_info("can not free_reserved_memory\n"); } } else { arm_smccc_smc(MTK_SIP_KERNEL_AMMS_ACK_PENDING, AMMS_PENDING_DRDI_FREE_BIT, 0, 0, 0, 0, 0, 0, &res); pr_info("amms: static memory already free, should not happened\n"); } } if (pending & (~(AMMS_PENDING_DRDI_FREE_BIT))) pr_info("amms:unknown pending interrupt\n"); return IRQ_HANDLED; } static int amms_probe(struct platform_device *pdev) { struct device_node *node; int amms_irq_num; struct device *amms_dev; amms_irq_num = platform_get_irq(pdev, 0); if (amms_irq_num < 0) { pr_info("Fail to get amms irq number from device tree\n"); WARN_ON(amms_irq_num == -ENXIO); return -EINVAL; } node = of_find_compatible_node(NULL, NULL, "mediatek,amms"); if (!node) { pr_info("%s, amms not exist\n", __func__); return -EINVAL; } amms_dev = &pdev->dev; if (devm_request_threaded_irq( &pdev->dev, amms_irq_num, NULL, amms_handler, IRQF_ONESHOT|IRQF_TRIGGER_NONE, "amms_irq", NULL) != 0) { pr_info("Fail to request amms_irq interrupt!\n"); return -EBUSY; } return 0; } static struct platform_driver amms_driver = { .probe = amms_probe, .driver = { .name = "amms", .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = amms_of_ids, #endif }, }; module_platform_driver(amms_driver); MODULE_DESCRIPTION("MEDIATEK Module AMMS Driver"); MODULE_AUTHOR(""); MODULE_LICENSE("GPL");