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