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 120 svr = loongson2_guts_get_svr(); 121 soc_die = loongson2_soc_die_match(svr, loongson2_soc_die); 122 if (soc_die) { 123 soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, 124 "Loongson %s", soc_die->die); 125 } else { 126 soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "Loongson"); 127 } 128 if (!soc_dev_attr.family) 129 return -ENOMEM; 130 soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL, 131 "svr:0x%08x", svr); 132 if (!soc_dev_attr.soc_id) 133 return -ENOMEM; 134 soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", 135 (svr >> 4) & 0xf, svr & 0xf); 136 if (!soc_dev_attr.revision) 137 return -ENOMEM; 138 139 soc_dev = soc_device_register(&soc_dev_attr); 140 if (IS_ERR(soc_dev)) 141 return PTR_ERR(soc_dev); 142 143 pr_info("Machine: %s\n", soc_dev_attr.machine); 144 pr_info("SoC family: %s\n", soc_dev_attr.family); 145 pr_info("SoC ID: %s, Revision: %s\n", 146 soc_dev_attr.soc_id, soc_dev_attr.revision); 147 148 return 0; 149 } 150 151 static void loongson2_guts_remove(struct platform_device *dev) 152 { 153 soc_device_unregister(soc_dev); 154 } 155 156 /* 157 * Table for matching compatible strings, for device tree 158 * guts node, for Loongson-2 SoCs. 159 */ 160 static const struct of_device_id loongson2_guts_of_match[] = { 161 { .compatible = "loongson,ls2k-chipid", }, 162 {} 163 }; 164 MODULE_DEVICE_TABLE(of, loongson2_guts_of_match); 165 166 static struct platform_driver loongson2_guts_driver = { 167 .driver = { 168 .name = "loongson2-guts", 169 .of_match_table = loongson2_guts_of_match, 170 }, 171 .probe = loongson2_guts_probe, 172 .remove = loongson2_guts_remove, 173 }; 174 175 static int __init loongson2_guts_init(void) 176 { 177 return platform_driver_register(&loongson2_guts_driver); 178 } 179 core_initcall(loongson2_guts_init); 180 181 static void __exit loongson2_guts_exit(void) 182 { 183 platform_driver_unregister(&loongson2_guts_driver); 184 } 185 module_exit(loongson2_guts_exit); 186 187 MODULE_DESCRIPTION("Loongson2 GUTS driver"); 188 MODULE_LICENSE("GPL"); 189