summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/serial/Kconfig1
-rw-r--r--drivers/tty/serial/meson_uart.c221
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);