Files
linux-nv-oot/drivers/thermal/tegra234-oc-event.c
Yi-Wei Wang b270392b4f drivers: thermal: Add Tegra234 OC event driver
SOC_THERM is the primary hardware component of Tegra on-die thermal
management strategies. SOC_THERM includes the function of
externally-signaled event detection (SOC_THERM_edp) which centralizes
a few mechanisms for detecting and responding to externally signaled
electrical events, such as overcurrent events, undervoltage events, and
so on. These are known as "OC alarms" because they often indicate an
overcurrent event.

The Jetson Module's on-board power monitor INA3221 is configured to
trigger CPU/GPU hardware clock throttling via Tegra234 SOCTHERM_OC when
the Module input current exceeds the preprogrammed overcurrent threshold
to keep Module power consumption within the TDP power budget.

CPU/GPU performance may drop when hardware clock throttling occurs.
To allow the user to infer whether the performance degradation is
related to the overcurrent event, the Tegra234 OC event driver is
implemented which sends a Message ReQuest (MRQ) to the BPMP firmware to
provide information regarding the overcurrent enable state and the event
count to userspace via the hwmon sysfs interface.

Bug 3571683

Signed-off-by: Yi-Wei Wang <yiweiw@nvidia.com>
Change-Id: I6cc3579944efc92916189f097ccfed2ccba26051
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2862007
Reviewed-by: Rajkumar Kasirajan <rkasirajan@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2023-04-17 07:03:26 -07:00

