[PATCH 3/3] usb: type-c: UCSI ACPI driver

From: Heikki Krogerus
Date: Tue Feb 09 2016 - 12:03:56 EST


Driver for ACPI enumerated UCSI devices.

Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
---
drivers/usb/type-c/Kconfig | 10 ++++
drivers/usb/type-c/Makefile | 1 +
drivers/usb/type-c/ucsi_acpi.c | 133 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 144 insertions(+)
create mode 100644 drivers/usb/type-c/ucsi_acpi.c

diff --git a/drivers/usb/type-c/Kconfig b/drivers/usb/type-c/Kconfig
index 02abd74..72b002e 100644
--- a/drivers/usb/type-c/Kconfig
+++ b/drivers/usb/type-c/Kconfig
@@ -12,4 +12,14 @@ config TYPEC_UCSI
registers and data structures used to interface with the USB Type-C
connectors on a system.

+if TYPEC_UCSI
+
+config TYPEC_UCSI_ACPI
+ tristate "UCSI ACPI Driver"
+ depends on ACPI
+ help
+ Driver for ACPI enumerated UCSI devices.
+
+endif
+
endmenu
diff --git a/drivers/usb/type-c/Makefile b/drivers/usb/type-c/Makefile
index ab974ba..17933dc 100644
--- a/drivers/usb/type-c/Makefile
+++ b/drivers/usb/type-c/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_TYPEC) += typec.o
obj-$(CONFIG_TYPEC_UCSI) += ucsi.o
+obj-$(CONFIG_TYPEC_UCSI_ACPI) += ucsi_acpi.o
diff --git a/drivers/usb/type-c/ucsi_acpi.c b/drivers/usb/type-c/ucsi_acpi.c
new file mode 100644
index 0000000..8445a7d
--- /dev/null
+++ b/drivers/usb/type-c/ucsi_acpi.c
@@ -0,0 +1,133 @@
+/*
+ * UCSI ACPI driver
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/acpi.h>
+
+#include "ucsi.h"
+
+struct ucsi_acpi {
+ struct device *dev;
+ struct ucsi *ucsi;
+ struct ucsi_ppm ppm;
+};
+
+static const u8 ucsi_uuid[] = {
+ 0xc2, 0x98, 0x83, 0x6f, 0xa4, 0x7c, 0xe4, 0x11,
+ 0xad, 0x36, 0x63, 0x10, 0x42, 0xb5, 0x00, 0x8f,
+};
+
+static int ucsi_acpi_cmd(struct ucsi_ppm *ppm)
+{
+ struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
+ union acpi_object *obj;
+
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(ua->dev), ucsi_uuid, 1, 1, NULL);
+ if (!obj) {
+ dev_err(ua->dev, "%s: failed to evaluate _DSM\n", __func__);
+ return -EIO;
+ }
+
+ ACPI_FREE(obj);
+ return 0;
+}
+
+static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct ucsi_acpi *ua = data;
+
+ if (!ucsi_interrupt(ua->ucsi))
+ dev_err(ua->dev, "spurious ACPI notification\n");
+}
+
+static int ucsi_acpi_probe(struct platform_device *pdev)
+{
+ struct ucsi_acpi *ua;
+ struct resource *res;
+ acpi_status status;
+ int ret;
+
+ ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL);
+ if (!ua)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ ua->ppm.data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!ua->ppm.data)
+ return -ENOMEM;
+
+ ua->ppm.cmd = ucsi_acpi_cmd;
+ ua->dev = &pdev->dev;
+
+ ua->ucsi = ucsi_register_ppm(&pdev->dev, &ua->ppm);
+ if (IS_ERR(ua->ucsi))
+ return PTR_ERR(ua->ucsi);
+
+ status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
+ ACPI_DEVICE_NOTIFY,
+ ucsi_acpi_notify, ua);
+ if (ACPI_FAILURE(status)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = ucsi_init(ua->ucsi);
+ if (ret) {
+ acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
+ ACPI_DEVICE_NOTIFY,
+ ucsi_acpi_notify);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, ua);
+ return 0;
+err:
+ ucsi_unregister_ppm(ua->ucsi);
+ return ret;
+}
+
+static int ucsi_acpi_remove(struct platform_device *pdev)
+{
+ struct ucsi_acpi *ua = platform_get_drvdata(pdev);
+
+ acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
+ ACPI_DEVICE_NOTIFY, ucsi_acpi_notify);
+ ucsi_unregister_ppm(ua->ucsi);
+ return 0;
+}
+
+static const struct acpi_device_id ucsi_acpi_match[] = {
+ { "PNP0CA0", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
+
+static struct platform_driver ucsi_acpi_platform_driver = {
+ .driver = {
+ .name = "ucsi_acpi",
+ .acpi_match_table = ACPI_PTR(ucsi_acpi_match),
+ },
+ .probe = ucsi_acpi_probe,
+ .remove = ucsi_acpi_remove,
+};
+
+module_platform_driver(ucsi_acpi_platform_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("UCSI ACPI driver");
--
2.7.0