u-boot/board/ifm/ac14xx/ac14xx.c
Simon Glass e895a4b06f fdt: Allow ft_board_setup() to report failure
This function can fail if the device tree runs out of space. Rather than
silently booting with an incomplete device tree, allow the failure to be
detected.

Unfortunately this involves changing a lot of places in the code. I have
not changed behvaiour to return an error where one is not currently
returned, to avoid unexpected breakage.

Eventually it would be nice to allow boards to register functions to be
called to update the device tree. This would avoid all the many functions
to do this. However it's not clear yet if this should be done using driver
model or with a linker list. This work is left for later.

Signed-off-by: Simon Glass <sjg@chromium.org>
Acked-by: Anatolij Gustschin <agust@denx.de>
2014-11-21 04:43:15 +01:00

617 lines
16 KiB
C

/*
* (C) Copyright 2009 Wolfgang Denk <wd@denx.de>
* (C) Copyright 2009 Dave Srl www.dave.eu
* (C) Copyright 2010 ifm ecomatic GmbH
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/bitops.h>
#include <command.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/mpc512x.h>
#include <fdt_support.h>
#ifdef CONFIG_MISC_INIT_R
#include <i2c.h>
#endif
static int eeprom_diag;
static int mac_diag;
static int gpio_diag;
DECLARE_GLOBAL_DATA_PTR;
static void gpio_configure(void)
{
immap_t *im;
gpio512x_t *gpioregs;
im = (immap_t *) CONFIG_SYS_IMMR;
gpioregs = &im->gpio;
out_be32(&gpioregs->gpodr, 0x00290000); /* open drain */
out_be32(&gpioregs->gpdat, 0x80001040); /* data (when output) */
/*
* out_be32(&gpioregs->gpdir, 0xC2293020);
* workaround for a hardware effect: configure direction in pieces,
* setting all outputs at once drops the reset line too low and
* makes us lose the MII connection (breaks ethernet for us)
*/
out_be32(&gpioregs->gpdir, 0x02003060); /* direction */
setbits_be32(&gpioregs->gpdir, 0x00200000); /* += reset asi */
udelay(10);
setbits_be32(&gpioregs->gpdir, 0x00080000); /* += reset safety */
udelay(10);
setbits_be32(&gpioregs->gpdir, 0x00010000); /* += reset comm */
udelay(10);
setbits_be32(&gpioregs->gpdir, 0xC0000000); /* += backlight, KB sel */
/* to turn from red to yellow when U-Boot runs */
setbits_be32(&gpioregs->gpdat, 0x00002020);
out_be32(&gpioregs->gpimr, 0x00000000); /* interrupt mask */
out_be32(&gpioregs->gpicr1, 0x00000004); /* interrupt sense part 1 */
out_be32(&gpioregs->gpicr2, 0x00A80000); /* interrupt sense part 2 */
out_be32(&gpioregs->gpier, 0xFFFFFFFF); /* interrupt events, clear */
}
/* the physical location of the pins */
#define GPIOKEY_ROW_BITMASK 0x40000000
#define GPIOKEY_ROW_UPPER 0
#define GPIOKEY_ROW_LOWER 1
#define GPIOKEY_COL0_BITMASK 0x20000000
#define GPIOKEY_COL1_BITMASK 0x10000000
#define GPIOKEY_COL2_BITMASK 0x08000000
/* the logical presentation of pressed keys */
#define GPIOKEY_BIT_FNLEFT (1 << 5)
#define GPIOKEY_BIT_FNRIGHT (1 << 4)
#define GPIOKEY_BIT_DIRUP (1 << 3)
#define GPIOKEY_BIT_DIRLEFT (1 << 2)
#define GPIOKEY_BIT_DIRRIGHT (1 << 1)
#define GPIOKEY_BIT_DIRDOWN (1 << 0)
/* the hotkey combination which starts recovery */
#define GPIOKEY_BITS_RECOVERY (GPIOKEY_BIT_FNLEFT | GPIOKEY_BIT_DIRUP | \
GPIOKEY_BIT_DIRDOWN)
static void gpio_selectrow(gpio512x_t *gpioregs, u32 row)
{
if (row)
setbits_be32(&gpioregs->gpdat, GPIOKEY_ROW_BITMASK);
else
clrbits_be32(&gpioregs->gpdat, GPIOKEY_ROW_BITMASK);
udelay(10);
}
static u32 gpio_querykbd(void)
{
immap_t *im;
gpio512x_t *gpioregs;
u32 keybits;
u32 input;
im = (immap_t *)CONFIG_SYS_IMMR;
gpioregs = &im->gpio;
keybits = 0;
/* query upper row */
gpio_selectrow(gpioregs, GPIOKEY_ROW_UPPER);
input = in_be32(&gpioregs->gpdat);
if ((input & GPIOKEY_COL0_BITMASK) == 0)
keybits |= GPIOKEY_BIT_FNLEFT;
if ((input & GPIOKEY_COL1_BITMASK) == 0)
keybits |= GPIOKEY_BIT_DIRUP;
if ((input & GPIOKEY_COL2_BITMASK) == 0)
keybits |= GPIOKEY_BIT_FNRIGHT;
/* query lower row */
gpio_selectrow(gpioregs, GPIOKEY_ROW_LOWER);
input = in_be32(&gpioregs->gpdat);
if ((input & GPIOKEY_COL0_BITMASK) == 0)
keybits |= GPIOKEY_BIT_DIRLEFT;
if ((input & GPIOKEY_COL1_BITMASK) == 0)
keybits |= GPIOKEY_BIT_DIRRIGHT;
if ((input & GPIOKEY_COL2_BITMASK) == 0)
keybits |= GPIOKEY_BIT_DIRDOWN;
/* return bit pattern for keys */
return keybits;
}
/* excerpt from the recovery's hw_info.h */
struct __attribute__ ((__packed__)) eeprom_layout {
char magic[3]; /** 'ifm' */
u8 len[2]; /** content length without magic/len fields */
u8 version[3]; /** structure version */
u8 type; /** type of PCB */
u8 reserved[0x37]; /** padding up to offset 0x40 */
u8 macaddress[6]; /** ethernet MAC (for the mainboard) @0x40 */
};
#define HW_COMP_MAINCPU 2
static struct eeprom_layout eeprom_content;
static int eeprom_was_read; /* has_been_read */
static int eeprom_is_valid;
static int eeprom_version;
#define get_eeprom_field_int(name) ({ \
int value; \
int idx; \
value = 0; \
for (idx = 0; idx < sizeof(name); idx++) { \
value <<= 8; \
value |= name[idx]; \
} \
value; \
})
static int read_eeprom(void)
{
int eeprom_datalen;
int ret;
if (eeprom_was_read)
return 0;
eeprom_is_valid = 0;
ret = i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0,
CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
(uchar *)&eeprom_content, sizeof(eeprom_content));
if (eeprom_diag) {
printf("DIAG: %s() read rc[%d], size[%d]\n",
__func__, ret, sizeof(eeprom_content));
}
if (ret != 0)
return -1;
eeprom_was_read = 1;
/*
* check validity of EEPROM content
* (check version, length, optionally checksum)
*/
eeprom_is_valid = 1;
eeprom_datalen = get_eeprom_field_int(eeprom_content.len);
eeprom_version = get_eeprom_field_int(eeprom_content.version);
if (eeprom_diag) {
printf("DIAG: %s() magic[%c%c%c] len[%d] ver[%d] type[%d]\n",
__func__, eeprom_content.magic[0],
eeprom_content.magic[1], eeprom_content.magic[2],
eeprom_datalen, eeprom_version, eeprom_content.type);
}
if (strncmp(eeprom_content.magic, "ifm", strlen("ifm")) != 0)
eeprom_is_valid = 0;
if (eeprom_datalen < sizeof(struct eeprom_layout) - 5)
eeprom_is_valid = 0;
if ((eeprom_version != 1) && (eeprom_version != 2))
eeprom_is_valid = 0;
if (eeprom_content.type != HW_COMP_MAINCPU)
eeprom_is_valid = 0;
if (eeprom_diag)
printf("DIAG: %s() valid[%d]\n", __func__, eeprom_is_valid);
return ret;
}
int mac_read_from_eeprom(void)
{
const u8 *mac;
const char *mac_txt;
if (read_eeprom()) {
printf("I2C EEPROM read failed.\n");
return -1;
}
if (!eeprom_is_valid) {
printf("I2C EEPROM content not valid\n");
return -1;
}
mac = NULL;
switch (eeprom_version) {
case 1:
case 2:
mac = (const u8 *)&eeprom_content.macaddress;
break;
}
if (mac && is_valid_ether_addr(mac)) {
eth_setenv_enetaddr("ethaddr", mac);
if (mac_diag) {
mac_txt = getenv("ethaddr");
if (mac_txt)
printf("DIAG: MAC value [%s]\n", mac_txt);
else
printf("DIAG: failed to setup MAC env\n");
}
}
return 0;
}
/*
* BEWARE!
* this board uses DDR1(!) Micron SDRAM, *NOT* the DDR2
* which the ADS, Aria or PDM360NG boards are using
* (the steps outlined here refer to the Micron datasheet)
*/
u32 sdram_init_seq[] = {
/* item 6, at least one NOP after CKE went high */
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
/* item 7, precharge all; item 8, tRP (20ns) */
CONFIG_SYS_DDRCMD_PCHG_ALL,
CONFIG_SYS_DDRCMD_NOP,
/* item 9, extended mode register; item 10, tMRD 10ns) */
CONFIG_SYS_MICRON_EMODE | CONFIG_SYS_MICRON_EMODE_PARAM,
CONFIG_SYS_DDRCMD_NOP,
/*
* item 11, (base) mode register _with_ reset DLL;
* item 12, tMRD (10ns)
*/
CONFIG_SYS_MICRON_BMODE | CONFIG_SYS_MICRON_BMODE_RSTDLL |
CONFIG_SYS_MICRON_BMODE_PARAM,
CONFIG_SYS_DDRCMD_NOP,
/* item 13, precharge all; item 14, tRP (20ns) */
CONFIG_SYS_DDRCMD_PCHG_ALL,
CONFIG_SYS_DDRCMD_NOP,
/*
* item 15, auto refresh (i.e. refresh with CKE held high);
* item 16, tRFC (70ns)
*/
CONFIG_SYS_DDRCMD_RFSH,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
/*
* item 17, auto refresh (i.e. refresh with CKE held high);
* item 18, tRFC (70ns)
*/
CONFIG_SYS_DDRCMD_RFSH,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
CONFIG_SYS_DDRCMD_NOP,
/* item 19, optional, unassert DLL reset; item 20, tMRD (20ns) */
CONFIG_SYS_MICRON_BMODE | CONFIG_SYS_MICRON_BMODE_PARAM,
CONFIG_SYS_DDRCMD_NOP,
/*
* item 21, "actually done", but make sure 200 DRAM clock cycles
* have passed after DLL reset before READ requests are issued
* (200 cycles at 160MHz -> 1.25 usec)
*/
/* EMPTY, optional, we don't do it */
};
phys_size_t initdram(int board_type)
{
return fixed_sdram(NULL, sdram_init_seq, ARRAY_SIZE(sdram_init_seq));
}
int misc_init_r(void)
{
u32 keys;
char *s;
int want_recovery;
/* we use bus I2C-0 for the on-board eeprom */
i2c_set_bus_num(0);
/* setup GPIO directions and initial values */
gpio_configure();
/*
* enforce the start of the recovery system when
* - the appropriate keys were pressed
* - "some" external software told us to
* - a previous installation was aborted or has failed
*/
want_recovery = 0;
keys = gpio_querykbd();
if (gpio_diag)
printf("GPIO keyboard status [0x%02X]\n", keys);
if ((keys & GPIOKEY_BITS_RECOVERY) == GPIOKEY_BITS_RECOVERY) {
printf("detected recovery request (keyboard)\n");
want_recovery = 1;
}
s = getenv("want_recovery");
if ((s != NULL) && (*s != '\0')) {
printf("detected recovery request (environment)\n");
want_recovery = 1;
}
s = getenv("install_in_progress");
if ((s != NULL) && (*s != '\0')) {
printf("previous installation has not completed\n");
want_recovery = 1;
}
s = getenv("install_failed");
if ((s != NULL) && (*s != '\0')) {
printf("previous installation has failed\n");
want_recovery = 1;
}
if (want_recovery) {
printf("enforced start of the recovery system\n");
setenv("bootcmd", "run recovery");
}
/*
* boot the recovery system without waiting; boot the
* production system without waiting by default, only
* insert a pause (to provide a chance to get a prompt)
* when GPIO keys were pressed during power on
*/
if (want_recovery)
setenv("bootdelay", "0");
else if (!keys)
setenv("bootdelay", "0");
else
setenv("bootdelay", "2");
/* get the ethernet MAC from I2C EEPROM */
mac_read_from_eeprom();
return 0;
}
/* setup specific IO pad configuration */
static iopin_t ioregs_init[] = {
{ /* LPC CS3 */
offsetof(struct ioctrl512x, io_control_nfc_ce0), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
{ /* LPC CS1 */
offsetof(struct ioctrl512x, io_control_lpc_cs1), 1,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{ /* LPC CS2 */
offsetof(struct ioctrl512x, io_control_lpc_cs2), 1,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{ /* LPC CS4, CS5 */
offsetof(struct ioctrl512x, io_control_pata_ce1), 2,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
{ /* SDHC CLK, CMD, D0, D1, D2, D3 */
offsetof(struct ioctrl512x, io_control_pata_ior), 6,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
{ /* GPIO keyboard */
offsetof(struct ioctrl512x, io_control_pci_ad30), 4,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO DN1 PF, LCD power, DN2 PF */
offsetof(struct ioctrl512x, io_control_pci_ad26), 3,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO reset AS-i */
offsetof(struct ioctrl512x, io_control_pci_ad21), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO reset safety */
offsetof(struct ioctrl512x, io_control_pci_ad19), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO reset netX */
offsetof(struct ioctrl512x, io_control_pci_ad16), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO ma2 en */
offsetof(struct ioctrl512x, io_control_pci_ad15), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO SD CD, SD WP */
offsetof(struct ioctrl512x, io_control_pci_ad08), 2,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* FEC RX DV */
offsetof(struct ioctrl512x, io_control_pci_ad06), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(2),
},
{ /* GPIO AS-i prog, AS-i done, LCD backlight */
offsetof(struct ioctrl512x, io_control_pci_ad05), 3,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO AS-i wdg */
offsetof(struct ioctrl512x, io_control_pci_req2), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO safety wdg */
offsetof(struct ioctrl512x, io_control_pci_req1), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO netX wdg */
offsetof(struct ioctrl512x, io_control_pci_req0), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO IRQ powerfail */
offsetof(struct ioctrl512x, io_control_pci_inta), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO AS-i PWRD */
offsetof(struct ioctrl512x, io_control_pci_frame), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO LED0, LED1 */
offsetof(struct ioctrl512x, io_control_pci_idsel), 2,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* GPIO IRQ AS-i 1, IRQ AS-i 2, IRQ safety */
offsetof(struct ioctrl512x, io_control_pci_irdy), 3,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /* DIU clk */
offsetof(struct ioctrl512x, io_control_spdif_txclk), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(2),
},
{ /* FEC TX ER, CRS */
offsetof(struct ioctrl512x, io_control_spdif_tx), 2,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
{ /* GPIO/GPT */ /* to *NOT* have the EXT IRQ0 float */
offsetof(struct ioctrl512x, io_control_irq0), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
{ /*
* FEC col, tx en, tx clk, txd 0-3, mdc, rx er,
* rdx 3-0, mdio, rx clk
*/
offsetof(struct ioctrl512x, io_control_psc0_0), 15,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
/* optional: make sure PSC3 remains the serial console */
{ /* LPC CS6 */
offsetof(struct ioctrl512x, io_control_psc3_4), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
/* make sure PSC4 remains available for SPI,
*BUT* PSC4_1 is a GPIO kind of SS! */
{ /* enforce drive strength on the SPI pin */
offsetof(struct ioctrl512x, io_control_psc4_0), 5,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{
offsetof(struct ioctrl512x, io_control_psc4_1), 1,
IO_PIN_OVER_FMUX,
IO_PIN_FMUX(3),
},
/* optional: make sure PSC5 remains available for SPI */
{ /* enforce drive strength on the SPI pin */
offsetof(struct ioctrl512x, io_control_psc5_0), 5,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(1),
},
{ /* LPC TSIZ1 */
offsetof(struct ioctrl512x, io_control_psc6_0), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(1) | IO_PIN_DS(2),
},
{ /* DIU hsync */
offsetof(struct ioctrl512x, io_control_psc6_1), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(1),
},
{ /* DIU vsync */
offsetof(struct ioctrl512x, io_control_psc6_4), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(1),
},
{ /* PSC7, part of DIU RGB */
offsetof(struct ioctrl512x, io_control_psc7_0), 2,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(1),
},
{ /* PSC7, safety UART */
offsetof(struct ioctrl512x, io_control_psc7_2), 2,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(0) | IO_PIN_DS(1),
},
{ /* DIU (part of) RGB[] */
offsetof(struct ioctrl512x, io_control_psc8_3), 16,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(1),
},
{ /* DIU data enable */
offsetof(struct ioctrl512x, io_control_psc11_4), 1,
IO_PIN_OVER_FMUX | IO_PIN_OVER_DRVSTR,
IO_PIN_FMUX(2) | IO_PIN_DS(1),
},
/* reduce LPB drive strength for improved EMI */
{ /* LPC OE, LPC RW */
offsetof(struct ioctrl512x, io_control_lpc_oe), 2,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{ /* LPC AX03 through LPC AD00 */
offsetof(struct ioctrl512x, io_control_lpc_ax03), 36,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{ /* LPC CS5 */
offsetof(struct ioctrl512x, io_control_pata_ce2), 1,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{ /* SDHC CLK */
offsetof(struct ioctrl512x, io_control_nfc_wp), 1,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
{ /* SDHC DATA */
offsetof(struct ioctrl512x, io_control_nfc_ale), 4,
IO_PIN_OVER_DRVSTR,
IO_PIN_DS(2),
},
};
int checkboard(void)
{
puts("Board: ifm AC14xx\n");
/* initialize function mux & slew rate IO inter alia on IO Pins */
iopin_initialize_bits(ioregs_init, ARRAY_SIZE(ioregs_init));
return 0;
}
#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP)
int ft_board_setup(void *blob, bd_t *bd)
{
ft_cpu_setup(blob, bd);
return 0;
}
#endif /* defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) */