Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965716AbcJYNKq (ORCPT ); Tue, 25 Oct 2016 09:10:46 -0400 Received: from mail-bl2nam02on0055.outbound.protection.outlook.com ([104.47.38.55]:9280 "EHLO NAM02-BL2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1759055AbcJYNKk (ORCPT ); Tue, 25 Oct 2016 09:10:40 -0400 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=nxp.com; nxp.com; dkim=none (message not signed) header.d=none;nxp.com; dmarc=fail action=none header.from=nxp.com;nxp.com; dkim=none (message not signed) header.d=none; From: Minghuan Lian To: , , CC: Marc Zyngier , Rob Herring , Jason Cooper , Roy Zang , Mingkai Hu , Stuart Yoder , Yang-Leo Li , Scott Wood , Minghuan Lian Subject: [PATCH] irqchip/ls-scfg-msi: update Layerscape SCFG MSI driver Date: Tue, 25 Oct 2016 20:39:22 +0800 Message-ID: <1477399162-22881-1-git-send-email-Minghuan.Lian@nxp.com> X-Mailer: git-send-email 1.9.1 X-EOPAttributedMessage: 0 X-Matching-Connectors: 131218726344739885;(91ab9b29-cfa4-454e-5278-08d120cd25b8);() X-Forefront-Antispam-Report: CIP:192.88.168.50;IPV:NLI;CTRY:US;EFV:NLI;SFV:NSPM;SFS:(10009020)(6009001)(7916002)(2980300002)(1110001)(1109001)(339900001)(189002)(199003)(87936001)(7416002)(92566002)(97736004)(85426001)(5660300001)(229853001)(106466001)(48376002)(5001770100001)(626004)(11100500001)(50226002)(104016004)(8676002)(77096005)(15975445007)(81166006)(50466002)(81156014)(2201001)(86362001)(575784001)(7846002)(8666005)(230783001)(305945005)(15650500001)(6666003)(68736007)(8936002)(356003)(50986999)(2906002)(4326007)(19580395003)(19580405001)(36756003)(47776003)(189998001)(5003940100001)(586003)(105606002)(7059030)(2004002)(217873001)(2101003);DIR:OUT;SFP:1101;SCL:1;SRVR:BN3PR03MB2369;H:tx30smr01.am.freescale.net;FPR:;SPF:Fail;PTR:InfoDomainNonexistent;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11FD017;1:OXmbCq3PaEuaGpd1nCPi25X+0GK4+AI1WIScytVDgJGHao0yzD7JB6Rl2tOz4ej3VtCobB6YnWYEOJulkoto7SjsmWte6+ayqf6UfF6r0Ume+xHv/BLeZIpQJ/bHK6MAJa65g0INhz5zFwV8377REHpAdsMW/BA0T3oGLQ94RhsofR0xPDE7FhR1UPTKKsI2BQ7d8L6BtGlGy3fIZFf0anuLWJ9eI/XjFq6hrwD7R0nrVvkF/4FsK3ylJ1Anqzsban8gDXbcs9Xxi1CTrI0UgMGiVzVphf5CzDcjALsy40mYAAtu4zR5GGBcfkBNcPg15+qeM0f2WCFImc44XFSbzQ3k7PPkErK/hWs6NDDXLJsI+2+16lxwPlp0lvuww0d8nEV19WARSTpcqm3Mcdf1E3LhvNiIvnQiUbCThRugRv4ztb8ty+WGUc0sVcWWq0ehXvUrQw+RU4JGYfy/UYuCk+X0IrgoL16+ZzC8JDVrVmUf0hvXBAMgkuOsY9diMC1DRxQLkOzyHIgqqijr3hMNz50ZQ7zS+aAeW90BVm57NuClYsQpffn4YbCYHhqCEufH3Rn/cFLeWcgxL5pV7IchnO9b3yQ0Xn9oYGo/HseOvQDVe3kcoKgReiHTfxp9SD2l/9R002tC7pL6WcDgLujZk/dgZEWsmewaas5/Z+bV1XiJHz1dPkF8dGdFx++R3seMZNGs4wEmOiIAyXxJo3yt5lxI95zAJILpvjvzb0irEzAedUrsz/sJ1HTWsB0AUtmh MIME-Version: 1.0 Content-Type: text/plain X-MS-Office365-Filtering-Correlation-Id: 30ec0d67-abd2-438e-eacc-08d3fcd3a61c X-Microsoft-Exchange-Diagnostics: 1;BN3PR03MB2369;2:HEs68LwEeYliFnlabceFstiUJBzSZ53uvADr12XWjJY0MPmWttTFGP4qV+uFJi90nywrNk6ZTOjMCWi7X+D0WUiENjfCFjMD340kBpmMYtjI1DpnWETOwqRPy4j1G7G3f+uIFGTTCBcsCzLnsAD0Qt7g4X45DxU2vHF82VwxUZLxWDfJlFfNkr65ZPYvYdx+Tuk25oNHGUNcd/oLO5rBJg==;3:m90HnPFCz6ax3XLPxqcs1hpPQ/7CT+6W0TjM8d7YQ7bijdi1p1LTVJ9qDGdUufmGoD/0va2ztYuZNwIEnfZX8nsUoIImRAnGaIKjzCVE7oLNfazWCvqU8mEf4KQUHqmkF2cHWz2q5SvDctx5T3jOnOI2iud9NCUN3q2VcarhVGtVklcnKcnvyIm35s0SVTLPbvVah6k9+rUHqChRq4QV+4thD/oqCIKZMT7KO4jug7iLcq91rEODzersCG2dewMy;25:OrvAc50Zzk8QqSEeHBIeQ+OroI1rqFKdEjLrunMPVlRoG8bhdJi1azHBPMXN3wN3mrvAqVq/pjv0cJYuQN5IAac6utnpxedY9KElya13LbLLZjyu7ifYXVC/sCRK9bxaMzocQe0dxKek7Eio4N/VEizmXDf+IcbGCJ7PQ+wnWjbdATi276HxDbxxpkB3vwGUy6EL/5jQxSeyO9Cd6Ncu1qijQgPSL+Es7FeiPBY4bUwqER8DfQZ82+UjYqDei0KmdBT06DNKJ9yetX7usS5BBENUAFfJeFmbQVKlfiAxagn3mLbaelE2z3lzp5plmK34EJG22oPLxKe0BJy7VgsArXx/z/pMH9Z8MM3P6nzUBHPg1fvrmX/AuPblItdn5XPrm+kt6k7bQUBB8RqLNSFChkvShuC3IhGCHo9kkWm7QuS5WUnQdOx8huzs9GUMNbT9tdJOCTuQGXL+01h4euj/ew== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BN3PR03MB2369; X-Microsoft-Exchange-Diagnostics: 1;BN3PR03MB2369;31:xMmXfBENgXovnZnf//rnohAMQ0NGtdlz/hCqgLqw/zCTALbZEv2unNjznlDOduIRNAaDPNcM3KN0KZzGWLfRFjZI5kWPVWyPF2poeWb96GHFXgWVXXyn4Sx9SsE2FPx05PLbISuXeF0kaQOj8/IJH1yU0t6XHdRyKOlwpp4lq4wXVf4nNLdn7wMM21kG+KBbCJ49iVx494OMbLnmAQPN0c5NEgT6EiakFtZZAllte18ZMnNFOMN39Ni46FSOLXyeHijznIW4wFGHg8mFwt8xBzbYavQ6d2DL1ASQh3bzqTw=;4:BJU4eP2ZZl4+TD8010N9tRczMXd80g5hL7l77CMExRJWled3Hk9FCm8jjbc2Vu7bs/0NCHVQZerMRtCGipFNhTg0/UeMUHCUUHuhf8gZMgnuHMxo7RacZftOae7FPvTNthf3LbHOZjSrWvLgIoSruUyOPXfp3AN3NNwca+2/2unxzQZNS5m7k7Kz7B3gHZH7sSxLh8dNYcmRUYW215qQaRlUftDLAUeZeC1LCE2MlR/ahMuUzxKQhSxReUrrcK/3CGm9+rG/ttQMXY4HwNZ2qMch1iBfLACIlrD8STqNHy8/hdW+iiGha/ylW7M36xa/xvzfeeOWQSkj5sJYbkaT7a9X3nduGQ0uZgIbQRRAplCH+X7MCwi6TPtQC1MJJv5BVOS7P6qJuJETFf5CajaeUs4pgG7+bXXyqYo2VdyFAMmabmxSsuoDyz21Tbn3J2YxgNxFx/K1fLbLOCiqHC4BgKwob24RuxTchIVARUIGSR+8PJDmkim9W7qgR83kCMVeXs+a50BdZn67pdyuqV/v1vFU0uBJLNOmIsLMunBgSbOOyJRsrrqkbfXOxV7/tmm4 X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(185117386973197); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040176)(601004)(2401047)(5005006)(8121501046)(13023025)(13018025)(13015025)(13024025)(13017025)(3002001)(10201501046)(6055026);SRVR:BN3PR03MB2369;BCL:0;PCL:0;RULEID:(400006);SRVR:BN3PR03MB2369; X-Forefront-PRVS: 01068D0A20 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BN3PR03MB2369;23:c8PGgFefnMMEzvg5gQD+mHtIetH822mpntWQYMqU2?= =?us-ascii?Q?z6jfWkET3KhUTjQoifjCwjGzaBa18O2IqrTCeLDTvjZGmQ703HgrAU7UNLJX?= =?us-ascii?Q?JMdS/PtughrnggNaHOLTBVKiZUpdFGHi69VMEJ0NOzaeOjRda5SUmbZq53u2?= =?us-ascii?Q?q3qWFo4rcD/zVJ+XvleT7ovdHQnzm4LDTOOXBAH5eWZxoXzD/CGLKsjTn00l?= =?us-ascii?Q?7GV2t2XPgq1fE4Wz6C9qRjhBPovLF6NukMMPDsaPU6HS4+urPdpD31CLNNnr?= =?us-ascii?Q?3LUMCIm8WgTHi4TjZXzBSpJ3yvvsu8vk58bELe4bJNmukg/tzpGXIfHP51Uo?= =?us-ascii?Q?3doaxZ1pL1BAsx1t0wi3whmKlEg184GghorHkzkO/Yj+gJFOS4NhAQJctfxM?= =?us-ascii?Q?8Fg1lIQi1m6iNTYFqSxMuzYp2NifFgz+p03kqrpoYGp3zgufowmlYEF0HGv5?= =?us-ascii?Q?4/i+vVYBqxLYAr+oRg9/tryc4/M8v4vJP76rNG8a9u8oxwbPpqtKqq7ZvJb6?= =?us-ascii?Q?GDhEulzg9umqalGCODax/vSZvF17p+1wdpjLpF3i+v1VzA6j8UntTOSqIJiK?= =?us-ascii?Q?VSYAACYYRgD2zjgRXoqlBD1mOaJaIe0uVp9NUpDWRgPMrkrmYEZODl/fpMiK?= =?us-ascii?Q?UxH9UJwDaBU5sLQnecSR0PudDAktgm7sCNliDtOP1RWHPxngG0PHsi2Jr10+?= =?us-ascii?Q?8akSZJV1dFKUkNiva1V2Ng9ZI+W45XIoQ5vyGblMBOOcjXCNV+x9Pj0UrH08?= =?us-ascii?Q?5iMYzrXeYo93YgYyw22iOY4QJnsIqXo+Q9HQ05M0ZjeRa5TmuwWLTBgn4LMf?= =?us-ascii?Q?Xit+gF7OqzuZOzHgTuvfCPR9sWdeJFWNcQ6yqmi0RI059oKpZ9Bhef0UmgLS?= =?us-ascii?Q?F2XCjIVd4uyI4vnFyfvw39MwOmuc95T1eioQN17ylksqMCqX+9F4e1dU27tH?= =?us-ascii?Q?wfdmnf1gXyzoP8T1lFVeKCaC8WoU6T1HdFuZPHe9UoZPgkMXkFOfVjDAWi5E?= =?us-ascii?Q?Z6yxw+vH8aMHIbqAoRPe+/du7WIid8JFkU7TdL/icXP0VR9GceSCPkP7JsEZ?= =?us-ascii?Q?MGXp/z979rYasj6XwS22n2cva1KJTGoQUS15ueRSoRRrDoEC4R5gRYIi+DEL?= =?us-ascii?Q?EkDm0FzjuTwQqUTVCYjl3RWSIX1jjCridGMDpOPWcplis+BV/vUPNe0XzsKz?= =?us-ascii?Q?U9H8wRryDzf2EcvjbZbPG2ZKPeqlbgGH7m2s2pVeFcyUNrMbW8Q0oLYds0eg?= =?us-ascii?Q?9/7Hvi1VamBlUbuniiQiG8pSXtsAvKp30mnaQ6eDzxI6spyPP0x4Ph2OL7fK?= =?us-ascii?Q?J93uvcxQqjFINMDkmWkIgw=3D?= X-Microsoft-Exchange-Diagnostics: 1;BN3PR03MB2369;6:2h6stHf9uyk0CltBfRnFUNEhUnYPckI8m3imfHJHIIBti3j1j+Si/7+Ciz2dgo59BD/ZFW0fiyQFU2iP6mMIbdurYJyLars+PWyGVlndyFe9vp4Ax2u2nmikKW9ak9jfi0WRAfBNGdq4YXerXRWtKN4ydr0mRwd3yTNBOHq8Z+I3CGWVvT7+gcH161s1Pd1/lLJudrFdHW9YS2dhe/TPg5KfCgbzwPeAPXVxgs3G2TtdBVXrx25l7CIObq3uzbhMVDDIxNEJN1i7TuOHKt+L0lWLajqu6VitlTMr08GXXAOw3KIYPZsGQbuJokeL8BVh;5:sMGCGvyLw0I03+vZwP6HdlMF8o6S+l5SdloR9CUq3eWv+uNV7PtzM360QTZGiieFmOz0e1YJyWshx9bIE/aDJekj4zJDkoUHID0sumq0xSRe+6LbYhwN/Nw6YpeugXUIHxFLhRJp6wmId9hN/uCYvWqeLO1pnNNxEmtydi5bQNh3aLeEfIRx9NCL9FEJq9dj;24:4BEJuqn84bP7znt0PV0I6wPqvhFp6XHcaxhB6u1Yao70f+lgeMTGQEGQY789khFGSV5HiavgpvWeYt3iOjOBi4U3SIeInYR3YzK5wuQJPe0= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;BN3PR03MB2369;7:KvvHN07xaFvt9CSYNLPo1Qov3+MNFj9E3JMmclpblBNBcx+6JUtO7ZwGA5qF7U3w9dyDvD5tEb++I0liIGigjX+yF7rIE0DvGK115hZhfxw7C3DBVMnRsrI0wRo3zHI+Bsir9OFJ6ZosvC5W5JxpUOnuqVuMJDRoBf61w9wJzS3tPNjmwOaicYRl428Oux3HMFf5l0E1lBsS5ABkuywPP8pUT7N+cRwSFdLbXz+c7H0+xT8mXp51Iz2Gj6mEdaAyMALHrjJdgAimC/pe0Nn/NejDNPQUu3SijYKW4aYBXLQ2I//kdh1vPZTVtxIEeRyukLODs8cOxKOU9UiGLdP7x89R3hURD/PHXloWQuAGhvQ= X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Oct 2016 12:37:14.2867 (UTC) X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e;Ip=[192.88.168.50];Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN3PR03MB2369 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15588 Lines: 576 1. The patch uses soc_device_match() to match the SoC family and revision instead of DTS compatible, because compatible cannot describe the SoC revision information. 2. The patch provides a new method to support Layerscape SCFG MSI. It tries to assign a dedicated MSIR to every core. When changing a MSI interrupt affinity, the MSI message data will be changed to refer to a new MSIR that has been associated with the core. Signed-off-by: Minghuan Lian --- The patch depends on https://patchwork.kernel.org/patch/9342915/ drivers/irqchip/irq-ls-scfg-msi.c | 444 +++++++++++++++++++++++++++++++------- 1 file changed, 367 insertions(+), 77 deletions(-) diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c index 02cca74c..0245d8a 100644 --- a/drivers/irqchip/irq-ls-scfg-msi.c +++ b/drivers/irqchip/irq-ls-scfg-msi.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -17,23 +18,91 @@ #include #include #include +#include +#include #include #include #include +#include -#define MSI_MAX_IRQS 32 -#define MSI_IBS_SHIFT 3 -#define MSIR 4 +#define LS_MSIR_NUM_MAX 4 /* MSIIR can index 4 MSI registers */ +#define IRQS_32_PER_MSIR 32 +#define IRQS_8_PER_MSIR 8 + +#define MSIR_OFFSET(idx) ((idx) * 0x4) + +enum msi_affinity_flag { + MSI_GROUP_AFFINITY_FLAG, + MSI_AFFINITY_FLAG +}; + +struct ls_scfg_msi; +struct ls_scfg_msi_ctrl; + +struct ls_scfg_msi_cfg { + u32 ibs_shift; /* Shift of interrupt bit select */ + u32 msir_irqs; /* The irq number per MSIR */ + u32 msir_base; /* The base address of MSIR */ +}; + +struct ls_scfg_msir { + struct ls_scfg_msi_ctrl *ctrl; + void __iomem *addr; + int index; + int virq; +}; + +struct ls_scfg_msi_ctrl { + struct list_head list; + struct ls_scfg_msi *msi_data; + void __iomem *regs; + phys_addr_t msiir_addr; + enum msi_affinity_flag flag; + int irq_base; + spinlock_t lock; + struct ls_scfg_msir *msir; + unsigned long *bm; +}; struct ls_scfg_msi { - spinlock_t lock; - struct platform_device *pdev; - struct irq_domain *parent; - struct irq_domain *msi_domain; - void __iomem *regs; - phys_addr_t msiir_addr; - int irq; - DECLARE_BITMAP(used, MSI_MAX_IRQS); + struct platform_device *pdev; + struct irq_domain *parent; + struct irq_domain *msi_domain; + struct list_head ctrl_list; + const struct ls_scfg_msi_cfg *cfg; + u32 cpu_num; +}; + +static struct ls_scfg_msi_cfg ls1021_msi_cfg = { + .ibs_shift = 3, + .msir_irqs = IRQS_32_PER_MSIR, + .msir_base = 0x4, +}; + +static struct ls_scfg_msi_cfg ls1043_rev11_msi_cfg = { + .ibs_shift = 2, + .msir_irqs = IRQS_8_PER_MSIR, + .msir_base = 0x10, +}; + +static struct ls_scfg_msi_cfg ls1046_msi_cfg = { + .ibs_shift = 2, + .msir_irqs = IRQS_32_PER_MSIR, + .msir_base = 0x4, +}; + +static struct soc_device_attribute soc_msi_matches[] = { + { .family = "QorIQ LS1021A", + .data = &ls1021_msi_cfg }, + { .family = "QorIQ LS1012A", + .data = &ls1021_msi_cfg }, + { .family = "QorIQ LS1043A", .revision = "1.0", + .data = &ls1021_msi_cfg }, + { .family = "QorIQ LS1043A", .revision = "1.1", + .data = &ls1043_rev11_msi_cfg }, + { .family = "QorIQ LS1046A", + .data = &ls1046_msi_cfg }, + { }, }; static struct irq_chip ls_scfg_msi_irq_chip = { @@ -49,19 +118,53 @@ struct ls_scfg_msi { .chip = &ls_scfg_msi_irq_chip, }; +static int ctrl_num; + +static irqreturn_t (*ls_scfg_msi_irq_handler)(int irq, void *arg); + static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) { - struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data); + struct ls_scfg_msi_ctrl *ctrl = irq_data_get_irq_chip_data(data); + u32 ibs, srs; - msg->address_hi = upper_32_bits(msi_data->msiir_addr); - msg->address_lo = lower_32_bits(msi_data->msiir_addr); - msg->data = data->hwirq << MSI_IBS_SHIFT; + msg->address_hi = upper_32_bits(ctrl->msiir_addr); + msg->address_lo = lower_32_bits(ctrl->msiir_addr); + + ibs = data->hwirq - ctrl->irq_base; + + srs = cpumask_first(irq_data_get_affinity_mask(data)); + if (srs >= ctrl->msi_data->cpu_num) + srs = 0; + + msg->data = ibs << ctrl->msi_data->cfg->ibs_shift | srs; + + pr_debug("%s: ibs %d srs %d address0x%x-0x%x data 0x%x\n", + __func__, ibs, srs, msg->address_hi, + msg->address_lo, msg->data); } -static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) +static int ls_scfg_msi_set_affinity(struct irq_data *data, + const struct cpumask *mask, bool force) { - return -EINVAL; + struct ls_scfg_msi_ctrl *ctrl = irq_data_get_irq_chip_data(data); + u32 cpu; + + if (!force) + cpu = cpumask_any_and(mask, cpu_online_mask); + else + cpu = cpumask_first(mask); + + if (cpu >= ctrl->msi_data->cpu_num) + return -EINVAL; + + if (ctrl->msir[cpu].virq <= 0) { + pr_warn("cannot bind the irq to cpu%d\n", cpu); + return -EINVAL; + } + + cpumask_copy(irq_data_get_affinity_mask(data), mask); + + return IRQ_SET_MASK_OK_NOCOPY; } static struct irq_chip ls_scfg_msi_parent_chip = { @@ -76,44 +179,57 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, void *args) { struct ls_scfg_msi *msi_data = domain->host_data; - int pos, err = 0; + static struct list_head *current_entry; + struct ls_scfg_msi_ctrl *ctrl; + int i, hwirq = -ENOMEM; + + if (!current_entry || current_entry->next == &msi_data->ctrl_list) + current_entry = &msi_data->ctrl_list; + + list_for_each_entry(ctrl, current_entry, list) { + spin_lock(&ctrl->lock); + hwirq = bitmap_find_free_region(ctrl->bm, + msi_data->cfg->msir_irqs, + order_base_2(nr_irqs)); + spin_unlock(&ctrl->lock); + + if (hwirq >= 0) + break; + } - WARN_ON(nr_irqs != 1); + if (hwirq < 0) + return hwirq; - spin_lock(&msi_data->lock); - pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS); - if (pos < MSI_MAX_IRQS) - __set_bit(pos, msi_data->used); - else - err = -ENOSPC; - spin_unlock(&msi_data->lock); + hwirq = hwirq + ctrl->irq_base; - if (err) - return err; + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &ls_scfg_msi_parent_chip, ctrl, + handle_simple_irq, NULL, NULL); - irq_domain_set_info(domain, virq, pos, - &ls_scfg_msi_parent_chip, msi_data, - handle_simple_irq, NULL, NULL); + current_entry = &ctrl->list; return 0; } static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) + unsigned int virq, + unsigned int nr_irqs) { struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d); + struct ls_scfg_msi_ctrl *ctrl = irq_data_get_irq_chip_data(d); int pos; - pos = d->hwirq; - if (pos < 0 || pos >= MSI_MAX_IRQS) { - pr_err("failed to teardown msi. Invalid hwirq %d\n", pos); + pos = d->hwirq - ctrl->irq_base; + + if (pos < 0 || pos >= ctrl->msi_data->cfg->msir_irqs) { + pr_err("Failed to teardown msi. Invalid hwirq %d\n", pos); return; } - spin_lock(&msi_data->lock); - __clear_bit(pos, msi_data->used); - spin_unlock(&msi_data->lock); + spin_lock(&ctrl->lock); + bitmap_release_region(ctrl->bm, pos, order_base_2(nr_irqs)); + spin_unlock(&ctrl->lock); } static const struct irq_domain_ops ls_scfg_msi_domain_ops = { @@ -121,29 +237,198 @@ static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain, .free = ls_scfg_msi_domain_irq_free, }; -static void ls_scfg_msi_irq_handler(struct irq_desc *desc) +static irqreturn_t ls_scfg_msi_irqs32_handler(int irq, void *arg) { - struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc); + struct ls_scfg_msir *msir = arg; + struct ls_scfg_msi_ctrl *ctrl = msir->ctrl; + struct ls_scfg_msi *msi_data = ctrl->msi_data; unsigned long val; - int pos, virq; + int pos = 0, hwirq, virq; + irqreturn_t ret = IRQ_NONE; - chained_irq_enter(irq_desc_get_chip(desc), desc); + val = ioread32be(msir->addr); - val = ioread32be(msi_data->regs + MSIR); - for_each_set_bit(pos, &val, MSI_MAX_IRQS) { - virq = irq_find_mapping(msi_data->parent, (31 - pos)); - if (virq) + for_each_set_bit(pos, &val, IRQS_32_PER_MSIR) { + hwirq = (IRQS_32_PER_MSIR - 1 - pos) + ctrl->irq_base; + virq = irq_find_mapping(msi_data->parent, hwirq); + if (virq) { generic_handle_irq(virq); + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static irqreturn_t ls_scfg_msi_irqs8_handler(int irq, void *arg) +{ + struct ls_scfg_msir *msir = arg; + struct ls_scfg_msi_ctrl *ctrl = msir->ctrl; + struct ls_scfg_msi *msi_data = ctrl->msi_data; + unsigned long val; + int pos = 0, hwirq, virq; + irqreturn_t ret = IRQ_NONE; + + val = ioread32be(msir->addr); + val = (val << (msir->index * 8)) & 0xff000000; + + for_each_set_bit(pos, &val, IRQS_32_PER_MSIR) { + hwirq = (IRQS_32_PER_MSIR - 1 - pos) + ctrl->irq_base; + virq = irq_find_mapping(msi_data->parent, hwirq); + if (virq) { + generic_handle_irq(virq); + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static void ls_scfg_msi_cascade(struct irq_desc *desc) +{ + struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + ls_scfg_msi_irq_handler(desc->irq_data.irq, msir); + chained_irq_exit(chip, desc); +} + +static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi_ctrl *ctrl, + struct device_node *node, + int index) +{ + struct ls_scfg_msir *msir = &ctrl->msir[index]; + int ret; + + msir->virq = of_irq_get(node, index); + if (msir->virq <= 0) + return -ENODEV; + + msir->index = index; + msir->ctrl = ctrl; + msir->addr = ctrl->regs + ctrl->msi_data->cfg->msir_base + + MSIR_OFFSET(msir->index); + + if (ctrl->flag == MSI_GROUP_AFFINITY_FLAG) { + ret = request_irq(msir->virq, ls_scfg_msi_irq_handler, + IRQF_NO_THREAD, "MSI-GROUP", msir); + if (ret) { + pr_err("failed to request irq %d\n", msir->virq); + msir->virq = 0; + return -ENODEV; + } + } else { + irq_set_chained_handler(msir->virq, ls_scfg_msi_cascade); + irq_set_handler_data(msir->virq, msir); + irq_set_affinity(msir->virq, get_cpu_mask(index)); + } + + return 0; +} + +static void ls_scfg_msi_ctrl_remove(struct ls_scfg_msi_ctrl *ctrl) +{ + struct ls_scfg_msir *msir; + int i; + + if (!ctrl) + return; + + if (ctrl->msir) { + for (i = 0; i < ctrl->msi_data->cpu_num; i++) { + msir = &ctrl->msir[i]; + + if (msir->virq <= 0) + continue; + + if (ctrl->flag == MSI_GROUP_AFFINITY_FLAG) + free_irq(msir->virq, msir); + else + irq_set_chained_handler_and_data(msir->virq, + NULL, NULL); + } + + kfree(ctrl->msir); } - chained_irq_exit(irq_desc_get_chip(desc), desc); + if (ctrl->regs) + iounmap(ctrl->regs); + + kfree(ctrl->bm); + kfree(ctrl); +} + +static int ls_scfg_msi_ctrl_probe(struct device_node *node, + struct ls_scfg_msi *msi_data) +{ + struct ls_scfg_msi_ctrl *ctrl; + struct resource res; + int err, irqs, i; + + err = of_address_to_resource(node, 0, &res); + if (err) { + pr_warn("%s: no regs\n", node->full_name); + return -ENXIO; + } + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->msi_data = msi_data; + ctrl->msiir_addr = res.start; + spin_lock_init(&ctrl->lock); + + ctrl->regs = ioremap(res.start, resource_size(&res)); + if (!ctrl->regs) { + pr_err("%s: unable to map registers\n", node->full_name); + err = -ENOMEM; + goto _err; + } + + ctrl->msir = kcalloc(msi_data->cpu_num, sizeof(struct ls_scfg_msir), + GFP_KERNEL); + if (!ctrl->msir) { + err = -ENOMEM; + goto _err; + } + + ctrl->bm = kcalloc(BITS_TO_LONGS(msi_data->cfg->msir_irqs), + sizeof(long), GFP_KERNEL); + if (!ctrl->bm) { + err = -ENOMEM; + goto _err; + } + + ctrl->irq_base = msi_data->cfg->msir_irqs * ctrl_num; + ctrl_num++; + + irqs = of_irq_count(node); + if (irqs >= msi_data->cpu_num) + ctrl->flag = MSI_AFFINITY_FLAG; + else + ctrl->flag = MSI_GROUP_AFFINITY_FLAG; + + for (i = 0; i < msi_data->cpu_num; i++) + ls_scfg_msi_setup_hwirq(ctrl, node, i); + + list_add_tail(&ctrl->list, &msi_data->ctrl_list); + + return 0; + +_err: + ls_scfg_msi_ctrl_remove(ctrl); + pr_err("MSI: failed probing %s (%d)\n", node->full_name, err); + return err; } static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) { /* Initialize MSI domain parent */ msi_data->parent = irq_domain_add_linear(NULL, - MSI_MAX_IRQS, + msi_data->cfg->msir_irqs * + ctrl_num, &ls_scfg_msi_domain_ops, msi_data); if (!msi_data->parent) { @@ -167,51 +452,57 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) static int ls_scfg_msi_probe(struct platform_device *pdev) { struct ls_scfg_msi *msi_data; - struct resource *res; - int ret; + const struct soc_device_attribute *match; + struct device_node *child; msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); if (!msi_data) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - msi_data->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(msi_data->regs)) { - dev_err(&pdev->dev, "failed to initialize 'regs'\n"); - return PTR_ERR(msi_data->regs); - } - msi_data->msiir_addr = res->start; - - msi_data->irq = platform_get_irq(pdev, 0); - if (msi_data->irq <= 0) { - dev_err(&pdev->dev, "failed to get MSI irq\n"); - return -ENODEV; - } + INIT_LIST_HEAD(&msi_data->ctrl_list); msi_data->pdev = pdev; - spin_lock_init(&msi_data->lock); + msi_data->cpu_num = num_possible_cpus(); + + match = soc_device_match(soc_msi_matches); + if (match) + msi_data->cfg = match->data; + else + msi_data->cfg = &ls1046_msi_cfg; + + if (msi_data->cfg->msir_irqs == IRQS_8_PER_MSIR) + ls_scfg_msi_irq_handler = ls_scfg_msi_irqs8_handler; + else + ls_scfg_msi_irq_handler = ls_scfg_msi_irqs32_handler; - ret = ls_scfg_msi_domains_init(msi_data); - if (ret) - return ret; + for_each_child_of_node(msi_data->pdev->dev.of_node, child) + ls_scfg_msi_ctrl_probe(child, msi_data); - irq_set_chained_handler_and_data(msi_data->irq, - ls_scfg_msi_irq_handler, - msi_data); + ls_scfg_msi_domains_init(msi_data); platform_set_drvdata(pdev, msi_data); + dev_info(&pdev->dev, "irqs:%dx%d ibs_shift:%d msir_base:0x%x\n", + msi_data->cfg->msir_irqs, ctrl_num, + msi_data->cfg->ibs_shift, msi_data->cfg->msir_base); + return 0; } static int ls_scfg_msi_remove(struct platform_device *pdev) { struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); + struct ls_scfg_msi_ctrl *ctrl, *temp; - irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL); + list_for_each_entry_safe(ctrl, temp, &msi_data->ctrl_list, list) { + list_move_tail(&ctrl->list, &msi_data->ctrl_list); + ls_scfg_msi_ctrl_remove(ctrl); + } - irq_domain_remove(msi_data->msi_domain); - irq_domain_remove(msi_data->parent); + if (msi_data->msi_domain) + irq_domain_remove(msi_data->msi_domain); + if (msi_data->parent) + irq_domain_remove(msi_data->parent); platform_set_drvdata(pdev, NULL); @@ -219,8 +510,7 @@ static int ls_scfg_msi_remove(struct platform_device *pdev) } static const struct of_device_id ls_scfg_msi_id[] = { - { .compatible = "fsl,1s1021a-msi", }, - { .compatible = "fsl,1s1043a-msi", }, + { .compatible = "fsl,ls-scfg-msi" }, {}, }; -- 1.9.1