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

Commit 3b48e5e0 authored by Ram Chandrasekar's avatar Ram Chandrasekar
Browse files

thermal: regulator_cooling: Add AOP based regulator cooling device



QTI chipsets need to place a voltage floor restriction at low
temperature as per the device operating spec. In order to place the
floor voltage restriction for regulators, AOP has to be notified with
when the low temperature reaches.

The new regulator cooling device will use the QMP messaging interface to
place a voltage floor restriction.

Change-Id: I0a3b878b356bab55ee1bb6458ea5c4016996a194
Signed-off-by: default avatarRam Chandrasekar <rkumbako@codeaurora.org>
parent e13b3810
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
RPMh regulator cooling device.

The RPMh regulator cooling device, will be used to place a voltage floor
restriction on a rail. This cooling device will use a QMP AOP mail box to send
the message to apply and clear voltage floor restriction.

The cooling device node should be a child of the regulator devicetree node,
which it is trying to place the floor restriction.

Properties:

- compatible:
	Usage: required
	Value type: <string>
	Definition: shall be "qcom,rpmh-reg-cdev"

- qcom,reg-resource-name:
	Usage: required
	Value type: <string>
	Definition: The regulator resource name to be used for communicating
			with RPMh. This value should be any of the below
			resource name,
			cx -> For CX rail,
			mx -> For MX rail,
			ebi -> For EBI rail.

- mboxes:
	Usage: required
	Value type: <phandle>
	Definition: A phandle to the QMP AOP mail box, that needs to be used
			for sending the floor restriction message.

- #cooling-cells: Must be 2. Please refer to
			<devicetree/bindings/thermal/thermal.txt> for more
			details.

Example:

	vdd_cx: rpmh-cx-regulator-cdev {
		compatible = "qcom,rpmh-reg-cdev";
		mboxes = <&qmp_aop 0>;
		qcom,reg-resource-name = "cx";
		#cooling-cells = <2>;
	};
+9 −0
Original line number Diff line number Diff line
@@ -40,3 +40,12 @@ config QTI_VIRTUAL_SENSOR
	  The virtual sensor information includes the underlying thermal
	  sensors to query for temperature and the aggregation logic to
	  determine the virtual sensor temperature.

config QTI_REG_COOLING_DEVICE
	bool "QTI Regulator cooling device"
	depends on THERMAL_OF && MSM_QMP
	help
	  This enables the Regulator cooling device. This cooling device
	  will be used by QTI chipset to place a floor voltage restriction at
	  low temperatures. The regulator cooling device will message the AOP
	  using mail box to establish the floor voltage.
