ux500-soc-id.c (5102B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) ST-Ericsson SA 2010 4 * 5 * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson 6 */ 7 8#include <linux/kernel.h> 9#include <linux/init.h> 10#include <linux/io.h> 11#include <linux/module.h> 12#include <linux/random.h> 13#include <linux/slab.h> 14#include <linux/of.h> 15#include <linux/of_address.h> 16#include <linux/sys_soc.h> 17 18#include <asm/cputype.h> 19#include <asm/tlbflush.h> 20#include <asm/cacheflush.h> 21#include <asm/mach/map.h> 22 23/** 24 * struct dbx500_asic_id - fields of the ASIC ID 25 * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" 26 * @partnumber: hithereto 0x8500 for DB8500 27 * @revision: version code in the series 28 */ 29struct dbx500_asic_id { 30 u16 partnumber; 31 u8 revision; 32 u8 process; 33}; 34 35static struct dbx500_asic_id dbx500_id; 36 37static unsigned int __init ux500_read_asicid(phys_addr_t addr) 38{ 39 void __iomem *virt = ioremap(addr, 4); 40 unsigned int asicid; 41 42 if (!virt) 43 return 0; 44 45 asicid = readl(virt); 46 iounmap(virt); 47 48 return asicid; 49} 50 51static void ux500_print_soc_info(unsigned int asicid) 52{ 53 unsigned int rev = dbx500_id.revision; 54 55 pr_info("DB%4x ", dbx500_id.partnumber); 56 57 if (rev == 0x01) 58 pr_cont("Early Drop"); 59 else if (rev >= 0xA0) 60 pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf); 61 else 62 pr_cont("Unknown"); 63 64 pr_cont(" [%#010x]\n", asicid); 65} 66 67static unsigned int partnumber(unsigned int asicid) 68{ 69 return (asicid >> 8) & 0xffff; 70} 71 72/* 73 * SOC MIDR ASICID ADDRESS ASICID VALUE 74 * DB8500ed 0x410fc090 0x9001FFF4 0x00850001 75 * DB8500v1 0x411fc091 0x9001FFF4 0x008500A0 76 * DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1 77 * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0 78 * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2 79 * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0 80 * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx 81 */ 82 83static void __init ux500_setup_id(void) 84{ 85 unsigned int cpuid = read_cpuid_id(); 86 unsigned int asicid = 0; 87 phys_addr_t addr = 0; 88 89 switch (cpuid) { 90 case 0x410fc090: /* DB8500ed */ 91 case 0x411fc091: /* DB8500v1 */ 92 addr = 0x9001FFF4; 93 break; 94 95 case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */ 96 asicid = ux500_read_asicid(0x9001DBF4); 97 if (partnumber(asicid) == 0x8500 || 98 partnumber(asicid) == 0x8520) 99 /* DB8500v2 */ 100 break; 101 102 /* DB5500v1 */ 103 addr = 0x9001FFF4; 104 break; 105 106 case 0x413fc090: /* DB9540 */ 107 addr = 0xFFFFDBF4; 108 break; 109 } 110 111 if (addr) 112 asicid = ux500_read_asicid(addr); 113 114 if (!asicid) { 115 pr_err("Unable to identify SoC\n"); 116 BUG(); 117 } 118 119 dbx500_id.process = asicid >> 24; 120 dbx500_id.partnumber = partnumber(asicid); 121 dbx500_id.revision = asicid & 0xff; 122 123 ux500_print_soc_info(asicid); 124} 125 126static const char * __init ux500_get_machine(void) 127{ 128 return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber); 129} 130 131static const char * __init ux500_get_family(void) 132{ 133 return kasprintf(GFP_KERNEL, "ux500"); 134} 135 136static const char * __init ux500_get_revision(void) 137{ 138 unsigned int rev = dbx500_id.revision; 139 140 if (rev == 0x01) 141 return kasprintf(GFP_KERNEL, "%s", "ED"); 142 else if (rev >= 0xA0) 143 return kasprintf(GFP_KERNEL, "%d.%d", 144 (rev >> 4) - 0xA + 1, rev & 0xf); 145 146 return kasprintf(GFP_KERNEL, "%s", "Unknown"); 147} 148 149static ssize_t 150process_show(struct device *dev, struct device_attribute *attr, char *buf) 151{ 152 if (dbx500_id.process == 0x00) 153 return sprintf(buf, "Standard\n"); 154 155 return sprintf(buf, "%02xnm\n", dbx500_id.process); 156} 157 158static DEVICE_ATTR_RO(process); 159 160static struct attribute *ux500_soc_attrs[] = { 161 &dev_attr_process.attr, 162 NULL 163}; 164 165ATTRIBUTE_GROUPS(ux500_soc); 166 167static const char *db8500_read_soc_id(struct device_node *backupram) 168{ 169 void __iomem *base; 170 void __iomem *uid; 171 const char *retstr; 172 173 base = of_iomap(backupram, 0); 174 if (!base) 175 return NULL; 176 uid = base + 0x1fc0; 177 178 /* Throw these device-specific numbers into the entropy pool */ 179 add_device_randomness(uid, 0x14); 180 retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", 181 readl((u32 *)uid+0), 182 readl((u32 *)uid+1), readl((u32 *)uid+2), 183 readl((u32 *)uid+3), readl((u32 *)uid+4)); 184 iounmap(base); 185 return retstr; 186} 187 188static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr, 189 struct device_node *backupram) 190{ 191 soc_dev_attr->soc_id = db8500_read_soc_id(backupram); 192 soc_dev_attr->machine = ux500_get_machine(); 193 soc_dev_attr->family = ux500_get_family(); 194 soc_dev_attr->revision = ux500_get_revision(); 195 soc_dev_attr->custom_attr_group = ux500_soc_groups[0]; 196} 197 198static int __init ux500_soc_device_init(void) 199{ 200 struct soc_device *soc_dev; 201 struct soc_device_attribute *soc_dev_attr; 202 struct device_node *backupram; 203 204 backupram = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram"); 205 if (!backupram) 206 return 0; 207 208 ux500_setup_id(); 209 210 soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); 211 if (!soc_dev_attr) { 212 of_node_put(backupram); 213 return -ENOMEM; 214 } 215 216 soc_info_populate(soc_dev_attr, backupram); 217 of_node_put(backupram); 218 219 soc_dev = soc_device_register(soc_dev_attr); 220 if (IS_ERR(soc_dev)) { 221 kfree(soc_dev_attr); 222 return PTR_ERR(soc_dev); 223 } 224 225 return 0; 226} 227subsys_initcall(ux500_soc_device_init);