core.c (4751B)
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 21static 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 37static 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 123unregister_charge_config: 124 platform_device_unregister(ec->charger_pdev); 125remove_sysfs: 126 wilco_ec_remove_sysfs(ec); 127unregister_rtc: 128 platform_device_unregister(ec->rtc_pdev); 129unregister_debugfs: 130 if (ec->debugfs_pdev) 131 platform_device_unregister(ec->debugfs_pdev); 132 cros_ec_lpc_mec_destroy(); 133 return ret; 134} 135 136static 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 153static const struct acpi_device_id wilco_ec_acpi_device_ids[] = { 154 { "GOOG000C", 0 }, 155 { } 156}; 157MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids); 158 159static 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 168module_platform_driver(wilco_ec_driver); 169 170MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); 171MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>"); 172MODULE_LICENSE("GPL v2"); 173MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver"); 174MODULE_ALIAS("platform:" DRV_NAME);