18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * System controller support for Armada 370, 375 and XP platforms. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Lior Amsalem <alior@marvell.com> 78c2ecf20Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com> 88c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 118c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 128c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The Armada 370, 375 and Armada XP SoCs have a range of 158c2ecf20Sopenharmony_ci * miscellaneous registers, that do not belong to a particular device, 168c2ecf20Sopenharmony_ci * but rather provide system-level features. This basic 178c2ecf20Sopenharmony_ci * system-controller driver provides a device tree binding for those 188c2ecf20Sopenharmony_ci * registers, and implements utility functions offering various 198c2ecf20Sopenharmony_ci * features related to those registers. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * For now, the feature set is limited to restarting the platform by a 228c2ecf20Sopenharmony_ci * soft-reset, but it might be extended in the future. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/init.h> 278c2ecf20Sopenharmony_ci#include <linux/of_address.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci#include <linux/reboot.h> 308c2ecf20Sopenharmony_ci#include "common.h" 318c2ecf20Sopenharmony_ci#include "mvebu-soc-id.h" 328c2ecf20Sopenharmony_ci#include "pmsu.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define ARMADA_375_CRYPT0_ENG_TARGET 41 358c2ecf20Sopenharmony_ci#define ARMADA_375_CRYPT0_ENG_ATTR 1 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void __iomem *system_controller_base; 388c2ecf20Sopenharmony_cistatic phys_addr_t system_controller_phys_base; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct mvebu_system_controller { 418c2ecf20Sopenharmony_ci u32 rstoutn_mask_offset; 428c2ecf20Sopenharmony_ci u32 system_soft_reset_offset; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci u32 rstoutn_mask_reset_out_en; 458c2ecf20Sopenharmony_ci u32 system_soft_reset; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci u32 resume_boot_addr; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci u32 dev_id; 508c2ecf20Sopenharmony_ci u32 rev_id; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_cistatic struct mvebu_system_controller *mvebu_sc; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct mvebu_system_controller armada_370_xp_system_controller = { 558c2ecf20Sopenharmony_ci .rstoutn_mask_offset = 0x60, 568c2ecf20Sopenharmony_ci .system_soft_reset_offset = 0x64, 578c2ecf20Sopenharmony_ci .rstoutn_mask_reset_out_en = 0x1, 588c2ecf20Sopenharmony_ci .system_soft_reset = 0x1, 598c2ecf20Sopenharmony_ci .dev_id = 0x38, 608c2ecf20Sopenharmony_ci .rev_id = 0x3c, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const struct mvebu_system_controller armada_375_system_controller = { 648c2ecf20Sopenharmony_ci .rstoutn_mask_offset = 0x54, 658c2ecf20Sopenharmony_ci .system_soft_reset_offset = 0x58, 668c2ecf20Sopenharmony_ci .rstoutn_mask_reset_out_en = 0x1, 678c2ecf20Sopenharmony_ci .system_soft_reset = 0x1, 688c2ecf20Sopenharmony_ci .resume_boot_addr = 0xd4, 698c2ecf20Sopenharmony_ci .dev_id = 0x38, 708c2ecf20Sopenharmony_ci .rev_id = 0x3c, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct mvebu_system_controller orion_system_controller = { 748c2ecf20Sopenharmony_ci .rstoutn_mask_offset = 0x108, 758c2ecf20Sopenharmony_ci .system_soft_reset_offset = 0x10c, 768c2ecf20Sopenharmony_ci .rstoutn_mask_reset_out_en = 0x4, 778c2ecf20Sopenharmony_ci .system_soft_reset = 0x1, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct of_device_id of_system_controller_table[] = { 818c2ecf20Sopenharmony_ci { 828c2ecf20Sopenharmony_ci .compatible = "marvell,orion-system-controller", 838c2ecf20Sopenharmony_ci .data = (void *) &orion_system_controller, 848c2ecf20Sopenharmony_ci }, { 858c2ecf20Sopenharmony_ci .compatible = "marvell,armada-370-xp-system-controller", 868c2ecf20Sopenharmony_ci .data = (void *) &armada_370_xp_system_controller, 878c2ecf20Sopenharmony_ci }, { 888c2ecf20Sopenharmony_ci .compatible = "marvell,armada-375-system-controller", 898c2ecf20Sopenharmony_ci .data = (void *) &armada_375_system_controller, 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci { /* end of list */ }, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_civoid mvebu_restart(enum reboot_mode mode, const char *cmd) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci if (!system_controller_base) { 978c2ecf20Sopenharmony_ci pr_err("Cannot restart, system-controller not available: check the device tree\n"); 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Enable soft reset to assert RSTOUTn. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci writel(mvebu_sc->rstoutn_mask_reset_out_en, 1038c2ecf20Sopenharmony_ci system_controller_base + 1048c2ecf20Sopenharmony_ci mvebu_sc->rstoutn_mask_offset); 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * Assert soft reset. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci writel(mvebu_sc->system_soft_reset, 1098c2ecf20Sopenharmony_ci system_controller_base + 1108c2ecf20Sopenharmony_ci mvebu_sc->system_soft_reset_offset); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci while (1) 1148c2ecf20Sopenharmony_ci ; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciint mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci if (of_machine_is_compatible("marvell,armada380") && 1208c2ecf20Sopenharmony_ci system_controller_base) { 1218c2ecf20Sopenharmony_ci *dev = readl(system_controller_base + mvebu_sc->dev_id) >> 16; 1228c2ecf20Sopenharmony_ci *rev = (readl(system_controller_base + mvebu_sc->rev_id) >> 8) 1238c2ecf20Sopenharmony_ci & 0xF; 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci } else 1268c2ecf20Sopenharmony_ci return -ENODEV; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#if defined(CONFIG_SMP) && defined(CONFIG_MACH_MVEBU_V7) 1308c2ecf20Sopenharmony_cistatic void mvebu_armada375_smp_wa_init(void) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci u32 dev, rev; 1338c2ecf20Sopenharmony_ci phys_addr_t resume_addr_reg; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (mvebu_get_soc_id(&dev, &rev) != 0) 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (rev != ARMADA_375_Z1_REV) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci resume_addr_reg = system_controller_phys_base + 1428c2ecf20Sopenharmony_ci mvebu_sc->resume_boot_addr; 1438c2ecf20Sopenharmony_ci mvebu_setup_boot_addr_wa(ARMADA_375_CRYPT0_ENG_TARGET, 1448c2ecf20Sopenharmony_ci ARMADA_375_CRYPT0_ENG_ATTR, 1458c2ecf20Sopenharmony_ci resume_addr_reg); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci BUG_ON(system_controller_base == NULL); 1518c2ecf20Sopenharmony_ci BUG_ON(mvebu_sc->resume_boot_addr == 0); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (of_machine_is_compatible("marvell,armada375")) 1548c2ecf20Sopenharmony_ci mvebu_armada375_smp_wa_init(); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci writel(__pa_symbol(boot_addr), system_controller_base + 1578c2ecf20Sopenharmony_ci mvebu_sc->resume_boot_addr); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci#endif 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int __init mvebu_system_controller_init(void) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci const struct of_device_id *match; 1648c2ecf20Sopenharmony_ci struct device_node *np; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci np = of_find_matching_node_and_match(NULL, of_system_controller_table, 1678c2ecf20Sopenharmony_ci &match); 1688c2ecf20Sopenharmony_ci if (np) { 1698c2ecf20Sopenharmony_ci struct resource res; 1708c2ecf20Sopenharmony_ci system_controller_base = of_iomap(np, 0); 1718c2ecf20Sopenharmony_ci of_address_to_resource(np, 0, &res); 1728c2ecf20Sopenharmony_ci system_controller_phys_base = res.start; 1738c2ecf20Sopenharmony_ci mvebu_sc = (struct mvebu_system_controller *)match->data; 1748c2ecf20Sopenharmony_ci of_node_put(np); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciearly_initcall(mvebu_system_controller_init); 181