0007-surface-sam-over-hid.patch 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. From 93304c49c78e9088b13bf6af9d5f52676930c0bb 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 | 35 +++++++++++++++++++++++++++++++++++
  44. 1 file changed, 35 insertions(+)
  45. diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
  46. index 4dd777cc0c89f..b2338618163ad 100644
  47. --- a/drivers/i2c/i2c-core-acpi.c
  48. +++ b/drivers/i2c/i2c-core-acpi.c
  49. @@ -639,6 +639,28 @@ 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 = AE_OK;
  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. +
  65. + if (ret < 0) {
  66. + dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret);
  67. + return ret;
  68. + }
  69. +
  70. + /* 1 transfer must have completed successfully */
  71. + return (ret == 1) ? 0 : -EIO;
  72. +}
  73. +
  74. static acpi_status
  75. i2c_acpi_space_handler(u32 function, acpi_physical_address command,
  76. u32 bits, u64 *value64,
  77. @@ -740,6 +762,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
  78. }
  79. break;
  80. + case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES:
  81. + if (action == ACPI_READ) {
  82. + dev_warn(&adapter->dev,
  83. + "protocol 0x%02x not supported for client 0x%02x\n",
  84. + accessor_type, client->addr);
  85. + ret = AE_BAD_PARAMETER;
  86. + goto err;
  87. + } else {
  88. + status = acpi_gsb_i2c_write_raw_bytes(client,
  89. + gsb->data, info->access_length);
  90. + }
  91. + break;
  92. +
  93. default:
  94. dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
  95. accessor_type, client->addr);
  96. --
  97. 2.43.0
  98. From dceded4d08d4f48e96d998d13135e0702bc84c0b Mon Sep 17 00:00:00 2001
  99. From: Maximilian Luz <luzmaximilian@gmail.com>
  100. Date: Sat, 13 Feb 2021 16:41:18 +0100
  101. Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch
  102. Add driver exposing the discrete GPU power-switch of the Microsoft
  103. Surface Book 1 to user-space.
  104. On the Surface Book 1, the dGPU power is controlled via the Surface
  105. System Aggregator Module (SAM). The specific SAM-over-HID command for
  106. this is exposed via ACPI. This module provides a simple driver exposing
  107. the ACPI call via a sysfs parameter to user-space, so that users can
  108. easily power-on/-off the dGPU.
  109. Patchset: surface-sam-over-hid
  110. ---
  111. drivers/platform/surface/Kconfig | 7 +
  112. drivers/platform/surface/Makefile | 1 +
  113. .../surface/surfacebook1_dgpu_switch.c | 162 ++++++++++++++++++
  114. 3 files changed, 170 insertions(+)
  115. create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c
  116. diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
  117. index b629e82af97c0..68656e8f309ed 100644
  118. --- a/drivers/platform/surface/Kconfig
  119. +++ b/drivers/platform/surface/Kconfig
  120. @@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH
  121. Select M or Y here, if you want to provide tablet-mode switch input
  122. events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio.
  123. +config SURFACE_BOOK1_DGPU_SWITCH
  124. + tristate "Surface Book 1 dGPU Switch Driver"
  125. + depends on SYSFS
  126. + help
  127. + This driver provides a sysfs switch to set the power-state of the
  128. + discrete GPU found on the Microsoft Surface Book 1.
  129. +
  130. config SURFACE_DTX
  131. tristate "Surface DTX (Detachment System) Driver"
  132. depends on SURFACE_AGGREGATOR
  133. diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
  134. index 53344330939bf..7efcd0cdb5329 100644
  135. --- a/drivers/platform/surface/Makefile
  136. +++ b/drivers/platform/surface/Makefile
  137. @@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
  138. obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o
  139. obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
  140. obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o
  141. +obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += surfacebook1_dgpu_switch.o
  142. obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o
  143. obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
  144. obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
  145. diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c
  146. new file mode 100644
  147. index 0000000000000..8b816ed8f35c6
  148. --- /dev/null
  149. +++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c
  150. @@ -0,0 +1,162 @@
  151. +// SPDX-License-Identifier: GPL-2.0-or-later
  152. +
  153. +#include <linux/kernel.h>
  154. +#include <linux/module.h>
  155. +#include <linux/acpi.h>
  156. +#include <linux/platform_device.h>
  157. +
  158. +
  159. +#ifdef pr_fmt
  160. +#undef pr_fmt
  161. +#endif
  162. +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
  163. +
  164. +
  165. +static const guid_t dgpu_sw_guid = GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4,
  166. + 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35);
  167. +
  168. +#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI"
  169. +#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON"
  170. +#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF"
  171. +
  172. +
  173. +static int sb1_dgpu_sw_dsmcall(void)
  174. +{
  175. + union acpi_object *ret;
  176. + acpi_handle handle;
  177. + acpi_status status;
  178. +
  179. + status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle);
  180. + if (status)
  181. + return -EINVAL;
  182. +
  183. + ret = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER);
  184. + if (!ret)
  185. + return -EINVAL;
  186. +
  187. + ACPI_FREE(ret);
  188. + return 0;
  189. +}
  190. +
  191. +static int sb1_dgpu_sw_hgon(void)
  192. +{
  193. + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
  194. + acpi_status status;
  195. +
  196. + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf);
  197. + if (status) {
  198. + pr_err("failed to run HGON: %d\n", status);
  199. + return -EINVAL;
  200. + }
  201. +
  202. + if (buf.pointer)
  203. + ACPI_FREE(buf.pointer);
  204. +
  205. + pr_info("turned-on dGPU via HGON\n");
  206. + return 0;
  207. +}
  208. +
  209. +static int sb1_dgpu_sw_hgof(void)
  210. +{
  211. + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
  212. + acpi_status status;
  213. +
  214. + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf);
  215. + if (status) {
  216. + pr_err("failed to run HGOF: %d\n", status);
  217. + return -EINVAL;
  218. + }
  219. +
  220. + if (buf.pointer)
  221. + ACPI_FREE(buf.pointer);
  222. +
  223. + pr_info("turned-off dGPU via HGOF\n");
  224. + return 0;
  225. +}
  226. +
  227. +
  228. +static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr,
  229. + const char *buf, size_t len)
  230. +{
  231. + int status, value;
  232. +
  233. + status = kstrtoint(buf, 0, &value);
  234. + if (status < 0)
  235. + return status;
  236. +
  237. + if (value != 1)
  238. + return -EINVAL;
  239. +
  240. + status = sb1_dgpu_sw_dsmcall();
  241. +
  242. + return status < 0 ? status : len;
  243. +}
  244. +
  245. +static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr,
  246. + const char *buf, size_t len)
  247. +{
  248. + bool power;
  249. + int status;
  250. +
  251. + status = kstrtobool(buf, &power);
  252. + if (status < 0)
  253. + return status;
  254. +
  255. + if (power)
  256. + status = sb1_dgpu_sw_hgon();
  257. + else
  258. + status = sb1_dgpu_sw_hgof();
  259. +
  260. + return status < 0 ? status : len;
  261. +}
  262. +
  263. +static DEVICE_ATTR_WO(dgpu_dsmcall);
  264. +static DEVICE_ATTR_WO(dgpu_power);
  265. +
  266. +static struct attribute *sb1_dgpu_sw_attrs[] = {
  267. + &dev_attr_dgpu_dsmcall.attr,
  268. + &dev_attr_dgpu_power.attr,
  269. + NULL,
  270. +};
  271. +
  272. +static const struct attribute_group sb1_dgpu_sw_attr_group = {
  273. + .attrs = sb1_dgpu_sw_attrs,
  274. +};
  275. +
  276. +
  277. +static int sb1_dgpu_sw_probe(struct platform_device *pdev)
  278. +{
  279. + return sysfs_create_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group);
  280. +}
  281. +
  282. +static int sb1_dgpu_sw_remove(struct platform_device *pdev)
  283. +{
  284. + sysfs_remove_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group);
  285. + return 0;
  286. +}
  287. +
  288. +/*
  289. + * The dGPU power seems to be actually handled by MSHW0040. However, that is
  290. + * also the power-/volume-button device with a mainline driver. So let's use
  291. + * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device.
  292. + */
  293. +static const struct acpi_device_id sb1_dgpu_sw_match[] = {
  294. + { "MSHW0041", },
  295. + { },
  296. +};
  297. +MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match);
  298. +
  299. +static struct platform_driver sb1_dgpu_sw = {
  300. + .probe = sb1_dgpu_sw_probe,
  301. + .remove = sb1_dgpu_sw_remove,
  302. + .driver = {
  303. + .name = "surfacebook1_dgpu_switch",
  304. + .acpi_match_table = sb1_dgpu_sw_match,
  305. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  306. + },
  307. +};
  308. +module_platform_driver(sb1_dgpu_sw);
  309. +
  310. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  311. +MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1");
  312. +MODULE_LICENSE("GPL");
  313. --
  314. 2.43.0