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

qcom_eud.c (5965B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
      4 */
      5
      6#include <linux/bitops.h>
      7#include <linux/err.h>
      8#include <linux/interrupt.h>
      9#include <linux/io.h>
     10#include <linux/iopoll.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/platform_device.h>
     15#include <linux/slab.h>
     16#include <linux/sysfs.h>
     17#include <linux/usb/role.h>
     18
     19#define EUD_REG_INT1_EN_MASK	0x0024
     20#define EUD_REG_INT_STATUS_1	0x0044
     21#define EUD_REG_CTL_OUT_1	0x0074
     22#define EUD_REG_VBUS_INT_CLR	0x0080
     23#define EUD_REG_CSR_EUD_EN	0x1014
     24#define EUD_REG_SW_ATTACH_DET	0x1018
     25#define EUD_REG_EUD_EN2        0x0000
     26
     27#define EUD_ENABLE		BIT(0)
     28#define EUD_INT_PET_EUD	BIT(0)
     29#define EUD_INT_VBUS		BIT(2)
     30#define EUD_INT_SAFE_MODE	BIT(4)
     31#define EUD_INT_ALL		(EUD_INT_VBUS | EUD_INT_SAFE_MODE)
     32
     33struct eud_chip {
     34	struct device			*dev;
     35	struct usb_role_switch		*role_sw;
     36	void __iomem			*base;
     37	void __iomem			*mode_mgr;
     38	unsigned int			int_status;
     39	int				irq;
     40	bool				enabled;
     41	bool				usb_attached;
     42};
     43
     44static int enable_eud(struct eud_chip *priv)
     45{
     46	writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN);
     47	writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE,
     48			priv->base + EUD_REG_INT1_EN_MASK);
     49	writel(1, priv->mode_mgr + EUD_REG_EUD_EN2);
     50
     51	return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE);
     52}
     53
     54static void disable_eud(struct eud_chip *priv)
     55{
     56	writel(0, priv->base + EUD_REG_CSR_EUD_EN);
     57	writel(0, priv->mode_mgr + EUD_REG_EUD_EN2);
     58}
     59
     60static ssize_t enable_show(struct device *dev,
     61		struct device_attribute *attr, char *buf)
     62{
     63	struct eud_chip *chip = dev_get_drvdata(dev);
     64
     65	return sysfs_emit(buf, "%d\n", chip->enabled);
     66}
     67
     68static ssize_t enable_store(struct device *dev,
     69		struct device_attribute *attr,
     70		const char *buf, size_t count)
     71{
     72	struct eud_chip *chip = dev_get_drvdata(dev);
     73	bool enable;
     74	int ret;
     75
     76	if (kstrtobool(buf, &enable))
     77		return -EINVAL;
     78
     79	if (enable) {
     80		ret = enable_eud(chip);
     81		if (!ret)
     82			chip->enabled = enable;
     83		else
     84			disable_eud(chip);
     85	} else {
     86		disable_eud(chip);
     87	}
     88
     89	return count;
     90}
     91
     92static DEVICE_ATTR_RW(enable);
     93
     94static struct attribute *eud_attrs[] = {
     95	&dev_attr_enable.attr,
     96	NULL,
     97};
     98ATTRIBUTE_GROUPS(eud);
     99
    100static void usb_attach_detach(struct eud_chip *chip)
    101{
    102	u32 reg;
    103
    104	/* read ctl_out_1[4] to find USB attach or detach event */
    105	reg = readl(chip->base + EUD_REG_CTL_OUT_1);
    106	chip->usb_attached = reg & EUD_INT_SAFE_MODE;
    107}
    108
    109static void pet_eud(struct eud_chip *chip)
    110{
    111	u32 reg;
    112	int ret;
    113
    114	/* When the EUD_INT_PET_EUD in SW_ATTACH_DET is set, the cable has been
    115	 * disconnected and we need to detach the pet to check if EUD is in safe
    116	 * mode before attaching again.
    117	 */
    118	reg = readl(chip->base + EUD_REG_SW_ATTACH_DET);
    119	if (reg & EUD_INT_PET_EUD) {
    120		/* Detach & Attach pet for EUD */
    121		writel(0, chip->base + EUD_REG_SW_ATTACH_DET);
    122		/* Delay to make sure detach pet is done before attach pet */
    123		ret = readl_poll_timeout(chip->base + EUD_REG_SW_ATTACH_DET,
    124					reg, (reg == 0), 1, 100);
    125		if (ret) {
    126			dev_err(chip->dev, "Detach pet failed\n");
    127			return;
    128		}
    129	}
    130	/* Attach pet for EUD */
    131	writel(EUD_INT_PET_EUD, chip->base + EUD_REG_SW_ATTACH_DET);
    132}
    133
    134static irqreturn_t handle_eud_irq(int irq, void *data)
    135{
    136	struct eud_chip *chip = data;
    137	u32 reg;
    138
    139	reg = readl(chip->base + EUD_REG_INT_STATUS_1);
    140	switch (reg & EUD_INT_ALL) {
    141	case EUD_INT_VBUS:
    142		usb_attach_detach(chip);
    143		return IRQ_WAKE_THREAD;
    144	case EUD_INT_SAFE_MODE:
    145		pet_eud(chip);
    146		return IRQ_HANDLED;
    147	default:
    148		return IRQ_NONE;
    149	}
    150}
    151
    152static irqreturn_t handle_eud_irq_thread(int irq, void *data)
    153{
    154	struct eud_chip *chip = data;
    155	int ret;
    156
    157	if (chip->usb_attached)
    158		ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_DEVICE);
    159	else
    160		ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_HOST);
    161	if (ret)
    162		dev_err(chip->dev, "failed to set role switch\n");
    163
    164	/* set and clear vbus_int_clr[0] to clear interrupt */
    165	writel(BIT(0), chip->base + EUD_REG_VBUS_INT_CLR);
    166	writel(0, chip->base + EUD_REG_VBUS_INT_CLR);
    167
    168	return IRQ_HANDLED;
    169}
    170
    171static void eud_role_switch_release(void *data)
    172{
    173	struct eud_chip *chip = data;
    174
    175	usb_role_switch_put(chip->role_sw);
    176}
    177
    178static int eud_probe(struct platform_device *pdev)
    179{
    180	struct eud_chip *chip;
    181	int ret;
    182
    183	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
    184	if (!chip)
    185		return -ENOMEM;
    186
    187	chip->dev = &pdev->dev;
    188
    189	chip->role_sw = usb_role_switch_get(&pdev->dev);
    190	if (IS_ERR(chip->role_sw))
    191		return dev_err_probe(chip->dev, PTR_ERR(chip->role_sw),
    192					"failed to get role switch\n");
    193
    194	ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip);
    195	if (ret)
    196		return dev_err_probe(chip->dev, ret,
    197				"failed to add role switch release action\n");
    198
    199	chip->base = devm_platform_ioremap_resource(pdev, 0);
    200	if (IS_ERR(chip->base))
    201		return PTR_ERR(chip->base);
    202
    203	chip->mode_mgr = devm_platform_ioremap_resource(pdev, 1);
    204	if (IS_ERR(chip->mode_mgr))
    205		return PTR_ERR(chip->mode_mgr);
    206
    207	chip->irq = platform_get_irq(pdev, 0);
    208	ret = devm_request_threaded_irq(&pdev->dev, chip->irq, handle_eud_irq,
    209			handle_eud_irq_thread, IRQF_ONESHOT, NULL, chip);
    210	if (ret)
    211		return dev_err_probe(chip->dev, ret, "failed to allocate irq\n");
    212
    213	enable_irq_wake(chip->irq);
    214
    215	platform_set_drvdata(pdev, chip);
    216
    217	return 0;
    218}
    219
    220static int eud_remove(struct platform_device *pdev)
    221{
    222	struct eud_chip *chip = platform_get_drvdata(pdev);
    223
    224	if (chip->enabled)
    225		disable_eud(chip);
    226
    227	device_init_wakeup(&pdev->dev, false);
    228	disable_irq_wake(chip->irq);
    229
    230	return 0;
    231}
    232
    233static const struct of_device_id eud_dt_match[] = {
    234	{ .compatible = "qcom,sc7280-eud" },
    235	{ }
    236};
    237MODULE_DEVICE_TABLE(of, eud_dt_match);
    238
    239static struct platform_driver eud_driver = {
    240	.probe	= eud_probe,
    241	.remove	= eud_remove,
    242	.driver	= {
    243		.name = "qcom_eud",
    244		.dev_groups = eud_groups,
    245		.of_match_table = eud_dt_match,
    246	},
    247};
    248module_platform_driver(eud_driver);
    249
    250MODULE_DESCRIPTION("QTI EUD driver");
    251MODULE_LICENSE("GPL v2");