diff options
Diffstat (limited to 'drivers/tty/serial/8250/8250_dw.c')
| -rw-r--r-- | drivers/tty/serial/8250/8250_dw.c | 634 |
1 files changed, 402 insertions, 232 deletions
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index aab3cccc6789..27af83f0ff46 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -9,40 +9,70 @@ * LCR is written whilst busy. If it is, then a busy detect interrupt is * raised, the LCR needs to be rewritten and the uart status register read. */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/serial_8250.h> -#include <linux/serial_reg.h> -#include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> +#include <linux/notifier.h> #include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/acpi.h> -#include <linux/clk.h> -#include <linux/reset.h> #include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/workqueue.h> #include <asm/byteorder.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> + #include "8250_dwlib.h" /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_DMASA 0xa8 /* DMA Software Ack */ + +#define OCTEON_UART_USR 0x27 /* UART Status Register */ + +#define RZN1_UART_TDMACR 0x10c /* DMA Control Register Transmit Mode */ +#define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */ /* DesignWare specific register fields */ #define DW_UART_MCR_SIRE BIT(6) +/* Renesas specific register fields */ +#define RZN1_UART_xDMACR_DMA_EN BIT(0) +#define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1) +#define RZN1_UART_xDMACR_4_WORD_BURST (1 << 1) +#define RZN1_UART_xDMACR_8_WORD_BURST (2 << 1) +#define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3) + +/* Quirks */ +#define DW_UART_QUIRK_OCTEON BIT(0) +#define DW_UART_QUIRK_ARMADA_38X BIT(1) +#define DW_UART_QUIRK_SKIP_SET_RATE BIT(2) +#define DW_UART_QUIRK_IS_DMA_FC BIT(3) +#define DW_UART_QUIRK_APMC0D08 BIT(4) +#define DW_UART_QUIRK_CPR_VALUE BIT(5) + +struct dw8250_platform_data { + u8 usr_reg; + u32 cpr_value; + unsigned int quirks; +}; + struct dw8250_data { struct dw8250_port_data data; + const struct dw8250_platform_data *pdata; - u8 usr_reg; - int msr_mask_on; - int msr_mask_off; + u32 msr_mask_on; + u32 msr_mask_off; struct clk *clk; struct clk *pclk; + struct notifier_block clk_notifier; + struct work_struct clk_work; struct reset_control *rst; unsigned int skip_autocfg:1; @@ -54,7 +84,17 @@ static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) return container_of(data, struct dw8250_data, data); } -static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) +static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) +{ + return container_of(nb, struct dw8250_data, clk_notifier); +} + +static inline struct dw8250_data *work_to_dw8250_data(struct work_struct *work) +{ + return container_of(work, struct dw8250_data, clk_work); +} + +static inline u32 dw8250_modify_msr(struct uart_port *p, unsigned int offset, u32 value) { struct dw8250_data *d = to_dw8250_data(p->private_data); @@ -67,22 +107,56 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) return value; } +/* + * This function is being called as part of the uart_port::serial_out() + * routine. Hence, it must not call serial_port_out() or serial_out() + * against the modified registers here, i.e. LCR. + */ static void dw8250_force_idle(struct uart_port *p) { struct uart_8250_port *up = up_to_u8250p(p); + unsigned int lsr; + /* + * The following call currently performs serial_out() + * against the FCR register. Because it differs to LCR + * there will be no infinite loop, but if it ever gets + * modified, we might need a new custom version of it + * that avoids infinite recursion. + */ serial8250_clear_and_reinit_fifos(up); - (void)p->serial_in(p, UART_RX); + + /* + * With PSLVERR_RESP_EN parameter set to 1, the device generates an + * error response when an attempt to read an empty RBR with FIFO + * enabled. + */ + if (up->fcr & UART_FCR_ENABLE_FIFO) { + lsr = serial_port_in(p, UART_LSR); + if (!(lsr & UART_LSR_DR)) + return; + } + + serial_port_in(p, UART_RX); } -static void dw8250_check_lcr(struct uart_port *p, int value) +/* + * This function is being called as part of the uart_port::serial_out() + * routine. Hence, it must not call serial_port_out() or serial_out() + * against the modified registers here, i.e. LCR. + */ +static void dw8250_check_lcr(struct uart_port *p, unsigned int offset, u32 value) { - void __iomem *offset = p->membase + (UART_LCR << p->regshift); + struct dw8250_data *d = to_dw8250_data(p->private_data); + void __iomem *addr = p->membase + (offset << p->regshift); int tries = 1000; + if (offset != UART_LCR || d->uart_16550_compatible) + return; + /* Make sure LCR write wasn't ignored */ while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); + u32 lcr = serial_port_in(p, offset); if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) return; @@ -91,15 +165,15 @@ static void dw8250_check_lcr(struct uart_port *p, int value) #ifdef CONFIG_64BIT if (p->type == PORT_OCTEON) - __raw_writeq(value & 0xff, offset); + __raw_writeq(value & 0xff, addr); else #endif if (p->iotype == UPIO_MEM32) - writel(value, offset); + writel(value, addr); else if (p->iotype == UPIO_MEM32BE) - iowrite32be(value, offset); + iowrite32be(value, addr); else - writeb(value, offset); + writeb(value, addr); } /* * FIXME: this deadlocks if port->lock is already held @@ -110,12 +184,15 @@ static void dw8250_check_lcr(struct uart_port *p, int value) /* Returns once the transmitter is empty or we run out of retries */ static void dw8250_tx_wait_empty(struct uart_port *p) { + struct uart_8250_port *up = up_to_u8250p(p); unsigned int tries = 20000; unsigned int delay_threshold = tries - 1000; unsigned int lsr; while (tries--) { lsr = readb (p->membase + (UART_LSR << p->regshift)); + up->lsr_saved_flags |= lsr & up->lsr_save_mask; + if (lsr & UART_LSR_TEMT) break; @@ -128,92 +205,69 @@ static void dw8250_tx_wait_empty(struct uart_port *p) } } -static void dw8250_serial_out38x(struct uart_port *p, int offset, int value) +static void dw8250_serial_out(struct uart_port *p, unsigned int offset, u32 value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - - /* Allow the TX to drain before we reconfigure */ - if (offset == UART_LCR) - dw8250_tx_wait_empty(p); - writeb(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } - -static void dw8250_serial_out(struct uart_port *p, int offset, int value) +static void dw8250_serial_out38x(struct uart_port *p, unsigned int offset, u32 value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - - writeb(value, p->membase + (offset << p->regshift)); + /* Allow the TX to drain before we reconfigure */ + if (offset == UART_LCR) + dw8250_tx_wait_empty(p); - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_serial_out(p, offset, value); } -static unsigned int dw8250_serial_in(struct uart_port *p, int offset) +static u32 dw8250_serial_in(struct uart_port *p, unsigned int offset) { - unsigned int value = readb(p->membase + (offset << p->regshift)); + u32 value = readb(p->membase + (offset << p->regshift)); return dw8250_modify_msr(p, offset, value); } #ifdef CONFIG_64BIT -static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) +static u32 dw8250_serial_inq(struct uart_port *p, unsigned int offset) { - unsigned int value; - - value = (u8)__raw_readq(p->membase + (offset << p->regshift)); + u8 value = __raw_readq(p->membase + (offset << p->regshift)); return dw8250_modify_msr(p, offset, value); } -static void dw8250_serial_outq(struct uart_port *p, int offset, int value) +static void dw8250_serial_outq(struct uart_port *p, unsigned int offset, u32 value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - value &= 0xff; __raw_writeq(value, p->membase + (offset << p->regshift)); /* Read back to ensure register write ordering. */ __raw_readq(p->membase + (UART_LCR << p->regshift)); - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } #endif /* CONFIG_64BIT */ -static void dw8250_serial_out32(struct uart_port *p, int offset, int value) +static void dw8250_serial_out32(struct uart_port *p, unsigned int offset, u32 value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - writel(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } -static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) +static u32 dw8250_serial_in32(struct uart_port *p, unsigned int offset) { - unsigned int value = readl(p->membase + (offset << p->regshift)); + u32 value = readl(p->membase + (offset << p->regshift)); return dw8250_modify_msr(p, offset, value); } -static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) +static void dw8250_serial_out32be(struct uart_port *p, unsigned int offset, u32 value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - iowrite32be(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } -static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) +static u32 dw8250_serial_in32be(struct uart_port *p, unsigned int offset) { - unsigned int value = ioread32be(p->membase + (offset << p->regshift)); + u32 value = ioread32be(p->membase + (offset << p->regshift)); return dw8250_modify_msr(p, offset, value); } @@ -223,7 +277,9 @@ static int dw8250_handle_irq(struct uart_port *p) { struct uart_8250_port *up = up_to_u8250p(p); struct dw8250_data *d = to_dw8250_data(p->private_data); - unsigned int iir = p->serial_in(p, UART_IIR); + unsigned int iir = serial_port_in(p, UART_IIR); + bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT; + unsigned int quirks = d->pdata->quirks; unsigned int status; unsigned long flags; @@ -237,14 +293,26 @@ static int dw8250_handle_irq(struct uart_port *p) * This problem has only been observed so far when not in DMA mode * so we limit the workaround only to non-DMA mode. */ - if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) { - spin_lock_irqsave(&p->lock, flags); - status = p->serial_in(p, UART_LSR); + if (!up->dma && rx_timeout) { + uart_port_lock_irqsave(p, &flags); + status = serial_lsr_in(up); if (!(status & (UART_LSR_DR | UART_LSR_BI))) - (void) p->serial_in(p, UART_RX); + serial_port_in(p, UART_RX); + + uart_port_unlock_irqrestore(p, flags); + } + + /* Manually stop the Rx DMA transfer when acting as flow controller */ + if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) { + uart_port_lock_irqsave(p, &flags); + status = serial_lsr_in(up); + uart_port_unlock_irqrestore(p, flags); - spin_unlock_irqrestore(&p->lock, flags); + if (status & (UART_LSR_DR | UART_LSR_BI)) { + dw8250_writel_ext(p, RZN1_UART_RDMACR, 0); + dw8250_writel_ext(p, DW_UART_DMASA, 1); + } } if (serial8250_handle_irq(p, iir)) @@ -252,7 +320,7 @@ static int dw8250_handle_irq(struct uart_port *p) if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR */ - (void)p->serial_in(p, d->usr_reg); + serial_port_in(p, d->pdata->usr_reg); return 1; } @@ -260,6 +328,46 @@ static int dw8250_handle_irq(struct uart_port *p) return 0; } +static void dw8250_clk_work_cb(struct work_struct *work) +{ + struct dw8250_data *d = work_to_dw8250_data(work); + struct uart_8250_port *up; + unsigned long rate; + + rate = clk_get_rate(d->clk); + if (rate <= 0) + return; + + up = serial8250_get_port(d->data.line); + + serial8250_update_uartclk(&up->port, rate); +} + +static int dw8250_clk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct dw8250_data *d = clk_to_dw8250_data(nb); + + /* + * We have no choice but to defer the uartclk update due to two + * deadlocks. First one is caused by a recursive mutex lock which + * happens when clk_set_rate() is called from dw8250_set_termios(). + * Second deadlock is more tricky and is caused by an inverted order of + * the clk and tty-port mutexes lock. It happens if clock rate change + * is requested asynchronously while set_termios() is executed between + * tty-port mutex lock and clk_set_rate() function invocation and + * vise-versa. Anyway if we didn't have the reference clock alteration + * in the dw8250_set_termios() method we wouldn't have needed this + * deferred event handling complication. + */ + if (event == POST_RATE_CHANGE) { + queue_work(system_dfl_wq, &d->clk_work); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + static void dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) { @@ -273,40 +381,33 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) } static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, - struct ktermios *old) + const struct ktermios *old) { - unsigned int baud = tty_termios_baud_rate(termios); + unsigned long newrate = tty_termios_baud_rate(termios) * 16; struct dw8250_data *d = to_dw8250_data(p->private_data); long rate; int ret; clk_disable_unprepare(d->clk); - rate = clk_round_rate(d->clk, baud * 16); - if (rate < 0) - ret = rate; - else if (rate == 0) - ret = -ENOENT; - else - ret = clk_set_rate(d->clk, rate); + rate = clk_round_rate(d->clk, newrate); + if (rate > 0) { + /* + * Note that any clock-notifier worker will block in + * serial8250_update_uartclk() until we are done. + */ + ret = clk_set_rate(d->clk, newrate); + if (!ret) + p->uartclk = rate; + } clk_prepare_enable(d->clk); - if (ret) - goto out; - - p->uartclk = rate; - -out: - p->status &= ~UPSTAT_AUTOCTS; - if (termios->c_cflag & CRTSCTS) - p->status |= UPSTAT_AUTOCTS; - - serial8250_do_set_termios(p, termios, old); + dw8250_do_set_termios(p, termios, old); } static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) { struct uart_8250_port *up = up_to_u8250p(p); - unsigned int mcr = p->serial_in(p, UART_MCR); + unsigned int mcr = serial_port_in(p, UART_MCR); if (up->capabilities & UART_CAP_IRDA) { if (termios->c_line == N_IRDA) @@ -314,7 +415,7 @@ static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) else mcr &= ~DW_UART_MCR_SIRE; - p->serial_out(p, UART_MCR, mcr); + serial_port_out(p, UART_MCR, mcr); } serial8250_do_set_ldisc(p, termios); } @@ -337,108 +438,151 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param) return param == chan->device->dev; } +static void dw8250_setup_dma_filter(struct uart_port *p, struct dw8250_data *data) +{ + /* Platforms with iDMA 64-bit */ + if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) { + data->data.dma.rx_param = p->dev->parent; + data->data.dma.tx_param = p->dev->parent; + data->data.dma.fn = dw8250_idma_filter; + } else { + data->data.dma.fn = dw8250_fallback_dma_filter; + } +} + +static u32 dw8250_rzn1_get_dmacr_burst(int max_burst) +{ + if (max_burst >= 8) + return RZN1_UART_xDMACR_8_WORD_BURST; + else if (max_burst >= 4) + return RZN1_UART_xDMACR_4_WORD_BURST; + else + return RZN1_UART_xDMACR_1_WORD_BURST; +} + +static void dw8250_prepare_tx_dma(struct uart_8250_port *p) +{ + struct uart_port *up = &p->port; + struct uart_8250_dma *dma = p->dma; + u32 val; + + dw8250_writel_ext(up, RZN1_UART_TDMACR, 0); + val = dw8250_rzn1_get_dmacr_burst(dma->txconf.dst_maxburst) | + RZN1_UART_xDMACR_BLK_SZ(dma->tx_size) | + RZN1_UART_xDMACR_DMA_EN; + dw8250_writel_ext(up, RZN1_UART_TDMACR, val); +} + +static void dw8250_prepare_rx_dma(struct uart_8250_port *p) +{ + struct uart_port *up = &p->port; + struct uart_8250_dma *dma = p->dma; + u32 val; + + dw8250_writel_ext(up, RZN1_UART_RDMACR, 0); + val = dw8250_rzn1_get_dmacr_burst(dma->rxconf.src_maxburst) | + RZN1_UART_xDMACR_BLK_SZ(dma->rx_size) | + RZN1_UART_xDMACR_DMA_EN; + dw8250_writel_ext(up, RZN1_UART_RDMACR, val); +} + static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { - if (p->dev->of_node) { - struct device_node *np = p->dev->of_node; - int id; + unsigned int quirks = data->pdata->quirks; + u32 cpr_value = data->pdata->cpr_value; + + if (quirks & DW_UART_QUIRK_CPR_VALUE) + data->data.cpr_value = cpr_value; - /* get index of serial line, if found in DT aliases */ - id = of_alias_get_id(np, "serial"); - if (id >= 0) - p->line = id; #ifdef CONFIG_64BIT - if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { - p->serial_in = dw8250_serial_inq; - p->serial_out = dw8250_serial_outq; - p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; - p->type = PORT_OCTEON; - data->usr_reg = 0x27; - data->skip_autocfg = true; - } + if (quirks & DW_UART_QUIRK_OCTEON) { + p->serial_in = dw8250_serial_inq; + p->serial_out = dw8250_serial_outq; + p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->skip_autocfg = true; + } #endif - if (of_device_is_big_endian(p->dev->of_node)) { - p->iotype = UPIO_MEM32BE; - p->serial_in = dw8250_serial_in32be; - p->serial_out = dw8250_serial_out32be; - } - if (of_device_is_compatible(np, "marvell,armada-38x-uart")) - p->serial_out = dw8250_serial_out38x; - } else if (acpi_dev_present("APMC0D08", NULL, -1)) { + if (quirks & DW_UART_QUIRK_ARMADA_38X) + p->serial_out = dw8250_serial_out38x; + if (quirks & DW_UART_QUIRK_SKIP_SET_RATE) + p->set_termios = dw8250_do_set_termios; + if (quirks & DW_UART_QUIRK_IS_DMA_FC) { + data->data.dma.txconf.device_fc = 1; + data->data.dma.rxconf.device_fc = 1; + data->data.dma.prepare_tx_dma = dw8250_prepare_tx_dma; + data->data.dma.prepare_rx_dma = dw8250_prepare_rx_dma; + } + if (quirks & DW_UART_QUIRK_APMC0D08) { p->iotype = UPIO_MEM32; p->regshift = 2; p->serial_in = dw8250_serial_in32; data->uart_16550_compatible = true; } +} - /* Platforms with iDMA 64-bit */ - if (platform_get_resource_byname(to_platform_device(p->dev), - IORESOURCE_MEM, "lpss_priv")) { - data->data.dma.rx_param = p->dev->parent; - data->data.dma.tx_param = p->dev->parent; - data->data.dma.fn = dw8250_idma_filter; - } +static void dw8250_reset_control_assert(void *data) +{ + reset_control_assert(data); } static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}, *up = &uart; - struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct uart_port *p = &up->port; struct device *dev = &pdev->dev; struct dw8250_data *data; - int irq; + struct resource *regs; int err; - u32 val; - if (!regs) { - dev_err(dev, "no registers defined\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "no registers defined\n"); spin_lock_init(&p->lock); - p->mapbase = regs->start; - p->irq = irq; - p->handle_irq = dw8250_handle_irq; p->pm = dw8250_do_pm; p->type = PORT_8250; - p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; + p->flags = UPF_FIXED_PORT; p->dev = dev; - p->iotype = UPIO_MEM; - p->serial_in = dw8250_serial_in; - p->serial_out = dw8250_serial_out; p->set_ldisc = dw8250_set_ldisc; p->set_termios = dw8250_set_termios; - p->membase = devm_ioremap(dev, regs->start, resource_size(regs)); - if (!p->membase) - return -ENOMEM; - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->data.dma.fn = dw8250_fallback_dma_filter; - data->usr_reg = DW_UART_USR; p->private_data = &data->data; - data->uart_16550_compatible = device_property_read_bool(dev, - "snps,uart-16550-compatible"); + p->mapbase = regs->start; + p->mapsize = resource_size(regs); - err = device_property_read_u32(dev, "reg-shift", &val); - if (!err) - p->regshift = val; + p->membase = devm_ioremap(dev, p->mapbase, p->mapsize); + if (!p->membase) + return -ENOMEM; - err = device_property_read_u32(dev, "reg-io-width", &val); - if (!err && val == 4) { - p->iotype = UPIO_MEM32; + err = uart_read_port_properties(p); + /* no interrupt -> fall back to polling */ + if (err == -ENXIO) + err = 0; + if (err) + return err; + + switch (p->iotype) { + case UPIO_MEM: + p->serial_in = dw8250_serial_in; + p->serial_out = dw8250_serial_out; + break; + case UPIO_MEM32: p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; + break; + case UPIO_MEM32BE: + p->serial_in = dw8250_serial_in32be; + p->serial_out = dw8250_serial_out32be; + break; + default: + return -ENODEV; } if (device_property_read_bool(dev, "dcd-override")) { @@ -465,54 +609,53 @@ static int dw8250_probe(struct platform_device *pdev) data->msr_mask_off |= UART_MSR_TERI; } - /* Always ask for fixed clock rate from a property. */ - device_property_read_u32(dev, "clock-frequency", &p->uartclk); - /* If there is separate baudclk, get the rate from it. */ - data->clk = devm_clk_get_optional(dev, "baudclk"); + data->clk = devm_clk_get_optional_enabled(dev, "baudclk"); if (data->clk == NULL) - data->clk = devm_clk_get_optional(dev, NULL); + data->clk = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(data->clk)) - return PTR_ERR(data->clk); + return dev_err_probe(dev, PTR_ERR(data->clk), + "failed to get baudclk\n"); - err = clk_prepare_enable(data->clk); - if (err) - dev_warn(dev, "could not enable optional baudclk: %d\n", err); + INIT_WORK(&data->clk_work, dw8250_clk_work_cb); + data->clk_notifier.notifier_call = dw8250_clk_notifier_cb; if (data->clk) p->uartclk = clk_get_rate(data->clk); /* If no clock rate is defined, fail. */ - if (!p->uartclk) { - dev_err(dev, "clock rate not defined\n"); - err = -EINVAL; - goto err_clk; - } + if (!p->uartclk) + return dev_err_probe(dev, -EINVAL, "clock rate not defined\n"); - data->pclk = devm_clk_get_optional(dev, "apb_pclk"); - if (IS_ERR(data->pclk)) { - err = PTR_ERR(data->pclk); - goto err_clk; - } + data->pclk = devm_clk_get_optional_enabled(dev, "apb_pclk"); + if (IS_ERR(data->pclk)) + return PTR_ERR(data->pclk); - err = clk_prepare_enable(data->pclk); - if (err) { - dev_err(dev, "could not enable apb_pclk\n"); - goto err_clk; - } + data->rst = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(data->rst)) + return PTR_ERR(data->rst); - data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(data->rst)) { - err = PTR_ERR(data->rst); - goto err_pclk; - } - reset_control_deassert(data->rst); + err = reset_control_deassert(data->rst); + if (err) + return dev_err_probe(dev, err, "failed to deassert resets\n"); + + err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst); + if (err) + return err; + + data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible"); - dw8250_quirks(p, data); + data->pdata = device_get_match_data(p->dev); + if (data->pdata) + dw8250_quirks(p, data); /* If the Busy Functionality is not implemented, don't handle it */ if (data->uart_16550_compatible) p->handle_irq = NULL; + else if (data->pdata) + p->handle_irq = dw8250_handle_irq; + + dw8250_setup_dma_filter(p, data); if (!data->skip_autocfg) dw8250_setup_port(p); @@ -525,9 +668,19 @@ static int dw8250_probe(struct platform_device *pdev) } data->data.line = serial8250_register_8250_port(up); - if (data->data.line < 0) { - err = data->data.line; - goto err_reset; + if (data->data.line < 0) + return data->data.line; + + /* + * Some platforms may provide a reference clock shared between several + * devices. In this case any clock state change must be known to the + * UART port at least post factum. + */ + if (data->clk) { + err = clk_notifier_register(data->clk, &data->clk_notifier); + if (err) + return dev_err_probe(dev, err, "Failed to set the clock notifier\n"); + queue_work(system_dfl_wq, &data->clk_work); } platform_set_drvdata(pdev, data); @@ -536,41 +689,27 @@ static int dw8250_probe(struct platform_device *pdev) pm_runtime_enable(dev); return 0; - -err_reset: - reset_control_assert(data->rst); - -err_pclk: - clk_disable_unprepare(data->pclk); - -err_clk: - clk_disable_unprepare(data->clk); - - return err; } -static int dw8250_remove(struct platform_device *pdev) +static void dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; pm_runtime_get_sync(dev); - serial8250_unregister_port(data->data.line); - - reset_control_assert(data->rst); + if (data->clk) { + clk_notifier_unregister(data->clk, &data->clk_notifier); - clk_disable_unprepare(data->pclk); + flush_work(&data->clk_work); + } - clk_disable_unprepare(data->clk); + serial8250_unregister_port(data->data.line); pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - - return 0; } -#ifdef CONFIG_PM_SLEEP static int dw8250_suspend(struct device *dev) { struct dw8250_data *data = dev_get_drvdata(dev); @@ -588,9 +727,7 @@ static int dw8250_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM static int dw8250_runtime_suspend(struct device *dev) { struct dw8250_data *data = dev_get_drvdata(dev); @@ -612,34 +749,67 @@ static int dw8250_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops dw8250_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) - SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) + RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) +}; + +static const struct dw8250_platform_data dw8250_dw_apb = { + .usr_reg = DW_UART_USR, +}; + +static const struct dw8250_platform_data dw8250_octeon_3860_data = { + .usr_reg = OCTEON_UART_USR, + .quirks = DW_UART_QUIRK_OCTEON, +}; + +static const struct dw8250_platform_data dw8250_armada_38x_data = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_ARMADA_38X, +}; + +static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { + .usr_reg = DW_UART_USR, + .cpr_value = 0x00012f32, + .quirks = DW_UART_QUIRK_CPR_VALUE | DW_UART_QUIRK_IS_DMA_FC, +}; + +static const struct dw8250_platform_data dw8250_skip_set_rate_data = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_SKIP_SET_RATE, }; static const struct of_device_id dw8250_of_match[] = { - { .compatible = "snps,dw-apb-uart" }, - { .compatible = "cavium,octeon-3860-uart" }, - { .compatible = "marvell,armada-38x-uart" }, - { .compatible = "renesas,rzn1-uart" }, + { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb }, + { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, + { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data }, + { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, + { .compatible = "sophgo,sg2044-uart", .data = &dw8250_skip_set_rate_data }, + { .compatible = "starfive,jh7100-uart", .data = &dw8250_skip_set_rate_data }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); +static const struct dw8250_platform_data dw8250_apmc0d08 = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_APMC0D08, +}; + static const struct acpi_device_id dw8250_acpi_match[] = { - { "INT33C4", 0 }, - { "INT33C5", 0 }, - { "INT3434", 0 }, - { "INT3435", 0 }, - { "80860F0A", 0 }, - { "8086228A", 0 }, - { "APMC0D08", 0}, - { "AMD0020", 0 }, - { "AMDI0020", 0 }, - { "BRCM2032", 0 }, - { "HISI0031", 0 }, + { "80860F0A", (kernel_ulong_t)&dw8250_dw_apb }, + { "8086228A", (kernel_ulong_t)&dw8250_dw_apb }, + { "AMD0020", (kernel_ulong_t)&dw8250_dw_apb }, + { "AMDI0020", (kernel_ulong_t)&dw8250_dw_apb }, + { "AMDI0022", (kernel_ulong_t)&dw8250_dw_apb }, + { "APMC0D08", (kernel_ulong_t)&dw8250_apmc0d08 }, + { "BRCM2032", (kernel_ulong_t)&dw8250_dw_apb }, + { "HISI0031", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT33C4", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT33C5", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT3434", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT3435", (kernel_ulong_t)&dw8250_dw_apb }, + { "INTC10EE", (kernel_ulong_t)&dw8250_dw_apb }, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); @@ -647,9 +817,9 @@ MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); static struct platform_driver dw8250_platform_driver = { .driver = { .name = "dw-apb-uart", - .pm = &dw8250_pm_ops, + .pm = pm_ptr(&dw8250_pm_ops), .of_match_table = dw8250_of_match, - .acpi_match_table = ACPI_PTR(dw8250_acpi_match), + .acpi_match_table = dw8250_acpi_match, }, .probe = dw8250_probe, .remove = dw8250_remove, |
