0008-surface-sam-over-hid.patch 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. From b31805e73ebae15726da67431317007ff11e91df Mon Sep 17 00:00:00 2001
  2. From: Maximilian Luz <luzmaximilian@gmail.com>
  3. Date: Sat, 25 Jul 2020 17:19:53 +0200
  4. Subject: [PATCH] i2c: acpi: Implement RawBytes read access
  5. Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C
  6. device via a generic serial bus operation region and RawBytes read
  7. access. On the Surface Book 1, this access is required to turn on (and
  8. off) the discrete GPU.
  9. Multiple things are to note here:
  10. a) The RawBytes access is device/driver dependent. The ACPI
  11. specification states:
  12. > Raw accesses assume that the writer has knowledge of the bus that
  13. > the access is made over and the device that is being accessed. The
  14. > protocol may only ensure that the buffer is transmitted to the
  15. > appropriate driver, but the driver must be able to interpret the
  16. > buffer to communicate to a register.
  17. Thus this implementation may likely not work on other devices
  18. accessing I2C via the RawBytes accessor type.
  19. b) The MSHW0030 I2C device is an HID-over-I2C device which seems to
  20. serve multiple functions:
  21. 1. It is the main access point for the legacy-type Surface Aggregator
  22. Module (also referred to as SAM-over-HID, as opposed to the newer
  23. SAM-over-SSH/UART). It has currently not been determined on how
  24. support for the legacy SAM should be implemented. Likely via a
  25. custom HID driver.
  26. 2. It seems to serve as the HID device for the Integrated Sensor Hub.
  27. This might complicate matters with regards to implementing a
  28. SAM-over-HID driver required by legacy SAM.
  29. In light of this, the simplest approach has been chosen for now.
  30. However, it may make more sense regarding breakage and compatibility to
  31. either provide functionality for replacing or enhancing the default
  32. operation region handler via some additional API functions, or even to
  33. completely blacklist MSHW0030 from the I2C core and provide a custom
  34. driver for it.
  35. Replacing/enhancing the default operation region handler would, however,
  36. either require some sort of secondary driver and access point for it,
  37. from which the new API functions would be called and the new handler
  38. (part) would be installed, or hard-coding them via some sort of
  39. quirk-like interface into the I2C core.
  40. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  41. Patchset: surface-sam-over-hid
  42. ---
  43. drivers/i2c/i2c-core-acpi.c | 34 ++++++++++++++++++++++++++++++++++
  44. 1 file changed, 34 insertions(+)
  45. diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
  46. index d2499f302b50..77ce5ec3dd9e 100644
  47. --- a/drivers/i2c/i2c-core-acpi.c
  48. +++ b/drivers/i2c/i2c-core-acpi.c
  49. @@ -661,6 +661,27 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
  50. return (ret == 1) ? 0 : -EIO;
  51. }
  52. +static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client,
  53. + u8 *data, u8 data_len)
  54. +{
  55. + struct i2c_msg msgs[1];
  56. + int ret;
  57. +
  58. + msgs[0].addr = client->addr;
  59. + msgs[0].flags = client->flags;
  60. + msgs[0].len = data_len + 1;
  61. + msgs[0].buf = data;
  62. +
  63. + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
  64. + if (ret < 0) {
  65. + dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret);
  66. + return ret;
  67. + }
  68. +
  69. + /* 1 transfer must have completed successfully */
  70. + return (ret == 1) ? 0 : -EIO;
  71. +}
  72. +
  73. static acpi_status
  74. i2c_acpi_space_handler(u32 function, acpi_physical_address command,
  75. u32 bits, u64 *value64,
  76. @@ -762,6 +783,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
  77. }
  78. break;
  79. + case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES:
  80. + if (action == ACPI_READ) {
  81. + dev_warn(&adapter->dev,
  82. + "protocol 0x%02x not supported for client 0x%02x\n",
  83. + accessor_type, client->addr);
  84. + ret = AE_BAD_PARAMETER;
  85. + goto err;
  86. + } else {
  87. + status = acpi_gsb_i2c_write_raw_bytes(client,
  88. + gsb->data, info->access_length);
  89. + }
  90. + break;
  91. +
  92. default:
  93. dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
  94. accessor_type, client->addr);
  95. --
  96. 2.49.0
  97. From 1383dcb9227a733f83c4a5f5fb266ade3eab27c8 Mon Sep 17 00:00:00 2001
  98. From: Maximilian Luz <luzmaximilian@gmail.com>
  99. Date: Sat, 13 Feb 2021 16:41:18 +0100
  100. Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch
  101. Add driver exposing the discrete GPU power-switch of the Microsoft
  102. Surface Book 1 to user-space.
  103. On the Surface Book 1, the dGPU power is controlled via the Surface
  104. System Aggregator Module (SAM). The specific SAM-over-HID command for
  105. this is exposed via ACPI. This module provides a simple driver exposing
  106. the ACPI call via a sysfs parameter to user-space, so that users can
  107. easily power-on/-off the dGPU.
  108. Patchset: surface-sam-over-hid
  109. ---
  110. drivers/platform/surface/Kconfig | 7 +
  111. drivers/platform/surface/Makefile | 1 +
  112. .../surface/surfacebook1_dgpu_switch.c | 136 ++++++++++++++++++
  113. 3 files changed, 144 insertions(+)
  114. create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c
  115. diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
  116. index b629e82af97c..68656e8f309e 100644
  117. --- a/drivers/platform/surface/Kconfig
  118. +++ b/drivers/platform/surface/Kconfig
  119. @@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH
  120. Select M or Y here, if you want to provide tablet-mode switch input
  121. events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio.
  122. +config SURFACE_BOOK1_DGPU_SWITCH
  123. + tristate "Surface Book 1 dGPU Switch Driver"
  124. + depends on SYSFS
  125. + help
  126. + This driver provides a sysfs switch to set the power-state of the
  127. + discrete GPU found on the Microsoft Surface Book 1.
  128. +
  129. config SURFACE_DTX
  130. tristate "Surface DTX (Detachment System) Driver"
  131. depends on SURFACE_AGGREGATOR
  132. diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
  133. index 53344330939b..7efcd0cdb532 100644
  134. --- a/drivers/platform/surface/Makefile
  135. +++ b/drivers/platform/surface/Makefile
  136. @@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
  137. obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o
  138. obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
  139. obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o
  140. +obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += surfacebook1_dgpu_switch.o
  141. obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o
  142. obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
  143. obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
  144. diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c
  145. new file mode 100644
  146. index 000000000000..68db237734a1
  147. --- /dev/null
  148. +++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c
  149. @@ -0,0 +1,136 @@
  150. +// SPDX-License-Identifier: GPL-2.0-or-later
  151. +
  152. +#include <linux/module.h>
  153. +#include <linux/acpi.h>
  154. +#include <linux/platform_device.h>
  155. +
  156. +/* MSHW0040/VGBI DSM UUID: 6fd05c69-cde3-49f4-95ed-ab1665498035 */
  157. +static const guid_t dgpu_sw_guid =
  158. + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4,
  159. + 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35);
  160. +
  161. +#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI"
  162. +#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON"
  163. +#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF"
  164. +
  165. +static int sb1_dgpu_sw_dsmcall(void)
  166. +{
  167. + union acpi_object *obj;
  168. + acpi_handle handle;
  169. + acpi_status status;
  170. +
  171. + status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle);
  172. + if (status)
  173. + return -EINVAL;
  174. +
  175. + obj = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER);
  176. + if (!obj)
  177. + return -EINVAL;
  178. +
  179. + ACPI_FREE(obj);
  180. + return 0;
  181. +}
  182. +
  183. +static int sb1_dgpu_sw_hgon(struct device *dev)
  184. +{
  185. + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
  186. + acpi_status status;
  187. +
  188. + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf);
  189. + if (status) {
  190. + dev_err(dev, "failed to run HGON: %d\n", status);
  191. + return -EINVAL;
  192. + }
  193. +
  194. + ACPI_FREE(buf.pointer);
  195. +
  196. + dev_info(dev, "turned-on dGPU via HGON\n");
  197. + return 0;
  198. +}
  199. +
  200. +static int sb1_dgpu_sw_hgof(struct device *dev)
  201. +{
  202. + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
  203. + acpi_status status;
  204. +
  205. + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf);
  206. + if (status) {
  207. + dev_err(dev, "failed to run HGOF: %d\n", status);
  208. + return -EINVAL;
  209. + }
  210. +
  211. + ACPI_FREE(buf.pointer);
  212. +
  213. + dev_info(dev, "turned-off dGPU via HGOF\n");
  214. + return 0;
  215. +}
  216. +
  217. +static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr,
  218. + const char *buf, size_t len)
  219. +{
  220. + bool value;
  221. + int status;
  222. +
  223. + status = kstrtobool(buf, &value);
  224. + if (status < 0)
  225. + return status;
  226. +
  227. + if (!value)
  228. + return 0;
  229. +
  230. + status = sb1_dgpu_sw_dsmcall();
  231. +
  232. + return status < 0 ? status : len;
  233. +}
  234. +static DEVICE_ATTR_WO(dgpu_dsmcall);
  235. +
  236. +static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr,
  237. + const char *buf, size_t len)
  238. +{
  239. + bool power;
  240. + int status;
  241. +
  242. + status = kstrtobool(buf, &power);
  243. + if (status < 0)
  244. + return status;
  245. +
  246. + if (power)
  247. + status = sb1_dgpu_sw_hgon(dev);
  248. + else
  249. + status = sb1_dgpu_sw_hgof(dev);
  250. +
  251. + return status < 0 ? status : len;
  252. +}
  253. +static DEVICE_ATTR_WO(dgpu_power);
  254. +
  255. +static struct attribute *sb1_dgpu_sw_attrs[] = {
  256. + &dev_attr_dgpu_dsmcall.attr,
  257. + &dev_attr_dgpu_power.attr,
  258. + NULL
  259. +};
  260. +ATTRIBUTE_GROUPS(sb1_dgpu_sw);
  261. +
  262. +/*
  263. + * The dGPU power seems to be actually handled by MSHW0040. However, that is
  264. + * also the power-/volume-button device with a mainline driver. So let's use
  265. + * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device.
  266. + */
  267. +static const struct acpi_device_id sb1_dgpu_sw_match[] = {
  268. + { "MSHW0041", },
  269. + { }
  270. +};
  271. +MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match);
  272. +
  273. +static struct platform_driver sb1_dgpu_sw = {
  274. + .driver = {
  275. + .name = "surfacebook1_dgpu_switch",
  276. + .acpi_match_table = sb1_dgpu_sw_match,
  277. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  278. + .dev_groups = sb1_dgpu_sw_groups,
  279. + },
  280. +};
  281. +module_platform_driver(sb1_dgpu_sw);
  282. +
  283. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  284. +MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1");
  285. +MODULE_LICENSE("GPL");
  286. --
  287. 2.49.0