18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * ID and revision information for mvebu SoCs 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 98c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 108c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * All the mvebu SoCs have information related to their variant and 138c2ecf20Sopenharmony_ci * revision that can be read from the PCI control register. This is 148c2ecf20Sopenharmony_ci * done before the PCI initialization to avoid any conflict. Once the 158c2ecf20Sopenharmony_ci * ID and revision are retrieved, the mapping is freed. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "mvebu-soc-id: " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/of_address.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/sys_soc.h> 288c2ecf20Sopenharmony_ci#include "common.h" 298c2ecf20Sopenharmony_ci#include "mvebu-soc-id.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PCIE_DEV_ID_OFF 0x0 328c2ecf20Sopenharmony_ci#define PCIE_DEV_REV_OFF 0x8 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define SOC_ID_MASK 0xFFFF0000 358c2ecf20Sopenharmony_ci#define SOC_REV_MASK 0xFF 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic u32 soc_dev_id; 388c2ecf20Sopenharmony_cistatic u32 soc_rev; 398c2ecf20Sopenharmony_cistatic bool is_id_valid; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct of_device_id mvebu_pcie_of_match_table[] = { 428c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-xp-pcie", }, 438c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-370-pcie", }, 448c2ecf20Sopenharmony_ci { .compatible = "marvell,kirkwood-pcie" }, 458c2ecf20Sopenharmony_ci {}, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciint mvebu_get_soc_id(u32 *dev, u32 *rev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci if (is_id_valid) { 518c2ecf20Sopenharmony_ci *dev = soc_dev_id; 528c2ecf20Sopenharmony_ci *rev = soc_rev; 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci } else 558c2ecf20Sopenharmony_ci return -ENODEV; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int __init get_soc_id_by_pci(void) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct device_node *np; 618c2ecf20Sopenharmony_ci int ret = 0; 628c2ecf20Sopenharmony_ci void __iomem *pci_base; 638c2ecf20Sopenharmony_ci struct clk *clk; 648c2ecf20Sopenharmony_ci struct device_node *child; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci np = of_find_matching_node(NULL, mvebu_pcie_of_match_table); 678c2ecf20Sopenharmony_ci if (!np) 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * ID and revision are available from any port, so we 728c2ecf20Sopenharmony_ci * just pick the first one 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci child = of_get_next_child(np, NULL); 758c2ecf20Sopenharmony_ci if (child == NULL) { 768c2ecf20Sopenharmony_ci pr_err("cannot get pci node\n"); 778c2ecf20Sopenharmony_ci ret = -ENOMEM; 788c2ecf20Sopenharmony_ci goto clk_err; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci clk = of_clk_get_by_name(child, NULL); 828c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 838c2ecf20Sopenharmony_ci pr_err("cannot get clock\n"); 848c2ecf20Sopenharmony_ci ret = -ENOMEM; 858c2ecf20Sopenharmony_ci goto clk_err; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 898c2ecf20Sopenharmony_ci if (ret) { 908c2ecf20Sopenharmony_ci pr_err("cannot enable clock\n"); 918c2ecf20Sopenharmony_ci goto clk_err; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci pci_base = of_iomap(child, 0); 958c2ecf20Sopenharmony_ci if (pci_base == NULL) { 968c2ecf20Sopenharmony_ci pr_err("cannot map registers\n"); 978c2ecf20Sopenharmony_ci ret = -ENOMEM; 988c2ecf20Sopenharmony_ci goto res_ioremap; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* SoC ID */ 1028c2ecf20Sopenharmony_ci soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* SoC revision */ 1058c2ecf20Sopenharmony_ci soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci is_id_valid = true; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci iounmap(pci_base); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cires_ioremap: 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * If the PCIe unit is actually enabled and we have PCI 1168c2ecf20Sopenharmony_ci * support in the kernel, we intentionally do not release the 1178c2ecf20Sopenharmony_ci * reference to the clock. We want to keep it running since 1188c2ecf20Sopenharmony_ci * the bootloader does some PCIe link configuration that the 1198c2ecf20Sopenharmony_ci * kernel is for now unable to do, and gating the clock would 1208c2ecf20Sopenharmony_ci * make us loose this precious configuration. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci if (!of_device_is_available(child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) { 1238c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 1248c2ecf20Sopenharmony_ci clk_put(clk); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ciclk_err: 1288c2ecf20Sopenharmony_ci of_node_put(child); 1298c2ecf20Sopenharmony_ci of_node_put(np); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int __init mvebu_soc_id_init(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * First try to get the ID and the revision by the system 1398c2ecf20Sopenharmony_ci * register and use PCI registers only if it is not possible 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci if (!mvebu_system_controller_get_soc_id(&soc_dev_id, &soc_rev)) { 1428c2ecf20Sopenharmony_ci is_id_valid = true; 1438c2ecf20Sopenharmony_ci pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return get_soc_id_by_pci(); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciearly_initcall(mvebu_soc_id_init); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int __init mvebu_soc_device(void) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct soc_device_attribute *soc_dev_attr; 1548c2ecf20Sopenharmony_ci struct soc_device *soc_dev; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Also protects against running on non-mvebu systems */ 1578c2ecf20Sopenharmony_ci if (!is_id_valid) 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (!soc_dev_attr) 1628c2ecf20Sopenharmony_ci return -ENOMEM; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell"); 1658c2ecf20Sopenharmony_ci soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev); 1668c2ecf20Sopenharmony_ci soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci soc_dev = soc_device_register(soc_dev_attr); 1698c2ecf20Sopenharmony_ci if (IS_ERR(soc_dev)) { 1708c2ecf20Sopenharmony_ci kfree(soc_dev_attr->family); 1718c2ecf20Sopenharmony_ci kfree(soc_dev_attr->revision); 1728c2ecf20Sopenharmony_ci kfree(soc_dev_attr->soc_id); 1738c2ecf20Sopenharmony_ci kfree(soc_dev_attr); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_cipostcore_initcall(mvebu_soc_device); 179