From 6aa35345823623e57bf486357cb8dd83229f9204 Mon Sep 17 00:00:00 2001 From: Jay Jayanna Date: Wed, 11 Sep 2019 16:17:21 -0700 Subject: [PATCH 1/2] soc: qcom: Add snapshot for QSEE IPC IRQ driver This snapshot is taken as of msm-4.19 'commit ("Merge "msm: npu: Add bandwidth monitor support"")'. Update made to the license. This is a basic driver for cascading interrupts from QSEE. This driver will perform the common interrupt handling code for interrupts from QSEE incluiding clearing the interrupt through a register write. Change-Id: I8336a60749d3d642c96d0d045f0874f56812d04e Signed-off-by: Jay Jayanna --- drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qsee_ipc_irq.c | 362 ++++++++++++++++++++++++++++++++ 3 files changed, 372 insertions(+) create mode 100644 drivers/soc/qcom/qsee_ipc_irq.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a772fdab6a39..e6ff08b06be3 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -386,6 +386,15 @@ config SENSORS_SSC exercising sensor use-cases. This driver loads firmware files for SSC and also does time synchronization with DSP clock. +config QSEE_IPC_IRQ + tristate "QSEE interrupt manager" + help + The QSEE IPC IRQ controller manages the interrupts from the QTI + secure execution environment. This interrupt controller will use + the registers in the spcs region to mask and clear interrupts. + Clients can use this driver to avoid adding common interrupt handling + code. + config MSM_CDSP_LOADER tristate "CDSP loader support" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 712d322351e4..2b0d89676771 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o obj-$(CONFIG_SENSORS_SSC) += sensors_ssc.o +obj-$(CONFIG_QSEE_IPC_IRQ) += qsee_ipc_irq.o diff --git a/drivers/soc/qcom/qsee_ipc_irq.c b/drivers/soc/qcom/qsee_ipc_irq.c new file mode 100644 index 000000000000..30d1351c4822 --- /dev/null +++ b/drivers/soc/qcom/qsee_ipc_irq.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BANK_IRQ 32 + +#define hwirq_to_index(_irq) (_irq / MAX_BANK_IRQ) +#define hwirq_to_bit(_irq) (_irq % MAX_BANK_IRQ) +#define to_hwirq(_index, _bit) ((_index * MAX_BANK_IRQ) + _bit) + +struct qsee_irq_data { + const char *name; + u32 status; + u32 clear; + u32 mask; + u32 msb; +}; + +struct qsee_irq_bank { + const struct qsee_irq_data *data; + int irq; + + DECLARE_BITMAP(irq_enabled, MAX_BANK_IRQ); + DECLARE_BITMAP(irq_rising, MAX_BANK_IRQ); + DECLARE_BITMAP(irq_falling, MAX_BANK_IRQ); +}; + +struct qsee_irq { + struct device *dev; + + struct irq_domain *domain; + struct regmap *regmap; + + int num_banks; + struct qsee_irq_bank *banks; +}; + +/** + * qsee_intr() - interrupt handler for incoming notifications + * @irq: unused + * @data: qsee driver context + * + * Handle notifications from the remote side to handle newly allocated entries + * or any changes to the state bits of existing entries. + */ +static irqreturn_t qsee_intr(int irq, void *data) +{ + struct qsee_irq *qirq = data; + struct qsee_irq_bank *bank = NULL; + struct irq_desc *desc; + int irq_pin; + u32 status; + u32 mask; + int i; + int j; + + for (i = 0; i < qirq->num_banks; i++) { + if (qirq->banks[i].irq == irq) { + bank = &qirq->banks[i]; + break; + } + } + if (!bank) { + dev_err(qirq->dev, "Unable to find bank for irq:%d\n", irq); + return IRQ_HANDLED; + } + + if (regmap_read(qirq->regmap, bank->data->status, &status)) { + dev_err(qirq->dev, "Error reading irq %d status\n", irq); + return IRQ_HANDLED; + } + if (regmap_read(qirq->regmap, bank->data->mask, &mask)) { + dev_err(qirq->dev, "Error reading irq %d mask\n", irq); + return IRQ_HANDLED; + } + + for_each_set_bit(j, bank->irq_enabled, bank->data->msb) { + if (!(status & BIT(j))) + continue; + + irq_pin = irq_find_mapping(qirq->domain, to_hwirq(i, j)); + desc = irq_to_desc(irq_pin); + + regmap_write(qirq->regmap, bank->data->clear, BIT(j)); + if (desc) + handle_level_irq(desc); + } + + return IRQ_HANDLED; +} + +static void qsee_mask_irq(struct irq_data *irqd) +{ + struct qsee_irq *qirq = irq_data_get_irq_chip_data(irqd); + irq_hw_number_t irq = irqd_to_hwirq(irqd); + struct qsee_irq_bank *bank; + int index; + u32 mask; + int bit; + + index = hwirq_to_index(irq); + bit = hwirq_to_bit(irq); + bank = &qirq->banks[index]; + + regmap_read(qirq->regmap, bank->data->mask, &mask); + mask |= BIT(bit); + regmap_write(qirq->regmap, bank->data->mask, mask); + + clear_bit(bit, bank->irq_enabled); +} + +static void qsee_unmask_irq(struct irq_data *irqd) +{ + struct qsee_irq *qirq = irq_data_get_irq_chip_data(irqd); + irq_hw_number_t irq = irqd_to_hwirq(irqd); + struct qsee_irq_bank *bank; + int index; + u32 mask; + int bit; + + index = hwirq_to_index(irq); + bit = hwirq_to_bit(irq); + bank = &qirq->banks[index]; + + regmap_read(qirq->regmap, bank->data->mask, &mask); + mask &= ~(BIT(bit)); + regmap_write(qirq->regmap, bank->data->mask, mask); + + set_bit(bit, bank->irq_enabled); +} + +static int qsee_set_irq_type(struct irq_data *irqd, unsigned int type) +{ + struct qsee_irq *qirq = irq_data_get_irq_chip_data(irqd); + irq_hw_number_t irq = irqd_to_hwirq(irqd); + struct qsee_irq_bank *bank; + int index; + int bit; + + index = hwirq_to_index(irq); + bit = hwirq_to_bit(irq); + bank = &qirq->banks[index]; + + if (type & IRQ_TYPE_LEVEL_HIGH) + return 0; + + if (!(type & IRQ_TYPE_EDGE_BOTH)) + return -EINVAL; + + if (type & IRQ_TYPE_EDGE_RISING) + set_bit(bit, bank->irq_rising); + else + clear_bit(bit, bank->irq_rising); + + if (type & IRQ_TYPE_EDGE_FALLING) + set_bit(bit, bank->irq_falling); + else + clear_bit(bit, bank->irq_falling); + + return 0; +} + +static struct irq_chip qsee_irq_chip = { + .name = "qsee", + .irq_mask = qsee_mask_irq, + .irq_unmask = qsee_unmask_irq, + .irq_set_type = qsee_set_irq_type, +}; + +static int qsee_irq_map(struct irq_domain *d, + unsigned int irq, + irq_hw_number_t hw) +{ + struct qsee_irq *qirq = d->host_data; + + irq_set_chip_and_handler(irq, &qsee_irq_chip, handle_level_irq); + irq_set_chip_data(irq, qirq); + irq_set_noprobe(irq); + + return 0; +} + +static int qsee_irq_xlate_threecell(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, + unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_type) +{ + struct qsee_irq *qirq = d->host_data; + u32 index, bit; + + if (WARN_ON(intsize < 3)) + return -EINVAL; + + index = intspec[0]; + if (WARN_ON(index >= qirq->num_banks)) + return -EINVAL; + + bit = intspec[1]; + if (WARN_ON(bit >= qirq->banks[index].data->msb)) + return -EINVAL; + + *out_hwirq = (index * MAX_BANK_IRQ) + bit; + *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; + + return 0; +} + +static const struct irq_domain_ops qsee_irq_ops = { + .map = qsee_irq_map, + .xlate = qsee_irq_xlate_threecell, +}; + +static int qsee_irq_probe(struct platform_device *pdev) +{ + const struct qsee_irq_data *data; + struct device *dev = &pdev->dev; + struct qsee_irq_bank *bank; + struct device_node *syscon; + struct qsee_irq *qirq; + int irq_count; + u32 mask; + int idx; + int ret; + int i; + + qirq = devm_kzalloc(dev, sizeof(*qirq), GFP_KERNEL); + if (!qirq) + return -ENOMEM; + + platform_set_drvdata(pdev, qirq); + qirq->dev = dev; + + syscon = of_parse_phandle(dev->of_node, "syscon", 0); + if (!syscon) { + dev_err(dev, "no syscon node\n"); + return -ENODEV; + } + + qirq->regmap = syscon_node_to_regmap(syscon); + if (IS_ERR(qirq->regmap)) + return PTR_ERR(qirq->regmap); + + data = (struct qsee_irq_data *)of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + irq_count = platform_irq_count(pdev); + qirq->banks = devm_kzalloc(dev, sizeof(*qirq->banks) * irq_count, + GFP_KERNEL); + if (!qirq->banks) + return -ENOMEM; + + qirq->num_banks = irq_count; + for (i = 0; data[i].name; i++) { + idx = of_property_match_string(dev->of_node, "interrupt-names", + data[i].name); + if (idx < 0) + return -EINVAL; + + bank = &qirq->banks[idx]; + bank->data = &data[i]; + bank->irq = platform_get_irq(pdev, idx); + if (bank->irq < 0) { + dev_err(dev, "unable to acquire %s interrupt\n", + data[i].name); + return -EINVAL; + } + + /* Mask all interrupts until client registers */ + mask = (1 << bank->data->msb) - 1; + regmap_write(qirq->regmap, bank->data->mask, mask); + + ret = devm_request_irq(dev, bank->irq, qsee_intr, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + "qsee_irq", qirq); + if (ret) { + dev_err(dev, "failed to request interrupt\n"); + return ret; + } + } + + qirq->domain = irq_domain_add_linear(dev->of_node, 32 * irq_count, + &qsee_irq_ops, qirq); + if (!qirq->domain) { + dev_err(dev, "failed to add irq_domain\n"); + return -ENOMEM; + } + + return 0; +} + +static int qsee_irq_remove(struct platform_device *pdev) +{ + struct qsee_irq *qirq = platform_get_drvdata(pdev); + + irq_domain_remove(qirq->domain); + + return 0; +} + +static const struct qsee_irq_data qsee_irq_data_init[] = { + { + .name = "sp_ipc0", + .status = 0x6000, + .clear = 0x6008, + .mask = 0x601C, + .msb = 4, + }, + { + .name = "sp_ipc1", + .status = 0x8000, + .clear = 0x8008, + .mask = 0x801C, + .msb = 4, + }, + {}, +}; + +static const struct of_device_id qsee_irq_of_match[] = { + { .compatible = "qcom,sm8150-qsee-irq", .data = &qsee_irq_data_init}, + { .compatible = "qcom,kona-qsee-irq", .data = &qsee_irq_data_init}, + {}, +}; +MODULE_DEVICE_TABLE(of, qsee_irq_of_match); + +static struct platform_driver qsee_irq_driver = { + .probe = qsee_irq_probe, + .remove = qsee_irq_remove, + .driver = { + .name = "qsee_irq", + .of_match_table = qsee_irq_of_match, + }, +}; + +static int __init qsee_irq_init(void) +{ + int rc; + + rc = platform_driver_register(&qsee_irq_driver); + if (rc) + pr_err("%s: platform driver reg failed %d\n", __func__, rc); + + return rc; +} +postcore_initcall(qsee_irq_init); + +MODULE_DESCRIPTION("QTI Secure Execution Environment IRQ driver"); +MODULE_LICENSE("GPL v2"); From af86cde1f5a10573007f4887277b649e698987b7 Mon Sep 17 00:00:00 2001 From: Jay Jayanna Date: Tue, 8 Oct 2019 10:47:03 -0700 Subject: [PATCH 2/2] arm64: defconfig: Enable IPC IRQ driver for Lahaina Enable QSEE IPC IRQ driver for Lahaina. This can now be compiled as a module. Change-Id: I3c196a6f819c3422663379cb72b0a9355c7c0233 Signed-off-by: Jay Jayanna --- arch/arm64/configs/vendor/lahaina_GKI.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/vendor/lahaina_GKI.config b/arch/arm64/configs/vendor/lahaina_GKI.config index 4d424360200f..b1fc688d0a5f 100644 --- a/arch/arm64/configs/vendor/lahaina_GKI.config +++ b/arch/arm64/configs/vendor/lahaina_GKI.config @@ -33,5 +33,6 @@ CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y CONFIG_MSM_PIL_SSR_GENERIC=m CONFIG_MSM_SERVICE_LOCATOR=m CONFIG_MSM_SERVICE_NOTIFIER=m +CONFIG_QSEE_IPC_IRQ=m CONFIG_REGULATOR_RPMH=m CONFIG_MSM_GCC_LAHAINA=m