brintos

brintos / linux-shallow public Read only

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