]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - board/karo/txa5/txa5.c
karo: add support for TXA5 (SAMA5D4) modules
[karo-tx-uboot.git] / board / karo / txa5 / txa5.c
diff --git a/board/karo/txa5/txa5.c b/board/karo/txa5/txa5.c
new file mode 100644 (file)
index 0000000..ce40c1c
--- /dev/null
@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2015 Lothar Waßmann <LW@KARO-electronics.de>
+ * based on: board/atmel/sama5d4_xplained/sama5d4_xplained.c
+ *     Copyright (C) 2014 Atmel Bo Shen <voice.shen@atmel.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#define DEBUG
+
+#include <common.h>
+#include <libfdt.h>
+#include <fdt_support.h>
+#include <lcd.h>
+#include <netdev.h>
+#include <mmc.h>
+#include <net.h>
+#include <nand.h>
+#include <atmel_hlcdc.h>
+#include <atmel_mci.h>
+#include <linux/fb.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/arch/at91_common.h>
+#include <asm/arch/at91_pmc.h>
+#include <asm/arch/at91_rstc.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/sama5d3_smc.h>
+#include <asm/arch/sama5d4.h>
+
+#include "../common/karo.h"
+
+#define TXA5_LCD_RST_GPIO      GPIO_PIN_PA(25)
+#define TXA5_LCD_PWR_GPIO      GPIO_PIN_PB(15)
+#define TXA5_LCD_BACKLIGHT_GPIO        GPIO_PIN_PA(24)
+
+#define TXA5_ETH_RST_GPIO      GPIO_PIN_PD(30)
+//#define TXA5_ETH_PWR_GPIO    GPIO_PIN_BU(7)
+#define TXA5_ETH_INT_GPIO      GPIO_PIN_PE(1)
+
+#define TXA5_LED_GPIO          GPIO_PIN_PD(29)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int wdreset;
+
+#ifdef CONFIG_NAND_ATMEL
+static void txa5_nand_hw_init(void)
+{
+       struct at91_smc *smc = (void *)ATMEL_BASE_SMC;
+
+       at91_periph_clk_enable(ATMEL_ID_SMC);
+
+       /* Configure SMC CS3 for NAND */
+       writel(AT91_SMC_SETUP_NWE(1) | AT91_SMC_SETUP_NCS_WR(1) |
+              AT91_SMC_SETUP_NRD(1) | AT91_SMC_SETUP_NCS_RD(1),
+              &smc->cs[3].setup);
+       writel(AT91_SMC_PULSE_NWE(2) | AT91_SMC_PULSE_NCS_WR(3) |
+              AT91_SMC_PULSE_NRD(2) | AT91_SMC_PULSE_NCS_RD(3),
+              &smc->cs[3].pulse);
+       writel(AT91_SMC_CYCLE_NWE(5) | AT91_SMC_CYCLE_NRD(5),
+              &smc->cs[3].cycle);
+       writel(AT91_SMC_TIMINGS_TCLR(2) | AT91_SMC_TIMINGS_TADL(7) |
+              AT91_SMC_TIMINGS_TAR(2)  | AT91_SMC_TIMINGS_TRR(3)   |
+              AT91_SMC_TIMINGS_TWB(7)  | AT91_SMC_TIMINGS_RBNSEL(3)|
+              AT91_SMC_TIMINGS_NFSEL(1), &smc->cs[3].timings);
+       writel(AT91_SMC_MODE_RM_NRD | AT91_SMC_MODE_WM_NWE |
+              AT91_SMC_MODE_EXNW_DISABLE |
+              AT91_SMC_MODE_DBW_8 |
+              AT91_SMC_MODE_TDF_CYCLE(3),
+              &smc->cs[3].mode);
+
+       at91_set_a_periph(AT91_PIO_PORTC, 5, 0);        /* D0 */
+       at91_set_a_periph(AT91_PIO_PORTC, 6, 0);        /* D1 */
+       at91_set_a_periph(AT91_PIO_PORTC, 7, 0);        /* D2 */
+       at91_set_a_periph(AT91_PIO_PORTC, 8, 0);        /* D3 */
+       at91_set_a_periph(AT91_PIO_PORTC, 9, 0);        /* D4 */
+       at91_set_a_periph(AT91_PIO_PORTC, 10, 0);       /* D5 */
+       at91_set_a_periph(AT91_PIO_PORTC, 11, 0);       /* D6 */
+       at91_set_a_periph(AT91_PIO_PORTC, 12, 0);       /* D7 */
+       at91_set_a_periph(AT91_PIO_PORTC, 13, 0);       /* RE */
+       at91_set_a_periph(AT91_PIO_PORTC, 14, 0);       /* WE */
+       at91_set_a_periph(AT91_PIO_PORTC, 15, 1);       /* NCS */
+       at91_set_a_periph(AT91_PIO_PORTC, 16, 1);       /* RDY */
+       at91_set_a_periph(AT91_PIO_PORTC, 17, 1);       /* ALE */
+       at91_set_a_periph(AT91_PIO_PORTC, 18, 1);       /* CLE */
+}
+#endif
+
+static inline void txa5_serial0_hw_init(void)
+{
+       at91_set_a_periph(AT91_PIO_PORTD, 13, 1);       /* TXD0 */
+       at91_set_a_periph(AT91_PIO_PORTD, 12, 0);       /* RXD0 */
+       at91_set_a_periph(AT91_PIO_PORTD, 10, 1);       /* CTS0 */
+       at91_set_a_periph(AT91_PIO_PORTD, 11, 0);       /* RTS0 */
+
+       /* Enable clock */
+       at91_periph_clk_enable(ATMEL_ID_USART0);
+}
+
+static inline void txa5_serial1_hw_init(void)
+{
+       at91_set_a_periph(AT91_PIO_PORTD, 17, 1);       /* TXD1 */
+       at91_set_a_periph(AT91_PIO_PORTD, 16, 0);       /* RXD1 */
+       at91_set_a_periph(AT91_PIO_PORTD, 14, 1);       /* CTS1 */
+       at91_set_a_periph(AT91_PIO_PORTD, 15, 0);       /* RTS1 */
+
+       /* Enable clock */
+       at91_periph_clk_enable(ATMEL_ID_USART1);
+}
+
+static inline void txa5_serial3_hw_init(void)
+{
+       at91_set_b_periph(AT91_PIO_PORTE, 17, 1);       /* TXD3 */
+       at91_set_b_periph(AT91_PIO_PORTE, 16, 0);       /* RXD3 */
+       at91_set_b_periph(AT91_PIO_PORTE, 5, 1);        /* CTS3 */
+       at91_set_b_periph(AT91_PIO_PORTE, 24, 0);       /* RTS3 */
+
+       /* Enable clock */
+       at91_periph_clk_enable(ATMEL_ID_USART3);
+}
+
+#ifdef CONFIG_GENERIC_ATMEL_MCI
+void txa5_mci1_hw_init(void)
+{
+       at91_set_c_periph(AT91_PIO_PORTE, 19, 1);       /* MCI1 CDA */
+       at91_set_c_periph(AT91_PIO_PORTE, 20, 1);       /* MCI1 DA0 */
+       at91_set_c_periph(AT91_PIO_PORTE, 21, 1);       /* MCI1 DA1 */
+       at91_set_c_periph(AT91_PIO_PORTE, 22, 1);       /* MCI1 DA2 */
+       at91_set_c_periph(AT91_PIO_PORTE, 23, 1);       /* MCI1 DA3 */
+       at91_set_c_periph(AT91_PIO_PORTE, 18, 0);       /* MCI1 CLK */
+
+       /*
+        * As the mci io internal pull down is too strong, so if the io needs
+        * external pull up, the pull up resistor will be very small, if so
+        * the power consumption will increase, so disable the interanl pull
+        * down to save the power.
+        */
+       at91_set_pio_pulldown(AT91_PIO_PORTE, 18, 0);
+       at91_set_pio_pulldown(AT91_PIO_PORTE, 19, 0);
+       at91_set_pio_pulldown(AT91_PIO_PORTE, 20, 0);
+       at91_set_pio_pulldown(AT91_PIO_PORTE, 21, 0);
+       at91_set_pio_pulldown(AT91_PIO_PORTE, 22, 0);
+       at91_set_pio_pulldown(AT91_PIO_PORTE, 23, 0);
+
+       /* SD Card Detect */
+       at91_set_pio_input(AT91_PIO_PORTE, 6, 1);
+       at91_set_pio_deglitch(AT91_PIO_PORTE, 6, 1);
+
+       /* Enable clock */
+       at91_periph_clk_enable(ATMEL_ID_MCI1);
+}
+#endif
+
+#ifdef CONFIG_MACB
+void txa5_macb0_hw_init(void)
+{
+       at91_set_a_periph(AT91_PIO_PORTB, 0, 0);        /* ETXCK_EREFCK */
+       at91_set_a_periph(AT91_PIO_PORTB, 6, 0);        /* ERXDV */
+       at91_set_a_periph(AT91_PIO_PORTB, 8, 0);        /* ERX0 */
+       at91_set_a_periph(AT91_PIO_PORTB, 9, 0);        /* ERX1 */
+       at91_set_a_periph(AT91_PIO_PORTB, 7, 0);        /* ERXER */
+       at91_set_a_periph(AT91_PIO_PORTB, 2, 0);        /* ETXEN */
+       at91_set_a_periph(AT91_PIO_PORTB, 12, 0);       /* ETX0 */
+       at91_set_a_periph(AT91_PIO_PORTB, 13, 0);       /* ETX1 */
+       at91_set_a_periph(AT91_PIO_PORTB, 17, 0);       /* EMDIO */
+       at91_set_a_periph(AT91_PIO_PORTB, 16, 0);       /* EMDC */
+
+       at91_set_pio_input(AT91_PIO_PORTE, 1, 0);       /* IRQ */
+       at91_set_pio_deglitch(AT91_PIO_PORTE, 1, 1);
+
+       at91_set_pio_output(AT91_PIO_PORTD, 30, 0);     /* PHY RESET */
+       at91_set_pio_pullup(AT91_PIO_PORTD, 30, 1);
+
+       /* Enable clock */
+       at91_periph_clk_enable(ATMEL_ID_GMAC0);
+}
+#endif
+
+int board_early_init_f(void)
+{
+       at91_periph_clk_enable(ATMEL_ID_PIOA);
+       at91_periph_clk_enable(ATMEL_ID_PIOB);
+       at91_periph_clk_enable(ATMEL_ID_PIOC);
+       at91_periph_clk_enable(ATMEL_ID_PIOD);
+       at91_periph_clk_enable(ATMEL_ID_PIOE);
+
+#ifdef CONFIG_USART_ID
+#if CONFIG_USART_ID == ATMEL_ID_USART0
+       txa5_serial0_hw_init();
+#elif CONFIG_USART_ID == ATMEL_ID_USART1
+       txa5_serial1_hw_init();
+#elif CONFIG_USART_ID == ATMEL_ID_USART3
+#else
+#error No console UART defined
+#endif
+       txa5_serial3_hw_init();
+#endif /* CONFIG_USART_ID */
+
+       return 0;
+}
+
+/* called with default environment! */
+int board_init(void)
+{
+       /* adress of boot parameters */
+       gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
+
+#ifdef CONFIG_NAND_ATMEL
+       txa5_nand_hw_init();
+#endif
+#ifdef CONFIG_GENERIC_ATMEL_MCI
+       txa5_mci1_hw_init();
+#endif
+       return 0;
+}
+
+int dram_init(void)
+{
+       gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE,
+                               CONFIG_SYS_SDRAM_SIZE);
+       return 0;
+}
+
+#ifdef CONFIG_GENERIC_ATMEL_MCI
+int board_mmc_init(bd_t *bis)
+{
+       return atmel_mci_init((void *)ATMEL_BASE_MCI1);
+}
+#endif /* CONFIG_GENERIC_ATMEL_MCI */
+
+#ifdef CONFIG_MACB
+int board_eth_init(bd_t *bis)
+{
+       int ret;
+
+       txa5_macb0_hw_init();
+
+       /* delay at least 21ms for the PHY internal POR signal to deassert */
+       udelay(22000);
+
+       /* Deassert RESET to the external phy */
+       at91_set_gpio_value(TXA5_ETH_RST_GPIO, 1);
+
+       ret = macb_eth_initialize(0, (void *)ATMEL_BASE_GMAC0, 0x00);
+
+       return ret;
+}
+#endif
+
+#ifndef ETH_ALEN
+#define ETH_ALEN      6
+#endif
+#define MAC_ID0_HI     (void *)0xfc060040
+#define MAC_ID0_LO     (void *)0xfc060044
+
+static void txa5_init_mac(void)
+{
+       uint8_t mac_addr[ETH_ALEN];
+       uint32_t mac_hi, mac_lo;
+
+       /* try reading mac address from efuse */
+       mac_lo = __raw_readl(MAC_ID0_LO);
+       mac_hi = __raw_readl(MAC_ID0_HI);
+
+       mac_addr[0] = mac_hi & 0xFF;
+       mac_addr[1] = (mac_hi >> 8) & 0xFF;
+       mac_addr[2] = (mac_hi >> 16) & 0xFF;
+       mac_addr[3] = (mac_hi >> 24) & 0xFF;
+       mac_addr[4] = mac_lo & 0xFF;
+       mac_addr[5] = (mac_lo >> 8) & 0xFF;
+
+       if (!is_valid_ether_addr(mac_addr)) {
+               printf("No valid MAC address programmed\n");
+               return;
+       }
+       printf("MAC addr from fuse: %pM\n", mac_addr);
+       eth_setenv_enetaddr("ethaddr", mac_addr);
+}
+
+enum {
+       LED_STATE_INIT = -1,
+       LED_STATE_OFF,
+       LED_STATE_ON,
+};
+
+void show_activity(int arg)
+{
+       static int led_state = LED_STATE_INIT;
+       static ulong last;
+
+       if (led_state == LED_STATE_INIT) {
+               last = get_timer(0);
+               at91_set_gpio_value(TXA5_LED_GPIO, 1);
+               led_state = LED_STATE_ON;
+       } else {
+               if (get_timer(last) > CONFIG_SYS_HZ) {
+                       last = get_timer(0);
+                       if (led_state == LED_STATE_ON) {
+                               at91_set_gpio_value(TXA5_LED_GPIO, 0);
+                       } else {
+                               at91_set_gpio_value(TXA5_LED_GPIO, 1);
+                       }
+                       led_state = 1 - led_state;
+               }
+       }
+}
+
+#ifdef CONFIG_LCD
+vidinfo_t panel_info = {
+       /* set to max. size supported by SoC */
+       .vl_col = 2048,
+       .vl_row = 2048,
+       .vl_bpix = LCD_COLOR16,
+       .vl_tft = 1,
+       .mmio = (void *)ATMEL_BASE_LCDC,
+};
+
+#define FB_SYNC_OE_LOW_ACT     (1 << 31)
+#define FB_SYNC_CLK_LAT_FALL   (1 << 30)
+
+static struct fb_videomode txa5_fb_modes[] = {
+       {
+               /* Standard VGA timing */
+               .name           = "VGA",
+               .refresh        = 60,
+               .xres           = 640,
+               .yres           = 480,
+               .pixclock       = KHZ2PICOS(25175),
+               .left_margin    = 48,
+               .hsync_len      = 96,
+               .right_margin   = 16,
+               .upper_margin   = 31,
+               .vsync_len      = 2,
+               .lower_margin   = 12,
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+       {
+               /* Emerging ETV570 640 x 480 display. Syncs low active,
+                * DE high active, 115.2 mm x 86.4 mm display area
+                * VGA compatible timing
+                */
+               .name           = "ETV570",
+               .refresh        = 60,
+               .xres           = 640,
+               .yres           = 480,
+               .pixclock       = KHZ2PICOS(25175),
+               .left_margin    = 114,
+               .hsync_len      = 30,
+               .right_margin   = 16,
+               .upper_margin   = 32,
+               .vsync_len      = 3,
+               .lower_margin   = 10,
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+       {
+               /* Emerging ET0350G0DH6 320 x 240 display.
+                * 70.08 mm x 52.56 mm display area.
+                */
+               .name           = "ET0350",
+               .refresh        = 60,
+               .xres           = 320,
+               .yres           = 240,
+               .pixclock       = KHZ2PICOS(6500),
+               .left_margin    = 68 - 34,
+               .hsync_len      = 34,
+               .right_margin   = 20,
+               .upper_margin   = 18 - 3,
+               .vsync_len      = 3,
+               .lower_margin   = 4,
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+       {
+               /* Emerging ET0430G0DH6 480 x 272 display.
+                * 95.04 mm x 53.856 mm display area.
+                */
+               .name           = "ET0430",
+               .refresh        = 60,
+               .xres           = 480,
+               .yres           = 272,
+               .pixclock       = KHZ2PICOS(9000),
+               .left_margin    = 2,
+               .hsync_len      = 41,
+               .right_margin   = 2,
+               .upper_margin   = 2,
+               .vsync_len      = 10,
+               .lower_margin   = 2,
+       },
+       {
+               /* Emerging ET0500G0DH6 800 x 480 display.
+                * 109.6 mm x 66.4 mm display area.
+                */
+               .name           = "ET0500",
+               .refresh        = 60,
+               .xres           = 800,
+               .yres           = 480,
+               .pixclock       = KHZ2PICOS(33260),
+               .left_margin    = 216 - 128,
+               .hsync_len      = 128,
+               .right_margin   = 1056 - 800 - 216,
+               .upper_margin   = 35 - 2,
+               .vsync_len      = 2,
+               .lower_margin   = 525 - 480 - 35,
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+       {
+               /* Emerging ETQ570G0DH6 320 x 240 display.
+                * 115.2 mm x 86.4 mm display area.
+                */
+               .name           = "ETQ570",
+               .refresh        = 60,
+               .xres           = 320,
+               .yres           = 240,
+               .pixclock       = KHZ2PICOS(6400),
+               .left_margin    = 38,
+               .hsync_len      = 30,
+               .right_margin   = 30,
+               .upper_margin   = 16, /* 15 according to datasheet */
+               .vsync_len      = 3, /* TVP -> 1>x>5 */
+               .lower_margin   = 4, /* 4.5 according to datasheet */
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+       {
+               /* Emerging ET0700G0DH6 800 x 480 display.
+                * 152.4 mm x 91.44 mm display area.
+                */
+               .name           = "ET0700",
+               .refresh        = 60,
+               .xres           = 800,
+               .yres           = 480,
+               .pixclock       = KHZ2PICOS(33260),
+               .left_margin    = 216 - 128,
+               .hsync_len      = 128,
+               .right_margin   = 1056 - 800 - 216,
+               .upper_margin   = 35 - 2,
+               .vsync_len      = 2,
+               .lower_margin   = 525 - 480 - 35,
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+       {
+               /* unnamed entry for assigning parameters parsed from 'video_mode' string */
+               .refresh        = 60,
+               .left_margin    = 48,
+               .hsync_len      = 96,
+               .right_margin   = 16,
+               .upper_margin   = 31,
+               .vsync_len      = 2,
+               .lower_margin   = 12,
+               .sync           = FB_SYNC_CLK_LAT_FALL,
+       },
+};
+
+static int lcd_enabled = 1;
+static int lcd_bl_polarity;
+int lcd_output_bpp;
+
+static int lcd_backlight_polarity(void)
+{
+       return lcd_bl_polarity;
+}
+
+void lcd_enable(void)
+{
+       /* HACK ALERT:
+        * global variable from common/lcd.c
+        * Set to 0 here to prevent messages from going to LCD
+        * rather than serial console
+        */
+       lcd_is_enabled = 0;
+
+       if (lcd_enabled) {
+               karo_load_splashimage(1);
+
+               debug("Switching LCD on\n");
+               at91_set_gpio_value(TXA5_LCD_PWR_GPIO, 1);
+               udelay(100);
+               at91_set_gpio_value(TXA5_LCD_RST_GPIO, 1);
+               udelay(300000);
+               at91_set_gpio_value(TXA5_LCD_BACKLIGHT_GPIO,
+                               lcd_backlight_polarity());
+       }
+}
+
+void lcd_disable(void)
+{
+}
+
+void lcd_panel_disable(void)
+{
+       if (lcd_enabled) {
+               debug("Switching LCD off\n");
+               at91_set_gpio_value(TXA5_LCD_BACKLIGHT_GPIO,
+                               !lcd_backlight_polarity());
+               at91_set_gpio_value(TXA5_LCD_PWR_GPIO, 0);
+               at91_set_gpio_value(TXA5_LCD_RST_GPIO, 0);
+       }
+}
+
+static void txa5_lcd_panel_setup(struct fb_videomode *fb)
+{
+       panel_info.vl_clk = PICOS2KHZ(fb->pixclock) * 1000;
+
+       panel_info.vl_col = fb->xres;
+       panel_info.vl_left_margin = fb->left_margin;
+       panel_info.vl_hsync_len = fb->hsync_len;
+       panel_info.vl_right_margin = fb->right_margin;
+
+       panel_info.vl_row = fb->yres;
+       panel_info.vl_upper_margin = fb->upper_margin;
+       panel_info.vl_vsync_len = fb->vsync_len;
+       panel_info.vl_lower_margin = fb->lower_margin;
+
+       if (!(fb->sync & FB_SYNC_HOR_HIGH_ACT))
+               panel_info.vl_sync |= LCDC_LCDCFG5_HSPOL;
+       if (!(fb->sync & FB_SYNC_VERT_HIGH_ACT))
+               panel_info.vl_sync |= LCDC_LCDCFG5_VSPOL;
+       if (fb->sync & FB_SYNC_OE_LOW_ACT)
+               panel_info.vl_sync |= LCDC_LCDCFG5_DISPPOL;
+
+       panel_info.vl_clk_pol = !!(fb->sync & FB_SYNC_CLK_LAT_FALL);
+}
+
+static void txa5_lcd_hw_init(void)
+{
+       at91_set_pio_output(AT91_PIO_PORTA, 24, 0);     /* LCDPWM */
+       at91_set_pio_output(AT91_PIO_PORTA, 25, 0);     /* LCD RST */
+       at91_set_pio_output(AT91_PIO_PORTB, 15, 0);     /* LCD PWR */
+
+       at91_set_a_periph(AT91_PIO_PORTA, 26, 0);       /* LCDVSYNC */
+       at91_set_a_periph(AT91_PIO_PORTA, 27, 0);       /* LCDHSYNC */
+       at91_set_a_periph(AT91_PIO_PORTA, 28, 0);       /* LCDDOTCK */
+       at91_set_a_periph(AT91_PIO_PORTA, 29, 0);       /* LCDDEN */
+
+       at91_set_a_periph(AT91_PIO_PORTA,  0, 0);       /* LCDD0 */
+       at91_set_a_periph(AT91_PIO_PORTA,  1, 0);       /* LCDD1 */
+       at91_set_a_periph(AT91_PIO_PORTA,  2, 0);       /* LCDD2 */
+       at91_set_a_periph(AT91_PIO_PORTA,  3, 0);       /* LCDD3 */
+       at91_set_a_periph(AT91_PIO_PORTA,  4, 0);       /* LCDD4 */
+       at91_set_a_periph(AT91_PIO_PORTA,  5, 0);       /* LCDD5 */
+       at91_set_a_periph(AT91_PIO_PORTA,  6, 0);       /* LCDD6 */
+       at91_set_a_periph(AT91_PIO_PORTA,  7, 0);       /* LCDD7 */
+
+       at91_set_a_periph(AT91_PIO_PORTA,  8, 0);       /* LCDD9 */
+       at91_set_a_periph(AT91_PIO_PORTA,  9, 0);       /* LCDD8 */
+       at91_set_a_periph(AT91_PIO_PORTA, 10, 0);       /* LCDD10 */
+       at91_set_a_periph(AT91_PIO_PORTA, 11, 0);       /* LCDD11 */
+       at91_set_a_periph(AT91_PIO_PORTA, 12, 0);       /* LCDD12 */
+       at91_set_a_periph(AT91_PIO_PORTA, 13, 0);       /* LCDD13 */
+       at91_set_a_periph(AT91_PIO_PORTA, 14, 0);       /* LCDD14 */
+       at91_set_a_periph(AT91_PIO_PORTA, 15, 0);       /* LCDD15 */
+
+       at91_set_a_periph(AT91_PIO_PORTA, 16, 0);       /* LCDD16 */
+       at91_set_a_periph(AT91_PIO_PORTA, 17, 0);       /* LCDD17 */
+       at91_set_a_periph(AT91_PIO_PORTA, 18, 0);       /* LCDD18 */
+       at91_set_a_periph(AT91_PIO_PORTA, 19, 0);       /* LCDD19 */
+       at91_set_a_periph(AT91_PIO_PORTA, 20, 0);       /* LCDD20 */
+       at91_set_a_periph(AT91_PIO_PORTA, 21, 0);       /* LCDD21 */
+       at91_set_a_periph(AT91_PIO_PORTA, 22, 0);       /* LCDD22 */
+       at91_set_a_periph(AT91_PIO_PORTA, 23, 0);       /* LCDD23 */
+
+       /* Enable clock */
+       at91_periph_clk_enable(ATMEL_ID_LCDC);
+}
+
+#ifdef CONFIG_LCD_INFO
+void lcd_show_board_info(void)
+{
+       ulong dram_size, nand_size;
+       int i;
+       char temp[32];
+
+       lcd_printf("2015 Ka-Ro electronics GmbH\n");
+       lcd_printf("%s CPU at %s MHz\n", get_cpu_name(),
+                  strmhz(temp, get_cpu_clk_rate()));
+
+       dram_size = 0;
+       for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
+               dram_size += gd->bd->bi_dram[i].size;
+
+       nand_size = 0;
+#ifdef CONFIG_NAND_ATMEL
+       for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+               nand_size += nand_info[i].size;
+#endif
+       lcd_printf("%ld MB SDRAM, %ld MB NAND\n",
+                  dram_size >> 20, nand_size >> 20);
+}
+#endif /* CONFIG_LCD_INFO */
+
+unsigned int has_lcdc(void)
+{
+       int color_depth = 16;
+       const char *video_mode = karo_get_vmode(getenv("video_mode"));
+       const char *vm;
+       unsigned long val;
+       int refresh = 60;
+       struct fb_videomode *p = &txa5_fb_modes[0];
+       struct fb_videomode fb_mode;
+       int xres_set = 0, yres_set = 0, bpp_set = 0, refresh_set = 0;
+       int lcd_bus_width;
+
+       if (!lcd_enabled) {
+               debug("LCD disabled\n");
+               return 0;
+       }
+
+       if (had_ctrlc() || wdreset) {
+               debug("Disabling LCD\n");
+               lcd_enabled = 0;
+               setenv("splashimage", NULL);
+               return 0;
+       }
+
+       karo_fdt_move_fdt();
+
+       if (video_mode == NULL) {
+               debug("Disabling LCD\n");
+               lcd_enabled = 0;
+               return 0;
+       }
+
+       lcd_bl_polarity = karo_fdt_get_backlight_polarity(working_fdt);
+       vm = video_mode;
+       if (karo_fdt_get_fb_mode(working_fdt, video_mode, &fb_mode) == 0) {
+               p = &fb_mode;
+               debug("Using video mode from FDT\n");
+               vm += strlen(vm);
+               if (fb_mode.xres > panel_info.vl_col ||
+                       fb_mode.yres > panel_info.vl_row) {
+                       printf("video resolution from DT: %dx%d exceeds hardware limits: %dx%d\n",
+                               fb_mode.xres, fb_mode.yres,
+                               panel_info.vl_col, panel_info.vl_row);
+                       lcd_enabled = 0;
+                       return 0;
+               }
+       }
+       if (p->name != NULL)
+               debug("Trying compiled-in video modes\n");
+       while (p->name != NULL) {
+               if (strcmp(p->name, vm) == 0) {
+                       debug("Using video mode: '%s'\n", p->name);
+                       vm += strlen(vm);
+                       break;
+               }
+               p++;
+       }
+       if (*vm != '\0')
+               debug("Trying to decode video_mode: '%s'\n", vm);
+       while (*vm != '\0') {
+               if (*vm >= '0' && *vm <= '9') {
+                       char *end;
+
+                       val = simple_strtoul(vm, &end, 0);
+                       if (end > vm) {
+                               if (!xres_set) {
+                                       if (val > panel_info.vl_col)
+                                               val = panel_info.vl_col;
+                                       p->xres = val;
+                                       panel_info.vl_col = val;
+                                       xres_set = 1;
+                               } else if (!yres_set) {
+                                       if (val > panel_info.vl_row)
+                                               val = panel_info.vl_row;
+                                       p->yres = val;
+                                       panel_info.vl_row = val;
+                                       yres_set = 1;
+                               } else if (!bpp_set) {
+                                       switch (val) {
+                                       case 16:
+                                               color_depth = val;
+                                               break;
+
+                                       default:
+                                               printf("Invalid color depth: '%.*s' in video_mode; using default: '%u'\n",
+                                                       end - vm, vm, color_depth);
+                                       }
+                                       bpp_set = 1;
+                               } else if (!refresh_set) {
+                                       refresh = val;
+                                       refresh_set = 1;
+                               }
+                       }
+                       vm = end;
+               }
+               switch (*vm) {
+               case '@':
+                       bpp_set = 1;
+                       /* fallthru */
+               case '-':
+                       yres_set = 1;
+                       /* fallthru */
+               case 'x':
+                       xres_set = 1;
+                       /* fallthru */
+               case 'M':
+               case 'R':
+                       vm++;
+                       break;
+
+               default:
+                       if (*vm != '\0')
+                               vm++;
+               }
+       }
+       if (p->xres == 0 || p->yres == 0) {
+               printf("Invalid video mode: %s\n", getenv("video_mode"));
+               lcd_enabled = 0;
+               printf("Supported video modes are:");
+               for (p = &txa5_fb_modes[0]; p->name != NULL; p++) {
+                       printf(" %s", p->name);
+               }
+               printf("\n");
+               return 0;
+       }
+       if (p->xres > panel_info.vl_col || p->yres > panel_info.vl_row) {
+               printf("video resolution: %dx%d exceeds hardware limits: %dx%d\n",
+                       p->xres, p->yres, panel_info.vl_col, panel_info.vl_row);
+               lcd_enabled = 0;
+               return 0;
+       }
+       panel_info.vl_col = p->xres;
+       panel_info.vl_row = p->yres;
+
+       p->pixclock = KHZ2PICOS(refresh *
+                       (p->xres + p->left_margin + p->right_margin + p->hsync_len) *
+                       (p->yres + p->upper_margin + p->lower_margin + p->vsync_len) /
+                       1000);
+       debug("Pixel clock set to %lu.%03lu MHz\n",
+               PICOS2KHZ(p->pixclock) / 1000, PICOS2KHZ(p->pixclock) % 1000);
+
+       if (p != &fb_mode) {
+               int ret;
+
+               debug("Creating new display-timing node from '%s'\n",
+                       video_mode);
+               ret = karo_fdt_create_fb_mode(working_fdt, video_mode, p);
+               if (ret)
+                       printf("Failed to create new display-timing node from '%s': %d\n",
+                               video_mode, ret);
+       }
+
+       txa5_lcd_hw_init();
+
+       lcd_bus_width = karo_fdt_get_lcd_bus_width(working_fdt, 24);
+       switch (lcd_bus_width) {
+       case 16:
+       case 18:
+       case 24:
+               lcd_output_bpp = lcd_bus_width;
+               break;
+
+       default:
+               lcd_enabled = 0;
+               printf("Invalid LCD bus width: %d\n", lcd_bus_width);
+               return 0;
+       }
+       if (karo_load_splashimage(0) == 0) {
+               debug("Initializing FB driver\n");
+               txa5_lcd_panel_setup(p);
+               return 1;
+       } else {
+               debug("Skipping initialization of LCD controller\n");
+               return 0;
+       }
+}
+#else
+#define lcd_enabled 0
+#endif /* CONFIG_LCD */
+
+static void stk5_board_init(void)
+{
+#if 0
+       gpio_request_array(stk5_gpios, ARRAY_SIZE(stk5_gpios));
+       txa5_set_pin_mux(stk5_pads, ARRAY_SIZE(stk5_pads));
+#endif
+}
+
+static void stk5v3_board_init(void)
+{
+       stk5_board_init();
+}
+
+static void stk5v5_board_init(void)
+{
+       stk5_board_init();
+
+#if 0
+       gpio_request_array(stk5v5_gpios, ARRAY_SIZE(stk5v5_gpios));
+       txa5_set_pin_mux(stk5v5_pads, ARRAY_SIZE(stk5v5_pads));
+#endif
+}
+
+/* called with environment from NAND or MMC */
+int board_late_init(void)
+{
+       int ret = 0;
+       const char *baseboard;
+
+       env_cleanup();
+
+//     txa5_set_cpu_clock();
+
+       if (had_ctrlc())
+               setenv_ulong("safeboot", 1);
+#if 0
+       else if (prm_rstst & PRM_RSTST_WDT1_RST)
+               setenv_ulong("wdreset", 1);
+#endif
+       else
+               karo_fdt_move_fdt();
+
+       baseboard = getenv("baseboard");
+       if (!baseboard)
+               goto exit;
+
+       if (strncmp(baseboard, "stk5", 4) == 0) {
+               printf("Baseboard: %s\n", baseboard);
+               if ((strlen(baseboard) == 4) ||
+                       strcmp(baseboard, "stk5-v3") == 0) {
+                       stk5v3_board_init();
+               } else if (strcmp(baseboard, "stk5-v5") == 0) {
+                       stk5v5_board_init();
+               } else {
+                       printf("WARNING: Unsupported STK5 board rev.: %s\n",
+                               baseboard + 4);
+               }
+       } else {
+               printf("WARNING: Unsupported baseboard: '%s'\n",
+                       baseboard);
+               ret = -EINVAL;
+       }
+
+exit:
+       txa5_init_mac();
+       clear_ctrlc();
+       return ret;
+}
+
+#if defined(CONFIG_OF_BOARD_SETUP)
+#ifdef CONFIG_FDT_FIXUP_PARTITIONS
+#include <jffs2/jffs2.h>
+#include <mtd_node.h>
+static struct node_info nodes[] = {
+       { "atmel,sama5d4-nand", MTD_DEV_TYPE_NAND, },
+};
+#else
+#define fdt_fixup_mtdparts(b,n,c) do { } while (0)
+#endif
+
+static const char *txa5_touchpanels[] = {
+       "ti,tsc2007",
+       "edt,edt-ft5x06",
+       "eeti,egalax_ts",
+};
+
+int ft_board_setup(void *blob, bd_t *bd)
+{
+       const char *baseboard = getenv("baseboard");
+       int stk5_v5 = baseboard != NULL && (strcmp(baseboard, "stk5-v5") == 0);
+       const char *video_mode = karo_get_vmode(getenv("video_mode"));
+       int ret;
+
+       ret = fdt_increase_size(blob, 4096);
+       if (ret) {
+               printf("Failed to increase FDT size: %s\n", fdt_strerror(ret));
+               return ret;
+       }
+       if (stk5_v5)
+               karo_fdt_enable_node(blob, "stk5led", 0);
+
+       fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes));
+       fdt_fixup_ethernet(blob);
+
+       karo_fdt_fixup_touchpanel(blob, txa5_touchpanels,
+                               ARRAY_SIZE(txa5_touchpanels));
+//     karo_fdt_fixup_usb_otg(blob, "usbotg", "fsl,usbphy", "vbus-supply");
+//     karo_fdt_fixup_flexcan(blob, stk5_v5);
+       karo_fdt_update_fb_mode(blob, video_mode);
+
+       return 0;
+}
+#endif /* CONFIG_OF_BOARD_SETUP */