Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

memory: Extend of_memory with LPDDR3 support

Add AC timings information needed to support LPDDR3 and memory
controllers along with helpers to obtain it. These will be necessary
for upcoming Exynos5422 Dynamic Memory Controller driver.

Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>

authored by

Lukasz Luba and committed by
Krzysztof Kozlowski
976897dd 54ecb8f7

+228
+61
drivers/memory/jedec_ddr.h
··· 29 29 #define DDR_TYPE_LPDDR2_S4 3 30 30 #define DDR_TYPE_LPDDR2_S2 4 31 31 #define DDR_TYPE_LPDDR2_NVM 5 32 + #define DDR_TYPE_LPDDR3 6 32 33 33 34 /* DDR IO width */ 34 35 #define DDR_IO_WIDTH_4 1 ··· 169 168 extern const struct lpddr2_timings 170 169 lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; 171 170 extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; 171 + 172 + /* 173 + * Structure for timings for LPDDR3 based on LPDDR2 plus additional fields. 174 + * All parameters are in pico seconds(ps) excluding max_freq, min_freq which 175 + * are in Hz. 176 + */ 177 + struct lpddr3_timings { 178 + u32 max_freq; 179 + u32 min_freq; 180 + u32 tRFC; 181 + u32 tRRD; 182 + u32 tRPab; 183 + u32 tRPpb; 184 + u32 tRCD; 185 + u32 tRC; 186 + u32 tRAS; 187 + u32 tWTR; 188 + u32 tWR; 189 + u32 tRTP; 190 + u32 tW2W_C2C; 191 + u32 tR2R_C2C; 192 + u32 tWL; 193 + u32 tDQSCK; 194 + u32 tRL; 195 + u32 tFAW; 196 + u32 tXSR; 197 + u32 tXP; 198 + u32 tCKE; 199 + u32 tCKESR; 200 + u32 tMRD; 201 + }; 202 + 203 + /* 204 + * Min value for some parameters in terms of number of tCK cycles(nCK) 205 + * Please set to zero parameters that are not valid for a given memory 206 + * type 207 + */ 208 + struct lpddr3_min_tck { 209 + u32 tRFC; 210 + u32 tRRD; 211 + u32 tRPab; 212 + u32 tRPpb; 213 + u32 tRCD; 214 + u32 tRC; 215 + u32 tRAS; 216 + u32 tWTR; 217 + u32 tWR; 218 + u32 tRTP; 219 + u32 tW2W_C2C; 220 + u32 tR2R_C2C; 221 + u32 tWL; 222 + u32 tDQSCK; 223 + u32 tRL; 224 + u32 tFAW; 225 + u32 tXSR; 226 + u32 tXP; 227 + u32 tCKE; 228 + u32 tCKESR; 229 + u32 tMRD; 230 + }; 172 231 173 232 #endif /* __JEDEC_DDR_H */
+149
drivers/memory/of_memory.c
··· 3 3 * OpenFirmware helpers for memory drivers 4 4 * 5 5 * Copyright (C) 2012 Texas Instruments, Inc. 6 + * Copyright (C) 2019 Samsung Electronics Co., Ltd. 6 7 */ 7 8 8 9 #include <linux/device.h> ··· 150 149 return lpddr2_jedec_timings; 151 150 } 152 151 EXPORT_SYMBOL(of_get_ddr_timings); 152 + 153 + /** 154 + * of_lpddr3_get_min_tck() - extract min timing values for lpddr3 155 + * @np: pointer to ddr device tree node 156 + * @device: device requesting for min timing values 157 + * 158 + * Populates the lpddr3_min_tck structure by extracting data 159 + * from device tree node. Returns a pointer to the populated 160 + * structure. If any error in populating the structure, returns NULL. 161 + */ 162 + const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, 163 + struct device *dev) 164 + { 165 + int ret = 0; 166 + struct lpddr3_min_tck *min; 167 + 168 + min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL); 169 + if (!min) 170 + goto default_min_tck; 171 + 172 + ret |= of_property_read_u32(np, "tRFC-min-tck", &min->tRFC); 173 + ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD); 174 + ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab); 175 + ret |= of_property_read_u32(np, "tRPpb-min-tck", &min->tRPpb); 176 + ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD); 177 + ret |= of_property_read_u32(np, "tRC-min-tck", &min->tRC); 178 + ret |= of_property_read_u32(np, "tRAS-min-tck", &min->tRAS); 179 + ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR); 180 + ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR); 181 + ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP); 182 + ret |= of_property_read_u32(np, "tW2W-C2C-min-tck", &min->tW2W_C2C); 183 + ret |= of_property_read_u32(np, "tR2R-C2C-min-tck", &min->tR2R_C2C); 184 + ret |= of_property_read_u32(np, "tWL-min-tck", &min->tWL); 185 + ret |= of_property_read_u32(np, "tDQSCK-min-tck", &min->tDQSCK); 186 + ret |= of_property_read_u32(np, "tRL-min-tck", &min->tRL); 187 + ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW); 188 + ret |= of_property_read_u32(np, "tXSR-min-tck", &min->tXSR); 189 + ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP); 190 + ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE); 191 + ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR); 192 + ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD); 193 + 194 + if (ret) { 195 + dev_warn(dev, "%s: errors while parsing min-tck values\n", 196 + __func__); 197 + devm_kfree(dev, min); 198 + goto default_min_tck; 199 + } 200 + 201 + return min; 202 + 203 + default_min_tck: 204 + dev_warn(dev, "%s: using default min-tck values\n", __func__); 205 + return NULL; 206 + } 207 + EXPORT_SYMBOL(of_lpddr3_get_min_tck); 208 + 209 + static int of_lpddr3_do_get_timings(struct device_node *np, 210 + struct lpddr3_timings *tim) 211 + { 212 + int ret; 213 + 214 + /* The 'reg' param required since DT has changed, used as 'max-freq' */ 215 + ret = of_property_read_u32(np, "reg", &tim->max_freq); 216 + ret |= of_property_read_u32(np, "min-freq", &tim->min_freq); 217 + ret |= of_property_read_u32(np, "tRFC", &tim->tRFC); 218 + ret |= of_property_read_u32(np, "tRRD", &tim->tRRD); 219 + ret |= of_property_read_u32(np, "tRPab", &tim->tRPab); 220 + ret |= of_property_read_u32(np, "tRPpb", &tim->tRPpb); 221 + ret |= of_property_read_u32(np, "tRCD", &tim->tRCD); 222 + ret |= of_property_read_u32(np, "tRC", &tim->tRC); 223 + ret |= of_property_read_u32(np, "tRAS", &tim->tRAS); 224 + ret |= of_property_read_u32(np, "tWTR", &tim->tWTR); 225 + ret |= of_property_read_u32(np, "tWR", &tim->tWR); 226 + ret |= of_property_read_u32(np, "tRTP", &tim->tRTP); 227 + ret |= of_property_read_u32(np, "tW2W-C2C", &tim->tW2W_C2C); 228 + ret |= of_property_read_u32(np, "tR2R-C2C", &tim->tR2R_C2C); 229 + ret |= of_property_read_u32(np, "tFAW", &tim->tFAW); 230 + ret |= of_property_read_u32(np, "tXSR", &tim->tXSR); 231 + ret |= of_property_read_u32(np, "tXP", &tim->tXP); 232 + ret |= of_property_read_u32(np, "tCKE", &tim->tCKE); 233 + ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR); 234 + ret |= of_property_read_u32(np, "tMRD", &tim->tMRD); 235 + 236 + return ret; 237 + } 238 + 239 + /** 240 + * of_lpddr3_get_ddr_timings() - extracts the lpddr3 timings and updates no of 241 + * frequencies available. 242 + * @np_ddr: Pointer to ddr device tree node 243 + * @dev: Device requesting for ddr timings 244 + * @device_type: Type of ddr 245 + * @nr_frequencies: No of frequencies available for ddr 246 + * (updated by this function) 247 + * 248 + * Populates lpddr3_timings structure by extracting data from device 249 + * tree node. Returns pointer to populated structure. If any error 250 + * while populating, returns NULL. 251 + */ 252 + const struct lpddr3_timings 253 + *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, struct device *dev, 254 + u32 device_type, u32 *nr_frequencies) 255 + { 256 + struct lpddr3_timings *timings = NULL; 257 + u32 arr_sz = 0, i = 0; 258 + struct device_node *np_tim; 259 + char *tim_compat = NULL; 260 + 261 + switch (device_type) { 262 + case DDR_TYPE_LPDDR3: 263 + tim_compat = "jedec,lpddr3-timings"; 264 + break; 265 + default: 266 + dev_warn(dev, "%s: un-supported memory type\n", __func__); 267 + } 268 + 269 + for_each_child_of_node(np_ddr, np_tim) 270 + if (of_device_is_compatible(np_tim, tim_compat)) 271 + arr_sz++; 272 + 273 + if (arr_sz) 274 + timings = devm_kcalloc(dev, arr_sz, sizeof(*timings), 275 + GFP_KERNEL); 276 + 277 + if (!timings) 278 + goto default_timings; 279 + 280 + for_each_child_of_node(np_ddr, np_tim) { 281 + if (of_device_is_compatible(np_tim, tim_compat)) { 282 + if (of_lpddr3_do_get_timings(np_tim, &timings[i])) { 283 + devm_kfree(dev, timings); 284 + goto default_timings; 285 + } 286 + i++; 287 + } 288 + } 289 + 290 + *nr_frequencies = arr_sz; 291 + 292 + return timings; 293 + 294 + default_timings: 295 + dev_warn(dev, "%s: failed to get timings\n", __func__); 296 + *nr_frequencies = 0; 297 + return NULL; 298 + } 299 + EXPORT_SYMBOL(of_lpddr3_get_ddr_timings);
+18
drivers/memory/of_memory.h
··· 14 14 extern const struct lpddr2_timings 15 15 *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, 16 16 u32 device_type, u32 *nr_frequencies); 17 + extern const struct lpddr3_min_tck 18 + *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev); 19 + extern const struct lpddr3_timings 20 + *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, 21 + struct device *dev, u32 device_type, u32 *nr_frequencies); 17 22 #else 18 23 static inline const struct lpddr2_min_tck 19 24 *of_get_min_tck(struct device_node *np, struct device *dev) ··· 29 24 static inline const struct lpddr2_timings 30 25 *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, 31 26 u32 device_type, u32 *nr_frequencies) 27 + { 28 + return NULL; 29 + } 30 + 31 + static inline const struct lpddr3_min_tck 32 + *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev) 33 + { 34 + return NULL; 35 + } 36 + 37 + static inline const struct lpddr3_timings 38 + *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, 39 + struct device *dev, u32 device_type, u32 *nr_frequencies) 32 40 { 33 41 return NULL; 34 42 }