[PATCH v3] serial: ns16550: Fix ordering of getting base address

Currently the driver gets ns16550 base address in the driver probe() routine, which may potentially break any ns16550 wrapper driver that does additional initialization before calling ns16550_serial_probe().
Things are complicated that we need consider ns16550 devices on both simple-bus and PCI bus. To fix the issue we move the base address assignment for simple-bus ns16550 device back to the ofdata_to_platdata(), and assign base address for PCI ns16550 device in ns16550_serial_probe().
This is still not perfect. If any PCI bus based ns16550 wrapper driver tries to access plat->base before calling probe(), it is still subject to break.
Fixes: 720f9e1fdb0c9 ("serial: ns16550: Move PCI access from ofdata_to_platdata() to probe()") Reported-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Signed-off-by: Bin Meng bmeng.cn@gmail.com Tested-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Reviewed-by: Wolfgang Wallner wolfgang.wallner@br-automation.com Tested-by: Wolfgang Wallner wolfgang.wallner@br-automation.com
---
Changes in v3: - extract the base address assignment to a helper
Changes in v2: - not to break Fixes, etc to two or more lines in the commit message - add the same CONFIG_SYS_NS16550_PORT_MAPPED ifdefs in the PCI case
drivers/serial/ns16550.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index c1b303f..a2f1b35 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -479,39 +479,38 @@ static int ns16550_serial_getinfo(struct udevice *dev, return 0; }
-#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) -static int ns1655_serial_set_base_addr(struct udevice *dev) +static int ns16550_serial_assign_base(struct ns16550_platdata *plat, ulong base) { - fdt_addr_t addr; - struct ns16550_platdata *plat; - - plat = dev_get_platdata(dev); - - addr = dev_read_addr_pci(dev); - if (addr == FDT_ADDR_T_NONE) + if (base == FDT_ADDR_T_NONE) return -EINVAL;
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED - plat->base = addr; + plat->base = base; #else - plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE); + plat->base = (unsigned long)map_physmem(base, 0, MAP_NOCACHE); #endif
return 0; } -#endif
int ns16550_serial_probe(struct udevice *dev) { + struct ns16550_platdata *plat = dev->platdata; struct NS16550 *const com_port = dev_get_priv(dev); struct reset_ctl_bulk reset_bulk; + fdt_addr_t addr; int ret;
-#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) - ret = ns1655_serial_set_base_addr(dev); - if (ret) - return ret; -#endif + /* + * If we are on PCI bus, either directly attached to a PCI root port, + * or via a PCI bridge, assign platdata->base before probing hardware. + */ + if (device_is_on_pci_bus(dev)) { + addr = devfdt_get_addr_pci(dev); + ret = ns16550_serial_assign_base(plat, addr); + if (ret) + return ret; + }
ret = reset_get_bulk(dev, &reset_bulk); if (!ret) @@ -535,9 +534,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev) { struct ns16550_platdata *plat = dev->platdata; const u32 port_type = dev_get_driver_data(dev); + fdt_addr_t addr; struct clk clk; int err;
+ addr = dev_read_addr(dev); + err = ns16550_serial_assign_base(plat, addr); + if (err && !device_is_on_pci_bus(dev)) + return err; + plat->reg_offset = dev_read_u32_default(dev, "reg-offset", 0); plat->reg_shift = dev_read_u32_default(dev, "reg-shift", 0); plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1);

On Fri, Apr 03, 2020 at 06:35:32PM -0700, Bin Meng wrote:
Currently the driver gets ns16550 base address in the driver probe() routine, which may potentially break any ns16550 wrapper driver that does additional initialization before calling ns16550_serial_probe().
Things are complicated that we need consider ns16550 devices on both simple-bus and PCI bus. To fix the issue we move the base address assignment for simple-bus ns16550 device back to the ofdata_to_platdata(), and assign base address for PCI ns16550 device in ns16550_serial_probe().
This is still not perfect. If any PCI bus based ns16550 wrapper driver tries to access plat->base before calling probe(), it is still subject to break.
Fixes: 720f9e1fdb0c9 ("serial: ns16550: Move PCI access from ofdata_to_platdata() to probe()") Reported-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Signed-off-by: Bin Meng bmeng.cn@gmail.com
And re-tested. Works for me, thanks!
Tested-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Reviewed-by: Wolfgang Wallner wolfgang.wallner@br-automation.com Tested-by: Wolfgang Wallner wolfgang.wallner@br-automation.com
Changes in v3:
- extract the base address assignment to a helper
Changes in v2:
- not to break Fixes, etc to two or more lines in the commit message
- add the same CONFIG_SYS_NS16550_PORT_MAPPED ifdefs in the PCI case
drivers/serial/ns16550.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index c1b303f..a2f1b35 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -479,39 +479,38 @@ static int ns16550_serial_getinfo(struct udevice *dev, return 0; }
-#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) -static int ns1655_serial_set_base_addr(struct udevice *dev) +static int ns16550_serial_assign_base(struct ns16550_platdata *plat, ulong base) {
- fdt_addr_t addr;
- struct ns16550_platdata *plat;
- plat = dev_get_platdata(dev);
- addr = dev_read_addr_pci(dev);
- if (addr == FDT_ADDR_T_NONE)
- if (base == FDT_ADDR_T_NONE) return -EINVAL;
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
- plat->base = addr;
- plat->base = base;
#else
- plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE);
- plat->base = (unsigned long)map_physmem(base, 0, MAP_NOCACHE);
#endif
return 0; } -#endif
int ns16550_serial_probe(struct udevice *dev) {
- struct ns16550_platdata *plat = dev->platdata; struct NS16550 *const com_port = dev_get_priv(dev); struct reset_ctl_bulk reset_bulk;
- fdt_addr_t addr; int ret;
-#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
- ret = ns1655_serial_set_base_addr(dev);
- if (ret)
return ret;
-#endif
/*
* If we are on PCI bus, either directly attached to a PCI root port,
* or via a PCI bridge, assign platdata->base before probing hardware.
*/
if (device_is_on_pci_bus(dev)) {
addr = devfdt_get_addr_pci(dev);
ret = ns16550_serial_assign_base(plat, addr);
if (ret)
return ret;
}
ret = reset_get_bulk(dev, &reset_bulk); if (!ret)
@@ -535,9 +534,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev) { struct ns16550_platdata *plat = dev->platdata; const u32 port_type = dev_get_driver_data(dev);
fdt_addr_t addr; struct clk clk; int err;
addr = dev_read_addr(dev);
err = ns16550_serial_assign_base(plat, addr);
if (err && !device_is_on_pci_bus(dev))
return err;
plat->reg_offset = dev_read_u32_default(dev, "reg-offset", 0); plat->reg_shift = dev_read_u32_default(dev, "reg-shift", 0); plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1);
-- 2.7.4

