]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - board/karo/tx48/tx48.c
karo: remove redundant cmap entry in panel_info
[karo-tx-uboot.git] / board / karo / tx48 / tx48.c
index f0af4b4c2107281a9a7de646fa626855c776317c..7383738a249121f4a5812b5c04fdf244512d5fc1 100644 (file)
@@ -1,13 +1,12 @@
 /*
- * tx48.c
- * Copyright (C) 2012 Lothar Waßmann <LW@KARO-electronics.de>
+ * Copyright (C) 2012-2013 Lothar Waßmann <LW@KARO-electronics.de>
  *
  * based on evm.c
  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
  *
  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  * kind, whether express or implied; without even the implied warranty
@@ -18,7 +17,7 @@
 #include <common.h>
 #include <errno.h>
 #include <miiphy.h>
-#include <netdev.h>
+#include <cpsw.h>
 #include <serial.h>
 #include <libfdt.h>
 #include <lcd.h>
 #include <nand.h>
 #include <net.h>
 #include <linux/mtd/nand.h>
+#include <linux/fb.h>
 #include <asm/gpio.h>
 #include <asm/cache.h>
-#include <asm/omap_common.h>
 #include <asm/io.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/mmc_host_def.h>
+#include <asm/arch/mux.h>
 #include <asm/arch/sys_proto.h>
-#include <asm/arch/nand.h>
 #include <asm/arch/clock.h>
 #include <video_fb.h>
 #include <asm/arch/da8xx-fb.h>
@@ -48,254 +47,12 @@ DECLARE_GLOBAL_DATA_PTR;
 #define TX48_LCD_RST_GPIO      AM33XX_GPIO_NR(1, 19)
 #define TX48_LCD_PWR_GPIO      AM33XX_GPIO_NR(1, 22)
 #define TX48_LCD_BACKLIGHT_GPIO        AM33XX_GPIO_NR(3, 14)
-
-#define GMII_SEL               (CTRL_BASE + 0x650)
-
-/* UART Defines */
-#define UART_SYSCFG_OFFSET     0x54
-#define UART_SYSSTS_OFFSET     0x58
-
-#define UART_RESET             (0x1 << 1)
-#define UART_CLK_RUNNING_MASK  0x1
-#define UART_SMART_IDLE_EN     (0x1 << 0x3)
-
-/* Timer Defines */
-#define TSICR_REG              0x54
-#define TIOCP_CFG_REG          0x10
-#define TCLR_REG               0x38
-
-/* RGMII mode define */
-#define RGMII_MODE_ENABLE      0xA
-#define RMII_MODE_ENABLE       0x5
-#define MII_MODE_ENABLE                0x0
+#define TX48_MMC_CD_GPIO       AM33XX_GPIO_NR(3, 15)
 
 #define NO_OF_MAC_ADDR         1
+#ifndef ETH_ALEN
 #define ETH_ALEN               6
