18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/random.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/sys_soc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/cputype.h> 198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 218c2ecf20Sopenharmony_ci#include <asm/mach/map.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * struct dbx500_asic_id - fields of the ASIC ID 258c2ecf20Sopenharmony_ci * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" 268c2ecf20Sopenharmony_ci * @partnumber: hithereto 0x8500 for DB8500 278c2ecf20Sopenharmony_ci * @revision: version code in the series 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistruct dbx500_asic_id { 308c2ecf20Sopenharmony_ci u16 partnumber; 318c2ecf20Sopenharmony_ci u8 revision; 328c2ecf20Sopenharmony_ci u8 process; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct dbx500_asic_id dbx500_id; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic unsigned int __init ux500_read_asicid(phys_addr_t addr) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci void __iomem *virt = ioremap(addr, 4); 408c2ecf20Sopenharmony_ci unsigned int asicid; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!virt) 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci asicid = readl(virt); 468c2ecf20Sopenharmony_ci iounmap(virt); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return asicid; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void ux500_print_soc_info(unsigned int asicid) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci unsigned int rev = dbx500_id.revision; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci pr_info("DB%4x ", dbx500_id.partnumber); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (rev == 0x01) 588c2ecf20Sopenharmony_ci pr_cont("Early Drop"); 598c2ecf20Sopenharmony_ci else if (rev >= 0xA0) 608c2ecf20Sopenharmony_ci pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf); 618c2ecf20Sopenharmony_ci else 628c2ecf20Sopenharmony_ci pr_cont("Unknown"); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci pr_cont(" [%#010x]\n", asicid); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic unsigned int partnumber(unsigned int asicid) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return (asicid >> 8) & 0xffff; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * SOC MIDR ASICID ADDRESS ASICID VALUE 748c2ecf20Sopenharmony_ci * DB8500ed 0x410fc090 0x9001FFF4 0x00850001 758c2ecf20Sopenharmony_ci * DB8500v1 0x411fc091 0x9001FFF4 0x008500A0 768c2ecf20Sopenharmony_ci * DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1 778c2ecf20Sopenharmony_ci * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0 788c2ecf20Sopenharmony_ci * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2 798c2ecf20Sopenharmony_ci * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0 808c2ecf20Sopenharmony_ci * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void __init ux500_setup_id(void) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci unsigned int cpuid = read_cpuid_id(); 868c2ecf20Sopenharmony_ci unsigned int asicid = 0; 878c2ecf20Sopenharmony_ci phys_addr_t addr = 0; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci switch (cpuid) { 908c2ecf20Sopenharmony_ci case 0x410fc090: /* DB8500ed */ 918c2ecf20Sopenharmony_ci case 0x411fc091: /* DB8500v1 */ 928c2ecf20Sopenharmony_ci addr = 0x9001FFF4; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */ 968c2ecf20Sopenharmony_ci asicid = ux500_read_asicid(0x9001DBF4); 978c2ecf20Sopenharmony_ci if (partnumber(asicid) == 0x8500 || 988c2ecf20Sopenharmony_ci partnumber(asicid) == 0x8520) 998c2ecf20Sopenharmony_ci /* DB8500v2 */ 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* DB5500v1 */ 1038c2ecf20Sopenharmony_ci addr = 0x9001FFF4; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci case 0x413fc090: /* DB9540 */ 1078c2ecf20Sopenharmony_ci addr = 0xFFFFDBF4; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (addr) 1128c2ecf20Sopenharmony_ci asicid = ux500_read_asicid(addr); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!asicid) { 1158c2ecf20Sopenharmony_ci pr_err("Unable to identify SoC\n"); 1168c2ecf20Sopenharmony_ci BUG(); 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci dbx500_id.process = asicid >> 24; 1208c2ecf20Sopenharmony_ci dbx500_id.partnumber = partnumber(asicid); 1218c2ecf20Sopenharmony_ci dbx500_id.revision = asicid & 0xff; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ux500_print_soc_info(asicid); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic const char * __init ux500_get_machine(void) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic const char * __init ux500_get_family(void) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "ux500"); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const char * __init ux500_get_revision(void) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci unsigned int rev = dbx500_id.revision; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (rev == 0x01) 1418c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%s", "ED"); 1428c2ecf20Sopenharmony_ci else if (rev >= 0xA0) 1438c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%d.%d", 1448c2ecf20Sopenharmony_ci (rev >> 4) - 0xA + 1, rev & 0xf); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%s", "Unknown"); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic ssize_t 1508c2ecf20Sopenharmony_ciprocess_show(struct device *dev, struct device_attribute *attr, char *buf) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci if (dbx500_id.process == 0x00) 1538c2ecf20Sopenharmony_ci return sprintf(buf, "Standard\n"); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return sprintf(buf, "%02xnm\n", dbx500_id.process); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(process); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct attribute *ux500_soc_attrs[] = { 1618c2ecf20Sopenharmony_ci &dev_attr_process.attr, 1628c2ecf20Sopenharmony_ci NULL 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ux500_soc); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const char *db8500_read_soc_id(struct device_node *backupram) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci void __iomem *base; 1708c2ecf20Sopenharmony_ci const char *retstr; 1718c2ecf20Sopenharmony_ci u32 uid[5]; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci base = of_iomap(backupram, 0); 1748c2ecf20Sopenharmony_ci if (!base) 1758c2ecf20Sopenharmony_ci return NULL; 1768c2ecf20Sopenharmony_ci memcpy_fromio(uid, base + 0x1fc0, sizeof(uid)); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Throw these device-specific numbers into the entropy pool */ 1798c2ecf20Sopenharmony_ci add_device_randomness(uid, sizeof(uid)); 1808c2ecf20Sopenharmony_ci retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", 1818c2ecf20Sopenharmony_ci uid[0], uid[1], uid[2], uid[3], uid[4]); 1828c2ecf20Sopenharmony_ci iounmap(base); 1838c2ecf20Sopenharmony_ci return retstr; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr, 1878c2ecf20Sopenharmony_ci struct device_node *backupram) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci soc_dev_attr->soc_id = db8500_read_soc_id(backupram); 1908c2ecf20Sopenharmony_ci soc_dev_attr->machine = ux500_get_machine(); 1918c2ecf20Sopenharmony_ci soc_dev_attr->family = ux500_get_family(); 1928c2ecf20Sopenharmony_ci soc_dev_attr->revision = ux500_get_revision(); 1938c2ecf20Sopenharmony_ci soc_dev_attr->custom_attr_group = ux500_soc_groups[0]; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int __init ux500_soc_device_init(void) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct soc_device *soc_dev; 1998c2ecf20Sopenharmony_ci struct soc_device_attribute *soc_dev_attr; 2008c2ecf20Sopenharmony_ci struct device_node *backupram; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci backupram = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram"); 2038c2ecf20Sopenharmony_ci if (!backupram) 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ux500_setup_id(); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); 2098c2ecf20Sopenharmony_ci if (!soc_dev_attr) { 2108c2ecf20Sopenharmony_ci of_node_put(backupram); 2118c2ecf20Sopenharmony_ci return -ENOMEM; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci soc_info_populate(soc_dev_attr, backupram); 2158c2ecf20Sopenharmony_ci of_node_put(backupram); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci soc_dev = soc_device_register(soc_dev_attr); 2188c2ecf20Sopenharmony_ci if (IS_ERR(soc_dev)) { 2198c2ecf20Sopenharmony_ci kfree(soc_dev_attr); 2208c2ecf20Sopenharmony_ci return PTR_ERR(soc_dev); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_cisubsys_initcall(ux500_soc_device_init); 226