diff options
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; |