162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2003 Deep Blue Solutions Ltd
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/amba/mmci.h>
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/irqchip.h>
962306a36Sopenharmony_ci#include <linux/of_irq.h>
1062306a36Sopenharmony_ci#include <linux/of_address.h>
1162306a36Sopenharmony_ci#include <linux/of_platform.h>
1262306a36Sopenharmony_ci#include <linux/sched_clock.h>
1362306a36Sopenharmony_ci#include <linux/regmap.h>
1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/mach/arch.h>
1762306a36Sopenharmony_ci#include <asm/mach/map.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "integrator-hardware.h"
2062306a36Sopenharmony_ci#include "integrator-cm.h"
2162306a36Sopenharmony_ci#include "integrator.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Base address to the core module header */
2462306a36Sopenharmony_cistatic struct regmap *cm_map;
2562306a36Sopenharmony_ci/* Base address to the CP controller */
2662306a36Sopenharmony_cistatic void __iomem *intcp_con_base;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define CM_COUNTER_OFFSET 0x28
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * Logical      Physical
3262306a36Sopenharmony_ci * f1400000	14000000	Interrupt controller
3362306a36Sopenharmony_ci * f1600000	16000000	UART 0
3462306a36Sopenharmony_ci * fca00000	ca000000	SIC
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct map_desc intcp_io_desc[] __initdata __maybe_unused = {
3862306a36Sopenharmony_ci	{
3962306a36Sopenharmony_ci		.virtual	= IO_ADDRESS(INTEGRATOR_IC_BASE),
4062306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(INTEGRATOR_IC_BASE),
4162306a36Sopenharmony_ci		.length		= SZ_4K,
4262306a36Sopenharmony_ci		.type		= MT_DEVICE
4362306a36Sopenharmony_ci	}, {
4462306a36Sopenharmony_ci		.virtual	= IO_ADDRESS(INTEGRATOR_UART0_BASE),
4562306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(INTEGRATOR_UART0_BASE),
4662306a36Sopenharmony_ci		.length		= SZ_4K,
4762306a36Sopenharmony_ci		.type		= MT_DEVICE
4862306a36Sopenharmony_ci	}, {
4962306a36Sopenharmony_ci		.virtual	= IO_ADDRESS(INTEGRATOR_CP_SIC_BASE),
5062306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(INTEGRATOR_CP_SIC_BASE),
5162306a36Sopenharmony_ci		.length		= SZ_4K,
5262306a36Sopenharmony_ci		.type		= MT_DEVICE
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void __init intcp_map_io(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	iotable_init(intcp_io_desc, ARRAY_SIZE(intcp_io_desc));
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * It seems that the card insertion interrupt remains active after
6362306a36Sopenharmony_ci * we've acknowledged it.  We therefore ignore the interrupt, and
6462306a36Sopenharmony_ci * rely on reading it from the SIC.  This also means that we must
6562306a36Sopenharmony_ci * clear the latched interrupt.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic unsigned int mmc_status(struct device *dev)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	unsigned int status = readl(__io_address(0xca000000 + 4));
7062306a36Sopenharmony_ci	writel(8, intcp_con_base + 8);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return status & 8;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic struct mmci_platform_data mmc_data = {
7662306a36Sopenharmony_ci	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
7762306a36Sopenharmony_ci	.status		= mmc_status,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic u64 notrace intcp_read_sched_clock(void)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	unsigned int val;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* MMIO so discard return code */
8562306a36Sopenharmony_ci	regmap_read(cm_map, CM_COUNTER_OFFSET, &val);
8662306a36Sopenharmony_ci	return val;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void __init intcp_init_early(void)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator");
9262306a36Sopenharmony_ci	if (IS_ERR(cm_map))
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci	sched_clock_register(intcp_read_sched_clock, 32, 24000000);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void __init intcp_init_irq_of(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	cm_init();
10062306a36Sopenharmony_ci	irqchip_init();
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/*
10462306a36Sopenharmony_ci * For the Device Tree, add in the UART, MMC and CLCD specifics as AUXDATA
10562306a36Sopenharmony_ci * and enforce the bus names since these are used for clock lookups.
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_cistatic struct of_dev_auxdata intcp_auxdata_lookup[] __initdata = {
10862306a36Sopenharmony_ci	OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_CP_MMC_BASE,
10962306a36Sopenharmony_ci		"mmci", &mmc_data),
11062306a36Sopenharmony_ci	{ /* sentinel */ },
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic const struct of_device_id intcp_syscon_match[] = {
11462306a36Sopenharmony_ci	{ .compatible = "arm,integrator-cp-syscon"},
11562306a36Sopenharmony_ci	{ },
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void __init intcp_init_of(void)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct device_node *cpcon;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	cpcon = of_find_matching_node(NULL, intcp_syscon_match);
12362306a36Sopenharmony_ci	if (!cpcon)
12462306a36Sopenharmony_ci		return;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	intcp_con_base = of_iomap(cpcon, 0);
12762306a36Sopenharmony_ci	if (!intcp_con_base)
12862306a36Sopenharmony_ci		return;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	of_platform_default_populate(NULL, intcp_auxdata_lookup, NULL);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const char * intcp_dt_board_compat[] = {
13462306a36Sopenharmony_ci	"arm,integrator-cp",
13562306a36Sopenharmony_ci	NULL,
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciDT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)")
13962306a36Sopenharmony_ci	.reserve	= integrator_reserve,
14062306a36Sopenharmony_ci	.map_io		= intcp_map_io,
14162306a36Sopenharmony_ci	.init_early	= intcp_init_early,
14262306a36Sopenharmony_ci	.init_irq	= intcp_init_irq_of,
14362306a36Sopenharmony_ci	.init_machine	= intcp_init_of,
14462306a36Sopenharmony_ci	.dt_compat      = intcp_dt_board_compat,
14562306a36Sopenharmony_ciMACHINE_END
146