From patchwork Thu Jul 2 14:17:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Grzegorz Jaszczyk X-Patchwork-Id: 11639063 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EF05413B6 for ; Thu, 2 Jul 2020 14:29:52 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B90CD20772 for ; Thu, 2 Jul 2020 14:29:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="FJjrdm7j"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="OPuK0bs8" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B90CD20772 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:MIME-Version:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:References:In-Reply-To:Message-Id:Date:Subject:To: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=YKAKWaoYmdY+gOMgn0tPaDtxlSvNUM+8ZSh6RWhA6bo=; b=FJjrdm7jDzDbuP7CnXsPo4lsGk oC1gRXzwGWzl7Cabwt0zuLfKCqzB7FH0SRKd6ewB5zD0TWLlpbvDs+g+UDqLPUEqSGH+54MmuksRM 38UTRgDi2DQ0vU37tGCAz0xJ0TlHw4L7tPHkuqi4iyS7rWza1pn1J/DQeKz4C40R2gwKnXFabNebD RuZ5lC+JdqIW3zXRbatYmqoZBuCf45DcEtRxvbQI2Z/ixfvQ29mrta8Y2k1/mKG+7XkovHcwvsHpQ irqr2yiJJsi0u7Lq4CqRYbjiYMBec/fVChyREMTumIyuRvue7z6nX1h5YSaxw6t9llnD5P/JvABh6 GZTMJZUQ==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jr0Ca-0000ca-AS; Thu, 02 Jul 2020 14:28:36 +0000 Received: from mail-ed1-x541.google.com ([2a00:1450:4864:20::541]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jr0CT-0000ZM-VO for linux-arm-kernel@lists.infradead.org; Thu, 02 Jul 2020 14:28:32 +0000 Received: by mail-ed1-x541.google.com with SMTP id e22so24117157edq.8 for ; Thu, 02 Jul 2020 07:28:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=/g1pL3/hSl0fTsy5dL717qIUgAnsVErwAcv2lpeHoaY=; b=OPuK0bs8473Rb8SzUxiI+RRd1mfxCrbbfDTMSxXud0kgnBLJvxe8UrPa/Gy7Rsf914 eG8NvZ5JaT8lKCZYSqk5I8uOrTtFl3DZsPDn1TmiCIWS2YnLwsIU3HQ0E5mpavgpZDjK H8jCw2W4zVXpZFNjIcQ48esa2f5BQVrbAvQReZk/R/yZsKWiG+7Cb0sc6mkgFC4M7xP3 H7jwG7B91xcJuwxQ+/1/eAbJY02ioZ0/q35OAx+3ErCdjiMWv9LD4NfPl2OpU4ZHSodI 3y65FP42qQmy0oL8d1iREkcvNDo/3weTm4BTCyZHQKgrZQFA6OL+0wR+cV3Z+ZQAFHBt 4F5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=/g1pL3/hSl0fTsy5dL717qIUgAnsVErwAcv2lpeHoaY=; b=ByGG5w8/jMyimUsp1mklgc90v2OFGKGgINtb4ikKCqW2ricrL5fpskR70FODzppL8A MB4P5P57cI/Xy5JjF4DRTYsYWRdITNkS84ob/NmW6DJMzLK0vqfqjyvrXwDNasEQWnSB t8pWrqc7asrF3tXsbLfXVFS6Iv9IdRALIqUmHPWNbuB0omxRvQhDfBNkaEQs+H7fX7kB Moj05K2vzO0hvHAna5RcrpJ12waoxIJ3MDbV8oGTdZA8UJHM42H+gNs+bHZUmMCwSif2 hzdVvboWxI5Bi5cpIMKytVL3buoI4Z2JwIUW9HtH5efvwpjW+werzWKB8vOzyqT8UAyZ /PUw== X-Gm-Message-State: AOAM530bV9s36VvfvMiRwu87prQaIKvVjcJHGq4F0Glme9lH49ZOG5Kp 0wjHqpxbsjd4qwG2Viz18Z8NenJDguhbVw== X-Google-Smtp-Source: ABdhPJzDzwX91MztklKLg7OXBXN69Lom5WIb+x5BV9YyBJ8m7FFyW6Nw6nSXYvPwaIKzaeCPmpoXBw== X-Received: by 2002:a2e:9ed0:: with SMTP id h16mr17588908ljk.366.1593699614324; Thu, 02 Jul 2020 07:20:14 -0700 (PDT) Received: from gilgamesh.semihalf.com (193-106-246-138.noc.fibertech.net.pl. [193.106.246.138]) by smtp.gmail.com with ESMTPSA id v10sm692581ljg.113.2020.07.02.07.20.13 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jul 2020 07:20:13 -0700 (PDT) From: Grzegorz Jaszczyk To: tglx@linutronix.de, jason@lakedaemon.net, maz@kernel.org, s-anna@ti.com Subject: [PATCHv3 6/6] irqchip/irq-pruss-intc: Add event mapping support Date: Thu, 2 Jul 2020 16:17:59 +0200 Message-Id: <1593699479-1445-7-git-send-email-grzegorz.jaszczyk@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1593699479-1445-1-git-send-email-grzegorz.jaszczyk@linaro.org> References: <1593699479-1445-1-git-send-email-grzegorz.jaszczyk@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200702_102830_055673_537A4CC5 X-CRM114-Status: GOOD ( 33.11 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2a00:1450:4864:20:0:0:0:541 listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, grzegorz.jaszczyk@linaro.org, david@lechnology.com, linux-kernel@vger.kernel.org, robh+dt@kernel.org, linux-omap@vger.kernel.org, lee.jones@linaro.org, wmills@ti.com, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The PRUSS INTC receives a number of system input interrupt source events and supports individual control configuration and hardware prioritization. These input events can be mapped to some output host interrupts through 2 levels of many-to-one mapping i.e. events to channel mapping and channels to host interrupts. This mapping information is provided through the PRU firmware that is loaded onto a PRU core/s or through the device tree node of the PRU application. The mapping configuration is triggered by the PRU remoteproc driver, and is setup before the PRU core is started and cleaned up after the PRU core is stopped. This event mapping configuration logic programs the Channel Map Registers (CMRx) and Host-Interrupt Map Registers (HMRx) only when a new program is being loaded/started and the same events and interrupt channels are reset to zero when stopping a PRU. Reference counting is used to allow multiple system events to share a single channel and to allow multiple channels to share a single host event. The remoteproc driver can register mappings read from a firmware blob as shown below. struct irq_fwspec fwspec; int irq; fwspec.fwnode = of_node_to_fwnode(dev->of_node); fwspec.param_count = 3; fwspec.param[0] = 63; // system event fwspec.param[1] = 5; // channel fwspec.param[2] = 6; // host event irq = irq_create_fwspec_mapping(&fwspec); if (irq < 0) { dev_err(dev, "failed to get irq\n"); return irq; } Suggested-by: David Lechner Signed-off-by: Suman Anna Signed-off-by: Grzegorz Jaszczyk --- v3: - This patch replaces https://patchwork.kernel.org/patch/11069753/ according to received feedback. Instead of exporting custom functions from interrupt driver, the xlate and irq domain map is used for interrupt parsing and mapping. --- drivers/irqchip/irq-pruss-intc.c | 265 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 264 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c index 362aa01..cf40a97 100644 --- a/drivers/irqchip/irq-pruss-intc.c +++ b/drivers/irqchip/irq-pruss-intc.c @@ -51,14 +51,42 @@ #define PRU_INTC_HIER 0x1500 /* CMR register bit-field macros */ +#define CMR_EVT_MAP_MASK 0xf +#define CMR_EVT_MAP_BITS 8 #define CMR_EVT_PER_REG 4 /* HMR register bit-field macros */ +#define HMR_CH_MAP_MASK 0xf +#define HMR_CH_MAP_BITS 8 #define HMR_CH_PER_REG 4 /* HIPIR register bit-fields */ #define INTC_HIPIR_NONE_HINT 0x80000000 +#define MAX_PRU_SYS_EVENTS 160 +#define MAX_PRU_CHANNELS 20 + +/** + * struct pruss_intc_hwirq_data - additional metadata associated with a PRU + * system event + * @channel: The PRU INTC channel that the system event should be mapped to + * @host: The PRU INTC host that the channel should be mapped to + */ +struct pruss_intc_hwirq_data { + u8 channel; + u8 host; +}; + +/** + * struct pruss_intc_map_record - keeps track of actual mapping state + * @value: The currently mapped value (channel or host) + * @ref_count: Keeps track of number of current users of this resource + */ +struct pruss_intc_map_record { + u8 value; + u8 ref_count; +}; + /** * struct pruss_intc_match_data - match data to handle SoC variations * @num_system_events: number of input system events handled by the PRUSS INTC @@ -71,18 +99,29 @@ struct pruss_intc_match_data { /** * struct pruss_intc - PRUSS interrupt controller structure + * @hwirq_data: table of additional mapping data received from device tree + * or PRU firmware + * @event_channel: current state of system event to channel mappings + * @channel_host: current state of channel to host mappings * @irqs: kernel irq numbers corresponding to PRUSS host interrupts * @base: base virtual address of INTC register space * @domain: irq domain for this interrupt controller * @soc_config: cached PRUSS INTC IP configuration data + * @lock: mutex to serialize access to INTC + * @dev: PRUSS INTC device pointer * @shared_intr: bit-map denoting if the MPU host interrupt is shared * @invalid_intr: bit-map denoting if host interrupt is not connected to MPU */ struct pruss_intc { + struct pruss_intc_hwirq_data hwirq_data[MAX_PRU_SYS_EVENTS]; + struct pruss_intc_map_record event_channel[MAX_PRU_SYS_EVENTS]; + struct pruss_intc_map_record channel_host[MAX_PRU_CHANNELS]; unsigned int irqs[MAX_NUM_HOST_IRQS]; void __iomem *base; struct irq_domain *domain; const struct pruss_intc_match_data *soc_config; + struct mutex lock; /* PRUSS INTC lock */ + struct device *dev; u16 shared_intr; u16 invalid_intr; }; @@ -98,6 +137,165 @@ static inline void pruss_intc_write_reg(struct pruss_intc *intc, writel_relaxed(val, intc->base + reg); } +static void pruss_intc_update_cmr(struct pruss_intc *intc, int evt, s8 ch) +{ + u32 idx, offset, val; + + idx = evt / CMR_EVT_PER_REG; + offset = (evt % CMR_EVT_PER_REG) * CMR_EVT_MAP_BITS; + + val = pruss_intc_read_reg(intc, PRU_INTC_CMR(idx)); + val &= ~(CMR_EVT_MAP_MASK << offset); + val |= ch << offset; + pruss_intc_write_reg(intc, PRU_INTC_CMR(idx), val); + + dev_dbg(intc->dev, "SYSEV%u -> CH%d (CMR%d 0x%08x)\n", evt, ch, + idx, pruss_intc_read_reg(intc, PRU_INTC_CMR(idx))); +} + +static void pruss_intc_update_hmr(struct pruss_intc *intc, int ch, s8 host) +{ + u32 idx, offset, val; + + idx = ch / HMR_CH_PER_REG; + offset = (ch % HMR_CH_PER_REG) * HMR_CH_MAP_BITS; + + val = pruss_intc_read_reg(intc, PRU_INTC_HMR(idx)); + val &= ~(HMR_CH_MAP_MASK << offset); + val |= host << offset; + pruss_intc_write_reg(intc, PRU_INTC_HMR(idx), val); + + dev_dbg(intc->dev, "CH%d -> HOST%d (HMR%d 0x%08x)\n", ch, host, idx, + pruss_intc_read_reg(intc, PRU_INTC_HMR(idx))); +} + +/** + * pruss_intc_map() - configure the PRUSS INTC + * @intc: PRUSS interrupt controller pointer + * @hwirq: the system event number + * + * Configures the PRUSS INTC with the provided configuration from the one + * parsed in the xlate function. Any existing event to channel mappings or + * channel to host interrupt mappings are checked to make sure there are no + * conflicting configuration between both the PRU cores. + * + * Returns 0 on success, or a suitable error code otherwise + */ +static int pruss_intc_map(struct pruss_intc *intc, unsigned long hwirq) +{ + struct device *dev = intc->dev; + int ret = 0; + u8 ch, host, reg_idx; + u32 val; + + if (hwirq >= intc->soc_config->num_system_events) + return -EINVAL; + + mutex_lock(&intc->lock); + + ch = intc->hwirq_data[hwirq].channel; + host = intc->hwirq_data[hwirq].host; + + /* check if sysevent already assigned */ + if (intc->event_channel[hwirq].ref_count > 0 && + intc->event_channel[hwirq].value != ch) { + dev_err(dev, "event %lu (req. channel %d) already assigned to channel %d\n", + hwirq, ch, intc->event_channel[hwirq].value); + ret = -EBUSY; + goto unlock; + } + + /* check if channel already assigned */ + if (intc->channel_host[ch].ref_count > 0 && + intc->channel_host[ch].value != host) { + dev_err(dev, "channel %d (req. host %d) already assigned to host %d\n", + ch, host, intc->channel_host[ch].value); + ret = -EBUSY; + goto unlock; + } + + if (++intc->event_channel[hwirq].ref_count == 1) { + intc->event_channel[hwirq].value = ch; + + pruss_intc_update_cmr(intc, hwirq, ch); + + reg_idx = hwirq / 32; + val = BIT(hwirq % 32); + + /* clear and enable system event */ + pruss_intc_write_reg(intc, PRU_INTC_ESR(reg_idx), val); + pruss_intc_write_reg(intc, PRU_INTC_SECR(reg_idx), val); + } + + if (++intc->channel_host[ch].ref_count == 1) { + intc->channel_host[ch].value = host; + + pruss_intc_update_hmr(intc, ch, host); + + /* enable host interrupts */ + pruss_intc_write_reg(intc, PRU_INTC_HIEISR, host); + } + + dev_dbg(dev, "mapped system_event = %lu channel = %d host = %d", + hwirq, ch, host); + + /* global interrupt enable */ + pruss_intc_write_reg(intc, PRU_INTC_GER, 1); + +unlock: + mutex_unlock(&intc->lock); + return ret; +} + +/** + * pruss_intc_unmap() - unconfigure the PRUSS INTC + * @intc: PRUSS interrupt controller pointer + * @hwirq: the system event number + * + * Undo whatever was done in pruss_intc_map() for a PRU core. + * Mappings are reference counted, so resources are only disabled when there + * are no longer any users. + */ +static void pruss_intc_unmap(struct pruss_intc *intc, unsigned long hwirq) +{ + u8 ch, host, reg_idx; + u32 val; + + if (hwirq >= intc->soc_config->num_system_events) + return; + + mutex_lock(&intc->lock); + + ch = intc->event_channel[hwirq].value; + host = intc->channel_host[ch].value; + + if (--intc->channel_host[ch].ref_count == 0) { + /* disable host interrupts */ + pruss_intc_write_reg(intc, PRU_INTC_HIDISR, host); + + /* clear the map using reset value 0 */ + pruss_intc_update_hmr(intc, ch, 0); + } + + if (--intc->event_channel[hwirq].ref_count == 0) { + reg_idx = hwirq / 32; + val = BIT(hwirq % 32); + + /* disable system events */ + pruss_intc_write_reg(intc, PRU_INTC_ECR(reg_idx), val); + /* clear any pending status */ + pruss_intc_write_reg(intc, PRU_INTC_SECR(reg_idx), val); + + /* clear the map using reset value 0 */ + pruss_intc_update_cmr(intc, hwirq, 0); + } + + dev_dbg(intc->dev, "unmapped system_event = %lu channel = %d host = %d\n", + hwirq, ch, host); + + mutex_unlock(&intc->lock); +} + static void pruss_intc_init(struct pruss_intc *intc) { const struct pruss_intc_match_data *soc_config = intc->soc_config; @@ -212,10 +410,67 @@ static struct irq_chip pruss_irqchip = { .irq_set_irqchip_state = pruss_intc_irq_set_irqchip_state, }; +static int +pruss_intc_irq_domain_xlate(struct irq_domain *d, struct device_node *node, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + struct pruss_intc *intc = d->host_data; + struct device *dev = intc->dev; + int sys_event, channel, host; + + if (intsize == 1) { + /* + * In case of short version (intsize == 1) verify if sysevent + * already mapped to channel/host irq if not return error + */ + sys_event = intspec[0]; + if (intc->event_channel[sys_event].ref_count) + goto already_mapped; + else + return -EINVAL; + } + + if (intsize < 3) + return -EINVAL; + + sys_event = intspec[0]; + if (sys_event < 0 || sys_event >= intc->soc_config->num_system_events) { + dev_err(dev, "not valid event number\n"); + return -EINVAL; + } + + channel = intspec[1]; + if (channel < 0 || channel >= intc->soc_config->num_host_intrs) { + dev_err(dev, "not valid channel number"); + return -EINVAL; + } + + host = intspec[2]; + if (host < 0 || host >= intc->soc_config->num_host_intrs) { + dev_err(dev, "not valid host irq number\n"); + return -EINVAL; + } + + intc->hwirq_data[sys_event].channel = channel; + intc->hwirq_data[sys_event].host = host; + +already_mapped: + *out_hwirq = sys_event; + *out_type = IRQ_TYPE_NONE; + + return 0; +} + static int pruss_intc_irq_domain_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { struct pruss_intc *intc = d->host_data; + int err; + + err = pruss_intc_map(intc, hw); + if (err < 0) + return err; irq_set_chip_data(virq, intc); irq_set_chip_and_handler(virq, &pruss_irqchip, handle_level_irq); @@ -225,12 +480,16 @@ static int pruss_intc_irq_domain_map(struct irq_domain *d, unsigned int virq, static void pruss_intc_irq_domain_unmap(struct irq_domain *d, unsigned int virq) { + struct pruss_intc *intc = d->host_data; + unsigned long hwirq = irqd_to_hwirq(irq_get_irq_data(virq)); + irq_set_chip_and_handler(virq, NULL, NULL); irq_set_chip_data(virq, NULL); + pruss_intc_unmap(intc, hwirq); } static const struct irq_domain_ops pruss_intc_irq_domain_ops = { - .xlate = irq_domain_xlate_onecell, + .xlate = pruss_intc_irq_domain_xlate, .map = pruss_intc_irq_domain_map, .unmap = pruss_intc_irq_domain_unmap, }; @@ -298,6 +557,8 @@ static int pruss_intc_probe(struct platform_device *pdev) intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL); if (!intc) return -ENOMEM; + + intc->dev = dev; intc->soc_config = data; platform_set_drvdata(pdev, intc); @@ -355,6 +616,8 @@ static int pruss_intc_probe(struct platform_device *pdev) pruss_intc_init(intc); + mutex_init(&intc->lock); + intc->domain = irq_domain_add_linear(dev->of_node, max_system_events, &pruss_intc_irq_domain_ops, intc); if (!intc->domain)