Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753147AbbHUNp3 (ORCPT ); Fri, 21 Aug 2015 09:45:29 -0400 Received: from mail-bn1bbn0105.outbound.protection.outlook.com ([157.56.111.105]:46208 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752068AbbHUNp1 (ORCPT ); Fri, 21 Aug 2015 09:45:27 -0400 From: Shenwei Wang To: "shawn.guo@linaro.org" , "tglx@linutronix.de" , "jason@lakedaemon.net" CC: "linux-arm-kernel@lists.infradead.org" , "linux-kernel@vger.kernel.org" , Huang Anson Subject: RE: [PATCH v8 1/2] irqchip: imx-gpcv2: IMX GPCv2 driver for wakeup sources Thread-Topic: [PATCH v8 1/2] irqchip: imx-gpcv2: IMX GPCv2 driver for wakeup sources Thread-Index: AQHQy9jL+RBhKH42jEGfmT8ssDAQNZ4Wl2dA Date: Fri, 21 Aug 2015 13:45:13 +0000 Message-ID: References: <1438378439-11569-1-git-send-email-shenwei.wang@freescale.com> <1438378439-11569-2-git-send-email-shenwei.wang@freescale.com> In-Reply-To: <1438378439-11569-2-git-send-email-shenwei.wang@freescale.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Shenwei.Wang@freescale.com; x-originating-ip: [192.88.168.50] x-microsoft-exchange-diagnostics: 1;CY1PR03MB1373;5:hkDf5FPvNn0UKmZj5SIjyOvXLMb96NJL59VJjscAkYRIfJFZSrkKfi8CGLmf93aN4wmRg3QOtKYmyDQr+tSONzbLdu4GhdfFW0AniCgj1RAOiEove1FX9NhOgTMWbLD6g37DnJ6urXQp+QbgWGRbXA==;24:mZHj8/nluQbWs2XUiM5pckSWSpQDhO+VLCMBDqOAWbRZiOc3iLzcdz6ORtgLnmdzK0Ims/GdXsJc46TSl9TmDwFPExK7QUzQ9cDnB/yZCkk=;20:bYLJVePFU1D5FByMG94+uRlII4hiopWYWzZuQ6sDfPRumNbM9f7cumcAPby9GUN+cre/v+/e9bxTv6cIF3ZaVQ== x-microsoft-antispam: UriScan:;BCL:0;PCL:0;RULEID:(42134001)(42139001);SRVR:CY1PR03MB1373; x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:; x-exchange-antispam-report-cfa-test: BCL:0;PCL:0;RULEID:(601004)(5005006)(8121501046)(3002001);SRVR:CY1PR03MB1373;BCL:0;PCL:0;RULEID:;SRVR:CY1PR03MB1373; x-forefront-prvs: 067553F396 x-forefront-antispam-report: SFV:NSPM;SFS:(10019020)(6009001)(13464003)(189002)(199003)(19580405001)(2950100001)(68736005)(76576001)(5003600100002)(74316001)(33656002)(99286002)(189998001)(77096005)(2501003)(2656002)(106116001)(10400500002)(64706001)(106356001)(5002640100001)(54356999)(92566002)(4001540100001)(66066001)(77156002)(76176999)(50986999)(97736004)(40100003)(2201001)(5001830100001)(5001860100001)(2900100001)(5001960100002)(19580395003)(107886002)(5001770100001)(62966003)(81156007)(87936001)(5004730100002)(46102003)(105586002)(122556002)(5007970100001)(15975445007)(86362001)(102836002)(101416001)(575784001)(5001920100001)(2004002)(4001430100001);DIR:OUT;SFP:1102;SCL:1;SRVR:CY1PR03MB1373;H:CY1PR0301MB0843.namprd03.prod.outlook.com;FPR:;SPF:None;PTR:InfoNoRecords;MX:1;A:1;LANG:en; Content-Type: text/plain; charset="gb2312" MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Aug 2015 13:45:13.2415 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-Transport-CrossTenantHeadersStamped: CY1PR03MB1373 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from base64 to 8bit by mail.home.local id t7LDjZiY001325 Content-Length: 10061 Lines: 341 Ping. Shenwei > -----Original Message----- > From: linux-kernel-owner@vger.kernel.org > [mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Shenwei Wang > Sent: 2015??7??31?? 16:34 > To: shawn.guo@linaro.org; tglx@linutronix.de; jason@lakedaemon.net > Cc: linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Huang > Yongcai-B20788 > Subject: [PATCH v8 1/2] irqchip: imx-gpcv2: IMX GPCv2 driver for wakeup sources > > IMX7D contains a new version of GPC IP block (GPCv2). It has two major functions: > power management and wakeup source management. > This patch adds a new irqchip driver to manage the interrupt wakeup sources on > IMX7D. > When the system is in WFI (wait for interrupt) mode, this GPC block will be the > first block on the platform to be activated and signaled. > Under normal wait mode during cpu idle, the system can be woke up by any > enabled interrupts. Under standby or suspend mode, the system can only be > woke up by the pre-defined wakeup sources. > > Signed-off-by: Shenwei Wang > Signed-off-by: Anson Huang > --- > drivers/irqchip/Kconfig | 7 ++ > drivers/irqchip/Makefile | 1 + > drivers/irqchip/irq-imx-gpcv2.c | 273 > ++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 281 insertions(+) > create mode 100644 drivers/irqchip/irq-imx-gpcv2.c > > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index > 120d815..3fc0fac 100644 > --- a/drivers/irqchip/Kconfig > +++ b/drivers/irqchip/Kconfig > @@ -177,3 +177,10 @@ config RENESAS_H8300H_INTC config > RENESAS_H8S_INTC > bool > select IRQ_DOMAIN > + > +config IMX_GPCV2 > + bool > + select IRQ_DOMAIN > + help > + Enables the wakeup IRQs for IMX platforms with GPCv2 block > + > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index > b8d4e96..8eb5f60 100644 > --- a/drivers/irqchip/Makefile > +++ b/drivers/irqchip/Makefile > @@ -52,3 +52,4 @@ obj-$(CONFIG_RENESAS_H8300H_INTC) += > irq-renesas-h8300h.o > obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o > obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o > obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o > +obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o > diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c new > file mode 100644 index 0000000..c2728b0 > --- /dev/null > +++ b/drivers/irqchip/irq-imx-gpcv2.c > @@ -0,0 +1,273 @@ > +/* > + * Copyright (C) 2015 Freescale Semiconductor, Inc. > + * > + * 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. > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#define IMR_NUM 4 > +#define GPC_MAX_IRQS (IMR_NUM * 32) > + > +#define GPC_IMR1_CORE0 0x30 > +#define GPC_IMR1_CORE1 0x40 > + > +struct gpcv2_irqchip_data { > + struct raw_spinlock rlock; > + void __iomem *gpc_base; > + u32 wakeup_sources[IMR_NUM]; > + u32 enabled_irqs[IMR_NUM]; > + u32 cpu2wakeup; > +}; > + > +static struct gpcv2_irqchip_data *imx_gpcv2_instance; > + > +u32 imx_gpcv2_get_wakeup_source(u32 **sources) { > + if (!imx_gpcv2_instance) > + return 0; > + > + if (sources) > + *sources = imx_gpcv2_instance->wakeup_sources; > + > + return IMR_NUM; > +} > + > +static int gpcv2_wakeup_source_save(void) { > + struct gpcv2_irqchip_data *cd; > + void __iomem *reg; > + int i; > + > + cd = imx_gpcv2_instance; > + if (!cd) > + return 0; > + > + for (i = 0; i < IMR_NUM; i++) { > + reg = cd->gpc_base + cd->cpu2wakeup + i * 4; > + cd->enabled_irqs[i] = readl_relaxed(reg); > + writel_relaxed(cd->wakeup_sources[i], reg); > + } > + > + return 0; > +} > + > +static void gpcv2_wakeup_source_restore(void) { > + struct gpcv2_irqchip_data *cd; > + void __iomem *reg; > + int i; > + > + cd = imx_gpcv2_instance; > + if (!cd) > + return; > + > + for (i = 0; i < IMR_NUM; i++) { > + reg = cd->gpc_base + cd->cpu2wakeup + i * 4; > + writel_relaxed(cd->enabled_irqs[i], reg); > + cd->wakeup_sources[i] = ~0; > + } > +} > + > +static struct syscore_ops imx_gpcv2_syscore_ops = { > + .suspend = gpcv2_wakeup_source_save, > + .resume = gpcv2_wakeup_source_restore, > +}; > + > +static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on) > +{ > + struct gpcv2_irqchip_data *cd = d->chip_data; > + unsigned int idx = d->hwirq / 32; > + unsigned long flags; > + void __iomem *reg; > + u32 mask, val; > + > + raw_spin_lock_irqsave(&cd->rlock, flags); > + reg = cd->gpc_base + cd->cpu2wakeup + idx * 4; > + mask = 1 << d->hwirq % 32; > + val = cd->wakeup_sources[idx]; > + > + cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask); > + raw_spin_unlock_irqrestore(&cd->rlock, flags); > + > + /* > + * Do *not* call into the parent, as the GIC doesn't have any > + * wake-up facility... > + */ > + > + return 0; > +} > + > +static void imx_gpcv2_irq_unmask(struct irq_data *d) { > + struct gpcv2_irqchip_data *cd = d->chip_data; > + void __iomem *reg; > + u32 val; > + > + raw_spin_lock(&cd->rlock); > + reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4; > + val = readl_relaxed(reg); > + val &= ~(1 << d->hwirq % 32); > + writel_relaxed(val, reg); > + raw_spin_unlock(&cd->rlock); > + > + irq_chip_unmask_parent(d); > +} > + > +static void imx_gpcv2_irq_mask(struct irq_data *d) { > + struct gpcv2_irqchip_data *cd = d->chip_data; > + void __iomem *reg; > + u32 val; > + > + raw_spin_lock(&cd->rlock); > + reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4; > + val = readl_relaxed(reg); > + val |= 1 << (d->hwirq % 32); > + writel_relaxed(val, reg); > + raw_spin_unlock(&cd->rlock); > + > + irq_chip_mask_parent(d); > +} > + > +static struct irq_chip gpcv2_irqchip_data_chip = { > + .name = "GPCv2", > + .irq_eoi = irq_chip_eoi_parent, > + .irq_mask = imx_gpcv2_irq_mask, > + .irq_unmask = imx_gpcv2_irq_unmask, > + .irq_set_wake = imx_gpcv2_irq_set_wake, > + .irq_retrigger = irq_chip_retrigger_hierarchy, > +#ifdef CONFIG_SMP > + .irq_set_affinity = irq_chip_set_affinity_parent, > +#endif > +}; > + > +static int imx_gpcv2_domain_xlate(struct irq_domain *domain, > + struct device_node *controller, > + const u32 *intspec, > + unsigned int intsize, > + unsigned long *out_hwirq, > + unsigned int *out_type) > +{ > + /* Shouldn't happen, really... */ > + if (domain->of_node != controller) > + return -EINVAL; > + > + /* Not GIC compliant */ > + if (intsize != 3) > + return -EINVAL; > + > + /* No PPI should point to this domain */ > + if (intspec[0] != 0) > + return -EINVAL; > + > + *out_hwirq = intspec[1]; > + *out_type = intspec[2]; > + return 0; > +} > + > +static int imx_gpcv2_domain_alloc(struct irq_domain *domain, > + unsigned int irq, unsigned int nr_irqs, > + void *data) > +{ > + struct of_phandle_args *args = data; > + struct of_phandle_args parent_args; > + irq_hw_number_t hwirq; > + int i; > + > + /* Not GIC compliant */ > + if (args->args_count != 3) > + return -EINVAL; > + > + /* No PPI should point to this domain */ > + if (args->args[0] != 0) > + return -EINVAL; > + > + /* Can't deal with this */ > + hwirq = args->args[1]; > + if (hwirq >= GPC_MAX_IRQS) > + return -EINVAL; > + > + for (i = 0; i < nr_irqs; i++) { > + irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i, > + &gpcv2_irqchip_data_chip, domain->host_data); > + } > + > + parent_args = *args; > + parent_args.np = domain->parent->of_node; > + return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, > +&parent_args); } > + > +static struct irq_domain_ops gpcv2_irqchip_data_domain_ops = { > + .xlate = imx_gpcv2_domain_xlate, > + .alloc = imx_gpcv2_domain_alloc, > + .free = irq_domain_free_irqs_common, > +}; > + > +static int __init imx_gpcv2_irqchip_init(struct device_node *node, > + struct device_node *parent) > +{ > + struct irq_domain *parent_domain, *domain; > + struct gpcv2_irqchip_data *cd; > + int i; > + > + if (!parent) { > + pr_err("%s: no parent, giving up\n", node->full_name); > + return -ENODEV; > + } > + > + parent_domain = irq_find_host(parent); > + if (!parent_domain) { > + pr_err("%s: unable to get parent domain\n", node->full_name); > + return -ENXIO; > + } > + > + cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL); > + BUG_ON(!cd); > + > + cd->gpc_base = of_iomap(node, 0); > + if (!cd->gpc_base) { > + pr_err("fsl-gpcv2: unable to map gpc registers\n"); > + kfree(cd); > + return -ENOMEM; > + } > + > + domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS, > + node, &gpcv2_irqchip_data_domain_ops, cd); > + if (!domain) { > + iounmap(cd->gpc_base); > + kfree(cd); > + return -ENOMEM; > + } > + irq_set_default_host(domain); > + > + /* Initially mask all interrupts */ > + for (i = 0; i < IMR_NUM; i++) { > + writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE0 + i * 4); > + writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE1 + i * 4); > + cd->wakeup_sources[i] = ~0; > + } > + > + /* Let CORE0 as the default CPU to wake up by GPC */ > + cd->cpu2wakeup = GPC_IMR1_CORE0; > + > + /* > + * Due to hardware design failure, need to make sure GPR > + * interrupt(#32) is unmasked during RUN mode to avoid entering > + * DSM by mistake. > + */ > + writel_relaxed(~0x1, cd->gpc_base + cd->cpu2wakeup); > + > + imx_gpcv2_instance = cd; > + register_syscore_ops(&imx_gpcv2_syscore_ops); > + > + return 0; > +} > + > +IRQCHIP_DECLARE(imx_gpcv2, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init); > -- > 2.5.0.rc2 > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ ????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?