From patchwork Thu May 25 11:37:23 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriele Paoloni X-Patchwork-Id: 100502 Delivered-To: patch@linaro.org Received: by 10.140.96.100 with SMTP id j91csp719872qge; Thu, 25 May 2017 04:41:15 -0700 (PDT) X-Received: by 10.98.4.3 with SMTP id 3mr1166000pfe.15.1495712474953; Thu, 25 May 2017 04:41:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1495712474; cv=none; d=google.com; s=arc-20160816; b=OFP6LFrT4WlaYPKW0DxA3p1KsasCdoPIiwns63JTqOi6/Bwnbt1cYbQ+TvqT5NIKHq MQ158dhZrv9jGSAroclJS+0RfquBBroN2VgaOiFbWfgoGZTp8W/7Lp3Sl96UJ3uuehHD VbKMso8BRjjtcHrEUXpuGPACYzhebhHWtQViMUtSCFSQHb/EFGr+tEFiH+yc3d30mLxN inV8zrfm/ZB6LzT+D0WycIuzpJJOJK1tGMWocnynlfk5ivkatq+r1Qde0VpZojqLH1Yf 5q/DlJwoB2eME9xq6Hl+se5bvQHvNPoPXNl1f4q6j7LZTqcz8NWRUf1aeQaawRjED7y6 GoVA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=Bkut4sR9yEOtHxA7xXholjPepfrmJ/TCTIOW7SAGNSI=; b=BH75KN3sSm786QcZXEGMokbE7Nlua0I5qrIE2LS+OFgLkyeBo+OfqmRVhmLzMr7XDf 2AFmVMQRHc77keyvHJJmIdhR0YJj4xWm7Il+XzaYvPMdsAu6/GjZnh41jWtUEcupSce4 LQE+vstGncHErxalAb7C/G5nc9OZCtcaiIMeB6RuaU5xNNbs4fqk29J4UqjXkwChlpOL I++u2K4r/q6T3oNuz8M+I3qe4Z8T3Q4RUVSiVXuqrNE4IRfBNR0PvUVRh5//79BP4shP 6SUNC4KgLY7ZSXy3LLHYgz14/REz65SrV+NNnJlWqbc94CjKDN9+TZ2WRdAjoEEVHP3E QaXg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v12si27685787pfj.72.2017.05.25.04.41.14; Thu, 25 May 2017 04:41:14 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1765443AbdEYLlG (ORCPT + 25 others); Thu, 25 May 2017 07:41:06 -0400 Received: from szxga02-in.huawei.com ([45.249.212.188]:6825 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1762170AbdEYLiw (ORCPT ); Thu, 25 May 2017 07:38:52 -0400 Received: from 172.30.72.54 (EHLO DGGEML401-HUB.china.huawei.com) ([172.30.72.54]) by dggrg02-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id AOF02094; Thu, 25 May 2017 19:38:47 +0800 (CST) Received: from G00308965-DELL1.china.huawei.com (10.203.181.156) by DGGEML401-HUB.china.huawei.com (10.3.17.32) with Microsoft SMTP Server id 14.3.301.0; Thu, 25 May 2017 19:38:37 +0800 From: Gabriele Paoloni To: , , , , , , , , CC: , , , , , , , , , , , , "zhichang.yuan" Subject: [PATCH v9 2/7] PCI: Apply the new generic I/O management on PCI IO hosts Date: Thu, 25 May 2017 12:37:23 +0100 Message-ID: <1495712248-5232-3-git-send-email-gabriele.paoloni@huawei.com> X-Mailer: git-send-email 2.7.1.windows.1 In-Reply-To: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> References: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.203.181.156] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020202.5926C248.016C, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: c8e0288b9cf60534d7944160e6c0a26d Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "zhichang.yuan" After introducing the new generic I/O space management(LOGIC_IO), the original PCI MMIO relevant helpers need to be updated based on the new interfaces defined in LOGIC_IO. This patch adapts the corresponding code to match the changes introduced by LOGIC_IO. Signed-off-by: zhichang.yuan Signed-off-by: Gabriele Paoloni Signed-off-by: Arnd Bergmann #earlier draft --- drivers/acpi/pci_root.c | 8 ++-- drivers/of/address.c | 5 ++- drivers/pci/pci.c | 101 ++++++++++------------------------------------- include/asm-generic/io.h | 2 +- include/linux/pci.h | 3 +- 5 files changed, 32 insertions(+), 87 deletions(-) -- 2.7.4 Acked-by: Bjorn Helgaas diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 919be0a..4d8cc24 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -730,7 +730,8 @@ static void acpi_pci_root_validate_resources(struct device *dev, } } -static void acpi_pci_root_remap_iospace(struct resource_entry *entry) +static void acpi_pci_root_remap_iospace(struct fwnode_handle *fwnode, + struct resource_entry *entry) { #ifdef PCI_IOBASE struct resource *res = entry->res; @@ -739,7 +740,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry) resource_size_t length = resource_size(res); unsigned long port; - if (pci_register_io_range(cpu_addr, length)) + if (pci_register_io_range(fwnode, cpu_addr, length)) goto err; port = pci_address_to_pio(cpu_addr); @@ -781,7 +782,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else { resource_list_for_each_entry_safe(entry, tmp, list) { if (entry->res->flags & IORESOURCE_IO) - acpi_pci_root_remap_iospace(entry); + acpi_pci_root_remap_iospace(&device->fwnode, + entry); if (entry->res->flags & IORESOURCE_DISABLED) resource_list_destroy_entry(entry); diff --git a/drivers/of/address.c b/drivers/of/address.c index 72914cd..34a55e8 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -2,8 +2,10 @@ #define pr_fmt(fmt) "OF: " fmt #include +#include #include #include +#include #include #include #include @@ -323,7 +325,8 @@ int of_pci_range_to_resource(struct of_pci_range *range, if (res->flags & IORESOURCE_IO) { unsigned long port; - err = pci_register_io_range(range->cpu_addr, range->size); + err = pci_register_io_range(&np->fwnode, range->cpu_addr, + range->size); if (err) goto invalid_range; port = pci_address_to_pio(range->cpu_addr); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b01bd5b..c9fe12b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -3241,68 +3242,34 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) } EXPORT_SYMBOL(pci_request_regions_exclusive); -#ifdef PCI_IOBASE -struct io_range { - struct list_head list; - phys_addr_t start; - resource_size_t size; -}; - -static LIST_HEAD(io_range_list); -static DEFINE_SPINLOCK(io_range_lock); -#endif - /* * Record the PCI IO range (expressed as CPU physical address + size). * Return a negative value if an error has occured, zero otherwise */ -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size) +int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr, + resource_size_t size) { - int err = 0; - + int ret = 0; #ifdef PCI_IOBASE - struct io_range *range; - resource_size_t allocated_size = 0; - - /* check if the range hasn't been previously recorded */ - spin_lock(&io_range_lock); - list_for_each_entry(range, &io_range_list, list) { - if (addr >= range->start && addr + size <= range->start + size) { - /* range already registered, bail out */ - goto end_register; - } - allocated_size += range->size; - } - - /* range not registed yet, check for available space */ - if (allocated_size + size - 1 > IO_SPACE_LIMIT) { - /* if it's too big check if 64K space can be reserved */ - if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) { - err = -E2BIG; - goto end_register; - } - - size = SZ_64K; - pr_warn("Requested IO range too big, new size set to 64K\n"); - } + struct logic_pio_hwaddr *range; - /* add the range to the list */ - range = kzalloc(sizeof(*range), GFP_ATOMIC); - if (!range) { - err = -ENOMEM; - goto end_register; - } + if (!size || addr + size < addr) + return -EINVAL; - range->start = addr; + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + range->fwnode = fwnode; range->size = size; + range->hw_start = addr; + range->flags = PIO_CPU_MMIO; - list_add_tail(&range->list, &io_range_list); - -end_register: - spin_unlock(&io_range_lock); + ret = logic_pio_register_range(range); + if (ret) + kfree(range); #endif - return err; + return ret; } phys_addr_t pci_pio_to_address(unsigned long pio) @@ -3310,21 +3277,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio) phys_addr_t address = (phys_addr_t)OF_BAD_ADDR; #ifdef PCI_IOBASE - struct io_range *range; - resource_size_t allocated_size = 0; - - if (pio > IO_SPACE_LIMIT) + if (pio >= MMIO_UPPER_LIMIT) return address; - spin_lock(&io_range_lock); - list_for_each_entry(range, &io_range_list, list) { - if (pio >= allocated_size && pio < allocated_size + range->size) { - address = range->start + pio - allocated_size; - break; - } - allocated_size += range->size; - } - spin_unlock(&io_range_lock); + address = logic_pio_to_hwaddr(pio); #endif return address; @@ -3333,25 +3289,8 @@ phys_addr_t pci_pio_to_address(unsigned long pio) unsigned long __weak pci_address_to_pio(phys_addr_t address) { #ifdef PCI_IOBASE - struct io_range *res; - resource_size_t offset = 0; - unsigned long addr = -1; - - spin_lock(&io_range_lock); - list_for_each_entry(res, &io_range_list, list) { - if (address >= res->start && address < res->start + res->size) { - addr = address - res->start + offset; - break; - } - offset += res->size; - } - spin_unlock(&io_range_lock); - - return addr; + return logic_pio_trans_cpuaddr(address); #else - if (address > IO_SPACE_LIMIT) - return (unsigned long)-1; - return (unsigned long) address; #endif } diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index f7fbec3..41ecc09 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -949,7 +949,7 @@ static inline void iounmap(void __iomem *addr) #define ioport_map ioport_map static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) { - return PCI_IOBASE + (port & IO_SPACE_LIMIT); + return PCI_IOBASE + (port & MMIO_UPPER_LIMIT); } #endif diff --git a/include/linux/pci.h b/include/linux/pci.h index 33c2b0b..e420d03 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1188,7 +1188,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus, void *alignf_data); -int pci_register_io_range(phys_addr_t addr, resource_size_t size); +int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr, + resource_size_t size); unsigned long pci_address_to_pio(phys_addr_t addr); phys_addr_t pci_pio_to_address(unsigned long pio); int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); From patchwork Thu May 25 11:37:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriele Paoloni X-Patchwork-Id: 100500 Delivered-To: patch@linaro.org Received: by 10.140.96.100 with SMTP id j91csp719665qge; Thu, 25 May 2017 04:40:35 -0700 (PDT) X-Received: by 10.84.231.199 with SMTP id g7mr49927820pln.70.1495712435450; Thu, 25 May 2017 04:40:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1495712435; cv=none; d=google.com; s=arc-20160816; b=1BpABIMtiBKJRtAuKHTUtX5ZfGte2S0ctijvJEo4HcI9LBV911S7Cd5Jzay/rB+EtR hmvS8yRh2MunPH2iebrCl1sfk30NbmY00k90yK5KDIH8OS6bVURymYXxUo3jOoOwVHNb mhlDsWyaOCl30tY+jI2eFIsfg6GkacPPnzIQtjDFNun8Wfk/0c2x3vaFCoM79jAW81WW 6ZWCgyY6Q88FewFRX8sLu+F5aLEx7TkON2DMk0WuDQeQOcUymmHeiyscm9XvknkoEzGX XFe7Hzordddpg91+EmfnmKJCLXRxs4QAIAs6DSsJNhzhLG4bF6oRam+Kv0Z3KXcjgIrS S1Fg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=dTPb+mDUaw07NLxMhdttJ9thi999dAeBSPoB/ac7VYk=; b=H6nTDSWmw7y7EU6EfaOEXK2udaVnwUoEFJJZAOzdTQLOeXC0TQc1L2loC6iEI4SUuF oU6pkl+dLuyaYmVlR4lfW6Xz3dYUyHI3gurHCeCzhHKSKkDh2s+xtIKeR+kUVCe0ZHkn yHSRYq6TFeCM1jopRs3UYyHUQ0g4rSEiqSKbNtPejmuNdpyIayUm2uSYSLsl6KnTdBw9 QayZaLpmPLJbCYeZnWrvvkq7u8G7E7GIPbsOgQ9QSCoseOA609IYfZezETTqnIGjhJUS NUsN6OUw4N9OSRfuDmac9Hxhhm6a+xLDpboMlww/Zj6cLxjSqxtChn0hiScd+37v9myM miOQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i35si19509770plg.96.2017.05.25.04.40.35; Thu, 25 May 2017 04:40:35 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1764163AbdEYLkV (ORCPT + 25 others); Thu, 25 May 2017 07:40:21 -0400 Received: from szxga02-in.huawei.com ([45.249.212.188]:6826 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751644AbdEYLjJ (ORCPT ); Thu, 25 May 2017 07:39:09 -0400 Received: from 172.30.72.55 (EHLO DGGEML401-HUB.china.huawei.com) ([172.30.72.55]) by dggrg02-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id AOF02102; Thu, 25 May 2017 19:38:51 +0800 (CST) Received: from G00308965-DELL1.china.huawei.com (10.203.181.156) by DGGEML401-HUB.china.huawei.com (10.3.17.32) with Microsoft SMTP Server id 14.3.301.0; Thu, 25 May 2017 19:38:44 +0800 From: Gabriele Paoloni To: , , , , , , , , CC: , , , , , , , , , , , , "zhichang.yuan" Subject: [PATCH v9 3/7] OF: Add missing I/O range exception for indirect-IO devices Date: Thu, 25 May 2017 12:37:24 +0100 Message-ID: <1495712248-5232-4-git-send-email-gabriele.paoloni@huawei.com> X-Mailer: git-send-email 2.7.1.windows.1 In-Reply-To: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> References: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.203.181.156] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020201.5926C24B.01C5, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: fc9478f547b05418191d424e89600ee2 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "zhichang.yuan" There are some special ISA/LPC devices that work on a specific I/O range where it is not correct to specify a 'ranges' property in DTS parent node as cpu addresses translated from DTS node are only for memory space on some architectures, such as Arm64. Without the parent 'ranges' property, current of_translate_address() return an error. Here we add special handlings for this case. During the OF address translation, some checkings will be perfromed to identify whether the device node is registered as indirect-IO. If yes, the I/O translation will be done in a different way from that one of PCI MMIO. In this way, the I/O 'reg' property of the special ISA/LPC devices will be parsed correctly. Signed-off-by: zhichang.yuan Signed-off-by: Gabriele Paoloni Signed-off-by: Arnd Bergmann #earlier draft Acked-by: Rob Herring --- drivers/of/address.c | 90 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 16 deletions(-) -- 2.7.4 diff --git a/drivers/of/address.c b/drivers/of/address.c index 34a55e8..c51de70 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -552,9 +552,14 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * that translation is impossible (that is we are not dealing with a value * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things + * + * Whenever the translation fails, the *host pointer will be set to the + * device that had registered logical PIO mapping, and the return code is + * relative to that node. */ static u64 __of_translate_address(struct device_node *dev, - const __be32 *in_addr, const char *rprop) + const __be32 *in_addr, const char *rprop, + struct device_node **host) { struct device_node *parent = NULL; struct of_bus *bus, *pbus; @@ -567,6 +572,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Increase refcount at current level */ of_node_get(dev); + *host = NULL; /* Get parent & match bus type */ parent = of_get_parent(dev); if (parent == NULL) @@ -587,6 +593,8 @@ static u64 __of_translate_address(struct device_node *dev, /* Translate */ for (;;) { + struct logic_pio_hwaddr *iorange; + /* Switch to parent bus */ of_node_put(dev); dev = parent; @@ -599,6 +607,19 @@ static u64 __of_translate_address(struct device_node *dev, break; } + /* + * For indirectIO device which has no ranges property, get + * the address from reg directly. + */ + iorange = find_io_range_by_fwnode(&dev->fwnode); + if (iorange && (iorange->flags != PIO_CPU_MMIO)) { + result = of_read_number(addr + 1, na - 1); + pr_debug("indirectIO matched(%s) 0x%llx\n", + of_node_full_name(dev), result); + *host = of_node_get(dev); + break; + } + /* Get new parent bus and counts */ pbus = of_match_bus(parent); pbus->count_cells(dev, &pna, &pns); @@ -631,13 +652,32 @@ static u64 __of_translate_address(struct device_node *dev, u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) { - return __of_translate_address(dev, in_addr, "ranges"); + struct device_node *host; + u64 ret; + + ret = __of_translate_address(dev, in_addr, "ranges", &host); + if (host) { + of_node_put(host); + return OF_BAD_ADDR; + } + + return ret; } EXPORT_SYMBOL(of_translate_address); u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) { - return __of_translate_address(dev, in_addr, "dma-ranges"); + struct device_node *host; + u64 ret; + + ret = __of_translate_address(dev, in_addr, "dma-ranges", &host); + + if (host) { + of_node_put(host); + return OF_BAD_ADDR; + } + + return ret; } EXPORT_SYMBOL(of_translate_dma_address); @@ -679,29 +719,47 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, } EXPORT_SYMBOL(of_get_address); +static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr) +{ + u64 taddr; + unsigned long port; + struct device_node *host; + + taddr = __of_translate_address(dev, in_addr, "ranges", &host); + if (host) { + /* host specific port access */ + port = logic_pio_trans_hwaddr(&host->fwnode, taddr); + of_node_put(host); + } else { + /* memory mapped I/O range */ + port = pci_address_to_pio(taddr); + } + + if (port == (unsigned long)-1) + return OF_BAD_ADDR; + + return port; +} + static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, const char *name, struct resource *r) { u64 taddr; - if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + if (flags & IORESOURCE_MEM) + taddr = of_translate_address(dev, addrp); + else if (flags & IORESOURCE_IO) + taddr = of_translate_ioport(dev, addrp); + else return -EINVAL; - taddr = of_translate_address(dev, addrp); + if (taddr == OF_BAD_ADDR) return -EINVAL; memset(r, 0, sizeof(struct resource)); - if (flags & IORESOURCE_IO) { - unsigned long port; - port = pci_address_to_pio(taddr); - if (port == (unsigned long)-1) - return -EINVAL; - r->start = port; - r->end = port + size - 1; - } else { - r->start = taddr; - r->end = taddr + size - 1; - } + + r->start = taddr; + r->end = taddr + size - 1; r->flags = flags; r->name = name ? name : dev->full_name; From patchwork Thu May 25 11:37:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriele Paoloni X-Patchwork-Id: 100497 Delivered-To: patch@linaro.org Received: by 10.140.96.100 with SMTP id j91csp719239qge; Thu, 25 May 2017 04:39:14 -0700 (PDT) X-Received: by 10.98.211.202 with SMTP id z71mr44121759pfk.46.1495712354585; Thu, 25 May 2017 04:39:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1495712354; cv=none; d=google.com; s=arc-20160816; b=L/QVm2zVHeaJpyK0T2q06j4xPBRFWBJkrEwq1y1XRGyYi9kuzo8Yp97W4U2xHEv7dO K5DIpXHo+5rYZLwIAn9lC8ab91kuWpRmODJ/RDGnKnWNYUB/5GIUXe6Md6+fJ3e89qCr icB2e5U4hjZgBcOoYNNk4aIKFbM4TX+b75URuCsYllZe8UUMpb3HRcQQNiROFhQJTM0C ClMAVs8Zux4CJIr1+SxCYrtvUVAJjtuQiJxoi22o6xwNWxt2KyZLk6cuQrtUtuh3RDYh ys5sM2yZYuAtkNiAtpYmuLPKtGqoWt11XJnzYhCIVoKpSctymrZm8tvg11a4ma5jhfAU zr0w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=/zXAByRfGW43XxDHtkMel4C1hRcfSxRxzZlQCunUF5U=; b=tyOJQ2eBYVUby+O0SgADUHKEmhQAspuVapIruHluEEoM59Xb/hZRI5EluI8pQiBs0E 2SmfjNAPdaXKStnVBDd883zjp97SGjmseI4EusYkDcLrdPeGl9H7jLF3iqHX0mcmzo7U upDH/xwiwN6g39c9j/amfFnZ0zKH/TQ9WegqxzpTtWU+5k8uwK2ponFZqWLzz4DAne+g wwPpww6bmY6M3WOrtw09GvUhLW9M0AM7Bks4DNkTZHjiyiiWIGw8EuYFp67NvfJvuAOO 6V/IHYxaGsD0o5+iPS0Tor6VIyyDz0Dit5+gNTIlqICJ9bYzju0rjCZEE5bqo816mRgm bvnQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c19si27611951pgk.90.2017.05.25.04.39.14; Thu, 25 May 2017 04:39:14 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1765308AbdEYLjL (ORCPT + 25 others); Thu, 25 May 2017 07:39:11 -0400 Received: from szxga02-in.huawei.com ([45.249.212.188]:6827 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752591AbdEYLjF (ORCPT ); Thu, 25 May 2017 07:39:05 -0400 Received: from 172.30.72.56 (EHLO DGGEML401-HUB.china.huawei.com) ([172.30.72.56]) by dggrg02-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id AOF02120; Thu, 25 May 2017 19:39:01 +0800 (CST) Received: from G00308965-DELL1.china.huawei.com (10.203.181.156) by DGGEML401-HUB.china.huawei.com (10.3.17.32) with Microsoft SMTP Server id 14.3.301.0; Thu, 25 May 2017 19:38:50 +0800 From: Gabriele Paoloni To: , , , , , , , , CC: , , , , , , , , , , , , "zhichang.yuan" Subject: [PATCH v9 4/7] LPC: Support the device-tree LPC host on Hip06/Hip07 Date: Thu, 25 May 2017 12:37:25 +0100 Message-ID: <1495712248-5232-5-git-send-email-gabriele.paoloni@huawei.com> X-Mailer: git-send-email 2.7.1.windows.1 In-Reply-To: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> References: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.203.181.156] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020201.5926C255.01A0, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 5e1c9398c18ce6651b55775eeb40fd98 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "zhichang.yuan" The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in I/O port addresses. This patch implements the LPC host controller driver which perform the I/O operations on the underlying hardware. We don't want to touch those existing peripherals' driver, such as ipmi-bt. So this driver applies the indirect-IO introduced in the previous patch after registering an indirect-IO node to the indirect-IO devices list which will be searched in the I/O accessors to retrieve the host-local I/O port. Signed-off-by: Zou Rongrong Signed-off-by: zhichang.yuan Signed-off-by: Gabriele Paoloni Acked-by: Rob Herring #dts part --- .../arm/hisilicon/hisilicon-low-pin-count.txt | 33 ++ drivers/bus/Kconfig | 9 + drivers/bus/Makefile | 1 + drivers/bus/hisi_lpc.c | 544 +++++++++++++++++++++ 4 files changed, 587 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt create mode 100644 drivers/bus/hisi_lpc.c -- 2.7.4 diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt new file mode 100644 index 0000000..213181f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt @@ -0,0 +1,33 @@ +Hisilicon Hip06 low-pin-count device + Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which + provides I/O access to some legacy ISA devices. + Hip06 is based on arm64 architecture where there is no I/O space. So, the + I/O ports here are not cpu addresses, and there is no 'ranges' property in + LPC device node. + +Required properties: +- compatible: value should be as follows: + (a) "hisilicon,hip06-lpc" + (b) "hisilicon,hip07-lpc" +- #address-cells: must be 2 which stick to the ISA/EISA binding doc. +- #size-cells: must be 1 which stick to the ISA/EISA binding doc. +- reg: base memory range where the LPC register set is mapped. + +Note: + The node name before '@' must be "isa" to represent the binding stick to the + ISA/EISA binding specification. + +Example: + +isa@a01b0000 { + compatible = "hisilicon,hip06-lpc"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x0 0xa01b0000 0x0 0x1000>; + + ipmi0: bt@e4 { + compatible = "ipmi-bt"; + device_type = "ipmi"; + reg = <0x01 0xe4 0x04>; + }; +}; diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 0a52da4..4b548dd 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -64,6 +64,15 @@ config BRCMSTB_GISB_ARB arbiter. This driver provides timeout and target abort error handling and internal bus master decoding. +config HISILICON_LPC + bool "Support for ISA I/O space on Hisilicon Hip0X" + depends on (ARM64 && ARCH_HISI) || COMPILE_TEST + select LOGIC_PIO + select INDIRECT_PIO + help + Driver needed for some legacy ISA devices attached to Low-Pin-Count + on Hisilicon Hip0X SoC. + config IMX_WEIM bool "Freescale EIM DRIVER" depends on ARCH_MXC diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index cc6364b..28e3862 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o obj-$(CONFIG_ARM_CCN) += arm-ccn.o obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o +obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c new file mode 100644 index 0000000..4f3bf76 --- /dev/null +++ b/drivers/bus/hisi_lpc.c @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved. + * Author: Zhichang Yuan + * Author: Zou Rongrong + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LPC_MIN_BUS_RANGE 0x0 + +/* + * The default maximal IO size for Hip06/Hip07 LPC bus. + * Defining the I/O range size as 0x400 here should be sufficient for + * all peripherals under the bus. + */ +#define LPC_BUS_IO_SIZE 0x400 + +/* + * Setting this bit means each IO operation will target to a + * different port address: + * 0 means repeatedly IO operations will stick on the same port, + * such as BT; + */ +#define FG_INCRADDR_LPC 0x02 + +struct lpc_cycle_para { + unsigned int opflags; + unsigned int csize; /* the data length of each operation */ +}; + +struct hisilpc_dev { + spinlock_t cycle_lock; + void __iomem *membase; + struct logic_pio_hwaddr *io_host; +}; + +/* The maximum continuous cycles per burst */ +#define LPC_MAX_BURST 16 +/* The IO cycle counts supported is four per operation at maximum */ +#define LPC_MAX_DULEN 4 +#if LPC_MAX_DULEN > LPC_MAX_BURST +#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!" +#endif + +#if LPC_MAX_BURST % LPC_MAX_DULEN +#error "LPC.. LPC_MAX_BURST must be multiple of LPC_MAX_DULEN!" +#endif + +#define LPC_REG_START 0x00 /* start a new LPC cycle */ +#define LPC_REG_OP_STATUS 0x04 /* the current LPC status */ +#define LPC_REG_IRQ_ST 0x08 /* interrupt enable&status */ +#define LPC_REG_OP_LEN 0x10 /* how many LPC cycles each start */ +#define LPC_REG_CMD 0x14 /* command for the required LPC cycle */ +#define LPC_REG_ADDR 0x20 /* LPC target address */ +#define LPC_REG_WDATA 0x24 /* data to be written */ +#define LPC_REG_RDATA 0x28 /* data coming from peer */ + + +/* The command register fields */ +#define LPC_CMD_SAMEADDR 0x08 +#define LPC_CMD_TYPE_IO 0x00 +#define LPC_CMD_WRITE 0x01 +#define LPC_CMD_READ 0x00 +/* the bit attribute is W1C. 1 represents OK. */ +#define LPC_STAT_BYIRQ 0x02 + +#define LPC_STATUS_IDLE 0x01 +#define LPC_OP_FINISHED 0x02 + +#define START_WORK 0x01 + +/* + * The minimal nanosecond interval for each query on LPC cycle status. + */ +#define LPC_NSEC_PERWAIT 100 +/* + * The maximum waiting time is about 128us. + * It is specific for stream I/O, such as ins. + * The fastest IO cycle time is about 390ns, but the worst case will wait + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum + * burst cycles is 16. So, the maximum waiting time is about 128us under + * worst case. + * choose 1300 as the maximum. + */ +#define LPC_MAX_WAITCNT 1300 +/* About 10us. This is specific for single IO operation, such as inb. */ +#define LPC_PEROP_WAITCNT 100 + + +static inline int wait_lpc_idle(unsigned char *mbase, + unsigned int waitcnt) { + u32 opstatus; + + while (waitcnt--) { + ndelay(LPC_NSEC_PERWAIT); + opstatus = readl(mbase + LPC_REG_OP_STATUS); + if (opstatus & LPC_STATUS_IDLE) + return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO); + } + return -ETIME; +} + +/* + * hisilpc_target_in - trigger a series of lpc cycles to read required data + * from target peripheral. + * @pdev: pointer to hisi lpc device + * @para: some parameters used to control the lpc I/O operations + * @ptaddr: the lpc I/O target port address + * @buf: where the read back data is stored + * @opcnt: how many I/O operations required in this calling + * + * Only one byte data is read each I/O operation. + * + * Returns 0 on success, non-zero on fail. + * + */ +static int +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para, + unsigned long ptaddr, unsigned char *buf, + unsigned long opcnt) +{ + unsigned long cnt_per_trans; + unsigned int cmd_word; + unsigned int waitcnt; + int ret; + + if (!buf || !opcnt || !para || !para->csize || !lpcdev) + return -EINVAL; + + cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ; + waitcnt = LPC_PEROP_WAITCNT; + if (!(para->opflags & FG_INCRADDR_LPC)) { + cmd_word |= LPC_CMD_SAMEADDR; + waitcnt = LPC_MAX_WAITCNT; + } + + ret = 0; + cnt_per_trans = (para->csize == 1) ? opcnt : para->csize; + for (; opcnt && !ret; cnt_per_trans = para->csize) { + unsigned long flags; + + /* whole operation must be atomic */ + spin_lock_irqsave(&lpcdev->cycle_lock, flags); + + writel_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN); + + writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD); + + writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR); + + writel(START_WORK, lpcdev->membase + LPC_REG_START); + + /* whether the operation is finished */ + ret = wait_lpc_idle(lpcdev->membase, waitcnt); + if (!ret) { + opcnt -= cnt_per_trans; + for (cnt_per_trans--; cnt_per_trans--; buf++) + *buf = readb_relaxed(lpcdev->membase + + LPC_REG_RDATA); + *buf = readb(lpcdev->membase + LPC_REG_RDATA); + } + + spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); + } + + return ret; +} + +/* + * hisilpc_target_out - trigger a series of lpc cycles to write required + * data to target peripheral. + * @pdev: pointer to hisi lpc device + * @para: some parameters used to control the lpc I/O operations + * @ptaddr: the lpc I/O target port address + * @buf: where the data to be written is stored + * @opcnt: how many I/O operations required + * + * Only one byte data is read each I/O operation. + * + * Returns 0 on success, non-zero on fail. + * + */ +static int +hisilpc_target_out(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para, + unsigned long ptaddr, const unsigned char *buf, + unsigned long opcnt) +{ + unsigned long cnt_per_trans; + unsigned int cmd_word; + unsigned int waitcnt; + int ret; + + if (!buf || !opcnt || !para || !lpcdev) + return -EINVAL; + + /* default is increasing address */ + cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE; + waitcnt = LPC_PEROP_WAITCNT; + if (!(para->opflags & FG_INCRADDR_LPC)) { + cmd_word |= LPC_CMD_SAMEADDR; + waitcnt = LPC_MAX_WAITCNT; + } + + ret = 0; + cnt_per_trans = (para->csize == 1) ? opcnt : para->csize; + for (; opcnt && !ret; cnt_per_trans = para->csize) { + unsigned long flags; + + spin_lock_irqsave(&lpcdev->cycle_lock, flags); + + writel_relaxed(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN); + writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD); + writel_relaxed(ptaddr, lpcdev->membase + LPC_REG_ADDR); + + opcnt -= cnt_per_trans; + for (; cnt_per_trans--; buf++) + writeb_relaxed(*buf, lpcdev->membase + LPC_REG_WDATA); + + writel(START_WORK, lpcdev->membase + LPC_REG_START); + + /* whether the operation is finished */ + ret = wait_lpc_idle(lpcdev->membase, waitcnt); + + spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); + } + + return ret; +} + +static inline unsigned long +hisi_lpc_pio_to_addr(struct hisilpc_dev *lpcdev, unsigned long pio) +{ + return pio - lpcdev->io_host->io_start + + lpcdev->io_host->hw_start; +} + + +/** + * hisilpc_comm_in - read/input the data from the I/O peripheral + * through LPC. + * @devobj: pointer to the device information relevant to LPC controller. + * @pio: the target I/O port address. + * @dlen: the data length required to read from the target I/O port. + * + * when succeed, the data read back is stored in buffer pointed by inbuf. + * For inb, return the data read from I/O or -1 when error occur. + */ +static u32 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen) +{ + int ret = 0; + u32 rd_data = 0; + unsigned long ptaddr; + unsigned char *newbuf; + struct lpc_cycle_para iopara; + struct hisilpc_dev *lpcdev = devobj; + + if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN) + return -1; + + newbuf = (unsigned char *)&rd_data; + + ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio); + + iopara.opflags = FG_INCRADDR_LPC; + iopara.csize = dlen; + + ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, newbuf, dlen); + if (ret) + return -1; + + return le32_to_cpu(rd_data); +} + +/** + * hisilpc_comm_out - output the data whose maximum length is four bytes + to the I/O peripheral through the LPC host. + * @devobj: pointer to the device information relevant to LPC controller. + * @outval: a value to be outputted from caller, maximum is four bytes. + * @pio: the target I/O port address. + * @dlen: the data length required writing to the target I/O port. + * + * This function is corresponding to out(b,w,l) only + * + */ +static void hisilpc_comm_out(void *devobj, unsigned long pio, + u32 outval, size_t dlen) +{ + unsigned long ptaddr; + struct hisilpc_dev *lpcdev = devobj; + struct lpc_cycle_para iopara; + const unsigned char *newbuf; + + if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN) + return; + + outval = cpu_to_le32(outval); + + newbuf = (const unsigned char *)&outval; + ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio); + + iopara.opflags = FG_INCRADDR_LPC; + iopara.csize = dlen; + + hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf, dlen); +} + +/* + * hisilpc_comm_ins - read/input the data in buffer to the I/O + * peripheral through LPC, it corresponds to ins(b,w,l) + * @devobj: pointer to the device information relevant to LPC controller. + * @pio: the target I/O port address. + * @inbuf: a buffer where read/input data bytes are stored. + * @dlen: the data length required writing to the target I/O port. + * @count: how many data units whose length is dlen will be read. + * + */ +static u32 +hisilpc_comm_ins(void *devobj, unsigned long pio, void *inbuf, + size_t dlen, unsigned int count) +{ + struct hisilpc_dev *lpcdev = devobj; + struct lpc_cycle_para iopara; + unsigned char *newbuf; + unsigned int loopcnt, cntleft; + unsigned long ptaddr; + + if (!lpcdev || !inbuf || !count || !dlen || dlen > LPC_MAX_DULEN || + count % dlen) + return -EINVAL; + + iopara.opflags = 0; + if (dlen > 1) + iopara.opflags |= FG_INCRADDR_LPC; + iopara.csize = dlen; + + ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio); + newbuf = (unsigned char *)inbuf; + /* + * ensure data stream whose length is multiple of dlen to be processed + * each IO input + */ + cntleft = count * dlen; + do { + int ret; + + loopcnt = (cntleft >= LPC_MAX_BURST) ? LPC_MAX_BURST : cntleft; + ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, + newbuf, loopcnt); + if (ret) + return ret; + newbuf += loopcnt; + cntleft -= loopcnt; + } while (cntleft); + + return 0; +} + +/* + * hisilpc_comm_outs - write/output the data in buffer to the I/O + * peripheral through LPC, it corresponds to outs(b,w,l) + * @devobj: pointer to the device information relevant to LPC controller. + * @pio: the target I/O port address. + * @outbuf: a buffer where write/output data bytes are stored. + * @dlen: the data length required writing to the target I/O port . + * @count: how many data units whose length is dlen will be written. + * + */ +static void +hisilpc_comm_outs(void *devobj, unsigned long pio, const void *outbuf, + size_t dlen, unsigned int count) +{ + struct hisilpc_dev *lpcdev = devobj; + struct lpc_cycle_para iopara; + const unsigned char *newbuf; + unsigned int loopcnt, cntleft; + unsigned long ptaddr; + + if (!lpcdev || !outbuf || !count || !dlen || dlen > LPC_MAX_DULEN || + count % dlen) + return; + + iopara.opflags = 0; + if (dlen > 1) + iopara.opflags |= FG_INCRADDR_LPC; + iopara.csize = dlen; + + ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio); + newbuf = (unsigned char *)outbuf; + /* + * ensure data stream whose length is multiple of dlen to be processed + * each IO input + */ + cntleft = count * dlen; + do { + loopcnt = (cntleft >= LPC_MAX_BURST) ? LPC_MAX_BURST : cntleft; + if (hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf, + loopcnt)) + break; + newbuf += loopcnt; + cntleft -= loopcnt; + } while (cntleft); +} + +static struct hostio_ops hisi_lpc_ops = { + .pfin = hisilpc_comm_in, + .pfout = hisilpc_comm_out, + .pfins = hisilpc_comm_ins, + .pfouts = hisilpc_comm_outs, +}; + +/** + * hisilpc_probe - the probe callback function for hisi lpc device, + * will finish all the initialization. + * @pdev: the platform device corresponding to hisi lpc + * + * Returns 0 on success, non-zero on fail. + * + */ +static int hisilpc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct hisilpc_dev *lpcdev; + int ret = 0; + + dev_info(dev, "probing...\n"); + + lpcdev = devm_kzalloc(dev, sizeof(struct hisilpc_dev), GFP_KERNEL); + if (!lpcdev) + return -ENOMEM; + + spin_lock_init(&lpcdev->cycle_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no MEM resource\n"); + return -ENODEV; + } + + lpcdev->membase = devm_ioremap_resource(dev, res); + if (IS_ERR(lpcdev->membase)) { + dev_err(dev, "remap failed\n"); + return PTR_ERR(lpcdev->membase); + } + + /* register the LPC host PIO resources */ + if (!has_acpi_companion(dev)) { + struct logic_pio_hwaddr *range; + + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + range->fwnode = dev->fwnode; + range->flags = PIO_INDIRECT; + range->size = LPC_BUS_IO_SIZE; + range->hw_start = LPC_MIN_BUS_RANGE; + + ret = logic_pio_register_range(range); + if (ret) { + kfree(range); + dev_err(dev, "OF: register IO range FAIL!\n"); + return -ret; + } + lpcdev->io_host = range; + } + if (!lpcdev->io_host) { + dev_err(dev, "Hisilpc IO hasn't registered!\n"); + return -EFAULT; + } + + lpcdev->io_host->devpara = lpcdev; + lpcdev->io_host->ops = &hisi_lpc_ops; + + platform_set_drvdata(pdev, lpcdev); + + /* + * It is time to start the children scannings.... + * For ACPI children, the corresponding devices had been created + * during the ACPI enumeration. + * The OF scanning must be performed after initialization of 'lpcdev' + * to avoid some children which complete the scanning trigger the + * MMIO accesses which will probably cause panic. + */ + if (!has_acpi_companion(dev)) + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + + if (!ret) { + dev_info(dev, "hslpc end probing. range[%pa - sz:%pa]\n", + &lpcdev->io_host->io_start, + &lpcdev->io_host->size); + } else { + /* + * When LPC probing is not completely successful, set 'devpara' + * as NULL. This will make all the LPC I/O return failure + * directly without any hardware operations. It will prevent + * some peripherals which had not finished the initialization to + * manipulate I/O for safety. + */ + lpcdev->io_host->devpara = NULL; + dev_info(dev, "OF: scan hisilpc children got failed(%d)\n", + ret); + } + + return ret; +} + +static const struct of_device_id hisilpc_of_match[] = { + { .compatible = "hisilicon,hip06-lpc", }, + { .compatible = "hisilicon,hip07-lpc", }, + {}, +}; + +static struct platform_driver hisilpc_driver = { + .driver = { + .name = "hisi_lpc", + .of_match_table = hisilpc_of_match, + }, + .probe = hisilpc_probe, +}; + +builtin_platform_driver(hisilpc_driver); From patchwork Thu May 25 11:37:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriele Paoloni X-Patchwork-Id: 100498 Delivered-To: patch@linaro.org Received: by 10.140.96.100 with SMTP id j91csp719374qge; Thu, 25 May 2017 04:39:38 -0700 (PDT) X-Received: by 10.99.110.202 with SMTP id j193mr44732129pgc.141.1495712378789; Thu, 25 May 2017 04:39:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1495712378; cv=none; d=google.com; s=arc-20160816; b=Zf/UvqZj5DkMHztcwgkwjutqbO5jv+5soY9ezr0O70cECxXSp4VRNVrJhf46AfkaeC L6b4oesNPgGcjrbAUYZEyYb2HYH3u1R38F2aAv+zmRdEIWlClg4nl132r/MaoDzBQ2wE xc/YmjLCIUsvDdR9jiycIH1QxAEitXf8a+pZvKDV4A01yRs6qOAhgtLP05Jj98MSbjRz clcyxwpnjHwX9vRSKlG+lCkC+A0mchemhhA2ZBpG3GFNcfIOz1bEZ4wRrzx7N4B94wwR CYhw+UqiAStUmut/v6JQT1lixRS94K3NYN953dVvwgsO+jQNa0Y5d7Bl6RV/3DlU9MXz SrAg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=PN3qRJihjexnlaZHiAZnLTc5z5IPb9X1L/XJ/UuRZGs=; b=gl+Dc8Uvz6QBDM8FWnAldKVo4oOaaVAtR6gWj58Fzv/733coooyJMXPCpjd304W9Bo q43tLXLrVY9G5e3AL8z6DMxq9McnmznwDjhEK9U5c1Zau6qMGimLFKyhmRxztqHdDsAC 4Dq2XXM0FYAKLVGIQiAWIH7lc6rBmc0NQ2+/dW5uVRpVZJHNaMatkhAPRXDbwk8Zx+kc uidDRMjoXMu+IE/bFNHvy1FNBDk25uI9O7c/laFNZioqJsBja6ZPiINEjzsq5Y8drl/O dr/I4WV41AowSB5HJ3qQEr2JqZgggCqvPnfCEuwGzp1d7M8FXyIe+fvS0yCrmnKAQPj0 em0g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d9si28684453plj.252.2017.05.25.04.39.38; Thu, 25 May 2017 04:39:38 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S937759AbdEYLj2 (ORCPT + 25 others); Thu, 25 May 2017 07:39:28 -0400 Received: from szxga02-in.huawei.com ([45.249.212.188]:6828 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1765310AbdEYLjU (ORCPT ); Thu, 25 May 2017 07:39:20 -0400 Received: from 172.30.72.56 (EHLO DGGEML401-HUB.china.huawei.com) ([172.30.72.56]) by dggrg02-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id AOF02136; Thu, 25 May 2017 19:39:06 +0800 (CST) Received: from G00308965-DELL1.china.huawei.com (10.203.181.156) by DGGEML401-HUB.china.huawei.com (10.3.17.32) with Microsoft SMTP Server id 14.3.301.0; Thu, 25 May 2017 19:38:57 +0800 From: Gabriele Paoloni To: , , , , , , , , CC: , , , , , , , , , , , Subject: [PATCH v9 5/7] ACPI: Translate the I/O range of non-MMIO devices before scanning Date: Thu, 25 May 2017 12:37:26 +0100 Message-ID: <1495712248-5232-6-git-send-email-gabriele.paoloni@huawei.com> X-Mailer: git-send-email 2.7.1.windows.1 In-Reply-To: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> References: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.203.181.156] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020204.5926C25A.01A4, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 8a4e8ff081346835fb40abc97181a885 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Gabriele On some platforms(such as Hip06/Hip07), the legacy ISA/LPC devices access I/O with some special host-local I/O ports known on x86. As their I/O space are not memory mapped like PCI/PCIE MMIO host bridges, this patch is meant to support a new class of I/O host controllers where the local IO ports of the children devices are translated into the Indirect I/O address space. Through the handler attach callback, all the I/O translations are done before starting the enumeration on children devices and the translated addresses are replaced in the children resources. Signed-off-by: zhichang.yuan Signed-off-by: Gabriele Paoloni --- drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/acpi_indirect_pio.c | 301 +++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 5 + drivers/acpi/scan.c | 1 + include/acpi/acpi_indirect_pio.h | 24 +++ 5 files changed, 332 insertions(+) create mode 100644 drivers/acpi/arm64/acpi_indirect_pio.c create mode 100644 include/acpi/acpi_indirect_pio.h -- 2.7.4 diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 1017def..3944775 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ACPI_IORT) += iort.o obj-$(CONFIG_ACPI_GTDT) += gtdt.o +obj-$(CONFIG_INDIRECT_PIO) += acpi_indirect_pio.o diff --git a/drivers/acpi/arm64/acpi_indirect_pio.c b/drivers/acpi/arm64/acpi_indirect_pio.c new file mode 100644 index 0000000..7813f73 --- /dev/null +++ b/drivers/acpi/arm64/acpi_indirect_pio.c @@ -0,0 +1,301 @@ +/* + * ACPI support for indirect-PIO bus. + * + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved. + * Author: Gabriele Paoloni + * Author: Zhichang Yuan + * + * 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 + +ACPI_MODULE_NAME("indirect PIO"); + +#define INDIRECT_PIO_INFO(desc) ((unsigned long)&desc) + +static acpi_status acpi_count_logic_iores(struct acpi_resource *res, + void *data) +{ + int *res_cnt = data; + + if (!acpi_dev_filter_resource_type(res, IORESOURCE_IO)) + (*res_cnt)++; + + return AE_OK; +} + +static acpi_status acpi_read_one_logicpiores(struct acpi_resource *res, + void *data) +{ + struct acpi_resource **resource = data; + + if (!acpi_dev_filter_resource_type(res, IORESOURCE_IO)) { + memcpy((*resource), res, sizeof(struct acpi_resource)); + (*resource)->length = sizeof(struct acpi_resource); + (*resource)->type = res->type; + (*resource)++; + } + + return AE_OK; +} + +static acpi_status +acpi_build_logicpiores_template(struct acpi_device *adev, + struct acpi_buffer *buffer) +{ + acpi_handle handle = adev->handle; + struct acpi_resource *resource; + acpi_status status; + int res_cnt = 0; + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + acpi_count_logic_iores, &res_cnt); + if (ACPI_FAILURE(status)) { + dev_err(&adev->dev, "can't evaluate _CRS: %d\n", status); + return -EINVAL; + } + + if (!res_cnt) { + dev_err(&adev->dev, "no logic IO resources\n"); + return -ENODEV; + } + + buffer->length = sizeof(struct acpi_resource) * (res_cnt + 1); + buffer->pointer = kzalloc(buffer->length, GFP_KERNEL); + if (!buffer->pointer) + return -ENOMEM; + + resource = (struct acpi_resource *)buffer->pointer; + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + acpi_read_one_logicpiores, &resource); + if (ACPI_FAILURE(status)) { + kfree(buffer->pointer); + dev_err(&adev->dev, "can't evaluate _CRS: %d\n", status); + return -EINVAL; + } + + resource->type = ACPI_RESOURCE_TYPE_END_TAG; + resource->length = sizeof(struct acpi_resource); + + return 0; +} + +static int acpi_translate_logicpiores(struct acpi_device *adev, + struct acpi_device *host, struct acpi_buffer *buffer) +{ + struct acpi_resource *resource = buffer->pointer; + unsigned long sys_port; + struct device *dev = &adev->dev; + union acpi_resource_data *trans_data = &resource->data; + resource_size_t bus_addr; + resource_size_t max_pio; + resource_size_t length; + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_ADDRESS16: + if (trans_data->address16.min_address_fixed != + trans_data->address16.max_address_fixed) { + dev_warn(dev, "variable I/O resource is invalid!\n"); + return -EINVAL; + } + bus_addr = trans_data->address16.address.minimum; + length = trans_data->address16.address.address_length; + max_pio = U16_MAX; + break; + case ACPI_RESOURCE_TYPE_ADDRESS32: + if (trans_data->address32.min_address_fixed != + trans_data->address32.max_address_fixed) { + dev_warn(dev, "variable I/O resource is invalid!\n"); + return -EINVAL; + } + bus_addr = trans_data->address32.address.minimum; + length = trans_data->address32.address.address_length; + max_pio = U32_MAX; + break; + case ACPI_RESOURCE_TYPE_ADDRESS64: + if (trans_data->address64.min_address_fixed != + trans_data->address64.max_address_fixed) { + dev_warn(dev, "variable I/O resource is invalid!\n"); + return -EINVAL; + } + bus_addr = trans_data->address64.address.minimum; + length = trans_data->address64.address.address_length; + max_pio = U64_MAX; + break; + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + if (trans_data->ext_address64.min_address_fixed != + trans_data->ext_address64.max_address_fixed) { + dev_warn(dev, "variable I/O resource is invalid!\n"); + return -EINVAL; + } + bus_addr = trans_data->ext_address64.address.minimum; + length = trans_data->ext_address64.address.address_length; + max_pio = U64_MAX; + break; + case ACPI_RESOURCE_TYPE_IO: + bus_addr = trans_data->io.minimum; + length = trans_data->io.address_length; + max_pio = U16_MAX; + break; + case ACPI_RESOURCE_TYPE_FIXED_IO: + bus_addr = trans_data->fixed_io.address; + length = trans_data->fixed_io.address_length; + max_pio = U16_MAX; + break; + default: + return -EINVAL; + } + + sys_port = logic_pio_trans_hwaddr(&host->fwnode, bus_addr); + if (sys_port == -1) { + dev_err(dev, "translate bus-addr(0x%llx) fail!\n", bus_addr); + return -EFAULT; + } + + /* + * we need to check if the resource address can contain the + * translated IO token + */ + if ((sys_port + length) > max_pio) { + dev_err(dev, "sys_port exceeds the max resource address\n"); + return -ENOSPC; + } + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_ADDRESS16: + trans_data->address16.address.minimum = sys_port; + trans_data->address16.address.maximum = sys_port + length; + break; + case ACPI_RESOURCE_TYPE_ADDRESS32: + trans_data->address32.address.minimum = sys_port; + trans_data->address32.address.maximum = sys_port + length; + break; + case ACPI_RESOURCE_TYPE_ADDRESS64: + trans_data->address64.address.minimum = sys_port; + trans_data->address64.address.maximum = sys_port + length; + break; + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + trans_data->ext_address64.address.minimum = sys_port; + trans_data->ext_address64.address.maximum = sys_port + length; + break; + case ACPI_RESOURCE_TYPE_IO: + trans_data->io.minimum = sys_port; + trans_data->io.maximum = sys_port + length; + break; + case ACPI_RESOURCE_TYPE_FIXED_IO: + trans_data->fixed_io.address = sys_port; + break; + } + return 0; +} + +/* + * update/set the current I/O resource of the designated device node. + * after this calling, the enumeration can be started as the I/O resource + * had been translated to logicial I/O from bus-local I/O. + * + * @adev: the device node to be updated the I/O resource; + * @host: the device node where 'adev' is attached, which can be not + * the parent of 'adev'; + * + * return 0 when successful, negative is for failure. + */ +int acpi_set_logic_pio_resource(struct device *child, + struct device *hostdev) +{ + struct acpi_device *adev; + struct acpi_device *host; + struct acpi_buffer buffer; + acpi_status status; + int ret; + + if (!child || !hostdev) + return -EINVAL; + + host = to_acpi_device(hostdev); + adev = to_acpi_device(child); + + /* check the device state */ + if (!adev->status.present) { + dev_info(child, "ACPI: device is not present!\n"); + return 0; + } + /* whether the child had been enumerated? */ + if (acpi_device_enumerated(adev)) { + dev_info(child, "ACPI: had been enumerated!\n"); + return 0; + } + + /* read the _CRS and convert as acpi_buffer */ + status = acpi_build_logicpiores_template(adev, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(child, "Failure evaluating %s\n", METHOD_NAME__CRS); + return -ENODEV; + } + + /* translate the I/O resources */ + ret = acpi_translate_logicpiores(adev, host, &buffer); + if (ret) { + kfree(buffer.pointer); + dev_err(child, "Translate I/O range FAIL!\n"); + return ret; + } + + /* set current resource... */ + status = acpi_set_current_resources(adev->handle, &buffer); + kfree(buffer.pointer); + if (ACPI_FAILURE(status)) { + dev_err(child, "Error evaluating _SRS (0x%x)\n", status); + ret = -EIO; + } + + return ret; +} + +/* All the host devices which apply indirect-PIO can be listed here. */ +static const struct acpi_device_id acpi_indirect_host_id[] = { + {""}, +}; + +static int acpi_indirectpio_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + struct indirect_pio_device_desc *hostdata; + struct platform_device *pdev; + int ret; + + hostdata = (struct indirect_pio_device_desc *)id->driver_data; + if (!hostdata || !hostdata->pre_setup) + return -EINVAL; + + ret = hostdata->pre_setup(adev, hostdata->pdata); + if (!ret) { + pdev = acpi_create_platform_device(adev, NULL); + if (IS_ERR_OR_NULL(pdev)) { + dev_err(&adev->dev, "Create platform device for host FAIL!\n"); + return -EFAULT; + } + acpi_device_set_enumerated(adev); + ret = 1; + } + + return ret; +} + + +static struct acpi_scan_handler acpi_indirect_handler = { + .ids = acpi_indirect_host_id, + .attach = acpi_indirectpio_attach, +}; + +void __init acpi_indirectio_scan_init(void) +{ + acpi_scan_add_handler(&acpi_indirect_handler); +} diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 66229ff..bf8aaf8 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -31,6 +31,11 @@ void acpi_processor_init(void); void acpi_platform_init(void); void acpi_pnp_init(void); void acpi_int340x_thermal_init(void); +#ifdef CONFIG_INDIRECT_PIO +void acpi_indirectio_scan_init(void); +#else +static inline void acpi_indirectio_scan_init(void) {} +#endif #ifdef CONFIG_ARM_AMBA void acpi_amba_init(void); #else diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e39ec7b..37dd23c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2035,6 +2035,7 @@ int __init acpi_scan_init(void) acpi_int340x_thermal_init(); acpi_amba_init(); acpi_watchdog_init(); + acpi_indirectio_scan_init(); acpi_scan_add_handler(&generic_device_handler); diff --git a/include/acpi/acpi_indirect_pio.h b/include/acpi/acpi_indirect_pio.h new file mode 100644 index 0000000..efc5c43 --- /dev/null +++ b/include/acpi/acpi_indirect_pio.h @@ -0,0 +1,24 @@ +/* + * ACPI support for indirect-PIO bus. + * + * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved. + * Author: Gabriele Paoloni + * Author: Zhichang Yuan + * + * 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. + */ + +#ifndef _ACPI_INDIRECT_PIO_H +#define _ACPI_INDIRECT_PIO_H + +struct indirect_pio_device_desc { + void *pdata; /* device relevant info data */ + int (*pre_setup)(struct acpi_device *adev, void *pdata); +}; + +int acpi_set_logic_pio_resource(struct device *child, + struct device *hostdev); + +#endif From patchwork Thu May 25 11:37:27 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriele Paoloni X-Patchwork-Id: 100499 Delivered-To: patch@linaro.org Received: by 10.140.96.100 with SMTP id j91csp719376qge; Thu, 25 May 2017 04:39:39 -0700 (PDT) X-Received: by 10.98.211.87 with SMTP id q84mr44729872pfg.126.1495712379181; Thu, 25 May 2017 04:39:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1495712379; cv=none; d=google.com; s=arc-20160816; b=ig5sfVPvIpKt91yCjmQ2efCyn0Jp30jfAESQffH+wpaLTpffoxEhULh9GZNCd31Nbl PJveNE3MejWen1+VQWXjJrJPoq41+hPut8O9/lFqsCKaLdXvg4D35bQpGyFuUhBe7/jp +53yqi9a06onwWxRlLQdgMQfi22lr5rOxyWI1G2adAh2RRDPl8tS8lP9t3zH5auud6Au QgvaC4HMRThbYkjsmY56Tn/VJ3UwVB+aPyDR4mwy0//F6iWB/0dHCKiAGu/tDqOqHI3T 3JqR6s77RMk7KP8Ug4vf1ESU5QUeLx8+k1k+AKOweoYCYx8s7jnHIfZ0u+9y/Z8fAx4S q3ow== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=gCw5ibRgoepvNs903cFKqEJX+iNOT+DrTj/yUEDjqe8=; b=SNx61ZZEfnOc/72YYbzBWiArZcFBgI94Wz/5WxrLP/FYGBoL4bXLtZfhv5GAms7YzY ecuzrx0r28FctqeIp2CrJN9nxFhUJxPEVl+90eT5HmtU6W3dCD3egUzBY8Ox0MxwoCFR /TTjuao9ODGG4vPb3jNLEuevAuWs0CfX2igF0zQOCbPU7dt8Vxf1lSAD+m7xh+8b/u1y RTyqPWTxnCnI2CoCnPiuffplcOwkWcZ7xgu2w2P7HssHKVemVdqqBySpTNQXRcEDX35B 0xLeidVGSNmqzR8zxDjV7k1NbhRUV9iP5tbBE2JbGlBuVhRTyNKsG2P6Wb5rFtXR7fOR Tbhw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d9si28684453plj.252.2017.05.25.04.39.38; Thu, 25 May 2017 04:39:39 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S937902AbdEYLjd (ORCPT + 25 others); Thu, 25 May 2017 07:39:33 -0400 Received: from szxga01-in.huawei.com ([45.249.212.187]:7266 "EHLO szxga01-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1765355AbdEYLjV (ORCPT ); Thu, 25 May 2017 07:39:21 -0400 Received: from 172.30.72.57 (EHLO DGGEML401-HUB.china.huawei.com) ([172.30.72.57]) by dggrg01-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id APE94826; Thu, 25 May 2017 19:39:12 +0800 (CST) Received: from G00308965-DELL1.china.huawei.com (10.203.181.156) by DGGEML401-HUB.china.huawei.com (10.3.17.32) with Microsoft SMTP Server id 14.3.301.0; Thu, 25 May 2017 19:39:03 +0800 From: Gabriele Paoloni To: , , , , , , , , CC: , , , , , , , , , , , , "zhichang.yuan" Subject: [PATCH v9 6/7] LPC: Add the ACPI LPC support Date: Thu, 25 May 2017 12:37:27 +0100 Message-ID: <1495712248-5232-7-git-send-email-gabriele.paoloni@huawei.com> X-Mailer: git-send-email 2.7.1.windows.1 In-Reply-To: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> References: <1495712248-5232-1-git-send-email-gabriele.paoloni@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.203.181.156] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A090204.5926C261.0170, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 5a95c541780afe83448bd7224a026c86 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "zhichang.yuan" Based on the provious patches, this patch supports the ACPI LPC host on Hip06/Hip07. Signed-off-by: zhichang.yuan Signed-off-by: John Garry Signed-off-by: Gabriele Paoloni Tested-by: dann frazier --- drivers/acpi/arm64/acpi_indirect_pio.c | 3 ++ drivers/bus/hisi_lpc.c | 71 ++++++++++++++++++++++++++++++++-- include/acpi/acpi_indirect_pio.h | 4 ++ 3 files changed, 75 insertions(+), 3 deletions(-) -- 2.7.4 diff --git a/drivers/acpi/arm64/acpi_indirect_pio.c b/drivers/acpi/arm64/acpi_indirect_pio.c index 7813f73..3a5ba7a 100644 --- a/drivers/acpi/arm64/acpi_indirect_pio.c +++ b/drivers/acpi/arm64/acpi_indirect_pio.c @@ -261,6 +261,9 @@ int acpi_set_logic_pio_resource(struct device *child, /* All the host devices which apply indirect-PIO can be listed here. */ static const struct acpi_device_id acpi_indirect_host_id[] = { +#ifdef CONFIG_HISILICON_LPC + {"HISI0191", INDIRECT_PIO_INFO(lpc_host_desc)}, +#endif {""}, }; diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c index 4f3bf76..05a0a84 100644 --- a/drivers/bus/hisi_lpc.c +++ b/drivers/bus/hisi_lpc.c @@ -467,7 +467,9 @@ static int hisilpc_probe(struct platform_device *pdev) } /* register the LPC host PIO resources */ - if (!has_acpi_companion(dev)) { + if (has_acpi_companion(dev)) { + lpcdev->io_host = find_io_range_by_fwnode(dev->fwnode); + } else { struct logic_pio_hwaddr *range; range = kzalloc(sizeof(*range), GFP_KERNEL); @@ -481,13 +483,14 @@ static int hisilpc_probe(struct platform_device *pdev) ret = logic_pio_register_range(range); if (ret) { kfree(range); - dev_err(dev, "OF: register IO range FAIL!\n"); + dev_err(dev, "OF: logic_pio_register_range returned %d!\n", + ret); return -ret; } lpcdev->io_host = range; } if (!lpcdev->io_host) { - dev_err(dev, "Hisilpc IO hasn't registered!\n"); + dev_err(dev, "HiSi LPC IO hasn't been registered!\n"); return -EFAULT; } @@ -533,10 +536,72 @@ static const struct of_device_id hisilpc_of_match[] = { {}, }; +#ifdef CONFIG_ACPI +#include + +struct lpc_private_data { + resource_size_t io_size; + resource_size_t io_start; +}; + +static struct lpc_private_data lpc_data = { + .io_size = LPC_BUS_IO_SIZE, + .io_start = LPC_MIN_BUS_RANGE, +}; + +static int lpc_host_io_setup(struct acpi_device *adev, void *pdata) +{ + int ret = 0; + struct logic_pio_hwaddr *range; + struct lpc_private_data *lpc_private; + struct acpi_device *child; + + lpc_private = (struct lpc_private_data *)pdata; + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + range->fwnode = &adev->fwnode; + range->flags = PIO_INDIRECT; + range->size = lpc_private->io_size; + range->hw_start = lpc_private->io_start; + + ret = logic_pio_register_range(range); + if (ret) { + kfree(range); + return ret; + } + + /* In HiSilicon lpc, only care about the children of the host. */ + list_for_each_entry(child, &adev->children, node) { + ret = acpi_set_logic_pio_resource(&child->dev, &adev->dev); + if (ret) { + dev_err(&child->dev, + "acpi_set_logic_pio_resource() returned %d\n", + ret); + return ret; + } + } + + return ret; +} + +static const struct acpi_device_id hisilpc_acpi_match[] = { + {"HISI0191", }, + {}, +}; + +const struct indirect_pio_device_desc lpc_host_desc = { + .pdata = &lpc_data, + .pre_setup = lpc_host_io_setup, +}; + +#endif + static struct platform_driver hisilpc_driver = { .driver = { .name = "hisi_lpc", .of_match_table = hisilpc_of_match, + .acpi_match_table = ACPI_PTR(hisilpc_acpi_match), }, .probe = hisilpc_probe, }; diff --git a/include/acpi/acpi_indirect_pio.h b/include/acpi/acpi_indirect_pio.h index efc5c43..7a8d26b 100644 --- a/include/acpi/acpi_indirect_pio.h +++ b/include/acpi/acpi_indirect_pio.h @@ -18,6 +18,10 @@ struct indirect_pio_device_desc { int (*pre_setup)(struct acpi_device *adev, void *pdata); }; +#ifdef CONFIG_HISILICON_LPC +extern const struct indirect_pio_device_desc lpc_host_desc; +#endif + int acpi_set_logic_pio_resource(struct device *child, struct device *hostdev);