xref: /arm-trusted-firmware/plat/st/common/stm32mp_dt.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu #include <errno.h>
9*91f16700Schasinglulu 
10*91f16700Schasinglulu #include <common/debug.h>
11*91f16700Schasinglulu #include <common/fdt_wrappers.h>
12*91f16700Schasinglulu #include <drivers/st/regulator.h>
13*91f16700Schasinglulu #include <drivers/st/stm32_gpio.h>
14*91f16700Schasinglulu #include <libfdt.h>
15*91f16700Schasinglulu 
16*91f16700Schasinglulu #include <platform_def.h>
17*91f16700Schasinglulu #include <stm32mp_dt.h>
18*91f16700Schasinglulu 
19*91f16700Schasinglulu static void *fdt;
20*91f16700Schasinglulu 
21*91f16700Schasinglulu /*******************************************************************************
22*91f16700Schasinglulu  * This function checks device tree file with its header.
23*91f16700Schasinglulu  * Returns 0 on success and a negative FDT error code on failure.
24*91f16700Schasinglulu  ******************************************************************************/
25*91f16700Schasinglulu int dt_open_and_check(uintptr_t dt_addr)
26*91f16700Schasinglulu {
27*91f16700Schasinglulu 	int ret;
28*91f16700Schasinglulu 
29*91f16700Schasinglulu 	ret = fdt_check_header((void *)dt_addr);
30*91f16700Schasinglulu 	if (ret == 0) {
31*91f16700Schasinglulu 		fdt = (void *)dt_addr;
32*91f16700Schasinglulu 	}
33*91f16700Schasinglulu 
34*91f16700Schasinglulu 	return ret;
35*91f16700Schasinglulu }
36*91f16700Schasinglulu 
37*91f16700Schasinglulu /*******************************************************************************
38*91f16700Schasinglulu  * This function gets the address of the DT.
39*91f16700Schasinglulu  * If DT is OK, fdt_addr is filled with DT address.
40*91f16700Schasinglulu  * Returns 1 if success, 0 otherwise.
41*91f16700Schasinglulu  ******************************************************************************/
42*91f16700Schasinglulu int fdt_get_address(void **fdt_addr)
43*91f16700Schasinglulu {
44*91f16700Schasinglulu 	if (fdt == NULL) {
45*91f16700Schasinglulu 		return 0;
46*91f16700Schasinglulu 	}
47*91f16700Schasinglulu 
48*91f16700Schasinglulu 	*fdt_addr = fdt;
49*91f16700Schasinglulu 
50*91f16700Schasinglulu 	return 1;
51*91f16700Schasinglulu }
52*91f16700Schasinglulu 
53*91f16700Schasinglulu /*******************************************************************************
54*91f16700Schasinglulu  * This function check the presence of a node (generic use of fdt library).
55*91f16700Schasinglulu  * Returns true if present, else return false.
56*91f16700Schasinglulu  ******************************************************************************/
57*91f16700Schasinglulu bool fdt_check_node(int node)
58*91f16700Schasinglulu {
59*91f16700Schasinglulu 	int len;
60*91f16700Schasinglulu 	const char *cchar;
61*91f16700Schasinglulu 
62*91f16700Schasinglulu 	cchar = fdt_get_name(fdt, node, &len);
63*91f16700Schasinglulu 
64*91f16700Schasinglulu 	return (cchar != NULL) && (len >= 0);
65*91f16700Schasinglulu }
66*91f16700Schasinglulu 
67*91f16700Schasinglulu /*******************************************************************************
68*91f16700Schasinglulu  * This function return global node status (generic use of fdt library).
69*91f16700Schasinglulu  ******************************************************************************/
70*91f16700Schasinglulu uint8_t fdt_get_status(int node)
71*91f16700Schasinglulu {
72*91f16700Schasinglulu 	uint8_t status = DT_DISABLED;
73*91f16700Schasinglulu 	const char *cchar;
74*91f16700Schasinglulu 
75*91f16700Schasinglulu 	cchar = fdt_getprop(fdt, node, "status", NULL);
76*91f16700Schasinglulu 	if ((cchar == NULL) ||
77*91f16700Schasinglulu 	    (strncmp(cchar, "okay", strlen("okay")) == 0)) {
78*91f16700Schasinglulu 		status |= DT_NON_SECURE;
79*91f16700Schasinglulu 	}
80*91f16700Schasinglulu 
81*91f16700Schasinglulu 	cchar = fdt_getprop(fdt, node, "secure-status", NULL);
82*91f16700Schasinglulu 	if (((cchar == NULL) && (status == DT_NON_SECURE)) ||
83*91f16700Schasinglulu 	    ((cchar != NULL) && (strncmp(cchar, "okay", strlen("okay")) == 0))) {
84*91f16700Schasinglulu 		status |= DT_SECURE;
85*91f16700Schasinglulu 	}
86*91f16700Schasinglulu 
87*91f16700Schasinglulu 	return status;
88*91f16700Schasinglulu }
89*91f16700Schasinglulu 
90*91f16700Schasinglulu #if ENABLE_ASSERTIONS
91*91f16700Schasinglulu /*******************************************************************************
92*91f16700Schasinglulu  * This function returns the address cells from the node parent.
93*91f16700Schasinglulu  * Returns:
94*91f16700Schasinglulu  * - #address-cells value if success.
95*91f16700Schasinglulu  * - invalid value if error.
96*91f16700Schasinglulu  * - a default value if undefined #address-cells property as per libfdt
97*91f16700Schasinglulu  *   implementation.
98*91f16700Schasinglulu  ******************************************************************************/
99*91f16700Schasinglulu static int fdt_get_node_parent_address_cells(int node)
100*91f16700Schasinglulu {
101*91f16700Schasinglulu 	int parent;
102*91f16700Schasinglulu 
103*91f16700Schasinglulu 	parent = fdt_parent_offset(fdt, node);
104*91f16700Schasinglulu 	if (parent < 0) {
105*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
106*91f16700Schasinglulu 	}
107*91f16700Schasinglulu 
108*91f16700Schasinglulu 	return fdt_address_cells(fdt, parent);
109*91f16700Schasinglulu }
110*91f16700Schasinglulu #endif
111*91f16700Schasinglulu 
112*91f16700Schasinglulu /*******************************************************************************
113*91f16700Schasinglulu  * This function gets the stdout pin configuration information from the DT.
114*91f16700Schasinglulu  * And then calls the sub-function to treat it and set GPIO registers.
115*91f16700Schasinglulu  * Returns 0 on success and a negative FDT error code on failure.
116*91f16700Schasinglulu  ******************************************************************************/
117*91f16700Schasinglulu int dt_set_stdout_pinctrl(void)
118*91f16700Schasinglulu {
119*91f16700Schasinglulu 	int node;
120*91f16700Schasinglulu 
121*91f16700Schasinglulu 	node = fdt_get_stdout_node_offset(fdt);
122*91f16700Schasinglulu 	if (node < 0) {
123*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
124*91f16700Schasinglulu 	}
125*91f16700Schasinglulu 
126*91f16700Schasinglulu 	return dt_set_pinctrl_config(node);
127*91f16700Schasinglulu }
128*91f16700Schasinglulu 
129*91f16700Schasinglulu /*******************************************************************************
130*91f16700Schasinglulu  * This function fills the generic information from a given node.
131*91f16700Schasinglulu  ******************************************************************************/
132*91f16700Schasinglulu void dt_fill_device_info(struct dt_node_info *info, int node)
133*91f16700Schasinglulu {
134*91f16700Schasinglulu 	const fdt32_t *cuint;
135*91f16700Schasinglulu 
136*91f16700Schasinglulu 	assert(fdt_get_node_parent_address_cells(node) == 1);
137*91f16700Schasinglulu 
138*91f16700Schasinglulu 	cuint = fdt_getprop(fdt, node, "reg", NULL);
139*91f16700Schasinglulu 	if (cuint != NULL) {
140*91f16700Schasinglulu 		info->base = fdt32_to_cpu(*cuint);
141*91f16700Schasinglulu 	} else {
142*91f16700Schasinglulu 		info->base = 0;
143*91f16700Schasinglulu 	}
144*91f16700Schasinglulu 
145*91f16700Schasinglulu 	cuint = fdt_getprop(fdt, node, "clocks", NULL);
146*91f16700Schasinglulu 	if (cuint != NULL) {
147*91f16700Schasinglulu 		cuint++;
148*91f16700Schasinglulu 		info->clock = (int)fdt32_to_cpu(*cuint);
149*91f16700Schasinglulu 	} else {
150*91f16700Schasinglulu 		info->clock = -1;
151*91f16700Schasinglulu 	}
152*91f16700Schasinglulu 
153*91f16700Schasinglulu 	cuint = fdt_getprop(fdt, node, "resets", NULL);
154*91f16700Schasinglulu 	if (cuint != NULL) {
155*91f16700Schasinglulu 		cuint++;
156*91f16700Schasinglulu 		info->reset = (int)fdt32_to_cpu(*cuint);
157*91f16700Schasinglulu 	} else {
158*91f16700Schasinglulu 		info->reset = -1;
159*91f16700Schasinglulu 	}
160*91f16700Schasinglulu 
161*91f16700Schasinglulu 	info->status = fdt_get_status(node);
162*91f16700Schasinglulu }
163*91f16700Schasinglulu 
164*91f16700Schasinglulu /*******************************************************************************
165*91f16700Schasinglulu  * This function retrieve the generic information from DT.
166*91f16700Schasinglulu  * Returns node on success and a negative FDT error code on failure.
167*91f16700Schasinglulu  ******************************************************************************/
168*91f16700Schasinglulu int dt_get_node(struct dt_node_info *info, int offset, const char *compat)
169*91f16700Schasinglulu {
170*91f16700Schasinglulu 	int node;
171*91f16700Schasinglulu 
172*91f16700Schasinglulu 	node = fdt_node_offset_by_compatible(fdt, offset, compat);
173*91f16700Schasinglulu 	if (node < 0) {
174*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
175*91f16700Schasinglulu 	}
176*91f16700Schasinglulu 
177*91f16700Schasinglulu 	dt_fill_device_info(info, node);
178*91f16700Schasinglulu 
179*91f16700Schasinglulu 	return node;
180*91f16700Schasinglulu }
181*91f16700Schasinglulu 
182*91f16700Schasinglulu /*******************************************************************************
183*91f16700Schasinglulu  * This function gets the UART instance info of stdout from the DT.
184*91f16700Schasinglulu  * Returns node on success and a negative FDT error code on failure.
185*91f16700Schasinglulu  ******************************************************************************/
186*91f16700Schasinglulu int dt_get_stdout_uart_info(struct dt_node_info *info)
187*91f16700Schasinglulu {
188*91f16700Schasinglulu 	int node;
189*91f16700Schasinglulu 
190*91f16700Schasinglulu 	node = fdt_get_stdout_node_offset(fdt);
191*91f16700Schasinglulu 	if (node < 0) {
192*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
193*91f16700Schasinglulu 	}
194*91f16700Schasinglulu 
195*91f16700Schasinglulu 	dt_fill_device_info(info, node);
196*91f16700Schasinglulu 
197*91f16700Schasinglulu 	return node;
198*91f16700Schasinglulu }
199*91f16700Schasinglulu 
200*91f16700Schasinglulu /*******************************************************************************
201*91f16700Schasinglulu  * This function returns the node offset matching compatible string in the DT,
202*91f16700Schasinglulu  * and also matching the reg property with the given address.
203*91f16700Schasinglulu  * Returns value on success, and error value on failure.
204*91f16700Schasinglulu  ******************************************************************************/
205*91f16700Schasinglulu int dt_match_instance_by_compatible(const char *compatible, uintptr_t address)
206*91f16700Schasinglulu {
207*91f16700Schasinglulu 	int node;
208*91f16700Schasinglulu 
209*91f16700Schasinglulu 	fdt_for_each_compatible_node(fdt, node, compatible) {
210*91f16700Schasinglulu 		const fdt32_t *cuint;
211*91f16700Schasinglulu 
212*91f16700Schasinglulu 		assert(fdt_get_node_parent_address_cells(node) == 1);
213*91f16700Schasinglulu 
214*91f16700Schasinglulu 		cuint = fdt_getprop(fdt, node, "reg", NULL);
215*91f16700Schasinglulu 		if (cuint == NULL) {
216*91f16700Schasinglulu 			continue;
217*91f16700Schasinglulu 		}
218*91f16700Schasinglulu 
219*91f16700Schasinglulu 		if ((uintptr_t)fdt32_to_cpu(*cuint) == address) {
220*91f16700Schasinglulu 			return node;
221*91f16700Schasinglulu 		}
222*91f16700Schasinglulu 	}
223*91f16700Schasinglulu 
224*91f16700Schasinglulu 	return -FDT_ERR_NOTFOUND;
225*91f16700Schasinglulu }
226*91f16700Schasinglulu 
227*91f16700Schasinglulu /*******************************************************************************
228*91f16700Schasinglulu  * This function gets DDR size information from the DT.
229*91f16700Schasinglulu  * Returns value in bytes on success, and 0 on failure.
230*91f16700Schasinglulu  ******************************************************************************/
231*91f16700Schasinglulu size_t dt_get_ddr_size(void)
232*91f16700Schasinglulu {
233*91f16700Schasinglulu 	static size_t size;
234*91f16700Schasinglulu 	int node;
235*91f16700Schasinglulu 
236*91f16700Schasinglulu 	if (size != 0U) {
237*91f16700Schasinglulu 		return size;
238*91f16700Schasinglulu 	}
239*91f16700Schasinglulu 
240*91f16700Schasinglulu 	node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT);
241*91f16700Schasinglulu 	if (node < 0) {
242*91f16700Schasinglulu 		INFO("%s: Cannot read DDR node in DT\n", __func__);
243*91f16700Schasinglulu 		return 0U;
244*91f16700Schasinglulu 	}
245*91f16700Schasinglulu 
246*91f16700Schasinglulu 	size = (size_t)fdt_read_uint32_default(fdt, node, "st,mem-size", 0U);
247*91f16700Schasinglulu 
248*91f16700Schasinglulu 	flush_dcache_range((uintptr_t)&size, sizeof(size_t));
249*91f16700Schasinglulu 
250*91f16700Schasinglulu 	return size;
251*91f16700Schasinglulu }
252*91f16700Schasinglulu 
253*91f16700Schasinglulu /*******************************************************************************
254*91f16700Schasinglulu  * This function gets PWR VDD regulator voltage information from the DT.
255*91f16700Schasinglulu  * Returns value in microvolts on success, and 0 on failure.
256*91f16700Schasinglulu  ******************************************************************************/
257*91f16700Schasinglulu uint32_t dt_get_pwr_vdd_voltage(void)
258*91f16700Schasinglulu {
259*91f16700Schasinglulu 	struct rdev *regul = dt_get_vdd_regulator();
260*91f16700Schasinglulu 	uint16_t min;
261*91f16700Schasinglulu 
262*91f16700Schasinglulu 	if (regul == NULL) {
263*91f16700Schasinglulu 		return 0;
264*91f16700Schasinglulu 	}
265*91f16700Schasinglulu 
266*91f16700Schasinglulu 	regulator_get_range(regul, &min, NULL);
267*91f16700Schasinglulu 
268*91f16700Schasinglulu 	return (uint32_t)min * 1000U;
269*91f16700Schasinglulu }
270*91f16700Schasinglulu 
271*91f16700Schasinglulu /*******************************************************************************
272*91f16700Schasinglulu  * This function retrieves VDD supply regulator from DT.
273*91f16700Schasinglulu  * Returns an rdev taken from supply node, NULL otherwise.
274*91f16700Schasinglulu  ******************************************************************************/
275*91f16700Schasinglulu struct rdev *dt_get_vdd_regulator(void)
276*91f16700Schasinglulu {
277*91f16700Schasinglulu 	int node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT);
278*91f16700Schasinglulu 
279*91f16700Schasinglulu 	if (node < 0) {
280*91f16700Schasinglulu 		return NULL;
281*91f16700Schasinglulu 	}
282*91f16700Schasinglulu 
283*91f16700Schasinglulu 	return regulator_get_by_supply_name(fdt, node, "vdd");
284*91f16700Schasinglulu }
285*91f16700Schasinglulu 
286*91f16700Schasinglulu /*******************************************************************************
287*91f16700Schasinglulu  * This function retrieves CPU supply regulator from DT.
288*91f16700Schasinglulu  * Returns an rdev taken from supply node, NULL otherwise.
289*91f16700Schasinglulu  ******************************************************************************/
290*91f16700Schasinglulu struct rdev *dt_get_cpu_regulator(void)
291*91f16700Schasinglulu {
292*91f16700Schasinglulu 	int node = fdt_path_offset(fdt, "/cpus/cpu@0");
293*91f16700Schasinglulu 
294*91f16700Schasinglulu 	if (node < 0) {
295*91f16700Schasinglulu 		return NULL;
296*91f16700Schasinglulu 	}
297*91f16700Schasinglulu 
298*91f16700Schasinglulu 	return regulator_get_by_supply_name(fdt, node, "cpu");
299*91f16700Schasinglulu }
300*91f16700Schasinglulu 
301*91f16700Schasinglulu /*******************************************************************************
302*91f16700Schasinglulu  * This function retrieves board model from DT
303*91f16700Schasinglulu  * Returns string taken from model node, NULL otherwise
304*91f16700Schasinglulu  ******************************************************************************/
305*91f16700Schasinglulu const char *dt_get_board_model(void)
306*91f16700Schasinglulu {
307*91f16700Schasinglulu 	int node = fdt_path_offset(fdt, "/");
308*91f16700Schasinglulu 
309*91f16700Schasinglulu 	if (node < 0) {
310*91f16700Schasinglulu 		return NULL;
311*91f16700Schasinglulu 	}
312*91f16700Schasinglulu 
313*91f16700Schasinglulu 	return (const char *)fdt_getprop(fdt, node, "model", NULL);
314*91f16700Schasinglulu }
315*91f16700Schasinglulu 
316*91f16700Schasinglulu /*******************************************************************************
317*91f16700Schasinglulu  * dt_find_otp_name: get OTP ID and length in DT.
318*91f16700Schasinglulu  * name: sub-node name to look up.
319*91f16700Schasinglulu  * otp: pointer to read OTP number or NULL.
320*91f16700Schasinglulu  * otp_len: pointer to read OTP length in bits or NULL.
321*91f16700Schasinglulu  * return value: 0 if no error, an FDT error value otherwise.
322*91f16700Schasinglulu  ******************************************************************************/
323*91f16700Schasinglulu int dt_find_otp_name(const char *name, uint32_t *otp, uint32_t *otp_len)
324*91f16700Schasinglulu {
325*91f16700Schasinglulu 	int node;
326*91f16700Schasinglulu 	int len;
327*91f16700Schasinglulu 	const fdt32_t *cuint;
328*91f16700Schasinglulu 
329*91f16700Schasinglulu 	if ((name == NULL) || (otp == NULL)) {
330*91f16700Schasinglulu 		return -FDT_ERR_BADVALUE;
331*91f16700Schasinglulu 	}
332*91f16700Schasinglulu 
333*91f16700Schasinglulu 	node = fdt_node_offset_by_compatible(fdt, -1, DT_BSEC_COMPAT);
334*91f16700Schasinglulu 	if (node < 0) {
335*91f16700Schasinglulu 		return node;
336*91f16700Schasinglulu 	}
337*91f16700Schasinglulu 
338*91f16700Schasinglulu 	node = fdt_subnode_offset(fdt, node, name);
339*91f16700Schasinglulu 	if (node < 0) {
340*91f16700Schasinglulu 		ERROR("nvmem node %s not found\n", name);
341*91f16700Schasinglulu 		return node;
342*91f16700Schasinglulu 	}
343*91f16700Schasinglulu 
344*91f16700Schasinglulu 	cuint = fdt_getprop(fdt, node, "reg", &len);
345*91f16700Schasinglulu 	if ((cuint == NULL) || (len != (2 * (int)sizeof(uint32_t)))) {
346*91f16700Schasinglulu 		ERROR("Malformed nvmem node %s: ignored\n", name);
347*91f16700Schasinglulu 		return -FDT_ERR_BADVALUE;
348*91f16700Schasinglulu 	}
349*91f16700Schasinglulu 
350*91f16700Schasinglulu 	if ((fdt32_to_cpu(*cuint) % sizeof(uint32_t)) != 0U) {
351*91f16700Schasinglulu 		ERROR("Misaligned nvmem %s element: ignored\n", name);
352*91f16700Schasinglulu 		return -FDT_ERR_BADVALUE;
353*91f16700Schasinglulu 	}
354*91f16700Schasinglulu 
355*91f16700Schasinglulu 	if (otp != NULL) {
356*91f16700Schasinglulu 		*otp = fdt32_to_cpu(*cuint) / sizeof(uint32_t);
357*91f16700Schasinglulu 	}
358*91f16700Schasinglulu 
359*91f16700Schasinglulu 	if (otp_len != NULL) {
360*91f16700Schasinglulu 		cuint++;
361*91f16700Schasinglulu 		*otp_len = fdt32_to_cpu(*cuint) * CHAR_BIT;
362*91f16700Schasinglulu 	}
363*91f16700Schasinglulu 
364*91f16700Schasinglulu 	return 0;
365*91f16700Schasinglulu }
366*91f16700Schasinglulu 
367*91f16700Schasinglulu /*******************************************************************************
368*91f16700Schasinglulu  * This function gets the pin count for a GPIO bank based from the FDT.
369*91f16700Schasinglulu  * It also checks node consistency.
370*91f16700Schasinglulu  ******************************************************************************/
371*91f16700Schasinglulu int fdt_get_gpio_bank_pin_count(unsigned int bank)
372*91f16700Schasinglulu {
373*91f16700Schasinglulu 	int pinctrl_node;
374*91f16700Schasinglulu 	int node;
375*91f16700Schasinglulu 	uint32_t bank_offset;
376*91f16700Schasinglulu 
377*91f16700Schasinglulu 	pinctrl_node = stm32_get_gpio_bank_pinctrl_node(fdt, bank);
378*91f16700Schasinglulu 	if (pinctrl_node < 0) {
379*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
380*91f16700Schasinglulu 	}
381*91f16700Schasinglulu 
382*91f16700Schasinglulu 	bank_offset = stm32_get_gpio_bank_offset(bank);
383*91f16700Schasinglulu 
384*91f16700Schasinglulu 	fdt_for_each_subnode(node, fdt, pinctrl_node) {
385*91f16700Schasinglulu 		const fdt32_t *cuint;
386*91f16700Schasinglulu 		int pin_count = 0;
387*91f16700Schasinglulu 		int len;
388*91f16700Schasinglulu 		int i;
389*91f16700Schasinglulu 
390*91f16700Schasinglulu 		if (fdt_getprop(fdt, node, "gpio-controller", NULL) == NULL) {
391*91f16700Schasinglulu 			continue;
392*91f16700Schasinglulu 		}
393*91f16700Schasinglulu 
394*91f16700Schasinglulu 		cuint = fdt_getprop(fdt, node, "reg", NULL);
395*91f16700Schasinglulu 		if (cuint == NULL) {
396*91f16700Schasinglulu 			continue;
397*91f16700Schasinglulu 		}
398*91f16700Schasinglulu 
399*91f16700Schasinglulu 		if (fdt32_to_cpu(*cuint) != bank_offset) {
400*91f16700Schasinglulu 			continue;
401*91f16700Schasinglulu 		}
402*91f16700Schasinglulu 
403*91f16700Schasinglulu 		if (fdt_get_status(node) == DT_DISABLED) {
404*91f16700Schasinglulu 			return 0;
405*91f16700Schasinglulu 		}
406*91f16700Schasinglulu 
407*91f16700Schasinglulu 		/* Parse gpio-ranges with its 4 parameters */
408*91f16700Schasinglulu 		cuint = fdt_getprop(fdt, node, "gpio-ranges", &len);
409*91f16700Schasinglulu 		len /= sizeof(*cuint);
410*91f16700Schasinglulu 		if ((len % 4) != 0) {
411*91f16700Schasinglulu 			return -FDT_ERR_BADVALUE;
412*91f16700Schasinglulu 		}
413*91f16700Schasinglulu 
414*91f16700Schasinglulu 		/* Get the last defined gpio line (offset + nb of pins) */
415*91f16700Schasinglulu 		for (i = 0; i < len; i += 4) {
416*91f16700Schasinglulu 			pin_count = MAX(pin_count, (int)(fdt32_to_cpu(cuint[i + 1]) +
417*91f16700Schasinglulu 							 fdt32_to_cpu(cuint[i + 3])));
418*91f16700Schasinglulu 		}
419*91f16700Schasinglulu 
420*91f16700Schasinglulu 		return pin_count;
421*91f16700Schasinglulu 	}
422*91f16700Schasinglulu 
423*91f16700Schasinglulu 	return 0;
424*91f16700Schasinglulu }
425