]> Git Repo - J-linux.git/blob - drivers/hwmon/surface_temp.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / hwmon / surface_temp.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 <[email protected]>
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 the
21  * presence of a sensor. So we have at most 16 possible sensors/channels.
22  */
23 #define SSAM_TMP_SENSOR_MAX_COUNT       16
24
25 /*
26  * All names observed so far are 6 characters long, but there's only
27  * zeros after the name, so perhaps they can be longer. This number reflects
28  * the maximum zero-padded space observed in the returned buffer.
29  */
30 #define SSAM_TMP_SENSOR_NAME_LENGTH     18
31
32 struct ssam_tmp_get_name_rsp {
33         __le16 unknown1;
34         char unknown2;
35         char name[SSAM_TMP_SENSOR_NAME_LENGTH];
36 } __packed;
37
38 static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21);
39
40 SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {
41         .target_category = SSAM_SSH_TC_TMP,
42         .command_id      = 0x04,
43 });
44
45 SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {
46         .target_category = SSAM_SSH_TC_TMP,
47         .command_id      = 0x01,
48 });
49
50 SSAM_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
55 static 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
68 static 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
82 static 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 not
93          * null-terminated or someone changed something in the struct
94          * definitions above, since our buffer and struct have the same
95          * capacity by design. So if this fails, log an error message. Since
96          * the more likely cause is that the returned string isn't
97          * null-terminated, we might have received garbage (as opposed to just
98          * 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
111 struct 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
117 static 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
129 static 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
138 static 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
148 static 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         NULL
169 };
170
171 static 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
177 static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {
178         .ops = &ssam_temp_hwmon_ops,
179         .info = ssam_temp_hwmon_info,
180 };
181
182 static 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
217 static const struct ssam_device_id ssam_temp_match[] = {
218         { SSAM_SDEV(TMP, SAM, 0x00, 0x02) },
219         { },
220 };
221 MODULE_DEVICE_TABLE(ssam, ssam_temp_match);
222
223 static 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 };
231 module_ssam_device_driver(ssam_temp);
232
233 MODULE_AUTHOR("Maximilian Luz <[email protected]>");
234 MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module");
235 MODULE_LICENSE("GPL");
This page took 0.038535 seconds and 4 git commands to generate.