On Sat, Apr 4, 2020 at 6:32 PM Andy Shevchenko andriy.shevchenko@linux.intel.com wrote:
On Fri, Apr 03, 2020 at 06:35:32PM -0700, Bin Meng wrote:
Currently the driver gets ns16550 base address in the driver probe() routine, which may potentially break any ns16550 wrapper driver that does additional initialization before calling ns16550_serial_probe().
Things are complicated that we need consider ns16550 devices on both simple-bus and PCI bus. To fix the issue we move the base address assignment for simple-bus ns16550 device back to the ofdata_to_platdata(), and assign base address for PCI ns16550 device in ns16550_serial_probe().
This is still not perfect. If any PCI bus based ns16550 wrapper driver tries to access plat->base before calling probe(), it is still subject to break.
Fixes: 720f9e1fdb0c9 ("serial: ns16550: Move PCI access from ofdata_to_platdata() to probe()") Reported-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Signed-off-by: Bin Meng bmeng.cn@gmail.com
And re-tested. Works for me, thanks!
Tested-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Reviewed-by: Wolfgang Wallner wolfgang.wallner@br-automation.com Tested-by: Wolfgang Wallner wolfgang.wallner@br-automation.com
applied to u-boot-x86, thanks!

Hi Bin,
-----"Bin Meng" bmeng.cn@gmail.com schrieb: -----
On Sat, Apr 4, 2020 at 6:32 PM Andy Shevchenko andriy.shevchenko@linux.intel.com wrote:
On Fri, Apr 03, 2020 at 06:35:32PM -0700, Bin Meng wrote:
Currently the driver gets ns16550 base address in the driver probe() routine, which may potentially break any ns16550 wrapper driver that does additional initialization before calling ns16550_serial_probe().
Things are complicated that we need consider ns16550 devices on both simple-bus and PCI bus. To fix the issue we move the base address assignment for simple-bus ns16550 device back to the ofdata_to_platdata(), and assign base address for PCI ns16550 device in ns16550_serial_probe().
This is still not perfect. If any PCI bus based ns16550 wrapper driver tries to access plat->base before calling probe(), it is still subject to break.
Fixes: 720f9e1fdb0c9 ("serial: ns16550: Move PCI access from
ofdata_to_platdata() to probe()")
Reported-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Signed-off-by: Bin Meng bmeng.cn@gmail.com
And re-tested. Works for me, thanks!
Tested-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Reviewed-by: Wolfgang Wallner
wolfgang.wallner@br-automation.com
Tested-by: Wolfgang Wallner wolfgang.wallner@br-automation.com
applied to u-boot-x86, thanks!
Maybe a little late, but I have also re-tested v3 now and can confirm it still works, thanks!
regards, Wolfgang

Hi Wolfgang,
On Mon, Apr 6, 2020 at 3:08 PM Wolfgang Wallner wolfgang.wallner@br-automation.com wrote:
Hi Bin,
-----"Bin Meng" bmeng.cn@gmail.com schrieb: -----
On Sat, Apr 4, 2020 at 6:32 PM Andy Shevchenko andriy.shevchenko@linux.intel.com wrote:
On Fri, Apr 03, 2020 at 06:35:32PM -0700, Bin Meng wrote:
Currently the driver gets ns16550 base address in the driver probe() routine, which may potentially break any ns16550 wrapper driver that does additional initialization before calling ns16550_serial_probe().
Things are complicated that we need consider ns16550 devices on both simple-bus and PCI bus. To fix the issue we move the base address assignment for simple-bus ns16550 device back to the ofdata_to_platdata(), and assign base address for PCI ns16550 device in ns16550_serial_probe().
This is still not perfect. If any PCI bus based ns16550 wrapper driver tries to access plat->base before calling probe(), it is still subject to break.
Fixes: 720f9e1fdb0c9 ("serial: ns16550: Move PCI access from
ofdata_to_platdata() to probe()")
Reported-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Signed-off-by: Bin Meng bmeng.cn@gmail.com
And re-tested. Works for me, thanks!
Tested-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Reviewed-by: Wolfgang Wallner
wolfgang.wallner@br-automation.com
Tested-by: Wolfgang Wallner wolfgang.wallner@br-automation.com
applied to u-boot-x86, thanks!
Maybe a little late, but I have also re-tested v3 now and can confirm it still works, thanks!
Thank you. The fix is now in the mainline.
Regards, Bin
participants (3)
-
Andy Shevchenko
-
Bin Meng
-
Wolfgang Wallner