1 /* 2 * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <linux/io.h> 8 #include <linux/of.h> 9 #include <linux/of_address.h> 10 #include <linux/of_platform.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.h> 13 #include <linux/sys_soc.h> 14 #include <linux/bitfield.h> 15 #include <linux/regmap.h> 16 #include <linux/mfd/syscon.h> 17 18 #define MESON_SOCINFO_MAJOR_VER_MESON6 0x16 19 #define MESON_SOCINFO_MAJOR_VER_MESON8 0x19 20 #define MESON_SOCINFO_MAJOR_VER_MESON8B 0x1b 21 22 #define MESON_MX_ASSIST_HW_REV 0x14c 23 24 #define MESON_MX_ANALOG_TOP_METAL_REVISION 0x0 25 26 #define MESON_MX_BOOTROM_MISC_VER 0x4 27 28 static const char *meson_mx_socinfo_revision(unsigned int major_ver, 29 unsigned int misc_ver, 30 unsigned int metal_rev) 31 { 32 unsigned int minor_ver; 33 34 switch (major_ver) { 35 case MESON_SOCINFO_MAJOR_VER_MESON6: 36 minor_ver = 0xa; 37 break; 38 39 case MESON_SOCINFO_MAJOR_VER_MESON8: 40 if (metal_rev == 0x11111112) 41 major_ver = 0x1d; 42 43 if (metal_rev == 0x11111111 || metal_rev == 0x11111112) 44 minor_ver = 0xa; 45 else if (metal_rev == 0x11111113) 46 minor_ver = 0xb; 47 else if (metal_rev == 0x11111133) 48 minor_ver = 0xc; 49 else 50 minor_ver = 0xd; 51 52 break; 53 54 case MESON_SOCINFO_MAJOR_VER_MESON8B: 55 if (metal_rev == 0x11111111) 56 minor_ver = 0xa; 57 else 58 minor_ver = 0xb; 59 60 break; 61 62 default: 63 minor_ver = 0x0; 64 break; 65 } 66 67 return kasprintf(GFP_KERNEL, "Rev%X (%x - 0:%X)", minor_ver, major_ver, 68 misc_ver); 69 } 70 71 static const char *meson_mx_socinfo_soc_id(unsigned int major_ver, 72 unsigned int metal_rev) 73 { 74 const char *soc_id; 75 76 switch (major_ver) { 77 case MESON_SOCINFO_MAJOR_VER_MESON6: 78 soc_id = "Meson6 (AML8726-MX)"; 79 break; 80 81 case MESON_SOCINFO_MAJOR_VER_MESON8: 82 if (metal_rev == 0x11111112) 83 soc_id = "Meson8m2 (S812)"; 84 else 85 soc_id = "Meson8 (S802)"; 86 87 break; 88 89 case MESON_SOCINFO_MAJOR_VER_MESON8B: 90 soc_id = "Meson8b (S805)"; 91 break; 92 93 default: 94 soc_id = "Unknown"; 95 break; 96 } 97 98 return kstrdup_const(soc_id, GFP_KERNEL); 99 } 100 101 static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = { 102 { .compatible = "amlogic,meson8-analog-top", }, 103 { .compatible = "amlogic,meson8b-analog-top", }, 104 { /* sentinel */ } 105 }; 106 107 static int __init meson_mx_socinfo_init(void) 108 { 109 struct soc_device_attribute *soc_dev_attr; 110 struct soc_device *soc_dev; 111 struct device_node *np; 112 struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap; 113 unsigned int major_ver, misc_ver, metal_rev = 0; 114 int ret; 115 116 assist_regmap = 117 syscon_regmap_lookup_by_compatible("amlogic,meson-mx-assist"); 118 if (IS_ERR(assist_regmap)) 119 return PTR_ERR(assist_regmap); 120 121 bootrom_regmap = 122 syscon_regmap_lookup_by_compatible("amlogic,meson-mx-bootrom"); 123 if (IS_ERR(bootrom_regmap)) 124 return PTR_ERR(bootrom_regmap); 125 126 np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids); 127 if (np) { 128 analog_top_regmap = syscon_node_to_regmap(np); 129 if (IS_ERR(analog_top_regmap)) 130 return PTR_ERR(analog_top_regmap); 131 132 ret = regmap_read(analog_top_regmap, 133 MESON_MX_ANALOG_TOP_METAL_REVISION, 134 &metal_rev); 135 if (ret) 136 return ret; 137 } 138 139 ret = regmap_read(assist_regmap, MESON_MX_ASSIST_HW_REV, &major_ver); 140 if (ret < 0) 141 return ret; 142 143 ret = regmap_read(bootrom_regmap, MESON_MX_BOOTROM_MISC_VER, 144 &misc_ver); 145 if (ret < 0) 146 return ret; 147 148 soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); 149 if (!soc_dev_attr) 150 return -ENODEV; 151 152 soc_dev_attr->family = "Amlogic Meson"; 153 154 np = of_find_node_by_path("/"); 155 of_property_read_string(np, "model", &soc_dev_attr->machine); 156 of_node_put(np); 157 158 soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver, 159 metal_rev); 160 soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev); 161 162 soc_dev = soc_device_register(soc_dev_attr); 163 if (IS_ERR(soc_dev)) { 164 kfree_const(soc_dev_attr->revision); 165 kfree_const(soc_dev_attr->soc_id); 166 kfree(soc_dev_attr); 167 return PTR_ERR(soc_dev); 168 } 169 170 dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n", 171 soc_dev_attr->soc_id, soc_dev_attr->revision); 172 173 return 0; 174 } 175 device_initcall(meson_mx_socinfo_init); 176