From patchwork Thu Oct 20 14:13:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: firas ashkar X-Patchwork-Id: 6220 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:4242:0:0:0:0:0 with SMTP id s2csp137767wrr; Thu, 20 Oct 2022 07:19:34 -0700 (PDT) X-Google-Smtp-Source: AMsMyM7KjaijrC9f+gZ0SjajV5QIqZ2YSWMtTvLSzHg9K2aOL+zzM61z1DZYBYKASp3p6ll/af/d X-Received: by 2002:a17:907:7290:b0:78d:ec20:fe4d with SMTP id dt16-20020a170907729000b0078dec20fe4dmr11402941ejc.528.1666275573926; Thu, 20 Oct 2022 07:19:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1666275573; cv=none; d=google.com; s=arc-20160816; b=M+U8WQQ7egnlgS4xGuqBwkxX7SsHBY+flw+BCbhFApQ+fMJ1m05dbCFeIP9M7ArZWN fm1LyI452M9v+D6go/WAIqyqDgiIGrmgmG5lUUFHEi4PADHkNiNC+V3pQqyxXRgnmDyC z2bXk6AXIJQedSWBiFJAEZG/PZw1vO1XQzRnV730HSvd1STFfr8GI9SFGvxJuP7Q8eUw IwU3IvI1mjxC7xzRJbT9xcnwDAzVG87Dac/nopmRBvZ8qQFkFJrAS1TD8kfC9PK3LA55 oiKVea3HLQbBm5lH7OZrSa6oxM1HXi71F7Avt30BMtVbCYuaWPOITHEe8jvzaNz92HK0 5vlQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature:dkim-filter; bh=OGmET8HWrWBU22FTwJ+6Tu9g87EbfiQCcesWxsajcmo=; b=Cz2AaEILSNJT9aICuVf055rWV8gLKigyCGi7u1/5a1d31cQuY4Uk4FjeeSrPq5KZb+ cTgmBZbbjkAeYLMjSOzrHrDWgZFK7lbi6csQhJVvGcqCA0kFPUKcmKnZa32YWAnKRhX5 DNb2teqhdgDSGQWO4lA1IPVOLTpQQ0DGcHibao/4cM3sIKy/gHs4HgRUUj6kowLwRAOC 4Hc1pE2p0gDj7dQH4+W3xG5SutA1XJZ7RkSaQjbjhzeZGdF51OjyBnsq0sjXrezxmhHu Gh5fqM1QflJMUNvCCKCvL0bZGSUhE5u9EhYXaQ4Szmj7bTOo64kDFYgGjz1VYBsMv5Rz A58w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@savoirfairelinux.com header.s=DFC430D2-D198-11EC-948E-34200CB392D2 header.b=TwpiT532; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id bx26-20020a170906a1da00b0078d45e9d485si13785694ejb.709.2022.10.20.07.18.45; Thu, 20 Oct 2022 07:19:33 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@savoirfairelinux.com header.s=DFC430D2-D198-11EC-948E-34200CB392D2 header.b=TwpiT532; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229836AbiJTOO2 (ORCPT + 99 others); Thu, 20 Oct 2022 10:14:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40096 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229456AbiJTOOT (ORCPT ); Thu, 20 Oct 2022 10:14:19 -0400 Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 09A9D1867B9 for ; Thu, 20 Oct 2022 07:14:03 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 2575F9C0794; Thu, 20 Oct 2022 10:14:02 -0400 (EDT) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id Gk38oE8C0Tq3; Thu, 20 Oct 2022 10:14:00 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id A18709C073B; Thu, 20 Oct 2022 10:14:00 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com A18709C073B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1666275240; bh=OGmET8HWrWBU22FTwJ+6Tu9g87EbfiQCcesWxsajcmo=; h=From:To:Date:Message-Id:MIME-Version; b=TwpiT532uiLMr9/6NKQfnGMnrXCxvNUbXWELkln6gQugpFZ/BxCMvtG47EUSl5X3y isLX0dRKXSjuwFLXgIRc/rFB7HVci24fYeNFDT5YMFtv3P05wGhknctFj1QItXdOmD PXLP5Q5sRu5C1YptLN5EZppzEaBK/uzrKX87ydeBtYvk3tksm+P1OoanRIZ7BUopiU oK/slJNC7aIabWmmkuF4P3DU9QGVebcmWvFvCB+91cK+DMwIiexEBbkk+SXIuYc/Gl tjQ4QwVSPCcgDyhaqOe3LAA8CrGgLtLUYp+3IaSwl97chmdPoBiAxMWEQuXN8Oqq2O 5c6waSFeGVj0Q== X-Virus-Scanned: amavisd-new at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id 1-n0iCiUntzG; Thu, 20 Oct 2022 10:14:00 -0400 (EDT) Received: from localhost.localdomain (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id 758379C0735; Thu, 20 Oct 2022 10:14:00 -0400 (EDT) From: Firas Ashkar To: alex@digriz.org.uk, tglx@linutronix.de, maz@kernel.org Cc: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Firas Ashkar Subject: [PATCH v2] irqchip: add TS7800v1 fpga based controller driver Date: Thu, 20 Oct 2022 10:13:51 -0400 Message-Id: <20221020141351.14829-1-firas.ashkar@savoirfairelinux.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1747032977357525277?= X-GMAIL-MSGID: =?utf-8?q?1747216576095420617?= 1. add TS-7800v1 fpga based irq controller driver, and 2. add related memory and irq resources By default only mapped FPGA interrupts will be chained/multiplexed, these chained interrupts will then be available to other device drivers to request via the corresponding Linux IRQs. $ cat /proc/cpuinfo processor : 0 model name : Feroceon rev 0 (v5l) BogoMIPS : 333.33 Features : swp half thumb fastmult edsp CPU implementer : 0x41 CPU architecture: 5TEJ CPU variant : 0x0 CPU part : 0x926 CPU revision : 0 Hardware : Technologic Systems TS-78xx SBC Revision : 0000 Serial : 0000000000000000 $ $ cat /proc/interrupts CPU0 1: 902 orion_irq Level orion_tick 4: 795 orion_irq Level ttyS0 13: 0 orion_irq Level ehci_hcd:usb2 18: 0 orion_irq Level ehci_hcd:usb1 22: 69 orion_irq Level eth0 23: 171 orion_irq Level orion-mdio 29: 0 orion_irq Level mv_crypto 31: 2 orion_irq Level mv_xor.0 65: 1 ts7800-irqc 0 Edge ts-dmac-cpupci Err: 0 $ $ uname -a Linux ts-7800 6.1.0-rc1 #2 PREEMPT Mon Oct 17 11:19:12 EDT 2022 armv5tel GNU/Linux $ Signed-off-by: Firas Ashkar --- Notes: Changes in V2 * limit the commit message * format comments in source code * use raw spin locks to protect mask/unmask ops * use 'handle_edge_irq' and 'irq_ack' logic * remove 'irq_domain_xlate_onecell' * remove unnecessary status flags * use 'builtin_platform_driver' helper routine :100644 100644 2f4fe3ca5c1a ed8378893208 M arch/arm/mach-orion5x/ts78xx-fpga.h :100644 100644 af810e7ccd79 d319a68b7160 M arch/arm/mach-orion5x/ts78xx-setup.c :100644 100644 7ef9f5e696d3 d184fb435c5d M drivers/irqchip/Kconfig :100644 100644 87b49a10962c b022eece2042 M drivers/irqchip/Makefile :000000 100644 000000000000 e975607fa4d5 A drivers/irqchip/irq-ts7800.c arch/arm/mach-orion5x/ts78xx-fpga.h | 1 + arch/arm/mach-orion5x/ts78xx-setup.c | 55 +++++ drivers/irqchip/Kconfig | 12 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ts7800.c | 347 +++++++++++++++++++++++++++ 5 files changed, 416 insertions(+) diff --git a/arch/arm/mach-orion5x/ts78xx-fpga.h b/arch/arm/mach-orion5x/ts78xx-fpga.h index 2f4fe3ca5c1a..ed8378893208 100644 --- a/arch/arm/mach-orion5x/ts78xx-fpga.h +++ b/arch/arm/mach-orion5x/ts78xx-fpga.h @@ -32,6 +32,7 @@ struct fpga_devices { struct fpga_device ts_rtc; struct fpga_device ts_nand; struct fpga_device ts_rng; + struct fpga_device ts_irqc; }; struct ts78xx_fpga_data { diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c index af810e7ccd79..d319a68b7160 100644 --- a/arch/arm/mach-orion5x/ts78xx-setup.c +++ b/arch/arm/mach-orion5x/ts78xx-setup.c @@ -322,6 +322,49 @@ static void ts78xx_ts_rng_unload(void) platform_device_del(&ts78xx_ts_rng_device); } +/***************************************************************************** + * fpga irq controller + ****************************************************************************/ +#define TS_IRQC_CTRL (TS78XX_FPGA_REGS_PHYS_BASE + 0x200) +#define TS_BRIDGE_CTRL (ORION5X_REGS_PHYS_BASE + 0x20400) +#define TS_IRQC_PARENT 0x2 +static struct resource ts_irqc_resources[] = { + DEFINE_RES_MEM_NAMED(TS_IRQC_CTRL, 0x100, "ts_irqc"), + DEFINE_RES_MEM_NAMED(TS_BRIDGE_CTRL, 0x10, "ts_bridge"), + DEFINE_RES_IRQ_NAMED(TS_IRQC_PARENT, "ts_irqc_parent"), +}; + +static struct platform_device ts_irqc_device = { + .name = "ts7800-irqc", + .id = -1, + .resource = ts_irqc_resources, + .num_resources = ARRAY_SIZE(ts_irqc_resources), +}; + +static int ts_irqc_load(void) +{ + int rc; + + if (ts78xx_fpga.supports.ts_irqc.init == 0) { + rc = platform_device_register(&ts_irqc_device); + if (!rc) + ts78xx_fpga.supports.ts_irqc.init = 1; + } else { + rc = platform_device_add(&ts_irqc_device); + } + + if (rc) + pr_info("FPGA based IRQ controller could not be registered: %d\n", + rc); + + return rc; +} + +static void ts_irqc_unload(void) +{ + platform_device_del(&ts_irqc_device); +} + /***************************************************************************** * FPGA 'hotplug' support code ****************************************************************************/ @@ -330,6 +373,7 @@ static void ts78xx_fpga_devices_zero_init(void) ts78xx_fpga.supports.ts_rtc.init = 0; ts78xx_fpga.supports.ts_nand.init = 0; ts78xx_fpga.supports.ts_rng.init = 0; + ts78xx_fpga.supports.ts_irqc.init = 0; } static void ts78xx_fpga_supports(void) @@ -348,6 +392,7 @@ static void ts78xx_fpga_supports(void) ts78xx_fpga.supports.ts_rtc.present = 1; ts78xx_fpga.supports.ts_nand.present = 1; ts78xx_fpga.supports.ts_rng.present = 1; + ts78xx_fpga.supports.ts_irqc.present = 1; break; default: /* enable devices if magic matches */ @@ -358,11 +403,13 @@ static void ts78xx_fpga_supports(void) ts78xx_fpga.supports.ts_rtc.present = 1; ts78xx_fpga.supports.ts_nand.present = 1; ts78xx_fpga.supports.ts_rng.present = 1; + ts78xx_fpga.supports.ts_irqc.present = 1; break; default: ts78xx_fpga.supports.ts_rtc.present = 0; ts78xx_fpga.supports.ts_nand.present = 0; ts78xx_fpga.supports.ts_rng.present = 0; + ts78xx_fpga.supports.ts_irqc.present = 0; } } } @@ -371,6 +418,12 @@ static int ts78xx_fpga_load_devices(void) { int tmp, ret = 0; + if (ts78xx_fpga.supports.ts_irqc.present == 1) { + tmp = ts_irqc_load(); + if (tmp) + ts78xx_fpga.supports.ts_irqc.present = 0; + ret |= tmp; + } if (ts78xx_fpga.supports.ts_rtc.present == 1) { tmp = ts78xx_ts_rtc_load(); if (tmp) @@ -402,6 +455,8 @@ static int ts78xx_fpga_unload_devices(void) ts78xx_ts_nand_unload(); if (ts78xx_fpga.supports.ts_rng.present == 1) ts78xx_ts_rng_unload(); + if (ts78xx_fpga.supports.ts_irqc.present == 1) + ts_irqc_unload(); return 0; } diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 7ef9f5e696d3..d184fb435c5d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -290,6 +290,18 @@ config TS4800_IRQ help Support for the TS-4800 FPGA IRQ controller +config TS7800_IRQ + tristate "TS-7800 IRQ controller" + select IRQ_DOMAIN + depends on HAS_IOMEM + depends on MACH_TS78XX + help + Support for TS-7800 FPGA based IRQ controller. + This IRQ controller acts as a multiplexer chaining mapped + FPGA interrupts to a single system bridge interrupt. + + If you have an FPGA IP corresponding to this driver, say Y or M here. + config VERSATILE_FPGA_IRQ bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 87b49a10962c..b022eece2042 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o obj-$(CONFIG_ST_IRQCHIP) += irq-st.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o +obj-$(CONFIG_TS7800_IRQ) += irq-ts7800.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o diff --git a/drivers/irqchip/irq-ts7800.c b/drivers/irqchip/irq-ts7800.c new file mode 100644 index 000000000000..e975607fa4d5 --- /dev/null +++ b/drivers/irqchip/irq-ts7800.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FPGA based IRQ contorller driver for TS-7800v1 + * + * Copyright (C) 2022 Savoir-faire Linux Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "ts7800-irqc" + +#define IRQ_MASK_REG 0x4 +#define IRQ_STATUS_REG 0x8 +#define TS7800_IRQ_NUM 0x20 + +/* + * FPGA IRQ Controller, list of all mappable/chainable interrupts + * + * IRQ Description + * --- ----------- + * 0x0 dma_cpu_pci_dack_o + * 0x1 dma_fpga_dack_o + * 0x2 SD Busy# + * 0x3 isa_irq3 + * 0x4 isa_irq4 + * 0x5 isa_irq5 + * 0x6 isa_irq6 + * 0x7 isa_irq7 + * 0x8 Reserved + * 0x9 isa_irq9 + * 0xa isa_irq10 + * 0xb isa_irq11 + * 0xc isa_irq12 + * 0xd isa_irq14 + * 0xe isa_irq15 + * 0x10:0x19 tsuart_irqs + * + * To map or enable a specific FPGA interrupt, add its corresponding number to + * 'enabled_mappings'. + * Example: + * 1. For 'dma_cpu_pci_dack_o' (FPGA based DMA controller), add 0x0 to + * 'enabled_mappings' + * 2. For 'tsuart_irqs' (FPGA based UARTs), add their FPGA interrupt range of + * 0x10-0x19 to 'enabled_mappings' + * + * By default only mapped/enabled interrupts will be chained/multiplexed, + * these chained interrupts will then be available to other device drivers to + * request via the corresponding Linux IRQs. + * + * Each mapped FPGA interrupt will have a corresponding Linux IRQ, this new IRQ + * will be appended to the system's current list of Linux IRQs, on TS78xx, the + * first mapped FPGA interrupt will have a corresponding new Linux IRQ starting + * at 65. The next mapped FPGA interrupt will have a Linux IRQ of 66 and so + * forth. + * + * Example-1: + * In order for a device driver, say the FPGA based DMA controller driver + * 'TS-DMAC', to use either of the corresponding FPGA interrupts + * 'dma_cpu_pci_dack_o' and 'dma_fpga_dack_o', the 'TS-DMAC' driver has to: + * + * 1. add FPGA interrupt numbers 0x0 and 0x1 to 'enabled_mappings', in this + * file, and + * 2. request the corresponding Linux IRQ numbers 65 and 66 respectively in its + * implementation. + * + * Eample-2: + * In order for another device driver, say the FPGA based 'TSUART' driver, to + * use the related FPGA interrupts, the 'TSUART' driver has to: + * + * 1. append FPGA interrupt numbers 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + * 0x17, 0x18, 0x19 to 'enabled_mappings', in this file, and + * 2. request the corresponding Linux IRQ numbers, these IRQs could start from + * 65 till 74, if no previous FPGA interrupts where mapped. However, if + * other FPGA interrupts, say those of 'TS-DMAC' in Example-1 were also + * mapped/enabled, then the correct corresponding Linux IRQs would start + * from 67 till 76. + */ + +static irq_hw_number_t enabled_mappings[] = { 0x0, 0x1 }; + +struct ts7800_irq_data { + int mpp7_virq; + raw_spinlock_t lock; + void __iomem *base; + void __iomem *bridge; + struct irq_domain *domain; + struct irq_chip irq_chip; +}; + +static void ts7800_irq_mask(struct irq_data *d) +{ + struct ts7800_irq_data *data = irq_data_get_irq_chip_data(d); + u32 fpga_mask_reg = readl(data->base + IRQ_MASK_REG); + u32 mask_bits = 1 << d->hwirq; + unsigned long flags; + + raw_spin_lock_irqsave(&data->lock, flags); + writel(fpga_mask_reg & ~mask_bits, data->base + IRQ_MASK_REG); + writel(fpga_mask_reg & ~mask_bits, data->bridge + IRQ_MASK_REG); + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static void ts7800_irq_unmask(struct irq_data *d) +{ + struct ts7800_irq_data *data = irq_data_get_irq_chip_data(d); + u32 fpga_mask_reg = readl(data->base + IRQ_MASK_REG); + u32 mask_bits = 1 << d->hwirq; + unsigned long flags; + + raw_spin_lock_irqsave(&data->lock, flags); + writel(fpga_mask_reg | mask_bits, data->base + IRQ_MASK_REG); + writel(fpga_mask_reg | mask_bits, data->bridge + IRQ_MASK_REG); + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static void ts7800_irq_ack(struct irq_data *d) +{ + struct ts7800_irq_data *data = irq_data_get_irq_chip_data(d); + u32 status = readl(data->bridge); + u32 mask_bits = 1 << d->hwirq; + unsigned int bit = __ffs(mask_bits); + unsigned long flags; + + status &= ~(1 << bit); + + raw_spin_lock_irqsave(&data->lock, flags); + writel(status, data->bridge); + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static int ts7800_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct ts7800_irq_data *data = d->host_data; + + irq_set_chip_and_handler(irq, &data->irq_chip, handle_edge_irq); + irq_clear_status_flags(irq, IRQ_LEVEL); + irq_set_chip_data(irq, data); + irq_set_noprobe(irq); + + return 0; +} + +static void ts7800_irqdomain_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops ts7800_ic_ops = { + .map = ts7800_irqdomain_map, + .unmap = ts7800_irqdomain_unmap +}; + +static void ts7800_ic_chained_handle_irq(struct irq_desc *desc) +{ + struct ts7800_irq_data *data = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 mask_bits = readl(data->base + IRQ_MASK_REG); + u32 status = readl(data->bridge); + + chained_irq_enter(chip, desc); + + if (unlikely(status == 0)) { + handle_bad_irq(desc); + goto out; + } + + do { + unsigned int bit = __ffs(status); + int irq = irq_find_mapping(data->domain, bit); + + if (irq && (mask_bits & BIT(bit))) + generic_handle_irq(irq); + + status &= ~(1 << bit); + + } while (status); + +out: + chained_irq_exit(chip, desc); +} + +static int ts7800_ic_probe(struct platform_device *pdev) +{ + struct ts7800_irq_data *data = NULL; + struct resource *mem_res = NULL, *brdg_res = NULL, *irq_res = NULL; + unsigned int irqdomain; + int i, ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (IS_ERR_OR_NULL(data)) { + dev_err(&pdev->dev, + "Failed to allocate TS7800 data, error %ld\n", + PTR_ERR(data)); + ret = PTR_ERR(data); + goto devm_kzalloc_err; + } + + mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ts_irqc"); + if (IS_ERR_OR_NULL(mem_res)) { + dev_err(&pdev->dev, + "Failed to get platform memory resource, error %ld\n", + PTR_ERR(mem_res)); + ret = PTR_ERR(mem_res); + goto pltfrm_get_res_mem_err; + } + + data->base = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR_OR_NULL(data->base)) { + dev_err(&pdev->dev, + "Failed to IO map mem-region %s, error %ld\n", + mem_res->name, PTR_ERR(data->base)); + ret = PTR_ERR(data->base); + goto devm_ioremap_res_mem_err; + } + + brdg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ts_bridge"); + if (IS_ERR_OR_NULL(brdg_res)) { + dev_err(&pdev->dev, + "Failed to get platform bridge resource, error %ld\n", + PTR_ERR(brdg_res)); + ret = PTR_ERR(brdg_res); + goto pltfrm_get_res_brdg_err; + } + + data->bridge = devm_ioremap_resource(&pdev->dev, brdg_res); + if (IS_ERR_OR_NULL(data->bridge)) { + dev_err(&pdev->dev, + "Failed to IO map bridge-region %s, error %ld\n", + mem_res->name, PTR_ERR(data->bridge)); + ret = PTR_ERR(data->bridge); + goto devm_ioremap_res_brdge_err; + } + + irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "ts_irqc_parent"); + if (IS_ERR_OR_NULL(irq_res)) { + dev_err(&pdev->dev, + "Failed to get platform parent irq resource, error %ld\n", + PTR_ERR(irq_res)); + ret = PTR_ERR(irq_res); + goto pltfrm_get_res_irq_err; + } + + raw_spin_lock_init(&data->lock); + data->mpp7_virq = irq_res->start; + data->irq_chip.name = dev_name(&pdev->dev); + data->irq_chip.irq_mask = ts7800_irq_mask; + data->irq_chip.irq_unmask = ts7800_irq_unmask; + data->irq_chip.irq_ack = ts7800_irq_ack; + + writel(0x0, data->bridge); + writel(0x0, data->bridge + IRQ_MASK_REG); + writel(0x0, data->base + IRQ_MASK_REG); + + data->domain = irq_domain_add_linear(pdev->dev.of_node, TS7800_IRQ_NUM, + &ts7800_ic_ops, data); + if (IS_ERR_OR_NULL(data->domain)) { + dev_err(&pdev->dev, "cannot add IRQ domain\n"); + ret = PTR_ERR(data->domain); + goto irq_domain_add_linear_err; + } + + for (i = 0; i < ARRAY_SIZE(enabled_mappings); ++i) { + irqdomain = + irq_create_mapping(data->domain, enabled_mappings[i]); + } + + irq_set_chained_handler_and_data(data->mpp7_virq, + ts7800_ic_chained_handle_irq, data); + + platform_set_drvdata(pdev, data); + + return 0; + +irq_domain_add_linear_err: +pltfrm_get_res_irq_err: + devm_iounmap(&pdev->dev, data->bridge); + +devm_ioremap_res_brdge_err: +pltfrm_get_res_brdg_err: + devm_iounmap(&pdev->dev, data->base); + +devm_ioremap_res_mem_err: +pltfrm_get_res_mem_err: + devm_kfree(&pdev->dev, data); + +devm_kzalloc_err: + return ret; +} + +static int ts7800_ic_remove(struct platform_device *pdev) +{ + struct ts7800_irq_data *data = platform_get_drvdata(pdev); + int i; + + if (!IS_ERR_OR_NULL(data)) { + irq_set_chained_handler_and_data(data->mpp7_virq, NULL, NULL); + + for (i = 0; i < ARRAY_SIZE(enabled_mappings); ++i) + irq_dispose_mapping(irq_find_mapping( + data->domain, enabled_mappings[i])); + + irq_domain_remove(data->domain); + } + + return 0; +} + +static const struct platform_device_id ts7800v1_ic_ids[] = { + { + .name = DRIVER_NAME, + }, + { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(platform, ts7800v1_ic_ids); + +static struct platform_driver ts7800_ic_driver = { + .probe = ts7800_ic_probe, + .remove = ts7800_ic_remove, + .id_table = ts7800v1_ic_ids, + .driver = { + .name = DRIVER_NAME, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +builtin_platform_driver(ts7800_ic_driver); + +MODULE_ALIAS("platform:ts7800-irqc"); +MODULE_DESCRIPTION("TS-7800v1 FPGA based IRQ controller Driver"); +MODULE_AUTHOR("Firas Ashkar "); +MODULE_LICENSE("GPL");