From af43e14bd0dbcdb0595b1e6620f85fd3513a9eaa Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Thu, 14 May 2020 17:47:27 -0700 Subject: [PATCH] serial: ns16550: do not write to device cfg struct when PCIE=y When PCIe is enabled for UART, the port address is probed during initialization and is written back into the device config struct. However, the device config struct is supposed to be const and read only. This results in page faults when MMU is enabled as the struct cannot be written into. So fix this by storing port address in device data struct if a particular UART instance is of PCIE. Signed-off-by: Daniel Leung --- drivers/serial/uart_ns16550.c | 43 +++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/serial/uart_ns16550.c b/drivers/serial/uart_ns16550.c index afa8f89f74f..88ffa89b2ea 100644 --- a/drivers/serial/uart_ns16550.c +++ b/drivers/serial/uart_ns16550.c @@ -207,21 +207,21 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "NS16550(s) in DT need CONFIG_PCIE"); #define DEV_DATA(dev) \ ((struct uart_ns16550_dev_data_t *)(dev)->driver_data) -#define THR(dev) (DEV_CFG(dev)->devconf.port + REG_THR * UART_REG_ADDR_INTERVAL) -#define RDR(dev) (DEV_CFG(dev)->devconf.port + REG_RDR * UART_REG_ADDR_INTERVAL) +#define THR(dev) (get_port(dev) + REG_THR * UART_REG_ADDR_INTERVAL) +#define RDR(dev) (get_port(dev) + REG_RDR * UART_REG_ADDR_INTERVAL) #define BRDL(dev) \ - (DEV_CFG(dev)->devconf.port + REG_BRDL * UART_REG_ADDR_INTERVAL) + (get_port(dev) + REG_BRDL * UART_REG_ADDR_INTERVAL) #define BRDH(dev) \ - (DEV_CFG(dev)->devconf.port + REG_BRDH * UART_REG_ADDR_INTERVAL) -#define IER(dev) (DEV_CFG(dev)->devconf.port + REG_IER * UART_REG_ADDR_INTERVAL) -#define IIR(dev) (DEV_CFG(dev)->devconf.port + REG_IIR * UART_REG_ADDR_INTERVAL) -#define FCR(dev) (DEV_CFG(dev)->devconf.port + REG_FCR * UART_REG_ADDR_INTERVAL) -#define LCR(dev) (DEV_CFG(dev)->devconf.port + REG_LCR * UART_REG_ADDR_INTERVAL) -#define MDC(dev) (DEV_CFG(dev)->devconf.port + REG_MDC * UART_REG_ADDR_INTERVAL) -#define LSR(dev) (DEV_CFG(dev)->devconf.port + REG_LSR * UART_REG_ADDR_INTERVAL) -#define MSR(dev) (DEV_CFG(dev)->devconf.port + REG_MSR * UART_REG_ADDR_INTERVAL) -#define DLF(dev) (DEV_CFG(dev)->devconf.port + REG_DLF) -#define PCP(dev) (DEV_CFG(dev)->devconf.port + REG_PCP) + (get_port(dev) + REG_BRDH * UART_REG_ADDR_INTERVAL) +#define IER(dev) (get_port(dev) + REG_IER * UART_REG_ADDR_INTERVAL) +#define IIR(dev) (get_port(dev) + REG_IIR * UART_REG_ADDR_INTERVAL) +#define FCR(dev) (get_port(dev) + REG_FCR * UART_REG_ADDR_INTERVAL) +#define LCR(dev) (get_port(dev) + REG_LCR * UART_REG_ADDR_INTERVAL) +#define MDC(dev) (get_port(dev) + REG_MDC * UART_REG_ADDR_INTERVAL) +#define LSR(dev) (get_port(dev) + REG_LSR * UART_REG_ADDR_INTERVAL) +#define MSR(dev) (get_port(dev) + REG_MSR * UART_REG_ADDR_INTERVAL) +#define DLF(dev) (get_port(dev) + REG_DLF) +#define PCP(dev) (get_port(dev) + REG_PCP) #define IIRC(dev) (DEV_DATA(dev)->iir_cache) @@ -257,6 +257,7 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "NS16550(s) in DT need CONFIG_PCIE"); /* device config */ struct uart_ns16550_device_config { struct uart_device_config devconf; + #ifdef UART_NS16550_PCP_ENABLED u32_t pcp; #endif @@ -270,6 +271,9 @@ struct uart_ns16550_device_config { /** Device data structure */ struct uart_ns16550_dev_data_t { +#ifdef UART_NS16550_PCIE_ENABLED + struct uart_device_config devconf; +#endif struct uart_config uart_config; struct k_spinlock lock; @@ -286,6 +290,17 @@ struct uart_ns16550_dev_data_t { static const struct uart_driver_api uart_ns16550_driver_api; +static inline u32_t get_port(struct device *dev) +{ +#ifdef UART_NS16550_PCIE_ENABLED + if (DEV_CFG(dev)->pcie) { + return DEV_DATA(dev)->devconf.port; + } +#endif /* UART_NS16550_PCIE_ENABLED */ + + return DEV_CFG(dev)->devconf.port; +} + static void set_baud_rate(struct device *dev, u32_t baud_rate) { const struct uart_ns16550_device_config * const dev_cfg = DEV_CFG(dev); @@ -337,7 +352,7 @@ static int uart_ns16550_configure(struct device *dev, goto out; } - dev_cfg->devconf.port = pcie_get_mbar(dev_cfg->pcie_bdf, 0); + dev_data->devconf.port = pcie_get_mbar(dev_cfg->pcie_bdf, 0); pcie_set_cmd(dev_cfg->pcie_bdf, PCIE_CONF_CMDSTAT_MEM, true); } #endif