| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* Copyright 2019 IBM Corp. */ |
| |
| #include <linux/io.h> |
| #include <linux/of.h> |
| #include <linux/of_address.h> |
| #include <linux/of_platform.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/sys_soc.h> |
| |
| static struct { |
| const char *name; |
| const u32 id; |
| } const rev_table[] = { |
| /* AST2400 */ |
| { "AST2400", 0x02000303 }, |
| { "AST1400", 0x02010103 }, |
| { "AST1250", 0x02010303 }, |
| /* AST2500 */ |
| { "AST2500", 0x04000303 }, |
| { "AST2510", 0x04000103 }, |
| { "AST2520", 0x04000203 }, |
| { "AST2530", 0x04000403 }, |
| /* AST2600 */ |
| { "AST2600", 0x05000303 }, |
| { "AST2620", 0x05010203 }, |
| { "AST2605", 0x05030103 }, |
| { "AST2625", 0x05030403 }, |
| }; |
| |
| static const char *siliconid_to_name(u32 siliconid) |
| { |
| unsigned int id = siliconid & 0xff00ffff; |
| unsigned int i; |
| |
| for (i = 0 ; i < ARRAY_SIZE(rev_table) ; ++i) { |
| if (rev_table[i].id == id) |
| return rev_table[i].name; |
| } |
| |
| return "Unknown"; |
| } |
| |
| static const char *siliconid_to_rev(u32 siliconid) |
| { |
| unsigned int rev = (siliconid >> 16) & 0xff; |
| unsigned int gen = (siliconid >> 24) & 0xff; |
| |
| if (gen < 0x5) { |
| /* AST2500 and below */ |
| switch (rev) { |
| case 0: |
| return "A0"; |
| case 1: |
| return "A1"; |
| case 3: |
| return "A2"; |
| } |
| } else { |
| /* AST2600 */ |
| switch (rev) { |
| case 0: |
| return "A0"; |
| case 1: |
| return "A1"; |
| case 2: |
| return "A2"; |
| case 3: |
| return "A3"; |
| } |
| } |
| |
| return "??"; |
| } |
| |
| static int __init aspeed_socinfo_init(void) |
| { |
| struct soc_device_attribute *attrs; |
| struct soc_device *soc_dev; |
| struct device_node *np; |
| void __iomem *reg; |
| bool has_chipid = false; |
| u32 siliconid; |
| u32 chipid[2]; |
| const char *machine = NULL; |
| |
| np = of_find_compatible_node(NULL, NULL, "aspeed,silicon-id"); |
| if (!of_device_is_available(np)) { |
| of_node_put(np); |
| return -ENODEV; |
| } |
| |
| reg = of_iomap(np, 0); |
| if (!reg) { |
| of_node_put(np); |
| return -ENODEV; |
| } |
| siliconid = readl(reg); |
| iounmap(reg); |
| |
| /* This is optional, the ast2400 does not have it */ |
| reg = of_iomap(np, 1); |
| if (reg) { |
| has_chipid = true; |
| chipid[0] = readl(reg); |
| chipid[1] = readl(reg + 4); |
| iounmap(reg); |
| } |
| of_node_put(np); |
| |
| attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); |
| if (!attrs) |
| return -ENODEV; |
| |
| /* |
| * Machine: Romulus BMC |
| * Family: AST2500 |
| * Revision: A1 |
| * SoC ID: raw silicon revision id |
| * Serial Number: 64-bit chipid |
| */ |
| |
| np = of_find_node_by_path("/"); |
| of_property_read_string(np, "model", &machine); |
| if (machine) |
| attrs->machine = kstrdup(machine, GFP_KERNEL); |
| of_node_put(np); |
| |
| attrs->family = siliconid_to_name(siliconid); |
| attrs->revision = siliconid_to_rev(siliconid); |
| attrs->soc_id = kasprintf(GFP_KERNEL, "%08x", siliconid); |
| |
| if (has_chipid) |
| attrs->serial_number = kasprintf(GFP_KERNEL, "%08x%08x", |
| chipid[1], chipid[0]); |
| |
| soc_dev = soc_device_register(attrs); |
| if (IS_ERR(soc_dev)) { |
| kfree(attrs->machine); |
| kfree(attrs->soc_id); |
| kfree(attrs->serial_number); |
| kfree(attrs); |
| return PTR_ERR(soc_dev); |
| } |
| |
| pr_info("ASPEED %s rev %s (%s)\n", |
| attrs->family, |
| attrs->revision, |
| attrs->soc_id); |
| |
| return 0; |
| } |
| early_initcall(aspeed_socinfo_init); |