From 04dd17822334871b23ea2862f7798fb0e0007777 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Sat, 11 May 2024 08:53:19 +0000 Subject: [PATCH] change otg to host mode --- kernel/drivers/tty/serial/8250/8250_dw.c | 432 ++++++++++++++++++++++++----------------------------- 1 files changed, 199 insertions(+), 233 deletions(-) diff --git a/kernel/drivers/tty/serial/8250/8250_dw.c b/kernel/drivers/tty/serial/8250/8250_dw.c index 2882547..82a4f6d 100644 --- a/kernel/drivers/tty/serial/8250/8250_dw.c +++ b/kernel/drivers/tty/serial/8250/8250_dw.c @@ -19,6 +19,8 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/notifier.h> #include <linux/slab.h> #include <linux/acpi.h> #include <linux/clk.h> @@ -27,44 +29,31 @@ #include <asm/byteorder.h> -#include "8250.h" +#ifdef MODULE +#include "8250_dwlib.c" +#else +#include "8250_dwlib.h" +#endif /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ -#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ #define DW_UART_RFL 0x21 /* UART Receive Fifo Level Register */ -#define DW_UART_CPR 0xf4 /* Component Parameter Register */ -#define DW_UART_UCV 0xf8 /* UART Component Version */ - -/* Component Parameter Register bits */ -#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) -#define DW_UART_CPR_AFCE_MODE (1 << 4) -#define DW_UART_CPR_THRE_MODE (1 << 5) -#define DW_UART_CPR_SIR_MODE (1 << 6) -#define DW_UART_CPR_SIR_LP_MODE (1 << 7) -#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) -#define DW_UART_CPR_FIFO_ACCESS (1 << 9) -#define DW_UART_CPR_FIFO_STAT (1 << 10) -#define DW_UART_CPR_SHADOW (1 << 11) -#define DW_UART_CPR_ENCODED_PARMS (1 << 12) -#define DW_UART_CPR_DMA_EXTRA (1 << 13) -#define DW_UART_CPR_FIFO_MODE (0xff << 16) -/* Helper for fifo size calculation */ -#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) /* DesignWare specific register fields */ #define DW_UART_MCR_SIRE BIT(6) struct dw8250_data { + struct dw8250_port_data data; + u8 usr_reg; - u8 dlf_size; - int line; int msr_mask_on; int msr_mask_off; struct clk *clk; struct clk *pclk; + struct notifier_block clk_notifier; + struct work_struct clk_work; struct reset_control *rst; - struct uart_8250_dma dma; + #ifdef CONFIG_ARCH_ROCKCHIP int irq; int irq_wake; @@ -74,24 +63,24 @@ unsigned int uart_16550_compatible:1; }; -static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) +static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) { - if (p->iotype == UPIO_MEM32BE) - return ioread32be(p->membase + offset); - return readl(p->membase + offset); + return container_of(data, struct dw8250_data, data); } -static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) +static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) { - if (p->iotype == UPIO_MEM32BE) - iowrite32be(reg, p->membase + offset); - else - writel(reg, p->membase + offset); + 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 int dw8250_modify_msr(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); /* Override any modem control signals if needed */ if (offset == UART_MSR) { @@ -145,12 +134,15 @@ /* 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 & LSR_SAVE_FLAGS; + if (lsr & UART_LSR_TEMT) break; @@ -165,7 +157,7 @@ static void dw8250_serial_out38x(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); /* Allow the TX to drain before we reconfigure */ if (offset == UART_LCR) @@ -180,7 +172,7 @@ static void dw8250_serial_out(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); writeb(value, p->membase + (offset << p->regshift)); @@ -207,7 +199,7 @@ static void dw8250_serial_outq(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); value &= 0xff; __raw_writeq(value, p->membase + (offset << p->regshift)); @@ -221,7 +213,7 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); writel(value, p->membase + (offset << p->regshift)); @@ -238,7 +230,7 @@ static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); iowrite32be(value, p->membase + (offset << p->regshift)); @@ -256,7 +248,7 @@ static int dw8250_handle_irq(struct uart_port *p) { - struct dw8250_data *d = p->private_data; + struct dw8250_data *d = to_dw8250_data(p->private_data); unsigned int iir = p->serial_in(p, UART_IIR); unsigned int status, usr, rfl; unsigned long flags; @@ -292,6 +284,46 @@ 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_unbound_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) { @@ -307,61 +339,67 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old) { - unsigned int baud = tty_termios_baud_rate(termios); - struct dw8250_data *d = p->private_data; +#ifndef CONFIG_ARCH_ROCKCHIP + unsigned long newrate = tty_termios_baud_rate(termios) * 16; +#endif + struct dw8250_data *d = to_dw8250_data(p->private_data); long rate; #ifdef CONFIG_ARCH_ROCKCHIP + unsigned int baud = tty_termios_baud_rate(termios); unsigned int rate_temp, diff; #endif int ret; - if (IS_ERR(d->clk)) - goto out; - clk_disable_unprepare(d->clk); #ifdef CONFIG_ARCH_ROCKCHIP - if (baud <= 115200) - rate = 24000000; - else if (baud == 230400) - rate = baud * 16 * 2; - else if (baud == 1152000) - rate = baud * 16 * 2; - else - rate = baud * 16; + if (d->clk) { + if (baud <= 115200) + rate = 24000000; + else if (baud == 230400) + rate = baud * 16 * 2; + else if (baud == 1152000) + rate = baud * 16 * 2; + else + rate = baud * 16; - ret = clk_set_rate(d->clk, rate); - rate_temp = clk_get_rate(d->clk); - diff = rate * 20 / 1000; - /* - * If rate_temp is not equal to rate, is means fractional frequency - * division is failed. Then use Integer frequency division, and - * the buad rate error must be under -+2% - */ - if ((rate_temp < rate) && ((rate - rate_temp) > diff)) { - ret = clk_set_rate(d->clk, rate + diff); + ret = clk_set_rate(d->clk, rate); rate_temp = clk_get_rate(d->clk); - if ((rate_temp < rate) && ((rate - rate_temp) > diff)) - dev_info(p->dev, "set rate:%ld, but get rate:%d\n", - rate, rate_temp); - else if ((rate < rate_temp) && ((rate_temp - rate) > diff)) - dev_info(p->dev, "set rate:%ld, but get rate:%d\n", - rate, rate_temp); + diff = rate * 20 / 1000; + /* + * If rate_temp is not equal to rate, is means fractional frequency + * division is failed. Then use Integer frequency division, and + * the baud rate error must be under -+2% + */ + if ((rate_temp < rate) && ((rate - rate_temp) > diff)) { + ret = clk_set_rate(d->clk, rate + diff); + rate_temp = clk_get_rate(d->clk); + if ((rate_temp < rate) && ((rate - rate_temp) > diff)) + dev_info(p->dev, "set rate:%ld, but get rate:%d\n", + rate, rate_temp); + else if ((rate < rate_temp) && ((rate_temp - rate) > diff)) + dev_info(p->dev, "set rate:%ld, but get rate:%d\n", + rate, rate_temp); + } + if (!ret) + p->uartclk = rate; } #else - 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) { + /* + * Premilinary set the uartclk to the new clock rate so the + * clock update event handler caused by the clk_set_rate() + * calling wouldn't actually update the UART divisor since + * we about to do this anyway. + */ + swap(p->uartclk, rate); + ret = clk_set_rate(d->clk, newrate); + if (ret) + swap(p->uartclk, rate); + } #endif clk_prepare_enable(d->clk); - if (!ret) - p->uartclk = rate; - -out: p->status &= ~UPSTAT_AUTOCTS; if (termios->c_cflag & CRTSCTS) p->status |= UPSTAT_AUTOCTS; @@ -403,37 +441,6 @@ return param == chan->device->dev; } -/* - * divisor = div(I) + div(F) - * "I" means integer, "F" means fractional - * quot = div(I) = clk / (16 * baud) - * frac = div(F) * 2^dlf_size - * - * let rem = clk % (16 * baud) - * we have: div(F) * (16 * baud) = rem - * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) - */ -static unsigned int dw8250_get_divisor(struct uart_port *p, - unsigned int baud, - unsigned int *frac) -{ - unsigned int quot, rem, base_baud = baud * 16; - struct dw8250_data *d = p->private_data; - - quot = p->uartclk / base_baud; - rem = p->uartclk % base_baud; - *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); - - return quot; -} - -static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, - unsigned int quot, unsigned int quot_frac) -{ - dw8250_writel_ext(p, DW_UART_DLF, quot_frac); - serial8250_do_set_divisor(p, baud, quot, quot_frac); -} - static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { if (p->dev->of_node) { @@ -444,6 +451,10 @@ id = of_alias_get_id(np, "serial"); if (id >= 0) p->line = id; + + if (IS_ENABLED(CONFIG_ROCKCHIP_MINI_KERNEL)) + return; + #ifdef CONFIG_64BIT if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { p->serial_in = dw8250_serial_inq; @@ -469,82 +480,26 @@ data->uart_16550_compatible = true; } + if (IS_ENABLED(CONFIG_ROCKCHIP_MINI_KERNEL)) + return; + /* Platforms with iDMA 64-bit */ if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) { - data->dma.rx_param = p->dev->parent; - data->dma.tx_param = p->dev->parent; - data->dma.fn = dw8250_idma_filter; + 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_setup_port(struct uart_port *p) -{ - struct uart_8250_port *up = up_to_u8250p(p); - u32 reg; - - /* - * If the Component Version Register returns zero, we know that - * ADDITIONAL_FEATURES are not enabled. No need to go any further. - */ - reg = dw8250_readl_ext(p, DW_UART_UCV); - if (!reg) - return; - - dev_dbg(p->dev, "Designware UART version %c.%c%c\n", - (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - - dw8250_writel_ext(p, DW_UART_DLF, ~0U); - reg = dw8250_readl_ext(p, DW_UART_DLF); - dw8250_writel_ext(p, DW_UART_DLF, 0); - - if (reg) { - struct dw8250_data *d = p->private_data; - - d->dlf_size = fls(reg); - p->get_divisor = dw8250_get_divisor; - p->set_divisor = dw8250_set_divisor; - } - - reg = dw8250_readl_ext(p, DW_UART_CPR); - -#ifdef CONFIG_ARCH_ROCKCHIP - /* - * The UART CPR may be 0 of some rockchip soc, - * but it supports fifo and AFC, fifo entry is 32 default. - */ - if (reg == 0) - reg = 0x00023ff2; -#endif - if (!reg) - return; - - /* Select the type based on fifo */ - if (reg & DW_UART_CPR_FIFO_MODE) { - p->type = PORT_16550A; - p->flags |= UPF_FIXED_TYPE; - p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); -#ifdef CONFIG_ARCH_ROCKCHIP - up->tx_loadsz = p->fifosize * 3 / 4; -#endif - up->capabilities = UART_CAP_FIFO; - } - - if (reg & DW_UART_CPR_AFCE_MODE) - up->capabilities |= UART_CAP_AFE; - - if (reg & DW_UART_CPR_SIR_MODE) - up->capabilities |= UART_CAP_IRDA; } static int dw8250_probe(struct platform_device *pdev) { - struct uart_8250_port uart = {}; + struct uart_8250_port uart = {}, *up = &uart; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int irq = platform_get_irq(pdev, 0); - struct uart_port *p = &uart.port; + struct uart_port *p = &up->port; struct device *dev = &pdev->dev; struct dw8250_data *data; + int irq; int err; u32 val; @@ -553,11 +508,9 @@ return -EINVAL; } - if (irq < 0) { - if (irq != -EPROBE_DEFER) - dev_err(dev, "cannot get irq\n"); + irq = platform_get_irq(pdev, 0); + if (irq < 0) return irq; - } spin_lock_init(&p->lock); p->mapbase = regs->start; @@ -581,12 +534,12 @@ if (!data) return -ENOMEM; - data->dma.fn = dw8250_fallback_dma_filter; + data->data.dma.fn = dw8250_fallback_dma_filter; data->usr_reg = DW_UART_USR; + p->private_data = &data->data; #ifdef CONFIG_ARCH_ROCKCHIP data->irq = irq; #endif - p->private_data = data; data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible"); @@ -637,19 +590,21 @@ device_property_read_u32(dev, "clock-frequency", &p->uartclk); /* If there is separate baudclk, get the rate from it. */ - data->clk = devm_clk_get(dev, "baudclk"); - if (IS_ERR(data->clk) && PTR_ERR(data->clk) != -EPROBE_DEFER) - data->clk = devm_clk_get(dev, NULL); - if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (!IS_ERR_OR_NULL(data->clk)) { - err = clk_prepare_enable(data->clk); - if (err) - dev_warn(dev, "could not enable optional baudclk: %d\n", - err); - else - p->uartclk = clk_get_rate(data->clk); - } + data->clk = devm_clk_get_optional(dev, "baudclk"); + if (data->clk == NULL) + data->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + INIT_WORK(&data->clk_work, dw8250_clk_work_cb); + data->clk_notifier.notifier_call = dw8250_clk_notifier_cb; + + err = clk_prepare_enable(data->clk); + if (err) + dev_warn(dev, "could not enable optional baudclk: %d\n", err); + + if (data->clk) + p->uartclk = clk_get_rate(data->clk); /* If no clock rate is defined, fail. */ if (!p->uartclk) { @@ -658,17 +613,16 @@ goto err_clk; } - data->pclk = devm_clk_get(dev, "apb_pclk"); - if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) { - err = -EPROBE_DEFER; + data->pclk = devm_clk_get_optional(dev, "apb_pclk"); + if (IS_ERR(data->pclk)) { + err = PTR_ERR(data->pclk); goto err_clk; } - if (!IS_ERR(data->pclk)) { - err = clk_prepare_enable(data->pclk); - if (err) { - dev_err(dev, "could not enable apb_pclk\n"); - goto err_clk; - } + + 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_get_optional_exclusive(dev, NULL); @@ -689,22 +643,33 @@ /* If we have a valid fifosize, try hooking up DMA */ if (p->fifosize) { - data->dma.rxconf.src_maxburst = p->fifosize / 4; - data->dma.txconf.dst_maxburst = p->fifosize / 4; - uart.dma = &data->dma; + data->data.dma.rxconf.src_maxburst = p->fifosize / 4; + data->data.dma.txconf.dst_maxburst = p->fifosize / 4; + up->dma = &data->data.dma; } - data->line = serial8250_register_8250_port(&uart); - if (data->line < 0) { - err = data->line; + data->data.line = serial8250_register_8250_port(up); + if (data->data.line < 0) { + err = data->data.line; goto err_reset; } + /* + * 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) + dev_warn(p->dev, "Failed to set the clock notifier\n"); + else + queue_work(system_unbound_wq, &data->clk_work); + } #ifdef CONFIG_ARCH_ROCKCHIP if (data->enable_wakeup) device_init_wakeup(&pdev->dev, true); #endif - platform_set_drvdata(pdev, data); pm_runtime_set_active(dev); @@ -716,12 +681,10 @@ reset_control_assert(data->rst); err_pclk: - if (!IS_ERR(data->pclk)) - clk_disable_unprepare(data->pclk); + clk_disable_unprepare(data->pclk); err_clk: - if (!IS_ERR(data->clk)) - clk_disable_unprepare(data->clk); + clk_disable_unprepare(data->clk); return err; } @@ -729,26 +692,30 @@ static int dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_sync(dev); - serial8250_unregister_port(data->line); + if (data->clk) { + clk_notifier_unregister(data->clk, &data->clk_notifier); + + flush_work(&data->clk_work); + } + + serial8250_unregister_port(data->data.line); reset_control_assert(data->rst); - if (!IS_ERR(data->pclk)) - clk_disable_unprepare(data->pclk); + clk_disable_unprepare(data->pclk); - if (!IS_ERR(data->clk)) - clk_disable_unprepare(data->clk); + clk_disable_unprepare(data->clk); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); #ifdef CONFIG_ARCH_ROCKCHIP if (data->enable_wakeup) device_init_wakeup(&pdev->dev, false); #endif - - pm_runtime_disable(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); return 0; } @@ -765,7 +732,7 @@ return 0; } #endif - serial8250_suspend_port(data->line); + serial8250_suspend_port(data->data.line); return 0; } @@ -783,7 +750,7 @@ return 0; } #endif - serial8250_resume_port(data->line); + serial8250_resume_port(data->data.line); return 0; } @@ -794,11 +761,9 @@ { struct dw8250_data *data = dev_get_drvdata(dev); - if (!IS_ERR(data->clk)) - clk_disable_unprepare(data->clk); + clk_disable_unprepare(data->clk); - if (!IS_ERR(data->pclk)) - clk_disable_unprepare(data->pclk); + clk_disable_unprepare(data->pclk); return 0; } @@ -807,11 +772,9 @@ { struct dw8250_data *data = dev_get_drvdata(dev); - if (!IS_ERR(data->pclk)) - clk_prepare_enable(data->pclk); + clk_prepare_enable(data->pclk); - if (!IS_ERR(data->clk)) - clk_prepare_enable(data->clk); + clk_prepare_enable(data->clk); return 0; } @@ -824,9 +787,11 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, +#ifndef CONFIG_ROCKCHIP_MINI_KERNEL { .compatible = "cavium,octeon-3860-uart" }, { .compatible = "marvell,armada-38x-uart" }, { .compatible = "renesas,rzn1-uart" }, +#endif { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); @@ -841,6 +806,7 @@ { "APMC0D08", 0}, { "AMD0020", 0 }, { "AMDI0020", 0 }, + { "AMDI0022", 0 }, { "BRCM2032", 0 }, { "HISI0031", 0 }, { }, -- Gitblit v1.6.2