diff options
Diffstat (limited to 'arch/arm/mach-omap1/serial.c')
-rw-r--r-- | arch/arm/mach-omap1/serial.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/arch/arm/mach-omap1/serial.c b/arch/arm/mach-omap1/serial.c new file mode 100644 index 0000000000..ffa4a9bece --- /dev/null +++ b/arch/arm/mach-omap1/serial.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/serial.c + * + * OMAP1 serial support. + */ +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/mach-types.h> + +#include "common.h" +#include "serial.h" +#include "mux.h" +#include "pm.h" +#include "soc.h" + +static struct clk * uart1_ck; +static struct clk * uart2_ck; +static struct clk * uart3_ck; + +static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, + int offset) +{ + offset <<= up->regshift; + return (unsigned int)__raw_readb(up->membase + offset); +} + +static inline void omap_serial_outp(struct plat_serial8250_port *p, int offset, + int value) +{ + offset <<= p->regshift; + __raw_writeb(value, p->membase + offset); +} + +/* + * Internal UARTs need to be initialized for the 8250 autoconfig to work + * properly. Note that the TX watermark initialization may not be needed + * once the 8250.c watermark handling code is merged. + */ +static void __init omap_serial_reset(struct plat_serial8250_port *p) +{ + omap_serial_outp(p, UART_OMAP_MDR1, + UART_OMAP_MDR1_DISABLE); /* disable UART */ + omap_serial_outp(p, UART_OMAP_SCR, 0x08); /* TX watermark */ + omap_serial_outp(p, UART_OMAP_MDR1, + UART_OMAP_MDR1_16X_MODE); /* enable UART */ + + if (!cpu_is_omap15xx()) { + omap_serial_outp(p, UART_OMAP_SYSC, 0x01); + while (!(omap_serial_in(p, UART_OMAP_SYSC) & 0x01)); + } +} + +static struct plat_serial8250_port serial_platform_data[] = { + { + .mapbase = OMAP1_UART1_BASE, + .irq = INT_UART1, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { + .mapbase = OMAP1_UART2_BASE, + .irq = INT_UART2, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { + .mapbase = OMAP1_UART3_BASE, + .irq = INT_UART3, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +/* + * Note that on Innovator-1510 UART2 pins conflict with USB2. + * By default UART2 does not work on Innovator-1510 if you have + * USB OHCI enabled. To use UART2, you must disable USB2 first. + */ +void __init omap_serial_init(void) +{ + int i; + + if (cpu_is_omap15xx()) { + serial_platform_data[0].uartclk = OMAP1510_BASE_BAUD * 16; + serial_platform_data[1].uartclk = OMAP1510_BASE_BAUD * 16; + serial_platform_data[2].uartclk = OMAP1510_BASE_BAUD * 16; + } + + for (i = 0; i < ARRAY_SIZE(serial_platform_data) - 1; i++) { + /* Static mapping, never released */ + serial_platform_data[i].membase = + ioremap(serial_platform_data[i].mapbase, SZ_2K); + if (!serial_platform_data[i].membase) { + printk(KERN_ERR "Could not ioremap uart%i\n", i); + continue; + } + switch (i) { + case 0: + uart1_ck = clk_get(NULL, "uart1_ck"); + if (IS_ERR(uart1_ck)) + printk("Could not get uart1_ck\n"); + else { + clk_prepare_enable(uart1_ck); + if (cpu_is_omap15xx()) + clk_set_rate(uart1_ck, 12000000); + } + break; + case 1: + uart2_ck = clk_get(NULL, "uart2_ck"); + if (IS_ERR(uart2_ck)) + printk("Could not get uart2_ck\n"); + else { + clk_prepare_enable(uart2_ck); + if (cpu_is_omap15xx()) + clk_set_rate(uart2_ck, 12000000); + else + clk_set_rate(uart2_ck, 48000000); + } + break; + case 2: + uart3_ck = clk_get(NULL, "uart3_ck"); + if (IS_ERR(uart3_ck)) + printk("Could not get uart3_ck\n"); + else { + clk_prepare_enable(uart3_ck); + if (cpu_is_omap15xx()) + clk_set_rate(uart3_ck, 12000000); + } + break; + } + omap_serial_reset(&serial_platform_data[i]); + } +} + +#ifdef CONFIG_OMAP_SERIAL_WAKE + +static irqreturn_t omap_serial_wake_interrupt(int irq, void *dev_id) +{ + /* Need to do something with serial port right after wake-up? */ + return IRQ_HANDLED; +} + +/* + * Reroutes serial RX lines to GPIO lines for the duration of + * sleep to allow waking up the device from serial port even + * in deep sleep. + */ +void omap_serial_wake_trigger(int enable) +{ + if (!cpu_is_omap16xx()) + return; + + if (uart1_ck != NULL) { + if (enable) + omap_cfg_reg(V14_16XX_GPIO37); + else + omap_cfg_reg(V14_16XX_UART1_RX); + } + if (uart2_ck != NULL) { + if (enable) + omap_cfg_reg(R9_16XX_GPIO18); + else + omap_cfg_reg(R9_16XX_UART2_RX); + } + if (uart3_ck != NULL) { + if (enable) + omap_cfg_reg(L14_16XX_GPIO49); + else + omap_cfg_reg(L14_16XX_UART3_RX); + } +} + +static void __init omap_serial_set_port_wakeup(int idx) +{ + struct gpio_desc *d; + int ret; + + d = gpiod_get_index(NULL, "wakeup", idx, GPIOD_IN); + if (IS_ERR(d)) { + pr_err("Unable to get UART wakeup GPIO descriptor\n"); + return; + } + ret = request_irq(gpiod_to_irq(d), &omap_serial_wake_interrupt, + IRQF_TRIGGER_RISING, "serial wakeup", NULL); + if (ret) { + gpiod_put(d); + pr_err("No interrupt for UART%d wake GPIO\n", idx + 1); + return; + } + enable_irq_wake(gpiod_to_irq(d)); +} + + +int __init omap_serial_wakeup_init(void) +{ + if (!cpu_is_omap16xx()) + return 0; + + if (uart1_ck != NULL) + omap_serial_set_port_wakeup(0); + if (uart2_ck != NULL) + omap_serial_set_port_wakeup(1); + if (uart3_ck != NULL) + omap_serial_set_port_wakeup(2); + + return 0; +} + +#endif /* CONFIG_OMAP_SERIAL_WAKE */ + +static int __init omap_init(void) +{ + if (!cpu_class_is_omap1()) + return -ENODEV; + + return platform_device_register(&serial_device); +} +arch_initcall(omap_init); |