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

Commit df021914 authored by Manaf Meethalavalappu Pallikunhi's avatar Manaf Meethalavalappu Pallikunhi
Browse files

drivers: thermal: Add support for RPM SMD cooling device



Add support for RPM SMD cooling device to thermal framework.
It can send multiple temperature bands to RPM hardware when
predefined trip is violated. It uses RPM SMD interface to
communicate to RPM hardware.

Change-Id: Iec1f8ecc9abb883203039c2952ad918d01eb1edb
Signed-off-by: default avatarManaf Meethalavalappu Pallikunhi <manafm@codeaurora.org>
parent 4dd6175f
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
Qualcomm Technologies, Inc. RPM SMD cooling device

The RPM shared memory(SMD) cooling device, will be used to set
different thermal band level to RPM hardware. When threshold violation
occurs, RPM SMD cooling device sends pre-configured thermal band level
to RPM hardware via SMD.

Required Parameters:
- compatible:
	Usage: required
	Value type: <string>
	Definition: should be "qcom,rpm-smd-cooling-device"

- #cooling-cells:
	Usage: required
	Value type: <integer>
	Definition: Must be 2. This is required by of-thermal and refer the doc
		<devicetree/bindings/thermal/thermal.txt> for more details.

Example:

&rpm_bus {
	rpm_smd_cdev: rpm-smd-cdev {
		compatible = "qcom,rpm-smd-cooling-device";
		#cooling-cells = <2>;
	};
};
+11 −0
Original line number Diff line number Diff line
@@ -114,3 +114,14 @@ config QTI_CX_IPEAK_COOLING_DEVICE
	  on the CX rail.

	  If you want this support, you should say Y here.

config QTI_RPM_SMD_COOLING_DEVICE
	bool "Qualcomm Technologies Inc. RPM SMD cooling device driver"
	depends on MSM_RPM_SMD && THERMAL_OF
	help
	  This implements a mitigation device to send temperature band
	  level to RPM hardware via SMD protocol. This mitigation device
	  will be used by temperature reliability rules to restrict a
	  railway at predefined voltage corner using RPM hardware.

	  If you want this support, you should say Y here.
+1 −0
Original line number Diff line number Diff line
@@ -10,3 +10,4 @@ obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o
obj-$(CONFIG_QTI_ADC_TM) += adc-tm.o adc-tm-common.o adc-tm5.o
obj-$(CONFIG_QTI_CX_IPEAK_COOLING_DEVICE) += cx_ipeak_cdev.o
obj-$(CONFIG_QTI_RPM_SMD_COOLING_DEVICE) += rpm_smd_cooling_device.o
+205 −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.
 */

#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__

#include <linux/platform_device.h>
#include <linux/thermal.h>
#include <linux/err.h>
#include <soc/qcom/rpm-smd.h>

#define RPM_SMD_CDEV_DRIVER   "rpm-smd-cooling-device"
#define RPM_SMD_RES_TYPE       0x6d726874
#define RPM_SMD_RES_ID         0
#define RPM_SMD_KEY            1

enum rpm_smd_temp_band {
	RPM_SMD_COLD_CRITICAL = 1,
	RPM_SMD_COLD,
	RPM_SMD_COOL,
	RPM_SMD_NORMAL,
	RPM_SMD_WARM,
	RPM_SMD_HOT,
	RPM_SMD_HOT_CRITICAL,
	RPM_SMD_TEMP_MAX_NR,
};

struct rpm_smd_cdev {
	struct thermal_cooling_device	*cool_dev;
	char				dev_name[THERMAL_NAME_LENGTH];
	unsigned int			state;
	struct msm_rpm_request		*rpm_handle;
};

static int rpm_smd_send_request_to_rpm(struct rpm_smd_cdev *rpm_smd_dev,
			unsigned int state)
{
	unsigned int band;
	int msg_id, ret;

	if (!rpm_smd_dev || !rpm_smd_dev->rpm_handle) {
		pr_err("Invalid RPM SMD handle\n");
		return -EINVAL;
	}

	if (rpm_smd_dev->state == state)
		return 0;

	/* if state is zero, then send RPM_SMD_NORMAL band */
	if (!state)
		band = RPM_SMD_NORMAL;
	else
		band = state;

	ret = msm_rpm_add_kvp_data(rpm_smd_dev->rpm_handle, RPM_SMD_KEY,
		(const uint8_t *)&band, (int)sizeof(band));
	if (ret) {
		pr_err("Adding KVP data failed. err:%d\n", ret);
		return ret;
	}

	msg_id = msm_rpm_send_request(rpm_smd_dev->rpm_handle);
	if (!msg_id) {
		pr_err("RPM SMD send request failed\n");
		return -ENXIO;
	}

	ret = msm_rpm_wait_for_ack(msg_id);
	if (ret) {
		pr_err("RPM SMD wait for ACK failed. err:%d\n", ret);
		return ret;
	}
	rpm_smd_dev->state = state;

	pr_debug("Requested RPM SMD band:%d for %s\n", band,
		rpm_smd_dev->dev_name);

	return ret;
}

