1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Freescale QorIQ Platforms GUTS Driver 4 * 5 * Copyright (C) 2016 Freescale Semiconductor, Inc. 6 */ 7 8 #include <linux/io.h> 9 #include <linux/slab.h> 10 #include <linux/module.h> 11 #include <linux/of_fdt.h> 12 #include <linux/sys_soc.h> 13 #include <linux/of_address.h> 14 #include <linux/platform_device.h> 15 #include <linux/fsl/guts.h> 16 17 struct fsl_soc_die_attr { 18 char *die; 19 u32 svr; 20 u32 mask; 21 }; 22 23 struct fsl_soc_data { 24 const char *sfp_compat; 25 u32 uid_offset; 26 }; 27 28 /* SoC die attribute definition for QorIQ platform */ 29 static const struct fsl_soc_die_attr fsl_soc_die[] = { 30 /* 31 * Power Architecture-based SoCs T Series 32 */ 33 34 /* Die: T4240, SoC: T4240/T4160/T4080 */ 35 { .die = "T4240", 36 .svr = 0x82400000, 37 .mask = 0xfff00000, 38 }, 39 /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */ 40 { .die = "T1040", 41 .svr = 0x85200000, 42 .mask = 0xfff00000, 43 }, 44 /* Die: T2080, SoC: T2080/T2081 */ 45 { .die = "T2080", 46 .svr = 0x85300000, 47 .mask = 0xfff00000, 48 }, 49 /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */ 50 { .die = "T1024", 51 .svr = 0x85400000, 52 .mask = 0xfff00000, 53 }, 54 55 /* 56 * ARM-based SoCs LS Series 57 */ 58 59 /* Die: LS1043A, SoC: LS1043A/LS1023A */ 60 { .die = "LS1043A", 61 .svr = 0x87920000, 62 .mask = 0xffff0000, 63 }, 64 /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */ 65 { .die = "LS2080A", 66 .svr = 0x87010000, 67 .mask = 0xff3f0000, 68 }, 69 /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */ 70 { .die = "LS1088A", 71 .svr = 0x87030000, 72 .mask = 0xff3f0000, 73 }, 74 /* Die: LS1012A, SoC: LS1012A */ 75 { .die = "LS1012A", 76 .svr = 0x87040000, 77 .mask = 0xffff0000, 78 }, 79 /* Die: LS1046A, SoC: LS1046A/LS1026A */ 80 { .die = "LS1046A", 81 .svr = 0x87070000, 82 .mask = 0xffff0000, 83 }, 84 /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */ 85 { .die = "LS2088A", 86 .svr = 0x87090000, 87 .mask = 0xff3f0000, 88 }, 89 /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */ 90 { .die = "LS1021A", 91 .svr = 0x87000000, 92 .mask = 0xfff70000, 93 }, 94 /* Die: LX2160A, SoC: LX2160A/LX2120A/LX2080A */ 95 { .die = "LX2160A", 96 .svr = 0x87360000, 97 .mask = 0xff3f0000, 98 }, 99 /* Die: LS1028A, SoC: LS1028A */ 100 { .die = "LS1028A", 101 .svr = 0x870b0000, 102 .mask = 0xff3f0000, 103 }, 104 { }, 105 }; 106 107 static const struct fsl_soc_die_attr *fsl_soc_die_match( 108 u32 svr, const struct fsl_soc_die_attr *matches) 109 { 110 while (matches->svr) { 111 if (matches->svr == (svr & matches->mask)) 112 return matches; 113 matches++; 114 } 115 return NULL; 116 } 117 118 static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) 119 { 120 struct device_node *np; 121 void __iomem *sfp_base; 122 u64 uid; 123 124 np = of_find_compatible_node(NULL, NULL, compat); 125 if (!np) 126 return 0; 127 128 sfp_base = of_iomap(np, 0); 129 if (!sfp_base) { 130 of_node_put(np); 131 return 0; 132 } 133 134 uid = ioread32(sfp_base + offset); 135 uid <<= 32; 136 uid |= ioread32(sfp_base + offset + 4); 137 138 iounmap(sfp_base); 139 of_node_put(np); 140 141 return uid; 142 } 143 144 static const struct fsl_soc_data ls1028a_data = { 145 .sfp_compat = "fsl,ls1028a-sfp", 146 .uid_offset = 0x21c, 147 }; 148 149 /* 150 * Table for matching compatible strings, for device tree 151 * guts node, for Freescale QorIQ SOCs. 152 */ 153 static const struct of_device_id fsl_guts_of_match[] = { 154 { .compatible = "fsl,qoriq-device-config-1.0", }, 155 { .compatible = "fsl,qoriq-device-config-2.0", }, 156 { .compatible = "fsl,p1010-guts", }, 157 { .compatible = "fsl,p1020-guts", }, 158 { .compatible = "fsl,p1021-guts", }, 159 { .compatible = "fsl,p1022-guts", }, 160 { .compatible = "fsl,p1023-guts", }, 161 { .compatible = "fsl,p2020-guts", }, 162 { .compatible = "fsl,bsc9131-guts", }, 163 { .compatible = "fsl,bsc9132-guts", }, 164 { .compatible = "fsl,mpc8536-guts", }, 165 { .compatible = "fsl,mpc8544-guts", }, 166 { .compatible = "fsl,mpc8548-guts", }, 167 { .compatible = "fsl,mpc8568-guts", }, 168 { .compatible = "fsl,mpc8569-guts", }, 169 { .compatible = "fsl,mpc8572-guts", }, 170 { .compatible = "fsl,ls1021a-dcfg", }, 171 { .compatible = "fsl,ls1043a-dcfg", }, 172 { .compatible = "fsl,ls2080a-dcfg", }, 173 { .compatible = "fsl,ls1088a-dcfg", }, 174 { .compatible = "fsl,ls1012a-dcfg", }, 175 { .compatible = "fsl,ls1046a-dcfg", }, 176 { .compatible = "fsl,lx2160a-dcfg", }, 177 { .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data}, 178 {} 179 }; 180 181 static int __init fsl_guts_init(void) 182 { 183 struct soc_device_attribute *soc_dev_attr; 184 static struct soc_device *soc_dev; 185 const struct fsl_soc_die_attr *soc_die; 186 const struct fsl_soc_data *soc_data; 187 const struct of_device_id *match; 188 struct ccsr_guts __iomem *regs; 189 const char *machine = NULL; 190 struct device_node *np; 191 bool little_endian; 192 u64 soc_uid = 0; 193 u32 svr; 194 int ret; 195 196 np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match); 197 if (!np) 198 return 0; 199 soc_data = match->data; 200 201 regs = of_iomap(np, 0); 202 if (!regs) { 203 of_node_put(np); 204 return -ENOMEM; 205 } 206 207 little_endian = of_property_read_bool(np, "little-endian"); 208 if (little_endian) 209 svr = ioread32(®s->svr); 210 else 211 svr = ioread32be(®s->svr); 212 iounmap(regs); 213 of_node_put(np); 214 215 /* Register soc device */ 216 soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); 217 if (!soc_dev_attr) 218 return -ENOMEM; 219 220 if (of_property_read_string(of_root, "model", &machine)) 221 of_property_read_string_index(of_root, "compatible", 0, &machine); 222 if (machine) { 223 soc_dev_attr->machine = kstrdup(machine, GFP_KERNEL); 224 if (!soc_dev_attr->machine) 225 goto err_nomem; 226 } 227 228 soc_die = fsl_soc_die_match(svr, fsl_soc_die); 229 if (soc_die) { 230 soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ %s", 231 soc_die->die); 232 } else { 233 soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ"); 234 } 235 if (!soc_dev_attr->family) 236 goto err_nomem; 237 238 soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", svr); 239 if (!soc_dev_attr->soc_id) 240 goto err_nomem; 241 242 soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d", 243 (svr >> 4) & 0xf, svr & 0xf); 244 if (!soc_dev_attr->revision) 245 goto err_nomem; 246 247 if (soc_data) 248 soc_uid = fsl_guts_get_soc_uid(soc_data->sfp_compat, 249 soc_data->uid_offset); 250 if (soc_uid) 251 soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", 252 soc_uid); 253 254 soc_dev = soc_device_register(soc_dev_attr); 255 if (IS_ERR(soc_dev)) { 256 ret = PTR_ERR(soc_dev); 257 goto err; 258 } 259 260 pr_info("Machine: %s\n", soc_dev_attr->machine); 261 pr_info("SoC family: %s\n", soc_dev_attr->family); 262 pr_info("SoC ID: %s, Revision: %s\n", 263 soc_dev_attr->soc_id, soc_dev_attr->revision); 264 265 return 0; 266 267 err_nomem: 268 ret = -ENOMEM; 269 err: 270 kfree(soc_dev_attr->machine); 271 kfree(soc_dev_attr->family); 272 kfree(soc_dev_attr->soc_id); 273 kfree(soc_dev_attr->revision); 274 kfree(soc_dev_attr->serial_number); 275 kfree(soc_dev_attr); 276 277 return ret; 278 } 279 core_initcall(fsl_guts_init); 280