diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index dd3d36f0..39cbb02c 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. obj-m += pex9749-thermal.o +obj-m += tegra234-oc-event.o diff --git a/drivers/thermal/tegra234-oc-event.c b/drivers/thermal/tegra234-oc-event.c new file mode 100644 index 00000000..62cc5ea8 --- /dev/null +++ b/drivers/thermal/tegra234-oc-event.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_DESCRIPTION("NVIDIA Tegra234 Overcurrent Event Driver"); +MODULE_LICENSE("GPL v2");