1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2008
4 * Heiko Schocher, DENX Software Engineering, hs@denx.de.
5 *
6 * (C) Copyright 2011
7 * Holger Brunck, Keymile GmbH Hannover, holger.brunck@keymile.com
8 */
9
10#include <common.h>
11#include <env.h>
12#include <ioports.h>
13#include <command.h>
14#include <malloc.h>
15#include <cli_hush.h>
16#include <net.h>
17#include <netdev.h>
18#include <asm/global_data.h>
19#include <asm/io.h>
20#include <linux/ctype.h>
21#include <linux/delay.h>
22#include <linux/bug.h>
23#include <bootcount.h>
24
25#if defined(CONFIG_POST)
26#include "post.h"
27#endif
28#include "common.h"
29#include <i2c.h>
30
31DECLARE_GLOBAL_DATA_PTR;
32
33/*
34 * Set Keymile specific environment variables
35 * Currently only some memory layout variables are calculated here
36 * ... ------------------------------------------------
37 * ... |@rootfsaddr |@pnvramaddr |@varaddr |@reserved |@END_OF_RAM
38 * ... |<------------------- pram ------------------->|
39 * ... ------------------------------------------------
40 * @END_OF_RAM: denotes the RAM size
41 * @pnvramaddr: Startadress of pseudo non volatile RAM in hex
42 * @pram      : preserved ram size in k
43 * @varaddr   : startadress for /var mounted into RAM
44 */
45int set_km_env(void)
46{
47	unsigned int pnvramaddr;
48	unsigned int pram;
49	unsigned int varaddr;
50	unsigned int kernelmem;
51	unsigned long rootfssize = 0;
52	char envval[16];
53	char *p;
54
55	pnvramaddr = CFG_SYS_SDRAM_BASE + gd->ram_size -
56		CONFIG_KM_RESERVED_PRAM - CONFIG_KM_PHRAM - CONFIG_KM_PNVRAM;
57	sprintf(envval, "0x%x", pnvramaddr);
58	env_set("pnvramaddr", envval);
59
60	/* try to read rootfssize (ram image) from environment */
61	p = env_get("rootfssize");
62	if (p)
63		strict_strtoul(p, 16, &rootfssize);
64	pram = (rootfssize + CONFIG_KM_RESERVED_PRAM + CONFIG_KM_PHRAM +
65		CONFIG_KM_PNVRAM) / 0x400;
66	env_set_ulong("pram", pram);
67
68	varaddr = CFG_SYS_SDRAM_BASE + gd->ram_size -
69		CONFIG_KM_RESERVED_PRAM - CONFIG_KM_PHRAM;
70	env_set_hex("varaddr", varaddr);
71	sprintf(envval, "0x%x", varaddr);
72	env_set("varaddr", envval);
73
74	kernelmem = gd->ram_size - 0x400 * pram;
75	sprintf(envval, "0x%x", kernelmem);
76	env_set("kernelmem", envval);
77
78	return 0;
79}
80
81#if IS_ENABLED(CONFIG_PG_WCOM_UBOOT_UPDATE_SUPPORTED)
82#if   ((!IS_ENABLED(CONFIG_PG_WCOM_UBOOT_BOOTPACKAGE) && \
83	!IS_ENABLED(CONFIG_PG_WCOM_UBOOT_UPDATE)) ||     \
84	(IS_ENABLED(CONFIG_PG_WCOM_UBOOT_BOOTPACKAGE) && \
85	IS_ENABLED(CONFIG_PG_WCOM_UBOOT_UPDATE)))
86#error "It has to be either bootpackage or update u-boot image!"
87#endif
88void check_for_uboot_update(void)
89{
90	void (*uboot_update_entry)(void) =
91		(void (*)(void)) CONFIG_PG_WCOM_UBOOT_UPDATE_TEXT_BASE;
92	char *isupdated = env_get("updateduboot");
93	ulong bootcount = bootcount_load();
94	ulong ebootcount = 0;
95
96	if (IS_ENABLED(CONFIG_PG_WCOM_UBOOT_BOOTPACKAGE)) {
97		/*
98		 * When running in factory burned u-boot move to the updated
99		 * u-boot version only if updateduboot envvar is set to 'yes'
100		 * and bootcount limit is not exceeded.
101		 * Board must be able to start in factory bootloader mode!
102		 */
103		if (isupdated && !strncmp(isupdated, "yes", 3) &&
104		    bootcount <= CONFIG_BOOTCOUNT_BOOTLIMIT) {
105			printf("Check update: update detected, ");
106			printf("starting new image @%08x ...\n",
107			       CONFIG_PG_WCOM_UBOOT_UPDATE_TEXT_BASE);
108			ebootcount = early_bootcount_load();
109			if (ebootcount <= CONFIG_BOOTCOUNT_BOOTLIMIT) {
110				early_bootcount_store(++ebootcount);
111				uboot_update_entry();
112			} else {
113				printf("Check update: warning: ");
114				printf("early bootcount exceeded (%lu)\n",
115				       ebootcount);
116			}
117		}
118		printf("Check update: starting factory image @%08x ...\n",
119		       CONFIG_TEXT_BASE);
120	} else if (IS_ENABLED(CONFIG_PG_WCOM_UBOOT_UPDATE)) {
121		/*
122		 * When running in field updated u-boot, make sure that
123		 * bootcount limit is never exceeded. Must never happen!
124		 */
125		WARN_ON(bootcount > CONFIG_BOOTCOUNT_BOOTLIMIT);
126		printf("Check update: updated u-boot starting @%08x ...\n",
127		       CONFIG_TEXT_BASE);
128	}
129}
130#endif
131
132#if defined(CONFIG_SYS_I2C_INIT_BOARD)
133static void i2c_write_start_seq(void)
134{
135	set_sda(1);
136	udelay(DELAY_HALF_PERIOD);
137	set_scl(1);
138	udelay(DELAY_HALF_PERIOD);
139	set_sda(0);
140	udelay(DELAY_HALF_PERIOD);
141	set_scl(0);
142	udelay(DELAY_HALF_PERIOD);
143}
144
145/*
146 * I2C is a synchronous protocol and resets of the processor in the middle
147 * of an access can block the I2C Bus until a powerdown of the full unit is
148 * done. This function toggles the SCL until the SCL and SCA line are
149 * released, but max. 16 times, after this a I2C start-sequence is sent.
150 * This I2C Deblocking mechanism was developed by Keymile in association
151 * with Anatech and Atmel in 1998.
152 */
153int i2c_make_abort(void)
154{
155	int	scl_state = 0;
156	int	sda_state = 0;
157	int	i = 0;
158	int	ret = 0;
159
160	if (!get_sda()) {
161		ret = -1;
162		while (i < 16) {
163			i++;
164			set_scl(0);
165			udelay(DELAY_ABORT_SEQ);
166			set_scl(1);
167			udelay(DELAY_ABORT_SEQ);
168			scl_state = get_scl();
169			sda_state = get_sda();
170			if (scl_state && sda_state) {
171				ret = 0;
172				break;
173			}
174		}
175	}
176	if (ret == 0)
177		for (i = 0; i < 5; i++)
178			i2c_write_start_seq();
179
180	/* respect stop setup time */
181	udelay(DELAY_ABORT_SEQ);
182	set_scl(1);
183	udelay(DELAY_ABORT_SEQ);
184	set_sda(1);
185	get_sda();
186
187	return ret;
188}
189
190/**
191 * i2c_init_board - reset i2c bus. When the board is powercycled during a
192 * bus transfer it might hang; for details see doc/I2C_Edge_Conditions.
193 */
194void i2c_init_board(void)
195{
196	/* Now run the AbortSequence() */
197	i2c_make_abort();
198}
199#endif
200
201#if defined(CONFIG_KM_COMMON_ETH_INIT)
202int board_eth_init(struct bd_info *bis)
203{
204	if (ethernet_present())
205		return cpu_eth_init(bis);
206
207	return -1;
208}
209#endif
210
211/*
212 * do_setboardid command
213 * read out the board id and the hw key from the intventory EEPROM and set
214 * this values as environment variables.
215 */
216static int do_setboardid(struct cmd_tbl *cmdtp, int flag, int argc,
217			 char *const argv[])
218{
219	unsigned char buf[32];
220	char *p;
221
222	p = get_local_var("IVM_BoardId");
223	if (!p) {
224		printf("can't get the IVM_Boardid\n");
225		return 1;
226	}
227	strcpy((char *)buf, p);
228	env_set("boardid", (char *)buf);
229	printf("set boardid=%s\n", buf);
230
231	p = get_local_var("IVM_HWKey");
232	if (!p) {
233		printf("can't get the IVM_HWKey\n");
234		return 1;
235	}
236	strcpy((char *)buf, p);
237	env_set("hwkey", (char *)buf);
238	printf("set hwkey=%s\n", buf);
239	printf("Execute manually saveenv for persistent storage.\n");
240
241	return 0;
242}
243
244U_BOOT_CMD(km_setboardid, 1, 0, do_setboardid, "setboardid",
245	   "read out bid and hwkey from IVM and set in environment");
246
247/*
248 * command km_checkbidhwk
249 *	if "boardid" and "hwkey" are not already set in the environment, do:
250 *		if a "boardIdListHex" exists in the environment:
251 *			- read ivm data for boardid and hwkey
252 *			- compare each entry of the boardIdListHex with the
253 *				IVM data:
254 *			if match:
255 *				set environment variables boardid, boardId,
256 *				hwkey, hwKey to	the found values
257 *				both (boardid and boardId) are set because
258 *				they might be used differently in the
259 *				application and in the init scripts (?)
260 *	return 0 in case of match, 1 if not match or error
261 */
262static int do_checkboardidhwk(struct cmd_tbl *cmdtp, int flag, int argc,
263			      char *const argv[])
264{
265	unsigned long ivmbid = 0, ivmhwkey = 0;
266	unsigned long envbid = 0, envhwkey = 0;
267	char *p;
268	int verbose = argc > 1 && *argv[1] == 'v';
269	int rc = 0;
270
271	/*
272	 * first read out the real inventory values, these values are
273	 * already stored in the local hush variables
274	 */
275	p = get_local_var("IVM_BoardId");
276	if (!p) {
277		printf("can't get the IVM_Boardid\n");
278		return 1;
279	}
280	rc = strict_strtoul(p, 16, &ivmbid);
281
282	p = get_local_var("IVM_HWKey");
283	if (!p) {
284		printf("can't get the IVM_HWKey\n");
285		return 1;
286	}
287	rc = strict_strtoul(p, 16, &ivmhwkey);
288
289	if (!ivmbid || !ivmhwkey) {
290		printf("Error: IVM_BoardId and/or IVM_HWKey not set!\n");
291		return rc;
292	}
293
294	/* now try to read values from environment if available */
295	p = env_get("boardid");
296	if (p)
297		rc = strict_strtoul(p, 16, &envbid);
298	p = env_get("hwkey");
299	if (p)
300		rc = strict_strtoul(p, 16, &envhwkey);
301	if (rc != 0) {
302		printf("strict_strtoul returns error: %d", rc);
303		return rc;
304	}
305
306	if (!envbid || !envhwkey) {
307		/*
308		 * BoardId/HWkey not available in the environment, so try the
309		 * environment variable for BoardId/HWkey list
310		 */
311		char *bidhwklist = env_get("boardIdListHex");
312
313		if (bidhwklist) {
314			int found = 0;
315			char *rest = bidhwklist;
316			char *endp;
317
318			if (verbose) {
319				printf("IVM_BoardId: %ld, IVM_HWKey=%ld\n",
320				       ivmbid, ivmhwkey);
321				printf("boardIdHwKeyList: %s\n", bidhwklist);
322			}
323			while (!found) {
324				/* loop over each bid/hwkey pair in the list */
325				unsigned long bid   = 0;
326				unsigned long hwkey = 0;
327
328				while (*rest && !isxdigit(*rest))
329					rest++;
330				/*
331				 * use simple_strtoul because we need &end and
332				 * we know we got non numeric char at the end
333				 */
334				bid = hextoul(rest, &endp);
335				/* BoardId and HWkey are separated with a "_" */
336				if (*endp == '_') {
337					rest  = endp + 1;
338					/*
339					 * use simple_strtoul because we need
340					 * &end
341					 */
342					hwkey = hextoul(rest, &endp);
343					rest  = endp;
344					while (*rest && !isxdigit(*rest))
345						rest++;
346				}
347				if (!bid || !hwkey) {
348					/* end of list */
349					break;
350				}
351				if (verbose) {
352					printf("trying bid=0x%lX, hwkey=%ld\n",
353					       bid, hwkey);
354				}
355				/*
356				 * Compare the values of the found entry in the
357				 * list with the valid values which are stored
358				 * in the inventory eeprom. If they are equal
359				 * set the values in environment variables.
360				 */
361				if (bid == ivmbid && hwkey == ivmhwkey) {
362					found = 1;
363					envbid   = bid;
364					envhwkey = hwkey;
365					env_set_hex("boardid", bid);
366					env_set_hex("hwkey", hwkey);
367				}
368			} /* end while( ! found ) */
369		}
370	}
371
372	/* compare now the values */
373	if (ivmbid == envbid && ivmhwkey == envhwkey) {
374		printf("boardid=0x%3lX, hwkey=%ld\n", envbid, envhwkey);
375		rc = 0; /* match */
376	} else {
377		printf("Error: env boardid=0x%3lX, hwkey=%ld\n", envbid,
378		       envhwkey);
379		printf("       IVM bId=0x%3lX, hwKey=%ld\n", ivmbid, ivmhwkey);
380		rc = 1; /* don't match */
381	}
382	return rc;
383}
384
385U_BOOT_CMD(km_checkbidhwk, 2, 0, do_checkboardidhwk,
386	   "check boardid and hwkey",
387	   "[v]\n  - check environment parameter \"boardIdListHex\" against stored boardid and hwkey from the IVM\n    v: verbose output"
388);
389
390/*
391 * command km_checktestboot
392 *  if the testpin of the board is asserted, return 1
393 *  *	else return 0
394 */
395static int do_checktestboot(struct cmd_tbl *cmdtp, int flag, int argc,
396			    char *const argv[])
397{
398	int testpin = 0;
399	char *s = NULL;
400	int testboot = 0;
401	int verbose = argc > 1 && *argv[1] == 'v';
402
403#if defined(CONFIG_POST)
404	testpin = post_hotkeys_pressed();
405#endif
406
407	s = env_get("test_bank");
408	/* when test_bank is not set, act as if testpin is not asserted */
409	testboot = (testpin != 0) && (s);
410	if (verbose) {
411		printf("testpin   = %d\n", testpin);
412		/* cppcheck-suppress nullPointer */
413		printf("test_bank = %s\n", s ? s : "not set");
414		printf("boot test app : %s\n", (testboot) ? "yes" : "no");
415	}
416	/* return 0 means: testboot, therefore we need the inversion */
417	return !testboot;
418}
419
420U_BOOT_CMD(km_checktestboot, 2, 0, do_checktestboot,
421	   "check if testpin is asserted",
422	   "[v]\n  v - verbose output"
423);
424