162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/arm/mach-lpc32xx/serial.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Kevin Wells <kevin.wells@nxp.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2010 NXP Semiconductors
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/serial.h>
1362306a36Sopenharmony_ci#include <linux/serial_core.h>
1462306a36Sopenharmony_ci#include <linux/serial_reg.h>
1562306a36Sopenharmony_ci#include <linux/serial_8250.h>
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <linux/io.h>
1862306a36Sopenharmony_ci#include <linux/soc/nxp/lpc32xx-misc.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "lpc32xx.h"
2162306a36Sopenharmony_ci#include "common.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define LPC32XX_SUART_FIFO_SIZE	64
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct uartinit {
2662306a36Sopenharmony_ci	char *uart_ck_name;
2762306a36Sopenharmony_ci	u32 ck_mode_mask;
2862306a36Sopenharmony_ci	void __iomem *pdiv_clk_reg;
2962306a36Sopenharmony_ci	resource_size_t mapbase;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct uartinit uartinit_data[] __initdata = {
3362306a36Sopenharmony_ci	{
3462306a36Sopenharmony_ci		.uart_ck_name = "uart5_ck",
3562306a36Sopenharmony_ci		.ck_mode_mask =
3662306a36Sopenharmony_ci			LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5),
3762306a36Sopenharmony_ci		.pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL,
3862306a36Sopenharmony_ci		.mapbase = LPC32XX_UART5_BASE,
3962306a36Sopenharmony_ci	},
4062306a36Sopenharmony_ci	{
4162306a36Sopenharmony_ci		.uart_ck_name = "uart3_ck",
4262306a36Sopenharmony_ci		.ck_mode_mask =
4362306a36Sopenharmony_ci			LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3),
4462306a36Sopenharmony_ci		.pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL,
4562306a36Sopenharmony_ci		.mapbase = LPC32XX_UART3_BASE,
4662306a36Sopenharmony_ci	},
4762306a36Sopenharmony_ci	{
4862306a36Sopenharmony_ci		.uart_ck_name = "uart4_ck",
4962306a36Sopenharmony_ci		.ck_mode_mask =
5062306a36Sopenharmony_ci			LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4),
5162306a36Sopenharmony_ci		.pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL,
5262306a36Sopenharmony_ci		.mapbase = LPC32XX_UART4_BASE,
5362306a36Sopenharmony_ci	},
5462306a36Sopenharmony_ci	{
5562306a36Sopenharmony_ci		.uart_ck_name = "uart6_ck",
5662306a36Sopenharmony_ci		.ck_mode_mask =
5762306a36Sopenharmony_ci			LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6),
5862306a36Sopenharmony_ci		.pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL,
5962306a36Sopenharmony_ci		.mapbase = LPC32XX_UART6_BASE,
6062306a36Sopenharmony_ci	},
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */
6462306a36Sopenharmony_civoid lpc32xx_loopback_set(resource_size_t mapbase, int state)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	int bit;
6762306a36Sopenharmony_ci	u32 tmp;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	switch (mapbase) {
7062306a36Sopenharmony_ci	case LPC32XX_HS_UART1_BASE:
7162306a36Sopenharmony_ci		bit = 0;
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci	case LPC32XX_HS_UART2_BASE:
7462306a36Sopenharmony_ci		bit = 1;
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	case LPC32XX_HS_UART7_BASE:
7762306a36Sopenharmony_ci		bit = 6;
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci	default:
8062306a36Sopenharmony_ci		WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase);
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	tmp = readl(LPC32XX_UARTCTL_CLOOP);
8562306a36Sopenharmony_ci	if (state)
8662306a36Sopenharmony_ci		tmp |= (1 << bit);
8762306a36Sopenharmony_ci	else
8862306a36Sopenharmony_ci		tmp &= ~(1 << bit);
8962306a36Sopenharmony_ci	writel(tmp, LPC32XX_UARTCTL_CLOOP);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lpc32xx_loopback_set);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_civoid __init lpc32xx_serial_init(void)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	u32 tmp, clkmodes = 0;
9662306a36Sopenharmony_ci	struct clk *clk;
9762306a36Sopenharmony_ci	unsigned int puart;
9862306a36Sopenharmony_ci	int i, j;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) {
10162306a36Sopenharmony_ci		clk = clk_get(NULL, uartinit_data[i].uart_ck_name);
10262306a36Sopenharmony_ci		if (!IS_ERR(clk)) {
10362306a36Sopenharmony_ci			clk_enable(clk);
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/* Setup UART clock modes for all UARTs, disable autoclock */
10762306a36Sopenharmony_ci		clkmodes |= uartinit_data[i].ck_mode_mask;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		/* pre-UART clock divider set to 1 */
11062306a36Sopenharmony_ci		__raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		/*
11362306a36Sopenharmony_ci		 * Force a flush of the RX FIFOs to work around a
11462306a36Sopenharmony_ci		 * HW bug
11562306a36Sopenharmony_ci		 */
11662306a36Sopenharmony_ci		puart = uartinit_data[i].mapbase;
11762306a36Sopenharmony_ci		__raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart));
11862306a36Sopenharmony_ci		__raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart));
11962306a36Sopenharmony_ci		j = LPC32XX_SUART_FIFO_SIZE;
12062306a36Sopenharmony_ci		while (j--)
12162306a36Sopenharmony_ci			tmp = __raw_readl(
12262306a36Sopenharmony_ci				LPC32XX_UART_DLL_FIFO(puart));
12362306a36Sopenharmony_ci		__raw_writel(0, LPC32XX_UART_IIR_FCR(puart));
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* This needs to be done after all UART clocks are setup */
12762306a36Sopenharmony_ci	__raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE);
12862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) {
12962306a36Sopenharmony_ci		/* Force a flush of the RX FIFOs to work around a HW bug */
13062306a36Sopenharmony_ci		puart = uartinit_data[i].mapbase;
13162306a36Sopenharmony_ci		__raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart));
13262306a36Sopenharmony_ci		__raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart));
13362306a36Sopenharmony_ci		j = LPC32XX_SUART_FIFO_SIZE;
13462306a36Sopenharmony_ci		while (j--)
13562306a36Sopenharmony_ci			tmp = __raw_readl(LPC32XX_UART_DLL_FIFO(puart));
13662306a36Sopenharmony_ci		__raw_writel(0, LPC32XX_UART_IIR_FCR(puart));
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Disable IrDA pulsing support on UART6 */
14062306a36Sopenharmony_ci	tmp = __raw_readl(LPC32XX_UARTCTL_CTRL);
14162306a36Sopenharmony_ci	tmp |= LPC32XX_UART_UART6_IRDAMOD_BYPASS;
14262306a36Sopenharmony_ci	__raw_writel(tmp, LPC32XX_UARTCTL_CTRL);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Disable UART5->USB transparent mode or USB won't work */
14562306a36Sopenharmony_ci	tmp = __raw_readl(LPC32XX_UARTCTL_CTRL);
14662306a36Sopenharmony_ci	tmp &= ~LPC32XX_UART_U5_ROUTE_TO_USB;
14762306a36Sopenharmony_ci	__raw_writel(tmp, LPC32XX_UARTCTL_CTRL);
14862306a36Sopenharmony_ci}
149