brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · cd21f33 Raw
236 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Thermal sensor subsystem driver for Surface System Aggregator Module (SSAM).4 *5 * Copyright (C) 2022-2023 Maximilian Luz <luzmaximilian@gmail.com>6 */7 8#include <linux/bitops.h>9#include <linux/hwmon.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/types.h>13 14#include <linux/surface_aggregator/controller.h>15#include <linux/surface_aggregator/device.h>16 17/* -- SAM interface. -------------------------------------------------------- */18 19/*20 * Available sensors are indicated by a 16-bit bitfield, where a 1 marks the21 * presence of a sensor. So we have at most 16 possible sensors/channels.22 */23#define SSAM_TMP_SENSOR_MAX_COUNT	1624 25/*26 * All names observed so far are 6 characters long, but there's only27 * zeros after the name, so perhaps they can be longer. This number reflects28 * the maximum zero-padded space observed in the returned buffer.29 */30#define SSAM_TMP_SENSOR_NAME_LENGTH	1831 32struct ssam_tmp_get_name_rsp {33	__le16 unknown1;34	char unknown2;35	char name[SSAM_TMP_SENSOR_NAME_LENGTH];36} __packed;37 38static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21);39 40SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {41	.target_category = SSAM_SSH_TC_TMP,42	.command_id      = 0x04,43});44 45SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {46	.target_category = SSAM_SSH_TC_TMP,47	.command_id      = 0x01,48});49 50SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_name, struct ssam_tmp_get_name_rsp, {51	.target_category = SSAM_SSH_TC_TMP,52	.command_id      = 0x0e,53});54 55static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors)56{57	__le16 sensors_le;58	int status;59 60	status = __ssam_tmp_get_available_sensors(sdev, &sensors_le);61	if (status)62		return status;63 64	*sensors = le16_to_cpu(sensors_le);65	return 0;66}67 68static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temperature)69{70	__le16 temp_le;71	int status;72 73	status = __ssam_tmp_get_temperature(sdev->ctrl, sdev->uid.target, iid, &temp_le);74	if (status)75		return status;76 77	/* Convert 1/10 °K to 1/1000 °C */78	*temperature = (le16_to_cpu(temp_le) - 2731) * 100L;79	return 0;80}81 82static int ssam_tmp_get_name(struct ssam_device *sdev, u8 iid, char *buf, size_t buf_len)83{84	struct ssam_tmp_get_name_rsp name_rsp;85	int status;86 87	status =  __ssam_tmp_get_name(sdev->ctrl, sdev->uid.target, iid, &name_rsp);88	if (status)89		return status;90 91	/*92	 * This should not fail unless the name in the returned struct is not93	 * null-terminated or someone changed something in the struct94	 * definitions above, since our buffer and struct have the same95	 * capacity by design. So if this fails, log an error message. Since96	 * the more likely cause is that the returned string isn't97	 * null-terminated, we might have received garbage (as opposed to just98	 * an incomplete string), so also fail the function.99	 */100	status = strscpy(buf, name_rsp.name, buf_len);101	if (status < 0) {102		dev_err(&sdev->dev, "received non-null-terminated sensor name string\n");103		return status;104	}105 106	return 0;107}108 109/* -- Driver.---------------------------------------------------------------- */110 111struct ssam_temp {112	struct ssam_device *sdev;113	s16 sensors;114	char names[SSAM_TMP_SENSOR_MAX_COUNT][SSAM_TMP_SENSOR_NAME_LENGTH];115};116 117static umode_t ssam_temp_hwmon_is_visible(const void *data,118					  enum hwmon_sensor_types type,119					  u32 attr, int channel)120{121	const struct ssam_temp *ssam_temp = data;122 123	if (!(ssam_temp->sensors & BIT(channel)))124		return 0;125 126	return 0444;127}128 129static int ssam_temp_hwmon_read(struct device *dev,130				enum hwmon_sensor_types type,131				u32 attr, int channel, long *value)132{133	const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);134 135	return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value);136}137 138static int ssam_temp_hwmon_read_string(struct device *dev,139				       enum hwmon_sensor_types type,140				       u32 attr, int channel, const char **str)141{142	const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);143 144	*str = ssam_temp->names[channel];145	return 0;146}147 148static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = {149	HWMON_CHANNEL_INFO(chip,150			   HWMON_C_REGISTER_TZ),151	HWMON_CHANNEL_INFO(temp,152			   HWMON_T_INPUT | HWMON_T_LABEL,153			   HWMON_T_INPUT | HWMON_T_LABEL,154			   HWMON_T_INPUT | HWMON_T_LABEL,155			   HWMON_T_INPUT | HWMON_T_LABEL,156			   HWMON_T_INPUT | HWMON_T_LABEL,157			   HWMON_T_INPUT | HWMON_T_LABEL,158			   HWMON_T_INPUT | HWMON_T_LABEL,159			   HWMON_T_INPUT | HWMON_T_LABEL,160			   HWMON_T_INPUT | HWMON_T_LABEL,161			   HWMON_T_INPUT | HWMON_T_LABEL,162			   HWMON_T_INPUT | HWMON_T_LABEL,163			   HWMON_T_INPUT | HWMON_T_LABEL,164			   HWMON_T_INPUT | HWMON_T_LABEL,165			   HWMON_T_INPUT | HWMON_T_LABEL,166			   HWMON_T_INPUT | HWMON_T_LABEL,167			   HWMON_T_INPUT | HWMON_T_LABEL),168	NULL169};170 171static const struct hwmon_ops ssam_temp_hwmon_ops = {172	.is_visible = ssam_temp_hwmon_is_visible,173	.read = ssam_temp_hwmon_read,174	.read_string = ssam_temp_hwmon_read_string,175};176 177static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {178	.ops = &ssam_temp_hwmon_ops,179	.info = ssam_temp_hwmon_info,180};181 182static int ssam_temp_probe(struct ssam_device *sdev)183{184	struct ssam_temp *ssam_temp;185	struct device *hwmon_dev;186	s16 sensors;187	int channel;188	int status;189 190	status = ssam_tmp_get_available_sensors(sdev, &sensors);191	if (status)192		return status;193 194	ssam_temp = devm_kzalloc(&sdev->dev, sizeof(*ssam_temp), GFP_KERNEL);195	if (!ssam_temp)196		return -ENOMEM;197 198	ssam_temp->sdev = sdev;199	ssam_temp->sensors = sensors;200 201	/* Retrieve the name for each available sensor. */202	for (channel = 0; channel < SSAM_TMP_SENSOR_MAX_COUNT; channel++) {203		if (!(sensors & BIT(channel)))204			continue;205 206		status = ssam_tmp_get_name(sdev, channel + 1, ssam_temp->names[channel],207					   SSAM_TMP_SENSOR_NAME_LENGTH);208		if (status)209			return status;210	}211 212	hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev, "surface_thermal", ssam_temp,213							 &ssam_temp_hwmon_chip_info, NULL);214	return PTR_ERR_OR_ZERO(hwmon_dev);215}216 217static const struct ssam_device_id ssam_temp_match[] = {218	{ SSAM_SDEV(TMP, SAM, 0x00, 0x02) },219	{ },220};221MODULE_DEVICE_TABLE(ssam, ssam_temp_match);222 223static struct ssam_device_driver ssam_temp = {224	.probe = ssam_temp_probe,225	.match_table = ssam_temp_match,226	.driver = {227		.name = "surface_temp",228		.probe_type = PROBE_PREFER_ASYNCHRONOUS,229	},230};231module_ssam_device_driver(ssam_temp);232 233MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");234MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module");235MODULE_LICENSE("GPL");236