mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-30 05:48:51 +09:00
![Simon Glass](/assets/img/avatar_default.png)
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>
895 lines
21 KiB
C
895 lines
21 KiB
C
/*
|
|
* Copyright 2009-2012 Freescale Semiconductor, Inc.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <i2c.h>
|
|
#include <netdev.h>
|
|
#include <linux/compiler.h>
|
|
#include <asm/mmu.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/immap_85xx.h>
|
|
#include <asm/fsl_law.h>
|
|
#include <asm/fsl_serdes.h>
|
|
#include <asm/fsl_portals.h>
|
|
#include <asm/fsl_liodn.h>
|
|
#include <fm_eth.h>
|
|
|
|
#include "../common/qixis.h"
|
|
#include "../common/vsc3316_3308.h"
|
|
#include "t4qds.h"
|
|
#include "t4240qds_qixis.h"
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
static int8_t vsc3316_fsm1_tx[8][2] = { {0, 0}, {1, 1}, {6, 6}, {7, 7},
|
|
{8, 8}, {9, 9}, {14, 14}, {15, 15} };
|
|
|
|
static int8_t vsc3316_fsm2_tx[8][2] = { {2, 2}, {3, 3}, {4, 4}, {5, 5},
|
|
{10, 10}, {11, 11}, {12, 12}, {13, 13} };
|
|
|
|
static int8_t vsc3316_fsm1_rx[8][2] = { {2, 12}, {3, 13}, {4, 5}, {5, 4},
|
|
{10, 11}, {11, 10}, {12, 2}, {13, 3} };
|
|
|
|
static int8_t vsc3316_fsm2_rx[8][2] = { {0, 15}, {1, 14}, {6, 7}, {7, 6},
|
|
{8, 9}, {9, 8}, {14, 1}, {15, 0} };
|
|
|
|
int checkboard(void)
|
|
{
|
|
char buf[64];
|
|
u8 sw;
|
|
struct cpu_type *cpu = gd->arch.cpu;
|
|
unsigned int i;
|
|
|
|
printf("Board: %sQDS, ", cpu->name);
|
|
printf("Sys ID: 0x%02x, Sys Ver: 0x%02x, ",
|
|
QIXIS_READ(id), QIXIS_READ(arch));
|
|
|
|
sw = QIXIS_READ(brdcfg[0]);
|
|
sw = (sw & QIXIS_LBMAP_MASK) >> QIXIS_LBMAP_SHIFT;
|
|
|
|
if (sw < 0x8)
|
|
printf("vBank: %d\n", sw);
|
|
else if (sw == 0x8)
|
|
puts("Promjet\n");
|
|
else if (sw == 0x9)
|
|
puts("NAND\n");
|
|
else
|
|
printf("invalid setting of SW%u\n", QIXIS_LBMAP_SWITCH);
|
|
|
|
printf("FPGA: v%d (%s), build %d",
|
|
(int)QIXIS_READ(scver), qixis_read_tag(buf),
|
|
(int)qixis_read_minor());
|
|
/* the timestamp string contains "\n" at the end */
|
|
printf(" on %s", qixis_read_time(buf));
|
|
|
|
/*
|
|
* Display the actual SERDES reference clocks as configured by the
|
|
* dip switches on the board. Note that the SWx registers could
|
|
* technically be set to force the reference clocks to match the
|
|
* values that the SERDES expects (or vice versa). For now, however,
|
|
* we just display both values and hope the user notices when they
|
|
* don't match.
|
|
*/
|
|
puts("SERDES Reference Clocks: ");
|
|
sw = QIXIS_READ(brdcfg[2]);
|
|
for (i = 0; i < MAX_SERDES; i++) {
|
|
static const char * const freq[] = {
|
|
"100", "125", "156.25", "161.1328125"};
|
|
unsigned int clock = (sw >> (6 - 2 * i)) & 3;
|
|
|
|
printf("SERDES%u=%sMHz ", i+1, freq[clock]);
|
|
}
|
|
puts("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int select_i2c_ch_pca9547(u8 ch)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_write(I2C_MUX_PCA_ADDR_PRI, 0, 1, &ch, 1);
|
|
if (ret) {
|
|
puts("PCA: failed to select proper channel\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* read_voltage from sensor on I2C bus
|
|
* We use average of 4 readings, waiting for 532us befor another reading
|
|
*/
|
|
#define NUM_READINGS 4 /* prefer to be power of 2 for efficiency */
|
|
#define WAIT_FOR_ADC 532 /* wait for 532 microseconds for ADC */
|
|
|
|
static inline int read_voltage(void)
|
|
{
|
|
int i, ret, voltage_read = 0;
|
|
u16 vol_mon;
|
|
|
|
for (i = 0; i < NUM_READINGS; i++) {
|
|
ret = i2c_read(I2C_VOL_MONITOR_ADDR,
|
|
I2C_VOL_MONITOR_BUS_V_OFFSET, 1, (void *)&vol_mon, 2);
|
|
if (ret) {
|
|
printf("VID: failed to read core voltage\n");
|
|
return ret;
|
|
}
|
|
if (vol_mon & I2C_VOL_MONITOR_BUS_V_OVF) {
|
|
printf("VID: Core voltage sensor error\n");
|
|
return -1;
|
|
}
|
|
debug("VID: bus voltage reads 0x%04x\n", vol_mon);
|
|
/* LSB = 4mv */
|
|
voltage_read += (vol_mon >> I2C_VOL_MONITOR_BUS_V_SHIFT) * 4;
|
|
udelay(WAIT_FOR_ADC);
|
|
}
|
|
/* calculate the average */
|
|
voltage_read /= NUM_READINGS;
|
|
|
|
return voltage_read;
|
|
}
|
|
|
|
/*
|
|
* We need to calculate how long before the voltage starts to drop or increase
|
|
* It returns with the loop count. Each loop takes several readings (532us)
|
|
*/
|
|
static inline int wait_for_voltage_change(int vdd_last)
|
|
{
|
|
int timeout, vdd_current;
|
|
|
|
vdd_current = read_voltage();
|
|
/* wait until voltage starts to drop */
|
|
for (timeout = 0; abs(vdd_last - vdd_current) <= 4 &&
|
|
timeout < 100; timeout++) {
|
|
vdd_current = read_voltage();
|
|
}
|
|
if (timeout >= 100) {
|
|
printf("VID: Voltage adjustment timeout\n");
|
|
return -1;
|
|
}
|
|
return timeout;
|
|
}
|
|
|
|
/*
|
|
* argument 'wait' is the time we know the voltage difference can be measured
|
|
* this function keeps reading the voltage until it is stable
|
|
*/
|
|
static inline int wait_for_voltage_stable(int wait)
|
|
{
|
|
int timeout, vdd_current, vdd_last;
|
|
|
|
vdd_last = read_voltage();
|
|
udelay(wait * NUM_READINGS * WAIT_FOR_ADC);
|
|
/* wait until voltage is stable */
|
|
vdd_current = read_voltage();
|
|
for (timeout = 0; abs(vdd_last - vdd_current) >= 4 &&
|
|
timeout < 100; timeout++) {
|
|
vdd_last = vdd_current;
|
|
udelay(wait * NUM_READINGS * WAIT_FOR_ADC);
|
|
vdd_current = read_voltage();
|
|
}
|
|
if (timeout >= 100) {
|
|
printf("VID: Voltage adjustment timeout\n");
|
|
return -1;
|
|
}
|
|
|
|
return vdd_current;
|
|
}
|
|
|
|
static inline int set_voltage(u8 vid)
|
|
{
|
|
int wait, vdd_last;
|
|
|
|
vdd_last = read_voltage();
|
|
QIXIS_WRITE(brdcfg[6], vid);
|
|
wait = wait_for_voltage_change(vdd_last);
|
|
if (wait < 0)
|
|
return -1;
|
|
debug("VID: Waited %d us\n", wait * NUM_READINGS * WAIT_FOR_ADC);
|
|
wait = wait ? wait : 1;
|
|
|
|
vdd_last = wait_for_voltage_stable(wait);
|
|
if (vdd_last < 0)
|
|
return -1;
|
|
debug("VID: Current voltage is %d mV\n", vdd_last);
|
|
|
|
return vdd_last;
|
|
}
|
|
|
|
|
|
static int adjust_vdd(ulong vdd_override)
|
|
{
|
|
int re_enable = disable_interrupts();
|
|
ccsr_gur_t __iomem *gur =
|
|
(void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
|
|
u32 fusesr;
|
|
u8 vid, vid_current;
|
|
int vdd_target, vdd_current, vdd_last;
|
|
int ret;
|
|
unsigned long vdd_string_override;
|
|
char *vdd_string;
|
|
static const uint16_t vdd[32] = {
|
|
0, /* unused */
|
|
9875, /* 0.9875V */
|
|
9750,
|
|
9625,
|
|
9500,
|
|
9375,
|
|
9250,
|
|
9125,
|
|
9000,
|
|
8875,
|
|
8750,
|
|
8625,
|
|
8500,
|
|
8375,
|
|
8250,
|
|
8125,
|
|
10000, /* 1.0000V */
|
|
10125,
|
|
10250,
|
|
10375,
|
|
10500,
|
|
10625,
|
|
10750,
|
|
10875,
|
|
11000,
|
|
0, /* reserved */
|
|
};
|
|
struct vdd_drive {
|
|
u8 vid;
|
|
unsigned voltage;
|
|
};
|
|
|
|
ret = select_i2c_ch_pca9547(I2C_MUX_CH_VOL_MONITOR);
|
|
if (ret) {
|
|
debug("VID: I2c failed to switch channel\n");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
|
|
/* get the voltage ID from fuse status register */
|
|
fusesr = in_be32(&gur->dcfg_fusesr);
|
|
vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_VID_SHIFT) &
|
|
FSL_CORENET_DCFG_FUSESR_VID_MASK;
|
|
if (vid == FSL_CORENET_DCFG_FUSESR_VID_MASK) {
|
|
vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_ALTVID_SHIFT) &
|
|
FSL_CORENET_DCFG_FUSESR_ALTVID_MASK;
|
|
}
|
|
vdd_target = vdd[vid];
|
|
|
|
/* check override variable for overriding VDD */
|
|
vdd_string = getenv("t4240qds_vdd_mv");
|
|
if (vdd_override == 0 && vdd_string &&
|
|
!strict_strtoul(vdd_string, 10, &vdd_string_override))
|
|
vdd_override = vdd_string_override;
|
|
if (vdd_override >= 819 && vdd_override <= 1212) {
|
|
vdd_target = vdd_override * 10; /* convert to 1/10 mV */
|
|
debug("VDD override is %lu\n", vdd_override);
|
|
} else if (vdd_override != 0) {
|
|
printf("Invalid value.\n");
|
|
}
|
|
|
|
if (vdd_target == 0) {
|
|
debug("VID: VID not used\n");
|
|
ret = 0;
|
|
goto exit;
|
|
} else {
|
|
/* round up and divice by 10 to get a value in mV */
|
|
vdd_target = DIV_ROUND_UP(vdd_target, 10);
|
|
debug("VID: vid = %d mV\n", vdd_target);
|
|
}
|
|
|
|
/*
|
|
* Check current board VID setting
|
|
* Voltage regulator support output to 6.250mv step
|
|
* The highes voltage allowed for this board is (vid=0x40) 1.21250V
|
|
* the lowest is (vid=0x7f) 0.81875V
|
|
*/
|
|
vid_current = QIXIS_READ(brdcfg[6]);
|
|
vdd_current = 121250 - (vid_current - 0x40) * 625;
|
|
debug("VID: Current vid setting is (0x%x) %d mV\n",
|
|
vid_current, vdd_current/100);
|
|
|
|
/*
|
|
* Read voltage monitor to check real voltage.
|
|
* Voltage monitor LSB is 4mv.
|
|
*/
|
|
vdd_last = read_voltage();
|
|
if (vdd_last < 0) {
|
|
printf("VID: Could not read voltage sensor abort VID adjustment\n");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
debug("VID: Core voltage is at %d mV\n", vdd_last);
|
|
/*
|
|
* Adjust voltage to at or 8mV above target.
|
|
* Each step of adjustment is 6.25mV.
|
|
* Stepping down too fast may cause over current.
|
|
*/
|
|
while (vdd_last > 0 && vid_current < 0x80 &&
|
|
vdd_last > (vdd_target + 8)) {
|
|
vid_current++;
|
|
vdd_last = set_voltage(vid_current);
|
|
}
|
|
/*
|
|
* Check if we need to step up
|
|
* This happens when board voltage switch was set too low
|
|
*/
|
|
while (vdd_last > 0 && vid_current >= 0x40 &&
|
|
vdd_last < vdd_target + 2) {
|
|
vid_current--;
|
|
vdd_last = set_voltage(vid_current);
|
|
}
|
|
if (vdd_last > 0)
|
|
printf("VID: Core voltage %d mV\n", vdd_last);
|
|
else
|
|
ret = -1;
|
|
|
|
exit:
|
|
if (re_enable)
|
|
enable_interrupts();
|
|
return ret;
|
|
}
|
|
|
|
/* Configure Crossbar switches for Front-Side SerDes Ports */
|
|
int config_frontside_crossbar_vsc3316(void)
|
|
{
|
|
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
|
|
u32 srds_prtcl_s1, srds_prtcl_s2;
|
|
int ret;
|
|
|
|
ret = select_i2c_ch_pca9547(I2C_MUX_CH_VSC3316_FS);
|
|
if (ret)
|
|
return ret;
|
|
|
|
srds_prtcl_s1 = in_be32(&gur->rcwsr[4]) &
|
|
FSL_CORENET2_RCWSR4_SRDS1_PRTCL;
|
|
srds_prtcl_s1 >>= FSL_CORENET2_RCWSR4_SRDS1_PRTCL_SHIFT;
|
|
switch (srds_prtcl_s1) {
|
|
case 37:
|
|
case 38:
|
|
/* swap first lane and third lane on slot1 */
|
|
vsc3316_fsm1_tx[0][1] = 14;
|
|
vsc3316_fsm1_tx[6][1] = 0;
|
|
vsc3316_fsm1_rx[1][1] = 2;
|
|
vsc3316_fsm1_rx[6][1] = 13;
|
|
case 39:
|
|
case 40:
|
|
case 45:
|
|
case 46:
|
|
case 47:
|
|
case 48:
|
|
/* swap first lane and third lane on slot2 */
|
|
vsc3316_fsm1_tx[2][1] = 8;
|
|
vsc3316_fsm1_tx[4][1] = 6;
|
|
vsc3316_fsm1_rx[2][1] = 10;
|
|
vsc3316_fsm1_rx[5][1] = 5;
|
|
default:
|
|
ret = vsc3316_config(VSC3316_FSM_TX_ADDR, vsc3316_fsm1_tx, 8);
|
|
if (ret)
|
|
return ret;
|
|
ret = vsc3316_config(VSC3316_FSM_RX_ADDR, vsc3316_fsm1_rx, 8);
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
}
|
|
|
|
srds_prtcl_s2 = in_be32(&gur->rcwsr[4]) &
|
|
FSL_CORENET2_RCWSR4_SRDS2_PRTCL;
|
|
srds_prtcl_s2 >>= FSL_CORENET2_RCWSR4_SRDS2_PRTCL_SHIFT;
|
|
switch (srds_prtcl_s2) {
|
|
case 37:
|
|
case 38:
|
|
/* swap first lane and third lane on slot3 */
|
|
vsc3316_fsm2_tx[2][1] = 11;
|
|
vsc3316_fsm2_tx[5][1] = 4;
|
|
vsc3316_fsm2_rx[2][1] = 9;
|
|
vsc3316_fsm2_rx[4][1] = 7;
|
|
case 39:
|
|
case 40:
|
|
case 45:
|
|
case 46:
|
|
case 47:
|
|
case 48:
|
|
case 49:
|
|
case 50:
|
|
case 51:
|
|
case 52:
|
|
case 53:
|
|
case 54:
|
|
/* swap first lane and third lane on slot4 */
|
|
vsc3316_fsm2_tx[6][1] = 3;
|
|
vsc3316_fsm2_tx[1][1] = 12;
|
|
vsc3316_fsm2_rx[0][1] = 1;
|
|
vsc3316_fsm2_rx[6][1] = 15;
|
|
default:
|
|
ret = vsc3316_config(VSC3316_FSM_TX_ADDR, vsc3316_fsm2_tx, 8);
|
|
if (ret)
|
|
return ret;
|
|
ret = vsc3316_config(VSC3316_FSM_RX_ADDR, vsc3316_fsm2_rx, 8);
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_backside_crossbar_mux(void)
|
|
{
|
|
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
|
|
u32 srds_prtcl_s3, srds_prtcl_s4;
|
|
u8 brdcfg;
|
|
|
|
srds_prtcl_s3 = in_be32(&gur->rcwsr[4]) &
|
|
FSL_CORENET2_RCWSR4_SRDS3_PRTCL;
|
|
srds_prtcl_s3 >>= FSL_CORENET2_RCWSR4_SRDS3_PRTCL_SHIFT;
|
|
switch (srds_prtcl_s3) {
|
|
case 0:
|
|
/* SerDes3 is not enabled */
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 9:
|
|
case 10:
|
|
/* SD3(0:7) => SLOT5(0:7) */
|
|
brdcfg = QIXIS_READ(brdcfg[12]);
|
|
brdcfg &= ~BRDCFG12_SD3MX_MASK;
|
|
brdcfg |= BRDCFG12_SD3MX_SLOT5;
|
|
QIXIS_WRITE(brdcfg[12], brdcfg);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 19:
|
|
case 20:
|
|
/* SD3(4:7) => SLOT6(0:3) */
|
|
brdcfg = QIXIS_READ(brdcfg[12]);
|
|
brdcfg &= ~BRDCFG12_SD3MX_MASK;
|
|
brdcfg |= BRDCFG12_SD3MX_SLOT6;
|
|
QIXIS_WRITE(brdcfg[12], brdcfg);
|
|
break;
|
|
default:
|
|
printf("WARNING: unsupported for SerDes3 Protocol %d\n",
|
|
srds_prtcl_s3);
|
|
return -1;
|
|
}
|
|
|
|
srds_prtcl_s4 = in_be32(&gur->rcwsr[4]) &
|
|
FSL_CORENET2_RCWSR4_SRDS4_PRTCL;
|
|
srds_prtcl_s4 >>= FSL_CORENET2_RCWSR4_SRDS4_PRTCL_SHIFT;
|
|
switch (srds_prtcl_s4) {
|
|
case 0:
|
|
/* SerDes4 is not enabled */
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
/* 10b, SD4(0:7) => SLOT7(0:7) */
|
|
brdcfg = QIXIS_READ(brdcfg[12]);
|
|
brdcfg &= ~BRDCFG12_SD4MX_MASK;
|
|
brdcfg |= BRDCFG12_SD4MX_SLOT7;
|
|
QIXIS_WRITE(brdcfg[12], brdcfg);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
/* x1b, SD4(4:7) => SLOT8(0:3) */
|
|
brdcfg = QIXIS_READ(brdcfg[12]);
|
|
brdcfg &= ~BRDCFG12_SD4MX_MASK;
|
|
brdcfg |= BRDCFG12_SD4MX_SLOT8;
|
|
QIXIS_WRITE(brdcfg[12], brdcfg);
|
|
break;
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 18:
|
|
/* 00b, SD4(4:5) => AURORA, SD4(6:7) => SATA */
|
|
brdcfg = QIXIS_READ(brdcfg[12]);
|
|
brdcfg &= ~BRDCFG12_SD4MX_MASK;
|
|
brdcfg |= BRDCFG12_SD4MX_AURO_SATA;
|
|
QIXIS_WRITE(brdcfg[12], brdcfg);
|
|
break;
|
|
default:
|
|
printf("WARNING: unsupported for SerDes4 Protocol %d\n",
|
|
srds_prtcl_s4);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int board_early_init_r(void)
|
|
{
|
|
const unsigned int flashbase = CONFIG_SYS_FLASH_BASE;
|
|
int flash_esel = find_tlb_idx((void *)flashbase, 1);
|
|
|
|
/*
|
|
* Remap Boot flash + PROMJET region to caching-inhibited
|
|
* so that flash can be erased properly.
|
|
*/
|
|
|
|
/* Flush d-cache and invalidate i-cache of any FLASH data */
|
|
flush_dcache();
|
|
invalidate_icache();
|
|
|
|
if (flash_esel == -1) {
|
|
/* very unlikely unless something is messed up */
|
|
puts("Error: Could not find TLB for FLASH BASE\n");
|
|
flash_esel = 2; /* give our best effort to continue */
|
|
} else {
|
|
/* invalidate existing TLB entry for flash + promjet */
|
|
disable_tlb(flash_esel);
|
|
}
|
|
|
|
set_tlb(1, flashbase, CONFIG_SYS_FLASH_BASE_PHYS,
|
|
MAS3_SX|MAS3_SW|MAS3_SR, MAS2_I|MAS2_G,
|
|
0, flash_esel, BOOKE_PAGESZ_256M, 1);
|
|
|
|
set_liodns();
|
|
#ifdef CONFIG_SYS_DPAA_QBMAN
|
|
setup_portals();
|
|
#endif
|
|
|
|
/* Disable remote I2C connection to qixis fpga */
|
|
QIXIS_WRITE(brdcfg[5], QIXIS_READ(brdcfg[5]) & ~BRDCFG5_IRE);
|
|
|
|
/*
|
|
* Adjust core voltage according to voltage ID
|
|
* This function changes I2C mux to channel 2.
|
|
*/
|
|
if (adjust_vdd(0))
|
|
printf("Warning: Adjusting core voltage failed.\n");
|
|
|
|
/* Configure board SERDES ports crossbar */
|
|
config_frontside_crossbar_vsc3316();
|
|
config_backside_crossbar_mux();
|
|
select_i2c_ch_pca9547(I2C_MUX_CH_DEFAULT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned long get_board_sys_clk(void)
|
|
{
|
|
u8 sysclk_conf = QIXIS_READ(brdcfg[1]);
|
|
#ifdef CONFIG_FSL_QIXIS_CLOCK_MEASUREMENT
|
|
/* use accurate clock measurement */
|
|
int freq = QIXIS_READ(clk_freq[0]) << 8 | QIXIS_READ(clk_freq[1]);
|
|
int base = QIXIS_READ(clk_base[0]) << 8 | QIXIS_READ(clk_base[1]);
|
|
u32 val;
|
|
|
|
val = freq * base;
|
|
if (val) {
|
|
debug("SYS Clock measurement is: %d\n", val);
|
|
return val;
|
|
} else {
|
|
printf("Warning: SYS clock measurement is invalid, using value from brdcfg1.\n");
|
|
}
|
|
#endif
|
|
|
|
switch (sysclk_conf & 0x0F) {
|
|
case QIXIS_SYSCLK_83:
|
|
return 83333333;
|
|
case QIXIS_SYSCLK_100:
|
|
return 100000000;
|
|
case QIXIS_SYSCLK_125:
|
|
return 125000000;
|
|
case QIXIS_SYSCLK_133:
|
|
return 133333333;
|
|
case QIXIS_SYSCLK_150:
|
|
return 150000000;
|
|
case QIXIS_SYSCLK_160:
|
|
return 160000000;
|
|
case QIXIS_SYSCLK_166:
|
|
return 166666666;
|
|
}
|
|
return 66666666;
|
|
}
|
|
|
|
unsigned long get_board_ddr_clk(void)
|
|
{
|
|
u8 ddrclk_conf = QIXIS_READ(brdcfg[1]);
|
|
#ifdef CONFIG_FSL_QIXIS_CLOCK_MEASUREMENT
|
|
/* use accurate clock measurement */
|
|
int freq = QIXIS_READ(clk_freq[2]) << 8 | QIXIS_READ(clk_freq[3]);
|
|
int base = QIXIS_READ(clk_base[0]) << 8 | QIXIS_READ(clk_base[1]);
|
|
u32 val;
|
|
|
|
val = freq * base;
|
|
if (val) {
|
|
debug("DDR Clock measurement is: %d\n", val);
|
|
return val;
|
|
} else {
|
|
printf("Warning: DDR clock measurement is invalid, using value from brdcfg1.\n");
|
|
}
|
|
#endif
|
|
|
|
switch ((ddrclk_conf & 0x30) >> 4) {
|
|
case QIXIS_DDRCLK_100:
|
|
return 100000000;
|
|
case QIXIS_DDRCLK_125:
|
|
return 125000000;
|
|
case QIXIS_DDRCLK_133:
|
|
return 133333333;
|
|
}
|
|
return 66666666;
|
|
}
|
|
|
|
int misc_init_r(void)
|
|
{
|
|
u8 sw;
|
|
void *srds_base = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
|
|
serdes_corenet_t *srds_regs;
|
|
u32 actual[MAX_SERDES];
|
|
u32 pllcr0, expected;
|
|
unsigned int i;
|
|
|
|
sw = QIXIS_READ(brdcfg[2]);
|
|
for (i = 0; i < MAX_SERDES; i++) {
|
|
unsigned int clock = (sw >> (6 - 2 * i)) & 3;
|
|
switch (clock) {
|
|
case 0:
|
|
actual[i] = SRDS_PLLCR0_RFCK_SEL_100;
|
|
break;
|
|
case 1:
|
|
actual[i] = SRDS_PLLCR0_RFCK_SEL_125;
|
|
break;
|
|
case 2:
|
|
actual[i] = SRDS_PLLCR0_RFCK_SEL_156_25;
|
|
break;
|
|
case 3:
|
|
actual[i] = SRDS_PLLCR0_RFCK_SEL_161_13;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_SERDES; i++) {
|
|
srds_regs = srds_base + i * 0x1000;
|
|
pllcr0 = srds_regs->bank[0].pllcr0;
|
|
expected = pllcr0 & SRDS_PLLCR0_RFCK_SEL_MASK;
|
|
if (expected != actual[i]) {
|
|
printf("Warning: SERDES%u expects reference clock %sMHz, but actual is %sMHz\n",
|
|
i + 1, serdes_clock_to_string(expected),
|
|
serdes_clock_to_string(actual[i]));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ft_board_setup(void *blob, bd_t *bd)
|
|
{
|
|
phys_addr_t base;
|
|
phys_size_t size;
|
|
|
|
ft_cpu_setup(blob, bd);
|
|
|
|
base = getenv_bootm_low();
|
|
size = getenv_bootm_size();
|
|
|
|
fdt_fixup_memory(blob, (u64)base, (u64)size);
|
|
|
|
#ifdef CONFIG_PCI
|
|
pci_of_setup(blob, bd);
|
|
#endif
|
|
|
|
fdt_fixup_liodn(blob);
|
|
fdt_fixup_dr_usb(blob, bd);
|
|
|
|
#ifdef CONFIG_SYS_DPAA_FMAN
|
|
fdt_fixup_fman_ethernet(blob);
|
|
fdt_fixup_board_enet(blob);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function is called by bdinfo to print detail board information.
|
|
* As an exmaple for future board, we organize the messages into
|
|
* several sections. If applicable, the message is in the format of
|
|
* <name> = <value>
|
|
* It should aligned with normal output of bdinfo command.
|
|
*
|
|
* Voltage: Core, DDR and another configurable voltages
|
|
* Clock : Critical clocks which are not printed already
|
|
* RCW : RCW source if not printed already
|
|
* Misc : Other important information not in above catagories
|
|
*/
|
|
void board_detail(void)
|
|
{
|
|
int i;
|
|
u8 brdcfg[16], dutcfg[16], rst_ctl;
|
|
int vdd, rcwsrc;
|
|
static const char * const clk[] = {"66.67", "100", "125", "133.33"};
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
brdcfg[i] = qixis_read(offsetof(struct qixis, brdcfg[0]) + i);
|
|
dutcfg[i] = qixis_read(offsetof(struct qixis, dutcfg[0]) + i);
|
|
}
|
|
|
|
/* Voltage secion */
|
|
if (!select_i2c_ch_pca9547(I2C_MUX_CH_VOL_MONITOR)) {
|
|
vdd = read_voltage();
|
|
if (vdd > 0)
|
|
printf("Core voltage= %d mV\n", vdd);
|
|
select_i2c_ch_pca9547(I2C_MUX_CH_DEFAULT);
|
|
}
|
|
|
|
printf("XVDD = 1.%d V\n", ((brdcfg[8] & 0xf) - 4) * 5 + 25);
|
|
|
|
/* clock section */
|
|
printf("SYSCLK = %s MHz\nDDRCLK = %s MHz\n",
|
|
clk[(brdcfg[11] >> 2) & 0x3], clk[brdcfg[11] & 3]);
|
|
|
|
/* RCW section */
|
|
rcwsrc = (dutcfg[0] << 1) + (dutcfg[1] & 1);
|
|
puts("RCW source = ");
|
|
switch (rcwsrc) {
|
|
case 0x017:
|
|
case 0x01f:
|
|
puts("8-bit NOR\n");
|
|
break;
|
|
case 0x027:
|
|
case 0x02F:
|
|
puts("16-bit NOR\n");
|
|
break;
|
|
case 0x040:
|
|
puts("SDHC/eMMC\n");
|
|
break;
|
|
case 0x044:
|
|
puts("SPI 16-bit addressing\n");
|
|
break;
|
|
case 0x045:
|
|
puts("SPI 24-bit addressing\n");
|
|
break;
|
|
case 0x048:
|
|
puts("I2C normal addressing\n");
|
|
break;
|
|
case 0x049:
|
|
puts("I2C extended addressing\n");
|
|
break;
|
|
case 0x108:
|
|
case 0x109:
|
|
case 0x10a:
|
|
case 0x10b:
|
|
puts("8-bit NAND, 2KB\n");
|
|
break;
|
|
default:
|
|
if ((rcwsrc >= 0x080) && (rcwsrc <= 0x09f))
|
|
puts("Hard-coded RCW\n");
|
|
else if ((rcwsrc >= 0x110) && (rcwsrc <= 0x11f))
|
|
puts("8-bit NAND, 4KB\n");
|
|
else
|
|
puts("unknown\n");
|
|
break;
|
|
}
|
|
|
|
/* Misc section */
|
|
rst_ctl = QIXIS_READ(rst_ctl);
|
|
puts("HRESET_REQ = ");
|
|
switch (rst_ctl & 0x30) {
|
|
case 0x00:
|
|
puts("Ignored\n");
|
|
break;
|
|
case 0x10:
|
|
puts("Assert HRESET\n");
|
|
break;
|
|
case 0x30:
|
|
puts("Reset system\n");
|
|
break;
|
|
default:
|
|
puts("N/A\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reverse engineering switch settings.
|
|
* Some bits cannot be figured out. They will be displayed as
|
|
* underscore in binary format. mask[] has those bits.
|
|
* Some bits are calculated differently than the actual switches
|
|
* if booting with overriding by FPGA.
|
|
*/
|
|
void qixis_dump_switch(void)
|
|
{
|
|
int i;
|
|
u8 sw[9];
|
|
|
|
/*
|
|
* Any bit with 1 means that bit cannot be reverse engineered.
|
|
* It will be displayed as _ in binary format.
|
|
*/
|
|
static const u8 mask[] = {0, 0, 0, 0, 0, 0x1, 0xcf, 0x3f, 0x1f};
|
|
char buf[10];
|
|
u8 brdcfg[16], dutcfg[16];
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
brdcfg[i] = qixis_read(offsetof(struct qixis, brdcfg[0]) + i);
|
|
dutcfg[i] = qixis_read(offsetof(struct qixis, dutcfg[0]) + i);
|
|
}
|
|
|
|
sw[0] = dutcfg[0];
|
|
sw[1] = (dutcfg[1] << 0x07) |
|
|
((dutcfg[12] & 0xC0) >> 1) |
|
|
((dutcfg[11] & 0xE0) >> 3) |
|
|
((dutcfg[6] & 0x80) >> 6) |
|
|
((dutcfg[1] & 0x80) >> 7);
|
|
sw[2] = ((brdcfg[1] & 0x0f) << 4) |
|
|
((brdcfg[1] & 0x30) >> 2) |
|
|
((brdcfg[1] & 0x40) >> 5) |
|
|
((brdcfg[1] & 0x80) >> 7);
|
|
sw[3] = brdcfg[2];
|
|
sw[4] = ((dutcfg[2] & 0x01) << 7) |
|
|
((dutcfg[2] & 0x06) << 4) |
|
|
((~QIXIS_READ(present)) & 0x10) |
|
|
((brdcfg[3] & 0x80) >> 4) |
|
|
((brdcfg[3] & 0x01) << 2) |
|
|
((brdcfg[6] == 0x62) ? 3 :
|
|
((brdcfg[6] == 0x5a) ? 2 :
|
|
((brdcfg[6] == 0x5e) ? 1 : 0)));
|
|
sw[5] = ((brdcfg[0] & 0x0f) << 4) |
|
|
((QIXIS_READ(rst_ctl) & 0x30) >> 2) |
|
|
((brdcfg[0] & 0x40) >> 5);
|
|
sw[6] = (brdcfg[11] & 0x20) |
|
|
((brdcfg[5] & 0x02) << 3);
|
|
sw[7] = (((~QIXIS_READ(rst_ctl)) & 0x40) << 1) |
|
|
((brdcfg[5] & 0x10) << 2);
|
|
sw[8] = ((brdcfg[12] & 0x08) << 4) |
|
|
((brdcfg[12] & 0x03) << 5);
|
|
|
|
puts("DIP switch (reverse-engineering)\n");
|
|
for (i = 0; i < 9; i++) {
|
|
printf("SW%d = 0b%s (0x%02x)\n",
|
|
i + 1, byte_to_binary_mask(sw[i], mask[i], buf), sw[i]);
|
|
}
|
|
}
|
|
|
|
static int do_vdd_adjust(cmd_tbl_t *cmdtp,
|
|
int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
ulong override;
|
|
|
|
if (argc < 2)
|
|
return CMD_RET_USAGE;
|
|
if (!strict_strtoul(argv[1], 10, &override))
|
|
adjust_vdd(override); /* the value is checked by callee */
|
|
else
|
|
return CMD_RET_USAGE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
vdd_override, 2, 0, do_vdd_adjust,
|
|
"Override VDD",
|
|
"- override with the voltage specified in mV, eg. 1050"
|
|
);
|