18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Atheros AR7XXX/AR9XXX SoC early printk support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
68c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/serial_reg.h>
128c2ecf20Sopenharmony_ci#include <asm/addrspace.h>
138c2ecf20Sopenharmony_ci#include <asm/setup.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/mach-ath79/ath79.h>
168c2ecf20Sopenharmony_ci#include <asm/mach-ath79/ar71xx_regs.h>
178c2ecf20Sopenharmony_ci#include <asm/mach-ath79/ar933x_uart.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic void (*_prom_putchar)(char);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic inline void prom_putchar_wait(void __iomem *reg, u32 mask, u32 val)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	u32 t;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	do {
268c2ecf20Sopenharmony_ci		t = __raw_readl(reg);
278c2ecf20Sopenharmony_ci		if ((t & mask) == val)
288c2ecf20Sopenharmony_ci			break;
298c2ecf20Sopenharmony_ci	} while (1);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic void prom_putchar_ar71xx(char ch)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE));
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY);
398c2ecf20Sopenharmony_ci	__raw_writel((unsigned char)ch, base + UART_TX * 4);
408c2ecf20Sopenharmony_ci	prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void prom_putchar_ar933x(char ch)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	void __iomem *base = (void __iomem *)(KSEG1ADDR(AR933X_UART_BASE));
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	prom_putchar_wait(base + AR933X_UART_DATA_REG, AR933X_UART_DATA_TX_CSR,
488c2ecf20Sopenharmony_ci			  AR933X_UART_DATA_TX_CSR);
498c2ecf20Sopenharmony_ci	__raw_writel(AR933X_UART_DATA_TX_CSR | (unsigned char)ch,
508c2ecf20Sopenharmony_ci		     base + AR933X_UART_DATA_REG);
518c2ecf20Sopenharmony_ci	prom_putchar_wait(base + AR933X_UART_DATA_REG, AR933X_UART_DATA_TX_CSR,
528c2ecf20Sopenharmony_ci			  AR933X_UART_DATA_TX_CSR);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void prom_putchar_dummy(char ch)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	/* nothing to do */
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void prom_enable_uart(u32 id)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	void __iomem *gpio_base;
638c2ecf20Sopenharmony_ci	u32 uart_en;
648c2ecf20Sopenharmony_ci	u32 t;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	switch (id) {
678c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR71XX:
688c2ecf20Sopenharmony_ci		uart_en = AR71XX_GPIO_FUNC_UART_EN;
698c2ecf20Sopenharmony_ci		break;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR7240:
728c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR7241:
738c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR7242:
748c2ecf20Sopenharmony_ci		uart_en = AR724X_GPIO_FUNC_UART_EN;
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR913X:
788c2ecf20Sopenharmony_ci		uart_en = AR913X_GPIO_FUNC_UART_EN;
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9330:
828c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9331:
838c2ecf20Sopenharmony_ci		uart_en = AR933X_GPIO_FUNC_UART_EN;
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9341:
878c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9342:
888c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9344:
898c2ecf20Sopenharmony_ci		/* TODO */
908c2ecf20Sopenharmony_ci	default:
918c2ecf20Sopenharmony_ci		return;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	gpio_base = (void __iomem *)KSEG1ADDR(AR71XX_GPIO_BASE);
958c2ecf20Sopenharmony_ci	t = __raw_readl(gpio_base + AR71XX_GPIO_REG_FUNC);
968c2ecf20Sopenharmony_ci	t |= uart_en;
978c2ecf20Sopenharmony_ci	__raw_writel(t, gpio_base + AR71XX_GPIO_REG_FUNC);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void prom_putchar_init(void)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	void __iomem *base;
1038c2ecf20Sopenharmony_ci	u32 id;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	base = (void __iomem *)(KSEG1ADDR(AR71XX_RESET_BASE));
1068c2ecf20Sopenharmony_ci	id = __raw_readl(base + AR71XX_RESET_REG_REV_ID);
1078c2ecf20Sopenharmony_ci	id &= REV_ID_MAJOR_MASK;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	switch (id) {
1108c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR71XX:
1118c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR7240:
1128c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR7241:
1138c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR7242:
1148c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR913X:
1158c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9341:
1168c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9342:
1178c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9344:
1188c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_QCA9533:
1198c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_QCA9533_V2:
1208c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_QCA9556:
1218c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_QCA9558:
1228c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_TP9343:
1238c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_QCA956X:
1248c2ecf20Sopenharmony_ci		_prom_putchar = prom_putchar_ar71xx;
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9330:
1288c2ecf20Sopenharmony_ci	case REV_ID_MAJOR_AR9331:
1298c2ecf20Sopenharmony_ci		_prom_putchar = prom_putchar_ar933x;
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	default:
1338c2ecf20Sopenharmony_ci		_prom_putchar = prom_putchar_dummy;
1348c2ecf20Sopenharmony_ci		return;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	prom_enable_uart(id);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_civoid prom_putchar(char ch)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	if (!_prom_putchar)
1438c2ecf20Sopenharmony_ci		prom_putchar_init();
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	_prom_putchar(ch);
1468c2ecf20Sopenharmony_ci}
147