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