// SPDX-License-Identifier: GPL-2.0
|
/*
|
* Core driver for Wilco Embedded Controller
|
*
|
* Copyright 2018 Google LLC
|
*
|
* This is the entry point for the drivers that control the Wilco EC.
|
*/
|
|
#include <linux/acpi.h>
|
#include <linux/device.h>
|
#include <linux/ioport.h>
|
#include <linux/module.h>
|
#include <linux/platform_data/wilco-ec.h>
|
#include <linux/platform_device.h>
|
|
#include "../cros_ec_lpc_mec.h"
|
|
#define DRV_NAME "wilco-ec"
|
|
static struct resource *wilco_get_resource(struct platform_device *pdev,
|
int index)
|
{
|
struct device *dev = &pdev->dev;
|
struct resource *res;
|
|
res = platform_get_resource(pdev, IORESOURCE_IO, index);
|
if (!res) {
|
dev_dbg(dev, "Couldn't find IO resource %d\n", index);
|
return res;
|
}
|
|
return devm_request_region(dev, res->start, resource_size(res),
|
dev_name(dev));
|
}
|
|
static int wilco_ec_probe(struct platform_device *pdev)
|
{
|
struct device *dev = &pdev->dev;
|
struct wilco_ec_device *ec;
|
int ret;
|
|
ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
|
if (!ec)
|
return -ENOMEM;
|
|
platform_set_drvdata(pdev, ec);
|
ec->dev = dev;
|
mutex_init(&ec->mailbox_lock);
|
|
ec->data_size = sizeof(struct wilco_ec_response) + EC_MAILBOX_DATA_SIZE;
|
ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL);
|
if (!ec->data_buffer)
|
return -ENOMEM;
|
|
/* Prepare access to IO regions provided by ACPI */
|
ec->io_data = wilco_get_resource(pdev, 0); /* Host Data */
|
ec->io_command = wilco_get_resource(pdev, 1); /* Host Command */
|
ec->io_packet = wilco_get_resource(pdev, 2); /* MEC EMI */
|
if (!ec->io_data || !ec->io_command || !ec->io_packet)
|
return -ENODEV;
|
|
/* Initialize cros_ec register interface for communication */
|
cros_ec_lpc_mec_init(ec->io_packet->start,
|
ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
|
|
/*
|
* Register a child device that will be found by the debugfs driver.
|
* Ignore failure.
|
*/
|
ec->debugfs_pdev = platform_device_register_data(dev,
|
"wilco-ec-debugfs",
|
PLATFORM_DEVID_AUTO,
|
NULL, 0);
|
|
/* Register a child device that will be found by the RTC driver. */
|
ec->rtc_pdev = platform_device_register_data(dev, "rtc-wilco-ec",
|
PLATFORM_DEVID_AUTO,
|
NULL, 0);
|
if (IS_ERR(ec->rtc_pdev)) {
|
dev_err(dev, "Failed to create RTC platform device\n");
|
ret = PTR_ERR(ec->rtc_pdev);
|
goto unregister_debugfs;
|
}
|
|
/* Set up the keyboard backlight LEDs. */
|
ret = wilco_keyboard_leds_init(ec);
|
if (ret < 0) {
|
dev_err(dev,
|
"Failed to initialize keyboard LEDs: %d\n",
|
ret);
|
goto unregister_rtc;
|
}
|
|
ret = wilco_ec_add_sysfs(ec);
|
if (ret < 0) {
|
dev_err(dev, "Failed to create sysfs entries: %d\n", ret);
|
goto unregister_rtc;
|
}
|
|
/* Register child device to be found by charger config driver. */
|
ec->charger_pdev = platform_device_register_data(dev, "wilco-charger",
|
PLATFORM_DEVID_AUTO,
|
NULL, 0);
|
if (IS_ERR(ec->charger_pdev)) {
|
dev_err(dev, "Failed to create charger platform device\n");
|
ret = PTR_ERR(ec->charger_pdev);
|
goto remove_sysfs;
|
}
|
|
/* Register child device that will be found by the telemetry driver. */
|
ec->telem_pdev = platform_device_register_data(dev, "wilco_telem",
|
PLATFORM_DEVID_AUTO,
|
ec, sizeof(*ec));
|
if (IS_ERR(ec->telem_pdev)) {
|
dev_err(dev, "Failed to create telemetry platform device\n");
|
ret = PTR_ERR(ec->telem_pdev);
|
goto unregister_charge_config;
|
}
|
|
return 0;
|
|
unregister_charge_config:
|
platform_device_unregister(ec->charger_pdev);
|
remove_sysfs:
|
wilco_ec_remove_sysfs(ec);
|
unregister_rtc:
|
platform_device_unregister(ec->rtc_pdev);
|
unregister_debugfs:
|
if (ec->debugfs_pdev)
|
platform_device_unregister(ec->debugfs_pdev);
|
cros_ec_lpc_mec_destroy();
|
return ret;
|
}
|
|
static int wilco_ec_remove(struct platform_device *pdev)
|
{
|
struct wilco_ec_device *ec = platform_get_drvdata(pdev);
|
|
platform_device_unregister(ec->telem_pdev);
|
platform_device_unregister(ec->charger_pdev);
|
wilco_ec_remove_sysfs(ec);
|
platform_device_unregister(ec->rtc_pdev);
|
if (ec->debugfs_pdev)
|
platform_device_unregister(ec->debugfs_pdev);
|
|
/* Teardown cros_ec interface */
|
cros_ec_lpc_mec_destroy();
|
|
return 0;
|
}
|
|
static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
|
{ "GOOG000C", 0 },
|
{ }
|
};
|
MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
|
|
static struct platform_driver wilco_ec_driver = {
|
.driver = {
|
.name = DRV_NAME,
|
.acpi_match_table = wilco_ec_acpi_device_ids,
|
},
|
.probe = wilco_ec_probe,
|
.remove = wilco_ec_remove,
|
};
|
|
module_platform_driver(wilco_ec_driver);
|
|
MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
|
MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
|
MODULE_LICENSE("GPL v2");
|
MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver");
|
MODULE_ALIAS("platform:" DRV_NAME);
|