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

Commit 0a4a45a1 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: Add RPM SMD Driver"

parents 624ad22c 6cd07873
Loading
Loading
Loading
Loading
+157 −0
Original line number Diff line number Diff line
Introduction
============

Resource Power Manager (RPM)

RPM is a dedicated hardware engine for managing shared SoC resources,
which includes buses, clocks, power rails, etc.  The goal of RPM is
to achieve the maximum power savings while satisfying the SoC's
operational and performance requirements.  RPM accepts resource
requests from multiple RPM masters.  It arbitrates and aggregates the
requests, and configures the shared resources.  The RPM masters are
the application processor, the modem processor, as well as some
hardware accelerators.

The RPM driver provides an API for interacting with RPM.  Kernel code
calls the RPM driver to request RPM-managed, shared resources.
Kernel code can also register with the driver for RPM notifications,
which are sent when the status of shared resources change.

Hardware description
====================

RPM exposes a separate region of registers to each of the RPM masters.
In general, each register represents some shared resource(s).  At a
very basic level, a master requests resources by writing to the
registers, then generating an interrupt to RPM.  RPM processes the
request, writes acknowledgment to the registers, then generates an
interrupt to the master.

In addition to the master-specific regions, RPM also exposes a shared
region that contains the current status of the shared resources.  Only
RPM can write to the status region, but every master can read from it.

RPM contains internal logics that aggregate and arbitrate among
requests from the various RPM masters.  It interfaces with the PMIC,
the bus arbitration block, and the clock controller block in order to
configure the shared resources.

Software description
====================

The RPM driver encapsulates the low level RPM interactions, which
rely on reading/writing registers and generating/processing
interrupts, and provides a higher level synchronuous set/clear/get
interface.  Most functions take an array of id-value pairs.
The ids identify the RPM registers which would correspond to some
RPM resources, the values specify the new resource values.

The RPM driver synchronizes accesses to RPM.  It protects against
simultaneous accesses from multiple tasks, on SMP cores, in task
contexts, and in atomic contexts.

Design
======

Design goals:
- Encapsulate low level RPM interactions.
- Provide a synchronuous set/clear/get interface.
- Synchronize simultaneous software accesses to RPM.

Power Management
================

RPM is part of the power management architecture for MSM 8660.  RPM
manages shared system resources to lower system power.

SMP/multi-core
==============

The RPM driver uses mutex to synchronize client accesses among tasks.
It uses spinlocks to synchronize accesses from atomic contexts and
SMP cores.

Security
========

None.

Performance
===========

None.

Interface
=========

msm_rpm_get_status():
The function reads the shared status region and returns the current
resource values, which are the arbitrated/aggregated results across
all RPM masters.

msm_rpm_set():
The function makes a resource request to RPM.

msm_rpm_set_noirq():
The function is similar to msm_rpm_set() except that it must be
called with interrupts masked.  If possible, use msm_rpm_set()
instead, to maximize CPU throughput.

msm_rpm_clear():
The function makes a resource request to RPM to clear resource values.
Once the values are cleared, the resources revert back to their default
values for this RPM master.  RPM internally uses the default values as
the requests from this RPM master when arbitrating and aggregating with
requests from other RPM masters.

msm_rpm_clear_noirq():
The function is similar to msm_rpm_clear() except that it must be
called with interrupts masked.  If possible, use msm_rpm_clear()
instead, to maximize CPU throughput.

msm_rpm_register_notification():
The function registers for RPM notification.  When the specified
resources change their status on RPM, RPM sends out notifications
and the driver will "up" the semaphore in struct
msm_rpm_notification.

msm_rpm_unregister_notification():
The function unregisters a notification.

msm_rpm_init():
The function initializes the RPM driver with platform specific data.

Driver parameters
=================

None.

Config options
==============

MSM_RPM

Dependencies
============

None.

User space utilities
====================

None.

Other
=====

None.

Known issues
============

None.

To do
=====

None.
+41 −0
Original line number Diff line number Diff line
Resource Power Manager(RPM)

RPM is a dedicated hardware engine for managing shared SoC resources,
which includes buses, clocks, power rails, etc.  The goal of RPM is
to achieve the maximum power savings while satisfying the SoC's
operational and performance requirements.  RPM accepts resource
requests from multiple RPM masters.  It arbitrates and aggregates the
requests, and configures the shared resources.  The RPM masters are
the application processor, the modem processor, as well as hardware
accelerators. The RPM driver communicates with the hardware engine using
SMD.

The devicetree representation of the SPM block should be:

Required properties

- compatible: "qcom,rpm-smd" or "qcom,rpm-glink"
- rpm-channel-name: The string corresponding to the channel name of the
			peripheral subsystem. Required for both smd and
			glink transports.
- rpm-channel-type: The interal SMD edge for this subsystem found in
			<soc/qcom/smd.h>
- qcom,glink-edge: Logical name of the remote subsystem. This is a required
			property when rpm-smd driver using glink as trasport.

Optional properties
- rpm-standalone: Allow RPM driver to run in standalone mode irrespective of RPM
			channel presence.
- reg: Contains the memory address at which rpm messaging format version is
  stored. If this field is not present, the target only supports v0 format.

