1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ID and revision information for mvebu SoCs 4 * 5 * Copyright (C) 2014 Marvell 6 * 7 * Gregory CLEMENT <gregory.clement@free-electrons.com> 8 * 9 * All the mvebu SoCs have information related to their variant and 10 * revision that can be read from the PCI control register. This is 11 * done before the PCI initialization to avoid any conflict. Once the 12 * ID and revision are retrieved, the mapping is freed. 13 */ 14 15 #define pr_fmt(fmt) "mvebu-soc-id: " fmt 16 17 #include <linux/clk.h> 18 #include <linux/init.h> 19 #include <linux/io.h> 20 #include <linux/kernel.h> 21 #include <linux/of.h> 22 #include <linux/of_address.h> 23 #include <linux/slab.h> 24 #include <linux/sys_soc.h> 25 #include "common.h" 26 #include "mvebu-soc-id.h" 27 28 #define PCIE_DEV_ID_OFF 0x0 29 #define PCIE_DEV_REV_OFF 0x8 30 31 #define SOC_ID_MASK 0xFFFF0000 32 #define SOC_REV_MASK 0xFF 33 34 static u32 soc_dev_id; 35 static u32 soc_rev; 36 static bool is_id_valid; 37 38 static const struct of_device_id mvebu_pcie_of_match_table[] = { 39 { .compatible = "marvell,armada-xp-pcie", }, 40 { .compatible = "marvell,armada-370-pcie", }, 41 { .compatible = "marvell,kirkwood-pcie" }, 42 {}, 43 }; 44 45 int mvebu_get_soc_id(u32 *dev, u32 *rev) 46 { 47 if (is_id_valid) { 48 *dev = soc_dev_id; 49 *rev = soc_rev; 50 return 0; 51 } else 52 return -ENODEV; 53 } 54 55 static int __init get_soc_id_by_pci(void) 56 { 57 struct device_node *np; 58 int ret = 0; 59 void __iomem *pci_base; 60 struct clk *clk; 61 struct device_node *child; 62 63 np = of_find_matching_node(NULL, mvebu_pcie_of_match_table); 64 if (!np) 65 return ret; 66 67 /* 68 * ID and revision are available from any port, so we 69 * just pick the first one 70 */ 71 child = of_get_next_child(np, NULL); 72 if (child == NULL) { 73 pr_err("cannot get pci node\n"); 74 ret = -ENOMEM; 75 goto clk_err; 76 } 77 78 clk = of_clk_get_by_name(child, NULL); 79 if (IS_ERR(clk)) { 80 pr_err("cannot get clock\n"); 81 ret = -ENOMEM; 82 goto clk_err; 83 } 84 85 ret = clk_prepare_enable(clk); 86 if (ret) { 87 pr_err("cannot enable clock\n"); 88 goto clk_err; 89 } 90 91 pci_base = of_iomap(child, 0); 92 if (pci_base == NULL) { 93 pr_err("cannot map registers\n"); 94 ret = -ENOMEM; 95 goto res_ioremap; 96 } 97 98 /* SoC ID */ 99 soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16; 100 101 /* SoC revision */ 102 soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; 103 104 is_id_valid = true; 105 106 pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); 107 108 iounmap(pci_base); 109 110 res_ioremap: 111 /* 112 * If the PCIe unit is actually enabled and we have PCI 113 * support in the kernel, we intentionally do not release the 114 * reference to the clock. We want to keep it running since 115 * the bootloader does some PCIe link configuration that the 116 * kernel is for now unable to do, and gating the clock would 117 * make us loose this precious configuration. 118 */ 119 if (!of_device_is_available(child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) { 120 clk_disable_unprepare(clk); 121 clk_put(clk); 122 } 123 124 clk_err: 125 of_node_put(child); 126 of_node_put(np); 127 128 return ret; 129 } 130 131 static int __init mvebu_soc_id_init(void) 132 { 133 134 /* 135 * First try to get the ID and the revision by the system 136 * register and use PCI registers only if it is not possible 137 */ 138 if (!mvebu_system_controller_get_soc_id(&soc_dev_id, &soc_rev)) { 139 is_id_valid = true; 140 pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); 141 return 0; 142 } 143 144 return get_soc_id_by_pci(); 145 } 146 early_initcall(mvebu_soc_id_init); 147 148 static int __init mvebu_soc_device(void) 149 { 150 struct soc_device_attribute *soc_dev_attr; 151 struct soc_device *soc_dev; 152 153 /* Also protects against running on non-mvebu systems */ 154 if (!is_id_valid) 155 return 0; 156 157 soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); 158 if (!soc_dev_attr) 159 return -ENOMEM; 160 161 soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell"); 162 soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev); 163 soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id); 164 165 soc_dev = soc_device_register(soc_dev_attr); 166 if (IS_ERR(soc_dev)) { 167 kfree(soc_dev_attr->family); 168 kfree(soc_dev_attr->revision); 169 kfree(soc_dev_attr->soc_id); 170 kfree(soc_dev_attr); 171 } 172 173 return 0; 174 } 175 postcore_initcall(mvebu_soc_device); 176
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.