static int rpm_smd_get_max_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	*state = RPM_SMD_TEMP_MAX_NR - 1;

	return 0;
}

static int rpm_smd_set_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
	struct rpm_smd_cdev *rpm_smd_dev = cdev->devdata;
	int ret = 0;

	if (state > (RPM_SMD_TEMP_MAX_NR - 1))
		state = RPM_SMD_TEMP_MAX_NR - 1;

	ret = rpm_smd_send_request_to_rpm(rpm_smd_dev, (unsigned int)state);
	if (ret)
		return ret;

	return ret;
}

static int rpm_smd_get_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	struct rpm_smd_cdev *rpm_smd_dev = cdev->devdata;

	*state = rpm_smd_dev->state;

	return 0;
}

static struct thermal_cooling_device_ops rpm_smd_device_ops = {
	.get_max_state = rpm_smd_get_max_state,
	.get_cur_state = rpm_smd_get_cur_state,
	.set_cur_state = rpm_smd_set_cur_state,
};

static int rpm_smd_cdev_remove(struct platform_device *pdev)
{
	 struct rpm_smd_cdev *rpm_smd_dev =
		(struct rpm_smd_cdev *)dev_get_drvdata(&pdev->dev);

	if (rpm_smd_dev) {
		if (rpm_smd_dev->cool_dev)
			thermal_cooling_device_unregister(
					rpm_smd_dev->cool_dev);

		rpm_smd_send_request_to_rpm(rpm_smd_dev, RPM_SMD_NORMAL);
		msm_rpm_free_request(rpm_smd_dev->rpm_handle);
	}

	return 0;
}

static int rpm_smd_cdev_probe(struct platform_device *pdev)
{
	struct rpm_smd_cdev *rpm_smd_dev;
	int ret = 0;
	struct device_node *np;

	np = dev_of_node(&pdev->dev);
	if (!np) {
		dev_err(&pdev->dev,
			"of node not available for rpm smd cooling device\n");
		return -EINVAL;
	}

	rpm_smd_dev = devm_kzalloc(&pdev->dev, sizeof(*rpm_smd_dev),
					GFP_KERNEL);
	if (!rpm_smd_dev)
		return -ENOMEM;

	rpm_smd_dev->rpm_handle = msm_rpm_create_request(MSM_RPM_CTX_ACTIVE_SET,
			RPM_SMD_RES_TYPE, RPM_SMD_RES_ID, 1);
	if (!rpm_smd_dev->rpm_handle) {
		dev_err(&pdev->dev, "Creating RPM SMD request handle failed\n");
		return -ENXIO;
	}

	strlcpy(rpm_smd_dev->dev_name, np->name, THERMAL_NAME_LENGTH);

	/* Be pro-active and mitigate till we get first vote from TF */
	rpm_smd_send_request_to_rpm(rpm_smd_dev, RPM_SMD_COLD);

	rpm_smd_dev->cool_dev = thermal_of_cooling_device_register(
					np, rpm_smd_dev->dev_name, rpm_smd_dev,
					&rpm_smd_device_ops);
	if (IS_ERR(rpm_smd_dev->cool_dev)) {
		ret = PTR_ERR(rpm_smd_dev->cool_dev);
		dev_err(&pdev->dev, "rpm_smd cdev register err:%d\n", ret);
		rpm_smd_dev->cool_dev = NULL;
		return ret;
	}

	dev_set_drvdata(&pdev->dev, rpm_smd_dev);

	return ret;
}

static const struct of_device_id rpm_smd_cdev_of_match[] = {
	{.compatible = "qcom,rpm-smd-cooling-device", },
	{}
};

static struct platform_driver rpm_smd_cdev_driver = {
	.driver = {
		.name = RPM_SMD_CDEV_DRIVER,
		.of_match_table = rpm_smd_cdev_of_match,
	},
	.probe = rpm_smd_cdev_probe,
	.remove = rpm_smd_cdev_remove,
};

builtin_platform_driver(rpm_smd_cdev_driver);