Example:

	qcom,rpm-smd@68150 {
		compatible = "qcom,rpm-smd", "qcom,rpm-glink";
		reg = <0x68150 0x3200>;
		qcom,rpm-channel-name = "rpm_requests";
		qcom,rpm-channel-type = 15; /* SMD_APPS_RPM */
		qcom,glink-edge = "rpm";
	}
}
+8 −0
Original line number Diff line number Diff line
@@ -406,6 +406,14 @@ config MINIDUMP_MAX_ENTRIES
	help
	  This defines maximum number of entries to be allocated for application
	  subsytem in Minidump table.
config MSM_RPM_SMD
	bool "RPM driver using SMD protocol"
	help
	  RPM is the dedicated hardware engine for managing shared SoC
	  resources. This config adds driver support for using SMD as a
	  transport layer communication with RPM hardware. It also selects
	  the MSM_MPM config that programs the MPM module to monitor interrupts
	  during sleep modes.

config QCOM_BUS_SCALING
	bool "Bus scaling driver"
+4 −0
Original line number Diff line number Diff line
@@ -75,3 +75,7 @@ ifdef CONFIG_QTI_RPMH_API
endif
obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o
obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
obj-$(CONFIG_MSM_RPM_SMD)   +=  rpm-smd.o
ifdef CONFIG_DEBUG_FS
obj-$(CONFIG_MSM_RPM_SMD)   +=  rpm-smd-debug.o
endif
+151 −0
Original line number Diff line number Diff line
/* Copyright (c) 2013-2014, 2017,  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.
 */

#define pr_fmt(fmt) "rpm-smd-debug: %s(): " fmt, __func__

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <soc/qcom/rpm-smd.h>

#define MAX_MSG_BUFFER 350
#define MAX_KEY_VALUE_PAIRS 20

static struct dentry *rpm_debugfs_dir;

static u32 string_to_uint(const u8 *str)
{
	int i, len;
	u32 output = 0;

	len = strnlen(str, sizeof(u32));
	for (i = 0; i < len; i++)
		output |= str[i] << (i * 8);

	return output;
}

static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer,
						size_t count, loff_t *position)
{
	char buf[MAX_MSG_BUFFER], rsc_type_str[6] = {}, rpm_set[8] = {},
						key_str[6] = {};
	int i, pos = -1, set = -1, nelems = -1;
	char *cmp;
	uint32_t rsc_type = 0, rsc_id = 0, key = 0, data = 0;
	struct msm_rpm_request *req;

	count = min(count, sizeof(buf) - 1);
	if (copy_from_user(&buf, user_buffer, count))
		return -EFAULT;
	buf[count] = '\0';
	cmp = strstrip(buf);

	if (sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str,
				&rsc_id, &nelems, &pos) != 4) {
		pr_err("Invalid number of arguments passed\n");
		goto err;
	}

	if (strlen(rpm_set) > 6 || strlen(rsc_type_str) > 4) {
		pr_err("Invalid value of set or resource type\n");
		goto err;
	}

	if (!strcmp(rpm_set, "active"))
		set = 0;
	else if (!strcmp(rpm_set, "sleep"))
		set = 1;

	rsc_type = string_to_uint(rsc_type_str);

	if (set < 0 || nelems < 0) {
		pr_err("Invalid value of set or nelems\n");
		goto err;
	}
	if (nelems > MAX_KEY_VALUE_PAIRS) {
		pr_err("Exceeded max no of key-value entries\n");
		goto err;
	}

	req = msm_rpm_create_request(set, rsc_type, rsc_id, nelems);
	if (!req)
		return -ENOMEM;

	for (i = 0; i < nelems; i++) {
		cmp += pos;
		if (sscanf(cmp, "%5s %n", key_str, &pos) != 1) {
			pr_err("Invalid number of arguments passed\n");
			goto err;
		}

		if (strlen(key_str) > 4) {
			pr_err("Key value cannot be more than 4 charecters");
			goto err;
		}
		key = string_to_uint(key_str);
		if (!key) {
			pr_err("Key values entered incorrectly\n");
			goto err;
		}

		cmp += pos;
		if (sscanf(cmp, "%u %n", &data, &pos) != 1) {
			pr_err("Invalid number of arguments passed\n");
			goto err;
		}

		if (msm_rpm_add_kvp_data(req, key,
				(void *)&data, sizeof(data)))
			goto err_request;
	}

	if (msm_rpm_wait_for_ack(msm_rpm_send_request(req)))
		pr_err("Sending the RPM message failed\n");

err_request:
	msm_rpm_free_request(req);
err:
	return count;
}

static const struct file_operations rsc_ops = {
	.write = rsc_ops_write,
};

static int __init rpm_smd_debugfs_init(void)
{
	rpm_debugfs_dir = debugfs_create_dir("rpm_send_msg", NULL);
	if (!rpm_debugfs_dir)
		return -ENOMEM;

	if (!debugfs_create_file("message", 0200, rpm_debugfs_dir, NULL,
								&rsc_ops))
		return -ENOMEM;

	return 0;
}
late_initcall(rpm_smd_debugfs_init);

static void __exit rpm_smd_debugfs_exit(void)
{
	debugfs_remove_recursive(rpm_debugfs_dir);
}
module_exit(rpm_smd_debugfs_exit);

MODULE_DESCRIPTION("RPM SMD Debug Driver");
MODULE_LICENSE("GPL v2");
Loading