From patchwork Thu Jul 18 18:34:58 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Shiyan X-Patchwork-Id: 2829766 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 034DE9F4D5 for ; Thu, 18 Jul 2013 18:40:02 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9C92A201BD for ; Thu, 18 Jul 2013 18:40:00 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 761F6201BB for ; Thu, 18 Jul 2013 18:39:58 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Uzt5S-0006RI-By; Thu, 18 Jul 2013 18:38:00 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Uzt4w-0001OM-7m; Thu, 18 Jul 2013 18:37:26 +0000 Received: from smtp49.i.mail.ru ([94.100.177.109]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Uzt4t-0001NQ-5G for linux-arm-kernel@lists.infradead.org; Thu, 18 Jul 2013 18:37:24 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=X6i7eLSFW9bhxjwVc/sj4Ygl1NlSZdGg3E+jBRWmIXg=; b=J3R7WV1oevWVkScQ15gRbtywXgs8Kwct8t/g0SY5hG0lTFNjRpF6Td5gtT1Q9px4tBj5UMpuaQ2vIWUj0zZniJf9O6ysjpQ96+f/W7Ea9zY/AZQwx1unlv9zkQWxIYHF2DWCLxytAXFuRiLyAu4U+gSXYlAH2+fcZrcxtNVllWc=; Received: from [188.134.40.128] (port=48263 helo=shc.zet) by smtp49.i.mail.ru with esmtpa (envelope-from ) id 1Uzt4X-0007tN-IB; Thu, 18 Jul 2013 22:37:01 +0400 From: Alexander Shiyan To: linux-arm-kernel@lists.infradead.org Subject: [PATCH 07/10] ARM: clps711x: Add CLPS711X irqchip driver Date: Thu, 18 Jul 2013 22:34:58 +0400 Message-Id: <1374172501-26796-8-git-send-email-shc_work@mail.ru> X-Mailer: git-send-email 1.8.1.5 In-Reply-To: <1374172501-26796-1-git-send-email-shc_work@mail.ru> References: <1374172501-26796-1-git-send-email-shc_work@mail.ru> X-Mras: Ok X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130718_143723_708842_8909BFF3 X-CRM114-Status: GOOD ( 22.85 ) X-Spam-Score: -2.0 (--) Cc: Mike Turquette , Alexander Shiyan , Arnd Bergmann , Daniel Lezcano , "Rafael J. Wysocki" , Olof Johansson , Russell King X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, FREEMAIL_FROM,RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This adds the irqchip driver for Cirrus Logic CLPS711X series SoCs. Designed primarily for migration CLPS711X subarch for multiplatform & DT, for this as the "OF" and "not-OF" calls implemented. Signed-off-by: Alexander Shiyan --- .../interrupt-controller/cirrus,clps711x-intc.txt | 42 ++++ arch/arm/Kconfig | 2 - drivers/irqchip/Kconfig | 6 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-clps711x.c | 277 +++++++++++++++++++++ 5 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt create mode 100644 drivers/irqchip/irq-clps711x.c diff --git a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt new file mode 100644 index 0000000..26f8983 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt @@ -0,0 +1,42 @@ +Cirrus Logic CLPS711X Interrupt Controller + +Required properties: + +- compatible: Should be "cirrus,clps711x-intc" +- reg: Specifies base physical address of the registers set +- interrupt-controller: Identifies the node as an interrupt controller +- #interrupt-cells: Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +The interrupt sources are as follows: +ID Name Description +--------------------------- +1: BLINT Battery low (FIQ) +3: MCINT Media changed (FIQ) +4: CSINT CODEC sound +5: EINT1 External 1 +6: EINT2 External 2 +7: EINT3 External 3 +8: TC1OI TC1 under flow +9: TC2OI TC2 under flow +10: RTCMI RTC compare match +11: TINT 64Hz tick +12: UTXINT1 UART1 transmit FIFO half empty +13: URXINT1 UART1 receive FIFO half full +14: UMSINT UART1 modem status changed +15: SSEOTI SSI1 end of transfer +16: KBDINT Keyboard +17: SS2RX SSI2 receive FIFO half or greater full +18: SS2TX SSI2 transmit FIFO less than half empty +28: UTXINT2 UART2 transmit FIFO half empty +29: URXINT2 UART2 receive FIFO half full +32: DAIINT DAI interface (FIQ) + +Example: + +intc: interrupt-controller { + compatible = "cirrus,clps711x-intc"; + reg = <0x80000000 0>; + interrupt-controller; + #interrupt-cells = <1>; +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5c60cb7..6f0d238 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -373,8 +373,6 @@ config ARCH_CLPS711X select COMMON_CLK select CPU_ARM720T select MFD_SYSCON - select MULTI_IRQ_HANDLER - select SPARSE_IRQ help Support for Cirrus Logic 711x/721x/731x based boards. diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 1fea003..c94d00a 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -30,6 +30,12 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config CLPS711X_IRQCHIP + def_bool y if ARCH_CLPS711X + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + select SPARSE_IRQ + config ORION_IRQCHIP bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index e65c41a..a80ff5b 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o +obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c new file mode 100644 index 0000000..819baa9 --- /dev/null +++ b/drivers/irqchip/irq-clps711x.c @@ -0,0 +1,277 @@ +/* + * CLPS711X IRQ driver + * + * Copyright (C) 2013 Alexander Shiyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "irqchip.h" + +#define CLPS711X_INTSR1 (0x0240) +#define CLPS711X_INTMR1 (0x0280) +#define CLPS711X_BLEOI (0x0600) +#define CLPS711X_MCEOI (0x0640) +#define CLPS711X_TEOI (0x0680) +#define CLPS711X_TC1EOI (0x06c0) +#define CLPS711X_TC2EOI (0x0700) +#define CLPS711X_RTCEOI (0x0740) +#define CLPS711X_UMSEOI (0x0780) +#define CLPS711X_COEOI (0x07c0) +#define CLPS711X_INTSR2 (0x1240) +#define CLPS711X_INTMR2 (0x1280) +#define CLPS711X_SRXEOF (0x1600) +#define CLPS711X_KBDEOI (0x1700) +#define CLPS711X_INTSR3 (0x2240) +#define CLPS711X_INTMR3 (0x2280) + +static const struct { +#define CLPS711X_FLAG_EN (1 << 0) +#define CLPS711X_FLAG_FIQ (1 << 1) + unsigned int flags; + phys_addr_t phys_eoi; +} clps711x_irqs[] __initconst = { + [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, }, + [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, }, + [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, }, + [5] = { CLPS711X_FLAG_EN, }, + [6] = { CLPS711X_FLAG_EN, }, + [7] = { CLPS711X_FLAG_EN, }, + [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, }, + [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, }, + [10] = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, }, + [11] = { CLPS711X_FLAG_EN, CLPS711X_TEOI, }, + [12] = { CLPS711X_FLAG_EN, }, + [13] = { CLPS711X_FLAG_EN, }, + [14] = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, }, + [15] = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, }, + [16] = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, }, + [17] = { CLPS711X_FLAG_EN, }, + [18] = { CLPS711X_FLAG_EN, }, + [28] = { CLPS711X_FLAG_EN, }, + [29] = { CLPS711X_FLAG_EN, }, + [32] = { CLPS711X_FLAG_FIQ, }, +}; + +static struct { + struct irq_domain *domain; + phys_addr_t phys_base; + void __iomem *intmr[3]; + void __iomem *intsr[3]; + void __iomem *eoi[ARRAY_SIZE(clps711x_irqs)]; +} *clps711x_intc; + + +static inline u32 fls16(u32 x) +{ + u32 r = 15; + + if (!(x & 0xff00)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf000)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc000)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x8000)) + r--; + + return r; +} + +static asmlinkage void __exception_irq_entry +clps711x_handle_irq(struct pt_regs *regs) +{ + do { + u32 irqnr, irqstat; + + irqstat = readw(clps711x_intc->intmr[0]) & + readw(clps711x_intc->intsr[0]); + if (irqstat) { + irqnr = irq_find_mapping(clps711x_intc->domain, + fls16(irqstat)); + handle_IRQ(irqnr, regs); + } + + irqstat = readw(clps711x_intc->intmr[1]) & + readw(clps711x_intc->intsr[1]); + if (irqstat) { + irqnr = irq_find_mapping(clps711x_intc->domain, + fls16(irqstat) + 16); + handle_IRQ(irqnr, regs); + continue; + } + + break; + } while (1); +} + +static void clps711x_intc_eoi(struct irq_data *d) +{ + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + writel(0, clps711x_intc->eoi[hwirq]); +} + +static void clps711x_intc_mask(struct irq_data *d) +{ + irq_hw_number_t hwirq = irqd_to_hwirq(d); + void __iomem *intmr = clps711x_intc->intmr[hwirq / 16]; + u32 tmp; + + tmp = readl(intmr); + tmp &= ~(1 << (hwirq % 16)); + writel(tmp, intmr); +} + +static void clps711x_intc_unmask(struct irq_data *d) +{ + irq_hw_number_t hwirq = irqd_to_hwirq(d); + void __iomem *intmr = clps711x_intc->intmr[hwirq / 16]; + u32 tmp; + + tmp = readl(intmr); + tmp |= 1 << (hwirq % 16); + writel(tmp, intmr); +} + +static struct irq_chip clps711x_intc_chip = { + .name = "clps711x-intc", + .irq_eoi = clps711x_intc_eoi, + .irq_mask = clps711x_intc_mask, + .irq_unmask = clps711x_intc_unmask, +}; + +static void __iomem __init *clps711x_ioremap_one(phys_addr_t reg) +{ + void __iomem *ret; + + if (!request_mem_region(clps711x_intc->phys_base + reg, SZ_4, NULL)) + return ERR_PTR(-EBUSY); + + ret = ioremap(clps711x_intc->phys_base + reg, SZ_4); + if (!ret) + return ERR_PTR(-ENOMEM); + + return ret; +} + +static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_flow_handler_t handler = handle_level_irq; + unsigned int flags = IRQF_VALID | IRQF_PROBE; + + if (!clps711x_irqs[hw].flags) + return 0; + + if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) { + handler = handle_bad_irq; + flags |= IRQF_NOAUTOEN; + } else if (clps711x_irqs[hw].phys_eoi) { + handler = handle_fasteoi_irq; + + clps711x_intc->eoi[hw] = + clps711x_ioremap_one(clps711x_irqs[hw].phys_eoi); + if (IS_ERR_OR_NULL(clps711x_intc->eoi[hw])) + return PTR_ERR(clps711x_intc->eoi[hw]); + + /* Clear down pending interrupt */ + writel(0, clps711x_intc->eoi[hw]); + } + + irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler); + set_irq_flags(virq, flags); + + return 0; +} + +static struct irq_domain_ops clps711x_intc_ops = { + .map = clps711x_intc_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static void __init _clps711x_intc_init(phys_addr_t phys_base, + struct device_node *np) +{ + clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL); + BUG_ON(!clps711x_intc); + + clps711x_intc->phys_base = phys_base; + + clps711x_intc->intsr[0] = clps711x_ioremap_one(CLPS711X_INTSR1); + BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intsr[0])); + + clps711x_intc->intmr[0] = clps711x_ioremap_one(CLPS711X_INTMR1); + BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intmr[0])); + + clps711x_intc->intsr[1] = clps711x_ioremap_one(CLPS711X_INTSR2); + BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intsr[1])); + + clps711x_intc->intmr[1] = clps711x_ioremap_one(CLPS711X_INTMR2); + BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intmr[1])); + + clps711x_intc->intsr[2] = clps711x_ioremap_one(CLPS711X_INTSR3); + BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intsr[2])); + + clps711x_intc->intmr[2] = clps711x_ioremap_one(CLPS711X_INTMR3); + BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intmr[2])); + + /* Disable interrupts */ + writel(0, clps711x_intc->intmr[0]); + writel(0, clps711x_intc->intmr[1]); + writel(0, clps711x_intc->intmr[2]); + + BUG_ON(IS_ERR_VALUE(irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), + numa_node_id()))); + + clps711x_intc->domain = + irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs), + 0, 0, &clps711x_intc_ops, NULL); + BUG_ON(!clps711x_intc->domain); + + irq_set_default_host(clps711x_intc->domain); + set_handle_irq(clps711x_handle_irq); + +#ifdef CONFIG_FIQ + init_FIQ(0); +#endif +} + +void __init clps711x_intc_init(phys_addr_t phys_base) +{ + _clps711x_intc_init(phys_base, NULL); +} + +#ifdef CONFIG_IRQCHIP +static int __init clps711x_intc_init_dt(struct device_node *np, + struct device_node *parent) +{ + struct resource res; + + BUG_ON(of_address_to_resource(np, 0, &res)); + + _clps711x_intc_init(res.start, np); + + return 0; +} +IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt); +#endif