linux/drivers/platform/chrome/wilco_ec/core.c
<<
>>
Prefs
   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
  25static 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
  41static 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
 108remove_sysfs:
 109        wilco_ec_remove_sysfs(ec);
 110unregister_rtc:
 111        platform_device_unregister(ec->rtc_pdev);
 112unregister_debugfs:
 113        if (ec->debugfs_pdev)
 114                platform_device_unregister(ec->debugfs_pdev);
 115        cros_ec_lpc_mec_destroy();
 116        return ret;
 117}
 118
 119static 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
 135static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
 136        { "GOOG000C", 0 },
 137        { }
 138};
 139MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
 140
 141static 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
 150module_platform_driver(wilco_ec_driver);
 151
 152MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
 153MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
 154MODULE_LICENSE("GPL v2");
 155MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver");
 156MODULE_ALIAS("platform:" DRV_NAME);
 157