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