224 lines
5.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <dt-bindings/thermal/tegra234-soctherm.h>
#include <linux/err.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <soc/tegra/bpmp-abi.h>
#include <soc/tegra/bpmp.h>
struct oc_soc_data {
const struct attribute_group **attr_groups;
};
struct tegra234_oc_event {
struct device *hwmon;
struct tegra_bpmp *bpmp;
struct oc_soc_data *soc_data;
};
static ssize_t throt_en_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int err = 0;
struct tegra234_oc_event *tegra234_oc = dev_get_drvdata(dev);
struct sensor_device_attribute *sensor_attr =
container_of(attr, struct sensor_device_attribute, dev_attr);
struct mrq_oc_status_response resp;
struct tegra_bpmp_message msg = {
.mrq = MRQ_OC_STATUS,
.rx = {
.data = &resp,
.size = sizeof(resp),
},
};
if (sensor_attr->index < 0) {
dev_err(dev, "Negative index for OC events\n");
return -EDOM;
}
err = tegra_bpmp_transfer(tegra234_oc->bpmp, &msg);
if (err) {
dev_err(dev, "Failed to transfer message: %d\n", err);
return err;
}
if (msg.rx.ret < 0) {
dev_err(dev, "Negative bpmp message return value: %d\n",
msg.rx.ret);
return -EINVAL;
}
return sprintf(buf, "%u\n", resp.throt_en[sensor_attr->index]);
}
static ssize_t event_cnt_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int err = 0;
struct tegra234_oc_event *tegra234_oc = dev_get_drvdata(dev);
struct sensor_device_attribute *sensor_attr =
container_of(attr, struct sensor_device_attribute, dev_attr);
struct mrq_oc_status_response resp;
struct tegra_bpmp_message msg = {
.mrq = MRQ_OC_STATUS,
.rx = {
.data = &resp,
.size = sizeof(resp),
},
};
if (sensor_attr->index < 0) {
dev_err(dev, "Negative index for OC events\n");
return -EDOM;
}
err = tegra_bpmp_transfer(tegra234_oc->bpmp, &msg);
if (err) {
dev_err(dev, "Failed to transfer message: %d\n", err);
return err;
}
if (msg.rx.ret < 0) {
dev_err(dev, "Negative bpmp message return value: %d\n",
msg.rx.ret);
return -EINVAL;
}
return sprintf(buf, "%u\n", resp.event_cnt[sensor_attr->index]);
}
static SENSOR_DEVICE_ATTR_RO(oc1_throt_en, throt_en, TEGRA234_SOCTHERM_EDP_OC1);
static SENSOR_DEVICE_ATTR_RO(oc1_event_cnt, event_cnt,
TEGRA234_SOCTHERM_EDP_OC1);
static SENSOR_DEVICE_ATTR_RO(oc2_throt_en, throt_en, TEGRA234_SOCTHERM_EDP_OC2);
static SENSOR_DEVICE_ATTR_RO(oc2_event_cnt, event_cnt,
TEGRA234_SOCTHERM_EDP_OC2);
static SENSOR_DEVICE_ATTR_RO(oc3_throt_en, throt_en, TEGRA234_SOCTHERM_EDP_OC3);
static SENSOR_DEVICE_ATTR_RO(oc3_event_cnt, event_cnt,
TEGRA234_SOCTHERM_EDP_OC3);
static struct attribute *t234_oc1_attrs[] = {
&sensor_dev_attr_oc1_throt_en.dev_attr.attr,
&sensor_dev_attr_oc1_event_cnt.dev_attr.attr,
NULL,
};
static struct attribute *t234_oc2_attrs[] = {
&sensor_dev_attr_oc2_throt_en.dev_attr.attr,
&sensor_dev_attr_oc2_event_cnt.dev_attr.attr,
NULL,
};
static struct attribute *t234_oc3_attrs[] = {
&sensor_dev_attr_oc3_throt_en.dev_attr.attr,
&sensor_dev_attr_oc3_event_cnt.dev_attr.attr,
NULL,
};
static const struct attribute_group oc1_data = {
.attrs = t234_oc1_attrs,
NULL,
};
static const struct attribute_group oc2_data = {
.attrs = t234_oc2_attrs,
NULL,
};
static const struct attribute_group oc3_data = {
.attrs = t234_oc3_attrs,
NULL,
};
static const struct attribute_group *t234_oc_groups[] = {
&oc1_data,
&oc2_data,
&oc3_data,
NULL,
};
static const struct oc_soc_data t234_oc_soc_data = {
.attr_groups = t234_oc_groups,
};
static const struct of_device_id of_tegra234_oc_event_match[] = {
{ .compatible = "nvidia,tegra234-oc-event", .data = &t234_oc_soc_data },
{},
};
MODULE_DEVICE_TABLE(of, of_tegra234_oc_event_match);
static int tegra234_oc_event_probe(struct platform_device *pdev)
{
int err = 0;
const struct of_device_id *match;
struct tegra_bpmp *tb;
struct tegra234_oc_event *tegra234_oc;
match = of_match_node(of_tegra234_oc_event_match, pdev->dev.of_node);
if (!match)
return -ENODEV;
tb = tegra_bpmp_get(&pdev->dev);
if (IS_ERR(tb))
return PTR_ERR(tb);
tegra234_oc =
devm_kzalloc(&pdev->dev, sizeof(*tegra234_oc), GFP_KERNEL);
if (!tegra234_oc) {
err = -ENOMEM;
goto put_bpmp;
}
platform_set_drvdata(pdev, tegra234_oc);
tegra234_oc->soc_data = match->data;
tegra234_oc->bpmp = tb;
tegra234_oc->hwmon = devm_hwmon_device_register_with_groups(
&pdev->dev, "soctherm_oc", tegra234_oc,
tegra234_oc->soc_data->attr_groups);
if (IS_ERR(tegra234_oc->hwmon)) {
dev_err(&pdev->dev, "Failed to register hwmon device\n");
err = -EINVAL;
goto put_bpmp;
}
return err;
put_bpmp:
tegra_bpmp_put(tb);
return err;
}
static int tegra234_oc_event_remove(struct platform_device *pdev)
{
struct tegra234_oc_event *tegra234_oc = platform_get_drvdata(pdev);
if (!tegra234_oc)
return -EINVAL;
tegra_bpmp_put(tegra234_oc->bpmp);
return 0;
}
static struct platform_driver tegra234_oc_event_driver = {
.probe = tegra234_oc_event_probe,
.remove = tegra234_oc_event_remove,
.driver = {
.name = "tegra234-oc-event",
.of_match_table = of_tegra234_oc_event_match,
},
};
module_platform_driver(tegra234_oc_event_driver);
MODULE_AUTHOR("Yi-Wei Wang <yiweiw@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra234 Overcurrent Event Driver");
MODULE_LICENSE("GPL v2");