-
-#define MUX_CFG(value, offset) {                                       \
-       __raw_writel(value, (CTRL_BASE + (offset)));                    \
-       }
-
-/* PAD Control Fields */
-#define SLEWCTRL       (0x1 << 6)
-#define        RXACTIVE        (0x1 << 5)
-#define        PULLUP_EN       (0x1 << 4) /* Pull UP Selection */
-#define PULLUDEN       (0x0 << 3) /* Pull up enabled */
-#define PULLUDDIS      (0x1 << 3) /* Pull up disabled */
-#define MODE(val)      (val)
-
-/*
- * PAD CONTROL OFFSETS
- * Field names corresponds to the pad signal name
- */
-struct pad_signals {
-       int gpmc_ad0;
-       int gpmc_ad1;
-       int gpmc_ad2;
-       int gpmc_ad3;
-       int gpmc_ad4;
-       int gpmc_ad5;
-       int gpmc_ad6;
-       int gpmc_ad7;
-       int gpmc_ad8;
-       int gpmc_ad9;
-       int gpmc_ad10;
-       int gpmc_ad11;
-       int gpmc_ad12;
-       int gpmc_ad13;
-       int gpmc_ad14;
-       int gpmc_ad15;
-       int gpmc_a0;
-       int gpmc_a1;
-       int gpmc_a2;
-       int gpmc_a3;
-       int gpmc_a4;
-       int gpmc_a5;
-       int gpmc_a6;
-       int gpmc_a7;
-       int gpmc_a8;
-       int gpmc_a9;
-       int gpmc_a10;
-       int gpmc_a11;
-       int gpmc_wait0;
-       int gpmc_wpn;
-       int gpmc_be1n;
-       int gpmc_csn0;
-       int gpmc_csn1;
-       int gpmc_csn2;
-       int gpmc_csn3;
-       int gpmc_clk;
-       int gpmc_advn_ale;
-       int gpmc_oen_ren;
-       int gpmc_wen;
-       int gpmc_be0n_cle;
-       int lcd_data0;
-       int lcd_data1;
-       int lcd_data2;
-       int lcd_data3;
-       int lcd_data4;
-       int lcd_data5;
-       int lcd_data6;
-       int lcd_data7;
-       int lcd_data8;
-       int lcd_data9;
-       int lcd_data10;
-       int lcd_data11;
-       int lcd_data12;
-       int lcd_data13;
-       int lcd_data14;
-       int lcd_data15;
-       int lcd_vsync;
-       int lcd_hsync;
-       int lcd_pclk;
-       int lcd_ac_bias_en;
-       int mmc0_dat3;
-       int mmc0_dat2;
-       int mmc0_dat1;
-       int mmc0_dat0;
-       int mmc0_clk;
-       int mmc0_cmd;
-       int mii1_col;
-       int mii1_crs;
-       int mii1_rxerr;
-       int mii1_txen;
-       int mii1_rxdv;
-       int mii1_txd3;
-       int mii1_txd2;
-       int mii1_txd1;
-       int mii1_txd0;
-       int mii1_txclk;
-       int mii1_rxclk;
-       int mii1_rxd3;
-       int mii1_rxd2;
-       int mii1_rxd1;
-       int mii1_rxd0;
-       int rmii1_refclk;
-       int mdio_data;
-       int mdio_clk;
-       int spi0_sclk;
-       int spi0_d0;
-       int spi0_d1;
-       int spi0_cs0;
-       int spi0_cs1;
-       int ecap0_in_pwm0_out;
-       int uart0_ctsn;
-       int uart0_rtsn;
-       int uart0_rxd;
-       int uart0_txd;
-       int uart1_ctsn;
-       int uart1_rtsn;
-       int uart1_rxd;
-       int uart1_txd;
-       int i2c0_sda;
-       int i2c0_scl;
-       int mcasp0_aclkx;
-       int mcasp0_fsx;
-       int mcasp0_axr0;
-       int mcasp0_ahclkr;
-       int mcasp0_aclkr;
-       int mcasp0_fsr;
-       int mcasp0_axr1;
-       int mcasp0_ahclkx;
-       int xdma_event_intr0;
-       int xdma_event_intr1;
-       int nresetin_out;
-       int porz;
-       int nnmi;
-       int osc0_in;
-       int osc0_out;
-       int rsvd1;
-       int tms;
-       int tdi;
-       int tdo;
-       int tck;
-       int ntrst;
-       int emu0;
-       int emu1;
-       int osc1_in;
-       int osc1_out;
-       int pmic_power_en;
-       int rtc_porz;
-       int rsvd2;
-       int ext_wakeup;
-       int enz_kaldo_1p8v;
-       int usb0_dm;
-       int usb0_dp;
-       int usb0_ce;
-       int usb0_id;
-       int usb0_vbus;
-       int usb0_drvvbus;
-       int usb1_dm;
-       int usb1_dp;
-       int usb1_ce;
-       int usb1_id;
-       int usb1_vbus;
-       int usb1_drvvbus;
-       int ddr_resetn;
-       int ddr_csn0;
-       int ddr_cke;
-       int ddr_ck;
-       int ddr_nck;
-       int ddr_casn;
-       int ddr_rasn;
-       int ddr_wen;
-       int ddr_ba0;
-       int ddr_ba1;
-       int ddr_ba2;
-       int ddr_a0;
-       int ddr_a1;
-       int ddr_a2;
-       int ddr_a3;
-       int ddr_a4;
-       int ddr_a5;
-       int ddr_a6;
-       int ddr_a7;
-       int ddr_a8;
-       int ddr_a9;
-       int ddr_a10;
-       int ddr_a11;
-       int ddr_a12;
-       int ddr_a13;
-       int ddr_a14;
-       int ddr_a15;
-       int ddr_odt;
-       int ddr_d0;
-       int ddr_d1;
-       int ddr_d2;
-       int ddr_d3;
-       int ddr_d4;
-       int ddr_d5;
-       int ddr_d6;
-       int ddr_d7;
-       int ddr_d8;
-       int ddr_d9;
-       int ddr_d10;
-       int ddr_d11;
-       int ddr_d12;
-       int ddr_d13;
-       int ddr_d14;
-       int ddr_d15;
-       int ddr_dqm0;
-       int ddr_dqm1;
-       int ddr_dqs0;
-       int ddr_dqsn0;
-       int ddr_dqs1;
-       int ddr_dqsn1;
-       int ddr_vref;
-       int ddr_vtp;
-       int ddr_strben0;
-       int ddr_strben1;
-       int ain7;
-       int ain6;
-       int ain5;
-       int ain4;
-       int ain3;
-       int ain2;
-       int ain1;
-       int ain0;
-       int vrefp;
-       int vrefn;
-};
+#endif
 
 struct pin_mux {
        short reg_offset;
@@ -315,7 +72,7 @@ static inline void tx48_set_pin_mux(const struct pin_mux *pin_mux,
        int i;
 
        for (i = 0; i < num_pins; i++)
-               MUX_CFG(pin_mux[i].val, pin_mux[i].reg_offset);
+               writel(pin_mux[i].val, CTRL_BASE + pin_mux[i].reg_offset);
 }
 
 #define PRM_RSTST_GLOBAL_COLD_RST      (1 << 0)
@@ -329,6 +86,23 @@ static u32 prm_rstst __attribute__((section(".data")));
 /*
  * Basic board specific setup
  */
+static const struct pin_mux tx48_pads[] = {
+       { OFFSET(i2c0_sda), MODE(7) | RXACTIVE | PULLUDEN | PULLUP_EN, },
+       { OFFSET(i2c0_scl), MODE(7) | RXACTIVE | PULLUDEN | PULLUP_EN, },
+       { OFFSET(emu1), MODE(7), }, /* ETH PHY Reset */
+};
+
+static const struct pin_mux tx48_i2c_pads[] = {
+       { OFFSET(i2c0_sda), MODE(0) | RXACTIVE | PULLUDEN | PULLUP_EN, },
+       { OFFSET(i2c0_scl), MODE(0) | RXACTIVE | PULLUDEN | PULLUP_EN, },
+};
+
+static const struct gpio tx48_gpios[] = {
+       { AM33XX_GPIO_NR(3, 5), GPIOFLAG_INPUT, "I2C1_SDA", },
+       { AM33XX_GPIO_NR(3, 6), GPIOFLAG_INPUT, "I2C1_SCL", },
+       { AM33XX_GPIO_NR(3, 8), GPIOFLAG_OUTPUT_INIT_LOW, "ETH_PHY_RESET", },
+};
+
 static const struct pin_mux stk5_pads[] = {
        /* heartbeat LED */
        { OFFSET(gpmc_a10), MODE(7) | PULLUDEN, },
@@ -338,6 +112,13 @@ static const struct pin_mux stk5_pads[] = {
        { OFFSET(gpmc_a6), MODE(7) | PULLUDEN, },
        /* LCD Backlight (PWM) */
        { OFFSET(mcasp0_aclkx), MODE(7) | PULLUDEN, },
+       /* MMC CD */
+       { OFFSET(mcasp0_fsx), MODE(7) | PULLUDEN | PULLUP_EN, },
+};
+
+static const struct gpio stk5_gpios[] = {
+       { TX48_LED_GPIO, GPIOFLAG_OUTPUT_INIT_LOW, "HEARTBEAT LED", },
+       { TX48_MMC_CD_GPIO, GPIOFLAG_INPUT, "MMC0 CD", },
 };
 
 static const struct pin_mux stk5_lcd_pads[] = {
@@ -365,14 +146,10 @@ static const struct pin_mux stk5_lcd_pads[] = {
        { OFFSET(lcd_ac_bias_en), MODE(0) | PULLUDEN, },
 };
 
-static const struct gpio stk5_gpios[] = {
-       { AM33XX_GPIO_NR(1, 26), GPIOF_OUTPUT_INIT_LOW, "HEARTBEAT LED", },
-};
-
 static const struct gpio stk5_lcd_gpios[] = {
-       { AM33XX_GPIO_NR(1, 19), GPIOF_OUTPUT_INIT_LOW, "LCD RESET", },
-       { AM33XX_GPIO_NR(1, 22), GPIOF_OUTPUT_INIT_LOW, "LCD POWER", },
-       { AM33XX_GPIO_NR(3, 14), GPIOF_OUTPUT_INIT_HIGH, "LCD BACKLIGHT", },
+       { AM33XX_GPIO_NR(1, 19), GPIOFLAG_OUTPUT_INIT_LOW, "LCD RESET", },
+       { AM33XX_GPIO_NR(1, 22), GPIOFLAG_OUTPUT_INIT_LOW, "LCD POWER", },
+       { AM33XX_GPIO_NR(3, 14), GPIOFLAG_OUTPUT_INIT_HIGH, "LCD BACKLIGHT", },
 };
 
 static const struct pin_mux stk5v5_pads[] = {
@@ -381,38 +158,159 @@ static const struct pin_mux stk5v5_pads[] = {
 };
 
 static const struct gpio stk5v5_gpios[] = {
-       { AM33XX_GPIO_NR(0, 22), GPIOF_OUTPUT_INIT_HIGH, "CAN XCVR", },
+       { AM33XX_GPIO_NR(0, 22), GPIOFLAG_OUTPUT_INIT_HIGH, "CAN XCVR", },
 };
 
 #ifdef CONFIG_LCD
-static u16 tx48_cmap[256];
 vidinfo_t panel_info = {
        /* set to max. size supported by SoC */
        .vl_col = 1366,
        .vl_row = 768,
 
-       .vl_bpix = LCD_COLOR24,    /* Bits per pixel, 0: 1bpp, 1: 2bpp, 2: 4bpp, 3: 8bpp ... */
-       .cmap = tx48_cmap,
+       .vl_bpix = LCD_COLOR32,    /* Bits per pixel, 0: 1bpp, 1: 2bpp, 2: 4bpp, 3: 8bpp ... */
+};
+
+static struct lcd_ctrl_config lcd_cfg = {
+       .bpp = 24,
 };
 
-static struct da8xx_panel tx48_lcd_panel = {
-       .name = "640x480MR@60",
-       .width = 640,
-       .height = 480,
-       .hfp = 12,
-       .hbp = 144,
-       .hsw = 30,
-       .vfp = 10,
-       .vbp = 35,
-       .vsw = 3,
-       .pxl_clk = 25000000,
-       .invert_pxl_clk = 1,
+#define FB_SYNC_OE_LOW_ACT     (1 << 31)
+#define FB_SYNC_CLK_LAT_FALL   (1 << 30)
+
+static struct fb_videomode tx48_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,
+       },
 };
 
 void *lcd_base;                        /* Start of framebuffer memory  */
 void *lcd_console_address;     /* Start of console buffer      */
 
-int lcd_line_length;
 int lcd_color_fg;
 int lcd_color_bg;
 
@@ -420,6 +318,12 @@ short console_col;
 short console_row;
 
 static int lcd_enabled = 1;
+static int lcd_bl_polarity;
+
+static int lcd_backlight_polarity(void)
+{
+       return lcd_bl_polarity;
+}
 
 void lcd_initcolregs(void)
 {
@@ -441,25 +345,49 @@ void lcd_enable(void)
        if (lcd_enabled) {
                karo_load_splashimage(1);
 
+               debug("Switching LCD on\n");
                gpio_set_value(TX48_LCD_PWR_GPIO, 1);
+               udelay(100);
                gpio_set_value(TX48_LCD_RST_GPIO, 1);
                udelay(300000);
-               gpio_set_value(TX48_LCD_BACKLIGHT_GPIO, 0);
+               gpio_set_value(TX48_LCD_BACKLIGHT_GPIO,
+                       lcd_backlight_polarity());
        }
 }
 
 void lcd_disable(void)
 {
        if (lcd_enabled) {
+               printf("Disabling LCD\n");
                da8xx_fb_disable();
                lcd_enabled = 0;
        }
 }
 
+static void tx48_lcd_panel_setup(struct da8xx_panel *p,
+                               struct fb_videomode *fb)
+{
+       p->pxl_clk = PICOS2KHZ(fb->pixclock) * 1000;
+
+       p->width = fb->xres;
+       p->hbp = fb->left_margin;
+       p->hsw = fb->hsync_len;
+       p->hfp = fb->right_margin;
+
+       p->height = fb->yres;
+       p->vbp = fb->upper_margin;
+       p->vsw = fb->vsync_len;
+       p->vfp = fb->lower_margin;
+
+       p->invert_pxl_clk = !!(fb->sync & FB_SYNC_CLK_LAT_FALL);
+}
+
 void lcd_panel_disable(void)
 {
        if (lcd_enabled) {
-               gpio_set_value(TX48_LCD_BACKLIGHT_GPIO, 1);
+               debug("Switching LCD off\n");
+               gpio_set_value(TX48_LCD_BACKLIGHT_GPIO,
+                       !lcd_backlight_polarity());
                gpio_set_value(TX48_LCD_PWR_GPIO, 0);
                gpio_set_value(TX48_LCD_RST_GPIO, 0);
        }
@@ -468,96 +396,178 @@ void lcd_panel_disable(void)
 void lcd_ctrl_init(void *lcdbase)
 {
        int color_depth = 24;
-       char *vm, *v;
+       const char *video_mode = karo_get_vmode(getenv("video_mode"));
+       const char *vm;
        unsigned long val;
-       struct da8xx_panel *p = &tx48_lcd_panel;
        int refresh = 60;
+       struct fb_videomode *p = &tx48_fb_modes[0];
+       struct fb_videomode fb_mode;
+       int xres_set = 0, yres_set = 0, bpp_set = 0, refresh_set = 0;
 
        if (!lcd_enabled) {
-               printf("LCD disabled\n");
+               debug("LCD disabled\n");
                return;
        }
 
-       if (tstc() || (prm_rstst & PRM_RSTST_WDT1_RST)) {
+       if (had_ctrlc() || (prm_rstst & PRM_RSTST_WDT1_RST)) {
+               debug("Disabling LCD\n");
                lcd_enabled = 0;
+               setenv("splashimage", NULL);
                return;
        }
 
-       vm = getenv("video_mode");
-       if (vm == NULL) {
+       karo_fdt_move_fdt();
+
+       if (video_mode == NULL) {
+               debug("Disabling LCD\n");
                lcd_enabled = 0;
                return;
        }
 
-       if ((v = strstr(vm, ":")))
-               vm = v + 1;
-
-       strncpy((char *)p->name, vm, sizeof(p->name));
-
-       val = simple_strtoul(vm, &vm, 10);
-       if (val != 0) {
-               if (val > panel_info.vl_col)
-                       val = panel_info.vl_col;
-               p->width = val;
-               panel_info.vl_col = val;
+       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;
+               }
        }
-       if (*vm == 'x') {
-               val = simple_strtoul(vm + 1, &vm, 10);
-               if (val > panel_info.vl_row)
-                       val = panel_info.vl_row;
-               p->height = val;
-               panel_info.vl_row = val;
+       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 24:
+                                       case 16:
+                                       case 8:
+                                               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;
 
-               case '-':
-                       color_depth = simple_strtoul(vm + 1, &vm, 10);
-                       break;
-
-               case '@':
-                       refresh = simple_strtoul(vm + 1, &vm, 10);
-                       break;
-
                default:
-                       debug("Ignoring '%c'\n", *vm);
-                       vm++;
+                       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 = &tx48_fb_modes[0]; p->name != NULL; p++) {
+                       printf(" %s", p->name);
+               }
+               printf("\n");
+               return;
+       }
+       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;
+       }
+       panel_info.vl_col = p->xres;
+       panel_info.vl_row = p->yres;
+
        switch (color_depth) {
        case 8:
-               panel_info.vl_bpix = 3;
+               panel_info.vl_bpix = LCD_COLOR8;
                break;
-
        case 16:
-               panel_info.vl_bpix = 4;
-               break;
-
-       case 24:
-               panel_info.vl_bpix = 5;
+               panel_info.vl_bpix = LCD_COLOR16;
                break;
-
        default:
-               printf("Invalid color_depth %u from video_mode '%s'; using default: %u\n",
-                       color_depth, getenv("video_mode"), 24);
+               panel_info.vl_bpix = LCD_COLOR32;
+       }
+
+       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);
        }
-       lcd_line_length = NBITS(panel_info.vl_bpix) / 8 * panel_info.vl_col;
-       p->pxl_clk = refresh *
-               (p->width + p->hfp + p->hbp + p->hsw) *
-               (p->height + p->vfp + p->vbp + p->vsw);
-       debug("Pixel clock set to %u.%03uMHz\n",
-               p->pxl_clk / 1000000, p->pxl_clk / 1000 % 1000);
 
        gpio_request_array(stk5_lcd_gpios, ARRAY_SIZE(stk5_lcd_gpios));
        tx48_set_pin_mux(stk5_lcd_pads, ARRAY_SIZE(stk5_lcd_pads));
-       debug("Initializing FB driver\n");
-       da8xx_video_init(&tx48_lcd_panel, color_depth);
 
        if (karo_load_splashimage(0) == 0) {
+               struct da8xx_panel da8xx_panel = { };
+
+               debug("Initializing FB driver\n");
+               tx48_lcd_panel_setup(&da8xx_panel, p);
+               da8xx_video_init(&da8xx_panel, &lcd_cfg, color_depth);
+
                debug("Initializing LCD controller\n");
                video_hw_init();
        } else {
@@ -570,6 +580,7 @@ void lcd_ctrl_init(void *lcdbase)
 
 static void stk5_board_init(void)
 {
+       gpio_request_array(stk5_gpios, ARRAY_SIZE(stk5_gpios));
        tx48_set_pin_mux(stk5_pads, ARRAY_SIZE(stk5_pads));
 }
 
@@ -581,13 +592,16 @@ static void stk5v3_board_init(void)
 static void stk5v5_board_init(void)
 {
        stk5_board_init();
-       tx48_set_pin_mux(stk5v5_pads, ARRAY_SIZE(stk5v5_pads));
+
        gpio_request_array(stk5v5_gpios, ARRAY_SIZE(stk5v5_gpios));
+       tx48_set_pin_mux(stk5v5_pads, ARRAY_SIZE(stk5v5_pads));
 }
 
 /* called with default environment! */
 int board_init(void)
 {
+       int i;
+
        /* mach type passed to kernel */
 #ifdef CONFIG_OF_LIBFDT
        gd->bd->bi_arch_number = -1;
@@ -595,6 +609,24 @@ int board_init(void)
        /* address of boot parameters */
        gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100;
 
+       if (ctrlc() || (prm_rstst & PRM_RSTST_WDT1_RST)) {
+               if (prm_rstst & PRM_RSTST_WDT1_RST)
+                       printf("WDOG RESET detected\n");
+               else
+                       printf("<CTRL-C> detected; safeboot enabled\n");
+       }
+
+       gpio_request_array(tx48_gpios, ARRAY_SIZE(tx48_gpios));
+       tx48_set_pin_mux(tx48_pads, ARRAY_SIZE(tx48_pads));
+
+       for (i = 0; i < ARRAY_SIZE(tx48_gpios); i++) {
+               int gpio = tx48_gpios[i].gpio;
+
+               if (gpio_get_value(gpio) == 0)
+                       gpio_direction_output(gpio, 1);
+       }
+
+       tx48_set_pin_mux(tx48_pads, ARRAY_SIZE(tx48_i2c_pads));
        return 0;
 }
 
@@ -635,11 +667,8 @@ int checkboard(void)
        prm_rstst = readl(PRM_RSTST);
        show_reset_cause(prm_rstst);
 
-#ifdef CONFIG_OF_LIBFDT
-       printf("Board: Ka-Ro TX48-7020 with FDT support\n");
-#else
        printf("Board: Ka-Ro TX48-7020\n");
-#endif
+
        timer_init();
        return 0;
 }
@@ -647,32 +676,75 @@ int checkboard(void)
 static void tx48_set_cpu_clock(void)
 {
        unsigned long cpu_clk = getenv_ulong("cpu_clk", 10, 0);
+       unsigned long act_cpu_clk;
 
-       if (tstc() || (prm_rstst & PRM_RSTST_WDT1_RST))
+       if (cpu_clk == 0 || cpu_clk == mpu_clk_rate() / 1000000)
                return;
 
-       if (cpu_clk == 0 || cpu_clk == mpu_clk_rate() / 1000000)
+       if (had_ctrlc() || (prm_rstst & PRM_RSTST_WDT1_RST)) {
+               printf("%s detected; skipping cpu clock change\n",
+                       (prm_rstst & PRM_RSTST_WDT1_RST) ?
+                       "WDOG RESET" : "<CTRL-C>");
                return;
+       }
 
-       mpu_pll_config(cpu_clk);
+       mpu_pll_config_val(cpu_clk);
 
-       printf("CPU clock set to %lu.%03lu MHz\n",
-               mpu_clk_rate() / 1000000,
-               mpu_clk_rate() / 1000 % 1000);
+       act_cpu_clk = mpu_clk_rate();
+       if (cpu_clk * 1000000 != act_cpu_clk) {
+               printf("Failed to set CPU clock to %lu MHz; using %lu.%03lu MHz instead\n",
+                       cpu_clk, act_cpu_clk / 1000000,
+                       act_cpu_clk / 1000 % 1000);
+       } else {
+               printf("CPU clock set to %lu.%03lu MHz\n",
+                       act_cpu_clk / 1000000, act_cpu_clk / 1000 % 1000);
+       }
+}
+
+static void tx48_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 & 0xFF00) >> 8;
+       mac_addr[2] = (mac_hi & 0xFF0000) >> 16;
+       mac_addr[3] = (mac_hi & 0xFF000000) >> 24;
+       mac_addr[4] = mac_lo & 0xFF;
+       mac_addr[5] = (mac_lo & 0xFF00) >> 8;
+
+       if (!is_valid_ethaddr(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);
 }
 
 /* called with environment from NAND or MMC */
 int board_late_init(void)
 {
+       int ret = 0;
        const char *baseboard;
 
+       env_cleanup();
+
        tx48_set_cpu_clock();
-#ifdef CONFIG_OF_BOARD_SETUP
-       karo_fdt_move_fdt();
-#endif
+
+       if (had_ctrlc())
+               setenv_ulong("safeboot", 1);
+       else if (prm_rstst & PRM_RSTST_WDT1_RST)
+               setenv_ulong("wdreset", 1);
+       else
+               karo_fdt_move_fdt();
+
        baseboard = getenv("baseboard");
        if (!baseboard)
-               return 0;
+               goto exit;
 
        if (strncmp(baseboard, "stk5", 4) == 0) {
                printf("Baseboard: %s\n", baseboard);
@@ -688,14 +760,17 @@ int board_late_init(void)
        } else {
                printf("WARNING: Unsupported baseboard: '%s'\n",
                        baseboard);
-               return -EINVAL;
+               ret = -EINVAL;
        }
 
-       return 0;
+exit:
+       tx48_init_mac();
+       clear_ctrlc();
+       return ret;
 }
 
 #ifdef CONFIG_DRIVER_TI_CPSW
-static void tx48_phy_init(char *name, int addr)
+static void tx48_phy_init(void)
 {
        debug("%s: Resetting ethernet PHY\n", __func__);
 
@@ -720,7 +795,7 @@ static struct cpsw_slave_data cpsw_slaves[] = {
        {
                .slave_reg_ofs  = 0x208,
                .sliver_reg_ofs = 0xd80,
-               .phy_id         = 0,
+               .phy_addr       = 0,
                .phy_if         = PHY_INTERFACE_MODE_RMII,
        },
 };
@@ -731,8 +806,8 @@ void s_init(void)
 }
 
 static struct cpsw_platform_data cpsw_data = {
-       .mdio_base              = AM335X_CPSW_MDIO_BASE,
-       .cpsw_base              = AM335X_CPSW_BASE,
+       .mdio_base              = CPSW_MDIO_BASE,
+       .cpsw_base              = CPSW_BASE,
        .mdio_div               = 0xff,
        .channels               = 8,
        .cpdma_reg_ofs          = 0x800,
@@ -744,41 +819,25 @@ static struct cpsw_platform_data cpsw_data = {
        .hw_stats_reg_ofs       = 0x900,
        .mac_control            = (1 << 5) /* MIIEN */,
        .control                = cpsw_control,
-       .phy_init               = tx48_phy_init,
-       .gigabit_en             = 0,
        .host_port_num          = 0,
        .version                = CPSW_CTRL_VERSION_2,
 };
 
 int board_eth_init(bd_t *bis)
 {
-       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 & 0xFF00) >> 8;
-       mac_addr[2] = (mac_hi & 0xFF0000) >> 16;
-       mac_addr[3] = (mac_hi & 0xFF000000) >> 24;
-       mac_addr[4] = mac_lo & 0xFF;
-       mac_addr[5] = (mac_lo & 0xFF00) >> 8;
-
-       if (is_valid_ether_addr(mac_addr)) {
-               debug("MAC addr set to: %pM\n", mac_addr);
-               eth_setenv_enetaddr("ethaddr", mac_addr);
-       } else {
-               printf("ERROR: Did not find a valid mac address in e-fuse\n");
-       }
-
        __raw_writel(RMII_MODE_ENABLE, MAC_MII_SEL);
-       __raw_writel(0x5D, GMII_SEL);
+       tx48_phy_init();
        return cpsw_register(&cpsw_data);
 }
 #endif /* CONFIG_DRIVER_TI_CPSW */
 
+#if defined(CONFIG_OMAP_HSMMC) && !defined(CONFIG_SPL_BUILD)
+int cpu_mmc_init(bd_t *bis)
+{
+       return omap_mmc_init(1, 0, 0, TX48_MMC_CD_GPIO, -1);
+}
+#endif
+
 void tx48_disable_watchdog(void)
 {
        struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;
@@ -791,46 +850,6 @@ void tx48_disable_watchdog(void)
        writel(0x5555, &wdtimer->wdtwspr);
 }
 
-#if defined(CONFIG_NAND_AM33XX) && defined(CONFIG_CMD_SWITCH_ECC)
-/******************************************************************************
- * Command to switch between NAND HW and SW ecc
- *****************************************************************************/
-static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
-{
-       int type = 0;
-
-       if (argc < 2)
-               goto usage;
-
-       if (strncmp(argv[1], "hw", 2) == 0) {
-               if (argc == 3)
-                       type = simple_strtoul(argv[2], NULL, 10);
-               am33xx_nand_switch_ecc(NAND_ECC_HW, type);
-       }
-       else if (strncmp(argv[1], "sw", 2) == 0)
-               am33xx_nand_switch_ecc(NAND_ECC_SOFT, 0);
-       else
-               goto usage;
-
-       return 0;
-
-usage:
-       printf("Usage: nandecc %s\n", cmdtp->usage);
-       return 1;
-}
-
-U_BOOT_CMD(
-       nandecc, 3, 1,  do_switch_ecc,
-       "Switch NAND ECC calculation algorithm b/w hardware and software",
-       "[sw|hw <hw_type>] \n"
-       "   [sw|hw]- Switch b/w hardware(hw) & software(sw) ecc algorithm\n"
-       "   hw_type- 0 for Hamming code\n"
-       "            1 for bch4\n"
-       "            2 for bch8\n"
-       "            3 for bch16\n"
-);
-#endif /* CONFIG_NAND_AM33XX && CONFIG_CMD_SWITCH_ECC */
-
 enum {
        LED_STATE_INIT = -1,
        LED_STATE_OFF,
@@ -863,33 +882,50 @@ void show_activity(int arg)
 #ifdef CONFIG_FDT_FIXUP_PARTITIONS
 #include <jffs2/jffs2.h>
 #include <mtd_node.h>
-struct node_info nodes[] = {
+static struct node_info nodes[] = {
        { "ti,omap2-nand", MTD_DEV_TYPE_NAND, },
+       { "ti,am3352-gpmc", MTD_DEV_TYPE_NAND, },
 };
 
 #else
 #define fdt_fixup_mtdparts(b,n,c) do { } while (0)
 #endif /* CONFIG_FDT_FIXUP_PARTITIONS */
 
-static void tx48_fixup_flexcan(void *blob)
+static const char *tx48_touchpanels[] = {
+       "ti,tsc2007",
+       "edt,edt-ft5x06",
+       "ti,am3359-tscadc",
+};
+
+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;
+       }
+       fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes));
 
