summaryrefslogtreecommitdiffstats
path: root/drivers/soc/amlogic/meson-mx-socinfo.c
blob: 78f0f1aeca578a1a7716b4e79c41b2b36c9015d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
 *
 * SPDX-License-Identifier: GPL-2.0+
 */

#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>

#define MESON_SOCINFO_MAJOR_VER_MESON6		0x16
#define MESON_SOCINFO_MAJOR_VER_MESON8		0x19
#define MESON_SOCINFO_MAJOR_VER_MESON8B		0x1b

#define MESON_MX_ASSIST_HW_REV			0x14c

#define MESON_MX_ANALOG_TOP_METAL_REVISION	0x0

#define MESON_MX_BOOTROM_MISC_VER		0x4

static const char *meson_mx_socinfo_revision(unsigned int major_ver,
					     unsigned int misc_ver,
					     unsigned int metal_rev)
{
	unsigned int minor_ver;

	switch (major_ver) {
	case MESON_SOCINFO_MAJOR_VER_MESON6:
		minor_ver = 0xa;
		break;

	case MESON_SOCINFO_MAJOR_VER_MESON8:
		if (metal_rev == 0x11111112)
			major_ver = 0x1d;

		if (metal_rev == 0x11111111 || metal_rev == 0x11111112)
			minor_ver = 0xa;
		else if (metal_rev == 0x11111113)
			minor_ver = 0xb;
		else if (metal_rev == 0x11111133)
			minor_ver = 0xc;
		else
			minor_ver = 0xd;

		break;

	case MESON_SOCINFO_MAJOR_VER_MESON8B:
		if (metal_rev == 0x11111111)
			minor_ver = 0xa;
		else
			minor_ver = 0xb;

		break;

	default:
		minor_ver = 0x0;
		break;
	}

	return kasprintf(GFP_KERNEL, "Rev%X (%x - 0:%X)", minor_ver, major_ver,
			 misc_ver);
}

static const char *meson_mx_socinfo_soc_id(unsigned int major_ver,
					   unsigned int metal_rev)
{
	const char *soc_id;

	switch (major_ver) {
	case MESON_SOCINFO_MAJOR_VER_MESON6:
		soc_id = "Meson6 (AML8726-MX)";
		break;

	case MESON_SOCINFO_MAJOR_VER_MESON8:
		if (metal_rev == 0x11111112)
			soc_id = "Meson8m2 (S812)";
		else
			soc_id = "Meson8 (S802)";

		break;

	case MESON_SOCINFO_MAJOR_VER_MESON8B:
		soc_id = "Meson8b (S805)";
		break;

	default:
		soc_id = "Unknown";
		break;
	}

	return kstrdup_const(soc_id, GFP_KERNEL);
}

static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = {
	{ .compatible = "amlogic,meson8-analog-top", },
	{ .compatible = "amlogic,meson8b-analog-top", },
	{ /* sentinel */ }
};

static int __init meson_mx_socinfo_init(void)
{
	struct soc_device_attribute *soc_dev_attr;
	struct soc_device *soc_dev;
	struct device_node *np;
	struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap;
	unsigned int major_ver, misc_ver, metal_rev = 0;
	int ret;

	assist_regmap =
		syscon_regmap_lookup_by_compatible("amlogic,meson-mx-assist");
	if (IS_ERR(assist_regmap))
		return PTR_ERR(assist_regmap);

	bootrom_regmap =
		syscon_regmap_lookup_by_compatible("amlogic,meson-mx-bootrom");
	if (IS_ERR(bootrom_regmap))
		return PTR_ERR(bootrom_regmap);

	np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids);
	if (np) {
		analog_top_regmap = syscon_node_to_regmap(np);
		if (IS_ERR(analog_top_regmap))
			return PTR_ERR(analog_top_regmap);

		ret = regmap_read(analog_top_regmap,
				  MESON_MX_ANALOG_TOP_METAL_REVISION,
				  &metal_rev);
		if (ret)
			return ret;
	}

	ret = regmap_read(assist_regmap, MESON_MX_ASSIST_HW_REV, &major_ver);
	if (ret < 0)
		return ret;

	ret = regmap_read(bootrom_regmap, MESON_MX_BOOTROM_MISC_VER,
			  &misc_ver);
	if (ret < 0)
		return ret;

	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
	if (!soc_dev_attr)
		return -ENODEV;

	soc_dev_attr->family = "Amlogic Meson";

	np = of_find_node_by_path("/");
	of_property_read_string(np, "model", &soc_dev_attr->machine);
	of_node_put(np);

	soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver,
							   metal_rev);
	soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev);

	soc_dev = soc_device_register(soc_dev_attr);
	if (IS_ERR(soc_dev)) {
		kfree_const(soc_dev_attr->revision);
		kfree_const(soc_dev_attr->soc_id);
		kfree(soc_dev_attr);
		return PTR_ERR(soc_dev);
	}

	dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n",
		 soc_dev_attr->soc_id, soc_dev_attr->revision);

	return 0;
}
device_initcall(meson_mx_socinfo_init);