1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
4 * (http://www.friendlyarm.com)
5 */
6
7#include <config.h>
8#include <common.h>
9#include <command.h>
10#include <fdt_support.h>
11#include <log.h>
12#ifdef CONFIG_PWM_NX
13#include <pwm.h>
14#endif
15#include <asm/global_data.h>
16#include <asm/io.h>
17
18#include <asm/arch/nexell.h>
19#include <asm/arch/nx_gpio.h>
20#include <asm/arch/display.h>
21#include <asm/arch/display_dev.h>
22
23#include <u-boot/md5.h>
24
25#include <linux/stringify.h>
26
27#include "hwrev.h"
28#include "onewire.h"
29#include "nxp-fb.h"
30
31#include <env_internal.h>	/* for env_save() */
32#include <asm/mach-types.h>
33
34DECLARE_GLOBAL_DATA_PTR;
35
36enum gpio_group {
37	gpio_a, gpio_b, gpio_c, gpio_d, gpio_e,
38};
39
40#ifdef CONFIG_PWM_NX
41struct pwm_device {
42	int grp;
43	int bit;
44	int io_fn;
45};
46
47static inline void bd_pwm_config_gpio(int ch)
48{
49	struct pwm_device pwm_dev[] = {
50		[0] = { .grp = gpio_d, .bit = 1,  .io_fn = 0 },
51		[1] = { .grp = gpio_c, .bit = 13, .io_fn = 1 },
52		[2] = { .grp = gpio_c, .bit = 14, .io_fn = 1 },
53		[3] = { .grp = gpio_d, .bit = 0,  .io_fn = 0 },
54	};
55
56	int gp = pwm_dev[ch].grp;
57	int io = pwm_dev[ch].bit;
58
59	/* pwm backlight OFF: HIGH, ON: LOW */
60	nx_gpio_set_pad_function(gp, io, pwm_dev[ch].io_fn);
61	nx_gpio_set_output_value(gp, io, 1);
62	nx_gpio_set_output_enable(gp, io, 1);
63}
64#endif
65
66static void bd_backlight_off(void)
67{
68#ifdef CONFIG_S5P4418_ONEWIRE
69	onewire_set_backlight(0);
70
71#elif defined(BACKLIGHT_CH)
72	bd_pwm_config_gpio(BACKLIGHT_CH);
73#endif
74}
75
76static void bd_backlight_on(void)
77{
78#ifdef CONFIG_S5P4418_ONEWIRE
79	onewire_set_backlight(127);
80
81#elif defined(BACKLIGHT_CH)
82	/* pwm backlight ON: HIGH, ON: LOW */
83	s5p_pwm_init(BACKLIGHT_CH,
84		 BACKLIGHT_DIV, BACKLIGHT_INV);
85	s5p_pwm_config(BACKLIGHT_CH,
86		   TO_DUTY_NS(BACKLIGHT_DUTY, BACKLIGHT_HZ),
87		   TO_PERIOD_NS(BACKLIGHT_HZ));
88#endif
89}
90
91static void bd_lcd_config_gpio(void)
92{
93	int i;
94
95	for (i = 0; i < 28; i++) {
96		nx_gpio_set_pad_function(gpio_a, i, 1);
97		nx_gpio_set_drive_strength(gpio_a, i, 0);
98		nx_gpio_set_pull_mode(gpio_a, i, 2);
99	}
100
101	nx_gpio_set_drive_strength(gpio_a, 0, 1);
102}
103
104/* DEFAULT mmc dev for eMMC boot (dwmmc.2) */
105static int mmc_boot_dev;
106
107int board_mmc_bootdev(void)
108{
109	return mmc_boot_dev;
110}
111
112/* call from common/env_mmc.c */
113int mmc_get_env_dev(void)
114{
115	return mmc_boot_dev;
116}
117
118#ifdef CONFIG_DISPLAY_BOARDINFO
119int checkboard(void)
120{
121	printf("Board: %s\n", get_board_name());
122
123	return 0;
124}
125#endif
126
127int nx_display_fixup_dp(struct nx_display_dev *dp)
128{
129	struct nxp_lcd *lcd = bd_get_lcd();
130	enum lcd_format fmt = bd_get_lcd_format();
131	struct nxp_lcd_timing *timing = &lcd->timing;
132	struct dp_sync_info *sync = &dp->sync;
133	struct dp_plane_info *plane = &dp->planes[0];
134	int i;
135	u32 clk = 800000000;
136	u32 div;
137
138	sync->h_active_len = lcd->width;
139	sync->h_sync_width = timing->h_sw;
140	sync->h_back_porch = timing->h_bp;
141	sync->h_front_porch = timing->h_fp;
142	sync->h_sync_invert = !lcd->polarity.inv_hsync;
143
144	sync->v_active_len = lcd->height;
145	sync->v_sync_width = timing->v_sw;
146	sync->v_back_porch = timing->v_bp;
147	sync->v_front_porch = timing->v_fp;
148	sync->v_sync_invert = !lcd->polarity.inv_vsync;
149
150	/* calculates pixel clock */
151	div  = timing->h_sw + timing->h_bp + timing->h_fp + lcd->width;
152	div *= timing->v_sw + timing->v_bp + timing->v_fp + lcd->height;
153	div *= lcd->freq ? : 60;
154	clk /= div;
155
156	dp->ctrl.clk_div_lv0 = clk;
157	dp->ctrl.clk_inv_lv0 = lcd->polarity.rise_vclk;
158
159	dp->top.screen_width = lcd->width;
160	dp->top.screen_height = lcd->height;
161
162	for (i = 0; i < dp->top.plane_num; i++, plane++) {
163		if (plane->enable) {
164			plane->width = lcd->width;
165			plane->height = lcd->height;
166		}
167	}
168
169	/* initialize display device type */
170	if (fmt == LCD_RGB) {
171		dp->dev_type = DP_DEVICE_RGBLCD;
172
173	} else if (fmt == LCD_HDMI) {
174		struct dp_hdmi_dev *dev = (struct dp_hdmi_dev *)dp->device;
175
176		dp->dev_type = DP_DEVICE_HDMI;
177		if (lcd->width == 1920 && lcd->height == 1080)
178			dev->preset = 1;
179		else
180			dev->preset = 0;
181
182	} else {
183		struct dp_lvds_dev *dev = (struct dp_lvds_dev *)dp->device;
184
185		dp->dev_type = DP_DEVICE_LVDS;
186		dev->lvds_format = (fmt & 0x3);
187	}
188
189	return 0;
190}
191
192/* --------------------------------------------------------------------------
193 * initialize board status.
194 */
195
196#define	MMC_BOOT_CH0		(0)
197#define	MMC_BOOT_CH1		(1 <<  3)
198#define	MMC_BOOT_CH2		(1 << 19)
199
200static void bd_bootdev_init(void)
201{
202	unsigned int rst = readl(PHY_BASEADDR_CLKPWR + SYSRSTCONFIG);
203
204	rst &= (1 << 19) | (1 << 3);
205	if (rst == MMC_BOOT_CH0) {
206		/* mmc dev 1 for SD boot */
207		mmc_boot_dev = 1;
208	}
209}
210
211#ifdef CONFIG_S5P4418_ONEWIRE
212static void bd_onewire_init(void)
213{
214	unsigned char lcd;
215	unsigned short fw_ver;
216
217	onewire_init();
218	onewire_get_info(&lcd, &fw_ver);
219}
220#endif
221
222static void bd_lcd_init(void)
223{
224	struct nxp_lcd *cfg;
225	int id = -1;
226	int ret;
227
228#ifdef CONFIG_S5P4418_ONEWIRE
229	id = onewire_get_lcd_id();
230	/* -1: onwire probe failed
231	 *  0: bad
232	 * >0: identified
233	 */
234#endif
235	ret = bd_setup_lcd_by_id(id);
236	if (id <= 0 || ret != id) {
237		printf("Panel: N/A (%d)\n", id);
238		bd_setup_lcd_by_name("HDMI720P60");
239
240	} else {
241		printf("Panel: %s\n", bd_get_lcd_name());
242
243		cfg = bd_get_lcd();
244		if (cfg->gpio_init)
245			cfg->gpio_init();
246	}
247}
248
249static int mac_read_from_generic_eeprom(u8 *addr)
250{
251	return -1;
252}
253
254static void make_ether_addr(u8 *addr)
255{
256	u32 hash[20];
257
258#define ETHER_MAC_TAG  "ethmac"
259	memset(hash, 0, sizeof(hash));
260	memcpy(hash + 12, ETHER_MAC_TAG, sizeof(ETHER_MAC_TAG));
261
262	hash[4] = readl(PHY_BASEADDR_ECID + 0x00);
263	hash[5] = readl(PHY_BASEADDR_ECID + 0x04);
264	hash[6] = readl(PHY_BASEADDR_ECID + 0x08);
265	hash[7] = readl(PHY_BASEADDR_ECID + 0x0c);
266
267	md5((unsigned char *)&hash[4], 64, (unsigned char *)hash);
268
269	hash[0] ^= hash[2];
270	hash[1] ^= hash[3];
271
272	memcpy(addr, (char *)hash, 6);
273	addr[0] &= 0xfe;	/* clear multicast bit */
274	addr[0] |= 0x02;
275}
276
277static void set_ether_addr(void)
278{
279	unsigned char mac[6];
280	char ethaddr[20];
281	int ret;
282
283	if (env_get("ethaddr"))
284		return;
285
286	ret = mac_read_from_generic_eeprom(mac);
287	if (ret < 0)
288		make_ether_addr(mac);
289
290	sprintf(ethaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
291		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
292	if (!ret)
293		printf("MAC:  [%s]\n", ethaddr);
294
295	env_set("ethaddr", ethaddr);
296}
297
298#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
299static void set_board_rev(void)
300{
301	char info[64] = {0, };
302
303	snprintf(info, ARRAY_SIZE(info), "%02x", get_board_revision());
304	env_set("board_rev", info);
305}
306#endif
307
308static void set_dtb_name(void)
309{
310	char info[64] = {0, };
311
312	snprintf(info, ARRAY_SIZE(info),
313		 "s5p4418-nanopi2-rev%02x.dtb", get_board_revision());
314	env_set("dtb_name", info);
315}
316
317static void bd_update_env(void)
318{
319	char *lcdtype = env_get("lcdtype");
320	char *lcddpi = env_get("lcddpi");
321	char *bootargs = env_get("bootargs");
322	const char *name;
323	char *p = NULL;
324	int rootdev = board_mmc_bootdev();
325	int need_save = 0;
326
327#define CMDLINE_LCD		" lcd="
328	char cmdline[CONFIG_SYS_CBSIZE];
329	int n = 1;
330
331	if (rootdev != CONFIG_ROOT_DEV && !env_get("firstboot")) {
332		env_set_ulong("rootdev", rootdev);
333		env_set("firstboot", "0");
334		need_save = 1;
335	}
336
337	if (lcdtype) {
338		/* Setup again as user specified LCD in env */
339		bd_setup_lcd_by_name(lcdtype);
340	}
341
342	name = bd_get_lcd_name();
343
344	if (bootargs)
345		n = strlen(bootargs);	/* isn't 0 for NULL */
346	else
347		cmdline[0] = '\0';
348
349	if ((n + strlen(name) + sizeof(CMDLINE_LCD)) > sizeof(cmdline)) {
350		printf("Error: `bootargs' is too large (%d)\n", n);
351		goto __exit;
352	}
353
354	if (bootargs) {
355		p = strstr(bootargs, CMDLINE_LCD);
356		if (p) {
357			n = (p - bootargs);
358			p += strlen(CMDLINE_LCD);
359		}
360		strncpy(cmdline, bootargs, n);
361	}
362
363	/* add `lcd=NAME,NUMdpi' */
364	strncpy(cmdline + n, CMDLINE_LCD, strlen(CMDLINE_LCD));
365	n += strlen(CMDLINE_LCD);
366
367	strcpy(cmdline + n, name);
368	n += strlen(name);
369
370	if (lcddpi) {
371		n += sprintf(cmdline + n, ",%sdpi", lcddpi);
372	} else {
373		int dpi = bd_get_lcd_density();
374
375		if (dpi > 0 && dpi < 600)
376			n += sprintf(cmdline + n, ",%ddpi", dpi);
377	}
378
379	/* copy remaining of bootargs */
380	if (p) {
381		p = strstr(p, " ");
382		if (p) {
383			strcpy(cmdline + n, p);
384			n += strlen(p);
385		}
386	}
387
388	/* append `bootdev=2' */
389#define CMDLINE_BDEV	" bootdev="
390	if (rootdev > 0 && !strstr(cmdline, CMDLINE_BDEV))
391		n += sprintf(cmdline + n, "%s2", CMDLINE_BDEV);
392
393	/* finally, let's update uboot env & save it */
394	if (bootargs && strncmp(cmdline, bootargs, sizeof(cmdline))) {
395		env_set("bootargs", cmdline);
396		need_save = 1;
397	}
398
399__exit:
400	if (need_save)
401		env_save();
402}
403
404/* --------------------------------------------------------------------------
405 * call from u-boot
406 */
407
408int board_early_init_f(void)
409{
410	return 0;
411}
412
413int board_init(void)
414{
415	bd_hwrev_init();
416	bd_base_rev_init();
417
418	bd_bootdev_init();
419#ifdef CONFIG_S5P4418_ONEWIRE
420	bd_onewire_init();
421#endif
422
423	bd_backlight_off();
424
425	bd_lcd_config_gpio();
426	bd_lcd_init();
427
428	if (IS_ENABLED(CONFIG_SILENT_CONSOLE))
429		gd->flags |= GD_FLG_SILENT;
430
431	return 0;
432}
433
434#ifdef CONFIG_BOARD_LATE_INIT
435int board_late_init(void)
436{
437	bd_update_env();
438
439#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
440	set_board_rev();
441#endif
442	set_dtb_name();
443
444	set_ether_addr();
445
446	if (IS_ENABLED(CONFIG_SILENT_CONSOLE))
447		gd->flags &= ~GD_FLG_SILENT;
448
449	bd_backlight_on();
450	printf("\n");
451
452	return 0;
453}
454#endif
455
456#ifdef CONFIG_SPLASH_SOURCE
457#include <splash.h>
458static struct splash_location splash_locations[] = {
459	{
460	.name = "mmc_fs",
461	.storage = SPLASH_STORAGE_MMC,
462	.flags = SPLASH_STORAGE_FS,
463	.devpart = __stringify(CONFIG_ROOT_DEV) ":"
464		   __stringify(CONFIG_BOOT_PART),
465	},
466};
467
468int splash_screen_prepare(void)
469{
470	int err;
471	char *env_cmd = env_get("load_splash");
472
473	debug("%s()\n", __func__);
474
475	if (env_cmd) {
476		err = run_command(env_cmd, 0);
477
478	} else {
479		char devpart[64] = { 0, };
480		int bootpart = env_get_ulong("bootpart", 0, CONFIG_BOOT_PART);
481		int rootdev;
482
483		if (env_get("firstboot"))
484			rootdev = env_get_ulong("rootdev", 0, CONFIG_ROOT_DEV);
485		else
486			rootdev = board_mmc_bootdev();
487
488		snprintf(devpart, ARRAY_SIZE(devpart), "%d:%d", rootdev,
489			 bootpart);
490		splash_locations[0].devpart = devpart;
491
492		err = splash_source_load(splash_locations,
493					 ARRAY_SIZE(splash_locations));
494	}
495
496	if (!err) {
497		char addr[64];
498
499		sprintf(addr, "0x%lx", gd->fb_base);
500		env_set("fb_addr", addr);
501	}
502
503	return err;
504}
505#endif
506
507/* u-boot dram initialize */
508int dram_init(void)
509{
510	gd->ram_size = CFG_SYS_SDRAM_SIZE;
511	return 0;
512}
513
514/* u-boot dram board specific */
515int dram_init_banksize(void)
516{
517#define SCR_USER_SIG6_READ		(SCR_ALIVE_BASE + 0x0F0)
518	unsigned int reg_val = readl(SCR_USER_SIG6_READ);
519
520	/* set global data memory */
521	gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x00000100;
522
523	gd->bd->bi_dram[0].start = CFG_SYS_SDRAM_BASE;
524	gd->bd->bi_dram[0].size  = CFG_SYS_SDRAM_SIZE;
525
526	/* Number of Row: 14 bits */
527	if ((reg_val >> 28) == 14)
528		gd->bd->bi_dram[0].size -= 0x20000000;
529
530	/* Number of Memory Chips */
531	if ((reg_val & 0x3) > 1) {
532		gd->bd->bi_dram[1].start = 0x80000000;
533		gd->bd->bi_dram[1].size  = 0x40000000;
534	}
535	return 0;
536}
537
538#if defined(CONFIG_OF_BOARD_SETUP)
539int ft_board_setup(void *blob, struct bd_info *bd)
540{
541	int nodeoff;
542	unsigned int rootdev;
543	unsigned int fb_addr;
544
545	if (board_mmc_bootdev() > 0) {
546		rootdev = fdt_getprop_u32_default(blob, "/board", "sdidx", 2);
547		if (rootdev) {
548			/* find or create "/chosen" node. */
549			nodeoff = fdt_find_or_add_subnode(blob, 0, "chosen");
550			if (nodeoff >= 0)
551				fdt_setprop_u32(blob, nodeoff, "linux,rootdev",
552						rootdev);
553		}
554	}
555
556	fb_addr = env_get_ulong("fb_addr", 0, 0);
557	if (fb_addr) {
558		nodeoff = fdt_path_offset(blob, "/reserved-memory");
559		if (nodeoff < 0)
560			return nodeoff;
561
562		nodeoff = fdt_add_subnode(blob, nodeoff, "display_reserved");
563		if (nodeoff >= 0) {
564			fdt32_t cells[2];
565
566			cells[0] = cpu_to_fdt32(fb_addr);
567			cells[1] = cpu_to_fdt32(0x800000);
568
569			fdt_setprop(blob, nodeoff, "reg", cells,
570				    sizeof(cells[0]) * 2);
571		}
572	}
573
574	return 0;
575}
576#endif
577