0007-surface-sam.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. From 3e6733db885b3a91339039897cbf0f3e05cda2a5 Mon Sep 17 00:00:00 2001
  2. From: Maximilian Luz <luzmaximilian@gmail.com>
  3. Date: Sat, 30 Dec 2023 18:07:54 +0100
  4. Subject: [PATCH] hwmon: Add thermal sensor driver for Surface Aggregator
  5. Module
  6. Some of the newer Microsoft Surface devices (such as the Surface Book
  7. 3 and Pro 9) have thermal sensors connected via the Surface Aggregator
  8. Module (the embedded controller on those devices). Add a basic driver
  9. to read out the temperature values of those sensors.
  10. Link: https://github.com/linux-surface/surface-aggregator-module/issues/59
  11. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  12. Patchset: surface-sam
  13. ---
  14. drivers/hwmon/Kconfig | 10 +++
  15. drivers/hwmon/Makefile | 1 +
  16. drivers/hwmon/surface_temp.c | 165 +++++++++++++++++++++++++++++++++++
  17. 3 files changed, 176 insertions(+)
  18. create mode 100644 drivers/hwmon/surface_temp.c
  19. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
  20. index e14ae18a973b..76eabe3e4435 100644
  21. --- a/drivers/hwmon/Kconfig
  22. +++ b/drivers/hwmon/Kconfig
  23. @@ -2093,6 +2093,16 @@ config SENSORS_SURFACE_FAN
  24. Select M or Y here, if you want to be able to read the fan's speed.
  25. +config SENSORS_SURFACE_TEMP
  26. + tristate "Microsoft Surface Thermal Sensor Driver"
  27. + depends on SURFACE_AGGREGATOR
  28. + help
  29. + Driver for monitoring thermal sensors connected via the Surface
  30. + Aggregator Module (embedded controller) on Microsoft Surface devices.
  31. +
  32. + This driver can also be built as a module. If so, the module
  33. + will be called surface_temp.
  34. +
  35. config SENSORS_ADC128D818
  36. tristate "Texas Instruments ADC128D818"
  37. depends on I2C
  38. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
  39. index e3f25475d1f0..eff74ab7f720 100644
  40. --- a/drivers/hwmon/Makefile
  41. +++ b/drivers/hwmon/Makefile
  42. @@ -209,6 +209,7 @@ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
  43. obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
  44. obj-$(CONFIG_SENSORS_STTS751) += stts751.o
  45. obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
  46. +obj-$(CONFIG_SENSORS_SURFACE_TEMP)+= surface_temp.o
  47. obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
  48. obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
  49. obj-$(CONFIG_SENSORS_TC74) += tc74.o
  50. diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c
  51. new file mode 100644
  52. index 000000000000..48c3e826713f
  53. --- /dev/null
  54. +++ b/drivers/hwmon/surface_temp.c
  55. @@ -0,0 +1,165 @@
  56. +// SPDX-License-Identifier: GPL-2.0+
  57. +/*
  58. + * Thermal sensor subsystem driver for Surface System Aggregator Module (SSAM).
  59. + *
  60. + * Copyright (C) 2022-2023 Maximilian Luz <luzmaximilian@gmail.com>
  61. + */
  62. +
  63. +#include <linux/bitops.h>
  64. +#include <linux/hwmon.h>
  65. +#include <linux/kernel.h>
  66. +#include <linux/module.h>
  67. +#include <linux/types.h>
  68. +
  69. +#include <linux/surface_aggregator/controller.h>
  70. +#include <linux/surface_aggregator/device.h>
  71. +
  72. +
  73. +/* -- SAM interface. -------------------------------------------------------- */
  74. +
  75. +SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {
  76. + .target_category = SSAM_SSH_TC_TMP,
  77. + .command_id = 0x04,
  78. +});
  79. +
  80. +SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {
  81. + .target_category = SSAM_SSH_TC_TMP,
  82. + .command_id = 0x01,
  83. +});
  84. +
  85. +static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors)
  86. +{
  87. + __le16 sensors_le;
  88. + int status;
  89. +
  90. + status = __ssam_tmp_get_available_sensors(sdev, &sensors_le);
  91. + if (status)
  92. + return status;
  93. +
  94. + *sensors = le16_to_cpu(sensors_le);
  95. + return 0;
  96. +}
  97. +
  98. +static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temperature)
  99. +{
  100. + __le16 temp_le;
  101. + int status;
  102. +
  103. + status = __ssam_tmp_get_temperature(sdev->ctrl, sdev->uid.target, iid, &temp_le);
  104. + if (status)
  105. + return status;
  106. +
  107. + /* Convert 1/10 °K to 1/1000 °C */
  108. + *temperature = (le16_to_cpu(temp_le) - 2731) * 100L;
  109. + return 0;
  110. +}
  111. +
  112. +
  113. +/* -- Driver.---------------------------------------------------------------- */
  114. +
  115. +struct ssam_temp {
  116. + struct ssam_device *sdev;
  117. + s16 sensors;
  118. +};
  119. +
  120. +static umode_t ssam_temp_hwmon_is_visible(const void *data,
  121. + enum hwmon_sensor_types type,
  122. + u32 attr, int channel)
  123. +{
  124. + const struct ssam_temp *ssam_temp = data;
  125. +
  126. + if (!(ssam_temp->sensors & BIT(channel)))
  127. + return 0;
  128. +
  129. + return 0444;
  130. +}
  131. +
  132. +static int ssam_temp_hwmon_read(struct device *dev,
  133. + enum hwmon_sensor_types type,
  134. + u32 attr, int channel, long *value)
  135. +{
  136. + const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);
  137. +
  138. + return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value);
  139. +}
  140. +
  141. +static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = {
  142. + HWMON_CHANNEL_INFO(chip,
  143. + HWMON_C_REGISTER_TZ),
  144. + /* We have at most 16 thermal sensor channels. */
  145. + HWMON_CHANNEL_INFO(temp,
  146. + HWMON_T_INPUT,
  147. + HWMON_T_INPUT,
  148. + HWMON_T_INPUT,
  149. + HWMON_T_INPUT,
  150. + HWMON_T_INPUT,
  151. + HWMON_T_INPUT,
  152. + HWMON_T_INPUT,
  153. + HWMON_T_INPUT,
  154. + HWMON_T_INPUT,
  155. + HWMON_T_INPUT,
  156. + HWMON_T_INPUT,
  157. + HWMON_T_INPUT,
  158. + HWMON_T_INPUT,
  159. + HWMON_T_INPUT,
  160. + HWMON_T_INPUT,
  161. + HWMON_T_INPUT),
  162. + NULL
  163. +};
  164. +
  165. +static const struct hwmon_ops ssam_temp_hwmon_ops = {
  166. + .is_visible = ssam_temp_hwmon_is_visible,
  167. + .read = ssam_temp_hwmon_read,
  168. +};
  169. +
  170. +static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {
  171. + .ops = &ssam_temp_hwmon_ops,
  172. + .info = ssam_temp_hwmon_info,
  173. +};
  174. +
  175. +static int ssam_temp_probe(struct ssam_device *sdev)
  176. +{
  177. + struct ssam_temp *ssam_temp;
  178. + struct device *hwmon_dev;
  179. + s16 sensors;
  180. + int status;
  181. +
  182. + status = ssam_tmp_get_available_sensors(sdev, &sensors);
  183. + if (status)
  184. + return status;
  185. +
  186. + ssam_temp = devm_kzalloc(&sdev->dev, sizeof(*ssam_temp), GFP_KERNEL);
  187. + if (!ssam_temp)
  188. + return -ENOMEM;
  189. +
  190. + ssam_temp->sdev = sdev;
  191. + ssam_temp->sensors = sensors;
  192. +
  193. + hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev,
  194. + "surface_thermal", ssam_temp, &ssam_temp_hwmon_chip_info,
  195. + NULL);
  196. + if (IS_ERR(hwmon_dev))
  197. + return PTR_ERR(hwmon_dev);
  198. +
  199. + return 0;
  200. +}
  201. +
  202. +static const struct ssam_device_id ssam_temp_match[] = {
  203. + { SSAM_SDEV(TMP, SAM, 0x00, 0x02) },
  204. + { },
  205. +};
  206. +MODULE_DEVICE_TABLE(ssam, ssam_temp_match);
  207. +
  208. +static struct ssam_device_driver ssam_temp = {
  209. + .probe = ssam_temp_probe,
  210. + .match_table = ssam_temp_match,
  211. + .driver = {
  212. + .name = "surface_temp",
  213. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  214. + },
  215. +};
  216. +module_ssam_device_driver(ssam_temp);
  217. +
  218. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  219. +MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module");
  220. +MODULE_LICENSE("GPL");
  221. --
  222. 2.46.1
  223. From 3e4479ac556ff21b45485822edb0980bbb27fdba Mon Sep 17 00:00:00 2001
  224. From: Maximilian Luz <luzmaximilian@gmail.com>
  225. Date: Sat, 30 Dec 2023 18:12:23 +0100
  226. Subject: [PATCH] hwmon: surface_temp: Add support for sensor names
  227. The thermal subsystem of the Surface Aggregator Module allows us to
  228. query the names of the respective thermal sensors. Forward those to
  229. userspace.
  230. Signed-off-by: Ivor Wanders <ivor@iwanders.net>
  231. Co-Developed-by: Maximilian Luz <luzmaximilian@gmail.com>
  232. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  233. Patchset: surface-sam
  234. ---
  235. drivers/hwmon/surface_temp.c | 113 +++++++++++++++++++++++++++++------
  236. 1 file changed, 96 insertions(+), 17 deletions(-)
  237. diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c
  238. index 48c3e826713f..4c08926139db 100644
  239. --- a/drivers/hwmon/surface_temp.c
  240. +++ b/drivers/hwmon/surface_temp.c
  241. @@ -17,6 +17,27 @@
  242. /* -- SAM interface. -------------------------------------------------------- */
  243. +/*
  244. + * Available sensors are indicated by a 16-bit bitfield, where a 1 marks the
  245. + * presence of a sensor. So we have at most 16 possible sensors/channels.
  246. + */
  247. +#define SSAM_TMP_SENSOR_MAX_COUNT 16
  248. +
  249. +/*
  250. + * All names observed so far are 6 characters long, but there's only
  251. + * zeros after the name, so perhaps they can be longer. This number reflects
  252. + * the maximum zero-padded space observed in the returned buffer.
  253. + */
  254. +#define SSAM_TMP_SENSOR_NAME_LENGTH 18
  255. +
  256. +struct ssam_tmp_get_name_rsp {
  257. + __le16 unknown1;
  258. + char unknown2;
  259. + char name[SSAM_TMP_SENSOR_NAME_LENGTH];
  260. +} __packed;
  261. +
  262. +static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21);
  263. +
  264. SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {
  265. .target_category = SSAM_SSH_TC_TMP,
  266. .command_id = 0x04,
  267. @@ -27,6 +48,11 @@ SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {
  268. .command_id = 0x01,
  269. });
  270. +SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_name, struct ssam_tmp_get_name_rsp, {
  271. + .target_category = SSAM_SSH_TC_TMP,
  272. + .command_id = 0x0e,
  273. +});
  274. +
  275. static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors)
  276. {
  277. __le16 sensors_le;
  278. @@ -54,12 +80,37 @@ static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temp
  279. return 0;
  280. }
  281. +static int ssam_tmp_get_name(struct ssam_device *sdev, u8 iid, char *buf, size_t buf_len)
  282. +{
  283. + struct ssam_tmp_get_name_rsp name_rsp;
  284. + int status;
  285. +
  286. + status = __ssam_tmp_get_name(sdev->ctrl, sdev->uid.target, iid, &name_rsp);
  287. + if (status)
  288. + return status;
  289. +
  290. + /*
  291. + * This should not fail unless the name in the returned struct is not
  292. + * null-terminated or someone changed something in the struct
  293. + * definitions above, since our buffer and struct have the same
  294. + * capacity by design. So if this fails blow this up with a warning.
  295. + * Since the more likely cause is that the returned string isn't
  296. + * null-terminated, we might have received garbage (as opposed to just
  297. + * an incomplete string), so also fail the function.
  298. + */
  299. + status = strscpy(buf, name_rsp.name, buf_len);
  300. + WARN_ON(status < 0);
  301. +
  302. + return status < 0 ? status : 0;
  303. +}
  304. +
  305. /* -- Driver.---------------------------------------------------------------- */
  306. struct ssam_temp {
  307. struct ssam_device *sdev;
  308. s16 sensors;
  309. + char names[SSAM_TMP_SENSOR_MAX_COUNT][SSAM_TMP_SENSOR_NAME_LENGTH];
  310. };
  311. static umode_t ssam_temp_hwmon_is_visible(const void *data,
  312. @@ -83,33 +134,47 @@ static int ssam_temp_hwmon_read(struct device *dev,
  313. return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value);
  314. }
  315. +static int ssam_temp_hwmon_read_string(struct device *dev,
  316. + enum hwmon_sensor_types type,
  317. + u32 attr, int channel, const char **str)
  318. +{
  319. + const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);
  320. +
  321. + *str = ssam_temp->names[channel];
  322. + return 0;
  323. +}
  324. +
  325. static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = {
  326. HWMON_CHANNEL_INFO(chip,
  327. HWMON_C_REGISTER_TZ),
  328. - /* We have at most 16 thermal sensor channels. */
  329. + /*
  330. + * We have at most SSAM_TMP_SENSOR_MAX_COUNT = 16 thermal sensor
  331. + * channels.
  332. + */
  333. HWMON_CHANNEL_INFO(temp,
  334. - HWMON_T_INPUT,
  335. - HWMON_T_INPUT,
  336. - HWMON_T_INPUT,
  337. - HWMON_T_INPUT,
  338. - HWMON_T_INPUT,
  339. - HWMON_T_INPUT,
  340. - HWMON_T_INPUT,
  341. - HWMON_T_INPUT,
  342. - HWMON_T_INPUT,
  343. - HWMON_T_INPUT,
  344. - HWMON_T_INPUT,
  345. - HWMON_T_INPUT,
  346. - HWMON_T_INPUT,
  347. - HWMON_T_INPUT,
  348. - HWMON_T_INPUT,
  349. - HWMON_T_INPUT),
  350. + HWMON_T_INPUT | HWMON_T_LABEL,
  351. + HWMON_T_INPUT | HWMON_T_LABEL,
  352. + HWMON_T_INPUT | HWMON_T_LABEL,
  353. + HWMON_T_INPUT | HWMON_T_LABEL,
  354. + HWMON_T_INPUT | HWMON_T_LABEL,
  355. + HWMON_T_INPUT | HWMON_T_LABEL,
  356. + HWMON_T_INPUT | HWMON_T_LABEL,
  357. + HWMON_T_INPUT | HWMON_T_LABEL,
  358. + HWMON_T_INPUT | HWMON_T_LABEL,
  359. + HWMON_T_INPUT | HWMON_T_LABEL,
  360. + HWMON_T_INPUT | HWMON_T_LABEL,
  361. + HWMON_T_INPUT | HWMON_T_LABEL,
  362. + HWMON_T_INPUT | HWMON_T_LABEL,
  363. + HWMON_T_INPUT | HWMON_T_LABEL,
  364. + HWMON_T_INPUT | HWMON_T_LABEL,
  365. + HWMON_T_INPUT | HWMON_T_LABEL),
  366. NULL
  367. };
  368. static const struct hwmon_ops ssam_temp_hwmon_ops = {
  369. .is_visible = ssam_temp_hwmon_is_visible,
  370. .read = ssam_temp_hwmon_read,
  371. + .read_string = ssam_temp_hwmon_read_string,
  372. };
  373. static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {
  374. @@ -122,6 +187,7 @@ static int ssam_temp_probe(struct ssam_device *sdev)
  375. struct ssam_temp *ssam_temp;
  376. struct device *hwmon_dev;
  377. s16 sensors;
  378. + int channel;
  379. int status;
  380. status = ssam_tmp_get_available_sensors(sdev, &sensors);
  381. @@ -135,6 +201,19 @@ static int ssam_temp_probe(struct ssam_device *sdev)
  382. ssam_temp->sdev = sdev;
  383. ssam_temp->sensors = sensors;
  384. + /* Retrieve the name for each available sensor. */
  385. + for (channel = 0; channel < SSAM_TMP_SENSOR_MAX_COUNT; channel++)
  386. + {
  387. + if (!(sensors & BIT(channel)))
  388. + continue;
  389. +
  390. + status = ssam_tmp_get_name(sdev, channel + 1,
  391. + ssam_temp->names[channel],
  392. + SSAM_TMP_SENSOR_NAME_LENGTH);
  393. + if (status)
  394. + return status;
  395. + }
  396. +
  397. hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev,
  398. "surface_thermal", ssam_temp, &ssam_temp_hwmon_chip_info,
  399. NULL);
  400. --
  401. 2.46.1