summaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorJimmy Zhang <jimmzhang@nvidia.com>2011-06-08 11:37:34 -0700
committerSimon Glass <sjg@chromium.org>2011-08-29 10:39:18 -0700
commita7351becb2472a4239de41c5d405f8ea06ef2b35 (patch)
tree1803737ced62a5f625bbf7f9441748ad58a4988e /board
parent08261cbb4ad46fd4e507a116807bdb22856239b2 (diff)
Set vdd_core and vdd_cpu to high
At cold boot, the default voltage supplied by pmu is not high enough to support emc to run at its highest clock frequency. The code added here is to update the default vdd_core and vdd_cpu to higher values. BUG=none TEST=do cold and warm reboot and check the pmu output thru i2c commands i2c commands used to check vdd_core and vdd_cpu: Tegra2 (SeaBoard) # i2c dev 0 Setting bus to 0 Tegra2 (SeaBoard) # i2c md 34 20 0020: 00 00 00 10 10 07 17 17 07 13 13 00 00 00 00 13 Change-Id: Idd31a3f0ca39b31f142c4e96bf5a9b1b594f0d88 Reviewed-on: http://gerrit.chromium.org/gerrit/2257 Tested-by: Jimmy Zhang <jimmzhang@nvidia.com> Reviewed-by: Tom Warren <twarren@nvidia.com>
Diffstat (limited to 'board')
-rw-r--r--board/nvidia/common/Makefile1
-rw-r--r--board/nvidia/common/board.c26
-rw-r--r--board/nvidia/common/board.h2
-rw-r--r--board/nvidia/common/pmu.c297
4 files changed, 326 insertions, 0 deletions
diff --git a/board/nvidia/common/Makefile b/board/nvidia/common/Makefile
index d7aee635a6..9d4831bcdb 100644
--- a/board/nvidia/common/Makefile
+++ b/board/nvidia/common/Makefile
@@ -29,6 +29,7 @@ COBJS-y += board.o
COBJS-$(CONFIG_USB_EHCI_TEGRA) += usb.o
COBJS-$(CONFIG_SPI_UART_SWITCH) += uart-spi-fix.o
COBJS-$(CONFIG_TEGRA2_KEYBOARD) += generic_kbc.o
+COBJS-$(CONFIG_TEGRA2_I2C) += pmu.o
COBJS := $(COBJS-y)
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index a770099f29..4e2c080c03 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -35,6 +35,7 @@
#include <asm/arch/uart.h>
#include <asm/arch/usb.h>
#include <asm/arch/pmc.h>
+#include <asm/arch/fuse.h>
#include <spi.h>
#include <fdt_decode.h>
#include <i2c.h>
@@ -212,6 +213,8 @@ int board_init(void)
power_det_init();
#ifdef CONFIG_TEGRA2_I2C
i2c_init_board();
+
+ pmu_set_nominal();
#endif
return 0;
@@ -287,3 +290,26 @@ int board_mmc_init(bd_t *bd)
return 0;
}
#endif
+
+int tegra_get_chip_type(void)
+{
+ uint tegra_sku_id;
+
+ struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE;
+
+ tegra_sku_id = readl(&fuse->sku_info) & 0xff;
+
+ switch (tegra_sku_id) {
+ case SKU_ID_T20:
+ return TEGRA_SOC_T20;
+ case SKU_ID_T25SE:
+ case SKU_ID_AP25:
+ case SKU_ID_T25:
+ case SKU_ID_AP25E:
+ case SKU_ID_T25E:
+ return TEGRA_SOC_T25;
+ default:
+ /* unknown sku id */
+ return TEGRA_SOC_UNKNOWN;
+ }
+}
diff --git a/board/nvidia/common/board.h b/board/nvidia/common/board.h
index ca7eac5434..179fa56dc0 100644
--- a/board/nvidia/common/board.h
+++ b/board/nvidia/common/board.h
@@ -30,5 +30,7 @@ void gpio_early_init_uart(const void *blob);
void gpio_config_mmc(void);
int tegra2_mmc_init(const void *blob);
void lcd_early_init(const void *blob);
+int tegra_get_chip_type(void);
+int pmu_set_nominal(void);
#endif /* BOARD_H */
diff --git a/board/nvidia/common/pmu.c b/board/nvidia/common/pmu.c
new file mode 100644
index 0000000000..0c0f4f84b9
--- /dev/null
+++ b/board/nvidia/common/pmu.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/bitfield.h>
+#include <asm/arch/tegra2.h>
+#include <asm/arch/sys_proto.h>
+
+#include <asm/arch/pmu.h>
+#include <asm/arch/pmc.h>
+#include <i2c.h>
+
+struct vdd_settings {
+ int data;
+ int nominal;
+};
+
+enum vdd_type {
+ core = 0,
+ cpu = 1,
+};
+
+static struct vdd_settings vdd_current[] = {
+ /* vdd core */
+ {.data = 0,
+ .nominal = 0,
+ },
+
+ /* vdd cpu */
+ {.data = 0,
+ .nominal = 0,
+ },
+};
+
+static int vdd_init_nominal_table(void)
+{
+ /* by default, the table has been filled with T25 settings */
+ switch (tegra_get_chip_type()) {
+ case TEGRA_SOC_T20:
+ vdd_current[core].nominal = VDD_CORE_NOMINAL_T20;
+ vdd_current[cpu].nominal = VDD_CPU_NOMINAL_T20;
+ break;
+ case TEGRA_SOC_T25:
+ vdd_current[core].nominal = VDD_CORE_NOMINAL_T25;
+ vdd_current[cpu].nominal = VDD_CPU_NOMINAL_T25;
+ break;
+ default:
+ /* unknown chip type */
+ return -1;
+ }
+ return 0;
+}
+
+#define MAX_I2C_RETRY 3
+static int pmu_read(int reg)
+{
+ int i;
+ uchar data;
+
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (!i2c_read(PMU_I2C_ADDRESS, reg, 0, &data, 1))
+ return (int)data;
+
+ /* i2c access failed, retry */
+ udelay(100);
+ }
+
+ return -1;
+}
+
+static int pmu_write(int reg, uchar *data, uint len)
+{
+ int i;
+
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (!i2c_write(PMU_I2C_ADDRESS, reg, 0, data, len))
+ return 0;
+
+ /* i2c access failed, retry */
+ udelay(100);
+ }
+
+ return -1;
+}
+
+/* get current vdd_core and vdd_cpu */
+static int tegra2_get_voltage(void)
+{
+ int reg;
+ int data1, data2;
+
+ /*
+ * Each vdd has two supply sources, ie, v1 and v2.
+ * The supply control reg1 and reg2 determine the current selection.
+ */
+ /* get supply control reg1 and reg2 */
+ if ((data1 = pmu_read(PMU_SUPPLY_CONTROL_REG1)) == -1)
+ return -1;
+
+ if ((data2 = pmu_read(PMU_SUPPLY_CONTROL_REG2)) == -1)
+ return -1;
+
+ reg = PMU_CORE_VOLTAGE_REG; /* set default to v1 */
+ if ((data1 | data2) & VDD_CORE_SUPPLY2_SEL)
+ reg++; /* v2 is selected*/
+
+ /* get vdd_core */
+ if ((vdd_current[core].data = pmu_read(reg)) == -1)
+ return -1;
+
+ reg = PMU_CPU_VOLTAGE_REG; /* set default to v1 */
+ if ((data1 | data2) & VDD_CPU_SUPPLY2_SEL)
+ reg++; /* v2 is selected*/
+
+ /* get vdd_cpu */
+ if ((vdd_current[cpu].data = pmu_read(reg)) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int tegra2_set_voltage(int reg, int data, int control)
+{
+ uchar data_buffer[3];
+ uchar control_bit = (uchar)control;
+
+ /*
+ * only one supply is needed in u-boot. set both v1 and v2 to
+ * same value.
+ *
+ * when both v1 and v2 are set to same value, we just need to set
+ * control1 reg to trigger the supply selection.
+ */
+ data_buffer[0] = data_buffer[1] = (uchar)data;
+ data_buffer[2] = VDD_TRANSITION_RATE;
+
+ if (!pmu_write(reg, data_buffer, 3) && /* v1, v2 and rate */
+ !pmu_write(PMU_SUPPLY_CONTROL_REG1, &control_bit, 1)) /* trigger */
+ return 0;
+ return -1;
+}
+
+int tegra2_pmu_is_voltage_nominal(void)
+{
+ if ((vdd_current[core].data == vdd_current[core].nominal) &&
+ (vdd_current[cpu].data == vdd_current[cpu].nominal))
+ return 1;
+ return 0;
+}
+
+void calculate_next_voltage(int *data, int nominal, int step)
+{
+ if (abs(nominal - *data) > VDD_TRANSITION_STEP)
+ *data += step;
+ else
+ *data = nominal;
+}
+
+/*
+ * It is requird by tegra2 soc that vdd_core must be higher than vdd_cpu
+ * with certain range at all time. If current settings doesn't meet this
+ * condition, pmu_adjust_voltage just simply returns without setting any
+ * voltage.
+ */
+static int pmu_adjust_voltage(void)
+{
+ int step_core, step_cpu;
+ int adjust_vdd_core_late;
+
+ /*
+ * if vdd_core < vdd_cpu + rel
+ * skip
+ *
+ * This condition may happen when system reboots due to kernel crash.
+ */
+ if (vdd_current[core].data < (vdd_current[cpu].data + VDD_RELATION))
+ return -1;
+
+ /*
+ * Since vdd_core and vdd_cpu may both stand at either greater or less
+ * than their nominal voltage, the adjustment may go either directions.
+ *
+ * Make sure vdd_core is always higher than vdd_cpu with certain margin.
+ * So, find out which vdd to adjust first in each step.
+ *
+ * case 1: both vdd_core and vdd_cpu need to move up
+ * adjust vdd_core before vdd_cpu
+ *
+ * case 2: both vdd_core and vdd_cpu need to move down
+ * adjust vdd_cpu before vdd_core
+ *
+ * case 3: vdd_core moves down and vdd_cpu moves up
+ * adjusting either one first is fine.
+ */
+ step_core = stp(vdd_current[core].data, vdd_current[core].nominal);
+ step_cpu = stp(vdd_current[cpu].data, vdd_current[cpu].nominal);
+
+ /*
+ * Adjust vdd_core and vdd_cpu one step at a time until they reach
+ * their nominal values.
+ */
+ while ((vdd_current[core].data != vdd_current[core].nominal) ||
+ (vdd_current[cpu].data != vdd_current[cpu].nominal)) {
+
+ adjust_vdd_core_late = 0;
+
+ /* if vdd_core hasn't reached its nominal value? */
+ if (vdd_current[core].data != vdd_current[core].nominal) {
+
+ calculate_next_voltage(&vdd_current[core].data,
+ vdd_current[core].nominal,
+ step_core);
+
+ /*
+ * if case 1 and case 3, set new vdd_core first.
+ * otherwise, hold down until new vdd_cpu is set.
+ */
+ if (step_cpu > 0) {
+ /* adjust vdd_core first */
+ if (tegra2_set_voltage(PMU_CORE_VOLTAGE_REG,
+ vdd_current[core].data,
+ VDD_CORE_SUPPLY_CONTROL))
+ return -1;
+ } else
+ /* set flag to adjust vdd_core later */
+ adjust_vdd_core_late = 1;
+ }
+
+ /* if vdd_cpu hasn't reached its nominal value? */
+ if (vdd_current[cpu].data != vdd_current[cpu].nominal) {
+
+ calculate_next_voltage(&vdd_current[cpu].data,
+ vdd_current[cpu].nominal,
+ step_cpu);
+
+ /* adjust vdd_cpu */
+ if (tegra2_set_voltage(PMU_CPU_VOLTAGE_REG,
+ vdd_current[cpu].data,
+ VDD_CPU_SUPPLY_CONTROL))
+ return -1;
+ }
+
+ /*
+ * if vdd_core late flag is set
+ */
+ if (adjust_vdd_core_late) {
+ /* adjust vdd_core */
+ if (tegra2_set_voltage(PMU_CORE_VOLTAGE_REG,
+ vdd_current[core].data,
+ VDD_CORE_SUPPLY_CONTROL))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int pmu_set_nominal(void)
+{
+ /* fill in nominal values based on chip type */
+ if (vdd_init_nominal_table())
+ return -1;
+
+ /* select current i2c bus to DVC */
+ i2c_set_bus_num(DVC_I2C_BUS_NUMBER);
+
+ /* get current voltage settings */
+ if (tegra2_get_voltage())
+ return -1;
+
+ /* if current voltage is already set to nominal, skip */
+ if (tegra2_pmu_is_voltage_nominal())
+ return 0;
+
+ /* adjust vdd_core and/or vdd_cpu */
+ return pmu_adjust_voltage();
+}