diff options
| -rw-r--r-- | drivers/soc/qcom/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/soc/qcom/Makefile | 1 | ||||
| -rw-r--r-- | drivers/soc/qcom/pasr.c | 95 |
3 files changed, 103 insertions, 0 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index b92c217dc1b5..1b68d1602c94 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -2,6 +2,13 @@ # QCOM Soc drivers # source "drivers/soc/qcom/hab/Kconfig" +config MSM_PASR + bool "MSM DDR Partial Array Self-Refresh Driver" + help + RPM controls DDR functionaliy. This driver + is an interface for linux memory hotplug to RPM + for start/stop self-refresh of hot added or removed + memory in DDR. config MSM_INRUSH_CURRENT_MITIGATION bool "Inrush-current mitigation Driver" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 8605b750c11d..5e565c863889 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MSM_PFE_WA) += pfe-wa.o obj-$(CONFIG_ARCH_MSM8996) += msm_cpu_voltage.o obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o +obj-$(CONFIG_MSM_PASR) += pasr.o ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += subsystem_notif.o diff --git a/drivers/soc/qcom/pasr.c b/drivers/soc/qcom/pasr.c new file mode 100644 index 000000000000..da85dd50529e --- /dev/null +++ b/drivers/soc/qcom/pasr.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/memory.h> +#include <linux/module.h> +#include <soc/qcom/rpm-smd.h> + +struct memory_refresh_request { + u64 start; /* Lower bit signifies action + * 0 - disable self-refresh + * 1 - enable self-refresh + * upper bits are for base address + */ + size_t size; /* size of memory region */ +}; +#define RPM_DDR_REQ 0x726464 + +static void mem_region_refresh_control(unsigned long pfn, + unsigned long nr_pages, bool enable) +{ + struct memory_refresh_request mem_req; + struct msm_rpm_kvp rpm_kvp; + int ret; + + mem_req.start = enable; + mem_req.start |= pfn << PAGE_SHIFT; + mem_req.size = nr_pages * PAGE_SIZE; + + rpm_kvp.key = RPM_DDR_REQ; + rpm_kvp.data = (void *)&mem_req; + rpm_kvp.length = sizeof(mem_req); + + ret = msm_rpm_send_message(MSM_RPM_CTX_ACTIVE_SET, RPM_DDR_REQ, 0, + &rpm_kvp, 1); + if (ret) + pr_err("PASR: Failed to send rpm message\n"); +} + +static int pasr_callback(struct notifier_block *self, + unsigned long action, void *arg) +{ + struct memory_notify *mn = arg; + unsigned long start, end; + + start = SECTION_ALIGN_DOWN(mn->start_pfn); + end = SECTION_ALIGN_UP(mn->start_pfn + mn->nr_pages); + + if ((start != mn->start_pfn) || (end != mn->start_pfn + mn->nr_pages)) { + pr_err("PASR: %s pfn not aligned to section\n", __func__); + pr_err("PASR: start pfn = %lu end pfn = %lu\n", + mn->start_pfn, mn->start_pfn + mn->nr_pages); + return -EINVAL; + } + + switch (action) { + case MEM_GOING_ONLINE: + pr_debug("PASR: MEM_GOING_ONLINE : start = %lx end = %lx", + mn->start_pfn << PAGE_SHIFT, + (mn->start_pfn + mn->nr_pages) << PAGE_SHIFT); + mem_region_refresh_control(mn->start_pfn, mn->nr_pages, true); + break; + case MEM_OFFLINE: + pr_debug("PASR: MEM_OFFLINE: start = %lx end = %lx", + mn->start_pfn << PAGE_SHIFT, + (mn->start_pfn + mn->nr_pages) << PAGE_SHIFT); + mem_region_refresh_control(mn->start_pfn, mn->nr_pages, false); + break; + case MEM_CANCEL_ONLINE: + pr_debug("PASR: MEM_CANCEL_ONLINE: start = %lx end = %lx", + mn->start_pfn << PAGE_SHIFT, + (mn->start_pfn + mn->nr_pages) << PAGE_SHIFT); + mem_region_refresh_control(mn->start_pfn, mn->nr_pages, false); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static int __init pasr_module_init(void) +{ + return hotplug_memory_notifier(pasr_callback, 0); +} +late_initcall(pasr_module_init); |
