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

cros_usbpd_notify.c (6832B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2020 Google LLC
      4 *
      5 * This driver serves as the receiver of cros_ec PD host events.
      6 */
      7
      8#include <linux/acpi.h>
      9#include <linux/module.h>
     10#include <linux/platform_data/cros_ec_proto.h>
     11#include <linux/platform_data/cros_usbpd_notify.h>
     12#include <linux/platform_device.h>
     13
     14#define DRV_NAME "cros-usbpd-notify"
     15#define DRV_NAME_PLAT_ACPI "cros-usbpd-notify-acpi"
     16#define ACPI_DRV_NAME "GOOG0003"
     17
     18static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list);
     19
     20struct cros_usbpd_notify_data {
     21	struct device *dev;
     22	struct cros_ec_device *ec;
     23	struct notifier_block nb;
     24};
     25
     26/**
     27 * cros_usbpd_register_notify - Register a notifier callback for PD events.
     28 * @nb: Notifier block pointer to register
     29 *
     30 * On ACPI platforms this corresponds to host events on the ECPD
     31 * "GOOG0003" ACPI device. On non-ACPI platforms this will filter mkbp events
     32 * for USB PD events.
     33 *
     34 * Return: 0 on success or negative error code.
     35 */
     36int cros_usbpd_register_notify(struct notifier_block *nb)
     37{
     38	return blocking_notifier_chain_register(&cros_usbpd_notifier_list,
     39						nb);
     40}
     41EXPORT_SYMBOL_GPL(cros_usbpd_register_notify);
     42
     43/**
     44 * cros_usbpd_unregister_notify - Unregister notifier callback for PD events.
     45 * @nb: Notifier block pointer to unregister
     46 *
     47 * Unregister a notifier callback that was previously registered with
     48 * cros_usbpd_register_notify().
     49 */
     50void cros_usbpd_unregister_notify(struct notifier_block *nb)
     51{
     52	blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb);
     53}
     54EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify);
     55
     56static void cros_usbpd_get_event_and_notify(struct device  *dev,
     57					    struct cros_ec_device *ec_dev)
     58{
     59	struct ec_response_host_event_status host_event_status;
     60	u32 event = 0;
     61	int ret;
     62
     63	/*
     64	 * We still send a 0 event out to older devices which don't
     65	 * have the updated device heirarchy.
     66	 */
     67	if (!ec_dev) {
     68		dev_dbg(dev,
     69			"EC device inaccessible; sending 0 event status.\n");
     70		goto send_notify;
     71	}
     72
     73	/* Check for PD host events on EC. */
     74	ret = cros_ec_command(ec_dev, 0, EC_CMD_PD_HOST_EVENT_STATUS,
     75			      NULL, 0, &host_event_status, sizeof(host_event_status));
     76	if (ret < 0) {
     77		dev_warn(dev, "Can't get host event status (err: %d)\n", ret);
     78		goto send_notify;
     79	}
     80
     81	event = host_event_status.status;
     82
     83send_notify:
     84	blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL);
     85}
     86
     87#ifdef CONFIG_ACPI
     88
     89static void cros_usbpd_notify_acpi(acpi_handle device, u32 event, void *data)
     90{
     91	struct cros_usbpd_notify_data *pdnotify = data;
     92
     93	cros_usbpd_get_event_and_notify(pdnotify->dev, pdnotify->ec);
     94}
     95
     96static int cros_usbpd_notify_probe_acpi(struct platform_device *pdev)
     97{
     98	struct cros_usbpd_notify_data *pdnotify;
     99	struct device *dev = &pdev->dev;
    100	struct acpi_device *adev;
    101	struct cros_ec_device *ec_dev;
    102	acpi_status status;
    103
    104	adev = ACPI_COMPANION(dev);
    105
    106	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
    107	if (!pdnotify)
    108		return -ENOMEM;
    109
    110	/* Get the EC device pointer needed to talk to the EC. */
    111	ec_dev = dev_get_drvdata(dev->parent);
    112	if (!ec_dev) {
    113		/*
    114		 * We continue even for older devices which don't have the
    115		 * correct device heirarchy, namely, GOOG0003 is a child
    116		 * of GOOG0004.
    117		 */
    118		dev_warn(dev, "Couldn't get Chrome EC device pointer.\n");
    119	}
    120
    121	pdnotify->dev = dev;
    122	pdnotify->ec = ec_dev;
    123
    124	status = acpi_install_notify_handler(adev->handle,
    125					     ACPI_ALL_NOTIFY,
    126					     cros_usbpd_notify_acpi,
    127					     pdnotify);
    128	if (ACPI_FAILURE(status)) {
    129		dev_warn(dev, "Failed to register notify handler %08x\n",
    130			 status);
    131		return -EINVAL;
    132	}
    133
    134	return 0;
    135}
    136
    137static int cros_usbpd_notify_remove_acpi(struct platform_device *pdev)
    138{
    139	struct device *dev = &pdev->dev;
    140	struct acpi_device *adev = ACPI_COMPANION(dev);
    141
    142	acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
    143				   cros_usbpd_notify_acpi);
    144
    145	return 0;
    146}
    147
    148static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = {
    149	{ ACPI_DRV_NAME, 0 },
    150	{ }
    151};
    152MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids);
    153
    154static struct platform_driver cros_usbpd_notify_acpi_driver = {
    155	.driver = {
    156		.name = DRV_NAME_PLAT_ACPI,
    157		.acpi_match_table = cros_usbpd_notify_acpi_device_ids,
    158	},
    159	.probe = cros_usbpd_notify_probe_acpi,
    160	.remove = cros_usbpd_notify_remove_acpi,
    161};
    162
    163#endif /* CONFIG_ACPI */
    164
    165static int cros_usbpd_notify_plat(struct notifier_block *nb,
    166				  unsigned long queued_during_suspend,
    167				  void *data)
    168{
    169	struct cros_usbpd_notify_data *pdnotify = container_of(nb,
    170			struct cros_usbpd_notify_data, nb);
    171	struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
    172	u32 host_event = cros_ec_get_host_event(ec_dev);
    173
    174	if (!host_event)
    175		return NOTIFY_DONE;
    176
    177	if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
    178			  EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) {
    179		cros_usbpd_get_event_and_notify(pdnotify->dev, ec_dev);
    180		return NOTIFY_OK;
    181	}
    182	return NOTIFY_DONE;
    183}
    184
    185static int cros_usbpd_notify_probe_plat(struct platform_device *pdev)
    186{
    187	struct device *dev = &pdev->dev;
    188	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
    189	struct cros_usbpd_notify_data *pdnotify;
    190	int ret;
    191
    192	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
    193	if (!pdnotify)
    194		return -ENOMEM;
    195
    196	pdnotify->dev = dev;
    197	pdnotify->ec = ecdev->ec_dev;
    198	pdnotify->nb.notifier_call = cros_usbpd_notify_plat;
    199
    200	dev_set_drvdata(dev, pdnotify);
    201
    202	ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier,
    203					       &pdnotify->nb);
    204	if (ret < 0) {
    205		dev_err(dev, "Failed to register notifier\n");
    206		return ret;
    207	}
    208
    209	return 0;
    210}
    211
    212static int cros_usbpd_notify_remove_plat(struct platform_device *pdev)
    213{
    214	struct device *dev = &pdev->dev;
    215	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
    216	struct cros_usbpd_notify_data *pdnotify =
    217		(struct cros_usbpd_notify_data *)dev_get_drvdata(dev);
    218
    219	blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier,
    220					   &pdnotify->nb);
    221
    222	return 0;
    223}
    224
    225static struct platform_driver cros_usbpd_notify_plat_driver = {
    226	.driver = {
    227		.name = DRV_NAME,
    228	},
    229	.probe = cros_usbpd_notify_probe_plat,
    230	.remove = cros_usbpd_notify_remove_plat,
    231};
    232
    233static int __init cros_usbpd_notify_init(void)
    234{
    235	int ret;
    236
    237	ret = platform_driver_register(&cros_usbpd_notify_plat_driver);
    238	if (ret < 0)
    239		return ret;
    240
    241#ifdef CONFIG_ACPI
    242	platform_driver_register(&cros_usbpd_notify_acpi_driver);
    243#endif
    244	return 0;
    245}
    246
    247static void __exit cros_usbpd_notify_exit(void)
    248{
    249#ifdef CONFIG_ACPI
    250	platform_driver_unregister(&cros_usbpd_notify_acpi_driver);
    251#endif
    252	platform_driver_unregister(&cros_usbpd_notify_plat_driver);
    253}
    254
    255module_init(cros_usbpd_notify_init);
    256module_exit(cros_usbpd_notify_exit);
    257
    258MODULE_LICENSE("GPL");
    259MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
    260MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
    261MODULE_ALIAS("platform:" DRV_NAME);