162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/init.h>
762306a36Sopenharmony_ci#include <linux/syscore_ops.h>
862306a36Sopenharmony_ci#include <linux/amba/bus.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/irqchip.h>
1162306a36Sopenharmony_ci#include <linux/of_irq.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/of_platform.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include <linux/termios.h>
1662306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/mach/arch.h>
2062306a36Sopenharmony_ci#include <asm/mach/map.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "integrator-hardware.h"
2362306a36Sopenharmony_ci#include "integrator-cm.h"
2462306a36Sopenharmony_ci#include "integrator.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Regmap to the AP system controller */
2762306a36Sopenharmony_cistatic struct regmap *ap_syscon_map;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
3162306a36Sopenharmony_ci * is the (PA >> 12).
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Setup a VA for the Integrator interrupt controller (for header #0,
3462306a36Sopenharmony_ci * just for now).
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci#define VA_IC_BASE	__io_address(INTEGRATOR_IC_BASE)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * Logical      Physical
4062306a36Sopenharmony_ci * f1400000	14000000	Interrupt controller
4162306a36Sopenharmony_ci * f1600000	16000000	UART 0
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct map_desc ap_io_desc[] __initdata __maybe_unused = {
4562306a36Sopenharmony_ci	{
4662306a36Sopenharmony_ci		.virtual	= IO_ADDRESS(INTEGRATOR_IC_BASE),
4762306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(INTEGRATOR_IC_BASE),
4862306a36Sopenharmony_ci		.length		= SZ_4K,
4962306a36Sopenharmony_ci		.type		= MT_DEVICE
5062306a36Sopenharmony_ci	}, {
5162306a36Sopenharmony_ci		.virtual	= IO_ADDRESS(INTEGRATOR_UART0_BASE),
5262306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(INTEGRATOR_UART0_BASE),
5362306a36Sopenharmony_ci		.length		= SZ_4K,
5462306a36Sopenharmony_ci		.type		= MT_DEVICE
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void __init ap_map_io(void)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	iotable_init(ap_io_desc, ARRAY_SIZE(ap_io_desc));
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#ifdef CONFIG_PM
6462306a36Sopenharmony_cistatic unsigned long ic_irq_enable;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int irq_suspend(void)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	ic_irq_enable = readl(VA_IC_BASE + IRQ_ENABLE);
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void irq_resume(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	/* disable all irq sources */
7562306a36Sopenharmony_ci	cm_clear_irqs();
7662306a36Sopenharmony_ci	writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR);
7762306a36Sopenharmony_ci	writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	writel(ic_irq_enable, VA_IC_BASE + IRQ_ENABLE_SET);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci#else
8262306a36Sopenharmony_ci#define irq_suspend NULL
8362306a36Sopenharmony_ci#define irq_resume NULL
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct syscore_ops irq_syscore_ops = {
8762306a36Sopenharmony_ci	.suspend	= irq_suspend,
8862306a36Sopenharmony_ci	.resume		= irq_resume,
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int __init irq_syscore_init(void)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	register_syscore_ops(&irq_syscore_ops);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cidevice_initcall(irq_syscore_init);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * For the PL010 found in the Integrator/AP some of the UART control is
10262306a36Sopenharmony_ci * implemented in the system controller and accessed using a callback
10362306a36Sopenharmony_ci * from the driver.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic void integrator_uart_set_mctrl(struct amba_device *dev,
10662306a36Sopenharmony_ci				void __iomem *base, unsigned int mctrl)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	unsigned int ctrls = 0, ctrlc = 0, rts_mask, dtr_mask;
10962306a36Sopenharmony_ci	u32 phybase = dev->res.start;
11062306a36Sopenharmony_ci	int ret;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (phybase == INTEGRATOR_UART0_BASE) {
11362306a36Sopenharmony_ci		/* UART0 */
11462306a36Sopenharmony_ci		rts_mask = 1 << 4;
11562306a36Sopenharmony_ci		dtr_mask = 1 << 5;
11662306a36Sopenharmony_ci	} else {
11762306a36Sopenharmony_ci		/* UART1 */
11862306a36Sopenharmony_ci		rts_mask = 1 << 6;
11962306a36Sopenharmony_ci		dtr_mask = 1 << 7;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (mctrl & TIOCM_RTS)
12362306a36Sopenharmony_ci		ctrlc |= rts_mask;
12462306a36Sopenharmony_ci	else
12562306a36Sopenharmony_ci		ctrls |= rts_mask;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (mctrl & TIOCM_DTR)
12862306a36Sopenharmony_ci		ctrlc |= dtr_mask;
12962306a36Sopenharmony_ci	else
13062306a36Sopenharmony_ci		ctrls |= dtr_mask;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = regmap_write(ap_syscon_map,
13362306a36Sopenharmony_ci			   INTEGRATOR_SC_CTRLS_OFFSET,
13462306a36Sopenharmony_ci			   ctrls);
13562306a36Sopenharmony_ci	if (ret)
13662306a36Sopenharmony_ci		pr_err("MODEM: unable to write PL010 UART CTRLS\n");
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = regmap_write(ap_syscon_map,
13962306a36Sopenharmony_ci			   INTEGRATOR_SC_CTRLC_OFFSET,
14062306a36Sopenharmony_ci			   ctrlc);
14162306a36Sopenharmony_ci	if (ret)
14262306a36Sopenharmony_ci		pr_err("MODEM: unable to write PL010 UART CRTLC\n");
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistruct amba_pl010_data ap_uart_data = {
14662306a36Sopenharmony_ci	.set_mctrl = integrator_uart_set_mctrl,
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void __init ap_init_irq_of(void)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	cm_init();
15262306a36Sopenharmony_ci	irqchip_init();
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* For the Device Tree, add in the UART callbacks as AUXDATA */
15662306a36Sopenharmony_cistatic struct of_dev_auxdata ap_auxdata_lookup[] __initdata = {
15762306a36Sopenharmony_ci	OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_UART0_BASE,
15862306a36Sopenharmony_ci		"uart0", &ap_uart_data),
15962306a36Sopenharmony_ci	OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_UART1_BASE,
16062306a36Sopenharmony_ci		"uart1", &ap_uart_data),
16162306a36Sopenharmony_ci	{ /* sentinel */ },
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const struct of_device_id ap_syscon_match[] = {
16562306a36Sopenharmony_ci	{ .compatible = "arm,integrator-ap-syscon"},
16662306a36Sopenharmony_ci	{ },
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void __init ap_init_of(void)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct device_node *syscon;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	of_platform_default_populate(NULL, ap_auxdata_lookup, NULL);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	syscon = of_find_matching_node(NULL, ap_syscon_match);
17662306a36Sopenharmony_ci	if (!syscon)
17762306a36Sopenharmony_ci		return;
17862306a36Sopenharmony_ci	ap_syscon_map = syscon_node_to_regmap(syscon);
17962306a36Sopenharmony_ci	if (IS_ERR(ap_syscon_map)) {
18062306a36Sopenharmony_ci		pr_crit("could not find Integrator/AP system controller\n");
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const char * ap_dt_board_compat[] = {
18662306a36Sopenharmony_ci	"arm,integrator-ap",
18762306a36Sopenharmony_ci	NULL,
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciDT_MACHINE_START(INTEGRATOR_AP_DT, "ARM Integrator/AP (Device Tree)")
19162306a36Sopenharmony_ci	.reserve	= integrator_reserve,
19262306a36Sopenharmony_ci	.map_io		= ap_map_io,
19362306a36Sopenharmony_ci	.init_irq	= ap_init_irq_of,
19462306a36Sopenharmony_ci	.init_machine	= ap_init_of,
19562306a36Sopenharmony_ci	.dt_compat      = ap_dt_board_compat,
19662306a36Sopenharmony_ciMACHINE_END
197