summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/configs/tegra3_android_defconfig2
-rw-r--r--arch/arm/configs/tegra_android_defconfig2
-rw-r--r--arch/arm/mach-tegra/Makefile4
-rw-r--r--arch/arm/mach-tegra/board-cardhu-panel.c194
-rw-r--r--arch/arm/mach-tegra/board-cardhu-pinmux.c22
-rw-r--r--arch/arm/mach-tegra/board-cardhu-power.c2
-rw-r--r--arch/arm/mach-tegra/board-cardhu-sensors.c8
-rw-r--r--arch/arm/mach-tegra/board-cardhu.c64
-rw-r--r--arch/arm/mach-tegra/board-cardhu.h1
-rw-r--r--arch/arm/mach-tegra/board-enterprise-baseband.c10
-rw-r--r--arch/arm/mach-tegra/board-enterprise-panel.c6
-rw-r--r--arch/arm/mach-tegra/board-enterprise-pinmux.c1
-rw-r--r--arch/arm/mach-tegra/board-enterprise-power.c13
-rw-r--r--arch/arm/mach-tegra/board-enterprise-sdhci.c5
-rw-r--r--arch/arm/mach-tegra/board-enterprise-sensors.c4
-rw-r--r--arch/arm/mach-tegra/board-enterprise.c42
-rw-r--r--arch/arm/mach-tegra/board-p1852.c15
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c8
-rw-r--r--arch/arm/mach-tegra/cpuquiet.c344
-rw-r--r--arch/arm/mach-tegra/dvfs.c25
-rw-r--r--arch/arm/mach-tegra/dvfs.h21
-rw-r--r--arch/arm/mach-tegra/pm.c15
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c68
-rw-r--r--arch/arm/mach-tegra/tegra3_usb_phy.c39
-rw-r--r--arch/arm/mach-tegra/usb_phy.c11
-rw-r--r--arch/arm/mm/pageattr.c9
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/cpuquiet/Kconfig11
-rw-r--r--drivers/cpuquiet/Makefile1
-rw-r--r--drivers/cpuquiet/cpuquiet.c32
-rw-r--r--drivers/cpuquiet/cpuquiet.h36
-rw-r--r--drivers/cpuquiet/driver.c200
-rw-r--r--drivers/cpuquiet/governor.c101
-rw-r--r--drivers/cpuquiet/governors/Makefile1
-rw-r--r--drivers/cpuquiet/governors/balanced.c473
-rw-r--r--drivers/cpuquiet/governors/userspace.c56
-rw-r--r--drivers/cpuquiet/sysfs.c290
-rw-r--r--drivers/media/video/tegra/Makefile2
-rw-r--r--drivers/media/video/tegra/ad5816.c22
-rw-r--r--drivers/media/video/tegra/nvavp/nvavp_dev.c6
-rw-r--r--drivers/media/video/tegra/ov5640.c2
-rw-r--r--drivers/media/video/tegra/tegra_camera.c11
-rw-r--r--drivers/misc/bcm4329_rfkill.c8
-rw-r--r--drivers/tty/serial/tegra_hsuart.c2
-rw-r--r--drivers/usb/otg/tegra-otg.c44
-rw-r--r--drivers/video/tegra/dc/Makefile1
-rw-r--r--drivers/video/tegra/dc/bandwidth.c275
-rw-r--r--drivers/video/tegra/dc/dc.c351
-rw-r--r--drivers/video/tegra/dc/dc_config.h14
-rw-r--r--drivers/video/tegra/dc/dc_priv.h88
-rw-r--r--drivers/video/tegra/dc/hdmi.c16
-rw-r--r--include/linux/cpuquiet.h53
-rw-r--r--include/linux/platform_data/tegra_usb.h2
-rw-r--r--sound/soc/tegra/tegra30_i2s.c13
55 files changed, 2478 insertions, 571 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5132b50295eb..d6ff29c77d2d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2119,6 +2119,8 @@ endif
source "drivers/cpuidle/Kconfig"
+source "drivers/cpuquiet/Kconfig"
+
endmenu
menu "Floating point emulation"
diff --git a/arch/arm/configs/tegra3_android_defconfig b/arch/arm/configs/tegra3_android_defconfig
index 50da47699f9f..af04716cc0cb 100644
--- a/arch/arm/configs/tegra3_android_defconfig
+++ b/arch/arm/configs/tegra3_android_defconfig
@@ -445,7 +445,7 @@ CONFIG_EFI_PARTITION=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
-# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_DEBUG_FS=y
CONFIG_LOCKUP_DETECTOR=y
diff --git a/arch/arm/configs/tegra_android_defconfig b/arch/arm/configs/tegra_android_defconfig
index 3cb78ad0741e..a129a8ee5a10 100644
--- a/arch/arm/configs/tegra_android_defconfig
+++ b/arch/arm/configs/tegra_android_defconfig
@@ -371,6 +371,7 @@ CONFIG_EFI_PARTITION=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_FS=y
CONFIG_LOCKUP_DETECTOR=y
# CONFIG_DETECT_HUNG_TASK is not set
@@ -383,4 +384,3 @@ CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_TWOFISH=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_DEV_TEGRA_AES=y
-# CONFIG_MAGIC_SYSRQ is not set
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 9acb8305a175..e3e33ba02642 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -81,8 +81,12 @@ obj-y += reset.o
obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
ifeq ($(CONFIG_TEGRA_AUTO_HOTPLUG),y)
+ifeq ($(CONFIG_CPUQUIET_FRAMEWORK),y)
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuquiet.o
+else
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpu-tegra3.o
endif
+endif
obj-$(CONFIG_TEGRA_PCI) += pcie.o
obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
ifeq ($(CONFIG_CPU_IDLE),y)
diff --git a/arch/arm/mach-tegra/board-cardhu-panel.c b/arch/arm/mach-tegra/board-cardhu-panel.c
index a62171060b84..b85c542e96f8 100644
--- a/arch/arm/mach-tegra/board-cardhu-panel.c
+++ b/arch/arm/mach-tegra/board-cardhu-panel.c
@@ -43,8 +43,16 @@
#include "gpio-names.h"
/* Select panel to be used. */
-#define DSI_PANEL_219 1
+#define DSI_PANEL_219 0
#define DSI_PANEL_218 0
+#define DSI_PANEL_1506 1
+
+#if DSI_PANEL_1506
+#define DC_CTRL_MODE TEGRA_DC_OUT_ONE_SHOT_MODE
+#else
+#define DC_CTRL_MODE 0
+#endif
+
#define AVDD_LCD PMU_TCA6416_GPIO_PORT17
#define DSI_PANEL_RESET 1
@@ -60,6 +68,13 @@
#define pm313_BPP TEGRA_GPIO_PN6 /* 0:24bpp, 1:18bpp */
#define pm313_lvds_shutdown TEGRA_GPIO_PH1
+/* E1506 display board pins */
+#define e1506_pm269_lcd_te TEGRA_GPIO_PJ1
+#define e1506_pm269_dsi_vddio TEGRA_GPIO_PH1
+#define e1506_pm269_dsia_bl_pwm TEGRA_GPIO_PH0
+#define e1506_pm269_panel_enb TEGRA_GPIO_PW1
+#define e1506_pm269_bl_enb TEGRA_GPIO_PH2
+
/* E1247 reworked for pm269 pins */
#define e1247_pm269_lvds_shutdown TEGRA_GPIO_PN6
@@ -71,10 +86,10 @@
#define cardhu_bl_pwm TEGRA_GPIO_PH0
#define cardhu_hdmi_hpd TEGRA_GPIO_PN7
-#if defined(DSI_PANEL_219) || defined(DSI_PANEL_218)
+#if defined(DSI_PANEL_219) || defined(DSI_PANEL_218) || defined(DSI_PANEL_1506)
#define cardhu_dsia_bl_enb TEGRA_GPIO_PW1
#define cardhu_dsib_bl_enb TEGRA_GPIO_PW0
-#define cardhu_dsi_218_panel_reset TEGRA_GPIO_PD2
+#define cardhu_dsi_panel_reset TEGRA_GPIO_PD2
#define cardhu_dsi_219_panel_reset TEGRA_GPIO_PW0
#endif
@@ -132,14 +147,14 @@ static tegra_dc_bl_output cardhu_bl_output_measured = {
248, 249, 250, 251, 252, 253, 254, 255
};
-static p_tegra_dc_bl_output bl_output;
+static p_tegra_dc_bl_output bl_output = cardhu_bl_output_measured;
+
+static bool kernel_1st_panel_init = true;
static int cardhu_backlight_init(struct device *dev)
{
int ret;
- bl_output = cardhu_bl_output_measured;
-
if (WARN_ON(ARRAY_SIZE(cardhu_bl_output_measured) != 256))
pr_err("bl_output array does not have 256 elements\n");
@@ -240,6 +255,9 @@ static int cardhu_backlight_notify(struct device *unused, int brightness)
#elif DSI_PANEL_219
/* DSIa */
gpio_set_value(cardhu_dsia_bl_enb, !!brightness);
+#elif DSI_PANEL_1506
+ /* DSIa */
+ gpio_set_value(e1506_pm269_bl_enb, !!brightness);
#endif
/* SD brightness is a percentage, 8-bit value. */
@@ -742,14 +760,17 @@ static int cardhu_dsi_panel_enable(void)
return PTR_ERR(cardhu_dsi_reg);
}
}
+
regulator_enable(cardhu_dsi_reg);
+#if !DSI_PANEL_1506
ret = gpio_request(AVDD_LCD, "avdd_lcd");
if (ret < 0)
gpio_free(AVDD_LCD);
ret = gpio_direction_output(AVDD_LCD, 1);
if (ret < 0)
gpio_free(AVDD_LCD);
+#endif
#if DSI_PANEL_219
@@ -779,22 +800,58 @@ static int cardhu_dsi_panel_enable(void)
mdelay(15);
#endif
+#if DSI_PANEL_1506
+ ret = gpio_request(e1506_pm269_dsi_vddio, "e1506_pm269_dsi_vddio");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(e1506_pm269_dsi_vddio, 0);
+ if (ret < 0) {
+ gpio_free(e1506_pm269_dsi_vddio);
+ return ret;
+ }
+
+ ret = gpio_request(e1506_pm269_panel_enb, "e1506_pm269_panel_enb");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(e1506_pm269_panel_enb, 0);
+ if (ret < 0) {
+ gpio_free(e1506_pm269_panel_enb);
+ return ret;
+ }
+
+ ret = gpio_request(e1506_pm269_bl_enb, "e1506_pm269_bl_enb");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(e1506_pm269_bl_enb, 0);
+ if (ret < 0) {
+ gpio_free(e1506_pm269_bl_enb);
+ return ret;
+ }
+
+ gpio_set_value(e1506_pm269_dsi_vddio, 1);
+ mdelay(10);
+ gpio_set_value(e1506_pm269_panel_enb, 1);
+ mdelay(10);
+ gpio_set_value(e1506_pm269_bl_enb, 1);
+ mdelay(15);
+#endif
+
#if DSI_PANEL_RESET
#if DSI_PANEL_218
- ret = gpio_request(cardhu_dsi_218_panel_reset, "dsi_panel_reset");
+ ret = gpio_request(cardhu_dsi_panel_reset, "dsi_panel_reset");
if (ret < 0) {
return ret;
}
- ret = gpio_direction_output(cardhu_dsi_218_panel_reset, 0);
+ ret = gpio_direction_output(cardhu_dsi_panel_reset, 0);
if (ret < 0) {
- gpio_free(cardhu_dsi_218_panel_reset);
+ gpio_free(cardhu_dsi_panel_reset);
return ret;
}
- gpio_set_value(cardhu_dsi_218_panel_reset, 1);
- gpio_set_value(cardhu_dsi_218_panel_reset, 0);
+ gpio_set_value(cardhu_dsi_panel_reset, 1);
+ gpio_set_value(cardhu_dsi_panel_reset, 0);
mdelay(2);
- gpio_set_value(cardhu_dsi_218_panel_reset, 1);
+ gpio_set_value(cardhu_dsi_panel_reset, 1);
mdelay(2);
#endif
@@ -816,6 +873,24 @@ static int cardhu_dsi_panel_enable(void)
gpio_set_value(cardhu_dsi_219_panel_reset, 1);
mdelay(15);
#endif
+
+#if DSI_PANEL_1506
+ ret = gpio_request(cardhu_dsi_panel_reset, "dsi_panel_reset");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(cardhu_dsi_panel_reset, 0);
+ if (ret < 0) {
+ gpio_free(cardhu_dsi_panel_reset);
+ return ret;
+ }
+
+ gpio_set_value(cardhu_dsi_panel_reset, 1);
+ mdelay(1);
+ gpio_set_value(cardhu_dsi_panel_reset, 0);
+ mdelay(1);
+ gpio_set_value(cardhu_dsi_panel_reset, 1);
+ mdelay(20);
+#endif
#endif
return 0;
@@ -826,7 +901,6 @@ static int cardhu_dsi_panel_disable(void)
int err;
err = 0;
- printk(KERN_INFO "DSI panel disable\n");
#if DSI_PANEL_219
gpio_free(cardhu_dsi_219_panel_reset);
@@ -839,6 +913,19 @@ static int cardhu_dsi_panel_disable(void)
gpio_free(cardhu_dsi_218_panel_reset);
#endif
+#if DSI_PANEL_1506
+ tegra_gpio_disable(e1506_pm269_bl_enb);
+ gpio_free(e1506_pm269_bl_enb);
+ tegra_gpio_disable(e1506_pm269_panel_enb);
+ gpio_free(e1506_pm269_panel_enb);
+ tegra_gpio_disable(e1506_pm269_dsi_vddio);
+ gpio_free(e1506_pm269_dsi_vddio);
+ if (kernel_1st_panel_init != true) {
+ tegra_gpio_disable(cardhu_dsi_panel_reset);
+ gpio_free(cardhu_dsi_panel_reset);
+ } else
+ kernel_1st_panel_init = false;
+#endif
return err;
}
@@ -865,16 +952,48 @@ static int cardhu_dsi_panel_postsuspend(void)
return err;
}
+u8 password_array[] = {0xb9, 0xff, 0x83, 0x92};
+
static struct tegra_dsi_cmd dsi_init_cmd[] = {
DSI_CMD_SHORT(0x05, 0x11, 0x00),
DSI_DLY_MS(150),
+#if DSI_PANEL_1506
+ DSI_CMD_LONG(0x39, password_array),
+ DSI_DLY_MS(10),
+ DSI_CMD_SHORT(0x15, 0xd4, 0x0c),
+ DSI_DLY_MS(10),
+ DSI_CMD_SHORT(0x15, 0xba, 0x11),
+ DSI_DLY_MS(10),
+#endif
+#if (DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x15, 0x35, 0x00),
+#endif
DSI_CMD_SHORT(0x05, 0x29, 0x00),
DSI_DLY_MS(20),
};
-static struct tegra_dsi_cmd dsi_suspend_cmd[] = {
+static struct tegra_dsi_cmd dsi_early_suspend_cmd[] = {
DSI_CMD_SHORT(0x05, 0x28, 0x00),
DSI_DLY_MS(20),
+#if (DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x05, 0x34, 0x00),
+#endif
+};
+
+static struct tegra_dsi_cmd dsi_late_resume_cmd[] = {
+#if (DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x15, 0x35, 0x00),
+#endif
+ DSI_CMD_SHORT(0x05, 0x29, 0x00),
+ DSI_DLY_MS(20),
+};
+
+static struct tegra_dsi_cmd dsi_suspend_cmd[] = {
+ DSI_CMD_SHORT(0x05, 0x28, 0x00),
+ DSI_DLY_MS(120),
+#if (DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x05, 0x34, 0x00),
+#endif
DSI_CMD_SHORT(0x05, 0x10, 0x00),
DSI_DLY_MS(5),
};
@@ -882,7 +1001,19 @@ static struct tegra_dsi_cmd dsi_suspend_cmd[] = {
struct tegra_dsi_out cardhu_dsi = {
.n_data_lanes = 2,
.pixel_format = TEGRA_DSI_PIXEL_FORMAT_24BIT_P,
+
+#if (DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ /*
+ * The one-shot frame time must be shorter than the time between TE.
+ * Increasing refresh_rate will result in a decrease in the frame time
+ * for one-shot. rated_refresh_rate is only an approximation of the
+ * TE rate, and is only used to report refresh rate to upper layers.
+ */
+ .refresh_rate = 66,
+ .rated_refresh_rate = 60,
+#else
.refresh_rate = 60,
+#endif
.virtual_channel = TEGRA_DSI_VIRTUAL_CHANNEL_0,
.panel_has_frame_buffer = true,
@@ -897,6 +1028,12 @@ struct tegra_dsi_out cardhu_dsi = {
.n_init_cmd = ARRAY_SIZE(dsi_init_cmd),
.dsi_init_cmd = dsi_init_cmd,
+ .n_early_suspend_cmd = ARRAY_SIZE(dsi_early_suspend_cmd),
+ .dsi_early_suspend_cmd = dsi_early_suspend_cmd,
+
+ .n_late_resume_cmd = ARRAY_SIZE(dsi_late_resume_cmd),
+ .dsi_late_resume_cmd = dsi_late_resume_cmd,
+
.n_suspend_cmd = ARRAY_SIZE(dsi_suspend_cmd),
.dsi_suspend_cmd = dsi_suspend_cmd,
@@ -937,6 +1074,21 @@ static struct tegra_dc_mode cardhu_dsi_modes[] = {
},
#endif
+#if DSI_PANEL_1506
+ {
+ .pclk = 61417000,
+ .h_ref_to_sync = 2,
+ .v_ref_to_sync = 2,
+ .h_sync_width = 4,
+ .v_sync_width = 4,
+ .h_back_porch = 100,
+ .v_back_porch = 14,
+ .h_active = 720,
+ .v_active = 1280,
+ .h_front_porch = 4,
+ .v_front_porch = 4,
+ },
+#endif
};
@@ -954,6 +1106,13 @@ static struct tegra_fb_data cardhu_dsi_fb_data = {
.yres = 480,
.bits_per_pixel = 32,
#endif
+
+#if DSI_PANEL_1506
+ .win = 0,
+ .xres = 720,
+ .yres = 1280,
+ .bits_per_pixel = 32,
+#endif
.flags = TEGRA_FB_FLIP_ON_PROBE,
};
#endif
@@ -977,6 +1136,7 @@ static struct tegra_dc_out cardhu_disp1_out = {
.enable = cardhu_panel_enable,
.disable = cardhu_panel_disable,
#else
+ .flags = DC_CTRL_MODE,
.type = TEGRA_DC_OUT_DSI,
.modes = cardhu_dsi_modes,
@@ -1233,6 +1393,12 @@ int __init cardhu_panel_init(void)
gpio_request(cardhu_hdmi_hpd, "hdmi_hpd");
gpio_direction_input(cardhu_hdmi_hpd);
+#if !(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ tegra_gpio_enable(e1506_pm269_lcd_te);
+ gpio_request(e1506_pm269_lcd_te, "lcd_te");
+ gpio_direction_input(e1506_pm269_lcd_te);
+#endif
+
#ifdef CONFIG_HAS_EARLYSUSPEND
cardhu_panel_early_suspender.suspend = cardhu_panel_early_suspend;
cardhu_panel_early_suspender.resume = cardhu_panel_late_resume;
diff --git a/arch/arm/mach-tegra/board-cardhu-pinmux.c b/arch/arm/mach-tegra/board-cardhu-pinmux.c
index 05e2179682b1..734047c2731f 100644
--- a/arch/arm/mach-tegra/board-cardhu-pinmux.c
+++ b/arch/arm/mach-tegra/board-cardhu-pinmux.c
@@ -495,6 +495,11 @@ static __initdata struct tegra_pingroup_config cardhu_pinmux_e1198[] = {
DEFAULT_PINMUX(SPI2_CS2_N, SPI2, PULL_UP, NORMAL, INPUT),
};
+static __initdata struct tegra_pingroup_config cardhu_pinmux_pm269_e1506[] = {
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_DC1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+};
+
static __initdata struct tegra_pingroup_config unused_pins_lowpower[] = {
DEFAULT_PINMUX(GMI_WAIT, NAND, PULL_UP, TRISTATE, OUTPUT),
DEFAULT_PINMUX(GMI_ADV_N, NAND, NORMAL, TRISTATE, OUTPUT),
@@ -619,6 +624,7 @@ static void __init cardhu_gpio_init_configure(void)
int __init cardhu_pinmux_init(void)
{
struct board_info board_info;
+ struct board_info display_board_info;
cardhu_gpio_init_configure();
@@ -627,6 +633,7 @@ int __init cardhu_pinmux_init(void)
ARRAY_SIZE(cardhu_drive_pinmux));
tegra_get_board_info(&board_info);
+ tegra_get_display_board_info(&display_board_info);
switch (board_info.board_id) {
case BOARD_E1198:
tegra_pinmux_config_table(cardhu_pinmux_e1198,
@@ -663,6 +670,12 @@ int __init cardhu_pinmux_init(void)
tegra_pinmux_config_table(cardhu_pinmux_e118x,
ARRAY_SIZE(cardhu_pinmux_e118x));
}
+
+ if (display_board_info.board_id == BOARD_DISPLAY_E1506) {
+ tegra_pinmux_config_table(cardhu_pinmux_pm269_e1506,
+ ARRAY_SIZE(cardhu_pinmux_pm269_e1506));
+ }
+
tegra_pinmux_config_table(unused_pins_lowpower,
ARRAY_SIZE(unused_pins_lowpower));
tegra_pinmux_config_table(gmi_pins_269,
@@ -722,6 +735,10 @@ struct gpio_init_pin_info vddio_gmi_pins_pm269_wo_pm313[] = {
PIN_GPIO_LPM("GMI_AD9", TEGRA_GPIO_PH1, 0, 0),
};
+struct gpio_init_pin_info vddio_gmi_pins_pm269_e1506[] = {
+ PIN_GPIO_LPM("GMI_CS2", TEGRA_GPIO_PK3, 1, 0),
+};
+
static void set_unused_pin_gpio(struct gpio_init_pin_info *lpm_pin_info,
int list_count)
{
@@ -781,7 +798,10 @@ int __init cardhu_pins_state_init(void)
set_unused_pin_gpio(&vddio_gmi_pins_pm269[0],
ARRAY_SIZE(vddio_gmi_pins_pm269));
- if (display_board_info.board_id != BOARD_DISPLAY_PM313) {
+ if (display_board_info.board_id == BOARD_DISPLAY_E1506) {
+ set_unused_pin_gpio(&vddio_gmi_pins_pm269_e1506[0],
+ ARRAY_SIZE(vddio_gmi_pins_pm269_e1506));
+ } else if (display_board_info.board_id != BOARD_DISPLAY_PM313) {
set_unused_pin_gpio(&vddio_gmi_pins_pm269_wo_pm313[0],
ARRAY_SIZE(vddio_gmi_pins_pm269_wo_pm313));
}
diff --git a/arch/arm/mach-tegra/board-cardhu-power.c b/arch/arm/mach-tegra/board-cardhu-power.c
index d517d8266204..39975eada203 100644
--- a/arch/arm/mach-tegra/board-cardhu-power.c
+++ b/arch/arm/mach-tegra/board-cardhu-power.c
@@ -610,6 +610,7 @@ static struct regulator_consumer_supply fixed_reg_cam1_ldo_en_supply[] = {
static struct regulator_consumer_supply fixed_reg_cam2_ldo_en_supply[] = {
REGULATOR_SUPPLY("vdd_2v8_cam2", NULL),
REGULATOR_SUPPLY("avdd", "7-0072"),
+ REGULATOR_SUPPLY("vdd", "7-000e"),
};
/* CAM3_LDO_EN from AP GPIO KB_ROW8 S00*/
@@ -650,6 +651,7 @@ static struct regulator_consumer_supply fixed_reg_en_1v8_cam_supply[] = {
REGULATOR_SUPPLY("dvdd", "6-0072"),
REGULATOR_SUPPLY("dvdd", "7-0072"),
REGULATOR_SUPPLY("vdd_i2c", "6-000e"),
+ REGULATOR_SUPPLY("vdd_i2c", "7-000e"),
REGULATOR_SUPPLY("vdd_i2c", "2-0033"),
};
diff --git a/arch/arm/mach-tegra/board-cardhu-sensors.c b/arch/arm/mach-tegra/board-cardhu-sensors.c
index 042f740c2935..c907ae95f597 100644
--- a/arch/arm/mach-tegra/board-cardhu-sensors.c
+++ b/arch/arm/mach-tegra/board-cardhu-sensors.c
@@ -576,7 +576,7 @@ static struct nvc_gpio_pdata pm269_sh532u_left_gpio_pdata[] = {
};
static struct sh532u_platform_data pm269_sh532u_left_pdata = {
- .cfg = 0,
+ .cfg = NVC_CFG_NODEV,
.num = 1,
.sync = 2,
.dev_name = "focuser",
@@ -589,7 +589,7 @@ static struct nvc_gpio_pdata pm269_sh532u_right_gpio_pdata[] = {
};
static struct sh532u_platform_data pm269_sh532u_right_pdata = {
- .cfg = 0,
+ .cfg = NVC_CFG_NODEV,
.num = 2,
.sync = 1,
.dev_name = "focuser",
@@ -624,7 +624,7 @@ static struct nvc_gpio_pdata pm269_ad5816_left_gpio_pdata[] = {
};
static struct ad5816_platform_data pm269_ad5816_left_pdata = {
- .cfg = 0,
+ .cfg = NVC_CFG_NODEV,
.num = 1,
.sync = 2,
.dev_name = "focuser",
@@ -637,7 +637,7 @@ static struct nvc_gpio_pdata pm269_ad5816_right_gpio_pdata[] = {
};
static struct ad5816_platform_data pm269_ad5816_right_pdata = {
- .cfg = 0,
+ .cfg = NVC_CFG_NODEV,
.num = 2,
.sync = 1,
.dev_name = "focuser",
diff --git a/arch/arm/mach-tegra/board-cardhu.c b/arch/arm/mach-tegra/board-cardhu.c
index 4480ab3ef47b..458053d56fcd 100644
--- a/arch/arm/mach-tegra/board-cardhu.c
+++ b/arch/arm/mach-tegra/board-cardhu.c
@@ -717,6 +717,32 @@ static struct platform_device *cardhu_devices[] __initdata = {
#endif
};
+#define E1506_MXT_CONFIG_CRC 0x62F903
+static const u8 e1506_config[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x32, 0x0A, 0x00, 0x05, 0x01, 0x00,
+ 0x00, 0x1E, 0x0A, 0x8B, 0x00, 0x00, 0x13, 0x0B,
+ 0x00, 0x10, 0x32, 0x03, 0x03, 0x00, 0x03, 0x01,
+ 0x00, 0x0A, 0x0A, 0x0A, 0x0A, 0xBF, 0x03, 0x1B,
+ 0x02, 0x00, 0x00, 0x37, 0x37, 0x00, 0x00, 0x00,
+ 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xA9, 0x7F, 0x9A, 0x0E, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x0A,
+ 0x0F, 0x14, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x08, 0x10,
+ 0x00
+};
+
#define MXT_CONFIG_CRC 0xD62DE8
static const u8 config[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -782,6 +808,23 @@ static struct mxt_platform_data atmel_mxt_info = {
.read_chg = NULL,
};
+static struct mxt_platform_data e1506_atmel_mxt_info = {
+ .x_line = 19,
+ .y_line = 11,
+ .x_size = 960,
+ .y_size = 540,
+ .blen = 0x10,
+ .threshold = 0x32,
+ .voltage = 3300000, /* 3.3V */
+ .orient = 3,
+ .config = e1506_config,
+ .config_length = 168,
+ .config_crc = E1506_MXT_CONFIG_CRC,
+ .irqflags = IRQF_TRIGGER_FALLING,
+/* .read_chg = &read_chg, */
+ .read_chg = NULL,
+};
+
static struct i2c_board_info __initdata atmel_i2c_info[] = {
{
I2C_BOARD_INFO("atmel_mxt_ts", 0x5A),
@@ -790,6 +833,14 @@ static struct i2c_board_info __initdata atmel_i2c_info[] = {
}
};
+static struct i2c_board_info __initdata e1506_atmel_i2c_info[] = {
+ {
+ I2C_BOARD_INFO("atmel_mxt_ts", 0x4A),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PH4),
+ .platform_data = &e1506_atmel_mxt_info,
+ }
+};
+
static __initdata struct tegra_clk_init_table spi_clk_init_table[] = {
/* name parent rate enabled */
{ "sbc1", "pll_p", 52000000, true},
@@ -798,10 +849,11 @@ static __initdata struct tegra_clk_init_table spi_clk_init_table[] = {
static int __init cardhu_touch_init(void)
{
- struct board_info BoardInfo;
+ struct board_info BoardInfo, DisplayBoardInfo;
- tegra_get_display_board_info(&BoardInfo);
- if (BoardInfo.board_id == BOARD_DISPLAY_PM313) {
+ tegra_get_board_info(&BoardInfo);
+ tegra_get_display_board_info(&DisplayBoardInfo);
+ if (DisplayBoardInfo.board_id == BOARD_DISPLAY_PM313) {
tegra_clk_init_from_table(spi_clk_init_table);
touch_init_raydium(TEGRA_GPIO_PH4, TEGRA_GPIO_PH6, 2);
@@ -815,13 +867,15 @@ static int __init cardhu_touch_init(void)
gpio_set_value(TEGRA_GPIO_PH6, 1);
msleep(100);
- tegra_get_board_info(&BoardInfo);
if ((BoardInfo.sku & SKU_TOUCH_MASK) == SKU_TOUCH_2000) {
atmel_mxt_info.config = config_sku2000;
atmel_mxt_info.config_crc = MXT_CONFIG_CRC_SKU2000;
}
- i2c_register_board_info(1, atmel_i2c_info, 1);
+ if (DisplayBoardInfo.board_id == BOARD_DISPLAY_E1506)
+ i2c_register_board_info(1, e1506_atmel_i2c_info, 1);
+ else
+ i2c_register_board_info(1, atmel_i2c_info, 1);
}
return 0;
diff --git a/arch/arm/mach-tegra/board-cardhu.h b/arch/arm/mach-tegra/board-cardhu.h
index 27d9e4739911..cde2993f7b72 100644
--- a/arch/arm/mach-tegra/board-cardhu.h
+++ b/arch/arm/mach-tegra/board-cardhu.h
@@ -82,6 +82,7 @@
/* Display Board ID */
#define BOARD_DISPLAY_PM313 0x030D
#define BOARD_DISPLAY_E1247 0x0C2F
+#define BOARD_DISPLAY_E1506 0x0F06
/* External peripheral act as gpio */
/* TPS6591x GPIOs */
diff --git a/arch/arm/mach-tegra/board-enterprise-baseband.c b/arch/arm/mach-tegra/board-enterprise-baseband.c
index f2c94fda7727..47cf83d0c4b9 100644
--- a/arch/arm/mach-tegra/board-enterprise-baseband.c
+++ b/arch/arm/mach-tegra/board-enterprise-baseband.c
@@ -233,16 +233,6 @@ static int baseband_init(void)
tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_GPIO_PV0,
TEGRA_PUPD_PULL_UP);
- tegra_gpio_enable(MODEM_PWR_ON);
- tegra_gpio_enable(MODEM_RESET);
- tegra_gpio_enable(AP2MDM_ACK2);
- tegra_gpio_enable(BB_RST_OUT);
- tegra_gpio_enable(AP2MDM_ACK);
- tegra_gpio_enable(MDM2AP_ACK);
- tegra_gpio_enable(TEGRA_GPIO_PY3);
- tegra_gpio_enable(TEGRA_GPIO_PO1);
- tegra_gpio_enable(TEGRA_GPIO_PO2);
-
/* export GPIO for user space access through sysfs */
gpio_export(MODEM_PWR_ON, false);
diff --git a/arch/arm/mach-tegra/board-enterprise-panel.c b/arch/arm/mach-tegra/board-enterprise-panel.c
index 5b3dacea135f..8876c8ebf88e 100644
--- a/arch/arm/mach-tegra/board-enterprise-panel.c
+++ b/arch/arm/mach-tegra/board-enterprise-panel.c
@@ -541,7 +541,6 @@ static int enterprise_dsi_panel_enable(void)
gpio_free(enterprise_dsi_panel_reset);
return ret;
}
- tegra_gpio_enable(enterprise_dsi_panel_reset);
gpio_set_value(enterprise_dsi_panel_reset, 0);
udelay(2000);
@@ -560,7 +559,6 @@ static int enterprise_dsi_panel_disable(void)
#if DSI_PANEL_RESET
if (kernel_1st_panel_init != true) {
- tegra_gpio_disable(enterprise_dsi_panel_reset);
gpio_free(enterprise_dsi_panel_reset);
} else
kernel_1st_panel_init = false;
@@ -863,22 +861,18 @@ int __init enterprise_panel_init(void)
enterprise_carveouts[1].size = tegra_carveout_size;
#endif
- tegra_gpio_enable(enterprise_hdmi_hpd);
gpio_request(enterprise_hdmi_hpd, "hdmi_hpd");
gpio_direction_input(enterprise_hdmi_hpd);
- tegra_gpio_enable(enterprise_lcd_2d_3d);
gpio_request(enterprise_lcd_2d_3d, "lcd_2d_3d");
gpio_direction_output(enterprise_lcd_2d_3d, 0);
enterprise_stereo_set_mode(enterprise_stereo.mode_2d_3d);
- tegra_gpio_enable(enterprise_lcd_swp_pl);
gpio_request(enterprise_lcd_swp_pl, "lcd_swp_pl");
gpio_direction_output(enterprise_lcd_swp_pl, 0);
enterprise_stereo_set_orientation(enterprise_stereo.orientation);
#if !(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
- tegra_gpio_enable(enterprise_lcd_te);
gpio_request(enterprise_lcd_swp_pl, "lcd_te");
gpio_direction_input(enterprise_lcd_te);
#endif
diff --git a/arch/arm/mach-tegra/board-enterprise-pinmux.c b/arch/arm/mach-tegra/board-enterprise-pinmux.c
index fc5851848864..e64d65b84153 100644
--- a/arch/arm/mach-tegra/board-enterprise-pinmux.c
+++ b/arch/arm/mach-tegra/board-enterprise-pinmux.c
@@ -534,7 +534,6 @@ static void enterprise_set_unused_pin_gpio(struct pin_info_low_power_mode *lpm_p
gpio_free(pin_info->gpio_nr);
continue;
}
- tegra_gpio_enable(pin_info->gpio_nr);
}
}
diff --git a/arch/arm/mach-tegra/board-enterprise-power.c b/arch/arm/mach-tegra/board-enterprise-power.c
index f32365d808a7..5e5d40728f15 100644
--- a/arch/arm/mach-tegra/board-enterprise-power.c
+++ b/arch/arm/mach-tegra/board-enterprise-power.c
@@ -309,7 +309,6 @@ int battery_charger_init(void *board_data)
" charger fails\n", __func__);
}
gpio_direction_output(TEGRA_GPIO_PF6, 1);
- tegra_gpio_enable(TEGRA_GPIO_PF6);
return 0;
}
@@ -683,8 +682,6 @@ static int __init enterprise_fixed_regulator_init(void)
for (i = 0; i < nfixreg_devs; ++i) {
struct fixed_voltage_config *fixed_reg_pdata =
fixed_regs_devices[i]->dev.platform_data;
- if (fixed_reg_pdata->gpio < TEGRA_NR_GPIOS)
- tegra_gpio_enable(fixed_reg_pdata->gpio);
}
return platform_add_devices(fixed_regs_devices, nfixreg_devs);
}
@@ -693,14 +690,6 @@ static int __init enterprise_gpio_regulator_init(void)
{
int i, j;
- for (i = 0; i < ARRAY_SIZE(gpio_regs_devices); ++i) {
- struct gpio_regulator_config *gpio_reg_pdata =
- gpio_regs_devices[i]->dev.platform_data;
- for (j = 0; j < gpio_reg_pdata->nr_gpios; ++j) {
- if (gpio_reg_pdata->gpios[j].gpio < TEGRA_NR_GPIOS)
- tegra_gpio_enable(gpio_reg_pdata->gpios[j].gpio);
- }
- }
return platform_add_devices(gpio_regs_devices,
ARRAY_SIZE(gpio_regs_devices));
}
@@ -841,8 +830,6 @@ void __init enterprise_bpc_mgmt_init(void)
{
int int_gpio = TEGRA_GPIO_TO_IRQ(TEGRA_BPC_TRIGGER);
- tegra_gpio_enable(TEGRA_BPC_TRIGGER);
-
#ifdef CONFIG_SMP
cpumask_setall(&(bpc_mgmt_platform_data.affinity_mask));
irq_set_affinity_hint(int_gpio,
diff --git a/arch/arm/mach-tegra/board-enterprise-sdhci.c b/arch/arm/mach-tegra/board-enterprise-sdhci.c
index 4e9690899082..7a5632601fe6 100644
--- a/arch/arm/mach-tegra/board-enterprise-sdhci.c
+++ b/arch/arm/mach-tegra/board-enterprise-sdhci.c
@@ -264,10 +264,6 @@ static int __init enterprise_wifi_init(void)
if (rc)
pr_err("WLAN_WOW gpio request failed:%d\n", rc);
- tegra_gpio_enable(ENTERPRISE_WLAN_PWR);
- tegra_gpio_enable(ENTERPRISE_WLAN_RST);
- tegra_gpio_enable(ENTERPRISE_WLAN_WOW);
-
rc = gpio_direction_output(ENTERPRISE_WLAN_PWR, 0);
if (rc)
pr_err("WLAN_PWR gpio direction configuration failed:%d\n", rc);
@@ -286,7 +282,6 @@ int __init enterprise_sdhci_init(void)
{
platform_device_register(&tegra_sdhci_device3);
- tegra_gpio_enable(ENTERPRISE_SD_CD);
tegra_sdhci_platform_data2.cd_gpio = ENTERPRISE_SD_CD;
platform_device_register(&tegra_sdhci_device2);
diff --git a/arch/arm/mach-tegra/board-enterprise-sensors.c b/arch/arm/mach-tegra/board-enterprise-sensors.c
index f775c2bd3b3e..9bfed40325df 100644
--- a/arch/arm/mach-tegra/board-enterprise-sensors.c
+++ b/arch/arm/mach-tegra/board-enterprise-sensors.c
@@ -131,7 +131,6 @@ static void enterprise_nct1008_init(void)
{
int ret;
- tegra_gpio_enable(TEGRA_GPIO_PH7);
ret = gpio_request(TEGRA_GPIO_PH7, "temp_alert");
if (ret < 0) {
pr_err("%s: gpio_request failed %d\n", __func__, ret);
@@ -213,7 +212,6 @@ static void mpuirq_init(void)
#if (MPU_GYRO_TYPE == MPU_TYPE_MPU3050)
#if MPU_ACCEL_IRQ_GPIO
/* ACCEL-IRQ assignment */
- tegra_gpio_enable(MPU_ACCEL_IRQ_GPIO);
ret = gpio_request(MPU_ACCEL_IRQ_GPIO, MPU_ACCEL_NAME);
if (ret < 0) {
pr_err("%s: gpio_request failed %d\n", __func__, ret);
@@ -230,7 +228,6 @@ static void mpuirq_init(void)
#endif
/* MPU-IRQ assignment */
- tegra_gpio_enable(MPU_GYRO_IRQ_GPIO);
ret = gpio_request(MPU_GYRO_IRQ_GPIO, MPU_GYRO_NAME);
if (ret < 0) {
pr_err("%s: gpio_request failed %d\n", __func__, ret);
@@ -589,7 +586,6 @@ static int enterprise_cam_init(void)
gpio_direction_output(enterprise_cam_gpio_data[i].gpio,
enterprise_cam_gpio_data[i].value);
gpio_export(enterprise_cam_gpio_data[i].gpio, false);
- tegra_gpio_enable(enterprise_cam_gpio_data[i].gpio);
}
tegra_get_board_info(&bi);
diff --git a/arch/arm/mach-tegra/board-enterprise.c b/arch/arm/mach-tegra/board-enterprise.c
index e23a9a468081..fb873a834138 100644
--- a/arch/arm/mach-tegra/board-enterprise.c
+++ b/arch/arm/mach-tegra/board-enterprise.c
@@ -126,8 +126,6 @@ static struct platform_device enterprise_bluesleep_device = {
static void __init enterprise_setup_bluesleep(void)
{
platform_device_register(&enterprise_bluesleep_device);
- tegra_gpio_enable(TEGRA_GPIO_PS2);
- tegra_gpio_enable(TEGRA_GPIO_PE7);
return;
}
@@ -599,9 +597,6 @@ static struct i2c_board_info __initdata atmel_i2c_info[] = {
static int __init enterprise_touch_init(void)
{
- tegra_gpio_enable(TEGRA_GPIO_PH6);
- tegra_gpio_enable(TEGRA_GPIO_PF5);
-
gpio_request(TEGRA_GPIO_PH6, "atmel-irq");
gpio_direction_input(TEGRA_GPIO_PH6);
@@ -706,6 +701,7 @@ static struct tegra_usb_platform_data tegra_udc_pdata = {
static struct tegra_usb_platform_data tegra_ehci1_utmi_pdata = {
.port_otg = true,
.has_hostpc = true,
+ .builtin_host_disabled = true,
.phy_intf = TEGRA_USB_PHY_INTF_UTMI,
.op_mode = TEGRA_USB_OPMODE_HOST,
.u_data.host = {
@@ -812,11 +808,6 @@ static void enterprise_audio_init(void)
ARRAY_SIZE(enterprise_audio_devices));
}
-static void enterprise_gps_init(void)
-{
- tegra_gpio_enable(TEGRA_GPIO_PE4);
- tegra_gpio_enable(TEGRA_GPIO_PE5);
-}
static struct baseband_power_platform_data tegra_baseband_power_data = {
.baseband_type = BASEBAND_XMM,
@@ -891,23 +882,11 @@ static void enterprise_baseband_init(void)
/* baseband-power.ko will register ehci2 device */
tegra_ehci2_device.dev.platform_data =
&tegra_ehci2_hsic_xmm_pdata;
- /* enable XMM6260 baseband gpio(s) */
- tegra_gpio_enable(tegra_baseband_power_data.modem.generic
- .mdm_reset);
- tegra_gpio_enable(tegra_baseband_power_data.modem.generic
- .mdm_on);
- tegra_gpio_enable(tegra_baseband_power_data.modem.generic
- .ap2mdm_ack);
- tegra_gpio_enable(tegra_baseband_power_data.modem.generic
- .mdm2ap_ack);
- tegra_gpio_enable(tegra_baseband_power_data.modem.generic
- .ap2mdm_ack2);
- tegra_gpio_enable(tegra_baseband_power_data.modem.generic
- .mdm2ap_ack2);
tegra_baseband_power_data.hsic_register =
&tegra_usb_hsic_host_register;
tegra_baseband_power_data.hsic_unregister =
&tegra_usb_hsic_host_unregister;
+
platform_device_register(&tegra_baseband_power_device);
platform_device_register(&tegra_baseband_power2_device);
break;
@@ -922,21 +901,6 @@ static void enterprise_baseband_init(void)
}
}
-static void enterprise_nfc_init(void)
-{
- struct board_info bi;
-
- tegra_gpio_enable(TEGRA_GPIO_PS4);
- tegra_gpio_enable(TEGRA_GPIO_PM6);
-
- /* Enable firmware GPIO PX7 for board E1205 */
- tegra_get_board_info(&bi);
- if (bi.board_id == BOARD_E1205 && bi.fab >= BOARD_FAB_A03) {
- nfc_pdata.firm_gpio = TEGRA_GPIO_PX7;
- tegra_gpio_enable(TEGRA_GPIO_PX7);
- }
-}
-
static void __init tegra_enterprise_init(void)
{
tegra_thermal_init(&thermal_data);
@@ -955,7 +919,6 @@ static void __init tegra_enterprise_init(void)
enterprise_kbc_init();
enterprise_touch_init();
enterprise_audio_init();
- enterprise_gps_init();
enterprise_baseband_init();
enterprise_panel_init();
enterprise_setup_bluesleep();
@@ -964,7 +927,6 @@ static void __init tegra_enterprise_init(void)
enterprise_suspend_init();
enterprise_bpc_mgmt_init();
tegra_release_bootloader_fb();
- enterprise_nfc_init();
}
static void __init tegra_enterprise_reserve(void)
diff --git a/arch/arm/mach-tegra/board-p1852.c b/arch/arm/mach-tegra/board-p1852.c
index eebbb24a9871..044c7a4815ff 100644
--- a/arch/arm/mach-tegra/board-p1852.c
+++ b/arch/arm/mach-tegra/board-p1852.c
@@ -367,11 +367,12 @@ static struct tegra_usb_platform_data tegra_ehci1_utmi_pdata = {
.idle_wait_delay = 17,
.elastic_limit = 16,
.term_range_adj = 6,
- .xcvr_setup = 15,
- .xcvr_setup_offset = 0,
+ .xcvr_setup = 63,
+ .xcvr_setup_offset = 6,
.xcvr_use_fuses = 1,
.xcvr_lsfslew = 2,
.xcvr_lsrslew = 2,
+ .xcvr_use_lsb = 1,
},
};
@@ -392,11 +393,12 @@ static struct tegra_usb_platform_data tegra_ehci2_utmi_pdata = {
.idle_wait_delay = 17,
.elastic_limit = 16,
.term_range_adj = 6,
- .xcvr_setup = 15,
- .xcvr_setup_offset = 0,
+ .xcvr_setup = 63,
+ .xcvr_setup_offset = 6,
.xcvr_use_fuses = 1,
.xcvr_lsfslew = 2,
.xcvr_lsrslew = 2,
+ .xcvr_use_lsb = 1,
},
};
@@ -417,11 +419,12 @@ static struct tegra_usb_platform_data tegra_ehci3_utmi_pdata = {
.idle_wait_delay = 17,
.elastic_limit = 16,
.term_range_adj = 6,
- .xcvr_setup = 8,
- .xcvr_setup_offset = 0,
+ .xcvr_setup = 63,
+ .xcvr_setup_offset = 6,
.xcvr_use_fuses = 1,
.xcvr_lsfslew = 2,
.xcvr_lsrslew = 2,
+ .xcvr_use_lsb = 1,
},
};
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index 595f07d33538..5c680b849435 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -242,12 +242,12 @@ int tegra_edp_update_thermal_zone(int temperature)
/* Update cpu rate if cpufreq (at least on cpu0) is already started;
alter cpu dvfs table for this thermal zone if necessary */
- tegra_cpu_dvfs_alter(edp_thermal_index, true);
+ tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, true);
if (target_cpu_speed[0]) {
edp_update_limit();
tegra_cpu_set_speed_cap(NULL);
}
- tegra_cpu_dvfs_alter(edp_thermal_index, false);
+ tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, false);
mutex_unlock(&tegra_cpu_lock);
return ret;
@@ -321,6 +321,7 @@ static int tegra_cpu_edp_notify(
case CPU_UP_PREPARE:
mutex_lock(&tegra_cpu_lock);
cpu_set(cpu, edp_cpumask);
+ tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, true);
edp_update_limit();
cpu_speed = tegra_getspeed(0);
@@ -335,13 +336,16 @@ static int tegra_cpu_edp_notify(
printk(KERN_DEBUG "tegra CPU:%sforce EDP limit %u kHz"
"\n", ret ? " failed to " : " ", new_speed);
}
+ tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, false);
mutex_unlock(&tegra_cpu_lock);
break;
case CPU_DEAD:
mutex_lock(&tegra_cpu_lock);
cpu_clear(cpu, edp_cpumask);
+ tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, true);
edp_update_limit();
tegra_cpu_set_speed_cap(NULL);
+ tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, false);
mutex_unlock(&tegra_cpu_lock);
break;
}
diff --git a/arch/arm/mach-tegra/cpuquiet.c b/arch/arm/mach-tegra/cpuquiet.c
new file mode 100644
index 000000000000..2b5826d97401
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuquiet.c
@@ -0,0 +1,344 @@
+/*
+ * arch/arm/mach-tegra/cpuquiet.c
+ *
+ * Cpuquiet driver for Tegra3 CPUs
+ *
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/pm_qos_params.h>
+#include <linux/cpuquiet.h>
+
+#include "pm.h"
+#include "cpu-tegra.h"
+#include "clock.h"
+
+#define INITIAL_STATE TEGRA_CPQ_IDLE
+#define UP_DELAY_MS 70
+#define DOWN_DELAY_MS 2000
+
+static struct mutex *tegra3_cpu_lock;
+static struct workqueue_struct *cpuquiet_wq;
+static struct delayed_work cpuquiet_work;
+static struct work_struct minmax_work;
+
+static bool no_lp;
+module_param(no_lp, bool, 0644);
+
+static unsigned long up_delay;
+module_param(up_delay, ulong, 0644);
+static unsigned long down_delay;
+module_param(down_delay, ulong, 0644);
+
+static int mp_overhead = 10;
+module_param(mp_overhead, int, 0644);
+static unsigned int idle_top_freq;
+module_param(idle_top_freq, uint, 0644);
+static unsigned int idle_bottom_freq;
+module_param(idle_bottom_freq, uint, 0644);
+
+static struct clk *cpu_clk;
+static struct clk *cpu_g_clk;
+static struct clk *cpu_lp_clk;
+
+static struct cpumask cr_online_requests;
+
+enum {
+ TEGRA_CPQ_DISABLED = 0,
+ TEGRA_CPQ_IDLE,
+ TEGRA_CPQ_SWITCH_TO_LP,
+ TEGRA_CPQ_SWITCH_TO_G,
+};
+
+static int cpq_state;
+
+static int update_core_config(unsigned int cpunumber, bool up)
+{
+ int ret = -EINVAL;
+ unsigned int nr_cpus = num_online_cpus();
+ int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
+ int min_cpus = pm_qos_request(PM_QOS_MIN_ONLINE_CPUS);
+
+ if (cpq_state == TEGRA_CPQ_DISABLED || cpunumber >= nr_cpu_ids)
+ return ret;
+
+ if (up) {
+ if(is_lp_cluster()) {
+ cpumask_set_cpu(cpunumber, &cr_online_requests);
+ ret = -EBUSY;
+ } else {
+ if (tegra_cpu_edp_favor_up(nr_cpus, mp_overhead) &&
+ nr_cpus < max_cpus)
+ ret = cpu_up(cpunumber);
+ }
+ } else {
+ if (is_lp_cluster()) {
+ ret = -EBUSY;
+ } else {
+ if (nr_cpus > min_cpus)
+ ret = cpu_down(cpunumber);
+ }
+ }
+
+ return ret;
+}
+
+static int tegra_quiesence_cpu(unsigned int cpunumber)
+{
+ return update_core_config(cpunumber, false);
+}
+
+static int tegra_wake_cpu(unsigned int cpunumber)
+{
+ return update_core_config(cpunumber, true);
+}
+
+static struct cpuquiet_driver tegra_cpuquiet_driver = {
+ .name = "tegra",
+ .quiesence_cpu = tegra_quiesence_cpu,
+ .wake_cpu = tegra_wake_cpu,
+};
+
+static void apply_core_config(void)
+{
+ unsigned int cpu;
+
+ if (is_lp_cluster() || cpq_state == TEGRA_CPQ_DISABLED)
+ return;
+
+ for_each_cpu_mask(cpu, cr_online_requests) {
+ if (cpu < nr_cpu_ids && !cpu_online(cpu))
+ if (!tegra_wake_cpu(cpu))
+ cpumask_clear_cpu(cpu, &cr_online_requests);
+ }
+}
+
+static void tegra_cpuquiet_work_func(struct work_struct *work)
+{
+ bool update_cr_config = false;
+
+ mutex_lock(tegra3_cpu_lock);
+
+ switch(cpq_state) {
+ case TEGRA_CPQ_DISABLED:
+ case TEGRA_CPQ_IDLE:
+ break;
+ case TEGRA_CPQ_SWITCH_TO_G:
+ if (is_lp_cluster()) {
+ if(!clk_set_parent(cpu_clk, cpu_g_clk)) {
+ /*catch-up with governor target speed */
+ tegra_cpu_set_speed_cap(NULL);
+ /* process pending core requests*/
+ update_cr_config = true;
+ }
+ }
+ break;
+ case TEGRA_CPQ_SWITCH_TO_LP:
+ if (!is_lp_cluster() && !no_lp &&
+ num_online_cpus() == 1) {
+ if (!clk_set_parent(cpu_clk, cpu_lp_clk)) {
+ /*catch-up with governor target speed*/
+ tegra_cpu_set_speed_cap(NULL);
+ }
+ }
+ break;
+ default:
+ pr_err("%s: invalid tegra hotplug state %d\n",
+ __func__, cpq_state);
+ }
+
+ mutex_unlock(tegra3_cpu_lock);
+
+ if (update_cr_config)
+ apply_core_config();
+}
+
+static void min_max_constraints_workfunc(struct work_struct *work)
+{
+ int count = -1;
+ bool up = false;
+ unsigned int cpu;
+
+ int nr_cpus = num_online_cpus();
+ int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
+ int min_cpus = pm_qos_request(PM_QOS_MIN_ONLINE_CPUS);
+
+ if (is_lp_cluster())
+ return;
+
+ if (nr_cpus < min_cpus) {
+ up = true;
+ count = min_cpus - nr_cpus;
+ } else if (nr_cpus > max_cpus && max_cpus >= min_cpus) {
+ count = nr_cpus - max_cpus;
+ }
+
+ for (;count > 0; count--) {
+ if (up) {
+ cpu = cpumask_next_zero(0, cpu_online_mask);
+ if (cpu < nr_cpu_ids)
+ cpu_up(cpu);
+ else
+ break;
+ } else {
+ cpu = cpumask_next(0, cpu_online_mask);
+ if (cpu < nr_cpu_ids)
+ cpu_down(cpu);
+ else
+ break;
+ }
+ }
+}
+
+static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
+{
+ mutex_lock(tegra3_cpu_lock);
+
+ if ((n >= 2) && is_lp_cluster()) {
+ /* make sure cpu rate is within g-mode range before switching */
+ unsigned int speed = max(
+ tegra_getspeed(0), clk_get_min_rate(cpu_g_clk) / 1000);
+ tegra_update_cpu_speed(speed);
+
+ clk_set_parent(cpu_clk, cpu_g_clk);
+ }
+
+ tegra_cpu_set_speed_cap(NULL);
+ mutex_unlock(tegra3_cpu_lock);
+
+ schedule_work(&minmax_work);
+
+ return NOTIFY_OK;
+}
+
+static int max_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
+{
+ if (n < num_online_cpus())
+ schedule_work(&minmax_work);
+
+ return NOTIFY_OK;
+}
+
+void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend)
+{
+ if (!is_g_cluster_present())
+ return;
+
+ if (cpq_state == TEGRA_CPQ_DISABLED)
+ return;
+
+ if (suspend) {
+ cpq_state = TEGRA_CPQ_IDLE;
+
+ /* Switch to G-mode if suspend rate is high enough */
+ if (is_lp_cluster() && (cpu_freq >= idle_bottom_freq)) {
+ clk_set_parent(cpu_clk, cpu_g_clk);
+ }
+ return;
+ }
+
+ if (is_lp_cluster() && pm_qos_request(PM_QOS_MIN_ONLINE_CPUS) >= 2) {
+ if (cpq_state != TEGRA_CPQ_SWITCH_TO_G) {
+ /* Force switch */
+ cpq_state = TEGRA_CPQ_SWITCH_TO_G;
+ queue_delayed_work(
+ cpuquiet_wq, &cpuquiet_work, up_delay);
+ }
+ return;
+ }
+
+ if (is_lp_cluster() && (cpu_freq >= idle_top_freq || no_lp)) {
+ cpq_state = TEGRA_CPQ_SWITCH_TO_G;
+ queue_delayed_work(cpuquiet_wq, &cpuquiet_work, up_delay);
+ } else if (!is_lp_cluster() && !no_lp &&
+ cpu_freq <= idle_bottom_freq) {
+ cpq_state = TEGRA_CPQ_SWITCH_TO_LP;
+ queue_delayed_work(cpuquiet_wq, &cpuquiet_work, down_delay);
+ } else {
+ cpq_state = TEGRA_CPQ_IDLE;
+ }
+}
+
+static struct notifier_block min_cpus_notifier = {
+ .notifier_call = min_cpus_notify,
+};
+
+static struct notifier_block max_cpus_notifier = {
+ .notifier_call = max_cpus_notify,
+};
+
+int tegra_auto_hotplug_init(struct mutex *cpu_lock)
+{
+ /*
+ * Not bound to the issuer CPU (=> high-priority), has rescue worker
+ * task, single-threaded, freezable.
+ */
+ cpuquiet_wq = alloc_workqueue(
+ "cpuquiet", WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1);
+
+ if (!cpuquiet_wq)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&cpuquiet_work, tegra_cpuquiet_work_func);
+ INIT_WORK(&minmax_work, min_max_constraints_workfunc);
+
+ cpu_clk = clk_get_sys(NULL, "cpu");
+ cpu_g_clk = clk_get_sys(NULL, "cpu_g");
+ cpu_lp_clk = clk_get_sys(NULL, "cpu_lp");
+
+ if (IS_ERR(cpu_clk) || IS_ERR(cpu_g_clk) || IS_ERR(cpu_lp_clk))
+ return -ENOENT;
+
+ idle_top_freq = clk_get_max_rate(cpu_lp_clk) / 1000;
+ idle_bottom_freq = clk_get_min_rate(cpu_g_clk) / 1000;
+
+ up_delay = msecs_to_jiffies(UP_DELAY_MS);
+ down_delay = msecs_to_jiffies(DOWN_DELAY_MS);
+ cpumask_clear(&cr_online_requests);
+ tegra3_cpu_lock = cpu_lock;
+
+ cpq_state = INITIAL_STATE;
+
+ pr_info("Tegra cpuquiet initialized: %s\n",
+ (cpq_state == TEGRA_CPQ_DISABLED) ? "disabled" : "enabled");
+
+ if (pm_qos_add_notifier(PM_QOS_MIN_ONLINE_CPUS, &min_cpus_notifier))
+ pr_err("%s: Failed to register min cpus PM QoS notifier\n",
+ __func__);
+ if (pm_qos_add_notifier(PM_QOS_MAX_ONLINE_CPUS, &max_cpus_notifier))
+ pr_err("%s: Failed to register max cpus PM QoS notifier\n",
+ __func__);
+
+ return cpuquiet_register_driver(&tegra_cpuquiet_driver);
+}
+
+void tegra_auto_hotplug_exit(void)
+{
+ destroy_workqueue(cpuquiet_wq);
+ cpuquiet_unregister_driver(&tegra_cpuquiet_driver);
+}
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 8723e6fa60df..cb33e3db862f 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -322,8 +322,7 @@ static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
static inline unsigned long *dvfs_get_freqs(struct dvfs *d)
{
- return (d->alt_freqs_state == ALT_FREQS_ENABLED) ?
- &d->alt_freqs[0] : &d->freqs[0];
+ return d->alt_freqs ? : &d->freqs[0];
}
static int
@@ -367,26 +366,16 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
return ret;
}
-static inline int dvfs_alt_freqs_set(struct dvfs *d, bool enable)
+int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs)
{
- if (d->alt_freqs_state == ALT_FREQS_NOT_SUPPORTED)
- return -ENOSYS;
-
- d->alt_freqs_state = enable ? ALT_FREQS_ENABLED : ALT_FREQS_DISABLED;
- return 0;
-}
-
-int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable)
-{
- int ret;
- enum dvfs_alt_freqs old_state;
+ int ret = 0;
mutex_lock(&dvfs_lock);
- old_state = d->alt_freqs_state;
- ret = dvfs_alt_freqs_set(d, enable);
- if (!ret && (old_state != d->alt_freqs_state))
+ if (d->alt_freqs != alt_freqs) {
+ d->alt_freqs = alt_freqs;
ret = __tegra_dvfs_set_rate(d, d->cur_rate);
+ }
mutex_unlock(&dvfs_lock);
return ret;
@@ -407,7 +396,7 @@ int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
* frequency limits. For now, just fail the call for clock that has
* alternative limits initialized.
*/
- if (c->dvfs->alt_freqs_state != ALT_FREQS_NOT_SUPPORTED)
+ if (c->dvfs->alt_freqs)
return -ENOSYS;
for (i = 0; i < c->dvfs->num_freqs; i++) {
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index a53a04ba95a2..3bdb13690278 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -73,12 +73,6 @@ struct dvfs_rail {
struct rail_stats stats;
};
-enum dvfs_alt_freqs {
- ALT_FREQS_NOT_SUPPORTED = 0,
- ALT_FREQS_DISABLED,
- ALT_FREQS_ENABLED,
-};
-
struct dvfs {
/* Used only by tegra2_clock.c */
const char *clk_name;
@@ -88,11 +82,10 @@ struct dvfs {
/* Must be initialized before tegra_dvfs_init */
int freqs_mult;
unsigned long freqs[MAX_DVFS_FREQS];
- unsigned long alt_freqs[MAX_DVFS_FREQS];
+ unsigned long *alt_freqs;
const int *millivolts;
struct dvfs_rail *dvfs_rail;
bool auto_dvfs;
- enum dvfs_alt_freqs alt_freqs_state;
/* Filled in by tegra_dvfs_init */
int max_millivolts;
@@ -124,8 +117,9 @@ struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id);
int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate);
void tegra_dvfs_core_cap_enable(bool enable);
void tegra_dvfs_core_cap_level_set(int level);
-int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable);
-void tegra_cpu_dvfs_alter(int edp_thermal_index, bool before_clk_update);
+int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs);
+void tegra_cpu_dvfs_alter(
+ int edp_thermal_index, const cpumask_t *cpus, bool before_clk_update);
#else
static inline void tegra_soc_init_dvfs(void)
{}
@@ -160,10 +154,11 @@ static inline void tegra_dvfs_core_cap_enable(bool enable)
{}
static inline void tegra_dvfs_core_cap_level_set(int level)
{}
-static inline int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable)
+static inline int tegra_dvfs_alt_freqs_set(struct dvfs *d,
+ unsigned long *alt_freqs)
{ return 0; }
-static inline void tegra_cpu_dvfs_alter(int edp_thermal_index,
- bool before_clk_update)
+static inline void tegra_cpu_dvfs_alter(
+ int edp_thermal_index, const cpumask_t *cpus, bool before_clk_update)
{}
#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 054eb3aea358..7a08bc1aef24 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -140,6 +140,11 @@ struct suspend_context tegra_sctx;
#define PMC_CPUPWROFF_TIMER 0xcc
#define PMC_COREPWROFF_TIMER PMC_WAKE_DELAY
+#define PMC_PWRGATE_TOGGLE 0x30
+#define PWRGATE_TOGGLE_START (1 << 8)
+#define UN_PWRGATE_CPU \
+ (PWRGATE_TOGGLE_START | TEGRA_CPU_POWERGATE_ID(TEGRA_POWERGATE_CPU))
+
#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
#define PMC_SCRATCH4_WAKE_CLUSTER_MASK (1<<31)
#endif
@@ -597,6 +602,16 @@ unsigned int tegra_idle_lp2_last(unsigned int sleep_time, unsigned int flags)
trace_cpu_cluster(POWER_CPU_CLUSTER_START);
set_power_timers(pdata->cpu_timer, 0,
clk_get_rate_all_locked(tegra_pclk));
+ if (flags & TEGRA_POWER_CLUSTER_G) {
+ /*
+ * To reduce the vdd_cpu up latency when LP->G
+ * transition. Before the transition, enable
+ * the vdd_cpu rail.
+ */
+ if (is_lp_cluster())
+ writel(UN_PWRGATE_CPU,
+ pmc + PMC_PWRGATE_TOGGLE);
+ }
tegra_cluster_switch_prolog(flags);
} else {
set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
index f36bfa774577..e34eb946e4f1 100644
--- a/arch/arm/mach-tegra/tegra3_dvfs.c
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -202,6 +202,10 @@ static struct dvfs cpu_dvfs_table[] = {
CPU_DVFS("cpu_g", -1, -1, MHZ, 1, 1, 216, 216, 300),
};
+static struct dvfs cpu_0_dvfs_table[] = {
+ /* Cpu voltages (mV): 800, 825, 850, 875, 900, 916, 950, 975, 1000, 1007, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, 1212, 1237 */
+};
+
#define CORE_DVFS(_clk_name, _speedo_id, _auto, _mult, _freqs...) \
{ \
.clk_name = _clk_name, \
@@ -352,6 +356,11 @@ static struct dvfs core_dvfs_table[] = {
CORE_DVFS("spdif_out", -1, 1, KHZ, 1, 26000, 26000, 26000, 26000, 26000, 26000, 26000, 26000),
};
+/* CPU alternative DVFS table for cold zone */
+static unsigned long cpu_cold_freqs[MAX_DVFS_FREQS];
+
+/* CPU alternative DVFS table for single G CPU core 0 */
+static unsigned long *cpu_0_freqs;
int tegra_dvfs_disable_core_set(const char *arg, const struct kernel_param *kp)
{
@@ -473,19 +482,18 @@ static void __init init_dvfs_cold(struct dvfs *d, int nominal_mv_index)
for (i = 0; i < d->num_freqs; i++) {
offs = cpu_cold_offs_mhz[i] * MHZ;
if (i > nominal_mv_index)
- d->alt_freqs[i] = d->alt_freqs[i - 1];
+ cpu_cold_freqs[i] = cpu_cold_freqs[i - 1];
else if (d->freqs[i] > offs)
- d->alt_freqs[i] = d->freqs[i] - offs;
+ cpu_cold_freqs[i] = d->freqs[i] - offs;
else {
- d->alt_freqs[i] = d->freqs[i];
+ cpu_cold_freqs[i] = d->freqs[i];
pr_warn("tegra3_dvfs: cold offset %lu is too high for"
" regular dvfs limit %lu\n", offs, d->freqs[i]);
}
if (i)
- BUG_ON(d->alt_freqs[i] < d->alt_freqs[i - 1]);
+ BUG_ON(cpu_cold_freqs[i] < cpu_cold_freqs[i - 1]);
}
- d->alt_freqs_state = ALT_FREQS_DISABLED;
}
static bool __init match_dvfs_one(struct dvfs *d, int speedo_id, int process_id)
@@ -500,6 +508,35 @@ static bool __init match_dvfs_one(struct dvfs *d, int speedo_id, int process_id)
return true;
}
+static void __init init_cpu_0_dvfs(struct dvfs *cpud)
+{
+ int i;
+ struct dvfs *d = NULL;
+
+ /* Init single G CPU core 0 dvfs if this particular SKU/bin has it.
+ Max rates in multi-core and single-core tables must be the same */
+ for (i = 0; i < ARRAY_SIZE(cpu_0_dvfs_table); i++) {
+ if (match_dvfs_one(&cpu_0_dvfs_table[i],
+ cpud->speedo_id, cpud->process_id)) {
+ d = &cpu_0_dvfs_table[i];
+ break;
+ }
+ }
+
+ if (d) {
+ for (i = 0; i < cpud->num_freqs; i++) {
+ d->freqs[i] *= d->freqs_mult;
+ if (d->freqs[i] == 0) {
+ BUG_ON(i == 0);
+ d->freqs[i] = d->freqs[i - 1];
+ }
+ }
+ BUG_ON(cpud->freqs[cpud->num_freqs - 1] !=
+ d->freqs[cpud->num_freqs - 1]);
+ cpu_0_freqs = d->freqs;
+ }
+}
+
static int __init get_cpu_nominal_mv_index(
int speedo_id, int process_id, struct dvfs **cpu_dvfs)
{
@@ -646,7 +683,10 @@ void __init tegra_soc_init_dvfs(void)
/* Initialize matching cpu dvfs entry already found when nominal
voltage was determined */
init_dvfs_one(cpu_dvfs, cpu_nominal_mv_index);
+
+ /* Initialize alternative cold zone and single core tables */
init_dvfs_cold(cpu_dvfs, cpu_nominal_mv_index);
+ init_cpu_0_dvfs(cpu_dvfs);
/* Finally disable dvfs on rails if necessary */
if (tegra_dvfs_core_disabled)
@@ -662,14 +702,18 @@ void __init tegra_soc_init_dvfs(void)
tegra_dvfs_core_disabled ? "disabled" : "enabled");
}
-void tegra_cpu_dvfs_alter(int edp_thermal_index, bool before_clk_update)
+void tegra_cpu_dvfs_alter(int edp_thermal_index, const cpumask_t *cpus,
+ bool before_clk_update)
{
- bool enable = !edp_thermal_index;
-
- if (enable != before_clk_update) {
- int ret = tegra_dvfs_alt_freqs_set(cpu_dvfs, enable);
- WARN_ONCE(ret, "tegra dvfs: failed to set CPU alternative"
- " frequency limits for cold temeperature\n");
+ bool cpu_warm = !!edp_thermal_index;
+ unsigned int n = cpumask_weight(cpus);
+ unsigned long *alt_freqs = cpu_warm ?
+ (n > 1 ? NULL : cpu_0_freqs) : cpu_cold_freqs;
+
+ if (cpu_warm == before_clk_update) {
+ int ret = tegra_dvfs_alt_freqs_set(cpu_dvfs, alt_freqs);
+ WARN_ONCE(ret, "tegra dvfs: failed to update CPU alternative"
+ " frequency limits\n");
}
}
diff --git a/arch/arm/mach-tegra/tegra3_usb_phy.c b/arch/arm/mach-tegra/tegra3_usb_phy.c
index 8ada255cad55..338b7e6284b0 100644
--- a/arch/arm/mach-tegra/tegra3_usb_phy.c
+++ b/arch/arm/mach-tegra/tegra3_usb_phy.c
@@ -402,7 +402,10 @@
#define UTMIP_PWR(inst) (1 << (inst))
#define FUSE_USB_CALIB_0 0x1F0
-#define FUSE_USB_CALIB_XCVR_SETUP(x) (((x) & 0x7F) << 0)
+#define XCVR_SETUP(x) (((x) & 0x7F) << 0)
+#define XCVR_SETUP_LSB_MASK 0xF
+#define XCVR_SETUP_MSB_MASK 0x70
+#define XCVR_SETUP_LSB_MAX_VAL 0xF
/* These values (in milli second) are taken from the battery charging spec */
#define TDP_SRC_ON_MS 100
@@ -1071,19 +1074,24 @@ static unsigned int utmi_phy_xcvr_setup_value(struct tegra_usb_phy *phy)
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (cfg->xcvr_use_fuses) {
- val = FUSE_USB_CALIB_XCVR_SETUP(
- tegra_fuse_readl(FUSE_USB_CALIB_0));
- if (cfg->xcvr_setup_offset <= UTMIP_XCVR_MAX_OFFSET)
- val = val + cfg->xcvr_setup_offset;
-
- if (val > UTMIP_XCVR_SETUP_MAX_VALUE) {
- val = UTMIP_XCVR_SETUP_MAX_VALUE;
- pr_info("%s: reset XCVR_SETUP to max value\n",
- __func__);
- } else if (val < UTMIP_XCVR_SETUP_MIN_VALUE) {
- val = UTMIP_XCVR_SETUP_MIN_VALUE;
- pr_info("%s: reset XCVR_SETUP to min value\n",
- __func__);
+ val = XCVR_SETUP(tegra_fuse_readl(FUSE_USB_CALIB_0));
+ if (cfg->xcvr_use_lsb) {
+ val = min(((val & XCVR_SETUP_LSB_MASK) + cfg->xcvr_setup_offset),
+ XCVR_SETUP_LSB_MAX_VAL);
+ val |= (cfg->xcvr_setup & XCVR_SETUP_MSB_MASK);
+ } else {
+ if (cfg->xcvr_setup_offset <= UTMIP_XCVR_MAX_OFFSET)
+ val = val + cfg->xcvr_setup_offset;
+
+ if (val > UTMIP_XCVR_SETUP_MAX_VALUE) {
+ val = UTMIP_XCVR_SETUP_MAX_VALUE;
+ pr_info("%s: reset XCVR_SETUP to max value\n",
+ __func__);
+ } else if (val < UTMIP_XCVR_SETUP_MIN_VALUE) {
+ val = UTMIP_XCVR_SETUP_MIN_VALUE;
+ pr_info("%s: reset XCVR_SETUP to min value\n",
+ __func__);
+ }
}
} else {
val = cfg->xcvr_setup;
@@ -1534,7 +1542,8 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val |= UTMIP_XCVR_SETUP_MSB(XCVR_SETUP_MSB_CALIB(phy->utmi_xcvr_setup));
val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
- val |= UTMIP_XCVR_HSSLEW_MSB(0x8);
+ if (!config->xcvr_use_lsb)
+ val |= UTMIP_XCVR_HSSLEW_MSB(0x8);
writel(val, base + UTMIP_XCVR_CFG0);
val = readl(base + UTMIP_XCVR_CFG1);
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index bfb9abae2c94..07fb86ed285c 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -379,6 +379,12 @@ void tegra_usb_phy_close(struct tegra_usb_phy *phy)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+ if (phy->ops && phy->ops->close)
+ phy->ops->close(phy);
+
+ if (phy->pdata->ops && phy->pdata->ops->close)
+ phy->pdata->ops->close();
+
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_DEVICE) {
if (phy->pdata->u_data.dev.vbus_pmu_irq)
free_irq(phy->pdata->u_data.dev.vbus_pmu_irq, phy);
@@ -403,11 +409,6 @@ void tegra_usb_phy_close(struct tegra_usb_phy *phy)
regulator_put(phy->vdd_reg);
}
- if (phy->ops && phy->ops->close)
- phy->ops->close(phy);
-
- if (phy->pdata->ops && phy->pdata->ops->close)
- phy->pdata->ops->close();
tegra_usb_phy_release_clocks(phy);
diff --git a/arch/arm/mm/pageattr.c b/arch/arm/mm/pageattr.c
index 5f8071110e88..b7ff7f19b541 100644
--- a/arch/arm/mm/pageattr.c
+++ b/arch/arm/mm/pageattr.c
@@ -331,6 +331,10 @@ static void __set_pmd_pte(pmd_t *pmd, unsigned long address, pte_t *pte)
cpa_debug("__set_pmd_pte %x %x %x\n", pmd, pte, *pte);
+ /* enforce pte entry stores ordering to avoid pmd writes
+ * bypassing pte stores.
+ */
+ dsb();
/* change init_mm */
pmd_populate_kernel(&init_mm, pmd, pte);
@@ -342,7 +346,10 @@ static void __set_pmd_pte(pmd_t *pmd, unsigned long address, pte_t *pte)
pgd_index(address), address);
pmd_populate_kernel(NULL, pmd, pte);
}
-
+ /* enforce pmd entry stores ordering to avoid tlb flush bypassing
+ * pmd entry stores.
+ */
+ dsb();
}
static int
diff --git a/drivers/Makefile b/drivers/Makefile
index 4cbb7ce0a0f9..d7f4b63904ac 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -5,6 +5,7 @@
# Rewritten to use lists instead of if-statements.
#
+obj-$(CONFIG_CPUQUIET_FRAMEWORK)+= cpuquiet/
obj-y += gpio/
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_PARISC) += parisc/
diff --git a/drivers/cpuquiet/Kconfig b/drivers/cpuquiet/Kconfig
new file mode 100644
index 000000000000..844cd34a69b3
--- /dev/null
+++ b/drivers/cpuquiet/Kconfig
@@ -0,0 +1,11 @@
+menu "CPUQUIET Framework"
+
+config CPUQUIET_FRAMEWORK
+ bool "Cpuquiet framework"
+ default n
+ help
+ Cpuquiet implements pluggable policies for forcing cpu cores into a
+ quiescent state. Appropriate policies will save power without hurting
+ performance.
+
+endmenu
diff --git a/drivers/cpuquiet/Makefile b/drivers/cpuquiet/Makefile
new file mode 100644
index 000000000000..0502d4f33012
--- /dev/null
+++ b/drivers/cpuquiet/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CPUQUIET_FRAMEWORK) += cpuquiet.o driver.o sysfs.o governor.o governors/
diff --git a/drivers/cpuquiet/cpuquiet.c b/drivers/cpuquiet/cpuquiet.c
new file mode 100644
index 000000000000..d902af26c8d7
--- /dev/null
+++ b/drivers/cpuquiet/cpuquiet.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/cpuquiet.h>
+#include "cpuquiet.h"
+
+DEFINE_MUTEX(cpuquiet_lock);
+
+static int __init cpuquiet_init(void)
+{
+ return cpuquiet_add_class_sysfs(&cpu_sysdev_class);
+}
+
+core_initcall(cpuquiet_init);
diff --git a/drivers/cpuquiet/cpuquiet.h b/drivers/cpuquiet/cpuquiet.h
new file mode 100644
index 000000000000..fa61946ff119
--- /dev/null
+++ b/drivers/cpuquiet/cpuquiet.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __DRIVER_CPUQUIET_H
+#define __DRIVER_CPUQUIET_H
+
+#include <linux/sysdev.h>
+
+extern struct mutex cpuquiet_lock;
+extern struct cpuquiet_governor *cpuquiet_curr_governor;
+extern struct list_head cpuquiet_governors;
+int cpuquiet_add_class_sysfs(struct sysdev_class *cls);
+struct cpuquiet_governor *cpuquiet_find_governor(const char *str);
+int cpuquiet_switch_governor(struct cpuquiet_governor *gov);
+struct cpuquiet_governor *cpuquiet_get_first_governor(void);
+struct cpuquiet_driver *cpuquiet_get_driver(void);
+void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu);
+void cpuquiet_remove_dev(unsigned int cpu);
+int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name, int cpu);
+#endif
diff --git a/drivers/cpuquiet/driver.c b/drivers/cpuquiet/driver.c
new file mode 100644
index 000000000000..f9dcdf018f58
--- /dev/null
+++ b/drivers/cpuquiet/driver.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <asm/cputime.h>
+
+#include "cpuquiet.h"
+
+struct cpuquiet_cpu_stat {
+ cputime64_t time_up_total;
+ u64 last_update;
+ unsigned int up_down_count;
+ struct kobject cpu_kobject;
+};
+
+struct cpu_attribute {
+ struct attribute attr;
+ enum { up_down_count, time_up_total } type;
+};
+
+static struct cpuquiet_driver *cpuquiet_curr_driver;
+struct cpuquiet_cpu_stat *stats;
+
+#define CPU_ATTRIBUTE(_name) \
+ static struct cpu_attribute _name ## _attr = { \
+ .attr = {.name = __stringify(_name), .mode = 0444 }, \
+ .type = _name, \
+}
+
+CPU_ATTRIBUTE(up_down_count);
+CPU_ATTRIBUTE(time_up_total);
+
+static struct attribute *cpu_attributes[] = {
+ &up_down_count_attr.attr,
+ &time_up_total_attr.attr,
+ NULL,
+};
+
+static void stats_update(struct cpuquiet_cpu_stat *stat, bool up)
+{
+ u64 cur_jiffies = get_jiffies_64();
+ bool was_up = stat->up_down_count & 0x1;
+
+ if (was_up)
+ stat->time_up_total = cputime64_add(stat->time_up_total,
+ cputime64_sub(cur_jiffies, stat->last_update));
+
+ if (was_up != up)
+ stat->up_down_count++;
+
+ stat->last_update = cur_jiffies;
+}
+
+int cpuquiet_quiesence_cpu(unsigned int cpunumber)
+{
+ int err = -EPERM;
+
+ if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu)
+ err = cpuquiet_curr_driver->quiesence_cpu(cpunumber);
+
+ stats_update(stats + cpunumber, 0);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_quiesence_cpu);
+
+int cpuquiet_wake_cpu(unsigned int cpunumber)
+{
+ int err = -EPERM;
+
+ if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu)
+ err = cpuquiet_curr_driver->wake_cpu(cpunumber);
+
+ stats_update(stats + cpunumber, 1);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_wake_cpu);
+
+static ssize_t stats_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpu_attribute *cattr =
+ container_of(attr, struct cpu_attribute, attr);
+ struct cpuquiet_cpu_stat *stat =
+ container_of(kobj, struct cpuquiet_cpu_stat, cpu_kobject);
+ ssize_t len = 0;
+ bool was_up = stat->up_down_count & 0x1;
+
+ stats_update(stat, was_up);
+
+ switch (cattr->type) {
+ case up_down_count:
+ len = sprintf(buf, "%u\n", stat->up_down_count);
+ break;
+ case time_up_total:
+ len = sprintf(buf, "%llu\n", stat->time_up_total);
+ break;
+ }
+
+ return len;
+}
+
+static const struct sysfs_ops stats_sysfs_ops = {
+ .show = stats_sysfs_show,
+};
+
+static struct kobj_type ktype_cpu_stats = {
+ .sysfs_ops = &stats_sysfs_ops,
+ .default_attrs = cpu_attributes,
+};
+
+int cpuquiet_register_driver(struct cpuquiet_driver *drv)
+{
+ int err = -EBUSY;
+ unsigned int cpu;
+ struct sys_device *sys_dev;
+ u64 cur_jiffies;
+
+ if (!drv)
+ return -EINVAL;
+
+ stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL);
+ if (!stats)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ cur_jiffies = get_jiffies_64();
+ stats[cpu].last_update = cur_jiffies;
+ if (cpu_online(cpu))
+ stats[cpu].up_down_count = 1;
+ sys_dev = get_cpu_sysdev(cpu);
+ if (sys_dev) {
+ cpuquiet_add_dev(sys_dev, cpu);
+ cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject,
+ &ktype_cpu_stats, "stats", cpu);
+ }
+ }
+
+ mutex_lock(&cpuquiet_lock);
+ if (!cpuquiet_curr_driver) {
+ err = 0;
+ cpuquiet_curr_driver = drv;
+ cpuquiet_switch_governor(cpuquiet_get_first_governor());
+ }
+ mutex_unlock(&cpuquiet_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_register_driver);
+
+struct cpuquiet_driver *cpuquiet_get_driver(void)
+{
+ return cpuquiet_curr_driver;
+}
+
+void cpuquiet_unregister_driver(struct cpuquiet_driver *drv)
+{
+ unsigned int cpu;
+
+ if (drv != cpuquiet_curr_driver) {
+ WARN(1, "invalid cpuquiet_unregister_driver(%s)\n",
+ drv->name);
+ return;
+ }
+
+ /* stop current governor first */
+ cpuquiet_switch_governor(NULL);
+
+ mutex_lock(&cpuquiet_lock);
+ cpuquiet_curr_driver = NULL;
+
+ for_each_possible_cpu(cpu) {
+ kobject_put(&stats[cpu].cpu_kobject);
+ cpuquiet_remove_dev(cpu);
+ }
+
+ mutex_unlock(&cpuquiet_lock);
+}
+EXPORT_SYMBOL(cpuquiet_unregister_driver);
diff --git a/drivers/cpuquiet/governor.c b/drivers/cpuquiet/governor.c
new file mode 100644
index 000000000000..1446b9ee5066
--- /dev/null
+++ b/drivers/cpuquiet/governor.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+
+#include "cpuquiet.h"
+
+LIST_HEAD(cpuquiet_governors);
+struct cpuquiet_governor *cpuquiet_curr_governor;
+
+struct cpuquiet_governor *cpuquiet_get_first_governor(void)
+{
+ if (!list_empty(&cpuquiet_governors))
+ return list_entry(&cpuquiet_governors, struct cpuquiet_governor,
+ governor_list);
+ else
+ return NULL;
+}
+
+struct cpuquiet_governor *cpuquiet_find_governor(const char *str)
+{
+ struct cpuquiet_governor *gov;
+
+ list_for_each_entry(gov, &cpuquiet_governors, governor_list)
+ if (!strnicmp(str, gov->name, CPUQUIET_NAME_LEN))
+ return gov;
+
+ return NULL;
+}
+
+int cpuquiet_switch_governor(struct cpuquiet_governor *gov)
+{
+ int err = 0;
+
+ if (cpuquiet_curr_governor) {
+ if (cpuquiet_curr_governor->stop)
+ cpuquiet_curr_governor->stop();
+ module_put(cpuquiet_curr_governor->owner);
+ }
+
+ cpuquiet_curr_governor = gov;
+
+ if (gov) {
+ if (!try_module_get(cpuquiet_curr_governor->owner))
+ return -EINVAL;
+ if (gov->start)
+ err = gov->start();
+ if (!err)
+ cpuquiet_curr_governor = gov;
+ }
+
+ return err;
+}
+
+int cpuquiet_register_governor(struct cpuquiet_governor *gov)
+{
+ int ret = -EEXIST;
+
+ if (!gov)
+ return -EINVAL;
+
+ mutex_lock(&cpuquiet_lock);
+ if (cpuquiet_find_governor(gov->name) == NULL) {
+ ret = 0;
+ list_add_tail(&gov->governor_list, &cpuquiet_governors);
+ if (!cpuquiet_curr_governor && cpuquiet_get_driver())
+ cpuquiet_switch_governor(gov);
+ }
+ mutex_unlock(&cpuquiet_lock);
+
+ return ret;
+}
+
+void cpuquiet_unregister_governor(struct cpuquiet_governor *gov)
+{
+ if (!gov)
+ return;
+
+ mutex_lock(&cpuquiet_lock);
+ if (cpuquiet_curr_governor == gov)
+ cpuquiet_switch_governor(NULL);
+ list_del(&gov->governor_list);
+ mutex_unlock(&cpuquiet_lock);
+}
diff --git a/drivers/cpuquiet/governors/Makefile b/drivers/cpuquiet/governors/Makefile
new file mode 100644
index 000000000000..c70803127082
--- /dev/null
+++ b/drivers/cpuquiet/governors/Makefile
@@ -0,0 +1 @@
+obj-y += userspace.o balanced.o
diff --git a/drivers/cpuquiet/governors/balanced.c b/drivers/cpuquiet/governors/balanced.c
new file mode 100644
index 000000000000..813a32e671d7
--- /dev/null
+++ b/drivers/cpuquiet/governors/balanced.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpuquiet.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/pm_qos_params.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <asm/cputime.h>
+
+#define CPUNAMELEN 8
+
+typedef enum {
+ CPU_SPEED_BALANCED,
+ CPU_SPEED_BIASED,
+ CPU_SPEED_SKEWED,
+} CPU_SPEED_BALANCE;
+
+typedef enum {
+ IDLE,
+ DOWN,
+ UP,
+} BALANCED_STATE;
+
+struct idle_info {
+ u64 idle_last;
+ u64 last_timestamp;
+ u64 idle_current;
+ u64 timestamp;
+};
+
+static DEFINE_PER_CPU(struct idle_info, idleinfo);
+static DEFINE_PER_CPU(unsigned int, cpu_load);
+
+static struct timer_list load_timer;
+static bool load_timer_active;
+struct balanced_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct balanced_attribute *attr, char *buf);
+ ssize_t (*store)(struct balanced_attribute *attr, const char *buf,
+ size_t count);
+ unsigned long *param;
+};
+
+#define BALANCED_ATTRIBUTE(_name, _mode) \
+ static struct balanced_attribute _name ## _attr = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = show_attribute, \
+ .store = store_attribute, \
+ .param = &_name, \
+}
+
+/* configurable parameters */
+static unsigned long balance_level = 75;
+static unsigned long idle_bottom_freq;
+static unsigned long idle_top_freq;
+static unsigned long up_delay;
+static unsigned long down_delay;
+
+static struct workqueue_struct *balanced_wq;
+static struct delayed_work balanced_work;
+static BALANCED_STATE balanced_state;
+static struct kobject *balanced_kobject;
+
+static void calculate_load_timer(unsigned long data)
+{
+ int i;
+ u64 idle_time, elapsed_time;
+
+ if (!load_timer_active)
+ return;
+
+ for_each_online_cpu(i) {
+ struct idle_info *iinfo = &per_cpu(idleinfo, i);
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ iinfo->idle_last = iinfo->idle_current;
+ iinfo->last_timestamp = iinfo->timestamp;
+ iinfo->idle_current =
+ get_cpu_idle_time_us(i, &iinfo->timestamp);
+ elapsed_time = iinfo->timestamp - iinfo->last_timestamp;
+
+ idle_time = iinfo->idle_current - iinfo->idle_last;
+ idle_time *= 100;
+ do_div(idle_time, elapsed_time);
+ *load = 100 - idle_time;
+ }
+ mod_timer(&load_timer, jiffies + msecs_to_jiffies(100));
+}
+
+static void start_load_timer(void)
+{
+ int i;
+
+ if (load_timer_active)
+ return;
+
+ load_timer_active = true;
+
+ for_each_online_cpu(i) {
+ struct idle_info *iinfo = &per_cpu(idleinfo, i);
+
+ iinfo->idle_current =
+ get_cpu_idle_time_us(i, &iinfo->timestamp);
+ }
+ mod_timer(&load_timer, jiffies + msecs_to_jiffies(100));
+}
+
+static void stop_load_timer(void)
+{
+ if (!load_timer_active)
+ return;
+
+ load_timer_active = false;
+ del_timer(&load_timer);
+}
+
+static unsigned int get_slowest_cpu_n(void)
+{
+ unsigned int cpu = nr_cpu_ids;
+ unsigned long minload = ULONG_MAX;
+ int i;
+
+ for_each_online_cpu(i) {
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ if ((i > 0) && (minload > *load)) {
+ cpu = i;
+ minload = *load;
+ }
+ }
+
+ return cpu;
+}
+
+static unsigned int cpu_highest_speed(void)
+{
+ unsigned int maxload = 0;
+ int i;
+
+ for_each_online_cpu(i) {
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ maxload = max(maxload, *load);
+ }
+
+ return maxload;
+}
+
+static unsigned int count_slow_cpus(unsigned int limit)
+{
+ unsigned int cnt = 0;
+ int i;
+
+ for_each_online_cpu(i) {
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ if (*load <= limit)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static CPU_SPEED_BALANCE balanced_speed_balance(void)
+{
+ unsigned long highest_speed = cpu_highest_speed();
+ unsigned long balanced_speed = highest_speed * balance_level / 100;
+ unsigned long skewed_speed = balanced_speed / 2;
+ unsigned int nr_cpus = num_online_cpus();
+ unsigned int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
+
+ /* balanced: freq targets for all CPUs are above 50% of highest speed
+ biased: freq target for at least one CPU is below 50% threshold
+ skewed: freq targets for at least 2 CPUs are below 25% threshold */
+ if (count_slow_cpus(skewed_speed) >= 2 || nr_cpus > max_cpus)
+ return CPU_SPEED_SKEWED;
+
+ if (count_slow_cpus(balanced_speed) >= 1 || nr_cpus == max_cpus)
+ return CPU_SPEED_BIASED;
+
+ return CPU_SPEED_BALANCED;
+}
+
+static void balanced_work_func(struct work_struct *work)
+{
+ bool up = false;
+ unsigned int cpu = nr_cpu_ids;
+ CPU_SPEED_BALANCE balance;
+
+ switch (balanced_state) {
+ case IDLE:
+ break;
+ case DOWN:
+ cpu = get_slowest_cpu_n();
+ if (cpu < nr_cpu_ids) {
+ up = false;
+ queue_delayed_work(balanced_wq,
+ &balanced_work, down_delay);
+ } else
+ stop_load_timer();
+ break;
+ case UP:
+ balance = balanced_speed_balance();
+ switch (balance) {
+
+ /* cpu speed is up and balanced - one more on-line */
+ case CPU_SPEED_BALANCED:
+ cpu = cpumask_next_zero(0, cpu_online_mask);
+ if (cpu < nr_cpu_ids)
+ up = true;
+ break;
+ /* cpu speed is up, but skewed - remove one core */
+ case CPU_SPEED_SKEWED:
+ cpu = get_slowest_cpu_n();
+ if (cpu < nr_cpu_ids)
+ up = false;
+ break;
+ /* cpu speed is up, but under-utilized - do nothing */
+ case CPU_SPEED_BIASED:
+ default:
+ break;
+ }
+ queue_delayed_work(
+ balanced_wq, &balanced_work, up_delay);
+ break;
+ default:
+ pr_err("%s: invalid cpuquiet balanced governor state %d\n",
+ __func__, balanced_state);
+ }
+
+ if (cpu < nr_cpu_ids) {
+ if (up)
+ cpuquiet_wake_cpu(cpu);
+ else
+ cpuquiet_quiesence_cpu(cpu);
+ }
+}
+
+static int balanced_cpufreq_transition(struct notifier_block *nb,
+ unsigned long state, void *data)
+{
+ struct cpufreq_freqs *freqs = data;
+ unsigned long cpu_freq;
+
+ if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) {
+ cpu_freq = freqs->new;
+
+ switch (balanced_state) {
+ case IDLE:
+ if (cpu_freq > idle_top_freq) {
+ balanced_state = UP;
+ queue_delayed_work(
+ balanced_wq, &balanced_work, up_delay);
+ start_load_timer();
+ } else if (cpu_freq <= idle_bottom_freq) {
+ balanced_state = DOWN;
+ queue_delayed_work(
+ balanced_wq, &balanced_work,
+ down_delay);
+ start_load_timer();
+ }
+ break;
+ case DOWN:
+ if (cpu_freq > idle_top_freq) {
+ balanced_state = UP;
+ queue_delayed_work(
+ balanced_wq, &balanced_work, up_delay);
+ start_load_timer();
+ }
+ break;
+ case UP:
+ if (cpu_freq <= idle_bottom_freq) {
+ balanced_state = DOWN;
+ queue_delayed_work(balanced_wq,
+ &balanced_work, down_delay);
+ start_load_timer();
+ }
+ break;
+ default:
+ pr_err("%s: invalid tegra hotplug state %d\n",
+ __func__, balanced_state);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block balanced_cpufreq_nb = {
+ .notifier_call = balanced_cpufreq_transition,
+};
+
+static ssize_t show_attribute(struct balanced_attribute *battr, char *buf)
+{
+ return sprintf(buf, "%lu\n", *(battr->param));
+}
+
+static ssize_t store_attribute(struct balanced_attribute *battr,
+ const char *buf, size_t count)
+{
+ int err;
+ unsigned long val;
+
+ err = strict_strtoul(buf, 0, &val);
+ if (err < 0)
+ return err;
+
+ *(battr->param) = val;
+
+ return count;
+}
+
+static ssize_t balanced_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct balanced_attribute *battr =
+ container_of(attr, struct balanced_attribute, attr);
+
+ if (battr->store)
+ return battr->store(battr, buf, count);
+
+ return -EINVAL;
+}
+
+static ssize_t balanced_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct balanced_attribute *battr =
+ container_of(attr, struct balanced_attribute, attr);
+
+ return battr->show(battr, buf);
+}
+
+BALANCED_ATTRIBUTE(balance_level, 0644);
+BALANCED_ATTRIBUTE(idle_bottom_freq, 0644);
+BALANCED_ATTRIBUTE(idle_top_freq, 0644);
+BALANCED_ATTRIBUTE(up_delay, 0644);
+BALANCED_ATTRIBUTE(down_delay, 0644);
+
+static struct attribute *balanced_attributes[] = {
+ &balance_level_attr.attr,
+ &idle_bottom_freq_attr.attr,
+ &idle_top_freq_attr.attr,
+ &up_delay_attr.attr,
+ &down_delay_attr.attr,
+ NULL,
+};
+
+static const struct sysfs_ops balanced_sysfs_ops = {
+ .show = balanced_sysfs_show,
+ .store = balanced_sysfs_store,
+};
+
+static struct kobj_type ktype_balanced = {
+ .sysfs_ops = &balanced_sysfs_ops,
+ .default_attrs = balanced_attributes,
+};
+
+static int balanced_sysfs(void)
+{
+ int err;
+
+ balanced_kobject = kzalloc(sizeof(*balanced_kobject),
+ GFP_KERNEL);
+
+ if (!balanced_kobject)
+ return -ENOMEM;
+
+ err = cpuquiet_kobject_init(balanced_kobject, &ktype_balanced,
+ "balanced");
+
+ if (err)
+ kfree(balanced_kobject);
+
+ return err;
+}
+
+static void balanced_stop(void)
+{
+
+ /*
+ first unregister the notifiers. This ensures the governor state
+ can't be modified by a cpufreq transition
+ */
+ cpufreq_unregister_notifier(&balanced_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ /* now we can force the governor to be idle */
+ balanced_state = IDLE;
+ cancel_delayed_work_sync(&balanced_work);
+ destroy_workqueue(balanced_wq);
+ del_timer(&load_timer);
+
+ kobject_put(balanced_kobject);
+}
+
+static int balanced_start(void)
+{
+ int err, count;
+ struct cpufreq_frequency_table *table;
+
+ err = balanced_sysfs();
+ if (err)
+ return err;
+
+ balanced_wq = alloc_workqueue("cpuquiet-balanced",
+ WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1);
+ if (!balanced_wq)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&balanced_work, balanced_work_func);
+
+ up_delay = msecs_to_jiffies(1000);
+ down_delay = msecs_to_jiffies(2000);
+
+ table = cpufreq_frequency_get_table(0);
+ for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
+ ;
+
+ idle_top_freq = table[(count / 2) - 1].frequency;
+ idle_bottom_freq = table[(count / 2) - 2].frequency;
+
+ cpufreq_register_notifier(&balanced_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ init_timer(&load_timer);
+ load_timer.function = calculate_load_timer;
+
+ return 0;
+}
+
+struct cpuquiet_governor balanced_governor = {
+ .name = "balanced",
+ .start = balanced_start,
+ .stop = balanced_stop,
+ .owner = THIS_MODULE,
+};
+
+static int __init init_balanced(void)
+{
+ return cpuquiet_register_governor(&balanced_governor);
+}
+
+static void __exit exit_balanced(void)
+{
+ cpuquiet_unregister_governor(&balanced_governor);
+}
+
+MODULE_LICENSE("GPL");
+module_init(init_balanced);
+module_exit(exit_balanced);
+
diff --git a/drivers/cpuquiet/governors/userspace.c b/drivers/cpuquiet/governors/userspace.c
new file mode 100644
index 000000000000..470056c5e32a
--- /dev/null
+++ b/drivers/cpuquiet/governors/userspace.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+#include <linux/sysfs.h>
+
+static DEFINE_MUTEX(userspace_mutex);
+
+static int governor_set(unsigned int cpu, bool active)
+{
+ mutex_lock(&userspace_mutex);
+ if (active)
+ cpuquiet_wake_cpu(cpu);
+ else
+ cpuquiet_quiesence_cpu(cpu);
+ mutex_unlock(&userspace_mutex);
+
+ return 0;
+}
+
+struct cpuquiet_governor userspace_governor = {
+ .name = "userspace",
+ .store_active = governor_set,
+ .owner = THIS_MODULE,
+};
+
+static int __init init_usermode(void)
+{
+ return cpuquiet_register_governor(&userspace_governor);
+}
+
+static void __exit exit_usermode(void)
+{
+ cpuquiet_unregister_governor(&userspace_governor);
+}
+
+MODULE_LICENSE("GPL");
+module_init(init_usermode);
+module_exit(exit_usermode);
diff --git a/drivers/cpuquiet/sysfs.c b/drivers/cpuquiet/sysfs.c
new file mode 100644
index 000000000000..1e1c14865b24
--- /dev/null
+++ b/drivers/cpuquiet/sysfs.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/cpuquiet.h>
+
+#include "cpuquiet.h"
+
+struct cpuquiet_dev {
+ unsigned int cpu;
+ struct kobject kobj;
+};
+
+struct cpuquiet_sysfs_attr {
+ struct attribute attr;
+ ssize_t (*show)(char *);
+ ssize_t (*store)(const char *, size_t count);
+};
+
+static struct kobject *cpuquiet_global_kobject;
+struct cpuquiet_dev *cpuquiet_cpu_devices[CONFIG_NR_CPUS];
+
+static ssize_t show_current_governor(char *buf)
+{
+ ssize_t ret;
+
+ mutex_lock(&cpuquiet_lock);
+
+ if (cpuquiet_curr_governor)
+ ret = sprintf(buf, "%s\n", cpuquiet_curr_governor->name);
+ else
+ ret = sprintf(buf, "none\n");
+
+ mutex_unlock(&cpuquiet_lock);
+
+ return ret;
+
+}
+
+static ssize_t store_current_governor(const char *buf, size_t count)
+{
+ char name[CPUQUIET_NAME_LEN];
+ struct cpuquiet_governor *gov;
+ int len = count, ret = -EINVAL;
+
+ if (!len || len >= sizeof(name))
+ return -EINVAL;
+
+ memcpy(name, buf, count);
+ name[len] = '\0';
+ if (name[len - 1] == '\n')
+ name[--len] = '\0';
+
+ mutex_lock(&cpuquiet_lock);
+ gov = cpuquiet_find_governor(name);
+ mutex_unlock(&cpuquiet_lock);
+
+ if (gov)
+ ret = cpuquiet_switch_governor(gov);
+
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t available_governors_show(char *buf)
+{
+ ssize_t ret = 0, len;
+ struct cpuquiet_governor *gov;
+
+ mutex_lock(&cpuquiet_lock);
+ if (!list_empty(&cpuquiet_governors)) {
+ list_for_each_entry(gov, &cpuquiet_governors, governor_list) {
+ len = sprintf(buf, "%s ", gov->name);
+ buf += len;
+ ret += len;
+ }
+ buf--;
+ *buf = '\n';
+ } else
+ ret = sprintf(buf, "none\n");
+
+ mutex_unlock(&cpuquiet_lock);
+
+ return ret;
+}
+
+struct cpuquiet_sysfs_attr attr_current_governor = __ATTR(current_governor,
+ 0644, show_current_governor, store_current_governor);
+struct cpuquiet_sysfs_attr attr_governors = __ATTR_RO(available_governors);
+
+
+static struct attribute *cpuquiet_default_attrs[] = {
+ &attr_current_governor.attr,
+ &attr_governors.attr,
+ NULL
+};
+
+static ssize_t cpuquiet_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpuquiet_sysfs_attr *cattr =
+ container_of(attr, struct cpuquiet_sysfs_attr, attr);
+
+ return cattr->show(buf);
+}
+
+static ssize_t cpuquiet_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct cpuquiet_sysfs_attr *cattr =
+ container_of(attr, struct cpuquiet_sysfs_attr, attr);
+
+ if (cattr->store)
+ return cattr->store(buf, count);
+
+ return -EINVAL;
+}
+
+static const struct sysfs_ops cpuquiet_sysfs_ops = {
+ .show = cpuquiet_sysfs_show,
+ .store = cpuquiet_sysfs_store,
+};
+
+static struct kobj_type ktype_cpuquiet_sysfs = {
+ .sysfs_ops = &cpuquiet_sysfs_ops,
+ .default_attrs = cpuquiet_default_attrs,
+};
+
+int cpuquiet_add_group(struct attribute_group *attrs)
+{
+ return sysfs_create_group(cpuquiet_global_kobject, attrs);
+}
+
+void cpuquiet_remove_group(struct attribute_group *attrs)
+{
+ sysfs_remove_group(cpuquiet_global_kobject, attrs);
+}
+
+int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name)
+{
+ int err;
+
+ err = kobject_init_and_add(kobj, type, cpuquiet_global_kobject, name);
+ if (!err)
+ kobject_uevent(kobj, KOBJ_ADD);
+
+ return err;
+}
+
+int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name, int cpu)
+{
+ int err;
+
+ err = kobject_init_and_add(kobj, type, &cpuquiet_cpu_devices[cpu]->kobj,
+ name);
+ if (!err)
+ kobject_uevent(kobj, KOBJ_ADD);
+
+ return err;
+}
+
+int cpuquiet_add_class_sysfs(struct sysdev_class *cls)
+{
+ int err;
+
+ cpuquiet_global_kobject = kzalloc(sizeof(*cpuquiet_global_kobject),
+ GFP_KERNEL);
+ if (!cpuquiet_global_kobject)
+ return -ENOMEM;
+
+ err = kobject_init_and_add(cpuquiet_global_kobject,
+ &ktype_cpuquiet_sysfs, &cls->kset.kobj, "cpuquiet");
+ if (!err)
+ kobject_uevent(cpuquiet_global_kobject, KOBJ_ADD);
+
+ return err;
+}
+
+
+struct cpuquiet_attr {
+ struct attribute attr;
+ ssize_t (*show)(unsigned int, char *);
+ ssize_t (*store)(unsigned int, const char *, size_t count);
+};
+
+
+static ssize_t cpuquiet_state_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpuquiet_attr *cattr = container_of(attr,
+ struct cpuquiet_attr, attr);
+ struct cpuquiet_dev *dev = container_of(kobj,
+ struct cpuquiet_dev, kobj);
+
+ return cattr->show(dev->cpu, buf);
+}
+
+static ssize_t cpuquiet_state_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct cpuquiet_attr *cattr = container_of(attr,
+ struct cpuquiet_attr, attr);
+ struct cpuquiet_dev *dev = container_of(kobj,
+ struct cpuquiet_dev, kobj);
+
+ if (cattr->store)
+ return cattr->store(dev->cpu, buf, count);
+
+ return -EINVAL;
+}
+
+static ssize_t show_active(unsigned int cpu, char *buf)
+{
+ return sprintf(buf, "%u\n", cpu_online(cpu));
+}
+
+static ssize_t store_active(unsigned int cpu, const char *value, size_t count)
+{
+ unsigned int active;
+ int ret;
+
+ if (!cpuquiet_curr_governor->store_active)
+ return -EINVAL;
+
+ ret = sscanf(value, "%u", &active);
+ if (ret != 1)
+ return -EINVAL;
+
+ cpuquiet_curr_governor->store_active(cpu, active);
+
+ return count;
+}
+
+struct cpuquiet_attr attr_active = __ATTR(active, 0644, show_active,
+ store_active);
+
+static struct attribute *cpuquiet_default_cpu_attrs[] = {
+ &attr_active.attr,
+ NULL
+};
+
+static const struct sysfs_ops cpuquiet_cpu_sysfs_ops = {
+ .show = cpuquiet_state_show,
+ .store = cpuquiet_state_store,
+};
+
+static struct kobj_type ktype_cpuquiet = {
+ .sysfs_ops = &cpuquiet_cpu_sysfs_ops,
+ .default_attrs = cpuquiet_default_cpu_attrs,
+};
+
+void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu)
+{
+ struct cpuquiet_dev *dev;
+ int err;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev->cpu = cpu;
+ cpuquiet_cpu_devices[cpu] = dev;
+ err = kobject_init_and_add(&dev->kobj, &ktype_cpuquiet,
+ &sys_dev->kobj, "cpuquiet");
+ if (!err)
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+}
+
+void cpuquiet_remove_dev(unsigned int cpu)
+{
+ kobject_put(cpuquiet_cpu_devices[cpu]);
+}
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index 3c1a0ce4638e..7d7285e06fb7 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -1,4 +1,6 @@
GCOV_PROFILE := y
+
+subdir-ccflags-y := -Werror
#
# Makefile for the video capture/playback device drivers.
#
diff --git a/drivers/media/video/tegra/ad5816.c b/drivers/media/video/tegra/ad5816.c
index ed113b3a187c..2005f150d6fd 100644
--- a/drivers/media/video/tegra/ad5816.c
+++ b/drivers/media/video/tegra/ad5816.c
@@ -628,16 +628,6 @@ static int ad5816_dev_id(struct ad5816_info *info)
return err;
}
-static void ad5816_sts_rd(struct ad5816_info *info)
-{
- /**
- * Device specific code for status
- *
- * TODO: Ad5816 has support to get status for over/under
- * voltage conditions but currently this feature is not
- * required.
- */
-}
/**
* Below are device specific functions.
*/
@@ -663,12 +653,13 @@ static int ad5816_position_rd(struct ad5816_info *info, unsigned *position)
static int ad5816_position_wr(struct ad5816_info *info, unsigned position)
{
- position = position + info->config.pos_low;
+ u16 data;
+ position = position + info->config.pos_low;
if(position > info->config.pos_high)
position = info->config.pos_high;
- u16 data = position & 0x03ff;
+ data = position & 0x03ff;
return ad5816_i2c_wr16(info, VCM_CODE_MSB, data);
}
@@ -757,10 +748,8 @@ static int ad5816_param_rd(struct ad5816_info *info, unsigned long arg)
}
static int ad5816_param_wr_s(struct ad5816_info *info,
- struct nvc_param *params,
- u32 u32val)
+ struct nvc_param *params, u32 u32val)
{
- struct nvc_focus_cap cap;
u8 u8val;
int err = 0;
u8val = (u8)u32val;
@@ -1102,10 +1091,11 @@ static int ad5816_probe(
struct i2c_client *client,
const struct i2c_device_id *id)
{
- pr_info("ad5816: probing focuser.\n");
struct ad5816_info *info;
char dname[16];
int err;
+
+ pr_info("ad5816: probing focuser.\n");
dev_dbg(&client->dev, "%s\n", __func__);
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c
index b695aa16af59..c593a0ad756b 100644
--- a/drivers/media/video/tegra/nvavp/nvavp_dev.c
+++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c
@@ -205,8 +205,8 @@ static void nvavp_set_channel_control_area(struct nvavp_info *nvavp, int channel
writel(0x0, &control->get);
pr_debug("nvavp_set_channel_control_area for channel_id (%d):\
- control->put (0x%x) control->get (0x%x)\n",
- channel_id, &control->put, &control->get);
+ control->put (0x%08x) control->get (0x%08x)\n",
+ channel_id, (u32) &control->put, (u32) &control->get);
/* enable avp VDE clock control and disable iram clock gating */
writel(0x0, &control->idle_clk_enable);
@@ -548,7 +548,7 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
control = channel_info->os_control;
pr_debug("nvavp_pushbuffer_update for channel_id (%d):\
control->put (0x%x) control->get (0x%x)\n",
- channel_id, &control->put, &control->get);
+ channel_id, (u32) &control->put, (u32) &control->get);
mutex_lock(&channel_info->pushbuffer_lock);
diff --git a/drivers/media/video/tegra/ov5640.c b/drivers/media/video/tegra/ov5640.c
index b20c036bb06c..26580b0105e3 100644
--- a/drivers/media/video/tegra/ov5640.c
+++ b/drivers/media/video/tegra/ov5640.c
@@ -108,6 +108,7 @@ static int ov5640_read_reg(struct i2c_client *client, u16 addr, u8 *val)
return 0;
}
+#ifdef KERNEL_WARNING
static int ov5640_write_reg(struct i2c_client *client, u8 addr, u8 value)
{
int count;
@@ -134,6 +135,7 @@ static int ov5640_write_reg(struct i2c_client *client, u8 addr, u8 value)
addr, (u32)value);
return -EIO;
}
+#endif
static int ov5640_write_bulk_reg(struct i2c_client *client, u8 *data, int len)
{
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c
index de0c662ba613..5c1cbfe63a28 100644
--- a/drivers/media/video/tegra/tegra_camera.c
+++ b/drivers/media/video/tegra/tegra_camera.c
@@ -183,10 +183,13 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- u32 val;
- void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
- val = readl(apb_misc + 0x42c);
- writel(val | 0x1, apb_misc + 0x42c);
+ {
+ u32 val;
+ void __iomem *apb_misc =
+ IO_ADDRESS(TEGRA_APB_MISC_BASE);
+ val = readl(apb_misc + 0x42c);
+ writel(val | 0x1, apb_misc + 0x42c);
+ }
#endif
}
diff --git a/drivers/misc/bcm4329_rfkill.c b/drivers/misc/bcm4329_rfkill.c
index cf56768e2baa..9dc33fd51e59 100644
--- a/drivers/misc/bcm4329_rfkill.c
+++ b/drivers/misc/bcm4329_rfkill.c
@@ -111,7 +111,9 @@ static int bcm4329_rfkill_probe(struct platform_device *pdev)
ret = gpio_request(bcm4329_rfkill->gpio_reset,
"bcm4329_nreset_gpio");
} else {
- pr_warn("%s : can't find reset gpio.\n", __func__);
+ pr_warn("%s : can't find reset gpio. "
+ "reset gpio may not be defined for "
+ "this platform \n", __func__);
bcm4329_rfkill->gpio_reset = 0;
}
@@ -123,7 +125,9 @@ static int bcm4329_rfkill_probe(struct platform_device *pdev)
ret = gpio_request(bcm4329_rfkill->gpio_shutdown,
"bcm4329_nshutdown_gpio");
} else {
- pr_warn("%s : can't find shutdown gpio.\n", __func__);
+ pr_warn("%s : can't find shutdown gpio "
+ "shutdown gpio may not be defined for "
+ "this platform \n", __func__);
bcm4329_rfkill->gpio_shutdown = 0;
}
diff --git a/drivers/tty/serial/tegra_hsuart.c b/drivers/tty/serial/tegra_hsuart.c
index 484e228dc044..ea20de6ebb41 100644
--- a/drivers/tty/serial/tegra_hsuart.c
+++ b/drivers/tty/serial/tegra_hsuart.c
@@ -895,7 +895,7 @@ static int tegra_startup(struct uart_port *u)
goto fail;
pdata = u->dev->platform_data;
- if (pdata->is_loopback)
+ if (pdata && pdata->is_loopback)
t->mcr_shadow |= UART_MCR_LOOP;
dev_dbg(u->dev, "Requesting IRQ %d\n", u->irq);
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
index 2719a62f873d..9aa155caaebd 100644
--- a/drivers/usb/otg/tegra-otg.c
+++ b/drivers/usb/otg/tegra-otg.c
@@ -66,6 +66,7 @@ struct tegra_otg_data {
callback_t charger_cb;
void *charger_cb_data;
bool interrupt_mode;
+ bool builtin_host;
};
static struct tegra_otg_data *tegra_clone;
@@ -104,8 +105,12 @@ static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en)
clk_enable(tegra->clk);
val = otg_readl(tegra, USB_PHY_WAKEUP);
- if (en)
- val |= USB_INT_EN;
+ if (en) {
+ if (tegra->builtin_host)
+ val |= USB_INT_EN;
+ else
+ val = USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN | USB_ID_PIN_WAKEUP_EN;
+ }
else
val &= ~USB_INT_EN;
otg_writel(tegra, val, USB_PHY_WAKEUP);
@@ -147,6 +152,7 @@ static void tegra_start_host(struct tegra_otg_data *tegra)
memcpy(platform_data, pdata->ehci_pdata,
sizeof(struct tegra_usb_platform_data));
pdev->dev.platform_data = platform_data;
+ tegra->builtin_host = !pdata->ehci_pdata->builtin_host_disabled;
val = platform_device_add(pdev);
if (val)
@@ -253,7 +259,7 @@ static void irq_work(struct work_struct *work)
DBG("%s(%d) got vbus interrupt\n", __func__, __LINE__);
}
- if (!(status & USB_ID_STATUS))
+ if (!(status & USB_ID_STATUS) && (status & USB_ID_INT_EN))
to = OTG_STATE_A_HOST;
else if (status & USB_VBUS_STATUS && from != OTG_STATE_A_HOST)
to = OTG_STATE_B_PERIPHERAL;
@@ -302,8 +308,12 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS))
val |= USB_VBUS_INT_STATUS;
- else if (!(val & USB_ID_STATUS))
- val |= USB_ID_INT_STATUS;
+ else if (!(val & USB_ID_STATUS)) {
+ if(!tegra->builtin_host)
+ val &= ~USB_ID_INT_STATUS;
+ else
+ val |= USB_ID_INT_STATUS;
+ }
else
val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS);
@@ -364,7 +374,6 @@ static ssize_t store_host_en(struct device *dev, struct device_attribute *attr,
struct platform_device *pdev = to_platform_device(dev);
struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
unsigned long host;
- int err;
if (sscanf(buf, "%d", &host) != 1 || host < 0 || host > 1)
return -EINVAL;
@@ -507,9 +516,9 @@ static int tegra_otg_suspend(struct device *dev)
tegra_state_name(otg->state));
clk_enable(tegra->clk);
- val = readl(tegra->regs + USB_PHY_WAKEUP);
- val &= ~USB_INT_EN;
- writel(val, tegra->regs + USB_PHY_WAKEUP);
+ val = otg_readl(tegra, USB_PHY_WAKEUP);
+ val &= ~(USB_ID_INT_EN | USB_VBUS_INT_EN);
+ otg_writel(tegra, val, USB_PHY_WAKEUP);
clk_disable(tegra->clk);
/* Suspend peripheral mode, host mode is taken care by host driver */
@@ -531,8 +540,8 @@ static void tegra_otg_resume(struct device *dev)
/* Clear pending interrupts */
clk_enable(tegra->clk);
- val = readl(tegra->regs + USB_PHY_WAKEUP);
- writel(val, tegra->regs + USB_PHY_WAKEUP);
+ val = otg_readl(tegra, USB_PHY_WAKEUP);
+ otg_writel(tegra, val, USB_PHY_WAKEUP);
DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val);
clk_disable(tegra->clk);
@@ -542,15 +551,16 @@ static void tegra_otg_resume(struct device *dev)
/* Enable interrupt and call work to set to appropriate state */
spin_lock_irqsave(&tegra->lock, flags);
- tegra->int_status = (val | USB_INT_EN);
+ if (tegra->builtin_host)
+ tegra->int_status = val | USB_INT_EN;
+ else
+ tegra->int_status = val | USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN |
+ USB_ID_PIN_WAKEUP_EN;
+
spin_unlock_irqrestore(&tegra->lock, flags);
irq_work(&tegra->work);
- clk_enable(tegra->clk);
- val = readl(tegra->regs + USB_PHY_WAKEUP);
- val |= USB_INT_EN;
- writel(val, tegra->regs + USB_PHY_WAKEUP);
- clk_disable(tegra->clk);
+ enable_interrupt(tegra, true);
DBG("%s(%d) END\n", __func__, __LINE__);
}
diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile
index ebe9e890fad6..8a826412f90c 100644
--- a/drivers/video/tegra/dc/Makefile
+++ b/drivers/video/tegra/dc/Makefile
@@ -1,4 +1,5 @@
GCOV_PROFILE := y
+obj-y += bandwidth.o
obj-y += dc.o
obj-y += rgb.o
obj-y += hdmi.o
diff --git a/drivers/video/tegra/dc/bandwidth.c b/drivers/video/tegra/dc/bandwidth.c
new file mode 100644
index 000000000000..a1da7ef0a995
--- /dev/null
+++ b/drivers/video/tegra/dc/bandwidth.c
@@ -0,0 +1,275 @@
+/*
+ * drivers/video/tegra/dc/bandwidth.c
+ *
+ * Copyright (C) 2010-2012 NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <mach/clk.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+#include <mach/mc.h>
+#include <linux/nvhost.h>
+#include <mach/latency_allowance.h>
+
+#include "dc_reg.h"
+#include "dc_config.h"
+#include "dc_priv.h"
+
+static int use_dynamic_emc = 1;
+
+module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR);
+
+/* uses the larger of w->bandwidth or w->new_bandwidth, and copies
+ * w->new_bandwidth into w->bandwidth */
+static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
+ struct tegra_dc_win *w)
+{
+ /* windows A, B, C for first and second display */
+ static const enum tegra_la_id la_id_tab[2][3] = {
+ /* first display */
+ { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B,
+ TEGRA_LA_DISPLAY_0C },
+ /* second display */
+ { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB,
+ TEGRA_LA_DISPLAY_0CB },
+ };
+ /* window B V-filter tap for first and second display. */
+ static const enum tegra_la_id vfilter_tab[2] = {
+ TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB,
+ };
+ unsigned long bw;
+
+ BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab));
+ BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab));
+ BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab));
+
+ bw = max(w->bandwidth, w->new_bandwidth);
+
+ /* tegra_dc_get_bandwidth() treats V filter windows as double
+ * bandwidth, but LA has a seperate client for V filter */
+ if (w->idx == 1 && win_use_v_filter(dc, w))
+ bw /= 2;
+
+ /* our bandwidth is in kbytes/sec, but LA takes MBps.
+ * round up bandwidth to next 1MBps */
+ bw = bw / 1000 + 1;
+
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+ tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw);
+ /* if window B, also set the 1B client for the 2-tap V filter. */
+ if (w->idx == 1)
+ tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw);
+#endif
+
+ w->bandwidth = w->new_bandwidth;
+}
+
+static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a,
+ struct tegra_dc_win *b)
+{
+ if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b))
+ return 0;
+
+ /* because memory access to load the fifo can overlap, only care
+ * if windows overlap vertically */
+ return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) ||
+ ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y));
+}
+
+static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[],
+ int n)
+{
+ unsigned i;
+ unsigned j;
+ unsigned overlap_count;
+ unsigned max_bw = 0;
+
+ WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely"
+ "inaccurate.\n");
+
+ /* If we had a large number of windows, we would compute adjacency
+ * graph representing 2 window overlaps, find all cliques in the graph,
+ * assign bandwidth to each clique, and then select the clique with
+ * maximum bandwidth. But because we have at most 3 windows,
+ * implementing proper Bron-Kerbosh algorithm would be an overkill,
+ * brute force will suffice.
+ *
+ * Thus: find maximum bandwidth for either single or a pair of windows
+ * and count number of window pair overlaps. If there are three
+ * pairs, all 3 window overlap.
+ */
+
+ overlap_count = 0;
+ for (i = 0; i < n; i++) {
+ unsigned int bw1;
+
+ if (wins[i] == NULL)
+ continue;
+ bw1 = wins[i]->new_bandwidth;
+ if (bw1 > max_bw)
+ /* Single window */
+ max_bw = bw1;
+
+ for (j = i + 1; j < n; j++) {
+ if (wins[j] == NULL)
+ continue;
+ if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) {
+ unsigned int bw2 = wins[j]->new_bandwidth;
+ if (bw1 + bw2 > max_bw)
+ /* Window pair overlaps */
+ max_bw = bw1 + bw2;
+ overlap_count++;
+ }
+ }
+ }
+
+ if (overlap_count == 3)
+ /* All three windows overlap */
+ max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth +
+ wins[2]->new_bandwidth;
+
+ return max_bw;
+}
+
+/*
+ * Calculate peak EMC bandwidth for each enabled window =
+ * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
+ * (windows_tiling ? 2 : 1)
+ *
+ * note:
+ * (*) We use 2 tap V filter, so need double BW if use V filter
+ * (*) Tiling mode on T30 and DDR3 requires double BW
+ *
+ * return:
+ * bandwidth in kBps
+ */
+static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
+ struct tegra_dc_win *w)
+{
+ unsigned long ret;
+ int tiled_windows_bw_multiplier;
+ unsigned long bpp;
+
+ if (!WIN_IS_ENABLED(w))
+ return 0;
+
+ if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 ||
+ w->out_w == 0 || w->out_h == 0)
+ return 0;
+
+ tiled_windows_bw_multiplier =
+ tegra_mc_get_tiled_memory_bandwidth_multiplier();
+
+ /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel,
+ * but the size reported by tegra_dc_fmt_bpp for the planar version
+ * is of the luma plane's size only. */
+ bpp = tegra_dc_is_yuv_planar(w->fmt) ?
+ 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
+ ret = dc->mode.pclk / 1000UL * bpp / 8 * (
+ win_use_v_filter(dc, w) ? 2 : 1) *
+ dfixed_trunc(w->w) / w->out_w * (WIN_IS_TILED(w) ?
+ tiled_windows_bw_multiplier : 1);
+
+ return ret;
+}
+
+static unsigned long tegra_dc_get_bandwidth(
+ struct tegra_dc_win *windows[], int n)
+{
+ int i;
+
+ BUG_ON(n > DC_N_WINDOWS);
+
+ /* emc rate and latency allowance both need to know per window
+ * bandwidths */
+ for (i = 0; i < n; i++) {
+ struct tegra_dc_win *w = windows[i];
+
+ if (w)
+ w->new_bandwidth =
+ tegra_dc_calc_win_bandwidth(w->dc, w);
+ }
+
+ return tegra_dc_find_max_bandwidth(windows, n);
+}
+
+/* to save power, call when display memory clients would be idle */
+void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
+{
+ trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__,
+ dc->emc_clk_rate);
+ if (tegra_is_clk_enabled(dc->emc_clk))
+ clk_disable(dc->emc_clk);
+ dc->emc_clk_rate = 0;
+}
+
+/* use the larger of dc->emc_clk_rate or dc->new_emc_clk_rate, and copies
+ * dc->new_emc_clk_rate into dc->emc_clk_rate.
+ * calling this function both before and after a flip is sufficient to select
+ * the best possible frequency and latency allowance.
+ */
+void tegra_dc_program_bandwidth(struct tegra_dc *dc)
+{
+ unsigned i;
+
+ if (dc->emc_clk_rate != dc->new_emc_clk_rate) {
+ /* going from 0 to non-zero */
+ if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk))
+ clk_enable(dc->emc_clk);
+
+ clk_set_rate(dc->emc_clk,
+ max(dc->emc_clk_rate, dc->new_emc_clk_rate));
+ dc->emc_clk_rate = dc->new_emc_clk_rate;
+
+ if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
+ clk_disable(dc->emc_clk);
+ }
+
+ for (i = 0; i < DC_N_WINDOWS; i++) {
+ struct tegra_dc_win *w = &dc->windows[i];
+
+ if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0)
+ tegra_dc_set_latency_allowance(dc, w);
+ trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx,
+ w->bandwidth);
+ }
+}
+
+int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
+{
+ unsigned long new_rate;
+ struct tegra_dc *dc;
+
+ if (!use_dynamic_emc)
+ return 0;
+
+ dc = windows[0]->dc;
+
+ /* calculate the new rate based on this POST */
+ new_rate = tegra_dc_get_bandwidth(windows, n);
+ if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n"))
+ new_rate = ULONG_MAX;
+ else
+ new_rate = EMC_BW_TO_FREQ(new_rate * 1000);
+
+ if (tegra_dc_has_multiple_dc())
+ new_rate = ULONG_MAX;
+
+ trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate);
+ dc->new_emc_clk_rate = new_rate;
+
+ return 0;
+}
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 5c960f9e45c0..f4844f1eecfc 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -85,106 +85,11 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc);
module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
-static int use_dynamic_emc = 1;
-
-module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR);
-
struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
DEFINE_MUTEX(tegra_dc_lock);
DEFINE_MUTEX(shared_lock);
-static inline bool win_use_v_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
-{
- return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) &&
- win->h.full != dfixed_const(win->out_h);
-}
-static inline bool win_use_h_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
-{
- return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) &&
- win->w.full != dfixed_const(win->out_w);
-}
-
-static inline int tegra_dc_fmt_bpp(int fmt)
-{
- switch (fmt) {
- case TEGRA_WIN_FMT_P1:
- return 1;
-
- case TEGRA_WIN_FMT_P2:
- return 2;
-
- case TEGRA_WIN_FMT_P4:
- return 4;
-
- case TEGRA_WIN_FMT_P8:
- return 8;
-
- case TEGRA_WIN_FMT_B4G4R4A4:
- case TEGRA_WIN_FMT_B5G5R5A:
- case TEGRA_WIN_FMT_B5G6R5:
- case TEGRA_WIN_FMT_AB5G5R5:
- return 16;
-
- case TEGRA_WIN_FMT_B8G8R8A8:
- case TEGRA_WIN_FMT_R8G8B8A8:
- case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
- case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
- return 32;
-
- /* for planar formats, size of the Y plane, 8bit */
- case TEGRA_WIN_FMT_YCbCr420P:
- case TEGRA_WIN_FMT_YUV420P:
- case TEGRA_WIN_FMT_YCbCr422P:
- case TEGRA_WIN_FMT_YUV422P:
- case TEGRA_WIN_FMT_YCbCr422R:
- case TEGRA_WIN_FMT_YUV422R:
- case TEGRA_WIN_FMT_YCbCr422RA:
- case TEGRA_WIN_FMT_YUV422RA:
- return 8;
-
- /* YUYV packed into 32-bits */
- case TEGRA_WIN_FMT_YCbCr422:
- case TEGRA_WIN_FMT_YUV422:
- return 16;
- }
- return 0;
-}
-
-static inline bool tegra_dc_is_yuv(int fmt)
-{
- switch (fmt) {
- case TEGRA_WIN_FMT_YUV420P:
- case TEGRA_WIN_FMT_YCbCr420P:
- case TEGRA_WIN_FMT_YCbCr422P:
- case TEGRA_WIN_FMT_YUV422P:
- case TEGRA_WIN_FMT_YCbCr422:
- case TEGRA_WIN_FMT_YUV422:
- case TEGRA_WIN_FMT_YCbCr422R:
- case TEGRA_WIN_FMT_YUV422R:
- case TEGRA_WIN_FMT_YCbCr422RA:
- case TEGRA_WIN_FMT_YUV422RA:
- return true;
- }
- return false;
-}
-
-static inline bool tegra_dc_is_yuv_planar(int fmt)
-{
- switch (fmt) {
- case TEGRA_WIN_FMT_YUV420P:
- case TEGRA_WIN_FMT_YCbCr420P:
- case TEGRA_WIN_FMT_YCbCr422P:
- case TEGRA_WIN_FMT_YUV422P:
- case TEGRA_WIN_FMT_YCbCr422R:
- case TEGRA_WIN_FMT_YUV422R:
- case TEGRA_WIN_FMT_YCbCr422RA:
- case TEGRA_WIN_FMT_YUV422RA:
- return true;
- }
- return false;
-}
-
#define DUMP_REG(a) do { \
snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
#a, a, tegra_dc_readl(dc, a)); \
@@ -569,7 +474,7 @@ out:
return ret;
}
-static unsigned int tegra_dc_has_multiple_dc(void)
+unsigned int tegra_dc_has_multiple_dc(void)
{
unsigned int idx;
unsigned int cnt = 0;
@@ -587,7 +492,6 @@ static unsigned int tegra_dc_has_multiple_dc(void)
* return: stride size in bytes for window win. or 0 if unavailble. */
int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
{
- u32 tmp;
u32 stride;
if (!dc->enabled)
@@ -595,8 +499,8 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
BUG_ON(win > DC_N_WINDOWS);
tegra_dc_writel(dc, WINDOW_A_SELECT << win,
DC_CMD_DISPLAY_WINDOW_HEADER);
- tmp = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
- return GET_LINE_STRIDE(tmp);
+ stride = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
+ return GET_LINE_STRIDE(stride);
}
EXPORT_SYMBOL(tegra_dc_get_stride);
@@ -883,239 +787,6 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
}
}
-static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
- struct tegra_dc_win *w)
-{
- /* windows A, B, C for first and second display */
- static const enum tegra_la_id la_id_tab[2][3] = {
- /* first display */
- { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B,
- TEGRA_LA_DISPLAY_0C },
- /* second display */
- { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB,
- TEGRA_LA_DISPLAY_0CB },
- };
- /* window B V-filter tap for first and second display. */
- static const enum tegra_la_id vfilter_tab[2] = {
- TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB,
- };
- unsigned long bw;
-
- BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab));
- BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab));
- BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab));
-
- bw = w->new_bandwidth;
-
- /* tegra_dc_get_bandwidth() treats V filter windows as double
- * bandwidth, but LA has a seperate client for V filter */
- if (w->idx == 1 && win_use_v_filter(dc, w))
- bw /= 2;
-
- /* our bandwidth is in kbytes/sec, but LA takes MBps.
- * round up bandwidth to next 1MBps */
- bw = bw / 1000 + 1;
-
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
- tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw);
- /* if window B, also set the 1B client for the 2-tap V filter. */
- if (w->idx == 1)
- tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw);
-#endif
-
- w->bandwidth = w->new_bandwidth;
-}
-
-static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a,
- struct tegra_dc_win *b)
-{
- if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b))
- return 0;
-
- /* because memory access to load the fifo can overlap, only care
- * if windows overlap vertically */
- return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) ||
- ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y));
-}
-
-static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[],
- int n)
-{
- unsigned i;
- unsigned j;
- unsigned overlap_count;
- unsigned max_bw = 0;
-
- WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely"
- "inaccurate.\n");
-
- /* If we had a large number of windows, we would compute adjacency
- * graph representing 2 window overlaps, find all cliques in the graph,
- * assign bandwidth to each clique, and then select the clique with
- * maximum bandwidth. But because we have at most 3 windows,
- * implementing proper Bron-Kerbosh algorithm would be an overkill,
- * brute force will suffice.
- *
- * Thus: find maximum bandwidth for either single or a pair of windows
- * and count number of window pair overlaps. If there are three
- * pairs, all 3 window overlap.
- */
-
- overlap_count = 0;
- for (i = 0; i < n; i++) {
- unsigned int bw1;
-
- if (wins[i] == NULL)
- continue;
- bw1 = wins[i]->new_bandwidth;
- if (bw1 > max_bw)
- /* Single window */
- max_bw = bw1;
-
- for (j = i + 1; j < n; j++) {
- if (wins[j] == NULL)
- continue;
- if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) {
- unsigned int bw2 = wins[j]->new_bandwidth;
- if (bw1 + bw2 > max_bw)
- /* Window pair overlaps */
- max_bw = bw1 + bw2;
- overlap_count++;
- }
- }
- }
-
- if (overlap_count == 3)
- /* All three windows overlap */
- max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth +
- wins[2]->new_bandwidth;
-
- return max_bw;
-}
-
-/*
- * Calculate peak EMC bandwidth for each enabled window =
- * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
- * (windows_tiling ? 2 : 1)
- *
- * note:
- * (*) We use 2 tap V filter, so need double BW if use V filter
- * (*) Tiling mode on T30 and DDR3 requires double BW
- *
- * return:
- * bandwidth in kBps
- */
-static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
- struct tegra_dc_win *w)
-{
- unsigned long ret;
- int tiled_windows_bw_multiplier;
- unsigned long bpp;
-
- if (!WIN_IS_ENABLED(w))
- return 0;
-
- if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 ||
- w->out_w == 0 || w->out_h == 0)
- return 0;
-
- tiled_windows_bw_multiplier =
- tegra_mc_get_tiled_memory_bandwidth_multiplier();
-
- /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel,
- * but the size reported by tegra_dc_fmt_bpp for the planar version
- * is of the luma plane's size only. */
- bpp = tegra_dc_is_yuv_planar(w->fmt) ?
- 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
- ret = dc->mode.pclk / 1000UL * bpp / 8 * (win_use_v_filter(dc, w) ? 2 : 1)
- * dfixed_trunc(w->w) / w->out_w *
- (WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1);
-
- return ret;
-}
-
-static unsigned long tegra_dc_get_bandwidth(
- struct tegra_dc_win *windows[], int n)
-{
- int i;
-
- BUG_ON(n > DC_N_WINDOWS);
-
- /* emc rate and latency allowance both need to know per window
- * bandwidths */
- for (i = 0; i < n; i++) {
- struct tegra_dc_win *w = windows[i];
-
- if (w)
- w->new_bandwidth =
- tegra_dc_calc_win_bandwidth(w->dc, w);
- }
-
- return tegra_dc_find_max_bandwidth(windows, n);
-}
-
-/* to save power, call when display memory clients would be idle */
-static void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
-{
- trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__,
- dc->emc_clk_rate);
- if (tegra_is_clk_enabled(dc->emc_clk))
- clk_disable(dc->emc_clk);
- dc->emc_clk_rate = 0;
-}
-
-static void tegra_dc_program_bandwidth(struct tegra_dc *dc)
-{
- unsigned i;
-
- if (dc->emc_clk_rate != dc->new_emc_clk_rate) {
- /* going from 0 to non-zero */
- if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk))
- clk_enable(dc->emc_clk);
-
- dc->emc_clk_rate = dc->new_emc_clk_rate;
- clk_set_rate(dc->emc_clk, dc->emc_clk_rate);
-
- if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
- clk_disable(dc->emc_clk);
- }
-
- for (i = 0; i < DC_N_WINDOWS; i++) {
- struct tegra_dc_win *w = &dc->windows[i];
-
- if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0)
- tegra_dc_set_latency_allowance(dc, w);
- trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx,
- w->bandwidth);
- }
-}
-
-static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
-{
- unsigned long new_rate;
- struct tegra_dc *dc;
-
- if (!use_dynamic_emc)
- return 0;
-
- dc = windows[0]->dc;
-
- /* calculate the new rate based on this POST */
- new_rate = tegra_dc_get_bandwidth(windows, n);
- if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n"))
- new_rate = ULONG_MAX;
- else
- new_rate = EMC_BW_TO_FREQ(new_rate * 1000);
-
- if (tegra_dc_has_multiple_dc())
- new_rate = ULONG_MAX;
-
- trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate);
- dc->new_emc_clk_rate = new_rate;
-
- return 0;
-}
-
static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int,
bool v, unsigned Bpp)
{
@@ -2128,6 +1799,10 @@ static void tegra_dc_vblank(struct work_struct *work)
bool nvsd_updated = false;
mutex_lock(&dc->lock);
+ /* use the new frame's bandwidth setting instead of max(current, new),
+ * skip this if we're using tegra_dc_one_shot_worker() */
+ if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE))
+ tegra_dc_program_bandwidth(dc);
/* Update the SD brightness */
if (dc->enabled && dc->out->sd_settings)
@@ -3226,6 +2901,17 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
#endif /* CONFIG_PM */
+static void tegra_dc_shutdown(struct nvhost_device *ndev)
+{
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+
+ if (!dc || !dc->enabled)
+ return;
+
+ tegra_dc_blank(dc);
+ tegra_dc_disable(dc);
+}
+
extern int suspend_set(const char *val, struct kernel_param *kp)
{
if (!strcmp(val, "dump"))
@@ -3260,6 +2946,7 @@ struct nvhost_driver tegra_dc_driver = {
.suspend = tegra_dc_suspend,
.resume = tegra_dc_resume,
#endif
+ .shutdown = tegra_dc_shutdown,
};
static int __init tegra_dc_module_init(void)
diff --git a/drivers/video/tegra/dc/dc_config.h b/drivers/video/tegra/dc/dc_config.h
index 314cd11e77f9..4cc924184275 100644
--- a/drivers/video/tegra/dc/dc_config.h
+++ b/drivers/video/tegra/dc/dc_config.h
@@ -145,4 +145,18 @@ int tegra_dc_feature_has_filter(struct tegra_dc *dc, int win_idx, int operation)
long *tegra_dc_parse_feature(struct tegra_dc *dc, int win_idx, int operation);
void tegra_dc_feature_register(struct tegra_dc *dc);
+
+static inline bool win_use_v_filter(struct tegra_dc *dc,
+ const struct tegra_dc_win *win)
+{
+ return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) &&
+ win->h.full != dfixed_const(win->out_h);
+}
+static inline bool win_use_h_filter(struct tegra_dc *dc,
+ const struct tegra_dc_win *win)
+{
+ return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) &&
+ win->w.full != dfixed_const(win->out_w);
+}
+
#endif
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index ce7ac3c7b0d0..4f53f60f2599 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -205,6 +205,86 @@ static inline unsigned long tegra_dc_get_default_emc_clk_rate(
return dc->pdata->emc_clk_rate ? dc->pdata->emc_clk_rate : ULONG_MAX;
}
+static inline int tegra_dc_fmt_bpp(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_P1:
+ return 1;
+
+ case TEGRA_WIN_FMT_P2:
+ return 2;
+
+ case TEGRA_WIN_FMT_P4:
+ return 4;
+
+ case TEGRA_WIN_FMT_P8:
+ return 8;
+
+ case TEGRA_WIN_FMT_B4G4R4A4:
+ case TEGRA_WIN_FMT_B5G5R5A:
+ case TEGRA_WIN_FMT_B5G6R5:
+ case TEGRA_WIN_FMT_AB5G5R5:
+ return 16;
+
+ case TEGRA_WIN_FMT_B8G8R8A8:
+ case TEGRA_WIN_FMT_R8G8B8A8:
+ case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
+ case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
+ return 32;
+
+ /* for planar formats, size of the Y plane, 8bit */
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
+ return 8;
+
+ /* YUYV packed into 32-bits */
+ case TEGRA_WIN_FMT_YCbCr422:
+ case TEGRA_WIN_FMT_YUV422:
+ return 16;
+ }
+ return 0;
+}
+
+static inline bool tegra_dc_is_yuv(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422:
+ case TEGRA_WIN_FMT_YUV422:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
+ return true;
+ }
+ return false;
+}
+
+static inline bool tegra_dc_is_yuv_planar(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
+ return true;
+ }
+ return false;
+}
+
void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk);
extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
@@ -227,5 +307,11 @@ void tegra_dc_disable_crc(struct tegra_dc *dc);
void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
const struct tegra_dc_out_pin *pins,
const unsigned int n_pins);
-#endif
+/* defined in dc.c, used in bandwidth.c */
+unsigned int tegra_dc_has_multiple_dc(void);
+/* defined in bandwidth.c, used in dc.c */
+void tegra_dc_clear_bandwidth(struct tegra_dc *dc);
+void tegra_dc_program_bandwidth(struct tegra_dc *dc);
+int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n);
+#endif
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
index 59b9afcec0e4..3cd8de5085cd 100644
--- a/drivers/video/tegra/dc/hdmi.c
+++ b/drivers/video/tegra/dc/hdmi.c
@@ -1213,22 +1213,6 @@ static int tegra_dc_calc_clock_per_frame(const struct fb_videomode *mode)
(mode->upper_margin + mode->yres +
mode->lower_margin + mode->vsync_len);
}
-static bool tegra_dc_hdmi_mode_equal(const struct fb_videomode *mode1,
- const struct fb_videomode *mode2)
-{
- int clock_per_frame1 = tegra_dc_calc_clock_per_frame(mode1);
- int clock_per_frame2 = tegra_dc_calc_clock_per_frame(mode2);
-
- /* allows up to 1Hz of pixclock difference */
- return (clock_per_frame1 == clock_per_frame2 &&
- mode1->xres == mode2->xres &&
- mode1->yres == mode2->yres &&
- mode1->vmode == mode2->vmode &&
- (mode1->pixclock == mode2->pixclock ||
- (abs(PICOS2KHZ(mode1->pixclock) -
- PICOS2KHZ(mode2->pixclock)) *
- 1000 / clock_per_frame1 <= 1)));
-}
static bool tegra_dc_hdmi_valid_pixclock(const struct tegra_dc *dc,
const struct fb_videomode *mode)
diff --git a/include/linux/cpuquiet.h b/include/linux/cpuquiet.h
new file mode 100644
index 000000000000..8459af7aad74
--- /dev/null
+++ b/include/linux/cpuquiet.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _LINUX_CPUONLINE_H
+#define _LINUX_CPUONLINE_H
+
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+
+#define CPUQUIET_NAME_LEN 16
+
+struct cpuquiet_governor {
+ char name[CPUQUIET_NAME_LEN];
+ struct list_head governor_list;
+ int (*start) (void);
+ void (*stop) (void);
+ int (*store_active) (unsigned int cpu, bool active);
+ struct module *owner;
+};
+
+struct cpuquiet_driver {
+ char name[CPUQUIET_NAME_LEN];
+ int (*quiesence_cpu) (unsigned int cpunumber);
+ int (*wake_cpu) (unsigned int cpunumber);
+};
+
+extern int cpuquiet_register_governor(struct cpuquiet_governor *gov);
+extern void cpuquiet_unregister_governor(struct cpuquiet_governor *gov);
+extern int cpuquiet_quiesence_cpu(unsigned int cpunumber);
+extern int cpuquiet_wake_cpu(unsigned int cpunumber);
+extern int cpuquiet_register_driver(struct cpuquiet_driver *drv);
+extern void cpuquiet_unregister_driver(struct cpuquiet_driver *drv);
+extern int cpuquiet_add_group(struct attribute_group *attrs);
+extern void cpuquiet_remove_group(struct attribute_group *attrs);
+int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name);
+extern unsigned int nr_cluster_ids;
+#endif
diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h
index 911175086977..97472714e8e6 100644
--- a/include/linux/platform_data/tegra_usb.h
+++ b/include/linux/platform_data/tegra_usb.h
@@ -47,6 +47,7 @@ struct tegra_utmi_config {
u8 xcvr_lsfslew;
u8 xcvr_lsrslew;
signed char xcvr_setup_offset;
+ u8 xcvr_use_lsb;
u8 xcvr_use_fuses;
};
@@ -119,6 +120,7 @@ struct tegra_usb_host_mode_data {
struct tegra_usb_platform_data {
bool port_otg;
bool has_hostpc;
+ bool builtin_host_disabled;
enum tegra_usb_phy_interface phy_intf;
enum tegra_usb_operation_mode op_mode;
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 8be00b840fb5..a50b853135ae 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -346,6 +346,7 @@ static int tegra30_i2s_tdm_setup_clocks(struct device *dev,
dev_err(dev, "Can't set parent of I2S clock\n");
return ret;
}
+
ret = clk_set_rate(i2s->clk_i2s, *i2sclock);
if (ret) {
dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
@@ -359,6 +360,13 @@ static int tegra30_i2s_tdm_setup_clocks(struct device *dev,
return ret;
}
+ ret = clk_set_parent(clk_get_parent(i2s->clk_audio_2x),
+ i2s->clk_i2s_sync);
+ if (ret) {
+ dev_err(dev, "Can't set parent of audio2x clock\n");
+ return ret;
+ }
+
ret = clk_set_rate(i2s->clk_audio_2x, *i2sclock);
if (ret) {
dev_err(dev, "Can't set audio2x clock rate\n");
@@ -367,7 +375,7 @@ static int tegra30_i2s_tdm_setup_clocks(struct device *dev,
ret = clk_set_parent(i2s->clk_i2s, i2s->clk_audio_2x);
if (ret) {
- dev_err(dev, "Can't set parent of audio2x clock\n");
+ dev_err(dev, "Can't set parent of i2s clock\n");
return ret;
}
}
@@ -382,7 +390,8 @@ static int tegra30_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
struct device *dev = substream->pcm->card->dev;
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
u32 val;
- int i2s_client_ch, i2s_audio_ch, i2s_audio_bits, i2s_client_bits;
+ int i2s_client_ch, i2s_audio_ch;
+ int i2s_audio_bits = 0, i2s_client_bits = 0;
int i2sclock, srate;
int ret;