GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / platform / chrome / wilco_ec / core.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Core driver for Wilco Embedded Controller
4  *
5  * Copyright 2018 Google LLC
6  *
7  * This is the entry point for the drivers that control the Wilco EC.
8  */
9
10 #include <linux/acpi.h>
11 #include <linux/device.h>
12 #include <linux/ioport.h>
13 #include <linux/module.h>
14 #include <linux/platform_data/wilco-ec.h>
15 #include <linux/platform_device.h>
16
17 #include "../cros_ec_lpc_mec.h"
18
19 #define DRV_NAME "wilco-ec"
20
21 static struct resource *wilco_get_resource(struct platform_device *pdev,
22                                            int index)
23 {
24         struct device *dev = &pdev->dev;
25         struct resource *res;
26
27         res = platform_get_resource(pdev, IORESOURCE_IO, index);
28         if (!res) {
29                 dev_dbg(dev, "Couldn't find IO resource %d\n", index);
30                 return res;
31         }
32
33         return devm_request_region(dev, res->start, resource_size(res),
34                                    dev_name(dev));
35 }
36
37 static int wilco_ec_probe(struct platform_device *pdev)
38 {
39         struct device *dev = &pdev->dev;
40         struct wilco_ec_device *ec;
41         int ret;
42
43         ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
44         if (!ec)
45                 return -ENOMEM;
46
47         platform_set_drvdata(pdev, ec);
48         ec->dev = dev;
49         mutex_init(&ec->mailbox_lock);
50
51         ec->data_size = sizeof(struct wilco_ec_response) + EC_MAILBOX_DATA_SIZE;
52         ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL);
53         if (!ec->data_buffer)
54                 return -ENOMEM;
55
56         /* Prepare access to IO regions provided by ACPI */
57         ec->io_data = wilco_get_resource(pdev, 0);      /* Host Data */
58         ec->io_command = wilco_get_resource(pdev, 1);   /* Host Command */
59         ec->io_packet = wilco_get_resource(pdev, 2);    /* MEC EMI */
60         if (!ec->io_data || !ec->io_command || !ec->io_packet)
61                 return -ENODEV;
62
63         /* Initialize cros_ec register interface for communication */
64         cros_ec_lpc_mec_init(ec->io_packet->start,
65                              ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
66
67         /*
68          * Register a child device that will be found by the debugfs driver.
69          * Ignore failure.
70          */
71         ec->debugfs_pdev = platform_device_register_data(dev,
72                                                          "wilco-ec-debugfs",
73                                                          PLATFORM_DEVID_AUTO,
74                                                          NULL, 0);
75
76         /* Register a child device that will be found by the RTC driver. */
77         ec->rtc_pdev = platform_device_register_data(dev, "rtc-wilco-ec",
78                                                      PLATFORM_DEVID_AUTO,
79                                                      NULL, 0);
80         if (IS_ERR(ec->rtc_pdev)) {
81                 dev_err(dev, "Failed to create RTC platform device\n");
82                 ret = PTR_ERR(ec->rtc_pdev);
83                 goto unregister_debugfs;
84         }
85
86         /* Set up the keyboard backlight LEDs. */
87         ret = wilco_keyboard_leds_init(ec);
88         if (ret < 0) {
89                 dev_err(dev,
90                         "Failed to initialize keyboard LEDs: %d\n",
91                         ret);
92                 goto unregister_rtc;
93         }
94
95         ret = wilco_ec_add_sysfs(ec);
96         if (ret < 0) {
97                 dev_err(dev, "Failed to create sysfs entries: %d\n", ret);
98                 goto unregister_rtc;
99         }
100
101         /* Register child device to be found by charger config driver. */
102         ec->charger_pdev = platform_device_register_data(dev, "wilco-charger",
103                                                          PLATFORM_DEVID_AUTO,
104                                                          NULL, 0);
105         if (IS_ERR(ec->charger_pdev)) {
106                 dev_err(dev, "Failed to create charger platform device\n");
107                 ret = PTR_ERR(ec->charger_pdev);
108                 goto remove_sysfs;
109         }
110
111         /* Register child device that will be found by the telemetry driver. */
112         ec->telem_pdev = platform_device_register_data(dev, "wilco_telem",
113                                                        PLATFORM_DEVID_AUTO,
114                                                        ec, sizeof(*ec));
115         if (IS_ERR(ec->telem_pdev)) {
116                 dev_err(dev, "Failed to create telemetry platform device\n");
117                 ret = PTR_ERR(ec->telem_pdev);
118                 goto unregister_charge_config;
119         }
120
121         return 0;
122
123 unregister_charge_config:
124         platform_device_unregister(ec->charger_pdev);
125 remove_sysfs:
126         wilco_ec_remove_sysfs(ec);
127 unregister_rtc:
128         platform_device_unregister(ec->rtc_pdev);
129 unregister_debugfs:
130         if (ec->debugfs_pdev)
131                 platform_device_unregister(ec->debugfs_pdev);
132         cros_ec_lpc_mec_destroy();
133         return ret;
134 }
135
136 static int wilco_ec_remove(struct platform_device *pdev)
137 {
138         struct wilco_ec_device *ec = platform_get_drvdata(pdev);
139
140         platform_device_unregister(ec->telem_pdev);
141         platform_device_unregister(ec->charger_pdev);
142         wilco_ec_remove_sysfs(ec);
143         platform_device_unregister(ec->rtc_pdev);
144         if (ec->debugfs_pdev)
145                 platform_device_unregister(ec->debugfs_pdev);
146
147         /* Teardown cros_ec interface */
148         cros_ec_lpc_mec_destroy();
149
150         return 0;
151 }
152
153 static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
154         { "GOOG000C", 0 },
155         { }
156 };
157 MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
158
159 static struct platform_driver wilco_ec_driver = {
160         .driver = {
161                 .name = DRV_NAME,
162                 .acpi_match_table = wilco_ec_acpi_device_ids,
163         },
164         .probe = wilco_ec_probe,
165         .remove = wilco_ec_remove,
166 };
167
168 module_platform_driver(wilco_ec_driver);
169
170 MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
171 MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
172 MODULE_LICENSE("GPL v2");
173 MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver");
174 MODULE_ALIAS("platform:" DRV_NAME);