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