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

extcon-qcom-spmi-misc.c (5430B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/**
      3 * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID
      4 *			and VBUS detection based on extcon-usb-gpio.c.
      5 *
      6 * Copyright (C) 2016 Linaro, Ltd.
      7 * Stephen Boyd <stephen.boyd@linaro.org>
      8 */
      9
     10#include <linux/devm-helpers.h>
     11#include <linux/extcon-provider.h>
     12#include <linux/init.h>
     13#include <linux/interrupt.h>
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/mod_devicetable.h>
     17#include <linux/platform_device.h>
     18#include <linux/slab.h>
     19#include <linux/workqueue.h>
     20
     21#define USB_ID_DEBOUNCE_MS	5	/* ms */
     22
     23struct qcom_usb_extcon_info {
     24	struct extcon_dev *edev;
     25	int id_irq;
     26	int vbus_irq;
     27	struct delayed_work wq_detcable;
     28	unsigned long debounce_jiffies;
     29};
     30
     31static const unsigned int qcom_usb_extcon_cable[] = {
     32	EXTCON_USB,
     33	EXTCON_USB_HOST,
     34	EXTCON_NONE,
     35};
     36
     37static void qcom_usb_extcon_detect_cable(struct work_struct *work)
     38{
     39	bool state = false;
     40	int ret;
     41	union extcon_property_value val;
     42	struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work),
     43						    struct qcom_usb_extcon_info,
     44						    wq_detcable);
     45
     46	if (info->id_irq > 0) {
     47		/* check ID and update cable state */
     48		ret = irq_get_irqchip_state(info->id_irq,
     49				IRQCHIP_STATE_LINE_LEVEL, &state);
     50		if (ret)
     51			return;
     52
     53		if (!state) {
     54			val.intval = true;
     55			extcon_set_property(info->edev, EXTCON_USB_HOST,
     56						EXTCON_PROP_USB_SS, val);
     57		}
     58		extcon_set_state_sync(info->edev, EXTCON_USB_HOST, !state);
     59	}
     60
     61	if (info->vbus_irq > 0) {
     62		/* check VBUS and update cable state */
     63		ret = irq_get_irqchip_state(info->vbus_irq,
     64				IRQCHIP_STATE_LINE_LEVEL, &state);
     65		if (ret)
     66			return;
     67
     68		if (state) {
     69			val.intval = true;
     70			extcon_set_property(info->edev, EXTCON_USB,
     71						EXTCON_PROP_USB_SS, val);
     72		}
     73		extcon_set_state_sync(info->edev, EXTCON_USB, state);
     74	}
     75}
     76
     77static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id)
     78{
     79	struct qcom_usb_extcon_info *info = dev_id;
     80
     81	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
     82			   info->debounce_jiffies);
     83
     84	return IRQ_HANDLED;
     85}
     86
     87static int qcom_usb_extcon_probe(struct platform_device *pdev)
     88{
     89	struct device *dev = &pdev->dev;
     90	struct qcom_usb_extcon_info *info;
     91	int ret;
     92
     93	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
     94	if (!info)
     95		return -ENOMEM;
     96
     97	info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable);
     98	if (IS_ERR(info->edev)) {
     99		dev_err(dev, "failed to allocate extcon device\n");
    100		return -ENOMEM;
    101	}
    102
    103	ret = devm_extcon_dev_register(dev, info->edev);
    104	if (ret < 0) {
    105		dev_err(dev, "failed to register extcon device\n");
    106		return ret;
    107	}
    108
    109	ret = extcon_set_property_capability(info->edev,
    110			EXTCON_USB, EXTCON_PROP_USB_SS);
    111	ret |= extcon_set_property_capability(info->edev,
    112			EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
    113	if (ret) {
    114		dev_err(dev, "failed to register extcon props rc=%d\n",
    115						ret);
    116		return ret;
    117	}
    118
    119	info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
    120
    121	ret = devm_delayed_work_autocancel(dev, &info->wq_detcable,
    122					   qcom_usb_extcon_detect_cable);
    123	if (ret)
    124		return ret;
    125
    126	info->id_irq = platform_get_irq_byname(pdev, "usb_id");
    127	if (info->id_irq > 0) {
    128		ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
    129					qcom_usb_irq_handler,
    130					IRQF_TRIGGER_RISING |
    131					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    132					pdev->name, info);
    133		if (ret < 0) {
    134			dev_err(dev, "failed to request handler for ID IRQ\n");
    135			return ret;
    136		}
    137	}
    138
    139	info->vbus_irq = platform_get_irq_byname(pdev, "usb_vbus");
    140	if (info->vbus_irq > 0) {
    141		ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
    142					qcom_usb_irq_handler,
    143					IRQF_TRIGGER_RISING |
    144					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    145					pdev->name, info);
    146		if (ret < 0) {
    147			dev_err(dev, "failed to request handler for VBUS IRQ\n");
    148			return ret;
    149		}
    150	}
    151
    152	if (info->id_irq < 0 && info->vbus_irq < 0) {
    153		dev_err(dev, "ID and VBUS IRQ not found\n");
    154		return -EINVAL;
    155	}
    156
    157	platform_set_drvdata(pdev, info);
    158	device_init_wakeup(dev, 1);
    159
    160	/* Perform initial detection */
    161	qcom_usb_extcon_detect_cable(&info->wq_detcable.work);
    162
    163	return 0;
    164}
    165
    166#ifdef CONFIG_PM_SLEEP
    167static int qcom_usb_extcon_suspend(struct device *dev)
    168{
    169	struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
    170	int ret = 0;
    171
    172	if (device_may_wakeup(dev)) {
    173		if (info->id_irq > 0)
    174			ret = enable_irq_wake(info->id_irq);
    175		if (info->vbus_irq > 0)
    176			ret = enable_irq_wake(info->vbus_irq);
    177	}
    178
    179	return ret;
    180}
    181
    182static int qcom_usb_extcon_resume(struct device *dev)
    183{
    184	struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
    185	int ret = 0;
    186
    187	if (device_may_wakeup(dev)) {
    188		if (info->id_irq > 0)
    189			ret = disable_irq_wake(info->id_irq);
    190		if (info->vbus_irq > 0)
    191			ret = disable_irq_wake(info->vbus_irq);
    192	}
    193
    194	return ret;
    195}
    196#endif
    197
    198static SIMPLE_DEV_PM_OPS(qcom_usb_extcon_pm_ops,
    199			 qcom_usb_extcon_suspend, qcom_usb_extcon_resume);
    200
    201static const struct of_device_id qcom_usb_extcon_dt_match[] = {
    202	{ .compatible = "qcom,pm8941-misc", },
    203	{ }
    204};
    205MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
    206
    207static struct platform_driver qcom_usb_extcon_driver = {
    208	.probe		= qcom_usb_extcon_probe,
    209	.driver		= {
    210		.name	= "extcon-pm8941-misc",
    211		.pm	= &qcom_usb_extcon_pm_ops,
    212		.of_match_table = qcom_usb_extcon_dt_match,
    213	},
    214};
    215module_platform_driver(qcom_usb_extcon_driver);
    216
    217MODULE_DESCRIPTION("QCOM USB ID extcon driver");
    218MODULE_AUTHOR("Stephen Boyd <stephen.boyd@linaro.org>");
    219MODULE_LICENSE("GPL v2");