brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 72ce14c Raw
329 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2015-2017, NVIDIA CORPORATION.  All rights reserved.4 *5 * Author:6 *	Mikko Perttunen <mperttunen@nvidia.com>7 *	Aapo Vienamo	<avienamo@nvidia.com>8 */9 10#include <linux/err.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/thermal.h>14#include <linux/workqueue.h>15 16#include <soc/tegra/bpmp.h>17#include <soc/tegra/bpmp-abi.h>18 19struct tegra_bpmp_thermal_zone {20	struct tegra_bpmp_thermal *tegra;21	struct thermal_zone_device *tzd;22	struct work_struct tz_device_update_work;23	unsigned int idx;24};25 26struct tegra_bpmp_thermal {27	struct device *dev;28	struct tegra_bpmp *bpmp;29	unsigned int num_zones;30	struct tegra_bpmp_thermal_zone **zones;31};32 33static int __tegra_bpmp_thermal_get_temp(struct tegra_bpmp_thermal_zone *zone,34					 int *out_temp)35{36	struct mrq_thermal_host_to_bpmp_request req;37	union mrq_thermal_bpmp_to_host_response reply;38	struct tegra_bpmp_message msg;39	int err;40 41	memset(&req, 0, sizeof(req));42	req.type = CMD_THERMAL_GET_TEMP;43	req.get_temp.zone = zone->idx;44 45	memset(&msg, 0, sizeof(msg));46	msg.mrq = MRQ_THERMAL;47	msg.tx.data = &req;48	msg.tx.size = sizeof(req);49	msg.rx.data = &reply;50	msg.rx.size = sizeof(reply);51 52	err = tegra_bpmp_transfer(zone->tegra->bpmp, &msg);53	if (err)54		return err;55	if (msg.rx.ret == -BPMP_EFAULT)56		return -EAGAIN;57	if (msg.rx.ret)58		return -EINVAL;59 60	*out_temp = reply.get_temp.temp;61 62	return 0;63}64 65static int tegra_bpmp_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp)66{67	struct tegra_bpmp_thermal_zone *zone = thermal_zone_device_priv(tz);68 69	return __tegra_bpmp_thermal_get_temp(zone, out_temp);70}71 72static int tegra_bpmp_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)73{74	struct tegra_bpmp_thermal_zone *zone = thermal_zone_device_priv(tz);75	struct mrq_thermal_host_to_bpmp_request req;76	struct tegra_bpmp_message msg;77	int err;78 79	memset(&req, 0, sizeof(req));80	req.type = CMD_THERMAL_SET_TRIP;81	req.set_trip.zone = zone->idx;82	req.set_trip.enabled = true;83	req.set_trip.low = low;84	req.set_trip.high = high;85 86	memset(&msg, 0, sizeof(msg));87	msg.mrq = MRQ_THERMAL;88	msg.tx.data = &req;89	msg.tx.size = sizeof(req);90 91	err = tegra_bpmp_transfer(zone->tegra->bpmp, &msg);92	if (err)93		return err;94	if (msg.rx.ret)95		return -EINVAL;96 97	return 0;98}99 100static void tz_device_update_work_fn(struct work_struct *work)101{102	struct tegra_bpmp_thermal_zone *zone;103 104	zone = container_of(work, struct tegra_bpmp_thermal_zone,105			    tz_device_update_work);106 107	thermal_zone_device_update(zone->tzd, THERMAL_TRIP_VIOLATED);108}109 110static void bpmp_mrq_thermal(unsigned int mrq, struct tegra_bpmp_channel *ch,111			     void *data)112{113	struct mrq_thermal_bpmp_to_host_request req;114	struct tegra_bpmp_thermal *tegra = data;115	size_t offset;116	int i;117 118	offset = offsetof(struct tegra_bpmp_mb_data, data);119	iosys_map_memcpy_from(&req, &ch->ib, offset, sizeof(req));120 121	if (req.type != CMD_THERMAL_HOST_TRIP_REACHED) {122		dev_err(tegra->dev, "%s: invalid request type: %d\n", __func__, req.type);123		tegra_bpmp_mrq_return(ch, -EINVAL, NULL, 0);124		return;125	}126 127	for (i = 0; i < tegra->num_zones; ++i) {128		if (tegra->zones[i]->idx != req.host_trip_reached.zone)129			continue;130 131		schedule_work(&tegra->zones[i]->tz_device_update_work);132		tegra_bpmp_mrq_return(ch, 0, NULL, 0);133		return;134	}135 136	dev_err(tegra->dev, "%s: invalid thermal zone: %d\n", __func__,137		req.host_trip_reached.zone);138	tegra_bpmp_mrq_return(ch, -EINVAL, NULL, 0);139}140 141static int tegra_bpmp_thermal_get_num_zones(struct tegra_bpmp *bpmp,142					    int *num_zones)143{144	struct mrq_thermal_host_to_bpmp_request req;145	union mrq_thermal_bpmp_to_host_response reply;146	struct tegra_bpmp_message msg;147	int err;148 149	memset(&req, 0, sizeof(req));150	req.type = CMD_THERMAL_GET_NUM_ZONES;151 152	memset(&msg, 0, sizeof(msg));153	msg.mrq = MRQ_THERMAL;154	msg.tx.data = &req;155	msg.tx.size = sizeof(req);156	msg.rx.data = &reply;157	msg.rx.size = sizeof(reply);158 159	err = tegra_bpmp_transfer(bpmp, &msg);160	if (err)161		return err;162	if (msg.rx.ret)163		return -EINVAL;164 165	*num_zones = reply.get_num_zones.num;166 167	return 0;168}169 170static int tegra_bpmp_thermal_trips_supported(struct tegra_bpmp *bpmp, bool *supported)171{172	struct mrq_thermal_host_to_bpmp_request req;173	union mrq_thermal_bpmp_to_host_response reply;174	struct tegra_bpmp_message msg;175	int err;176 177	memset(&req, 0, sizeof(req));178	req.type = CMD_THERMAL_QUERY_ABI;179	req.query_abi.type = CMD_THERMAL_SET_TRIP;180 181	memset(&msg, 0, sizeof(msg));182	msg.mrq = MRQ_THERMAL;183	msg.tx.data = &req;184	msg.tx.size = sizeof(req);185	msg.rx.data = &reply;186	msg.rx.size = sizeof(reply);187 188	err = tegra_bpmp_transfer(bpmp, &msg);189	if (err)190		return err;191 192	if (msg.rx.ret == 0) {193		*supported = true;194		return 0;195	} else if (msg.rx.ret == -BPMP_ENODEV) {196		*supported = false;197		return 0;198	} else {199		return -EINVAL;200	}201}202 203static const struct thermal_zone_device_ops tegra_bpmp_of_thermal_ops = {204	.get_temp = tegra_bpmp_thermal_get_temp,205	.set_trips = tegra_bpmp_thermal_set_trips,206};207 208static const struct thermal_zone_device_ops tegra_bpmp_of_thermal_ops_notrips = {209	.get_temp = tegra_bpmp_thermal_get_temp,210};211 212static int tegra_bpmp_thermal_probe(struct platform_device *pdev)213{214	struct tegra_bpmp *bpmp = dev_get_drvdata(pdev->dev.parent);215	const struct thermal_zone_device_ops *thermal_ops;216	struct tegra_bpmp_thermal *tegra;217	struct thermal_zone_device *tzd;218	unsigned int i, max_num_zones;219	bool supported;220	int err;221 222	err = tegra_bpmp_thermal_trips_supported(bpmp, &supported);223	if (err) {224		dev_err(&pdev->dev, "failed to determine if trip points are supported\n");225		return err;226	}227 228	if (supported)229		thermal_ops = &tegra_bpmp_of_thermal_ops;230	else231		thermal_ops = &tegra_bpmp_of_thermal_ops_notrips;232 233	tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);234	if (!tegra)235		return -ENOMEM;236 237	tegra->dev = &pdev->dev;238	tegra->bpmp = bpmp;239 240	err = tegra_bpmp_thermal_get_num_zones(bpmp, &max_num_zones);241	if (err) {242		dev_err(&pdev->dev, "failed to get the number of zones: %d\n",243			err);244		return err;245	}246 247	tegra->zones = devm_kcalloc(&pdev->dev, max_num_zones,248				    sizeof(*tegra->zones), GFP_KERNEL);249	if (!tegra->zones)250		return -ENOMEM;251 252	for (i = 0; i < max_num_zones; ++i) {253		struct tegra_bpmp_thermal_zone *zone;254		int temp;255 256		zone = devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);257		if (!zone)258			return -ENOMEM;259 260		zone->idx = i;261		zone->tegra = tegra;262 263		err = __tegra_bpmp_thermal_get_temp(zone, &temp);264 265		/*266		 * Sensors in powergated domains may temporarily fail to be read267		 * (-EAGAIN), but will become accessible when the domain is powered on.268		 */269		if (err < 0 && err != -EAGAIN) {270			devm_kfree(&pdev->dev, zone);271			continue;272		}273 274		tzd = devm_thermal_of_zone_register(275			&pdev->dev, i, zone, thermal_ops);276		if (IS_ERR(tzd)) {277			if (PTR_ERR(tzd) == -EPROBE_DEFER)278				return -EPROBE_DEFER;279			devm_kfree(&pdev->dev, zone);280			continue;281		}282 283		zone->tzd = tzd;284		INIT_WORK(&zone->tz_device_update_work,285			  tz_device_update_work_fn);286 287		tegra->zones[tegra->num_zones++] = zone;288	}289 290	err = tegra_bpmp_request_mrq(bpmp, MRQ_THERMAL, bpmp_mrq_thermal,291				     tegra);292	if (err) {293		dev_err(&pdev->dev, "failed to register mrq handler: %d\n",294			err);295		return err;296	}297 298	platform_set_drvdata(pdev, tegra);299 300	return 0;301}302 303static void tegra_bpmp_thermal_remove(struct platform_device *pdev)304{305	struct tegra_bpmp_thermal *tegra = platform_get_drvdata(pdev);306 307	tegra_bpmp_free_mrq(tegra->bpmp, MRQ_THERMAL, tegra);308}309 310static const struct of_device_id tegra_bpmp_thermal_of_match[] = {311	{ .compatible = "nvidia,tegra186-bpmp-thermal" },312	{ },313};314MODULE_DEVICE_TABLE(of, tegra_bpmp_thermal_of_match);315 316static struct platform_driver tegra_bpmp_thermal_driver = {317	.probe = tegra_bpmp_thermal_probe,318	.remove_new = tegra_bpmp_thermal_remove,319	.driver = {320		.name = "tegra-bpmp-thermal",321		.of_match_table = tegra_bpmp_thermal_of_match,322	},323};324module_platform_driver(tegra_bpmp_thermal_driver);325 326MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");327MODULE_DESCRIPTION("NVIDIA Tegra BPMP thermal sensor driver");328MODULE_LICENSE("GPL v2");329