+1 −0
Original line number Diff line number Diff line
@@ -3,3 +3,4 @@ qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o
obj-$(CONFIG_QTI_VIRTUAL_SENSOR) += qti_virtual_sensor.o
obj-$(CONFIG_QTI_REG_COOLING_DEVICE) += regulator_cooling.o
+224 −0
Original line number Diff line number Diff line
/* Copyright (c) 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.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/mailbox_client.h>

#define REG_CDEV_DRIVER "reg-cooling-device"
#define REG_MSG_FORMAT "{class:volt_flr, event:zero_temp, res:%s, value:%s}"
#define REG_CDEV_MAX_STATE 1
#define MBOX_TOUT_MS 1000
#define REG_MSG_MAX_LEN 100

struct reg_cooling_device {
	struct thermal_cooling_device	*cdev;
	unsigned int			min_state;
	const char			*resource_name;
	struct mbox_chan		*qmp_chan;
	struct mbox_client		*client;
};

struct aop_msg {
	uint32_t len;
	void *msg;
};

enum regulator_rail_type {
	REG_COOLING_CX,
	REG_COOLING_MX,
	REG_COOLING_EBI,
	REG_COOLING_NR,
};

static char *regulator_rail[REG_COOLING_NR] = {
	"cx",
	"mx",
	"ebi",
};

static int aop_send_msg(struct reg_cooling_device *reg_dev, int min_state)
{
	char msg_buf[REG_MSG_MAX_LEN] = {0};
	int ret = 0;
	struct aop_msg msg;

	if (!reg_dev->qmp_chan) {
		pr_err("mbox not initialized for resource:%s\n",
				reg_dev->resource_name);
		return -EINVAL;
	}

	ret = snprintf(msg_buf, REG_MSG_MAX_LEN, REG_MSG_FORMAT,
			reg_dev->resource_name,
			(min_state == REG_CDEV_MAX_STATE) ? "off" : "on");
	if (ret >= REG_MSG_MAX_LEN) {
		pr_err("Message too long for resource:%s\n",
				reg_dev->resource_name);
		return -E2BIG;
	}
	msg.len = REG_MSG_MAX_LEN;
	msg.msg = msg_buf;
	ret = mbox_send_message(reg_dev->qmp_chan, &msg);

	return (ret < 0) ? ret : 0;
}

static int reg_get_max_state(struct thermal_cooling_device *cdev,
				unsigned long *state)
{
	*state = REG_CDEV_MAX_STATE;
	return 0;
}

static int reg_get_min_state(struct thermal_cooling_device *cdev,
				unsigned long *state)
{
	struct reg_cooling_device *reg_dev = cdev->devdata;

	*state = reg_dev->min_state;
	return 0;
}

static int reg_send_min_state(struct thermal_cooling_device *cdev,
				unsigned long state)
{
	struct reg_cooling_device *reg_dev = cdev->devdata;
	int ret = 0;

	if (state > REG_CDEV_MAX_STATE)
		state = REG_CDEV_MAX_STATE;

	if (reg_dev->min_state == state)
		return ret;

	ret = aop_send_msg(reg_dev, state);
	if (ret) {
		pr_err("regulator:%s switching to floor %lu error. err:%d\n",
			reg_dev->resource_name, state, ret);
	} else {
		pr_debug("regulator:%s switched to %lu from %d\n",
			reg_dev->resource_name, state, reg_dev->min_state);
		reg_dev->min_state = state;
	}

	return ret;
}

static int reg_get_cur_state(struct thermal_cooling_device *cdev,
				unsigned long *state)
{
	*state = 0;
	return 0;
}

static int reg_send_cur_state(struct thermal_cooling_device *cdev,
				unsigned long state)
{
	return 0;
}

static struct thermal_cooling_device_ops reg_dev_ops = {
	.get_max_state = reg_get_max_state,
	.get_cur_state = reg_get_cur_state,
	.set_cur_state = reg_send_cur_state,
	.set_min_state = reg_send_min_state,
	.get_min_state = reg_get_min_state,
};

static int reg_init_mbox(struct platform_device *pdev,
			struct reg_cooling_device *reg_dev)
{
	reg_dev->client = devm_kzalloc(&pdev->dev, sizeof(*reg_dev->client),
					GFP_KERNEL);
	if (!reg_dev->client)
		return -ENOMEM;

	reg_dev->client->dev = &pdev->dev;
	reg_dev->client->tx_block = true;
	reg_dev->client->tx_tout = MBOX_TOUT_MS;
	reg_dev->client->knows_txdone = false;

	reg_dev->qmp_chan = mbox_request_channel(reg_dev->client, 0);
	if (IS_ERR(reg_dev->qmp_chan)) {
		dev_err(&pdev->dev, "Mbox request failed. err:%ld\n",
				PTR_ERR(reg_dev->qmp_chan));
		return PTR_ERR(reg_dev->qmp_chan);
	}

	return 0;
}

static int reg_dev_probe(struct platform_device *pdev)
{
	int ret = 0, idx = 0;
	struct reg_cooling_device *reg_dev = NULL;

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

	ret = reg_init_mbox(pdev, reg_dev);
	if (ret)
		return ret;

	ret = of_property_read_string(pdev->dev.of_node,
			"qcom,reg-resource-name",
			&reg_dev->resource_name);
	if (ret) {
		dev_err(&pdev->dev, "Error reading resource name. err:%d\n",
			ret);
		goto mbox_free;
	}

	for (idx = 0; idx < REG_COOLING_NR; idx++) {
		if (!strcmp(reg_dev->resource_name, regulator_rail[idx]))
			break;
	}
	if (idx == REG_COOLING_NR) {
		dev_err(&pdev->dev, "Invalid regulator resource name:%s\n",
				reg_dev->resource_name);
		ret = -EINVAL;
		goto mbox_free;
	}
	reg_dev->min_state = REG_CDEV_MAX_STATE;
	reg_dev->cdev = thermal_of_cooling_device_register(
				pdev->dev.of_node,
				(char *)reg_dev->resource_name,
				reg_dev, &reg_dev_ops);
	if (IS_ERR(reg_dev->cdev))
		goto mbox_free;

	return ret;

mbox_free:
	mbox_free_channel(reg_dev->qmp_chan);

	return ret;
}

static const struct of_device_id reg_dev_of_match[] = {
	{.compatible = "qcom,rpmh-reg-cdev", },
	{}
};

static struct platform_driver reg_dev_driver = {
	.driver = {
		.name = REG_CDEV_DRIVER,
		.of_match_table = reg_dev_of_match,
	},
	.probe = reg_dev_probe,
};
builtin_platform_driver(reg_dev_driver);