GNU Linux-libre 4.14.295-gnu1
[releases.git] / drivers / usb / typec / ucsi / ucsi_acpi.c
1 /*
2  * UCSI ACPI driver
3  *
4  * Copyright (C) 2017, Intel Corporation
5  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/platform_device.h>
13 #include <linux/module.h>
14 #include <linux/acpi.h>
15
16 #include "ucsi.h"
17
18 #define UCSI_DSM_UUID           "6f8398c2-7ca4-11e4-ad36-631042b5008f"
19 #define UCSI_DSM_FUNC_WRITE     1
20 #define UCSI_DSM_FUNC_READ      2
21
22 struct ucsi_acpi {
23         struct device *dev;
24         struct ucsi *ucsi;
25         struct ucsi_ppm ppm;
26         guid_t guid;
27 };
28
29 static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
30 {
31         union acpi_object *obj;
32
33         obj = acpi_evaluate_dsm(ACPI_HANDLE(ua->dev), &ua->guid, 1, func,
34                                 NULL);
35         if (!obj) {
36                 dev_err(ua->dev, "%s: failed to evaluate _DSM %d\n",
37                         __func__, func);
38                 return -EIO;
39         }
40
41         ACPI_FREE(obj);
42         return 0;
43 }
44
45 static int ucsi_acpi_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
46 {
47         struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
48
49         ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
50
51         return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
52 }
53
54 static int ucsi_acpi_sync(struct ucsi_ppm *ppm)
55 {
56         struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
57
58         return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
59 }
60
61 static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
62 {
63         struct ucsi_acpi *ua = data;
64
65         ucsi_notify(ua->ucsi);
66 }
67
68 static int ucsi_acpi_probe(struct platform_device *pdev)
69 {
70         struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
71         struct ucsi_acpi *ua;
72         struct resource *res;
73         acpi_status status;
74         int ret;
75
76         if (adev->dep_unmet)
77                 return -EPROBE_DEFER;
78
79         ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL);
80         if (!ua)
81                 return -ENOMEM;
82
83         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
84         if (!res) {
85                 dev_err(&pdev->dev, "missing memory resource\n");
86                 return -ENODEV;
87         }
88
89         /* This will make sure we can use ioremap_nocache() */
90         status = acpi_release_memory(ACPI_HANDLE(&pdev->dev), res, 1);
91         if (ACPI_FAILURE(status))
92                 return -ENOMEM;
93
94         /*
95          * NOTE: The memory region for the data structures is used also in an
96          * operation region, which means ACPI has already reserved it. Therefore
97          * it can not be requested here, and we can not use
98          * devm_ioremap_resource().
99          */
100         ua->ppm.data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
101         if (!ua->ppm.data)
102                 return -ENOMEM;
103
104         if (!ua->ppm.data->version)
105                 return -ENODEV;
106
107         ret = guid_parse(UCSI_DSM_UUID, &ua->guid);
108         if (ret)
109                 return ret;
110
111         ua->ppm.cmd = ucsi_acpi_cmd;
112         ua->ppm.sync = ucsi_acpi_sync;
113         ua->dev = &pdev->dev;
114
115         status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
116                                              ACPI_DEVICE_NOTIFY,
117                                              ucsi_acpi_notify, ua);
118         if (ACPI_FAILURE(status)) {
119                 dev_err(&pdev->dev, "failed to install notify handler\n");
120                 return -ENODEV;
121         }
122
123         ua->ucsi = ucsi_register_ppm(&pdev->dev, &ua->ppm);
124         if (IS_ERR(ua->ucsi)) {
125                 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
126                                            ACPI_DEVICE_NOTIFY,
127                                            ucsi_acpi_notify);
128                 return PTR_ERR(ua->ucsi);
129         }
130
131         platform_set_drvdata(pdev, ua);
132
133         return 0;
134 }
135
136 static int ucsi_acpi_remove(struct platform_device *pdev)
137 {
138         struct ucsi_acpi *ua = platform_get_drvdata(pdev);
139
140         ucsi_unregister_ppm(ua->ucsi);
141
142         acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
143                                    ucsi_acpi_notify);
144
145         return 0;
146 }
147
148 static const struct acpi_device_id ucsi_acpi_match[] = {
149         { "PNP0CA0", 0 },
150         { },
151 };
152 MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
153
154 static struct platform_driver ucsi_acpi_platform_driver = {
155         .driver = {
156                 .name = "ucsi_acpi",
157                 .acpi_match_table = ACPI_PTR(ucsi_acpi_match),
158         },
159         .probe = ucsi_acpi_probe,
160         .remove = ucsi_acpi_remove,
161 };
162
163 module_platform_driver(ucsi_acpi_platform_driver);
164
165 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
166 MODULE_LICENSE("GPL v2");
167 MODULE_DESCRIPTION("UCSI ACPI driver");