-       if (baseboard && strcmp(baseboard, "stk5-v5") == 0)
-               return;
+       karo_fdt_fixup_touchpanel(blob, tx48_touchpanels,
+                               ARRAY_SIZE(tx48_touchpanels));
+       karo_fdt_fixup_usb_otg(blob, "usb0", "phys", "vcc-supply");
+       karo_fdt_fixup_flexcan(blob, stk5_v5);
 
-       karo_fdt_del_prop(blob, "ti,dcan", 0x481cc000, "can-xcvr-enable");
-       karo_fdt_del_prop(blob, "ti,dcan", 0x481d0000, "can-xcvr-enable");
-}
+       karo_fdt_update_fb_mode(blob, video_mode);
 
-void ft_board_setup(void *blob, bd_t *bd)
-{
-       fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes));
-       fdt_fixup_ethernet(blob);
+       tx48_disable_watchdog();
 
-       karo_fdt_fixup_touchpanel(blob);
-       tx48_fixup_flexcan(blob);
+       if (get_cpu_rev() == 0) {
+               karo_fdt_del_prop(blob, "lltc,ltc3589-2", 0x34, "interrupts");
+               karo_fdt_del_prop(blob, "lltc,ltc3589-2", 0x34,
+                               "interrupt-parent");
+       }
 
-       tx48_disable_watchdog();
+       return 0;
 }
 #endif /* CONFIG_OF_BOARD_SETUP */