pci: serial: Support reading PCI-register size with base

The PCI helpers read only the base address for a PCI region. In some cases
the size is needed as well, e.g. to pass along to a driver which needs to
know the size of its register area.

Update the functions to allow the size to be returned. For serial, record
the information and provided it with the serial_info() call.

A limitation still exists in that the size is not available when OF_LIVE
is enabled, so take account of that in the tests.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 962b9eb..6abce9e 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1123,8 +1123,8 @@
 		pci@1,0 {
 			compatible = "pci-generic";
 			/* reg 0 is at 0x14, using FDT_PCI_SPACE_MEM32 */
-			reg = <0x02000814 0 0 0 0
-			       0x01000810 0 0 0 0>;
+			reg = <0x02000814 0 0 0x80 0
+			       0x01000810 0 0 0xc0 0>;
 			sandbox,emul = <&swap_case_emul0_1>;
 		};
 		p2sb-pci@2,0 {
@@ -1151,7 +1151,7 @@
 		pci@1f,0 {
 			compatible = "pci-generic";
 			/* reg 0 is at 0x10, using FDT_PCI_SPACE_IO */
-			reg = <0x0100f810 0 0 0 0>;
+			reg = <0x0100f810 0 0 0x100 0>;
 			sandbox,emul = <&swap_case_emul0_1f>;
 		};
 	};
diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c
index 546db67..b79d138 100644
--- a/drivers/core/fdtaddr.c
+++ b/drivers/core/fdtaddr.c
@@ -215,7 +215,7 @@
 	return map_physmem(addr, size, MAP_NOCACHE);
 }
 
-fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev)
+fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev, fdt_size_t *sizep)
 {
 	ulong addr;
 
@@ -226,12 +226,12 @@
 		int ret;
 
 		ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_MEM32,
-					   "reg", &pci_addr);
+					   "reg", &pci_addr, sizep);
 		if (ret) {
 			/* try if there is any i/o-mapped register */
 			ret = ofnode_read_pci_addr(dev_ofnode(dev),
 						   FDT_PCI_SPACE_IO, "reg",
-						   &pci_addr);
+						   &pci_addr, sizep);
 			if (ret)
 				return FDT_ADDR_T_NONE;
 		}
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 18d2eb0..29a4294 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -1270,7 +1270,8 @@
 }
 
 int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type,
-			 const char *propname, struct fdt_pci_addr *addr)
+			 const char *propname, struct fdt_pci_addr *addr,
+			 fdt_size_t *size)
 {
 	const fdt32_t *cell;
 	int len;
@@ -1298,14 +1299,18 @@
 			      (ulong)fdt32_to_cpu(cell[1]),
 			      (ulong)fdt32_to_cpu(cell[2]));
 			if ((fdt32_to_cpu(*cell) & type) == type) {
+				const unaligned_fdt64_t *ptr;
+
 				addr->phys_hi = fdt32_to_cpu(cell[0]);
 				addr->phys_mid = fdt32_to_cpu(cell[1]);
 				addr->phys_lo = fdt32_to_cpu(cell[2]);
+				ptr = (const unaligned_fdt64_t *)(cell + 3);
+				if (size)
+					*size = fdt64_to_cpu(*ptr);
 				break;
 			}
 
-			cell += (FDT_PCI_ADDR_CELLS +
-				 FDT_PCI_SIZE_CELLS);
+			cell += FDT_PCI_ADDR_CELLS + FDT_PCI_SIZE_CELLS;
 		}
 
 		if (i == num) {
diff --git a/drivers/core/read.c b/drivers/core/read.c
index 49066b5..4190134 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -405,13 +405,15 @@
 	return fdtdec_get_alias_highest_id(gd->fdt_blob, stem);
 }
 
-fdt_addr_t dev_read_addr_pci(const struct udevice *dev)
+fdt_addr_t dev_read_addr_pci(const struct udevice *dev, fdt_size_t *sizep)
 {
 	ulong addr;
 
 	addr = dev_read_addr(dev);
+	if (sizep)
+		*sizep = 0;
 	if (addr == FDT_ADDR_T_NONE && !of_live_active())
-		addr = devfdt_get_addr_pci(dev);
+		addr = devfdt_get_addr_pci(dev, sizep);
 
 	return addr;
 }
diff --git a/drivers/core/util.c b/drivers/core/util.c
index aa60fdd..81497df 100644
--- a/drivers/core/util.c
+++ b/drivers/core/util.c
@@ -30,7 +30,7 @@
 
 	/* Extract the devfn from fdt_pci_addr */
 	ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_CONFIG,
-				   "reg", &addr);
+				   "reg", &addr, NULL);
 	if (ret) {
 		if (ret != -ENOENT)
 			return -EINVAL;
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index ae7350a..e0d01f6 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -123,7 +123,7 @@
 
 	dev_for_each_subnode(node, bus) {
 		ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg",
-					   &addr);
+					   &addr, NULL);
 		if (ret)
 			continue;
 
diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c
index 3697cd8..8355955 100644
--- a/drivers/pci/pci_mvebu.c
+++ b/drivers/pci/pci_mvebu.c
@@ -641,7 +641,8 @@
 		pcie->is_x4 = true;
 
 	/* devfn is in bits [15:8], see PCI_DEV usage */
-	ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr);
+	ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr,
+				   NULL);
 	if (ret < 0) {
 		printf("%s: property \"reg\" is invalid\n", pcie->name);
 		goto err;
diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c
index 131c21b..d6374a5 100644
--- a/drivers/pci/pci_tegra.c
+++ b/drivers/pci/pci_tegra.c
@@ -462,7 +462,7 @@
 
 	*lanes = err;
 
-	err = ofnode_read_pci_addr(node, 0, "reg", &addr);
+	err = ofnode_read_pci_addr(node, 0, "reg", &addr, NULL);
 	if (err < 0) {
 		pr_err("failed to parse \"reg\" property\n");
 		return err;
diff --git a/drivers/pci/pcie_mediatek.c b/drivers/pci/pcie_mediatek.c
index ed25a10..f0f34b5 100644
--- a/drivers/pci/pcie_mediatek.c
+++ b/drivers/pci/pcie_mediatek.c
@@ -661,7 +661,7 @@
 		if (!ofnode_is_enabled(subnode))
 			continue;
 
-		err = ofnode_read_pci_addr(subnode, 0, "reg", &addr);
+		err = ofnode_read_pci_addr(subnode, 0, "reg", &addr, NULL);
 		if (err)
 			return err;
 
@@ -700,7 +700,7 @@
 		if (!ofnode_is_enabled(subnode))
 			continue;
 
-		err = ofnode_read_pci_addr(subnode, 0, "reg", &addr);
+		err = ofnode_read_pci_addr(subnode, 0, "reg", &addr, NULL);
 		if (err)
 			return err;
 
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
index 5ca2828..6deb1d8 100644
--- a/drivers/serial/ns16550.c
+++ b/drivers/serial/ns16550.c
@@ -484,6 +484,7 @@
 	info->addr_space = SERIAL_ADDRESS_SPACE_MEMORY;
 #endif
 	info->addr = plat->base;
+	info->size = plat->size;
 	info->reg_width = plat->reg_width;
 	info->reg_shift = plat->reg_shift;
 	info->reg_offset = plat->reg_offset;
@@ -492,7 +493,8 @@
 	return 0;
 }
 
-static int ns16550_serial_assign_base(struct ns16550_plat *plat, fdt_addr_t base)
+static int ns16550_serial_assign_base(struct ns16550_plat *plat,
+				      fdt_addr_t base, fdt_size_t size)
 {
 	if (base == FDT_ADDR_T_NONE)
 		return -EINVAL;
@@ -502,6 +504,7 @@
 #else
 	plat->base = (unsigned long)map_physmem(base, 0, MAP_NOCACHE);
 #endif
+	plat->size = size;
 
 	return 0;
 }
@@ -512,6 +515,7 @@
 	struct ns16550 *const com_port = dev_get_priv(dev);
 	struct reset_ctl_bulk reset_bulk;
 	fdt_addr_t addr;
+	fdt_addr_t size;
 	int ret;
 
 	/*
@@ -519,8 +523,8 @@
 	 * or via a PCI bridge, assign plat->base before probing hardware.
 	 */
 	if (device_is_on_pci_bus(dev)) {
-		addr = devfdt_get_addr_pci(dev);
-		ret = ns16550_serial_assign_base(plat, addr);
+		addr = devfdt_get_addr_pci(dev, &size);
+		ret = ns16550_serial_assign_base(plat, addr, size);
 		if (ret)
 			return ret;
 	}
@@ -547,12 +551,14 @@
 {
 	struct ns16550_plat *plat = dev_get_plat(dev);
 	const u32 port_type = dev_get_driver_data(dev);
+	fdt_size_t size = 0;
 	fdt_addr_t addr;
 	struct clk clk;
 	int err;
 
-	addr = dev_read_addr(dev);
-	err = ns16550_serial_assign_base(plat, addr);
+	addr = spl_in_proper() ? dev_read_addr_size(dev, &size) :
+		dev_read_addr(dev);
+	err = ns16550_serial_assign_base(plat, addr, size);
 	if (err && !device_is_on_pci_bus(dev))
 		return err;
 
diff --git a/include/dm/fdtaddr.h b/include/dm/fdtaddr.h
index dcdc191..6d2fa8f 100644
--- a/include/dm/fdtaddr.h
+++ b/include/dm/fdtaddr.h
@@ -168,8 +168,9 @@
  * devfdt_get_addr_pci() - Read an address and handle PCI address translation
  *
  * @dev: Device to read from
+ * @sizep: If non-NULL, returns size of address space
  * Return: address or FDT_ADDR_T_NONE if not found
  */
-fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev);
+fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev, fdt_size_t *sizep);
 
 #endif
diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h
index ef1437c..19e97a9 100644
--- a/include/dm/ofnode.h
+++ b/include/dm/ofnode.h
@@ -1153,13 +1153,15 @@
  * @type:	pci address type (FDT_PCI_SPACE_xxx)
  * @propname:	name of property to find
  * @addr:	returns pci address in the form of fdt_pci_addr
+ * @size:	if non-null, returns register-space size
  * Return:
  * 0 if ok, -ENOENT if the property did not exist, -EINVAL if the
  * format of the property was invalid, -ENXIO if the requested
  * address type was not found
  */
 int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type,
-			 const char *propname, struct fdt_pci_addr *addr);
+			 const char *propname, struct fdt_pci_addr *addr,
+			 fdt_size_t *size);
 
 /**
  * ofnode_read_pci_vendev() - look up PCI vendor and device id
diff --git a/include/dm/read.h b/include/dm/read.h
index c2615f7..3c2eea6 100644
--- a/include/dm/read.h
+++ b/include/dm/read.h
@@ -346,9 +346,10 @@
  *    fdtdec_get_addr() and friends.
  *
  * @dev: Device to read from
+ * @sizep: If non-NULL, returns size of address space found
  * Return: address or FDT_ADDR_T_NONE if not found
  */
-fdt_addr_t dev_read_addr_pci(const struct udevice *dev);
+fdt_addr_t dev_read_addr_pci(const struct udevice *dev, fdt_size_t *sizep);
 
 /**
  * dev_remap_addr() - Get the reg property of a device as a
@@ -996,9 +997,10 @@
 	return devfdt_get_addr_ptr(dev);
 }
 
-static inline fdt_addr_t dev_read_addr_pci(const struct udevice *dev)
+static inline fdt_addr_t dev_read_addr_pci(const struct udevice *dev,
+					   fdt_size_t *sizep)
 {
-	return devfdt_get_addr_pci(dev);
+	return devfdt_get_addr_pci(dev, sizep);
 }
 
 static inline void *dev_remap_addr(const struct udevice *dev)
diff --git a/include/ns16550.h b/include/ns16550.h
index e7e6866..7f48130 100644
--- a/include/ns16550.h
+++ b/include/ns16550.h
@@ -58,6 +58,7 @@
  * struct ns16550_plat - information about a NS16550 port
  *
  * @base:		Base register address
+ * @size:		Size of register area in bytes
  * @reg_width:		IO accesses size of registers (in bytes, 1 or 4)
  * @reg_shift:		Shift size of registers (0=byte, 1=16bit, 2=32bit...)
  * @reg_offset:		Offset to start of registers (normally 0)
@@ -67,7 +68,8 @@
  * @bdf:		PCI slot/function (pci_dev_t)
  */
 struct ns16550_plat {
-	unsigned long base;
+	ulong base;
+	ulong size;
 	int reg_width;
 	int reg_shift;
 	int reg_offset;
diff --git a/include/serial.h b/include/serial.h
index 42bdf37..205889d 100644
--- a/include/serial.h
+++ b/include/serial.h
@@ -137,6 +137,7 @@
  * @type:	type of the UART chip
  * @addr_space:	address space to access the registers
  * @addr:	physical address of the registers
+ * @size:	size of the register area in bytes
  * @reg_width:	size (in bytes) of the IO accesses to the registers
  * @reg_offset:	offset to apply to the @addr from the start of the registers
  * @reg_shift:	quantity to shift the register offsets by
@@ -147,6 +148,7 @@
 	enum serial_chip_type type;
 	enum adr_space_type addr_space;
 	ulong addr;
+	ulong size;
 	u8 reg_width;
 	u8 reg_offset;
 	u8 reg_shift;
diff --git a/test/dm/pci.c b/test/dm/pci.c
index 70a736c..8c5e7da 100644
--- a/test/dm/pci.c
+++ b/test/dm/pci.c
@@ -301,10 +301,12 @@
 {
 	struct udevice *swap1f, *swap1;
 	ulong io_addr, mem_addr;
+	fdt_addr_t size;
 
 	ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f));
 	io_addr = dm_pci_read_bar32(swap1f, 0);
-	ut_asserteq(io_addr, dev_read_addr_pci(swap1f));
+	ut_asserteq(io_addr, dev_read_addr_pci(swap1f, &size));
+	ut_asserteq(0, size);
 
 	/*
 	 * This device has both I/O and MEM spaces but the MEM space appears
@@ -312,7 +314,8 @@
 	 */
 	ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1));
 	mem_addr = dm_pci_read_bar32(swap1, 1);
-	ut_asserteq(mem_addr, dev_read_addr_pci(swap1));
+	ut_asserteq(mem_addr, dev_read_addr_pci(swap1, &size));
+	ut_asserteq(0, size);
 
 	return 0;
 }
@@ -329,12 +332,15 @@
 static int dm_test_pci_addr_live(struct unit_test_state *uts)
 {
 	struct udevice *swap1f, *swap1;
+	fdt_size_t size;
 
 	ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f));
-	ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f));
+	ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f, &size));
+	ut_asserteq(0, size);
 
 	ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1));
-	ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1));
+	ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1, &size));
+	ut_asserteq(0, size);
 
 	return 0;
 }