diff options
-rw-r--r-- | drivers/tty/serial/Kconfig | 1 | ||||
-rw-r--r-- | drivers/tty/serial/meson_uart.c | 221 |
2 files changed, 67 insertions, 155 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a0f2b82fc18b..e952ec5c7a7c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -200,7 +200,6 @@ config SERIAL_KGDB_NMI config SERIAL_MESON tristate "Meson serial port support" depends on ARCH_MESON || COMPILE_TEST - depends on COMMON_CLK select SERIAL_CORE help This enables the driver for the on-chip UARTs of the Amlogic diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index bf6be5468aaf..45e00d928253 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -6,7 +6,6 @@ */ #include <linux/clk.h> -#include <linux/clk-provider.h> #include <linux/console.h> #include <linux/delay.h> #include <linux/init.h> @@ -66,7 +65,9 @@ #define AML_UART_RECV_IRQ(c) ((c) & 0xff) /* AML_UART_REG5 bits */ +#define AML_UART_BAUD_MASK 0x7fffff #define AML_UART_BAUD_USE BIT(23) +#define AML_UART_BAUD_XTAL BIT(24) #define AML_UART_PORT_NUM 12 #define AML_UART_PORT_OFFSET 6 @@ -75,11 +76,6 @@ #define AML_UART_POLL_USEC 5 #define AML_UART_TIMEOUT_USEC 10000 -struct meson_uart_data { - struct clk *baud_clk; - bool use_xtal_clk; -}; - static struct uart_driver meson_uart_driver; static struct uart_port *meson_ports[AML_UART_PORT_NUM]; @@ -297,17 +293,19 @@ static int meson_uart_startup(struct uart_port *port) static void meson_uart_change_speed(struct uart_port *port, unsigned long baud) { - struct meson_uart_data *private_data = port->private_data; u32 val; while (!meson_uart_tx_empty(port)) cpu_relax(); - val = readl(port->membase + AML_UART_REG5); + if (port->uartclk == 24000000) { + val = ((port->uartclk / 3) / baud) - 1; + val |= AML_UART_BAUD_XTAL; + } else { + val = ((port->uartclk * 10 / (baud * 4) + 5) / 10) - 1; + } val |= AML_UART_BAUD_USE; writel(val, port->membase + AML_UART_REG5); - - clk_set_rate(private_data->baud_clk, baud); } static void meson_uart_set_termios(struct uart_port *port, @@ -365,13 +363,8 @@ static void meson_uart_set_termios(struct uart_port *port, writel(val, port->membase + AML_UART_CONTROL); baud = uart_get_baud_rate(port, termios, old, 50, 4000000); - - spin_unlock_irqrestore(&port->lock, flags); - meson_uart_change_speed(port, baud); - spin_lock_irqsave(&port->lock, flags); - port->read_status_mask = AML_UART_TX_FIFO_WERR; if (iflags & INPCK) port->read_status_mask |= AML_UART_PARITY_ERR | @@ -402,19 +395,23 @@ static int meson_uart_verify_port(struct uart_port *port, static void meson_uart_release_port(struct uart_port *port) { - struct meson_uart_data *private_data = port->private_data; - - clk_disable_unprepare(private_data->baud_clk); + devm_iounmap(port->dev, port->membase); + port->membase = NULL; + devm_release_mem_region(port->dev, port->mapbase, port->mapsize); } static int meson_uart_request_port(struct uart_port *port) { - struct meson_uart_data *private_data = port->private_data; - int ret; + if (!devm_request_mem_region(port->dev, port->mapbase, port->mapsize, + dev_name(port->dev))) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } - ret = clk_prepare_enable(private_data->baud_clk); - if (ret) - return ret; + port->membase = devm_ioremap(port->dev, port->mapbase, + port->mapsize); + if (!port->membase) + return -ENOMEM; return 0; } @@ -645,106 +642,57 @@ static struct uart_driver meson_uart_driver = { .cons = MESON_SERIAL_CONSOLE, }; -static const struct clk_div_table xtal_div_table[] = { - { 0, 3 }, - { 1, 1 }, - { 2, 2 }, - { 3, 2 }, -}; +static inline struct clk *meson_uart_probe_clock(struct device *dev, + const char *id) +{ + struct clk *clk = NULL; + int ret; + + clk = devm_clk_get(dev, id); + if (IS_ERR(clk)) + return clk; + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "couldn't enable clk\n"); + return ERR_PTR(ret); + } + + devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + clk); -static u32 use_xtal_mux_table; + return clk; +} -static int meson_uart_probe_clocks(struct uart_port *port) +static int meson_uart_probe_clocks(struct platform_device *pdev, + struct uart_port *port) { - struct meson_uart_data *private_data = port->private_data; - struct clk *clk_baud, *clk_xtal; - struct clk_hw *hw, *clk81_div4_hw; - char clk_name[32]; - struct clk_parent_data use_xtal_mux_parents; + struct clk *clk_xtal = NULL; + struct clk *clk_pclk = NULL; + struct clk *clk_baud = NULL; - clk_baud = devm_clk_get(port->dev, "baud"); - if (IS_ERR(clk_baud)) { - dev_err(port->dev, "Failed to get the 'baud' clock\n"); - return PTR_ERR(clk_baud); - } + clk_pclk = meson_uart_probe_clock(&pdev->dev, "pclk"); + if (IS_ERR(clk_pclk)) + return PTR_ERR(clk_pclk); - clk_xtal = devm_clk_get(port->dev, "xtal"); + clk_xtal = meson_uart_probe_clock(&pdev->dev, "xtal"); if (IS_ERR(clk_xtal)) - return dev_err_probe(port->dev, PTR_ERR(clk_xtal), - "Failed to get the 'xtal' clock\n"); - - snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(port->dev), - "clk81_div4"); - clk81_div4_hw = devm_clk_hw_register_fixed_factor(port->dev, - clk_name, - __clk_get_name(clk_baud), - CLK_SET_RATE_NO_REPARENT, - 1, 4); - if (IS_ERR(clk81_div4_hw)) - return PTR_ERR(clk81_div4_hw); - - snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(port->dev), - "xtal_div"); - hw = devm_clk_hw_register_divider_table(port->dev, - clk_name, - __clk_get_name(clk_baud), - CLK_SET_RATE_NO_REPARENT, - port->membase + AML_UART_REG5, - 26, 2, - CLK_DIVIDER_ROUND_CLOSEST, - xtal_div_table, NULL); - if (IS_ERR(hw)) - return PTR_ERR(hw); - - if (private_data->use_xtal_clk) { - use_xtal_mux_table = 1; - use_xtal_mux_parents.hw = hw; - } else { - use_xtal_mux_parents.hw = clk81_div4_hw; - } + return PTR_ERR(clk_xtal); + + clk_baud = meson_uart_probe_clock(&pdev->dev, "baud"); + if (IS_ERR(clk_baud)) + return PTR_ERR(clk_baud); - snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(port->dev), - "use_xtal"); - hw = __devm_clk_hw_register_mux(port->dev, NULL, - clk_name, - 1, - NULL, NULL, - &use_xtal_mux_parents, - CLK_SET_RATE_PARENT, - port->membase + AML_UART_REG5, - 24, 0x1, - CLK_MUX_ROUND_CLOSEST, - &use_xtal_mux_table, NULL); - - if (IS_ERR(hw)) - return PTR_ERR(hw); - - port->uartclk = clk_hw_get_rate(hw); - - snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(port->dev), - "baud_div"); - hw = devm_clk_hw_register_divider(port->dev, - clk_name, - clk_hw_get_name(hw), - CLK_SET_RATE_PARENT, - port->membase + AML_UART_REG5, - 0, 23, - CLK_DIVIDER_ROUND_CLOSEST, - NULL); - if (IS_ERR(hw)) - return PTR_ERR(hw); - - private_data->baud_clk = hw->clk; + port->uartclk = clk_get_rate(clk_baud); return 0; } static int meson_uart_probe(struct platform_device *pdev) { - struct meson_uart_data *private_data; struct resource *res_mem; struct uart_port *port; - struct clk *pclk; u32 fifosize = 64; /* Default is 64, 128 for EE UART_0 */ int ret = 0; int irq; @@ -770,15 +718,6 @@ static int meson_uart_probe(struct platform_device *pdev) if (!res_mem) return -ENODEV; - pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(pclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(pclk), - "Failed to get the 'pclk' clock\n"); - - ret = clk_prepare_enable(pclk); - if (ret) - return ret; - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -794,17 +733,9 @@ static int meson_uart_probe(struct platform_device *pdev) if (!port) return -ENOMEM; - port->membase = devm_ioremap_resource(&pdev->dev, res_mem); - if (IS_ERR(port->membase)) - return PTR_ERR(port->membase); - - private_data = devm_kzalloc(&pdev->dev, sizeof(*private_data), - GFP_KERNEL); - if (!private_data) - return -ENOMEM; - - if (device_get_match_data(&pdev->dev)) - private_data->use_xtal_clk = true; + ret = meson_uart_probe_clocks(pdev, port); + if (ret) + return ret; port->iotype = UPIO_MEM; port->mapbase = res_mem->start; @@ -818,17 +749,15 @@ static int meson_uart_probe(struct platform_device *pdev) port->x_char = 0; port->ops = &meson_uart_ops; port->fifosize = fifosize; - port->private_data = private_data; - - ret = meson_uart_probe_clocks(port); - if (ret) - return ret; meson_ports[pdev->id] = port; platform_set_drvdata(pdev, port); /* reset port before registering (and possibly registering console) */ - meson_uart_reset(port); + if (meson_uart_request_port(port) >= 0) { + meson_uart_reset(port); + meson_uart_release_port(port); + } ret = uart_add_one_port(&meson_uart_driver, port); if (ret) @@ -849,26 +778,10 @@ static int meson_uart_remove(struct platform_device *pdev) } static const struct of_device_id meson_uart_dt_match[] = { - { - .compatible = "amlogic,meson6-uart", - .data = (void *)false, - }, - { - .compatible = "amlogic,meson8-uart", - .data = (void *)false, - }, - { - .compatible = "amlogic,meson8b-uart", - .data = (void *)false, - }, - { - .compatible = "amlogic,meson-gx-uart", - .data = (void *)true, - }, - { - .compatible = "amlogic,meson-s4-uart", - .data = (void *)true, - }, + { .compatible = "amlogic,meson6-uart" }, + { .compatible = "amlogic,meson8-uart" }, + { .compatible = "amlogic,meson8b-uart" }, + { .compatible = "amlogic,meson-gx-uart" }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, meson_uart_dt_match); |