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