xref: /linux/drivers/soc/qcom/smem_dramc.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4  */
5 
6 #include <linux/debugfs.h>
7 #include <linux/io.h>
8 #include <linux/module.h>
9 #include <linux/of_device.h>
10 #include <linux/of.h>
11 #include <linux/platform_device.h>
12 #include <linux/soc/qcom/smem.h>
13 #include <linux/units.h>
14 
15 #include "smem.h"
16 
17 #define SMEM_DDR_INFO_ID		603
18 
19 #define MAX_DDR_FREQ_NUM_V3		13
20 #define MAX_DDR_FREQ_NUM_V5		14
21 
22 #define MAX_CHAN_NUM			8
23 #define MAX_RANK_NUM			2
24 
25 #define DDR_HBB_MIN			13
26 #define DDR_HBB_MAX			19
27 
28 #define MAX_SHUB_ENTRIES		8
29 
30 static struct smem_dram *__dram;
31 
32 enum ddr_info_version {
33 	INFO_UNKNOWN,
34 	INFO_V3,
35 	INFO_V3_WITH_14_FREQS,
36 	INFO_V4,
37 	INFO_V5,
38 	INFO_V5_WITH_6_REGIONS,
39 	INFO_V6, /* INFO_V6 seems to only have shipped with 6 DDR regions, unlike V7 */
40 	INFO_V7,
41 	INFO_V7_WITH_6_REGIONS,
42 };
43 
44 struct smem_dram {
45 	unsigned long frequencies[MAX_DDR_FREQ_NUM_V5];
46 	u32 num_frequencies;
47 	u8 hbb;
48 };
49 
50 enum ddr_type {
51 	DDR_TYPE_NODDR = 0,
52 	DDR_TYPE_LPDDR1 = 1,
53 	DDR_TYPE_LPDDR2 = 2,
54 	DDR_TYPE_PCDDR2 = 3,
55 	DDR_TYPE_PCDDR3 = 4,
56 	DDR_TYPE_LPDDR3 = 5,
57 	DDR_TYPE_LPDDR4 = 6,
58 	DDR_TYPE_LPDDR4X = 7,
59 	DDR_TYPE_LPDDR5 = 8,
60 	DDR_TYPE_LPDDR5X = 9,
61 };
62 
63 /* The data structures below are NOT __packed on purpose! */
64 
65 /* Structs used across multiple versions */
66 struct ddr_part_details {
67 	__le16 revision_id1;
68 	__le16 revision_id2;
69 	__le16 width;
70 	__le16 density;
71 };
72 
73 struct ddr_freq_table {
74 	__le32 freq_khz;
75 	u8 enabled;
76 };
77 
78 /* V3 */
79 struct ddr_freq_plan_v3 {
80 	struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V3];
81 	u8 num_ddr_freqs;
82 	phys_addr_t clk_period_address;
83 };
84 
85 struct ddr_details_v3 {
86 	u8 manufacturer_id;
87 	u8 device_type;
88 	struct ddr_part_details ddr_params[MAX_CHAN_NUM];
89 	struct ddr_freq_plan_v3 ddr_freq_tbl;
90 	u8 num_channels;
91 };
92 
93 /* Some V3 structs have an additional frequency level */
94 struct ddr_freq_plan_v3_14freqs {
95 	struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V3 + 1];
96 	u8 num_ddr_freqs;
97 	phys_addr_t clk_period_address;
98 };
99 
100 struct ddr_details_v3_14freqs {
101 	u8 manufacturer_id;
102 	u8 device_type;
103 	struct ddr_part_details ddr_params[MAX_CHAN_NUM];
104 	struct ddr_freq_plan_v3_14freqs ddr_freq_tbl;
105 	u8 num_channels;
106 };
107 
108 /* V4 */
109 struct ddr_details_v4 {
110 	u8 manufacturer_id;
111 	u8 device_type;
112 	struct ddr_part_details ddr_params[MAX_CHAN_NUM];
113 	struct ddr_freq_plan_v3 ddr_freq_tbl;
114 	u8 num_channels;
115 	u8 num_ranks[MAX_CHAN_NUM];
116 	u8 highest_bank_addr_bit[MAX_CHAN_NUM][MAX_RANK_NUM];
117 };
118 
119 /* V5 */
120 struct shub_freq_table {
121 	u8 enable;
122 	__le32 freq_khz;
123 };
124 
125 struct shub_freq_plan_entry {
126 	u8 num_shub_freqs;
127 	struct shub_freq_table shub_freq[MAX_SHUB_ENTRIES];
128 };
129 
130 struct ddr_xbl2quantum_smem_data {
131 	phys_addr_t ssr_cookie_addr;
132 	__le32 reserved[10];
133 };
134 
135 struct ddr_freq_plan_v5 {
136 	struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V5];
137 	u8 num_ddr_freqs;
138 	phys_addr_t clk_period_address;
139 	__le32 max_nom_ddr_freq;
140 };
141 
142 struct ddr_region_v5 {
143 	__le64 start_address;
144 	__le64 size;
145 	__le64 mem_controller_address;
146 	__le32 granule_size; /* MiB */
147 	u8  ddr_rank;
148 #define DDR_RANK_0	BIT(0)
149 #define DDR_RANK_1	BIT(1)
150 	u8  segments_start_index;
151 	__le64 segments_start_offset;
152 };
153 
154 struct ddr_regions_v5 {
155 	__le32 ddr_region_num; /* We expect this to always be 4 or 6 */
156 	__le64 ddr_rank0_size;
157 	__le64 ddr_rank1_size;
158 	__le64 ddr_cs0_start_addr;
159 	__le64 ddr_cs1_start_addr;
160 	__le32 highest_bank_addr_bit;
161 	struct ddr_region_v5 ddr_region[] __counted_by_le(ddr_region_num);
162 };
163 
164 struct ddr_details_v5 {
165 	u8 manufacturer_id;
166 	u8 device_type;
167 	struct ddr_part_details ddr_params[MAX_CHAN_NUM];
168 	struct ddr_freq_plan_v5 ddr_freq_tbl;
169 	u8 num_channels;
170 	u8 _padding;
171 	struct ddr_regions_v5 ddr_regions;
172 };
173 
174 /* V6 */
175 struct ddr_misc_info_v6 {
176 	__le32 dsf_version;
177 	__le32 reserved[10];
178 };
179 
180 /* V7 */
181 struct ddr_details_v7 {
182 	u8 manufacturer_id;
183 	u8 device_type;
184 	struct ddr_part_details ddr_params[MAX_CHAN_NUM];
185 	struct ddr_freq_plan_v5 ddr_freq_tbl;
186 	u8 num_channels;
187 	u8 sct_config;
188 	struct ddr_regions_v5 ddr_regions;
189 };
190 
191 /**
192  * qcom_smem_dram_get_hbb(): Get the Highest bank address bit
193  *
194  * Context: Check qcom_smem_is_available() before calling this function.
195  * Because __dram * is initialized by smem_dram_parse(), which is in turn
196  * called from * qcom_smem_probe(), __dram will only be NULL if the data
197  * couldn't have been found/interpreted correctly.
198  *
199  * Return: highest bank bit on success, -ENODATA on failure.
200  */
201 int qcom_smem_dram_get_hbb(void)
202 {
203 	if (!__dram || !__dram->hbb)
204 		return -ENODATA;
205 
206 	if (__dram->hbb < DDR_HBB_MIN || __dram->hbb > DDR_HBB_MAX)
207 		return -ENODATA;
208 
209 	return __dram->hbb;
210 }
211 EXPORT_SYMBOL_GPL(qcom_smem_dram_get_hbb);
212 
213 static void smem_dram_parse_v3_data(struct smem_dram *dram, void *data)
214 {
215 	struct ddr_details_v3 *details = data;
216 
217 	for (int i = 0; i < MAX_DDR_FREQ_NUM_V3; i++) {
218 		struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
219 
220 		if (freq_entry->freq_khz && freq_entry->enabled) {
221 			u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
222 			dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
223 		}
224 	}
225 }
226 
227 static void smem_dram_parse_v3_14freqs_data(struct smem_dram *dram, void *data)
228 {
229 	struct ddr_details_v3_14freqs *details = data;
230 
231 	for (int i = 0; i < MAX_DDR_FREQ_NUM_V3 + 1; i++) {
232 		struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
233 
234 		if (freq_entry->freq_khz && freq_entry->enabled)
235 			dram->frequencies[dram->num_frequencies++] = 1000 * freq_entry->freq_khz;
236 	}
237 }
238 
239 static void smem_dram_parse_v4_data(struct smem_dram *dram, void *data)
240 {
241 	struct ddr_details_v4 *details = data;
242 
243 	/* Rank 0 channel 0 entry holds the correct value */
244 	dram->hbb = details->highest_bank_addr_bit[0][0];
245 
246 	for (int i = 0; i < MAX_DDR_FREQ_NUM_V3; i++) {
247 		struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
248 
249 		if (freq_entry->freq_khz && freq_entry->enabled) {
250 			u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
251 			dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
252 		}
253 	}
254 }
255 
256 static void smem_dram_parse_v5_data(struct smem_dram *dram, void *data)
257 {
258 	struct ddr_details_v5 *details = data;
259 	struct ddr_regions_v5 *region = &details->ddr_regions;
260 
261 	dram->hbb = le32_to_cpu(region[0].highest_bank_addr_bit);
262 
263 	for (int i = 0; i < MAX_DDR_FREQ_NUM_V5; i++) {
264 		struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
265 
266 		if (freq_entry->freq_khz && freq_entry->enabled) {
267 			u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
268 			dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
269 		}
270 	}
271 }
272 
273 static void smem_dram_parse_v7_data(struct smem_dram *dram, void *data)
274 {
275 	struct ddr_details_v7 *details = data;
276 	struct ddr_regions_v5 *region = &details->ddr_regions;
277 
278 	dram->hbb = le32_to_cpu(region[0].highest_bank_addr_bit);
279 
280 	for (int i = 0; i < MAX_DDR_FREQ_NUM_V5; i++) {
281 		struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
282 
283 		if (freq_entry->freq_khz && freq_entry->enabled) {
284 			u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
285 			dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
286 		}
287 	}
288 }
289 
290 /* The structure contains no version field, so we have to perform some guesswork.. */
291 static int smem_dram_infer_struct_version(size_t size)
292 {
293 	/* Some early versions provided less bytes of less useful data */
294 	if (size < sizeof(struct ddr_details_v3))
295 		return -EINVAL;
296 
297 	if (size == sizeof(struct ddr_details_v3))
298 		return INFO_V3;
299 
300 	if (size == sizeof(struct ddr_details_v3_14freqs))
301 		return INFO_V3_WITH_14_FREQS;
302 
303 	if (size == sizeof(struct ddr_details_v4))
304 		return INFO_V4;
305 
306 	if (size == sizeof(struct ddr_details_v5) +
307 		    4 * sizeof(struct ddr_region_v5))
308 		return INFO_V5;
309 
310 	if (size == sizeof(struct ddr_details_v5) +
311 		    4 * sizeof(struct ddr_region_v5) +
312 		    sizeof(struct ddr_xbl2quantum_smem_data) +
313 		    sizeof(struct shub_freq_plan_entry))
314 		return INFO_V5;
315 
316 	if (size == sizeof(struct ddr_details_v5) +
317 		    6 * sizeof(struct ddr_region_v5))
318 		return INFO_V5_WITH_6_REGIONS;
319 
320 	if (size == sizeof(struct ddr_details_v5) +
321 		    6 * sizeof(struct ddr_region_v5) +
322 		    sizeof(struct ddr_xbl2quantum_smem_data) +
323 		    sizeof(struct shub_freq_plan_entry))
324 		return INFO_V5_WITH_6_REGIONS;
325 
326 	if (size == sizeof(struct ddr_details_v5) +
327 		    6 * sizeof(struct ddr_region_v5) +
328 		    sizeof(struct ddr_misc_info_v6) +
329 		    sizeof(struct shub_freq_plan_entry))
330 		return INFO_V6;
331 
332 	if (size == sizeof(struct ddr_details_v7) +
333 		    4 * sizeof(struct ddr_region_v5) +
334 		    sizeof(struct ddr_misc_info_v6) +
335 		    sizeof(struct shub_freq_plan_entry))
336 		return INFO_V7;
337 
338 	if (size == sizeof(struct ddr_details_v7) +
339 		    6 * sizeof(struct ddr_region_v5) +
340 		    sizeof(struct ddr_misc_info_v6) +
341 		    sizeof(struct shub_freq_plan_entry))
342 		return INFO_V7_WITH_6_REGIONS;
343 
344 	return INFO_UNKNOWN;
345 }
346 
347 static int smem_dram_frequencies_show(struct seq_file *s, void *unused)
348 {
349 	struct smem_dram *dram = s->private;
350 
351 	for (int i = 0; i < dram->num_frequencies; i++)
352 		seq_printf(s, "%lu\n", dram->frequencies[i]);
353 
354 	return 0;
355 }
356 DEFINE_SHOW_ATTRIBUTE(smem_dram_frequencies);
357 
358 static int smem_hbb_show(struct seq_file *s, void *unused)
359 {
360 	struct smem_dram *dram = s->private;
361 
362 	if (!dram->hbb)
363 		return -EINVAL;
364 
365 	seq_printf(s, "%d\n", dram->hbb);
366 
367 	return 0;
368 }
369 DEFINE_SHOW_ATTRIBUTE(smem_hbb);
370 
371 struct dentry *smem_dram_parse(struct qcom_smem *smem, struct device *dev)
372 {
373 	struct dentry *debugfs_dir;
374 	enum ddr_info_version ver;
375 	struct smem_dram *dram;
376 	size_t actual_size;
377 	void *data;
378 
379 	/* No need to check qcom_smem_is_available(), this func is called by the SMEM driver */
380 	data = __qcom_smem_get(smem, QCOM_SMEM_HOST_ANY, SMEM_DDR_INFO_ID, &actual_size);
381 	if (IS_ERR_OR_NULL(data))
382 		return ERR_PTR(-ENODATA);
383 
384 	ver = smem_dram_infer_struct_version(actual_size);
385 	if (ver < 0) {
386 		/* Some SoCs don't provide data that's useful for us */
387 		return ERR_PTR(-ENODATA);
388 	} else if (ver == INFO_UNKNOWN) {
389 		/* In other cases, we may not have added support for a newer struct revision */
390 		dev_err(dev, "Found an unknown type of DRAM info struct (size = %zu)\n",
391 			actual_size);
392 		return ERR_PTR(-EINVAL);
393 	}
394 
395 	dram = devm_kzalloc(dev, sizeof(*dram), GFP_KERNEL);
396 	if (!dram)
397 		return ERR_PTR(-ENOMEM);
398 
399 	switch (ver) {
400 	case INFO_V3:
401 		smem_dram_parse_v3_data(dram, data);
402 		break;
403 	case INFO_V3_WITH_14_FREQS:
404 		smem_dram_parse_v3_14freqs_data(dram, data);
405 		break;
406 	case INFO_V4:
407 		smem_dram_parse_v4_data(dram, data);
408 		break;
409 	case INFO_V5:
410 	case INFO_V5_WITH_6_REGIONS:
411 	case INFO_V6:
412 		smem_dram_parse_v5_data(dram, data);
413 		break;
414 	case INFO_V7:
415 	case INFO_V7_WITH_6_REGIONS:
416 		smem_dram_parse_v7_data(dram, data);
417 		break;
418 	default:
419 		return ERR_PTR(-EINVAL);
420 	}
421 
422 	debugfs_dir = debugfs_create_dir("qcom_smem", NULL);
423 	debugfs_create_file("dram_frequencies", 0444, debugfs_dir, dram,
424 			    &smem_dram_frequencies_fops);
425 	debugfs_create_file("hbb", 0444, debugfs_dir, dram, &smem_hbb_fops);
426 
427 	__dram = dram;
428 
429 	return debugfs_dir;
430 }
431