1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e6e912c4SKevin Wells /*
3e6e912c4SKevin Wells * arch/arm/mach-lpc32xx/serial.c
4e6e912c4SKevin Wells *
5e6e912c4SKevin Wells * Author: Kevin Wells <kevin.wells@nxp.com>
6e6e912c4SKevin Wells *
7e6e912c4SKevin Wells * Copyright (C) 2010 NXP Semiconductors
8e6e912c4SKevin Wells */
9e6e912c4SKevin Wells
10e6e912c4SKevin Wells #include <linux/kernel.h>
11e6e912c4SKevin Wells #include <linux/types.h>
12e6e912c4SKevin Wells #include <linux/serial.h>
13e6e912c4SKevin Wells #include <linux/serial_core.h>
14e6e912c4SKevin Wells #include <linux/serial_reg.h>
15e6e912c4SKevin Wells #include <linux/serial_8250.h>
16e6e912c4SKevin Wells #include <linux/clk.h>
17e6e912c4SKevin Wells #include <linux/io.h>
18*ecd2a576SArnd Bergmann #include <linux/soc/nxp/lpc32xx-misc.h>
19e6e912c4SKevin Wells
20d3532910SArnd Bergmann #include "lpc32xx.h"
21e6e912c4SKevin Wells #include "common.h"
22e6e912c4SKevin Wells
23e6e912c4SKevin Wells #define LPC32XX_SUART_FIFO_SIZE 64
24e6e912c4SKevin Wells
25e6e912c4SKevin Wells struct uartinit {
26e6e912c4SKevin Wells char *uart_ck_name;
27e6e912c4SKevin Wells u32 ck_mode_mask;
28e6e912c4SKevin Wells void __iomem *pdiv_clk_reg;
292707208eSRoland Stigge resource_size_t mapbase;
30e6e912c4SKevin Wells };
31e6e912c4SKevin Wells
32e6e912c4SKevin Wells static struct uartinit uartinit_data[] __initdata = {
33e6e912c4SKevin Wells {
34e6e912c4SKevin Wells .uart_ck_name = "uart5_ck",
35e6e912c4SKevin Wells .ck_mode_mask =
36e6e912c4SKevin Wells LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5),
37e6e912c4SKevin Wells .pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL,
382707208eSRoland Stigge .mapbase = LPC32XX_UART5_BASE,
39e6e912c4SKevin Wells },
40e6e912c4SKevin Wells {
41e6e912c4SKevin Wells .uart_ck_name = "uart3_ck",
42e6e912c4SKevin Wells .ck_mode_mask =
43e6e912c4SKevin Wells LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3),
44e6e912c4SKevin Wells .pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL,
452707208eSRoland Stigge .mapbase = LPC32XX_UART3_BASE,
46e6e912c4SKevin Wells },
47e6e912c4SKevin Wells {
48e6e912c4SKevin Wells .uart_ck_name = "uart4_ck",
49e6e912c4SKevin Wells .ck_mode_mask =
50e6e912c4SKevin Wells LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4),
51e6e912c4SKevin Wells .pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL,
522707208eSRoland Stigge .mapbase = LPC32XX_UART4_BASE,
53e6e912c4SKevin Wells },
54e6e912c4SKevin Wells {
55e6e912c4SKevin Wells .uart_ck_name = "uart6_ck",
56e6e912c4SKevin Wells .ck_mode_mask =
57e6e912c4SKevin Wells LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6),
58e6e912c4SKevin Wells .pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL,
592707208eSRoland Stigge .mapbase = LPC32XX_UART6_BASE,
60e6e912c4SKevin Wells },
61e6e912c4SKevin Wells };
62e6e912c4SKevin Wells
63ffba29c9SArnd Bergmann /* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */
lpc32xx_loopback_set(resource_size_t mapbase,int state)64ffba29c9SArnd Bergmann void lpc32xx_loopback_set(resource_size_t mapbase, int state)
65ffba29c9SArnd Bergmann {
66ffba29c9SArnd Bergmann int bit;
67ffba29c9SArnd Bergmann u32 tmp;
68ffba29c9SArnd Bergmann
69ffba29c9SArnd Bergmann switch (mapbase) {
70ffba29c9SArnd Bergmann case LPC32XX_HS_UART1_BASE:
71ffba29c9SArnd Bergmann bit = 0;
72ffba29c9SArnd Bergmann break;
73ffba29c9SArnd Bergmann case LPC32XX_HS_UART2_BASE:
74ffba29c9SArnd Bergmann bit = 1;
75ffba29c9SArnd Bergmann break;
76ffba29c9SArnd Bergmann case LPC32XX_HS_UART7_BASE:
77ffba29c9SArnd Bergmann bit = 6;
78ffba29c9SArnd Bergmann break;
79ffba29c9SArnd Bergmann default:
80ffba29c9SArnd Bergmann WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase);
81ffba29c9SArnd Bergmann return;
82ffba29c9SArnd Bergmann }
83ffba29c9SArnd Bergmann
84ffba29c9SArnd Bergmann tmp = readl(LPC32XX_UARTCTL_CLOOP);
85ffba29c9SArnd Bergmann if (state)
86ffba29c9SArnd Bergmann tmp |= (1 << bit);
87ffba29c9SArnd Bergmann else
88ffba29c9SArnd Bergmann tmp &= ~(1 << bit);
89ffba29c9SArnd Bergmann writel(tmp, LPC32XX_UARTCTL_CLOOP);
90ffba29c9SArnd Bergmann }
91ffba29c9SArnd Bergmann EXPORT_SYMBOL_GPL(lpc32xx_loopback_set);
92ffba29c9SArnd Bergmann
lpc32xx_serial_init(void)93e6e912c4SKevin Wells void __init lpc32xx_serial_init(void)
94e6e912c4SKevin Wells {
95e6e912c4SKevin Wells u32 tmp, clkmodes = 0;
96e6e912c4SKevin Wells struct clk *clk;
97e6e912c4SKevin Wells unsigned int puart;
98e6e912c4SKevin Wells int i, j;
99e6e912c4SKevin Wells
100e6e912c4SKevin Wells for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) {
101e6e912c4SKevin Wells clk = clk_get(NULL, uartinit_data[i].uart_ck_name);
102e6e912c4SKevin Wells if (!IS_ERR(clk)) {
103e6e912c4SKevin Wells clk_enable(clk);
104e6e912c4SKevin Wells }
105e6e912c4SKevin Wells
106e6e912c4SKevin Wells /* Setup UART clock modes for all UARTs, disable autoclock */
107e6e912c4SKevin Wells clkmodes |= uartinit_data[i].ck_mode_mask;
108e6e912c4SKevin Wells
109e6e912c4SKevin Wells /* pre-UART clock divider set to 1 */
110e6e912c4SKevin Wells __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg);
1112707208eSRoland Stigge
1122707208eSRoland Stigge /*
1132707208eSRoland Stigge * Force a flush of the RX FIFOs to work around a
1142707208eSRoland Stigge * HW bug
1152707208eSRoland Stigge */
1162707208eSRoland Stigge puart = uartinit_data[i].mapbase;
1172707208eSRoland Stigge __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart));
1182707208eSRoland Stigge __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart));
1192707208eSRoland Stigge j = LPC32XX_SUART_FIFO_SIZE;
1202707208eSRoland Stigge while (j--)
1212707208eSRoland Stigge tmp = __raw_readl(
1222707208eSRoland Stigge LPC32XX_UART_DLL_FIFO(puart));
1232707208eSRoland Stigge __raw_writel(0, LPC32XX_UART_IIR_FCR(puart));
124e6e912c4SKevin Wells }
125e6e912c4SKevin Wells
126e6e912c4SKevin Wells /* This needs to be done after all UART clocks are setup */
127e6e912c4SKevin Wells __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE);
128ff424aa4SRoland Stigge for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) {
129e6e912c4SKevin Wells /* Force a flush of the RX FIFOs to work around a HW bug */
130c70426f1SRoland Stigge puart = uartinit_data[i].mapbase;
131e6e912c4SKevin Wells __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart));
132e6e912c4SKevin Wells __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart));
133e6e912c4SKevin Wells j = LPC32XX_SUART_FIFO_SIZE;
134e6e912c4SKevin Wells while (j--)
135e6e912c4SKevin Wells tmp = __raw_readl(LPC32XX_UART_DLL_FIFO(puart));
136e6e912c4SKevin Wells __raw_writel(0, LPC32XX_UART_IIR_FCR(puart));
137e6e912c4SKevin Wells }
138e6e912c4SKevin Wells
1395fe8f11cSAlexandre Pereira da Silva /* Disable IrDA pulsing support on UART6 */
1405fe8f11cSAlexandre Pereira da Silva tmp = __raw_readl(LPC32XX_UARTCTL_CTRL);
1415fe8f11cSAlexandre Pereira da Silva tmp |= LPC32XX_UART_UART6_IRDAMOD_BYPASS;
1425fe8f11cSAlexandre Pereira da Silva __raw_writel(tmp, LPC32XX_UARTCTL_CTRL);
1435fe8f11cSAlexandre Pereira da Silva
144e6e912c4SKevin Wells /* Disable UART5->USB transparent mode or USB won't work */
145e6e912c4SKevin Wells tmp = __raw_readl(LPC32XX_UARTCTL_CTRL);
146e6e912c4SKevin Wells tmp &= ~LPC32XX_UART_U5_ROUTE_TO_USB;
147e6e912c4SKevin Wells __raw_writel(tmp, LPC32XX_UARTCTL_CTRL);
148e6e912c4SKevin Wells }
149