Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58a8e739 authored by Arun KS's avatar Arun KS
Browse files

soc: qcom: pasr: Add MSM PASR driver



Partial Array Self-Refresh driver is used to interface
with rpm to vote/unvote on memory self-refresh from HLOS.

Driver listens to memory hotplug notifications and decides
to vote or unvote depending on memory online and offline.
This vote is considered by RPM to avoid self-refresh on
offlined DDR segments. And hence a reduce in power consumption.

Change-Id: Ida2b36d671c6379dc3c07258a95cf15ae07a4bc0
Signed-off-by: default avatarArun KS <arunks@codeaurora.org>
parent d0d5319f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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"
+1 −0
Original line number Diff line number Diff line
@@ -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
+95 −0
Original line number Diff line number Diff line
/*
 * 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);