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 struct resource *res; 98 const struct loongson2_soc_die_attr *soc_die; 99 const char *machine; 100 u32 svr; 101 102 /* Initialize guts */ 103 guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL); 104 if (!guts) 105 return -ENOMEM; 106 107 guts->little_endian = of_property_read_bool(np, "little-endian"); 108 109 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 110 guts->regs = ioremap(res->start, res->end - res->start + 1); 111 if (IS_ERR(guts->regs)) 112 return PTR_ERR(guts->regs); 113 114 /* Register soc device */ 115 root = of_find_node_by_path("/"); 116 if (of_property_read_string(root, "model", &machine)) 117 of_property_read_string_index(root, "compatible", 0, &machine); 118 of_node_put(root); 119 if (machine) 120 soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL); 121 122 svr = loongson2_guts_get_svr(); 123 soc_die = loongson2_soc_die_match(svr, loongson2_soc_die); 124 if (soc_die) { 125 soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, 126 "Loongson %s", soc_die->die); 127 } else { 128 soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "Loongson"); 129 } 130 if (!soc_dev_attr.family) 131 return -ENOMEM; 132 soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL, 133 "svr:0x%08x", svr); 134 if (!soc_dev_attr.soc_id) 135 return -ENOMEM; 136 soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", 137 (svr >> 4) & 0xf, svr & 0xf); 138 if (!soc_dev_attr.revision) 139 return -ENOMEM; 140 141 soc_dev = soc_device_register(&soc_dev_attr); 142 if (IS_ERR(soc_dev)) 143 return PTR_ERR(soc_dev); 144 145 pr_info("Machine: %s\n", soc_dev_attr.machine); 146 pr_info("SoC family: %s\n", soc_dev_attr.family); 147 pr_info("SoC ID: %s, Revision: %s\n", 148 soc_dev_attr.soc_id, soc_dev_attr.revision); 149 150 return 0; 151 } 152 153 static int loongson2_guts_remove(struct platform_device *dev) 154 { 155 soc_device_unregister(soc_dev); 156 157 return 0; 158 } 159 160 /* 161 * Table for matching compatible strings, for device tree 162 * guts node, for Loongson-2 SoCs. 163 */ 164 static const struct of_device_id loongson2_guts_of_match[] = { 165 { .compatible = "loongson,ls2k-chipid", }, 166 {} 167 }; 168 MODULE_DEVICE_TABLE(of, loongson2_guts_of_match); 169 170 static struct platform_driver loongson2_guts_driver = { 171 .driver = { 172 .name = "loongson2-guts", 173 .of_match_table = loongson2_guts_of_match, 174 }, 175 .probe = loongson2_guts_probe, 176 .remove = loongson2_guts_remove, 177 }; 178 179 static int __init loongson2_guts_init(void) 180 { 181 return platform_driver_register(&loongson2_guts_driver); 182 } 183 core_initcall(loongson2_guts_init); 184 185 static void __exit loongson2_guts_exit(void) 186 { 187 platform_driver_unregister(&loongson2_guts_driver); 188 } 189 module_exit(loongson2_guts_exit); 190 191 MODULE_DESCRIPTION("Loongson2 GUTS driver"); 192 MODULE_LICENSE("GPL"); 193