summaryrefslogtreecommitdiffstats
path: root/drivers/serial/serial_ns16550.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/serial/serial_ns16550.c')
-rw-r--r--drivers/serial/serial_ns16550.c185
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),