cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

chromeos_tbmc.c (2979B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Driver to detect Tablet Mode for ChromeOS convertible.
      3//
      4// Copyright (C) 2017 Google, Inc.
      5// Author: Gwendal Grignou <gwendal@chromium.org>
      6//
      7// On Chromebook using ACPI, this device listens for notification
      8// from GOOG0006 and issue method TBMC to retrieve the status.
      9//
     10// GOOG0006 issues the notification when it receives EC_HOST_EVENT_MODE_CHANGE
     11// from the EC.
     12// Method TBMC reads EC_ACPI_MEM_DEVICE_ORIENTATION byte from the shared
     13// memory region.
     14
     15#include <linux/acpi.h>
     16#include <linux/input.h>
     17#include <linux/io.h>
     18#include <linux/module.h>
     19#include <linux/printk.h>
     20
     21#define DRV_NAME "chromeos_tbmc"
     22#define ACPI_DRV_NAME "GOOG0006"
     23
     24static int chromeos_tbmc_query_switch(struct acpi_device *adev,
     25				     struct input_dev *idev)
     26{
     27	unsigned long long state;
     28	acpi_status status;
     29
     30	status = acpi_evaluate_integer(adev->handle, "TBMC", NULL, &state);
     31	if (ACPI_FAILURE(status))
     32		return -ENODEV;
     33
     34	/* input layer checks if event is redundant */
     35	input_report_switch(idev, SW_TABLET_MODE, state);
     36	input_sync(idev);
     37
     38	return 0;
     39}
     40
     41static __maybe_unused int chromeos_tbmc_resume(struct device *dev)
     42{
     43	struct acpi_device *adev = to_acpi_device(dev);
     44
     45	return chromeos_tbmc_query_switch(adev, adev->driver_data);
     46}
     47
     48static void chromeos_tbmc_notify(struct acpi_device *adev, u32 event)
     49{
     50	acpi_pm_wakeup_event(&adev->dev);
     51	switch (event) {
     52	case 0x80:
     53		chromeos_tbmc_query_switch(adev, adev->driver_data);
     54		break;
     55	default:
     56		dev_err(&adev->dev, "Unexpected event: 0x%08X\n", event);
     57	}
     58}
     59
     60static int chromeos_tbmc_open(struct input_dev *idev)
     61{
     62	struct acpi_device *adev = input_get_drvdata(idev);
     63
     64	return chromeos_tbmc_query_switch(adev, idev);
     65}
     66
     67static int chromeos_tbmc_add(struct acpi_device *adev)
     68{
     69	struct input_dev *idev;
     70	struct device *dev = &adev->dev;
     71	int ret;
     72
     73	idev = devm_input_allocate_device(dev);
     74	if (!idev)
     75		return -ENOMEM;
     76
     77	idev->name = "Tablet Mode Switch";
     78	idev->phys = acpi_device_hid(adev);
     79
     80	idev->id.bustype = BUS_HOST;
     81	idev->id.version = 1;
     82	idev->id.product = 0;
     83	idev->open = chromeos_tbmc_open;
     84
     85	input_set_drvdata(idev, adev);
     86	adev->driver_data = idev;
     87
     88	input_set_capability(idev, EV_SW, SW_TABLET_MODE);
     89	ret = input_register_device(idev);
     90	if (ret) {
     91		dev_err(dev, "cannot register input device\n");
     92		return ret;
     93	}
     94	device_init_wakeup(dev, true);
     95	return 0;
     96}
     97
     98static const struct acpi_device_id chromeos_tbmc_acpi_device_ids[] = {
     99	{ ACPI_DRV_NAME, 0 },
    100	{ }
    101};
    102MODULE_DEVICE_TABLE(acpi, chromeos_tbmc_acpi_device_ids);
    103
    104static SIMPLE_DEV_PM_OPS(chromeos_tbmc_pm_ops, NULL,
    105		chromeos_tbmc_resume);
    106
    107static struct acpi_driver chromeos_tbmc_driver = {
    108	.name = DRV_NAME,
    109	.class = DRV_NAME,
    110	.ids = chromeos_tbmc_acpi_device_ids,
    111	.ops = {
    112		.add = chromeos_tbmc_add,
    113		.notify = chromeos_tbmc_notify,
    114	},
    115	.drv.pm = &chromeos_tbmc_pm_ops,
    116};
    117
    118module_acpi_driver(chromeos_tbmc_driver);
    119
    120MODULE_LICENSE("GPL v2");
    121MODULE_DESCRIPTION("ChromeOS ACPI tablet switch driver");