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