162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Support for Ingenic SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
662306a36Sopenharmony_ci * Copyright (C) 2011, Maarten ter Huurne <maarten@treewalker.org>
762306a36Sopenharmony_ci * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/of_fdt.h>
1462306a36Sopenharmony_ci#include <linux/pm.h>
1562306a36Sopenharmony_ci#include <linux/sizes.h>
1662306a36Sopenharmony_ci#include <linux/suspend.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/bootinfo.h>
2062306a36Sopenharmony_ci#include <asm/io.h>
2162306a36Sopenharmony_ci#include <asm/machine.h>
2262306a36Sopenharmony_ci#include <asm/reboot.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic __init char *ingenic_get_system_type(unsigned long machtype)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	switch (machtype) {
2762306a36Sopenharmony_ci	case MACH_INGENIC_X2100:
2862306a36Sopenharmony_ci		return "X2100";
2962306a36Sopenharmony_ci	case MACH_INGENIC_X2000H:
3062306a36Sopenharmony_ci		return "X2000H";
3162306a36Sopenharmony_ci	case MACH_INGENIC_X2000E:
3262306a36Sopenharmony_ci		return "X2000E";
3362306a36Sopenharmony_ci	case MACH_INGENIC_X2000:
3462306a36Sopenharmony_ci		return "X2000";
3562306a36Sopenharmony_ci	case MACH_INGENIC_X1830:
3662306a36Sopenharmony_ci		return "X1830";
3762306a36Sopenharmony_ci	case MACH_INGENIC_X1000E:
3862306a36Sopenharmony_ci		return "X1000E";
3962306a36Sopenharmony_ci	case MACH_INGENIC_X1000:
4062306a36Sopenharmony_ci		return "X1000";
4162306a36Sopenharmony_ci	case MACH_INGENIC_JZ4780:
4262306a36Sopenharmony_ci		return "JZ4780";
4362306a36Sopenharmony_ci	case MACH_INGENIC_JZ4775:
4462306a36Sopenharmony_ci		return "JZ4775";
4562306a36Sopenharmony_ci	case MACH_INGENIC_JZ4770:
4662306a36Sopenharmony_ci		return "JZ4770";
4762306a36Sopenharmony_ci	case MACH_INGENIC_JZ4760B:
4862306a36Sopenharmony_ci		return "JZ4760B";
4962306a36Sopenharmony_ci	case MACH_INGENIC_JZ4760:
5062306a36Sopenharmony_ci		return "JZ4760";
5162306a36Sopenharmony_ci	case MACH_INGENIC_JZ4755:
5262306a36Sopenharmony_ci		return "JZ4755";
5362306a36Sopenharmony_ci	case MACH_INGENIC_JZ4750:
5462306a36Sopenharmony_ci		return "JZ4750";
5562306a36Sopenharmony_ci	case MACH_INGENIC_JZ4725B:
5662306a36Sopenharmony_ci		return "JZ4725B";
5762306a36Sopenharmony_ci	case MACH_INGENIC_JZ4730:
5862306a36Sopenharmony_ci		return "JZ4730";
5962306a36Sopenharmony_ci	default:
6062306a36Sopenharmony_ci		return "JZ4740";
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define INGENIC_CGU_BASE	0x10000000
6562306a36Sopenharmony_ci#define JZ4750_CGU_CPCCR_ECS	BIT(30)
6662306a36Sopenharmony_ci#define JZ4760_CGU_CPCCR_ECS	BIT(31)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic __init void ingenic_force_12M_ext(const void *fdt, unsigned int mask)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	const __be32 *prop;
7162306a36Sopenharmony_ci	unsigned int cpccr;
7262306a36Sopenharmony_ci	void __iomem *cgu;
7362306a36Sopenharmony_ci	bool use_div;
7462306a36Sopenharmony_ci	int offset;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	offset = fdt_path_offset(fdt, "/ext");
7762306a36Sopenharmony_ci	if (offset < 0)
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	prop = fdt_getprop(fdt, offset, "clock-frequency", NULL);
8162306a36Sopenharmony_ci	if (!prop)
8262306a36Sopenharmony_ci		return;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/*
8562306a36Sopenharmony_ci	 * If the external oscillator is 24 MHz, enable the /2 divider to
8662306a36Sopenharmony_ci	 * drive it down to 12 MHz, since this is what the hardware can work
8762306a36Sopenharmony_ci	 * with.
8862306a36Sopenharmony_ci	 * The 16 MHz cutoff value is arbitrary; setting it to 12 MHz would not
8962306a36Sopenharmony_ci	 * work as the crystal frequency (as reported in the Device Tree) might
9062306a36Sopenharmony_ci	 * be slightly above this value.
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	use_div = be32_to_cpup(prop) >= 16000000;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	cgu = ioremap(INGENIC_CGU_BASE, 0x4);
9562306a36Sopenharmony_ci	if (!cgu)
9662306a36Sopenharmony_ci		return;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	cpccr = ioread32(cgu);
9962306a36Sopenharmony_ci	if (use_div)
10062306a36Sopenharmony_ci		cpccr |= mask;
10162306a36Sopenharmony_ci	else
10262306a36Sopenharmony_ci		cpccr &= ~mask;
10362306a36Sopenharmony_ci	iowrite32(cpccr, cgu);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	iounmap(cgu);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic __init const void *ingenic_fixup_fdt(const void *fdt, const void *match_data)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	/*
11162306a36Sopenharmony_ci	 * Old devicetree files for the qi,lb60 board did not have a /memory
11262306a36Sopenharmony_ci	 * node. Hardcode the memory info here.
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	if (!fdt_node_check_compatible(fdt, 0, "qi,lb60") &&
11562306a36Sopenharmony_ci	    fdt_path_offset(fdt, "/memory") < 0)
11662306a36Sopenharmony_ci		early_init_dt_add_memory_arch(0, SZ_32M);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	mips_machtype = (unsigned long)match_data;
11962306a36Sopenharmony_ci	system_type = ingenic_get_system_type(mips_machtype);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	switch (mips_machtype) {
12262306a36Sopenharmony_ci	case MACH_INGENIC_JZ4750:
12362306a36Sopenharmony_ci	case MACH_INGENIC_JZ4755:
12462306a36Sopenharmony_ci		ingenic_force_12M_ext(fdt, JZ4750_CGU_CPCCR_ECS);
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	case MACH_INGENIC_JZ4760:
12762306a36Sopenharmony_ci		ingenic_force_12M_ext(fdt, JZ4760_CGU_CPCCR_ECS);
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	default:
13062306a36Sopenharmony_ci		break;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return fdt;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic const struct of_device_id ingenic_of_match[] __initconst = {
13762306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4730", .data = (void *)MACH_INGENIC_JZ4730 },
13862306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4740", .data = (void *)MACH_INGENIC_JZ4740 },
13962306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4725b", .data = (void *)MACH_INGENIC_JZ4725B },
14062306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4750", .data = (void *)MACH_INGENIC_JZ4750 },
14162306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4755", .data = (void *)MACH_INGENIC_JZ4755 },
14262306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4760", .data = (void *)MACH_INGENIC_JZ4760 },
14362306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4760b", .data = (void *)MACH_INGENIC_JZ4760B },
14462306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4770", .data = (void *)MACH_INGENIC_JZ4770 },
14562306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4775", .data = (void *)MACH_INGENIC_JZ4775 },
14662306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4780", .data = (void *)MACH_INGENIC_JZ4780 },
14762306a36Sopenharmony_ci	{ .compatible = "ingenic,x1000", .data = (void *)MACH_INGENIC_X1000 },
14862306a36Sopenharmony_ci	{ .compatible = "ingenic,x1000e", .data = (void *)MACH_INGENIC_X1000E },
14962306a36Sopenharmony_ci	{ .compatible = "ingenic,x1830", .data = (void *)MACH_INGENIC_X1830 },
15062306a36Sopenharmony_ci	{ .compatible = "ingenic,x2000", .data = (void *)MACH_INGENIC_X2000 },
15162306a36Sopenharmony_ci	{ .compatible = "ingenic,x2000e", .data = (void *)MACH_INGENIC_X2000E },
15262306a36Sopenharmony_ci	{ .compatible = "ingenic,x2000h", .data = (void *)MACH_INGENIC_X2000H },
15362306a36Sopenharmony_ci	{ .compatible = "ingenic,x2100", .data = (void *)MACH_INGENIC_X2100 },
15462306a36Sopenharmony_ci	{}
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ciMIPS_MACHINE(ingenic) = {
15862306a36Sopenharmony_ci	.matches = ingenic_of_match,
15962306a36Sopenharmony_ci	.fixup_fdt = ingenic_fixup_fdt,
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void ingenic_wait_instr(void)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	__asm__(".set push;\n"
16562306a36Sopenharmony_ci		".set mips3;\n"
16662306a36Sopenharmony_ci		"wait;\n"
16762306a36Sopenharmony_ci		".set pop;\n"
16862306a36Sopenharmony_ci	);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void ingenic_halt(void)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	for (;;)
17462306a36Sopenharmony_ci		ingenic_wait_instr();
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int ingenic_pm_enter(suspend_state_t state)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	ingenic_wait_instr();
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic const struct platform_suspend_ops ingenic_pm_ops = {
18562306a36Sopenharmony_ci	.valid = suspend_valid_only_mem,
18662306a36Sopenharmony_ci	.enter = ingenic_pm_enter,
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int __init ingenic_pm_init(void)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	if (boot_cpu_type() == CPU_XBURST) {
19262306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_PM_SLEEP))
19362306a36Sopenharmony_ci			suspend_set_ops(&ingenic_pm_ops);
19462306a36Sopenharmony_ci		_machine_halt = ingenic_halt;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_cilate_initcall(ingenic_pm_init);
201