xref: /linux-6.15/drivers/memory/of_memory.c (revision 48af14fb)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e6b42eb6SAneesh V /*
3e6b42eb6SAneesh V  * OpenFirmware helpers for memory drivers
4e6b42eb6SAneesh V  *
5e6b42eb6SAneesh V  * Copyright (C) 2012 Texas Instruments, Inc.
6976897ddSLukasz Luba  * Copyright (C) 2019 Samsung Electronics Co., Ltd.
7efc46463SKrzysztof Kozlowski  * Copyright (C) 2020 Krzysztof Kozlowski <[email protected]>
8e6b42eb6SAneesh V  */
9e6b42eb6SAneesh V 
10e6b42eb6SAneesh V #include <linux/device.h>
11e6b42eb6SAneesh V #include <linux/of.h>
12e6b42eb6SAneesh V #include <linux/gfp.h>
13e6b42eb6SAneesh V #include <linux/export.h>
145ec47cdaSMasahiro Yamada 
155ec47cdaSMasahiro Yamada #include "jedec_ddr.h"
16aeb83d70SBaoyou Xie #include "of_memory.h"
17e6b42eb6SAneesh V 
18e6b42eb6SAneesh V /**
19e6b42eb6SAneesh V  * of_get_min_tck() - extract min timing values for ddr
20e6b42eb6SAneesh V  * @np: pointer to ddr device tree node
2146c71118SKrzysztof Kozlowski  * @dev: device requesting for min timing values
22e6b42eb6SAneesh V  *
23e6b42eb6SAneesh V  * Populates the lpddr2_min_tck structure by extracting data
24e6b42eb6SAneesh V  * from device tree node. Returns a pointer to the populated
25e6b42eb6SAneesh V  * structure. If any error in populating the structure, returns
26e6b42eb6SAneesh V  * default min timings provided by JEDEC.
27e6b42eb6SAneesh V  */
of_get_min_tck(struct device_node * np,struct device * dev)28e6b42eb6SAneesh V const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
29e6b42eb6SAneesh V 					    struct device *dev)
30e6b42eb6SAneesh V {
31e6b42eb6SAneesh V 	int			ret = 0;
32e6b42eb6SAneesh V 	struct lpddr2_min_tck	*min;
33e6b42eb6SAneesh V 
34e6b42eb6SAneesh V 	min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
35e6b42eb6SAneesh V 	if (!min)
36e6b42eb6SAneesh V 		goto default_min_tck;
37e6b42eb6SAneesh V 
38e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
39e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
40e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
41e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
42e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
43e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
44e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
45e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
46e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
47e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
48e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
49e6b42eb6SAneesh V 
50e6b42eb6SAneesh V 	if (ret) {
51e6b42eb6SAneesh V 		devm_kfree(dev, min);
52e6b42eb6SAneesh V 		goto default_min_tck;
53e6b42eb6SAneesh V 	}
54e6b42eb6SAneesh V 
55e6b42eb6SAneesh V 	return min;
56e6b42eb6SAneesh V 
57e6b42eb6SAneesh V default_min_tck:
58efc46463SKrzysztof Kozlowski 	dev_warn(dev, "Using default min-tck values\n");
59e6b42eb6SAneesh V 	return &lpddr2_jedec_min_tck;
60e6b42eb6SAneesh V }
61e6b42eb6SAneesh V EXPORT_SYMBOL(of_get_min_tck);
62e6b42eb6SAneesh V 
of_do_get_timings(struct device_node * np,struct lpddr2_timings * tim)63e6b42eb6SAneesh V static int of_do_get_timings(struct device_node *np,
64e6b42eb6SAneesh V 			     struct lpddr2_timings *tim)
65e6b42eb6SAneesh V {
66e6b42eb6SAneesh V 	int ret;
67e6b42eb6SAneesh V 
68e6b42eb6SAneesh V 	ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
69e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
70e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
71e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
72e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tWR", &tim->tWR);
73e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
74e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
75e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
76e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tXP", &tim->tXP);
77e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
78e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
79e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
80e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
81e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
82e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
83e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
84e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
85e6b42eb6SAneesh V 	ret |= of_property_read_u32(np, "tDQSCK-max-derated",
86e6b42eb6SAneesh V 				    &tim->tDQSCK_max_derated);
87e6b42eb6SAneesh V 
88e6b42eb6SAneesh V 	return ret;
89e6b42eb6SAneesh V }
90e6b42eb6SAneesh V 
91e6b42eb6SAneesh V /**
92e6b42eb6SAneesh V  * of_get_ddr_timings() - extracts the ddr timings and updates no of
93e6b42eb6SAneesh V  * frequencies available.
94e6b42eb6SAneesh V  * @np_ddr: Pointer to ddr device tree node
95e6b42eb6SAneesh V  * @dev: Device requesting for ddr timings
96e6b42eb6SAneesh V  * @device_type: Type of ddr(LPDDR2 S2/S4)
97e6b42eb6SAneesh V  * @nr_frequencies: No of frequencies available for ddr
98e6b42eb6SAneesh V  * (updated by this function)
99e6b42eb6SAneesh V  *
100e6b42eb6SAneesh V  * Populates lpddr2_timings structure by extracting data from device
101e6b42eb6SAneesh V  * tree node. Returns pointer to populated structure. If any error
102e6b42eb6SAneesh V  * while populating, returns default timings provided by JEDEC.
103e6b42eb6SAneesh V  */
of_get_ddr_timings(struct device_node * np_ddr,struct device * dev,u32 device_type,u32 * nr_frequencies)104e6b42eb6SAneesh V const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
1059825095aSKrzysztof Kozlowski 						struct device *dev,
1069825095aSKrzysztof Kozlowski 						u32 device_type,
1079825095aSKrzysztof Kozlowski 						u32 *nr_frequencies)
108e6b42eb6SAneesh V {
109e6b42eb6SAneesh V 	struct lpddr2_timings	*timings = NULL;
110e6b42eb6SAneesh V 	u32			arr_sz = 0, i = 0;
111e6b42eb6SAneesh V 	struct device_node	*np_tim;
112ae53e374SDan Carpenter 	char			*tim_compat = NULL;
113e6b42eb6SAneesh V 
114e6b42eb6SAneesh V 	switch (device_type) {
115e6b42eb6SAneesh V 	case DDR_TYPE_LPDDR2_S2:
116e6b42eb6SAneesh V 	case DDR_TYPE_LPDDR2_S4:
117e6b42eb6SAneesh V 		tim_compat = "jedec,lpddr2-timings";
118e6b42eb6SAneesh V 		break;
119e6b42eb6SAneesh V 	default:
120efc46463SKrzysztof Kozlowski 		dev_warn(dev, "Unsupported memory type\n");
121e6b42eb6SAneesh V 	}
122e6b42eb6SAneesh V 
123e6b42eb6SAneesh V 	for_each_child_of_node(np_ddr, np_tim)
124e6b42eb6SAneesh V 		if (of_device_is_compatible(np_tim, tim_compat))
125e6b42eb6SAneesh V 			arr_sz++;
126e6b42eb6SAneesh V 
127e6b42eb6SAneesh V 	if (arr_sz)
128a86854d0SKees Cook 		timings = devm_kcalloc(dev, arr_sz, sizeof(*timings),
129e6b42eb6SAneesh V 				       GFP_KERNEL);
130e6b42eb6SAneesh V 
131e6b42eb6SAneesh V 	if (!timings)
132e6b42eb6SAneesh V 		goto default_timings;
133e6b42eb6SAneesh V 
134e6b42eb6SAneesh V 	for_each_child_of_node(np_ddr, np_tim) {
135e6b42eb6SAneesh V 		if (of_device_is_compatible(np_tim, tim_compat)) {
136e6b42eb6SAneesh V 			if (of_do_get_timings(np_tim, &timings[i])) {
13705215fb3SLiang He 				of_node_put(np_tim);
138e6b42eb6SAneesh V 				devm_kfree(dev, timings);
139e6b42eb6SAneesh V 				goto default_timings;
140e6b42eb6SAneesh V 			}
141e6b42eb6SAneesh V 			i++;
142e6b42eb6SAneesh V 		}
143e6b42eb6SAneesh V 	}
144e6b42eb6SAneesh V 
145e6b42eb6SAneesh V 	*nr_frequencies = arr_sz;
146e6b42eb6SAneesh V 
147e6b42eb6SAneesh V 	return timings;
148e6b42eb6SAneesh V 
149e6b42eb6SAneesh V default_timings:
150efc46463SKrzysztof Kozlowski 	dev_warn(dev, "Using default memory timings\n");
151e6b42eb6SAneesh V 	*nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
152e6b42eb6SAneesh V 	return lpddr2_jedec_timings;
153e6b42eb6SAneesh V }
154e6b42eb6SAneesh V EXPORT_SYMBOL(of_get_ddr_timings);
155976897ddSLukasz Luba 
156976897ddSLukasz Luba /**
157976897ddSLukasz Luba  * of_lpddr3_get_min_tck() - extract min timing values for lpddr3
158976897ddSLukasz Luba  * @np: pointer to ddr device tree node
15946c71118SKrzysztof Kozlowski  * @dev: device requesting for min timing values
160976897ddSLukasz Luba  *
161976897ddSLukasz Luba  * Populates the lpddr3_min_tck structure by extracting data
162976897ddSLukasz Luba  * from device tree node. Returns a pointer to the populated
163976897ddSLukasz Luba  * structure. If any error in populating the structure, returns NULL.
164976897ddSLukasz Luba  */
of_lpddr3_get_min_tck(struct device_node * np,struct device * dev)165976897ddSLukasz Luba const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
166976897ddSLukasz Luba 						   struct device *dev)
167976897ddSLukasz Luba {
168976897ddSLukasz Luba 	int			ret = 0;
169976897ddSLukasz Luba 	struct lpddr3_min_tck	*min;
170976897ddSLukasz Luba 
171976897ddSLukasz Luba 	min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
172976897ddSLukasz Luba 	if (!min)
173976897ddSLukasz Luba 		goto default_min_tck;
174976897ddSLukasz Luba 
175976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRFC-min-tck", &min->tRFC);
176976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
177976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
178976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRPpb-min-tck", &min->tRPpb);
179976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
180976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRC-min-tck", &min->tRC);
181976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRAS-min-tck", &min->tRAS);
182976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
183976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
184976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
185976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tW2W-C2C-min-tck", &min->tW2W_C2C);
186976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tR2R-C2C-min-tck", &min->tR2R_C2C);
187976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tWL-min-tck", &min->tWL);
188976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tDQSCK-min-tck", &min->tDQSCK);
189976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRL-min-tck", &min->tRL);
190976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
191976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tXSR-min-tck", &min->tXSR);
192976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
193976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
194976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
195976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD);
196976897ddSLukasz Luba 
197976897ddSLukasz Luba 	if (ret) {
198efc46463SKrzysztof Kozlowski 		dev_warn(dev, "Errors while parsing min-tck values\n");
199976897ddSLukasz Luba 		devm_kfree(dev, min);
200976897ddSLukasz Luba 		goto default_min_tck;
201976897ddSLukasz Luba 	}
202976897ddSLukasz Luba 
203976897ddSLukasz Luba 	return min;
204976897ddSLukasz Luba 
205976897ddSLukasz Luba default_min_tck:
206efc46463SKrzysztof Kozlowski 	dev_warn(dev, "Using default min-tck values\n");
207976897ddSLukasz Luba 	return NULL;
208976897ddSLukasz Luba }
209976897ddSLukasz Luba EXPORT_SYMBOL(of_lpddr3_get_min_tck);
210976897ddSLukasz Luba 
of_lpddr3_do_get_timings(struct device_node * np,struct lpddr3_timings * tim)211976897ddSLukasz Luba static int of_lpddr3_do_get_timings(struct device_node *np,
212976897ddSLukasz Luba 				    struct lpddr3_timings *tim)
213976897ddSLukasz Luba {
214976897ddSLukasz Luba 	int ret;
215976897ddSLukasz Luba 
2164e890b22SKrzysztof Kozlowski 	ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
2174e890b22SKrzysztof Kozlowski 	if (ret)
2184e890b22SKrzysztof Kozlowski 		/* Deprecated way of passing max-freq as 'reg' */
219976897ddSLukasz Luba 		ret = of_property_read_u32(np, "reg", &tim->max_freq);
220976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
221976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRFC", &tim->tRFC);
222976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
223976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
224976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRPpb", &tim->tRPpb);
225976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
226976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRC", &tim->tRC);
227976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRAS", &tim->tRAS);
228976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
229976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tWR", &tim->tWR);
230976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
231976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tW2W-C2C", &tim->tW2W_C2C);
232976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tR2R-C2C", &tim->tR2R_C2C);
233976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
234976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tXSR", &tim->tXSR);
235976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tXP", &tim->tXP);
236976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tCKE", &tim->tCKE);
237976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
238976897ddSLukasz Luba 	ret |= of_property_read_u32(np, "tMRD", &tim->tMRD);
239976897ddSLukasz Luba 
240976897ddSLukasz Luba 	return ret;
241976897ddSLukasz Luba }
242976897ddSLukasz Luba 
243976897ddSLukasz Luba /**
244976897ddSLukasz Luba  * of_lpddr3_get_ddr_timings() - extracts the lpddr3 timings and updates no of
245976897ddSLukasz Luba  * frequencies available.
246976897ddSLukasz Luba  * @np_ddr: Pointer to ddr device tree node
247976897ddSLukasz Luba  * @dev: Device requesting for ddr timings
248976897ddSLukasz Luba  * @device_type: Type of ddr
249976897ddSLukasz Luba  * @nr_frequencies: No of frequencies available for ddr
250976897ddSLukasz Luba  * (updated by this function)
251976897ddSLukasz Luba  *
252976897ddSLukasz Luba  * Populates lpddr3_timings structure by extracting data from device
253976897ddSLukasz Luba  * tree node. Returns pointer to populated structure. If any error
254976897ddSLukasz Luba  * while populating, returns NULL.
255976897ddSLukasz Luba  */
256976897ddSLukasz Luba const struct lpddr3_timings
of_lpddr3_get_ddr_timings(struct device_node * np_ddr,struct device * dev,u32 device_type,u32 * nr_frequencies)257976897ddSLukasz Luba *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
258976897ddSLukasz Luba 			   u32 device_type, u32 *nr_frequencies)
259976897ddSLukasz Luba {
260976897ddSLukasz Luba 	struct lpddr3_timings	*timings = NULL;
261976897ddSLukasz Luba 	u32			arr_sz = 0, i = 0;
262976897ddSLukasz Luba 	struct device_node	*np_tim;
263976897ddSLukasz Luba 	char			*tim_compat = NULL;
264976897ddSLukasz Luba 
265976897ddSLukasz Luba 	switch (device_type) {
266976897ddSLukasz Luba 	case DDR_TYPE_LPDDR3:
267976897ddSLukasz Luba 		tim_compat = "jedec,lpddr3-timings";
268976897ddSLukasz Luba 		break;
269976897ddSLukasz Luba 	default:
270efc46463SKrzysztof Kozlowski 		dev_warn(dev, "Unsupported memory type\n");
271976897ddSLukasz Luba 	}
272976897ddSLukasz Luba 
273976897ddSLukasz Luba 	for_each_child_of_node(np_ddr, np_tim)
274976897ddSLukasz Luba 		if (of_device_is_compatible(np_tim, tim_compat))
275976897ddSLukasz Luba 			arr_sz++;
276976897ddSLukasz Luba 
277976897ddSLukasz Luba 	if (arr_sz)
278976897ddSLukasz Luba 		timings = devm_kcalloc(dev, arr_sz, sizeof(*timings),
279976897ddSLukasz Luba 				       GFP_KERNEL);
280976897ddSLukasz Luba 
281976897ddSLukasz Luba 	if (!timings)
282976897ddSLukasz Luba 		goto default_timings;
283976897ddSLukasz Luba 
284976897ddSLukasz Luba 	for_each_child_of_node(np_ddr, np_tim) {
285976897ddSLukasz Luba 		if (of_device_is_compatible(np_tim, tim_compat)) {
286976897ddSLukasz Luba 			if (of_lpddr3_do_get_timings(np_tim, &timings[i])) {
287976897ddSLukasz Luba 				devm_kfree(dev, timings);
288*48af14fbSLiang He 				of_node_put(np_tim);
289976897ddSLukasz Luba 				goto default_timings;
290976897ddSLukasz Luba 			}
291976897ddSLukasz Luba 			i++;
292976897ddSLukasz Luba 		}
293976897ddSLukasz Luba 	}
294976897ddSLukasz Luba 
295976897ddSLukasz Luba 	*nr_frequencies = arr_sz;
296976897ddSLukasz Luba 
297976897ddSLukasz Luba 	return timings;
298976897ddSLukasz Luba 
299976897ddSLukasz Luba default_timings:
300efc46463SKrzysztof Kozlowski 	dev_warn(dev, "Failed to get timings\n");
301976897ddSLukasz Luba 	*nr_frequencies = 0;
302976897ddSLukasz Luba 	return NULL;
303976897ddSLukasz Luba }
304976897ddSLukasz Luba EXPORT_SYMBOL(of_lpddr3_get_ddr_timings);
30538322cf4SDmitry Osipenko 
30638322cf4SDmitry Osipenko /**
30738322cf4SDmitry Osipenko  * of_lpddr2_get_info() - extracts information about the lpddr2 chip.
30838322cf4SDmitry Osipenko  * @np: Pointer to device tree node containing lpddr2 info
30938322cf4SDmitry Osipenko  * @dev: Device requesting info
31038322cf4SDmitry Osipenko  *
31138322cf4SDmitry Osipenko  * Populates lpddr2_info structure by extracting data from device
31238322cf4SDmitry Osipenko  * tree node. Returns pointer to populated structure. If error
31338322cf4SDmitry Osipenko  * happened while populating, returns NULL. If property is missing
31438322cf4SDmitry Osipenko  * in a device-tree, then the corresponding value is set to -ENOENT.
31538322cf4SDmitry Osipenko  */
31638322cf4SDmitry Osipenko const struct lpddr2_info
of_lpddr2_get_info(struct device_node * np,struct device * dev)31738322cf4SDmitry Osipenko *of_lpddr2_get_info(struct device_node *np, struct device *dev)
31838322cf4SDmitry Osipenko {
31938322cf4SDmitry Osipenko 	struct lpddr2_info *ret_info, info = {};
32038322cf4SDmitry Osipenko 	struct property *prop;
32138322cf4SDmitry Osipenko 	const char *cp;
32238322cf4SDmitry Osipenko 	int err;
323a06bf59dSJulius Werner 	u32 revision_id[2];
32438322cf4SDmitry Osipenko 
325a06bf59dSJulius Werner 	err = of_property_read_u32_array(np, "revision-id", revision_id, 2);
326a06bf59dSJulius Werner 	if (!err) {
327a06bf59dSJulius Werner 		info.revision_id1 = revision_id[0];
328a06bf59dSJulius Werner 		info.revision_id2 = revision_id[1];
329a06bf59dSJulius Werner 	} else {
33038322cf4SDmitry Osipenko 		err = of_property_read_u32(np, "revision-id1", &info.revision_id1);
33138322cf4SDmitry Osipenko 		if (err)
33238322cf4SDmitry Osipenko 			info.revision_id1 = -ENOENT;
33338322cf4SDmitry Osipenko 
33438322cf4SDmitry Osipenko 		err = of_property_read_u32(np, "revision-id2", &info.revision_id2);
33538322cf4SDmitry Osipenko 		if (err)
33638322cf4SDmitry Osipenko 			info.revision_id2 = -ENOENT;
337a06bf59dSJulius Werner 	}
33838322cf4SDmitry Osipenko 
33938322cf4SDmitry Osipenko 	err = of_property_read_u32(np, "io-width", &info.io_width);
34038322cf4SDmitry Osipenko 	if (err)
34138322cf4SDmitry Osipenko 		return NULL;
34238322cf4SDmitry Osipenko 
34338322cf4SDmitry Osipenko 	info.io_width = 32 / info.io_width - 1;
34438322cf4SDmitry Osipenko 
34538322cf4SDmitry Osipenko 	err = of_property_read_u32(np, "density", &info.density);
34638322cf4SDmitry Osipenko 	if (err)
34738322cf4SDmitry Osipenko 		return NULL;
34838322cf4SDmitry Osipenko 
34938322cf4SDmitry Osipenko 	info.density = ffs(info.density) - 7;
35038322cf4SDmitry Osipenko 
35138322cf4SDmitry Osipenko 	if (of_device_is_compatible(np, "jedec,lpddr2-s4"))
35238322cf4SDmitry Osipenko 		info.arch_type = LPDDR2_TYPE_S4;
35338322cf4SDmitry Osipenko 	else if (of_device_is_compatible(np, "jedec,lpddr2-s2"))
35438322cf4SDmitry Osipenko 		info.arch_type = LPDDR2_TYPE_S2;
35538322cf4SDmitry Osipenko 	else if (of_device_is_compatible(np, "jedec,lpddr2-nvm"))
35638322cf4SDmitry Osipenko 		info.arch_type = LPDDR2_TYPE_NVM;
35738322cf4SDmitry Osipenko 	else
35838322cf4SDmitry Osipenko 		return NULL;
35938322cf4SDmitry Osipenko 
36038322cf4SDmitry Osipenko 	prop = of_find_property(np, "compatible", NULL);
36138322cf4SDmitry Osipenko 	for (cp = of_prop_next_string(prop, NULL); cp;
36238322cf4SDmitry Osipenko 	     cp = of_prop_next_string(prop, cp)) {
36338322cf4SDmitry Osipenko 
36438322cf4SDmitry Osipenko #define OF_LPDDR2_VENDOR_CMP(compat, ID) \
36538322cf4SDmitry Osipenko 		if (!of_compat_cmp(cp, compat ",", strlen(compat ","))) { \
36638322cf4SDmitry Osipenko 			info.manufacturer_id = LPDDR2_MANID_##ID; \
36738322cf4SDmitry Osipenko 			break; \
36838322cf4SDmitry Osipenko 		}
36938322cf4SDmitry Osipenko 
37038322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("samsung", SAMSUNG)
37138322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("qimonda", QIMONDA)
37238322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("elpida", ELPIDA)
37338322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("etron", ETRON)
37438322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("nanya", NANYA)
37538322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("hynix", HYNIX)
37638322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("mosel", MOSEL)
37738322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("winbond", WINBOND)
37838322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("esmt", ESMT)
37938322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("spansion", SPANSION)
38038322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("sst", SST)
38138322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("zmos", ZMOS)
38238322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("intel", INTEL)
38338322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("numonyx", NUMONYX)
38438322cf4SDmitry Osipenko 		OF_LPDDR2_VENDOR_CMP("micron", MICRON)
38538322cf4SDmitry Osipenko 
38638322cf4SDmitry Osipenko #undef OF_LPDDR2_VENDOR_CMP
38738322cf4SDmitry Osipenko 	}
38838322cf4SDmitry Osipenko 
38938322cf4SDmitry Osipenko 	if (!info.manufacturer_id)
39038322cf4SDmitry Osipenko 		info.manufacturer_id = -ENOENT;
39138322cf4SDmitry Osipenko 
39238322cf4SDmitry Osipenko 	ret_info = devm_kzalloc(dev, sizeof(*ret_info), GFP_KERNEL);
39338322cf4SDmitry Osipenko 	if (ret_info)
39438322cf4SDmitry Osipenko 		*ret_info = info;
39538322cf4SDmitry Osipenko 
39638322cf4SDmitry Osipenko 	return ret_info;
39738322cf4SDmitry Osipenko }
39838322cf4SDmitry Osipenko EXPORT_SYMBOL(of_lpddr2_get_info);
399