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