diff options
Diffstat (limited to 'drivers/serial/serial_ns16550.c')
-rw-r--r-- | drivers/serial/serial_ns16550.c | 185 |
1 files changed, 115 insertions, 70 deletions
diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index f7cab8db4b..1b1692658f 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -40,6 +40,10 @@ struct ns16550_priv { void (*write_reg)(struct ns16550_priv *, uint8_t val, unsigned offset); uint8_t (*read_reg)(struct ns16550_priv *, unsigned offset); const char *access_type; + + bool rs485_mode; + bool rs485_rts_active_low; + bool rs485_rx_during_tx; }; struct ns16550_drvdata { @@ -242,14 +246,10 @@ static void ns16550_jz_init_port(struct console_device *cdev) ns16550_serial_init_port(cdev); } -#define BCM2836_AUX_CLOCK_ENB 0x3f215004 /* BCM2835 AUX Clock enable register */ -#define BCM2836_AUX_CLOCK_EN_UART BIT(0) /* Bit 0 enables the Miniuart */ - static void rpi_init_port(struct console_device *cdev) { struct ns16550_priv *priv = to_ns16550_priv(cdev); - writeb(BCM2836_AUX_CLOCK_EN_UART, BCM2836_AUX_CLOCK_ENB); priv->plat.shift = 2; /* * We double the clock rate since the 16550 will divide by 16 @@ -269,9 +269,37 @@ static void rpi_init_port(struct console_device *cdev) */ static void ns16550_putc(struct console_device *cdev, char c) { - /* Loop Doing Nothing */ - while ((ns16550_read(cdev, lsr) & LSR_THRE) == 0) ; + struct ns16550_priv *priv = to_ns16550_priv(cdev); + + /* wait until FIFO can accept at least one byte */ + while ((ns16550_read(cdev, lsr) & (LSR_THRE)) != (LSR_THRE)) + ; + + if (priv->rs485_mode) { + if (priv->rs485_rts_active_low) + ns16550_write(cdev, MCR_RTS, mcr); + else + ns16550_write(cdev, 0, mcr); + + if (!priv->rs485_rx_during_tx) + ns16550_write(cdev, CNTL_TXEN, cntl); + } + ns16550_write(cdev, c, thr); + + if (priv->rs485_mode) { + /* wait until FIFO is cleared*/ + while ((ns16550_read(cdev, lsr) & (LSR_EMPTY)) != (LSR_EMPTY)) + ; + + if (priv->rs485_rts_active_low) + ns16550_write(cdev, 0, mcr); + else + ns16550_write(cdev, MCR_RTS, mcr); + + if (!priv->rs485_rx_during_tx) + ns16550_write(cdev, CNTL_TXEN | CNTL_RXEN, cntl); + } } /** @@ -311,13 +339,13 @@ static void ns16550_flush(struct console_device *cdev) while ((ns16550_read(cdev, lsr) & LSR_TEMT) == 0) ; } -static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) +static void ns16550_probe_dt(struct device *dev, struct ns16550_priv *priv) { - struct device_node *np = dev->device_node; + struct device_node *np = dev_of_node(dev); u32 offset; u32 width = 1; - if (!IS_ENABLED(CONFIG_OFDEVICE)) + if (!np) return; of_property_read_u32(np, "clock-frequency", &priv->plat.clock); @@ -325,6 +353,12 @@ static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) priv->mmiobase += offset; of_property_read_u32(np, "reg-shift", &priv->plat.shift); of_property_read_u32(np, "reg-io-width", &width); + priv->rs485_rts_active_low = + of_property_read_bool(np, "rs485-rts-active-low"); + priv->rs485_mode = + of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"); + priv->rs485_rx_during_tx = + of_property_read_bool(np, "rs485-rx-during-tx"); switch (width) { case 1: @@ -372,7 +406,7 @@ static __maybe_unused struct ns16550_drvdata omap_drvdata = { .linux_earlycon_name = "omap8250", }; -static __maybe_unused struct ns16550_drvdata am43xx_drvdata = { +static __maybe_unused struct ns16550_drvdata omap_clk48m_drvdata = { .init_port = ns16550_omap_init_port, .linux_console_name = "ttyO", .clk_default = 48000000, @@ -389,23 +423,51 @@ static __maybe_unused struct ns16550_drvdata rpi_drvdata = { .linux_earlycon_name = "bcm2835aux", }; -static int ns16550_init_iomem(struct device_d *dev, struct ns16550_priv *priv) +/** + * @return the requested resource to be properly released in case probe fail + */ +static struct resource *ns16550_init_iores(struct device *dev, struct ns16550_priv *priv) { - struct resource *iores; struct resource *res; - int width; + struct resource *iores; + unsigned long flags; res = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(res)) - return PTR_ERR(res); + res = dev_get_resource(dev, IORESOURCE_IO, 0); + if (IS_ERR(res)) + return res; + + flags = res->flags & (IORESOURCE_MEM_TYPE_MASK | IORESOURCE_IO); - iores = dev_request_mem_resource(dev, 0); + if (flags & IORESOURCE_IO) + iores = request_ioport_region(dev_name(dev), res->start, res->end); + else + iores = request_iomem_region(dev_name(dev), res->start, res->end); if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->mmiobase = IOMEM(iores->start); + return iores; - width = res->flags & IORESOURCE_MEM_TYPE_MASK; - switch (width) { + if (flags & IORESOURCE_IO) + priv->iobase = iores->start; + else + priv->mmiobase = IOMEM(iores->start); + + switch (flags) { + case IORESOURCE_IO | IORESOURCE_MEM_8BIT: + priv->read_reg = ns16550_read_reg_ioport_8; + priv->write_reg = ns16550_write_reg_ioport_8; + priv->access_type = "io"; + break; + case IORESOURCE_IO | IORESOURCE_MEM_16BIT: + priv->read_reg = ns16550_read_reg_ioport_16; + priv->write_reg = ns16550_write_reg_ioport_16; + priv->access_type = "io"; + break; + case IORESOURCE_IO | IORESOURCE_MEM_32BIT: + priv->read_reg = ns16550_read_reg_ioport_32; + priv->write_reg = ns16550_write_reg_ioport_32; + priv->access_type = "io"; + break; case IORESOURCE_MEM_8BIT: priv->read_reg = ns16550_read_reg_mmio_8; priv->write_reg = ns16550_write_reg_mmio_8; @@ -423,43 +485,7 @@ static int ns16550_init_iomem(struct device_d *dev, struct ns16550_priv *priv) break; } - return 0; -} - -static int ns16550_init_ioport(struct device_d *dev, struct ns16550_priv *priv) -{ - struct resource *res; - int width; - - res = dev_get_resource(dev, IORESOURCE_IO, 0); - if (IS_ERR(res)) - return PTR_ERR(res); - - res = request_ioport_region(dev_name(dev), res->start, res->end); - if (IS_ERR(res)) - return PTR_ERR(res); - - priv->iobase = res->start; - - width = res->flags & IORESOURCE_MEM_TYPE_MASK; - switch (width) { - case IORESOURCE_MEM_8BIT: - priv->read_reg = ns16550_read_reg_ioport_8; - priv->write_reg = ns16550_write_reg_ioport_8; - break; - case IORESOURCE_MEM_16BIT: - priv->read_reg = ns16550_read_reg_ioport_16; - priv->write_reg = ns16550_write_reg_ioport_16; - break; - case IORESOURCE_MEM_32BIT: - priv->read_reg = ns16550_read_reg_ioport_32; - priv->write_reg = ns16550_write_reg_ioport_32; - break; - } - - priv->access_type = "io"; - - return 0; + return iores; } /** @@ -471,24 +497,24 @@ static int ns16550_init_ioport(struct device_d *dev, struct ns16550_priv *priv) * ENOMEM if calloc failed * else return result of console_register */ -static int ns16550_probe(struct device_d *dev) +static int ns16550_probe(struct device *dev) { struct ns16550_priv *priv; struct console_device *cdev; struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; const struct ns16550_drvdata *devtype; + struct resource *iores; int ret; devtype = device_get_match_data(dev) ?: &ns16550_drvdata; priv = xzalloc(sizeof(*priv)); - ret = ns16550_init_iomem(dev, priv); - if (ret) - ret = ns16550_init_ioport(dev, priv); - - if (ret) - return ret; + iores = ns16550_init_iores(dev, priv); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto err; + } if (plat) priv->plat = *plat; @@ -503,16 +529,18 @@ static int ns16550_probe(struct device_d *dev) if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); dev_err(dev, "failed to get clk (%d)\n", ret); - goto err; + goto release_region; } - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + goto clk_put; priv->plat.clock = clk_get_rate(priv->clk); } if (priv->plat.clock == 0) { dev_err(dev, "no valid clockrate\n"); ret = -EINVAL; - goto err; + goto clk_disable; } cdev = &priv->cdev; @@ -532,8 +560,18 @@ static int ns16550_probe(struct device_d *dev) devtype->init_port(cdev); - return console_register(cdev); + ret = console_register(cdev); + if (ret) + goto clk_disable; + return 0; + +clk_disable: + clk_disable(priv->clk); +clk_put: + clk_put(priv->clk); +release_region: + release_region(iores); err: free(priv); @@ -553,6 +591,12 @@ static struct of_device_id ns16550_serial_dt_ids[] = { }, { .compatible = "nvidia,tegra20-uart", }, +#if IS_ENABLED(CONFIG_ARCH_K3) + { + .compatible = "ti,am654-uart", + .data = &omap_clk48m_drvdata, + }, +#endif #if IS_ENABLED(CONFIG_ARCH_OMAP) { .compatible = "ti,omap2-uart", @@ -565,7 +609,7 @@ static struct of_device_id ns16550_serial_dt_ids[] = { .data = &omap_drvdata, }, { .compatible = "ti,am4372-uart", - .data = &am43xx_drvdata, + .data = &omap_clk48m_drvdata, }, #endif #if IS_ENABLED(CONFIG_MACH_MIPS_XBURST) @@ -584,11 +628,12 @@ static struct of_device_id ns16550_serial_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, ns16550_serial_dt_ids); static __maybe_unused struct platform_device_id ns16550_serial_ids[] = { { .name = "omap-uart", - .driver_data = (unsigned long)&omap_drvdata, + .driver_data = (unsigned long)&omap_clk48m_drvdata, }, { /* sentinel */ }, @@ -597,7 +642,7 @@ static __maybe_unused struct platform_device_id ns16550_serial_ids[] = { /** * @brief Driver registration structure */ -static struct driver_d ns16550_serial_driver = { +static struct driver ns16550_serial_driver = { .name = "ns16550_serial", .probe = ns16550_probe, .of_compatible = DRV_OF_COMPAT(ns16550_serial_dt_ids), |