diff mbox series

[v11,5/9] OF: Add missing I/O range exception for indirect-IO devices

Message ID 1516537647-50553-6-git-send-email-john.garry@huawei.com
State Superseded
Headers show
Series LPC: legacy ISA I/O support | expand

Commit Message

John Garry Jan. 21, 2018, 12:27 p.m. UTC
From: Zhichang Yuan <yuanzhichang@hisilicon.com>


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 <yuanzhichang@hisilicon.com>

Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>

Signed-off-by: Arnd Bergmann <arnd@arndb.de>    #earlier draft

Acked-by: Rob Herring <robh@kernel.org>

---
 drivers/of/address.c | 91 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 75 insertions(+), 16 deletions(-)

-- 
1.9.1

Comments

kernel test robot Jan. 21, 2018, 3:53 p.m. UTC | #1
Hi Zhichang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.15-rc8 next-20180119]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/John-Garry/LPC-legacy-ISA-I-O-support/20180121-194832
config: tile-allyesconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=tile 

All errors (new ones prefixed by >>):

   drivers//of/address.c: In function '__of_translate_address':
   drivers//of/address.c:625:13: error: implicit declaration of function 'find_io_range_by_fwnode' [-Werror=implicit-function-declaration]
      iorange = find_io_range_by_fwnode(&dev->fwnode);
                ^~~~~~~~~~~~~~~~~~~~~~~
   drivers//of/address.c:625:11: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
      iorange = find_io_range_by_fwnode(&dev->fwnode);
              ^
   drivers//of/address.c:626:26: error: dereferencing pointer to incomplete type 'struct logic_pio_hwaddr'
      if (iorange && (iorange->flags != PIO_CPU_MMIO)) {
                             ^~
>> drivers//of/address.c:626:37: error: 'PIO_CPU_MMIO' undeclared (first use in this function); did you mean 'CHIP_HAS_MMIO'?

      if (iorange && (iorange->flags != PIO_CPU_MMIO)) {
                                        ^~~~~~~~~~~~
                                        CHIP_HAS_MMIO
   drivers//of/address.c:626:37: note: each undeclared identifier is reported only once for each function it appears in
   drivers//of/address.c: In function 'of_translate_ioport':
   drivers//of/address.c:742:10: error: implicit declaration of function 'logic_pio_trans_hwaddr' [-Werror=implicit-function-declaration]
      port = logic_pio_trans_hwaddr(&host->fwnode, taddr, size);
             ^~~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +626 drivers//of/address.c

   556	
   557	/*
   558	 * Translate an address from the device-tree into a CPU physical address,
   559	 * this walks up the tree and applies the various bus mappings on the
   560	 * way.
   561	 *
   562	 * Note: We consider that crossing any level with #size-cells == 0 to mean
   563	 * that translation is impossible (that is we are not dealing with a value
   564	 * that can be mapped to a cpu physical address). This is not really specified
   565	 * that way, but this is traditionally the way IBM at least do things
   566	 *
   567	 * Whenever the translation fails, the *host pointer will be set to the
   568	 * device that had registered logical PIO mapping, and the return code is
   569	 * relative to that node.
   570	 */
   571	static u64 __of_translate_address(struct device_node *dev,
   572					  const __be32 *in_addr, const char *rprop,
   573					  struct device_node **host)
   574	{
   575		struct device_node *parent = NULL;
   576		struct of_bus *bus, *pbus;
   577		__be32 addr[OF_MAX_ADDR_CELLS];
   578		int na, ns, pna, pns;
   579		u64 result = OF_BAD_ADDR;
   580	
   581		pr_debug("** translation for device %pOF **\n", dev);
   582	
   583		/* Increase refcount at current level */
   584		of_node_get(dev);
   585	
   586		*host = NULL;
   587		/* Get parent & match bus type */
   588		parent = of_get_parent(dev);
   589		if (parent == NULL)
   590			goto bail;
   591		bus = of_match_bus(parent);
   592	
   593		/* Count address cells & copy address locally */
   594		bus->count_cells(dev, &na, &ns);
   595		if (!OF_CHECK_COUNTS(na, ns)) {
   596			pr_debug("Bad cell count for %pOF\n", dev);
   597			goto bail;
   598		}
   599		memcpy(addr, in_addr, na * 4);
   600	
   601		pr_debug("bus is %s (na=%d, ns=%d) on %pOF\n",
   602		    bus->name, na, ns, parent);
   603		of_dump_addr("translating address:", addr, na);
   604	
   605		/* Translate */
   606		for (;;) {
   607			struct logic_pio_hwaddr *iorange;
   608	
   609			/* Switch to parent bus */
   610			of_node_put(dev);
   611			dev = parent;
   612			parent = of_get_parent(dev);
   613	
   614			/* If root, we have finished */
   615			if (parent == NULL) {
   616				pr_debug("reached root node\n");
   617				result = of_read_number(addr, na);
   618				break;
   619			}
   620	
   621			/*
   622			 * For indirectIO device which has no ranges property, get
   623			 * the address from reg directly.
   624			 */
 > 625			iorange = find_io_range_by_fwnode(&dev->fwnode);

 > 626			if (iorange && (iorange->flags != PIO_CPU_MMIO)) {

   627				result = of_read_number(addr + 1, na - 1);
   628				pr_debug("indirectIO matched(%s) 0x%llx\n",
   629						of_node_full_name(dev), result);
   630				*host = of_node_get(dev);
   631				break;
   632			}
   633	
   634			/* Get new parent bus and counts */
   635			pbus = of_match_bus(parent);
   636			pbus->count_cells(dev, &pna, &pns);
   637			if (!OF_CHECK_COUNTS(pna, pns)) {
   638				pr_err("Bad cell count for %pOF\n", dev);
   639				break;
   640			}
   641	
   642			pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n",
   643			    pbus->name, pna, pns, parent);
   644	
   645			/* Apply bus translation */
   646			if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
   647				break;
   648	
   649			/* Complete the move up one level */
   650			na = pna;
   651			ns = pns;
   652			bus = pbus;
   653	
   654			of_dump_addr("one level translation:", addr, na);
   655		}
   656	 bail:
   657		of_node_put(parent);
   658		of_node_put(dev);
   659	
   660		return result;
   661	}
   662	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox series

Patch

diff --git a/drivers/of/address.c b/drivers/of/address.c
index 85975fe..2cca626 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -564,9 +564,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;
@@ -579,6 +584,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)
@@ -599,6 +605,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;
@@ -611,6 +619,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);
@@ -642,13 +663,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);
 
@@ -690,29 +730,48 @@  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 size)
+{
+	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, size);
+		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, size);
+	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;