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-palmas.c (13411B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Palmas USB transceiver driver
      4 *
      5 * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
      6 * Author: Graeme Gregory <gg@slimlogic.co.uk>
      7 * Author: Kishon Vijay Abraham I <kishon@ti.com>
      8 * Based on twl6030_usb.c
      9 * Author: Hema HK <hemahk@ti.com>
     10 */
     11
     12#include <linux/devm-helpers.h>
     13#include <linux/module.h>
     14#include <linux/interrupt.h>
     15#include <linux/platform_device.h>
     16#include <linux/slab.h>
     17#include <linux/err.h>
     18#include <linux/mfd/palmas.h>
     19#include <linux/of.h>
     20#include <linux/of_platform.h>
     21#include <linux/of_gpio.h>
     22#include <linux/gpio/consumer.h>
     23#include <linux/workqueue.h>
     24
     25#define USB_GPIO_DEBOUNCE_MS	20	/* ms */
     26
     27static const unsigned int palmas_extcon_cable[] = {
     28	EXTCON_USB,
     29	EXTCON_USB_HOST,
     30	EXTCON_NONE,
     31};
     32
     33static void palmas_usb_wakeup(struct palmas *palmas, int enable)
     34{
     35	if (enable)
     36		palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP,
     37			PALMAS_USB_WAKEUP_ID_WK_UP_COMP);
     38	else
     39		palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0);
     40}
     41
     42static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
     43{
     44	struct palmas_usb *palmas_usb = _palmas_usb;
     45	struct extcon_dev *edev = palmas_usb->edev;
     46	unsigned int vbus_line_state;
     47
     48	palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE,
     49		PALMAS_INT3_LINE_STATE, &vbus_line_state);
     50
     51	if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
     52		if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
     53			palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
     54			extcon_set_state_sync(edev, EXTCON_USB, true);
     55			dev_dbg(palmas_usb->dev, "USB cable is attached\n");
     56		} else {
     57			dev_dbg(palmas_usb->dev,
     58				"Spurious connect event detected\n");
     59		}
     60	} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
     61		if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
     62			palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
     63			extcon_set_state_sync(edev, EXTCON_USB, false);
     64			dev_dbg(palmas_usb->dev, "USB cable is detached\n");
     65		} else {
     66			dev_dbg(palmas_usb->dev,
     67				"Spurious disconnect event detected\n");
     68		}
     69	}
     70
     71	return IRQ_HANDLED;
     72}
     73
     74static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
     75{
     76	unsigned int set, id_src;
     77	struct palmas_usb *palmas_usb = _palmas_usb;
     78	struct extcon_dev *edev = palmas_usb->edev;
     79
     80	palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
     81		PALMAS_USB_ID_INT_LATCH_SET, &set);
     82	palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
     83		PALMAS_USB_ID_INT_SRC, &id_src);
     84
     85	if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) &&
     86				(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
     87		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
     88			PALMAS_USB_ID_INT_LATCH_CLR,
     89			PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
     90		palmas_usb->linkstat = PALMAS_USB_STATE_ID;
     91		extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
     92		dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
     93	} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
     94				(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
     95		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
     96			PALMAS_USB_ID_INT_LATCH_CLR,
     97			PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
     98		palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
     99		extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
    100		dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
    101	} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
    102				(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
    103		palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
    104		extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
    105		dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
    106	} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
    107				(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
    108		palmas_usb->linkstat = PALMAS_USB_STATE_ID;
    109		extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
    110		dev_dbg(palmas_usb->dev, " USB-HOST cable is attached\n");
    111	}
    112
    113	return IRQ_HANDLED;
    114}
    115
    116static void palmas_gpio_id_detect(struct work_struct *work)
    117{
    118	int id;
    119	struct palmas_usb *palmas_usb = container_of(to_delayed_work(work),
    120						     struct palmas_usb,
    121						     wq_detectid);
    122	struct extcon_dev *edev = palmas_usb->edev;
    123
    124	if (!palmas_usb->id_gpiod)
    125		return;
    126
    127	id = gpiod_get_value_cansleep(palmas_usb->id_gpiod);
    128
    129	if (id) {
    130		extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
    131		dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
    132	} else {
    133		extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
    134		dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
    135	}
    136}
    137
    138static irqreturn_t palmas_gpio_id_irq_handler(int irq, void *_palmas_usb)
    139{
    140	struct palmas_usb *palmas_usb = _palmas_usb;
    141
    142	queue_delayed_work(system_power_efficient_wq, &palmas_usb->wq_detectid,
    143			   palmas_usb->sw_debounce_jiffies);
    144
    145	return IRQ_HANDLED;
    146}
    147
    148static void palmas_enable_irq(struct palmas_usb *palmas_usb)
    149{
    150	palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
    151		PALMAS_USB_VBUS_CTRL_SET,
    152		PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP);
    153
    154	if (palmas_usb->enable_id_detection) {
    155		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
    156			     PALMAS_USB_ID_CTRL_SET,
    157			     PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
    158
    159		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
    160			     PALMAS_USB_ID_INT_EN_HI_SET,
    161			     PALMAS_USB_ID_INT_EN_HI_SET_ID_GND |
    162			     PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
    163	}
    164
    165	if (palmas_usb->enable_vbus_detection)
    166		palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
    167
    168	/* cold plug for host mode needs this delay */
    169	if (palmas_usb->enable_id_detection) {
    170		msleep(30);
    171		palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb);
    172	}
    173}
    174
    175static int palmas_usb_probe(struct platform_device *pdev)
    176{
    177	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
    178	struct palmas_usb_platform_data	*pdata = dev_get_platdata(&pdev->dev);
    179	struct device_node *node = pdev->dev.of_node;
    180	struct palmas_usb *palmas_usb;
    181	int status;
    182
    183	if (!palmas) {
    184		dev_err(&pdev->dev, "failed to get valid parent\n");
    185		return -EINVAL;
    186	}
    187
    188	palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
    189	if (!palmas_usb)
    190		return -ENOMEM;
    191
    192	if (node && !pdata) {
    193		palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup");
    194		palmas_usb->enable_id_detection = of_property_read_bool(node,
    195						"ti,enable-id-detection");
    196		palmas_usb->enable_vbus_detection = of_property_read_bool(node,
    197						"ti,enable-vbus-detection");
    198	} else {
    199		palmas_usb->wakeup = true;
    200		palmas_usb->enable_id_detection = true;
    201		palmas_usb->enable_vbus_detection = true;
    202
    203		if (pdata)
    204			palmas_usb->wakeup = pdata->wakeup;
    205	}
    206
    207	palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id",
    208							GPIOD_IN);
    209	if (IS_ERR(palmas_usb->id_gpiod))
    210		return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->id_gpiod),
    211				     "failed to get id gpio\n");
    212
    213	palmas_usb->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
    214							GPIOD_IN);
    215	if (IS_ERR(palmas_usb->vbus_gpiod))
    216		return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->vbus_gpiod),
    217				     "failed to get id gpio\n");
    218
    219	if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) {
    220		palmas_usb->enable_id_detection = false;
    221		palmas_usb->enable_gpio_id_detection = true;
    222	}
    223
    224	if (palmas_usb->enable_vbus_detection && palmas_usb->vbus_gpiod) {
    225		palmas_usb->enable_vbus_detection = false;
    226		palmas_usb->enable_gpio_vbus_detection = true;
    227	}
    228
    229	if (palmas_usb->enable_gpio_id_detection) {
    230		u32 debounce;
    231
    232		if (of_property_read_u32(node, "debounce-delay-ms", &debounce))
    233			debounce = USB_GPIO_DEBOUNCE_MS;
    234
    235		status = gpiod_set_debounce(palmas_usb->id_gpiod,
    236					    debounce * 1000);
    237		if (status < 0)
    238			palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
    239	}
    240
    241	status = devm_delayed_work_autocancel(&pdev->dev,
    242					      &palmas_usb->wq_detectid,
    243					      palmas_gpio_id_detect);
    244	if (status)
    245		return status;
    246
    247	palmas->usb = palmas_usb;
    248	palmas_usb->palmas = palmas;
    249
    250	palmas_usb->dev	 = &pdev->dev;
    251
    252	palmas_usb_wakeup(palmas, palmas_usb->wakeup);
    253
    254	platform_set_drvdata(pdev, palmas_usb);
    255
    256	palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
    257						    palmas_extcon_cable);
    258	if (IS_ERR(palmas_usb->edev)) {
    259		dev_err(&pdev->dev, "failed to allocate extcon device\n");
    260		return -ENOMEM;
    261	}
    262
    263	status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
    264	if (status) {
    265		dev_err(&pdev->dev, "failed to register extcon device\n");
    266		return status;
    267	}
    268
    269	if (palmas_usb->enable_id_detection) {
    270		palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
    271							     PALMAS_ID_OTG_IRQ);
    272		palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
    273							 PALMAS_ID_IRQ);
    274		status = devm_request_threaded_irq(palmas_usb->dev,
    275				palmas_usb->id_irq,
    276				NULL, palmas_id_irq_handler,
    277				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
    278				IRQF_ONESHOT,
    279				"palmas_usb_id", palmas_usb);
    280		if (status < 0) {
    281			dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
    282					palmas_usb->id_irq, status);
    283			return status;
    284		}
    285	} else if (palmas_usb->enable_gpio_id_detection) {
    286		palmas_usb->gpio_id_irq = gpiod_to_irq(palmas_usb->id_gpiod);
    287		if (palmas_usb->gpio_id_irq < 0) {
    288			dev_err(&pdev->dev, "failed to get id irq\n");
    289			return palmas_usb->gpio_id_irq;
    290		}
    291		status = devm_request_threaded_irq(&pdev->dev,
    292						   palmas_usb->gpio_id_irq,
    293						   NULL,
    294						   palmas_gpio_id_irq_handler,
    295						   IRQF_TRIGGER_RISING |
    296						   IRQF_TRIGGER_FALLING |
    297						   IRQF_ONESHOT,
    298						   "palmas_usb_id",
    299						   palmas_usb);
    300		if (status < 0) {
    301			dev_err(&pdev->dev,
    302				"failed to request handler for id irq\n");
    303			return status;
    304		}
    305	}
    306
    307	if (palmas_usb->enable_vbus_detection) {
    308		palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
    309						       PALMAS_VBUS_OTG_IRQ);
    310		palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
    311							   PALMAS_VBUS_IRQ);
    312		status = devm_request_threaded_irq(palmas_usb->dev,
    313				palmas_usb->vbus_irq, NULL,
    314				palmas_vbus_irq_handler,
    315				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
    316				IRQF_ONESHOT,
    317				"palmas_usb_vbus", palmas_usb);
    318		if (status < 0) {
    319			dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
    320					palmas_usb->vbus_irq, status);
    321			return status;
    322		}
    323	} else if (palmas_usb->enable_gpio_vbus_detection) {
    324		/* remux GPIO_1 as VBUSDET */
    325		status = palmas_update_bits(palmas,
    326			PALMAS_PU_PD_OD_BASE,
    327			PALMAS_PRIMARY_SECONDARY_PAD1,
    328			PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK,
    329			(1 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT));
    330		if (status < 0) {
    331			dev_err(&pdev->dev, "can't remux GPIO1\n");
    332			return status;
    333		}
    334
    335		palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
    336						       PALMAS_VBUS_OTG_IRQ);
    337		palmas_usb->gpio_vbus_irq = gpiod_to_irq(palmas_usb->vbus_gpiod);
    338		if (palmas_usb->gpio_vbus_irq < 0) {
    339			dev_err(&pdev->dev, "failed to get vbus irq\n");
    340			return palmas_usb->gpio_vbus_irq;
    341		}
    342		status = devm_request_threaded_irq(&pdev->dev,
    343						palmas_usb->gpio_vbus_irq,
    344						NULL,
    345						palmas_vbus_irq_handler,
    346						IRQF_TRIGGER_FALLING |
    347						IRQF_TRIGGER_RISING |
    348						IRQF_ONESHOT,
    349						"palmas_usb_vbus",
    350						palmas_usb);
    351		if (status < 0) {
    352			dev_err(&pdev->dev,
    353				"failed to request handler for vbus irq\n");
    354			return status;
    355		}
    356	}
    357
    358	palmas_enable_irq(palmas_usb);
    359	/* perform initial detection */
    360	if (palmas_usb->enable_gpio_vbus_detection)
    361		palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
    362	palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
    363	device_set_wakeup_capable(&pdev->dev, true);
    364	return 0;
    365}
    366
    367#ifdef CONFIG_PM_SLEEP
    368static int palmas_usb_suspend(struct device *dev)
    369{
    370	struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
    371
    372	if (device_may_wakeup(dev)) {
    373		if (palmas_usb->enable_vbus_detection)
    374			enable_irq_wake(palmas_usb->vbus_irq);
    375		if (palmas_usb->enable_gpio_vbus_detection)
    376			enable_irq_wake(palmas_usb->gpio_vbus_irq);
    377		if (palmas_usb->enable_id_detection)
    378			enable_irq_wake(palmas_usb->id_irq);
    379		if (palmas_usb->enable_gpio_id_detection)
    380			enable_irq_wake(palmas_usb->gpio_id_irq);
    381	}
    382	return 0;
    383}
    384
    385static int palmas_usb_resume(struct device *dev)
    386{
    387	struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
    388
    389	if (device_may_wakeup(dev)) {
    390		if (palmas_usb->enable_vbus_detection)
    391			disable_irq_wake(palmas_usb->vbus_irq);
    392		if (palmas_usb->enable_gpio_vbus_detection)
    393			disable_irq_wake(palmas_usb->gpio_vbus_irq);
    394		if (palmas_usb->enable_id_detection)
    395			disable_irq_wake(palmas_usb->id_irq);
    396		if (palmas_usb->enable_gpio_id_detection)
    397			disable_irq_wake(palmas_usb->gpio_id_irq);
    398	}
    399
    400	/* check if GPIO states changed while suspend/resume */
    401	if (palmas_usb->enable_gpio_vbus_detection)
    402		palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
    403	palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
    404
    405	return 0;
    406};
    407#endif
    408
    409static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);
    410
    411static const struct of_device_id of_palmas_match_tbl[] = {
    412	{ .compatible = "ti,palmas-usb", },
    413	{ .compatible = "ti,palmas-usb-vid", },
    414	{ .compatible = "ti,twl6035-usb", },
    415	{ .compatible = "ti,twl6035-usb-vid", },
    416	{ /* end */ }
    417};
    418
    419static struct platform_driver palmas_usb_driver = {
    420	.probe = palmas_usb_probe,
    421	.driver = {
    422		.name = "palmas-usb",
    423		.of_match_table = of_palmas_match_tbl,
    424		.pm = &palmas_pm_ops,
    425	},
    426};
    427
    428module_platform_driver(palmas_usb_driver);
    429
    430MODULE_ALIAS("platform:palmas-usb");
    431MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
    432MODULE_DESCRIPTION("Palmas USB transceiver driver");
    433MODULE_LICENSE("GPL");
    434MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);