1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Author: Yinbo Zhu <zhuyinbo@loongson.cn> 4 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited 5 */ 6 7 #include <linux/io.h> 8 #include <linux/slab.h> 9 #include <linux/module.h> 10 #include <linux/of_fdt.h> 11 #include <linux/sys_soc.h> 12 #include <linux/of_address.h> 13 #include <linux/platform_device.h> 14 15 static struct soc_device_attribute soc_dev_attr; 16 static struct soc_device *soc_dev; 17 18 /* 19 * Global Utility Registers. 20 * 21 * Not all registers defined in this structure are available on all chips, so 22 * you are expected to know whether a given register actually exists on your 23 * chip before you access it. 24 * 25 * Also, some registers are similar on different chips but have slightly 26 * different names. In these cases, one name is chosen to avoid extraneous 27 * #ifdefs. 28 */ 29 struct scfg_guts { 30 u32 svr; /* Version Register */ 31 u8 res0[4]; 32 u16 feature; /* Feature Register */ 33 u32 vendor; /* Vendor Register */ 34 u8 res1[6]; 35 u32 id; 36 u8 res2[0x3ff8 - 0x18]; 37 u32 chip; 38 }; 39 40 static struct guts { 41 struct scfg_guts __iomem *regs; 42 bool little_endian; 43 } *guts; 44 45 struct loongson2_soc_die_attr { 46 char *die; 47 u32 svr; 48 u32 mask; 49 }; 50 51 /* SoC die attribute definition for Loongson-2 platform */ 52 static const struct loongson2_soc_die_attr loongson2_soc_die[] = { 53 54 /* 55 * LoongArch-based SoCs Loongson-2 Series 56 */ 57 58 /* Die: 2k1000, SoC: 2k1000 */ 59 { .die = "2K1000", 60 .svr = 0x00000013, 61 .mask = 0x000000ff, 62 }, 63 { }, 64 }; 65 66 static const struct loongson2_soc_die_attr *loongson2_soc_die_match( 67 u32 svr, const struct loongson2_soc_die_attr *matches) 68 { 69 while (matches->svr) { 70 if (matches->svr == (svr & matches->mask)) 71 return matches; 72 matches++; 73 } 74 75 return NULL; 76 } 77 78 static u32 loongson2_guts_get_svr(void) 79 { 80 u32 svr = 0; 81 82 if (!guts || !guts->regs) 83 return svr; 84 85 if (guts->little_endian) 86 svr = ioread32(&guts->regs->svr); 87 else 88 svr = ioread32be(&guts->regs->svr); 89 90 return svr; 91 } 92 93 static int loongson2_guts_probe(struct platform_device *pdev) 94 { 95 struct device_node *root, *np = pdev->dev.of_node; 96 struct device *dev = &pdev->dev; 97 const struct loongson2_soc_die_attr *soc_die; 98 const char *machine; 99 u32 svr; 100 101 /* Initialize guts */ 102 guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL); 103 if (!guts) 104 return -ENOMEM; 105 106 guts->little_endian = of_property_read_bool(np, "little-endian"); 107 108 guts->regs = devm_platform_ioremap_resource(pdev, 0); 109 if (IS_ERR(guts->regs)) 110 return PTR_ERR(guts->regs); 111 112 /* Register soc device */ 113 root = of_find_node_by_path("/"); 114 if (of_property_read_string(root, "model", &machine)) 115 of_property_read_string_index(root, "compatible", 0, &machine); 116 of_node_put(root); 117 if (machine) { 118 soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL); 119 if (!soc_dev_attr.machine) 120 return -ENOMEM; 121 } 122 123 svr = loongson2_guts_get_svr(); 124 soc_die = loongson2_soc_die_match(svr, loongson2_soc_die); 125 if (soc_die) { 126 soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, 127 "Loongson %s", soc_die->die); 128 } else { 129 soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "Loongson"); 130 } 131 if (!soc_dev_attr.family) 132 return -ENOMEM; 133 soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL, 134 "svr:0x%08x", svr); 135 if (!soc_dev_attr.soc_id) 136 return -ENOMEM; 137 soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", 138 (svr >> 4) & 0xf, svr & 0xf); 139 if (!soc_dev_attr.revision) 140 return -ENOMEM; 141 142 soc_dev = soc_device_register(&soc_dev_attr); 143 if (IS_ERR(soc_dev)) 144 return PTR_ERR(soc_dev); 145 146 pr_info("Machine: %s\n", soc_dev_attr.machine); 147 pr_info("SoC family: %s\n", soc_dev_attr.family); 148 pr_info("SoC ID: %s, Revision: %s\n", 149 soc_dev_attr.soc_id, soc_dev_attr.revision); 150 151 return 0; 152 } 153 154 static void loongson2_guts_remove(struct platform_device *dev) 155 { 156 soc_device_unregister(soc_dev); 157 } 158 159 /* 160 * Table for matching compatible strings, for device tree 161 * guts node, for Loongson-2 SoCs. 162 */ 163 static const struct of_device_id loongson2_guts_of_match[] = { 164 { .compatible = "loongson,ls2k-chipid", }, 165 {} 166 }; 167 MODULE_DEVICE_TABLE(of, loongson2_guts_of_match); 168 169 static struct platform_driver loongson2_guts_driver = { 170 .driver = { 171 .name = "loongson2-guts", 172 .of_match_table = loongson2_guts_of_match, 173 }, 174 .probe = loongson2_guts_probe, 175 .remove = loongson2_guts_remove, 176 }; 177 178 static int __init loongson2_guts_init(void) 179 { 180 return platform_driver_register(&loongson2_guts_driver); 181 } 182 core_initcall(loongson2_guts_init); 183 184 static void __exit loongson2_guts_exit(void) 185 { 186 platform_driver_unregister(&loongson2_guts_driver); 187 } 188 module_exit(loongson2_guts_exit); 189 190 MODULE_DESCRIPTION("Loongson2 GUTS driver"); 191 MODULE_LICENSE("GPL"); 192