1a9434e96SKevin Hilman /* 2a9434e96SKevin Hilman * Copyright (c) 2013 Linaro Ltd. 3a9434e96SKevin Hilman * Copyright (c) 2013 Hisilicon Limited. 4a9434e96SKevin Hilman * Based on arch/arm/mach-vexpress/platsmp.c, Copyright (C) 2002 ARM Ltd. 5a9434e96SKevin Hilman * 6a9434e96SKevin Hilman * This program is free software; you can redistribute it and/or modify it 7a9434e96SKevin Hilman * under the terms and conditions of the GNU General Public License, 8a9434e96SKevin Hilman * version 2, as published by the Free Software Foundation. 9a9434e96SKevin Hilman */ 10a9434e96SKevin Hilman #include <linux/smp.h> 11a9434e96SKevin Hilman #include <linux/io.h> 12a9434e96SKevin Hilman #include <linux/of_address.h> 137fda91e7SWang Long #include <linux/delay.h> 14a9434e96SKevin Hilman 15a9434e96SKevin Hilman #include <asm/cacheflush.h> 16a9434e96SKevin Hilman #include <asm/smp_plat.h> 17a9434e96SKevin Hilman #include <asm/smp_scu.h> 187fda91e7SWang Long #include <asm/mach/map.h> 19a9434e96SKevin Hilman 20a9434e96SKevin Hilman #include "core.h" 21a9434e96SKevin Hilman 2206cc5c1dSHaifeng Yan #define HIX5HD2_BOOT_ADDRESS 0xffff0000 2306cc5c1dSHaifeng Yan 24a9434e96SKevin Hilman static void __iomem *ctrl_base; 25a9434e96SKevin Hilman 26a9434e96SKevin Hilman void hi3xxx_set_cpu_jump(int cpu, void *jump_addr) 27a9434e96SKevin Hilman { 28a9434e96SKevin Hilman cpu = cpu_logical_map(cpu); 29a9434e96SKevin Hilman if (!cpu || !ctrl_base) 30a9434e96SKevin Hilman return; 3164fc2a94SFlorian Fainelli writel_relaxed(__pa_symbol(jump_addr), ctrl_base + ((cpu - 1) << 2)); 32a9434e96SKevin Hilman } 33a9434e96SKevin Hilman 34a9434e96SKevin Hilman int hi3xxx_get_cpu_jump(int cpu) 35a9434e96SKevin Hilman { 36a9434e96SKevin Hilman cpu = cpu_logical_map(cpu); 37a9434e96SKevin Hilman if (!cpu || !ctrl_base) 38a9434e96SKevin Hilman return 0; 39a9434e96SKevin Hilman return readl_relaxed(ctrl_base + ((cpu - 1) << 2)); 40a9434e96SKevin Hilman } 41a9434e96SKevin Hilman 4206cc5c1dSHaifeng Yan static void __init hisi_enable_scu_a9(void) 43a9434e96SKevin Hilman { 44a9434e96SKevin Hilman unsigned long base = 0; 45a9434e96SKevin Hilman void __iomem *scu_base = NULL; 46a9434e96SKevin Hilman 47a9434e96SKevin Hilman if (scu_a9_has_base()) { 48a9434e96SKevin Hilman base = scu_a9_get_base(); 49a9434e96SKevin Hilman scu_base = ioremap(base, SZ_4K); 50a9434e96SKevin Hilman if (!scu_base) { 51a9434e96SKevin Hilman pr_err("ioremap(scu_base) failed\n"); 52a9434e96SKevin Hilman return; 53a9434e96SKevin Hilman } 54a9434e96SKevin Hilman scu_enable(scu_base); 55a9434e96SKevin Hilman iounmap(scu_base); 56a9434e96SKevin Hilman } 5706cc5c1dSHaifeng Yan } 5806cc5c1dSHaifeng Yan 5906cc5c1dSHaifeng Yan static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus) 6006cc5c1dSHaifeng Yan { 6106cc5c1dSHaifeng Yan struct device_node *np = NULL; 6206cc5c1dSHaifeng Yan u32 offset = 0; 6306cc5c1dSHaifeng Yan 6406cc5c1dSHaifeng Yan hisi_enable_scu_a9(); 65a9434e96SKevin Hilman if (!ctrl_base) { 66a9434e96SKevin Hilman np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 67a9434e96SKevin Hilman if (!np) { 68a9434e96SKevin Hilman pr_err("failed to find hisilicon,sysctrl node\n"); 69a9434e96SKevin Hilman return; 70a9434e96SKevin Hilman } 71a9434e96SKevin Hilman ctrl_base = of_iomap(np, 0); 72a9434e96SKevin Hilman if (!ctrl_base) { 73a9434e96SKevin Hilman pr_err("failed to map address\n"); 74a9434e96SKevin Hilman return; 75a9434e96SKevin Hilman } 76a9434e96SKevin Hilman if (of_property_read_u32(np, "smp-offset", &offset) < 0) { 77a9434e96SKevin Hilman pr_err("failed to find smp-offset property\n"); 78a9434e96SKevin Hilman return; 79a9434e96SKevin Hilman } 80a9434e96SKevin Hilman ctrl_base += offset; 81a9434e96SKevin Hilman } 82a9434e96SKevin Hilman } 83a9434e96SKevin Hilman 84a9434e96SKevin Hilman static int hi3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle) 85a9434e96SKevin Hilman { 8622bae429SZhangfei Gao hi3xxx_set_cpu(cpu, true); 87a9434e96SKevin Hilman hi3xxx_set_cpu_jump(cpu, secondary_startup); 88a9434e96SKevin Hilman arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 89a9434e96SKevin Hilman return 0; 90a9434e96SKevin Hilman } 91a9434e96SKevin Hilman 9275305275SMasahiro Yamada static const struct smp_operations hi3xxx_smp_ops __initconst = { 93a9434e96SKevin Hilman .smp_prepare_cpus = hi3xxx_smp_prepare_cpus, 94a9434e96SKevin Hilman .smp_boot_secondary = hi3xxx_boot_secondary, 9522bae429SZhangfei Gao #ifdef CONFIG_HOTPLUG_CPU 9622bae429SZhangfei Gao .cpu_die = hi3xxx_cpu_die, 9722bae429SZhangfei Gao .cpu_kill = hi3xxx_cpu_kill, 9822bae429SZhangfei Gao #endif 99a9434e96SKevin Hilman }; 10006cc5c1dSHaifeng Yan 101e243f943SWang Long static void __init hisi_common_smp_prepare_cpus(unsigned int max_cpus) 10206cc5c1dSHaifeng Yan { 10306cc5c1dSHaifeng Yan hisi_enable_scu_a9(); 10406cc5c1dSHaifeng Yan } 10506cc5c1dSHaifeng Yan 10609ca62f5SBen Dooks static void hix5hd2_set_scu_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) 10706cc5c1dSHaifeng Yan { 10806cc5c1dSHaifeng Yan void __iomem *virt; 10906cc5c1dSHaifeng Yan 11006cc5c1dSHaifeng Yan virt = ioremap(start_addr, PAGE_SIZE); 11106cc5c1dSHaifeng Yan 112*d2057bbbSYunzhi Li writel_relaxed(0xe51ff004, virt); /* ldr pc, [pc, #-4] */ 11306cc5c1dSHaifeng Yan writel_relaxed(jump_addr, virt + 4); /* pc jump phy address */ 11406cc5c1dSHaifeng Yan iounmap(virt); 11506cc5c1dSHaifeng Yan } 11606cc5c1dSHaifeng Yan 11706cc5c1dSHaifeng Yan static int hix5hd2_boot_secondary(unsigned int cpu, struct task_struct *idle) 11806cc5c1dSHaifeng Yan { 11906cc5c1dSHaifeng Yan phys_addr_t jumpaddr; 12006cc5c1dSHaifeng Yan 12164fc2a94SFlorian Fainelli jumpaddr = __pa_symbol(secondary_startup); 12206cc5c1dSHaifeng Yan hix5hd2_set_scu_boot_addr(HIX5HD2_BOOT_ADDRESS, jumpaddr); 12306cc5c1dSHaifeng Yan hix5hd2_set_cpu(cpu, true); 12406cc5c1dSHaifeng Yan arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 12506cc5c1dSHaifeng Yan return 0; 12606cc5c1dSHaifeng Yan } 12706cc5c1dSHaifeng Yan 12806cc5c1dSHaifeng Yan 12975305275SMasahiro Yamada static const struct smp_operations hix5hd2_smp_ops __initconst = { 130e243f943SWang Long .smp_prepare_cpus = hisi_common_smp_prepare_cpus, 13106cc5c1dSHaifeng Yan .smp_boot_secondary = hix5hd2_boot_secondary, 13206cc5c1dSHaifeng Yan #ifdef CONFIG_HOTPLUG_CPU 13306cc5c1dSHaifeng Yan .cpu_die = hix5hd2_cpu_die, 13406cc5c1dSHaifeng Yan #endif 13506cc5c1dSHaifeng Yan }; 136c2fff85eSHaojian Zhuang 1377fda91e7SWang Long 1387fda91e7SWang Long #define SC_SCTL_REMAP_CLR 0x00000100 1397fda91e7SWang Long #define HIP01_BOOT_ADDRESS 0x80000000 1407fda91e7SWang Long #define REG_SC_CTRL 0x000 1417fda91e7SWang Long 14209ca62f5SBen Dooks static void hip01_set_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) 1437fda91e7SWang Long { 1447fda91e7SWang Long void __iomem *virt; 1457fda91e7SWang Long 1467fda91e7SWang Long virt = phys_to_virt(start_addr); 1477fda91e7SWang Long 1487fda91e7SWang Long writel_relaxed(0xe51ff004, virt); 1497fda91e7SWang Long writel_relaxed(jump_addr, virt + 4); 1507fda91e7SWang Long } 1517fda91e7SWang Long 1527fda91e7SWang Long static int hip01_boot_secondary(unsigned int cpu, struct task_struct *idle) 1537fda91e7SWang Long { 1547fda91e7SWang Long phys_addr_t jumpaddr; 1557fda91e7SWang Long unsigned int remap_reg_value = 0; 1567fda91e7SWang Long struct device_node *node; 1577fda91e7SWang Long 1587fda91e7SWang Long 15964fc2a94SFlorian Fainelli jumpaddr = __pa_symbol(secondary_startup); 1607fda91e7SWang Long hip01_set_boot_addr(HIP01_BOOT_ADDRESS, jumpaddr); 1617fda91e7SWang Long 1627fda91e7SWang Long node = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl"); 1637fda91e7SWang Long if (WARN_ON(!node)) 1647fda91e7SWang Long return -1; 1657fda91e7SWang Long ctrl_base = of_iomap(node, 0); 1667fda91e7SWang Long 1677fda91e7SWang Long /* set the secondary core boot from DDR */ 1687fda91e7SWang Long remap_reg_value = readl_relaxed(ctrl_base + REG_SC_CTRL); 1697fda91e7SWang Long barrier(); 1707fda91e7SWang Long remap_reg_value |= SC_SCTL_REMAP_CLR; 1717fda91e7SWang Long barrier(); 1727fda91e7SWang Long writel_relaxed(remap_reg_value, ctrl_base + REG_SC_CTRL); 1737fda91e7SWang Long 1747fda91e7SWang Long hip01_set_cpu(cpu, true); 1757fda91e7SWang Long 1767fda91e7SWang Long return 0; 1777fda91e7SWang Long } 1787fda91e7SWang Long 17975305275SMasahiro Yamada static const struct smp_operations hip01_smp_ops __initconst = { 1807fda91e7SWang Long .smp_prepare_cpus = hisi_common_smp_prepare_cpus, 1817fda91e7SWang Long .smp_boot_secondary = hip01_boot_secondary, 1827fda91e7SWang Long }; 1837fda91e7SWang Long 184c2fff85eSHaojian Zhuang CPU_METHOD_OF_DECLARE(hi3xxx_smp, "hisilicon,hi3620-smp", &hi3xxx_smp_ops); 185c2fff85eSHaojian Zhuang CPU_METHOD_OF_DECLARE(hix5hd2_smp, "hisilicon,hix5hd2-smp", &hix5hd2_smp_ops); 1867fda91e7SWang Long CPU_METHOD_OF_DECLARE(hip01_smp, "hisilicon,hip01-smp", &hip01_smp_ops); 187