From patchwork Thu Oct 13 05:06:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Youlin Pei X-Patchwork-Id: 9374333 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 00E3660779 for ; Thu, 13 Oct 2016 05:08:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E7DDC29835 for ; Thu, 13 Oct 2016 05:08:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DC1D729846; Thu, 13 Oct 2016 05:08:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 5E59829835 for ; Thu, 13 Oct 2016 05:08:39 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1buYFy-00058A-Mx; Thu, 13 Oct 2016 05:08:38 +0000 Received: from [210.61.82.183] (helo=mailgw01.mediatek.com) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1buYFU-0004oN-Bl; Thu, 13 Oct 2016 05:08:11 +0000 Received: from mtkhts07.mediatek.inc [(172.21.101.69)] by mailgw01.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 557778412; Thu, 13 Oct 2016 13:07:45 +0800 Received: from mhfsdcap03.mhfswrd (10.17.3.153) by mtkhts07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 14.3.266.1; Thu, 13 Oct 2016 13:07:43 +0800 From: Youlin Pei To: Marc Zyngier , Rob Herring , Matthias Brugger Subject: [PATCH 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement Date: Thu, 13 Oct 2016 13:06:33 +0800 Message-ID: <1476335194-26604-3-git-send-email-youlin.pei@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1476335194-26604-1-git-send-email-youlin.pei@mediatek.com> References: <1476335194-26604-1-git-send-email-youlin.pei@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161012_220808_878616_23B1779B X-CRM114-Status: GOOD ( 21.91 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: youlin.pei@mediatek.com, Mark Rutland , hongkun.cao@mediatek.com, Jason Cooper , srv_heupstream@mediatek.com, devicetree@vger.kernel.org, erin.lo@mediatek.com, linux-kernel@vger.kernel.org, Russell King , linux-mediatek@lists.infradead.org, yong.wu@mediatek.com, Thomas Gleixner , linux-arm-kernel@lists.infradead.org Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+patchwork-linux-mediatek=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This commit add the mtk-cirq implement for mt2701. Signed-off-by: Youlin Pei --- drivers/irqchip/Makefile | 2 +- drivers/irqchip/irq-mtk-cirq.c | 257 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 drivers/irqchip/irq-mtk-cirq.c diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 4c203b6..eee95c6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -59,7 +59,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o -obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o +obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c new file mode 100644 index 0000000..544767d --- /dev/null +++ b/drivers/irqchip/irq-mtk-cirq.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Youlin.Pei + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CIRQ_MASK_SET 0xC0 +#define CIRQ_MASK_CLR 0x100 +#define CIRQ_SENS_SET 0x180 +#define CIRQ_SENS_CLR 0x1C0 +#define CIRQ_POL_SET 0x240 +#define CIRQ_POL_CLR 0x280 +#define CIRQ_CONTROL 0x300 + +#define CIRQ_EN 0x1 +#define CIRQ_EDGE 0x2 +#define CIRQ_FLUSH 0x4 + +#define CIRQ_IRQ_NUM 0x200 + +struct mtk_cirq_chip_data { + void __iomem *base; + unsigned int ext_irq_start; +}; + +struct mtk_cirq_chip_data *cirq_data; + +static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset) +{ + struct mtk_cirq_chip_data *chip_data = data->chip_data; + unsigned int cirq_num = data->hwirq - chip_data->ext_irq_start; + u32 mask = 1 << (cirq_num % 32); + + writel(mask, chip_data->base + offset + (cirq_num / 32) * 4); +} + +static void mtk_cirq_mask(struct irq_data *data) +{ + mtk_cirq_write_mask(data, CIRQ_MASK_SET); + irq_chip_mask_parent(data); +} + +static void mtk_cirq_unmask(struct irq_data *data) +{ + mtk_cirq_write_mask(data, CIRQ_MASK_CLR); + irq_chip_unmask_parent(data); +} + +static int mtk_cirq_set_type(struct irq_data *data, unsigned int type) +{ + int ret; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_FALLING: + mtk_cirq_write_mask(data, CIRQ_POL_CLR); + mtk_cirq_write_mask(data, CIRQ_SENS_CLR); + break; + case IRQ_TYPE_EDGE_RISING: + mtk_cirq_write_mask(data, CIRQ_POL_SET); + mtk_cirq_write_mask(data, CIRQ_SENS_CLR); + break; + case IRQ_TYPE_LEVEL_LOW: + mtk_cirq_write_mask(data, CIRQ_POL_CLR); + mtk_cirq_write_mask(data, CIRQ_SENS_SET); + break; + case IRQ_TYPE_LEVEL_HIGH: + mtk_cirq_write_mask(data, CIRQ_POL_SET); + mtk_cirq_write_mask(data, CIRQ_SENS_SET); + break; + default: + break; + } + + data = data->parent_data; + ret = data->chip->irq_set_type(data, type); + return ret; +} + +static struct irq_chip mtk_cirq_chip = { + .name = "MT_CIRQ", + .irq_mask = mtk_cirq_mask, + .irq_unmask = mtk_cirq_unmask, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = mtk_cirq_set_type, + .irq_retrigger = irq_chip_retrigger_hierarchy, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif +}; + +static int mtk_cirq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 3) + return -EINVAL; + + /* No PPI should point to this domain */ + if (fwspec->param[0] != 0) + return -EINVAL; + + /* cirq support irq number check */ + if (fwspec->param[1] < cirq_data->ext_irq_start) + return -EINVAL; + + *hwirq = fwspec->param[1]; + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + return 0; + } + + return -EINVAL; +} + +static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int i; + irq_hw_number_t hwirq; + struct irq_fwspec *fwspec = arg; + struct irq_fwspec parent_fwspec = *fwspec; + + if (fwspec->param_count != 3) + return -EINVAL; + + /* cirq doesn't support PPI */ + if (fwspec->param[0]) + return -EINVAL; + + if (fwspec->param[1] < cirq_data->ext_irq_start) + return -EINVAL; + + hwirq = fwspec->param[1]; + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mtk_cirq_chip, + domain->host_data); + + parent_fwspec.fwnode = domain->parent->fwnode; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops cirq_domain_ops = { + .translate = mtk_cirq_domain_translate, + .alloc = mtk_cirq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +#ifdef CONFIG_PM_SLEEP +static int mtk_cirq_suspend(void) +{ + u32 value; + + /* set edge_only mode, record edge-triggerd interrupts */ + /* enable cirq */ + value = readl_relaxed(cirq_data->base + CIRQ_CONTROL); + value |= (CIRQ_EDGE | CIRQ_EN); + writel(value, cirq_data->base + CIRQ_CONTROL); + return 0; +} + +static void mtk_cirq_resume(void) +{ + u32 value; + + /* flush recored interrupts, will send signals to parent controller */ + value = readl_relaxed(cirq_data->base + CIRQ_CONTROL); + writel(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL); + + /* disable cirq */ + value = readl_relaxed(cirq_data->base + CIRQ_CONTROL); + value &= ~(CIRQ_EDGE | CIRQ_EN); + writel(value, cirq_data->base + CIRQ_CONTROL); +} + +static struct syscore_ops mtk_cirq_syscore_ops = { + .suspend = mtk_cirq_suspend, + .resume = mtk_cirq_resume, +}; + +static void mtk_cirq_syscore_init(void) +{ + register_syscore_ops(&mtk_cirq_syscore_ops); +} +#else +static inline void mtk_cirq_syscore_init(void) {} +#endif + +static int __init mtk_cirq_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *domain, *domain_parent; + int ret; + + domain_parent = irq_find_host(parent); + if (!domain_parent) { + pr_err("mtk_cirq: interrupt-parent not found\n"); + return -EINVAL; + } + + cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL); + if (!cirq_data) + return -ENOMEM; + + cirq_data->base = of_iomap(node, 0); + if (!cirq_data->base) { + pr_err("mtk_cirq: unable to map cirq register\n"); + ret = -ENXIO; + goto out_free; + } + + if (of_property_read_u32(node, "mediatek,ext-irq-start", + &cirq_data->ext_irq_start)) { + ret = -EINVAL; + goto out_free; + } + + domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node, + &cirq_domain_ops, cirq_data); + if (!domain) { + ret = -ENOMEM; + goto out_unmap; + } + + mtk_cirq_syscore_init(); + + return 0; + +out_unmap: + iounmap(cirq_data->base); +out_free: + kfree(cirq_data); + return ret; +} + +IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt2701-cirq", mtk_cirq_of_init);