diff options
Diffstat (limited to 'arch/arm/mach-omap2')
36 files changed, 2161 insertions, 92 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index f49cd51e162..662b2bb0921 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -207,6 +207,12 @@ config MACH_OMAP3_BEAGLE  	depends on ARCH_OMAP3  	default y  	select OMAP_PACKAGE_CBB +	 +config MACH_OMAP3_H1 +	bool "OMAP3 H1 board" +	depends on ARCH_OMAP3 +	default y +	select OMAP_PACKAGE_CBP  config MACH_DEVKIT8000  	bool "DEVKIT8000 board" @@ -368,6 +374,12 @@ config MACH_OMAP_3630SDP  	default y  	select OMAP_PACKAGE_CBP +config MACH_MINNOW +	bool "Motorola Minnow Product" +	depends on ARCH_OMAP3 +	default y +	select OMAP_PACKAGE_CBP +  config MACH_TI8168EVM  	bool "TI8168 Evaluation Module"  	depends on SOC_TI81XX @@ -414,6 +426,25 @@ config OMAP3_SDRC_AC_TIMING  	  wish to say no.  Selecting yes without understanding what is  	  going on could result in system crashes; + +config OMAP3_PAD_WKUP_IO +	bool "Enable Generation of IRQ from pad status" +	depends on ARCH_OMAP3 +	default n +	help +	 For offmode, gpio (and possibly other) domains can wakeup the system +	 but will not retain their irq status internally.  This function generates +	 the configured irq on each time the io irq (315) fires. + +config DISABLE_OMAP_ERRATA_i583 +	bool "Override errata i583" +	depends on ARCH_OMAP3 +	default n +	help +	  Errata i583 affects JDEC memory timing when waking from OFF mode on +	  ES 1.1 and older OMAP3.  If the system clock is 26MHz or 38.4MHz and +	  there is no DDR on CS1, then it is safe to disable this errata check. +  config OMAP4_ERRATA_I688  	bool "OMAP4 errata: Async Bridge Corruption"  	depends on (ARCH_OMAP4 || SOC_OMAP5) && !ARCH_MULTIPLATFORM diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 55a9d677768..8df971b821b 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -8,7 +8,7 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \  # Common support  obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o gpmc.o timer.o pm.o \  	 common.o gpio.o dma.o wd_timer.o display.o i2c.o hdq1w.o omap_hwmod.o \ -	 omap_device.o sram.o +	 omap_device.o sram.o pad_wkup.o  omap-2-3-common				= irq.o  hwmod-common				= omap_hwmod.o omap_hwmod_reset.o \ @@ -85,7 +85,7 @@ obj-$(CONFIG_ARCH_OMAP2)		+= sleep24xx.o  obj-$(CONFIG_ARCH_OMAP3)		+= pm34xx.o sleep34xx.o  obj-$(CONFIG_ARCH_OMAP4)		+= pm44xx.o omap-mpuss-lowpower.o  obj-$(CONFIG_SOC_OMAP5)			+= omap-mpuss-lowpower.o -obj-$(CONFIG_PM_DEBUG)			+= pm-debug.o +obj-$(CONFIG_PM_DEBUG)			+= pm-debug.o pm-debug-regs.o  obj-$(CONFIG_POWER_AVS_OMAP)		+= sr_device.o  obj-$(CONFIG_POWER_AVS_OMAP_CLASS3)    += smartreflex-class3.o @@ -225,6 +225,8 @@ obj-$(CONFIG_MACH_OMAP_GENERIC)		+= board-generic.o  obj-$(CONFIG_MACH_OMAP_H4)		+= board-h4.o  obj-$(CONFIG_MACH_OMAP_2430SDP)		+= board-2430sdp.o  obj-$(CONFIG_MACH_OMAP3_BEAGLE)		+= board-omap3beagle.o +obj-$(CONFIG_MACH_OMAP3_H1)			+= board-omap3h1.o \ +										board-omap3h1-bluetooth.o  obj-$(CONFIG_MACH_DEVKIT8000)     	+= board-devkit8000.o  obj-$(CONFIG_MACH_OMAP_LDP)		+= board-ldp.o  obj-$(CONFIG_MACH_OMAP3530_LV_SOM)      += board-omap3logic.o @@ -253,6 +255,7 @@ obj-$(CONFIG_MACH_IGEP0020)		+= board-igep0020.o  obj-$(CONFIG_MACH_TOUCHBOOK)		+= board-omap3touchbook.o  obj-$(CONFIG_MACH_OMAP_4430SDP)		+= board-4430sdp.o  obj-$(CONFIG_MACH_OMAP4_PANDA)		+= board-omap4panda.o +obj-$(CONFIG_MACH_MINNOW)		+= board-minnow.o  obj-$(CONFIG_MACH_OMAP3517EVM)		+= board-am3517evm.o diff --git a/arch/arm/mach-omap2/board-minnow.c b/arch/arm/mach-omap2/board-minnow.c new file mode 100644 index 00000000000..af6f13b371d --- /dev/null +++ b/arch/arm/mach-omap2/board-minnow.c @@ -0,0 +1,122 @@ +/* + * linux/arch/arm/mach-omap2/board-minnow.c + * + * Copyright (C) 2013 Motorola Mobility, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <asm/mach/arch.h> +#include <asm/mach-types.h> + +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/input/touch_platform.h> +#include <linux/usb/musb.h> +#include <linux/usb/phy.h> +#include <linux/usb/nop-usb-xceiv.h> +#include <linux/ti_wilink_st.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include "mux.h" +#include "common.h" +#include "dss-common.h" +#include "control.h" + +#include "sdram-toshiba-hynix-numonyx.h" + +static int platform_wilink_kim_suspend(struct platform_device *pdev, +		pm_message_t msg); +static int platform_wilink_kim_resume(struct platform_device *pdev); + +static struct of_device_id omap_dt_match_table[] __initdata = { +	{ .compatible = "simple-bus", }, +	{ .compatible = "ti,omap-infra", }, +	{ } +}; + +static const char *omap3_gp_boards_compat[] __initdata = { +	"mot,omap3-minnow", +	NULL, +}; + +struct ti_st_plat_data wilink_pdata = { +	.nshutdown_gpio = 83, +	.dev_name = "/dev/ttyO1", +	.port_index = 1, +	.flow_cntrl = 1, +	.baud_rate = 3000000, +	.suspend = platform_wilink_kim_suspend, +	.resume = platform_wilink_kim_resume, +}; + +static struct platform_device wl18xx_device = { +	.name              = "kim", +	.id                = -1, +	.dev.platform_data = &wilink_pdata, +}; + +static struct platform_device hci_tty_device = { +	.name = "hci_tty", +	.id = -1, +}; + +static int platform_wilink_kim_suspend(struct platform_device *pdev, +		pm_message_t msg) +{ +	return 0; +} + +static int platform_wilink_kim_resume(struct platform_device *pdev) +{ +	return 0; +} + +static inline void __init minnow_init_btwilink(void) +{ +	platform_device_register(&wl18xx_device); +	platform_device_register(&hci_tty_device); +} + +static void __init minnow_init_gpio_clock(void) +{ +	struct of_phandle_args clkspec; +	struct clk *clk; +	struct clk_lookup *cl; +	clkspec.np = of_find_compatible_node(NULL, NULL, "gpio-clock"); +	if (clkspec.np) { +		of_gpio_clk_setup(clkspec.np); +		clk = of_clk_get_from_provider(&clkspec); +		if (!IS_ERR(clk)) { +			cl = clkdev_alloc(clk, clkspec.np->name, NULL); +			if (cl) +				clkdev_add(cl); +		} +	} +} + +static void __init minnow_init(void) +{ +	of_platform_populate(NULL, omap_dt_match_table, NULL, NULL); +	minnow_init_gpio_clock(); +	omap_sdrc_init(JEDEC_JESD209A_sdrc_params, JEDEC_JESD209A_sdrc_params); +	omap3_enable_usim_buffer(); /* Needed for GPIOs in USIM block */ +	omap_minnow_display_init(); +	minnow_init_btwilink(); +} + +MACHINE_START(MINNOW, "minnow") +	.atag_offset    = 0x100, +	.reserve        = omap_reserve, +	.map_io         = omap3_map_io, +	.init_early     = omap3630_init_early, +	.init_irq       = omap_intc_of_init, +	.handle_irq     = omap3_intc_handle_irq, +	.init_machine   = minnow_init, +	.init_late      = omap3630_init_late, +	.init_time      = omap3_sync32k_timer_init, +	.dt_compat	    = omap3_gp_boards_compat, +	.restart        = omap3xxx_restart, +MACHINE_END diff --git a/arch/arm/mach-omap2/board-omap3h1-bluetooth.c b/arch/arm/mach-omap2/board-omap3h1-bluetooth.c new file mode 100644 index 00000000000..52bf419b549 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3h1-bluetooth.c @@ -0,0 +1,387 @@ +/* + * Bluetooth Broadcomm  and low power control via GPIO + * + *  Copyright (C) 2011 Google, Inc. + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ +  + /* +  * Adapted from board-tuna-bluetooth.c by Evan Wilson <evan@oliodevices.com +  * +  */ +  + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/hrtimer.h> +#include <linux/irq.h> +#include <linux/rfkill.h> +#include <linux/platform_device.h> +#include <linux/wakelock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <asm/mach-types.h> +#include "serial.h" +#include "board-omap3h1.h" +#include <linux/regulator/driver.h> + +static void update_host_wake_locked(int); + +#define BT_REG_GPIO 180 + +#define BT_WAKE_GPIO 93 +#define BT_HOST_WAKE_GPIO 11 + +static struct rfkill *bt_rfkill; +static struct regulator *clk32ksys_reg; +static bool bt_enabled; +static bool host_wake_uart_enabled; +static bool wake_uart_enabled; +static struct dentry *btdebugdent; + +struct bcm_bt_lpm { +	int wake; +	int host_wake; + +	struct hrtimer enter_lpm_timer; +	ktime_t enter_lpm_delay; + +	struct uart_port *uport; + +	struct wake_lock wake_lock; +	char wake_lock_name[100]; +} bt_lpm; + +static int bcm20702_bt_rfkill_set_power(void *data, bool blocked) +{ +	// rfkill_ops callback. Turn transmitter on when blocked is false +	if (!blocked) { +		if (clk32ksys_reg && !bt_enabled) +			regulator_enable(clk32ksys_reg); + +		gpio_set_value(BT_REG_GPIO, 1); + +	} else { +		// Chip won't toggle host_wake after reset.  Make sure +		// we don't hold the wake_lock until chip wakes up again. +		update_host_wake_locked(0); +		 +		gpio_set_value(BT_REG_GPIO, 0); +		if (clk32ksys_reg && bt_enabled) +			regulator_disable(clk32ksys_reg); +	} + +	bt_enabled = !blocked; + +	return 0; +} + +static const struct rfkill_ops bcm20702_bt_rfkill_ops = { +	.set_block = bcm20702_bt_rfkill_set_power, +}; + +static void set_wake_locked(int wake) +{ +	bt_lpm.wake = wake; + +	if (!wake) +		wake_unlock(&bt_lpm.wake_lock); + +	if (!wake_uart_enabled && wake) +		//omap_uart_enable(2); + +	gpio_set_value(BT_WAKE_GPIO, wake); + +	if (wake_uart_enabled && !wake) +		//omap_uart_disable(2); + +	wake_uart_enabled = wake; +} + +static enum hrtimer_restart enter_lpm(struct hrtimer *timer) { +	unsigned long flags; +	spin_lock_irqsave(&bt_lpm.uport->lock, flags); +	set_wake_locked(0); +	spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + +	return HRTIMER_NORESTART; +} + +void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport) { +	bt_lpm.uport = uport; + +	hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer); + +	set_wake_locked(1); + +	hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay, +		HRTIMER_MODE_REL); +} +EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked); + +static void update_host_wake_locked(int host_wake) +{ +	if (host_wake == bt_lpm.host_wake) +		return; + +	bt_lpm.host_wake = host_wake; + +	if (host_wake) { +		wake_lock(&bt_lpm.wake_lock); +		if (!host_wake_uart_enabled) { +			//omap_uart_enable(2); +		} +	} else  { +		if (host_wake_uart_enabled) { +			//omap_uart_disable(2); +		} +		// Take a timed wakelock, so that upper layers can take it. +		// The chipset deasserts the hostwake lock, when there is no +		// more data to send. +		wake_lock_timeout(&bt_lpm.wake_lock, HZ/2); +	} + +	host_wake_uart_enabled = host_wake; + +} + +static irqreturn_t host_wake_isr(int irq, void *dev) +{ +	int host_wake; +	unsigned long flags; + +	host_wake = gpio_get_value(BT_HOST_WAKE_GPIO); +	irq_set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + +	if (!bt_lpm.uport) { +		bt_lpm.host_wake = host_wake; +		return IRQ_HANDLED; +	} + +	spin_lock_irqsave(&bt_lpm.uport->lock, flags); +	update_host_wake_locked(host_wake); +	spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + +	return IRQ_HANDLED; +} + +static int bcm_bt_lpm_init(struct platform_device *pdev) +{ +	int irq; +	int ret; +	int rc; + +	rc = gpio_request(BT_WAKE_GPIO, "bcm20702_wake_gpio"); +	if (unlikely(rc)) { +		return rc; +	} + +	rc = gpio_request(BT_HOST_WAKE_GPIO, "bcm20702_host_wake_gpio"); +	if (unlikely(rc)) { +		gpio_free(BT_WAKE_GPIO); +		return rc; +	} + +	hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +	bt_lpm.enter_lpm_delay = ktime_set(1, 0);  /* 1 sec */ +	bt_lpm.enter_lpm_timer.function = enter_lpm; + +	bt_lpm.host_wake = 0; + +	irq = gpio_to_irq(BT_HOST_WAKE_GPIO); +	ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH, +		"bt host_wake", NULL); +	if (ret) { +		gpio_free(BT_WAKE_GPIO); +		gpio_free(BT_HOST_WAKE_GPIO); +		return ret; +	} + +	ret = irq_set_irq_wake(irq, 1); +	if (ret) { +		gpio_free(BT_WAKE_GPIO); +		gpio_free(BT_HOST_WAKE_GPIO); +		return ret; +	} + +	gpio_direction_output(BT_WAKE_GPIO, 0); +	gpio_direction_input(BT_HOST_WAKE_GPIO); + +	snprintf(bt_lpm.wake_lock_name, sizeof(bt_lpm.wake_lock_name), +			"BTLowPower"); +	wake_lock_init(&bt_lpm.wake_lock, WAKE_LOCK_SUSPEND, +			 bt_lpm.wake_lock_name); +	return 0; +} + +static int btdebug_dump(struct seq_file *sf, void *private) +{ +	seq_printf(sf, "en=%d bt_wake=%d lpm.w=%d w_uart_en=%d\n", +		   bt_enabled, gpio_get_value(BT_WAKE_GPIO), +		   bt_lpm.wake, wake_uart_enabled); +	seq_printf(sf, "bt_host_wake=%d lpm.hw=%d hw_uart_en=%d\n", +		   gpio_get_value(BT_HOST_WAKE_GPIO), bt_lpm.host_wake, +		   host_wake_uart_enabled); +	return 0; +} + +static int btdebug_open(struct inode *inode, struct file *file) +{ +	return single_open(file, btdebug_dump, NULL); +} + +static const struct file_operations btdebug_fops = { +	.open = btdebug_open, +	.read = seq_read, +	.llseek = seq_lseek, +	.release = single_release, +}; + +static int bcm20702_bluetooth_probe(struct platform_device *pdev) +{ +	int rc = 0; +	int ret = 0; + +//	rc = gpio_request(BT_RESET_GPIO, "bcm20702_nreset_gpip"); +//	if (unlikely(rc)) { +//		return rc; +//	} + +	rc = gpio_request(BT_REG_GPIO, "bcm20702_nshutdown_gpio"); +	if (unlikely(rc)) { +		//gpio_free(BT_RESET_GPIO); +		return rc; +	} + +	clk32ksys_reg = regulator_get(0, "clk32ksys"); +	if (IS_ERR(clk32ksys_reg)) { +		pr_err("clk32ksys reg not found!\n"); +		clk32ksys_reg = NULL; +	} + +	gpio_direction_output(BT_REG_GPIO, 1); +	//gpio_direction_output(BT_RESET_GPIO, 1); + +	bt_rfkill = rfkill_alloc("bcm20702 Bluetooth", &pdev->dev, +				RFKILL_TYPE_BLUETOOTH, &bcm20702_bt_rfkill_ops, +				NULL); + +	if (unlikely(!bt_rfkill)) { +		//gpio_free(BT_RESET_GPIO); +		gpio_free(BT_REG_GPIO); +		return -ENOMEM; +	} + +	rfkill_set_states(bt_rfkill, true, false); +	rc = rfkill_register(bt_rfkill); + +	if (unlikely(rc)) { +		rfkill_destroy(bt_rfkill); +		//gpio_free(BT_RESET_GPIO); +		gpio_free(BT_REG_GPIO); +		return -1; +	} + +	ret = bcm_bt_lpm_init(pdev); +	if (ret) { +		rfkill_unregister(bt_rfkill); +		rfkill_destroy(bt_rfkill); + +		//gpio_free(BT_RESET_GPIO); +		gpio_free(BT_REG_GPIO); +	} + +	btdebugdent = debugfs_create_file("bt", S_IRUGO, NULL, NULL, +					  &btdebug_fops); +	if (IS_ERR_OR_NULL(btdebugdent)) +		pr_err("%s: failed to create debugfs file\n", __func__); + +	return ret; +} + +static int bcm20702_bluetooth_remove(struct platform_device *pdev) +{ +	rfkill_unregister(bt_rfkill); +	rfkill_destroy(bt_rfkill); + +	if (!IS_ERR_OR_NULL(btdebugdent)) +		debugfs_remove(btdebugdent); + +	gpio_free(BT_REG_GPIO); +	//gpio_free(BT_RESET_GPIO); +	gpio_free(BT_WAKE_GPIO); +	gpio_free(BT_HOST_WAKE_GPIO); +	regulator_put(clk32ksys_reg); + +	wake_lock_destroy(&bt_lpm.wake_lock); +	return 0; +} + +int bcm4430_bluetooth_suspend(struct platform_device *pdev, pm_message_t state) +{ +	int irq = gpio_to_irq(BT_HOST_WAKE_GPIO); +	int host_wake; + +	disable_irq(irq); +	host_wake = gpio_get_value(BT_HOST_WAKE_GPIO); + +	if (host_wake) { +		enable_irq(irq); +		return -EBUSY; +	} + +	return 0; +} + +int bcm4430_bluetooth_resume(struct platform_device *pdev) +{ +	int irq = gpio_to_irq(BT_HOST_WAKE_GPIO); +	enable_irq(irq); +	return 0; +} + +static struct platform_driver bcm20702_bluetooth_platform_driver = { +	.probe = bcm20702_bluetooth_probe, +	.remove = bcm20702_bluetooth_remove, +	.suspend = bcm4430_bluetooth_suspend, +	.resume = bcm4430_bluetooth_resume, +	.driver = { +		   .name = "bcm20702_bluetooth", +		   .owner = THIS_MODULE, +		   }, +}; + +static int __init bcm20702_bluetooth_init(void) +{ +	bt_enabled = false; +	return platform_driver_register(&bcm20702_bluetooth_platform_driver); +} + +static void __exit bcm20702_bluetooth_exit(void) +{ +	platform_driver_unregister(&bcm20702_bluetooth_platform_driver); +} + + +module_init(bcm20702_bluetooth_init); +module_exit(bcm20702_bluetooth_exit); + +MODULE_ALIAS("platform:bcm20702"); +MODULE_DESCRIPTION("bcm20702_bluetooth"); +MODULE_AUTHOR("Jaikumar Ganesh <jaikumar@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-omap2/board-omap3h1.c b/arch/arm/mach-omap2/board-omap3h1.c new file mode 100644 index 00000000000..a24b1fdda01 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3h1.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2014 Olio Devices, Inc. + * Authors: Evan Wilson <evan@oliodevices.com> + *			Mattis Fjallstrom <mattis@oliodevices.com> + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * Modified from the original mach-omap/omap2/board-generic.c did by Paul + * to support the OMAP2+ device tree boards with an unique board file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/opp.h> +#include <linux/cpu.h> +#include <linux/mpu.h> +#include <linux/spi/spi.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand.h> + +#include <linux/regulator/machine.h> + +#include <linux/led-lm3530.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> +#include <linux/platform_data/mtd-nand-omap2.h> + +#include "common.h" +#include "omap_device.h" +#include "gpmc.h" +#include "soc.h" +#include "mux.h" +#include "pm.h" +#include "board-flash.h" +#include "common-board-devices.h" +#include "board-omap3h1.h" + +#define NAND_CS 0 +#define MPUIRQ_GPIO 31 +#define LCD_RESET_GPIO 122 + +static struct mtd_partition omap3h1_nand_partitions[] = { +	/* All the partition sizes are listed in terms of NAND block size */ +	{ +		.name		= "X-Loader", +		.offset		= 0, +		.size		= 4 * NAND_BLOCK_SIZE, +		.mask_flags	= MTD_WRITEABLE,	/* force read-only */ +	}, +	{ +		.name		= "U-Boot", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x80000 */ +		.size		= 15 * NAND_BLOCK_SIZE, +		.mask_flags	= MTD_WRITEABLE,	/* force read-only */ +	}, +	{ +		.name		= "U-Boot Env", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x260000 */ +		.size		= 1 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "kernel", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x280000 */ +		.size		= 40 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "initramfs", +		.offset		= MTDPART_OFS_APPEND, 	/* Offset = 0xC80000 */ +		.size		= 80 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "ramdisk", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x1180000 */ +		.size		= 40 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "system", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x1680000 */ +		.size		= 2000 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "userdata", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x11180000 */ +		.size		= MTDPART_SIZ_FULL, +	}, +}; + +static struct omap_dss_device omap3h1_lcd_device = { +	.type				= OMAP_DISPLAY_TYPE_DPI, +	.name				= "olio_h1_panel", +	.driver_name 		= "ili9342_panel", +	.phy.dpi.data_lines = 18, +	.reset_gpio			= LCD_RESET_GPIO, +}; + +static struct omap_dss_device *omap3h1_dss_devices[] = { +		&omap3h1_lcd_device, +}; + +static struct omap_dss_board_info omap3h1_dss_data = { +	.num_devices 	= 1, +	.devices = omap3h1_dss_devices, +	.default_device = &omap3h1_lcd_device, +}; + +static struct spi_board_info omap3h1_spi_board_info[] __initdata = { +	{ +		.modalias		= "ili9342-spi", +		.bus_num		= 1, +		.chip_select 	= 1, +		.max_speed_hz 	= 375000, +		.platform_data	= &omap3h1_lcd_device, +		.mode			= SPI_MODE_0, +	} +}; + +static int __init omap3h1_spi_init(void) { +	spi_register_board_info(omap3h1_spi_board_info, +			ARRAY_SIZE(omap3h1_spi_board_info)); +	return 0; +} + +static struct mpu_platform_data mpu_data = { +	.int_config  = 0x00, +	.level_shifter = 0, +	.orientation = {  -1,  0,  0, +					   0,  1,  0, +					   0,  0, -1 }, +}; + +static struct lm3530_platform_data omap3h1_backlight_platform_data = { +	.mode = LM3530_BL_MODE_MANUAL, +	//.als_input_mode = LM3530_INPUT_ALS1, +	.max_current = LM3530_FS_CURR_12mA, +	//.pwm_pol_hi = true, +	//.als_avrg_time = LM3530_ALS_AVRG_TIME_512ms, +	.brt_ramp_law = 0, +	.brt_ramp_fall = LM3530_RAMP_TIME_2s, +	.brt_ramp_rise = LM3530_RAMP_TIME_2s, +	//.als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm, +	//.als2_resistor_sel = LM3530_ALS_IMPD_Z, +	//.als_vmin = 730,	/* mV */ +	//.als_vmax = 1020,	/* mV */ +	.brt_val = 0x40,	/* Max brightness */ +}; + +static struct platform_device bcm20702_bluetooth_device = { +	.name = "bcm20702_bluetooth", +	.id = -1, + }; + +static struct i2c_board_info __initdata omap3h1_i2c1_board_info[] = { +		{ +			I2C_BOARD_INFO("mpu6515", 0x68), +			// This is needed for the interrupt wake. IH_GPIO_BASE changed in 3.10 kernel +			//.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +			.platform_data = &mpu_data, +        }, +    	{ +    		/* Backlight */ +    		I2C_BOARD_INFO("lm3530-led", 0x38), +    		.platform_data = &omap3h1_backlight_platform_data, +    	}, +}; + +static int __init omap3_h1_i2c_init(void) +{ +	omap_register_i2c_bus(1, 400, omap3h1_i2c1_board_info, ARRAY_SIZE(omap3h1_i2c1_board_info)); +	return 0; +} + +static struct platform_device *omap3h1_devices[] __initdata = { +        &bcm20702_bluetooth_device, +}; + +#ifdef CONFIG_OMAP_MUX +static struct omap_board_mux board_mux[] __initdata = { +	OMAP3_MUX(HSUSB0_DIR, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT), + +	OMAP3_MUX(DSS_PCLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_HSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_VSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA0, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA2, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA3, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA4, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA5, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA6, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA7, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA8, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA9, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA10, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA11, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA12, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA13, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA14, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA15, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA16, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA17, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), + +	OMAP3_MUX(MCSPI1_CLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(MCSPI1_SIMO, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(MCSPI1_SOMI, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), +	OMAP3_MUX(MCSPI1_CS1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), + +	{ .reg_offset = OMAP_MUX_TERMINATOR }, +}; +#endif + +static void __init omap3_h1_init(void) +{ +	omap3_mux_init(board_mux, OMAP_PACKAGE_CBP); + +	omap3_h1_i2c_init(); +	omap_display_init(&omap3h1_dss_data); +	omap_serial_init(); +	omap_sdrc_init(NULL, NULL); + +	board_nand_init(omap3h1_nand_partitions, +			ARRAY_SIZE(omap3h1_nand_partitions), NAND_CS, +			NAND_BUSWIDTH_16, NULL); + +	platform_add_devices(omap3h1_devices, ARRAY_SIZE(omap3h1_devices)); +	omap3h1_spi_init(); +	//h1_opp_init(); +} + +MACHINE_START(OMAP3_H1, "Olio OMAP3 H1 Board") +	.atag_offset	= 0x100, +	.reserve		= omap_reserve, +	.map_io			= omap3_map_io, +	.init_early		= omap3630_init_early, +	.init_irq		= omap3_init_irq, +	.handle_irq		= omap3_intc_handle_irq, +	.init_machine	= omap3_h1_init, +	.init_late		= omap3630_init_late, +	.init_time		= omap3_secure_sync32k_timer_init, +	//.dt_compat		= omap3_h1_boards_compat, +	.restart		= omap3xxx_restart, +MACHINE_END diff --git a/arch/arm/mach-omap2/board-omap3h1.h b/arch/arm/mach-omap2/board-omap3h1.h new file mode 100644 index 00000000000..343eaeaaf86 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3h1.h @@ -0,0 +1,30 @@ +/* + * Bluetooth Broadcomm  and low power control via GPIO + * + *  Copyright (C) 2011 Samsung, Inc. + *  Copyright (C) 2011 Google, Inc. + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#ifndef __BOARD_OMAP3H1__H__ +#define __BOARD_OMAP3H1__H__ + +#include <linux/serial_core.h> + +extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport); + +#endif /*  __BOARD_OMAP3H1_H__  */ diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c index 45cd26430d1..e823edd05eb 100644 --- a/arch/arm/mach-omap2/cclock3xxx_data.c +++ b/arch/arm/mach-omap2/cclock3xxx_data.c @@ -3540,6 +3540,10 @@ static struct omap_clk omap3xxx_clks[] = {  	CLK(NULL,	"timer_32k_ck",	&omap_32k_fck),  	CLK(NULL,	"timer_sys_ck",	&sys_ck),  	CLK(NULL,	"cpufreq_ck",	&dpll1_ck), +	CLK("cpufreq-cpu0.0", NULL,	&dpll1_ck), +	CLK("48307220.vc", NULL,	&osc_sys_ck), +	CLK("483072b0.vp", NULL,	&osc_sys_ck), +	CLK("483072d0.vp", NULL,	&osc_sys_ck),  };  static const char *enable_init_clks[] = { @@ -3550,8 +3554,10 @@ static const char *enable_init_clks[] = {  int __init omap3xxx_clk_init(void)  { -	if (omap3_has_192mhz_clk()) +	if (omap3_has_192mhz_clk()) {  		omap_96m_alwon_fck = omap_96m_alwon_fck_3630; +		omap_96m_alwon_fck_3630.hw->clk = &omap_96m_alwon_fck; +	}  	if (cpu_is_omap3630()) {  		dpll3_m3x2_ck = dpll3_m3x2_ck_3630; diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c index 2adb2683f07..ab5ec4b28ab 100644 --- a/arch/arm/mach-omap2/control.c +++ b/arch/arm/mach-omap2/control.c @@ -235,6 +235,32 @@ void omap3_ctrl_write_boot_mode(u8 bootmode)  	__raw_writel(l, OMAP2_L4_IO_ADDRESS(OMAP343X_SCRATCHPAD + 4));  } +/** + * omap3_enable_usim_IO - enable USIM block input/output buffers + * + * Enable USIM block input/output buffers.  By default the buffers are + * not enabled.  Once vdds is stable, they can be enabled.  They must + * be enabled before using any GPIOs from this block. + */ +void omap3_enable_usim_buffer(void) +{ +	u32 reg; +	/* +	 * Configure USIM pins for 1.8V control and disable high-z state +	 * CTRL_PBIAS_LITE = 0x20b +	 */ +	reg = omap_ctrl_readl(OMAP343X_CONTROL_PBIAS_LITE); +	reg |= (OMAP2_PBIASLITEVMODE0 | OMAP2_PBIASLITEPWRDNZ0); +	reg |= OMAP343X_PBIASLITEPWRDNZ1; +	reg &= ~OMAP343X_PBIASLITEVMODE1; +	reg &= ~OMAP2_PBIASSPEEDCTRL0; +	omap_ctrl_writel(reg, OMAP343X_CONTROL_PBIAS_LITE); + +	reg = omap_ctrl_readl(OMAP343X_CONTROL_WKUP_CTRL); +	reg |= OMAP343X_USIM_IO_PWRDNZ; +	omap_ctrl_writel(reg, OMAP343X_CONTROL_WKUP_CTRL); +} +  #endif  /** diff --git a/arch/arm/mach-omap2/control.h b/arch/arm/mach-omap2/control.h index e6c328128a0..8698aed1e84 100644 --- a/arch/arm/mach-omap2/control.h +++ b/arch/arm/mach-omap2/control.h @@ -243,6 +243,9 @@  #define OMAP343X_CONTROL_WKUP_DEBOBS3 (OMAP343X_CONTROL_GENERAL_WKUP + 0x014)  #define OMAP343X_CONTROL_WKUP_DEBOBS4 (OMAP343X_CONTROL_GENERAL_WKUP + 0x018) +/* 34xx USIM register offsets */ +#define OMAP343X_CONTROL_WKUP_CTRL    (OMAP2_CONTROL_INTERFACE + 0x0A5C) +  /* 36xx-only RTA - Retention till Access control registers and bits */  #define OMAP36XX_CONTROL_MEM_RTA_CTRL	0x40C  #define OMAP36XX_RTA_DISABLE		0x0 @@ -323,6 +326,9 @@  #define OMAP343X_SCRATCHPAD_REGADDR(reg)	OMAP2_L4_IO_ADDRESS(\  						OMAP343X_SCRATCHPAD + reg) +/* CONTROL_WKUP bits */ +#define OMAP343X_USIM_IO_PWRDNZ         (1 << 6) +  /* AM35XX_CONTROL_IPSS_CLK_CTRL bits */  #define AM35XX_USBOTG_VBUSP_CLK_SHIFT	0  #define AM35XX_CPGMAC_VBUSP_CLK_SHIFT	1 @@ -390,6 +396,7 @@  #define		FEAT_NEON_NONE		1 +  #ifndef __ASSEMBLY__  #ifdef CONFIG_ARCH_OMAP2PLUS  extern void __iomem *omap_ctrl_base_get(void); @@ -417,6 +424,8 @@ extern void omap3630_ctrl_disable_rta(void);  extern int omap3_ctrl_save_padconf(void);  extern void omap2_set_globals_control(void __iomem *ctrl,  				      void __iomem *ctrl_pad); +/* called from board-minnow.c to enable USIM GPIOs */ +extern void omap3_enable_usim_buffer(void);  #else  #define omap_ctrl_base_get()		0  #define omap_ctrl_readb(x)		0 @@ -429,6 +438,5 @@ extern void omap2_set_globals_control(void __iomem *ctrl,  #define omap4_ctrl_pad_writel(x, y)	WARN_ON(1)  #endif  #endif	/* __ASSEMBLY__ */ -  #endif /* __ARCH_ARM_MACH_OMAP2_CONTROL_H */ diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index e18709d3b95..039fe44215d 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -86,7 +86,7 @@ static struct omap3_idle_statedata omap3_idle_data[] = {  	{  		.mpu_state = PWRDM_POWER_RET,  		.core_state = PWRDM_POWER_RET, -		.per_min_state = PWRDM_POWER_OFF, +		.per_min_state = PWRDM_POWER_RET,  	},  	{  		.mpu_state = PWRDM_POWER_OFF, @@ -131,7 +131,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,  		cpu_pm_enter();  	/* Execute ARM wfi */ -	omap_sram_idle(); +	omap_sram_idle(false);  	/*  	 * Call idle CPU PM enter notifier chain to restore diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 4269fc14569..9017d5ea9f3 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -633,6 +633,32 @@ static void __init omap_init_ocp2scp(void)  static inline void omap_init_ocp2scp(void) { }  #endif +#if defined(CONFIG_SGX_OMAP3630) +static struct platform_device mapphone_omaplfb_device = { +	.name	= "omaplfb", +	.id	= -1, +}; +static void __init omap_init_gpu(void) +{ +	struct omap_hwmod *oh; +	struct platform_device *pdev; +	const char *oh_name = "gpu"; +	const char *name = "pvrsrvkm"; +	oh = omap_hwmod_lookup(oh_name); +	if (!oh) { +		pr_err("%s: Could not look up %s\n", __func__, oh_name); +		return; +	} + +	pdev = omap_device_build(name, -1, oh, NULL, 0); +	WARN(IS_ERR(pdev), +	     "%s, Can't build omap_device for %s\n", __func__, name); +	if (platform_device_register(&mapphone_omaplfb_device) < 0) +		pr_err("%s: Could not register OMAP-LFB device\n", __func__); +} +#else +static inline void omap_init_gpu(void) { } +#endif  /*-------------------------------------------------------------------------*/  static int __init omap2_init_devices(void) @@ -662,6 +688,7 @@ static int __init omap2_init_devices(void)  	omap_init_rng();  	omap_init_vout();  	omap_init_ocp2scp(); +	omap_init_gpu();  	return 0;  } diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index ff37be1f6f9..d22833a0f2a 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -320,6 +320,10 @@ static enum omapdss_version __init omap_display_get_version(void)  		return OMAPDSS_VER_UNKNOWN;  } +#ifdef	CONFIG_OMAP2_DSS_RESET +static int reset_omap_dss(void); +#endif +  int __init omap_display_init(struct omap_dss_board_info *board_data)  {  	int r = 0; @@ -415,7 +419,9 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)  			return PTR_ERR(pdev);  		}  	} - +#ifdef	CONFIG_OMAP2_DSS_RESET +	reset_omap_dss(); +#endif  	return 0;  } @@ -564,3 +570,60 @@ int omap_dss_reset(struct omap_hwmod *oh)  	return r;  } + +#ifdef	CONFIG_OMAP2_DSS_RESET +#define	DSS_CLKS	4 +static int reset_omap_dss(void) +{ +	static const char * const clkstr[DSS_CLKS] = { +		"dss_ick", +		"dss1_alwon_fck", +		"dss2_alwon_fck", +		"dss_tv_fck" +	}; +	struct clk *clk[DSS_CLKS]; +	int i, r = 0, c = 0; +	struct omap_hwmod *oh_dss = omap_hwmod_lookup("dss_core"); +	struct omap_hwmod *oh_dispc = omap_hwmod_lookup("dss_dispc"); +	struct omap_hwmod *oh_dsi = omap_hwmod_lookup("dss_dsi1"); + +	WARN((!oh_dss || !oh_dispc || !oh_dsi), "Reset lost!"); + +	for (i = 0; i < DSS_CLKS; i++) { +		clk[i] = clk_get(NULL, clkstr[i]); +		if (!IS_ERR(clk[i])) { +			r = clk_prepare_enable(clk[i]); +			if (!r) +				continue; +			clk_put(clk[i]); +		} +		pr_warn("dss_core: failed enable %s\n", clkstr[i]); +		clk[i] = ERR_PTR(-ENOENT); +	} + +	/* +	 * clear DSS_CONTROL register to switch DSS clock sources to +	 * PRCM clock, if any +	 */ +	omap_hwmod_write(0x0, oh_dss, DSS_CONTROL); +	dispc_disable_outputs(); +	omap_hwmod_write(0x0, oh_dss, DSS_SDI_CONTROL); +	omap_hwmod_write(0x0, oh_dss, DSS_PLL_CONTROL); + +	omap_hwmod_write(0x02, oh_dss, DSS_SYSCONFIG); +	omap_test_timeout((omap_hwmod_read(oh_dss, DSS_SYSSTATUS) +				& SYSS_RESETDONE_MASK), +			MAX_MODULE_SOFTRESET_WAIT, c); +	r = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0; +	pr_warn("dss_core: reset DSS %s\n", r ? "failed" : "done"); + +	for (i = 0; i < DSS_CLKS; i++) { +		if (!IS_ERR(clk[i])) { +			clk_disable_unprepare(clk[i]); +			clk_put(clk[i]); +		} +	} + +	return r; +} +#endif diff --git a/arch/arm/mach-omap2/dss-common.c b/arch/arm/mach-omap2/dss-common.c index 393aeefaebb..d850864aa7f 100644 --- a/arch/arm/mach-omap2/dss-common.c +++ b/arch/arm/mach-omap2/dss-common.c @@ -249,3 +249,29 @@ void __init omap_4430sdp_display_init_of(void)  	omap_display_init(&sdp4430_dss_data);  } + +static struct omap_dss_device minnow_panel_lcd_device = { +	.name = "lcd", +	.driver_name = "minnow-panel", +	.type = OMAP_DISPLAY_TYPE_DSI, +	.data = NULL, +	.phy.dsi		= { +		.module		= 0, +	}, +	.channel		= OMAP_DSS_CHANNEL_LCD, +}; + +static struct omap_dss_device *minnow_panel_dss_devices[] = { +	&minnow_panel_lcd_device, +}; + +static struct omap_dss_board_info minnow_panel_dss_data = { +	.num_devices		= ARRAY_SIZE(minnow_panel_dss_devices), +	.devices		= minnow_panel_dss_devices, +	.default_device		= &minnow_panel_lcd_device, +}; + +void __init omap_minnow_display_init(void) +{ +	omap_display_init(&minnow_panel_dss_data); +} diff --git a/arch/arm/mach-omap2/dss-common.h b/arch/arm/mach-omap2/dss-common.h index 915f6fff510..c8d24f42f49 100644 --- a/arch/arm/mach-omap2/dss-common.h +++ b/arch/arm/mach-omap2/dss-common.h @@ -11,4 +11,6 @@ void __init omap4_panda_display_init_of(void);  void __init omap_4430sdp_display_init(void);  void __init omap_4430sdp_display_init_of(void); +void __init omap_minnow_display_init(void); +  #endif diff --git a/arch/arm/mach-omap2/i2c.c b/arch/arm/mach-omap2/i2c.c index d940e53dd9f..728ca6729a3 100644 --- a/arch/arm/mach-omap2/i2c.c +++ b/arch/arm/mach-omap2/i2c.c @@ -121,16 +121,6 @@ static int __init omap_i2c_nr_ports(void)  	return ports;  } -/* - * XXX This function is a temporary compatibility wrapper - only - * needed until the I2C driver can be converted to call - * omap_pm_set_max_dev_wakeup_lat() and handle a return code. - */ -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t) -{ -	omap_pm_set_max_mpu_wakeup_lat(dev, t); -} -  static const char name[] = "omap_i2c";  int __init omap_i2c_add_bus(struct omap_i2c_bus_platform_data *i2c_pdata, @@ -168,15 +158,6 @@ int __init omap_i2c_add_bus(struct omap_i2c_bus_platform_data *i2c_pdata,  	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;  	pdata->flags = dev_attr->flags; -	/* -	 * When waiting for completion of a i2c transfer, we need to -	 * set a wake up latency constraint for the MPU. This is to -	 * ensure quick enough wakeup from idle, when transfer -	 * completes. -	 * Only omap3 has support for constraints -	 */ -	if (cpu_is_omap34xx()) -		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;  	pdev = omap_device_build(name, bus_id, oh, pdata,  				 sizeof(struct omap_i2c_bus_platform_data));  	WARN(IS_ERR(pdev), "Could not build omap_device for %s\n", name); diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index f82cf878d6a..776ca198119 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -425,6 +425,35 @@ static irqreturn_t omap_hwmod_mux_handle_irq(int irq, void *unused)  	return IRQ_HANDLED;  } +/** + * _omap_hwmod_mux_count + * + * Increment data if given hwmod has a mux configured + */ +static int _omap_hwmod_mux_count(struct omap_hwmod *oh, void *data) +{ +	int *count = (int *)data; + +	if (oh->mux) +		(*count)++; + +	return 0; +} + +/** + * omap_hwmod_mux_count + * + * Count the total number of muxes configured. + */ +static int omap_hwmod_mux_count(void) +{ +	int count = 0; + +	omap_hwmod_for_each(_omap_hwmod_mux_count, &count); + +	return count; +} +  /* Assumes the calling function takes care of locking */  void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state)  { @@ -811,12 +840,17 @@ int __init omap_mux_late_init(void)  		}  	} -	ret = request_irq(omap_prcm_event_to_irq("io"), -		omap_hwmod_mux_handle_irq, IRQF_SHARED | IRQF_NO_SUSPEND, -			"hwmod_io", omap_mux_late_init); +	/* setup io interrupt handler if we have any configured mux'es */ +	if (omap_hwmod_mux_count()) { +		ret = request_irq(omap_prcm_event_to_irq("io"), +				omap_hwmod_mux_handle_irq, +				IRQF_SHARED | IRQF_NO_SUSPEND, +				"hwmod_io", omap_mux_late_init); -	if (ret) -		pr_warning("mux: Failed to setup hwmod io irq %d\n", ret); +		if (ret) +			pr_warning("mux: Failed to setup hwmod io irq %d\n", +					ret); +	}  	omap_mux_dbg_init(); diff --git a/arch/arm/mach-omap2/omap-pm-noop.c b/arch/arm/mach-omap2/omap-pm-noop.c index 6a3be2bebdd..aeb217bfab1 100644 --- a/arch/arm/mach-omap2/omap-pm-noop.c +++ b/arch/arm/mach-omap2/omap-pm-noop.c @@ -25,7 +25,6 @@  #include "omap_device.h"  #include "omap-pm.h" -static bool off_mode_enabled;  static int dummy_context_loss_counter;  /* @@ -281,28 +280,6 @@ unsigned long omap_pm_cpu_get_freq(void)  	return 0;  } -/** - * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been enabled. - */ -void omap_pm_enable_off_mode(void) -{ -	off_mode_enabled = true; -} - -/** - * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been disabled. - */ -void omap_pm_disable_off_mode(void) -{ -	off_mode_enabled = false; -} -  /*   * Device context loss tracking   */ @@ -320,12 +297,14 @@ int omap_pm_get_dev_context_loss_count(struct device *dev)  	if (dev->pm_domain == &omap_device_pm_domain) {  		count = omap_device_get_context_loss_count(pdev);  	} else { -		WARN_ONCE(off_mode_enabled, "omap_pm: using dummy context loss counter; device %s should be converted to omap_device", +		WARN_ONCE(omap_pm_get_off_mode(), +			"omap_pm: using dummy context loss counter; device %s " +			"should be converted to omap_device",  			  dev_name(dev));  		count = dummy_context_loss_counter; -		if (off_mode_enabled) { +		if (omap_pm_get_off_mode()) {  			count++;  			/*  			 * Context loss count has to be a non-negative value. diff --git a/arch/arm/mach-omap2/omap-pm.h b/arch/arm/mach-omap2/omap-pm.h index 67faa7b8fe9..4511a53b8e3 100644 --- a/arch/arm/mach-omap2/omap-pm.h +++ b/arch/arm/mach-omap2/omap-pm.h @@ -18,6 +18,7 @@  #include <linux/cpufreq.h>  #include <linux/clk.h>  #include <linux/opp.h> +#include <linux/power/omap_prm.h>  /*   * agent_id values for use with omap_pm_set_min_bus_tput(): @@ -346,7 +347,4 @@ unsigned long omap_pm_cpu_get_freq(void);   */  int omap_pm_get_dev_context_loss_count(struct device *dev); -void omap_pm_enable_off_mode(void); -void omap_pm_disable_off_mode(void); -  #endif diff --git a/arch/arm/mach-omap2/omap3-restart.c b/arch/arm/mach-omap2/omap3-restart.c index 923c582189e..92a177654e2 100644 --- a/arch/arm/mach-omap2/omap3-restart.c +++ b/arch/arm/mach-omap2/omap3-restart.c @@ -28,9 +28,40 @@   * Resets the SoC.  For @cmd, see the 'reboot' syscall in   * kernel/sys.c.  No return value.   */ + + +static int in_panic; + +static int panic_prep_restart(struct notifier_block *this, +			      unsigned long event, void *ptr) +{ +	in_panic = 1; +	return NOTIFY_DONE; +} + +static struct notifier_block panic_block = { +	.notifier_call	= panic_prep_restart, +}; +  void omap3xxx_restart(char mode, const char *cmd)  { +	if (cmd != NULL) { +		if ((strncmp(cmd, "bootloader", 10)) && +		    (strncmp(cmd, "recovery", 8))) +			cmd = NULL; +	} + +	if (in_panic) +		cmd = "panic"; +  	omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));  	omap3xxx_prm_dpll3_reset(); /* never returns */  	while (1);  } + +static int __init omap3xxx_restart_init(void) +{ +	atomic_notifier_chain_register(&panic_notifier_list, &panic_block); +	return 0; +} +early_initcall(omap3xxx_restart_init); diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 7341eff63f5..5cc51234add 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -2015,7 +2015,7 @@ static int _reset(struct omap_hwmod *oh)   * XXX When the PRM code is moved to drivers, this function can be removed,   * as the PRM infrastructure should abstract this.   */ -static void _reconfigure_io_chain(void) +void _reconfigure_io_chain(void)  {  	unsigned long flags; diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h index 0c898f58ac9..dd6852ff898 100644 --- a/arch/arm/mach-omap2/omap_hwmod.h +++ b/arch/arm/mach-omap2/omap_hwmod.h @@ -703,4 +703,6 @@ extern int am33xx_hwmod_init(void);  extern int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois); +void _reconfigure_io_chain(void); +  #endif diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 31c7126eb3b..7c5a6b81875 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -916,7 +916,8 @@ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = {  static struct omap_hwmod omap3xxx_gpio1_hwmod = {  	.name		= "gpio1", -	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET, +	.flags		= (HWMOD_CONTROL_OPT_CLKS_IN_RESET | +			   HWMOD_INIT_NO_RESET),  	.mpu_irqs	= omap2_gpio1_irqs,  	.main_clk	= "gpio1_ick",  	.opt_clks	= gpio1_opt_clks, @@ -941,7 +942,8 @@ static struct omap_hwmod_opt_clk gpio2_opt_clks[] = {  static struct omap_hwmod omap3xxx_gpio2_hwmod = {  	.name		= "gpio2", -	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET, +	.flags		= (HWMOD_CONTROL_OPT_CLKS_IN_RESET | +			   HWMOD_INIT_NO_RESET),  	.mpu_irqs	= omap2_gpio2_irqs,  	.main_clk	= "gpio2_ick",  	.opt_clks	= gpio2_opt_clks, @@ -966,7 +968,8 @@ static struct omap_hwmod_opt_clk gpio3_opt_clks[] = {  static struct omap_hwmod omap3xxx_gpio3_hwmod = {  	.name		= "gpio3", -	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET, +	.flags		= (HWMOD_CONTROL_OPT_CLKS_IN_RESET | +			   HWMOD_INIT_NO_RESET),  	.mpu_irqs	= omap2_gpio3_irqs,  	.main_clk	= "gpio3_ick",  	.opt_clks	= gpio3_opt_clks, @@ -991,7 +994,8 @@ static struct omap_hwmod_opt_clk gpio4_opt_clks[] = {  static struct omap_hwmod omap3xxx_gpio4_hwmod = {  	.name		= "gpio4", -	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET, +	.flags		= (HWMOD_CONTROL_OPT_CLKS_IN_RESET | +			   HWMOD_INIT_NO_RESET),  	.mpu_irqs	= omap2_gpio4_irqs,  	.main_clk	= "gpio4_ick",  	.opt_clks	= gpio4_opt_clks, @@ -1021,7 +1025,8 @@ static struct omap_hwmod_opt_clk gpio5_opt_clks[] = {  static struct omap_hwmod omap3xxx_gpio5_hwmod = {  	.name		= "gpio5", -	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET, +	.flags		= (HWMOD_CONTROL_OPT_CLKS_IN_RESET | +			   HWMOD_INIT_NO_RESET),  	.mpu_irqs	= omap3xxx_gpio5_irqs,  	.main_clk	= "gpio5_ick",  	.opt_clks	= gpio5_opt_clks, @@ -1051,7 +1056,8 @@ static struct omap_hwmod_opt_clk gpio6_opt_clks[] = {  static struct omap_hwmod omap3xxx_gpio6_hwmod = {  	.name		= "gpio6", -	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET, +	.flags		= (HWMOD_CONTROL_OPT_CLKS_IN_RESET | +			   HWMOD_INIT_NO_RESET),  	.mpu_irqs	= omap3xxx_gpio6_irqs,  	.main_clk	= "gpio6_ick",  	.opt_clks	= gpio6_opt_clks, @@ -2181,6 +2187,80 @@ static struct omap_hwmod omap3xxx_gpmc_hwmod = {  			   HWMOD_NO_IDLEST),  }; +#ifdef	CONFIG_SGX_OMAP3630 +/* + * 'gpu' class + * 2d/3d graphics accelerator + */ + +static struct omap_hwmod_class_sysconfig omap3630_gpu_sysc = { +	.rev_offs	= 0xfe00, +	.sysc_offs	= 0xfe10, +	.sysc_flags	= (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE), +	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | +			   SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO | +			   MSTANDBY_SMART | MSTANDBY_SMART_WKUP), +	.sysc_fields	= &omap_hwmod_sysc_type2, +}; + +static struct omap_hwmod_class omap3630_gpu_hwmod_class = { +	.name	= "gpu", +	.sysc	= &omap3630_gpu_sysc, +}; + +/* gpu */ +static struct omap_hwmod_irq_info omap3630_gpu_irqs[] = { +	{ .irq = 21 + OMAP_INTC_START }, +	{ .irq = -1 } +}; + +static struct omap_hwmod omap3630_gpu_hwmod = { +	.name		= "gpu", +	.class		= &omap3630_gpu_hwmod_class, +	.clkdm_name	= "sgx_clkdm", +	.mpu_irqs	= omap3630_gpu_irqs, +	.main_clk	= "sgx_fck", +	.prcm = { +		.omap2 = { +			.module_offs = OMAP3430ES2_SGX_MOD, +			.prcm_reg_id = 1, +			.idlest_reg_id = 1, +		}, +	}, +	.flags		= HWMOD_NO_IDLEST | HWMOD_NO_OCP_AUTOIDLE, +}; + +/* gpu -> l3_main */ +static struct omap_hwmod_ocp_if omap3630_gpu__l3_main = { +	.master         = &omap3630_gpu_hwmod, +	.slave          = &omap3xxx_l3_main_hwmod, +	.clk		= "core_l3_ick", +	.user           = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +static struct omap_hwmod_addr_space omap3630_gpu_addrs[] = { +	{ +		.pa_start       = 0x50000000, +		.pa_end         = 0x5000ffff, +		.flags          = ADDR_TYPE_RT +	}, +	{ } +}; + +/* l3_main -> gpu interface */ +static struct omap_hwmod_ocp_if omap3630_l3_main__gpu = { +	.master         = &omap3xxx_l3_main_hwmod, +	.slave          = &omap3630_gpu_hwmod, +	.clk		= "sgx_ick", +	.addr           = omap3630_gpu_addrs, +	.user           = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +#endif + +  /*   * interfaces   */ @@ -3830,6 +3910,10 @@ static struct omap_hwmod_ocp_if *omap36xx_hwmod_ocp_ifs[] __initdata = {  #ifdef CONFIG_OMAP_IOMMU_IVA2  	&omap3xxx_l3_main__mmu_iva,  #endif +#ifdef CONFIG_SGX_OMAP3630 +	&omap3630_l3_main__gpu, +	&omap3630_gpu__l3_main, +#endif  	NULL  }; diff --git a/arch/arm/mach-omap2/opp.c b/arch/arm/mach-omap2/opp.c index bd41d59a7ca..82fd8c72f75 100644 --- a/arch/arm/mach-omap2/opp.c +++ b/arch/arm/mach-omap2/opp.c @@ -17,6 +17,7 @@   * GNU General Public License for more details.   */  #include <linux/module.h> +#include <linux/of.h>  #include <linux/opp.h>  #include <linux/cpu.h> @@ -40,6 +41,9 @@ int __init omap_init_opp_table(struct omap_opp_def *opp_def,  {  	int i, r; +	if (of_have_populated_dt()) +		return -EINVAL; +  	if (!opp_def || !opp_def_size) {  		pr_err("%s: invalid params!\n", __func__);  		return -EINVAL; diff --git a/arch/arm/mach-omap2/pad_wkup.c b/arch/arm/mach-omap2/pad_wkup.c new file mode 100644 index 00000000000..021c5ff9737 --- /dev/null +++ b/arch/arm/mach-omap2/pad_wkup.c @@ -0,0 +1,167 @@ +/* + * OMAP3 Pad Wakeup Handler + * + * Copyright (C) 2014-2008 Motorola, LLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/wakeup_reason.h> + +#include "soc.h" +#include "common.h" +#include "mux34xx.h" +#include "prm3xxx.h" +#include "iomap.h" + +#include "pad_wkup.h" + +#define WAKEUPEVENT		0x8000 +struct offmode_wkup { +	struct list_head node; +	u32 irq; +	u32 pad; +	u32 pad_shift; +	bool handle; /* if true generate irq otherwise for debug only */ +}; +LIST_HEAD(offmode_wkup_list); + +static void omap_register_pad_wkup(struct device *dev, u32 pad, u32 irq, +		bool handle) +{ +	struct offmode_wkup *wkup; + +	pr_info("register pad (0x%04x, %u, %d)\n", pad, irq, handle); + +	wkup = devm_kzalloc(dev, sizeof(struct offmode_wkup), GFP_KERNEL); +	if (wkup) { +		wkup->irq = irq; +		wkup->pad = pad & 0xFFFFFFFC; +		wkup->pad_shift = pad % 4 ? 16 : 0; +		wkup->handle = handle; +		list_add_tail(&wkup->node, &offmode_wkup_list); +	} +} + +static inline int is_pad_wkup(const struct offmode_wkup *wkup) +{ +	int pad = __raw_readl( +		OMAP2_L4_IO_ADDRESS(OMAP3_CONTROL_PADCONF_MUX_PBASE)+ wkup->pad); +	pad = pad >> wkup->pad_shift; + +	return (pad & WAKEUPEVENT) ? 1 : 0; +} + +/* prcm_handle_pad_wkup: + * + *   map i/o pad wkup bits to interrupts.  The primary function is + *   to generate interrupts.  If the handle flag is set, then generate + *   interrupt.  If the wkup bit is set on multiple pads, interrupts + *   are generated for all.   The secondary function is to log the + *   Typically there should only be one wakeup event, but if there + *   are multiple wakeup reasons, only the first in the list will be + *   anointed "the" wakeup reason and logged.  This means the order + *   items are listed in the device tree will control which irq + *   wins in the case of a tie. + */ +void prcm_handle_pad_wkup(void) +{ +	struct offmode_wkup *wkup; +	int wkup_irq = -1; + +	list_for_each_entry(wkup, &offmode_wkup_list, node) { +		if (is_pad_wkup(wkup)) { +			pr_info("%s IRQ = %d\n", __func__, wkup->irq); +			if (wkup_irq < 0) +				wkup_irq = wkup->irq; +			if (wkup->handle) +				generic_handle_irq(wkup->irq); +		} +	} +	if (wkup_irq >= 0) +		log_wakeup_reason(wkup_irq); + +} + +#ifdef CONFIG_OMAP3_PAD_WKUP_IO +static irqreturn_t omap3_pad_wkup_handle_irq(int irq, void *unused) +{ +	prcm_handle_pad_wkup(); + +	return IRQ_HANDLED; +} +#endif + +static int omap3_pad_wkup_probe(struct platform_device *pdev) +{ +	struct device_node *node = pdev->dev.of_node; +	int ndx = 0; +	uint32_t pad, irq, handle; +	int ret = 0; + +	if (!node) +		return -ENODEV; + +	do { +		if (of_property_read_u32_index(node, "ti,pad_irq", ndx, &pad)) +			break; +		ndx++; +		if (of_property_read_u32_index(node, "ti,pad_irq", ndx, &irq)) +			break; +		ndx++; +		if (of_property_read_u32_index(node, "ti,pad_irq", ndx, +				&handle)) +			break; +		ndx++; +		omap_register_pad_wkup(&pdev->dev, pad, irq, handle); +	} while (1); + +#ifdef CONFIG_OMAP3_PAD_WKUP_IO +	if (ndx > 1) { +		dev_info(&pdev->dev, "request pad_wkup_io\n"); +		ret = request_irq(omap_prcm_event_to_irq("io"), +				omap3_pad_wkup_handle_irq, +				IRQF_SHARED | IRQF_NO_SUSPEND, +				"pad_wkup_io", &pdev->dev); + +		if (ret) +			pr_warning("wkup: Failed to setup pad_wkup_io irq %d\n", +				ret); +	} +#endif + +	return ret; +} + +static int omap3_pad_wkup_remove(struct platform_device *pdev) +{ +	return 0; +} + +static const struct of_device_id omap3_pad_wkup_table[] = { +	{ .compatible = "ti,pad-wkup", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, omap3_pad_wkup_table); + +static struct platform_driver omap3_pad_wkup_driver = { +	.probe = omap3_pad_wkup_probe, +	.remove = omap3_pad_wkup_remove, +	.driver = { +		.name = "omap3_pad_wkup", +		.owner = THIS_MODULE, +		.of_match_table = omap3_pad_wkup_table, +	}, +}; + +static int __init omap_pad_wkup_init(void) +{ +	return platform_driver_register(&omap3_pad_wkup_driver); +} +omap_late_initcall(omap_pad_wkup_init); diff --git a/arch/arm/mach-omap2/pad_wkup.h b/arch/arm/mach-omap2/pad_wkup.h new file mode 100644 index 00000000000..9a3b402e411 --- /dev/null +++ b/arch/arm/mach-omap2/pad_wkup.h @@ -0,0 +1,5 @@ +#ifndef __OMAP_PADWKUP_H__ +#define __OMAP_PADWKUP_H__ + +extern void prcm_handle_pad_wkup(void); +#endif diff --git a/arch/arm/mach-omap2/pm-debug-regs.c b/arch/arm/mach-omap2/pm-debug-regs.c new file mode 100644 index 00000000000..b006ae80974 --- /dev/null +++ b/arch/arm/mach-omap2/pm-debug-regs.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC + * + * 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. + * + * Work based on OMAP Power Management debug routines + * Copyright (C) 2005 Texas Instruments, Inc. + * Copyright (C) 2006-2008 Nokia Corporation + * + * Written by: + *   Richard Woodruff <r-woodruff2@ti.com> + *   Tony Lindgren + *   Juha Yrjola + *   Amit Kucheria <amit.kucheria@nokia.com> + *   Igor Stoppa <igor.stoppa@nokia.com> + *   Jouni Hogander + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "iomap.h" +#include "clock.h" +#include "powerdomain.h" +#include "clockdomain.h" +#include "omap-pm.h" + +#include "soc.h" +#include "cm2xxx_3xxx.h" +#include "prm3xxx.h" +#include "prm2xxx_3xxx.h" +#include "pm.h" + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +static struct dentry *pm_dbg_dir; +#endif + +struct pm_module_def { +	char name[8];           /* Name of the module     */ +	short type;             /* CM or PRM              */ +	unsigned short offset; +	int low; /* First register address on this module */ +	int high; /* Last register address on this module */ +}; + +#define MOD_CM 0 +#define MOD_PRM 1 + + +static const struct pm_module_def pm_dbg_reg_modules[] = { +	{ "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c }, +	{ "OCP",  MOD_CM, OCP_MOD, 0, 0x10 }, +	{ "MPU",  MOD_CM, MPU_MOD, 4, 0x4c }, +	{ "CORE", MOD_CM, CORE_MOD, 0, 0x4c }, +	{ "SGX",  MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c }, +	{ "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 }, +	{ "CCR",  MOD_CM, PLL_MOD, 0, 0x70 }, +	{ "DSS",  MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c }, +	{ "CAM",  MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c }, +	{ "PER",  MOD_CM, OMAP3430_PER_MOD, 0, 0x4c }, +	{ "EMU",  MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 }, +	{ "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 }, +	{ "USB",  MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c }, + +	{ "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc }, +	{ "OCP",  MOD_PRM, OCP_MOD, 4, 0x1c }, +	{ "MPU",  MOD_PRM, MPU_MOD, 0x58, 0xe8 }, +	{ "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 }, +	{ "SGX",  MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 }, +	{ "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 }, +	{ "CCR",  MOD_PRM, PLL_MOD, 0x40, 0x70 }, +	{ "DSS",  MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 }, +	{ "CAM",  MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 }, +	{ "PER",  MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 }, +	{ "EMU",  MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 }, +	{ "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 }, +	{ "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 }, +	{ "USB",  MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 }, +	{ "", 0, 0, 0, 0 }, +}; + +#define PM_DBG_MAX_REG_SETS 4 + +static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS] = {0}; + +static int pm_dbg_get_regset_size(void) +{ +	static int regset_size = 0; + +	if (regset_size == 0) { +		int i = 0; +		while (pm_dbg_reg_modules[i].name[0] != 0) { +			regset_size += pm_dbg_reg_modules[i].high + +				4 - pm_dbg_reg_modules[i].low; +			i++; +		} +	} +	return regset_size; +} + +static void pm_dbg_regset_store(u32 *ptr) +{ +	int i = 0; +	int j; +	u32 val; + +	while (pm_dbg_reg_modules[i].name[0] != 0) { +		for (j = pm_dbg_reg_modules[i].low; +			j <= pm_dbg_reg_modules[i].high; j += 4) { +			if (pm_dbg_reg_modules[i].type == MOD_CM) +				val = omap2_cm_read_mod_reg( +					pm_dbg_reg_modules[i].offset, j); +			else +				val = omap2_prm_read_mod_reg( +					pm_dbg_reg_modules[i].offset, j); +			*(ptr++) = val; +		} +		i++; +	} +} + +void pm_dbg_regs_copy(int tgt, int src) +{ +	size_t sz = pm_dbg_get_regset_size(); + +	pr_debug("saved reference copy %s(%d, %d) size = %u <- %pS\n", +		__func__, tgt, src, sz, __builtin_return_address(0)); +	memcpy(pm_dbg_reg_set[tgt - 1], pm_dbg_reg_set[src - 1], sz); +} + +void pm_dbg_regs_save(int reg_set) +{ +	if (pm_dbg_reg_set[reg_set - 1] == NULL) +		return; + +	pm_dbg_regset_store(pm_dbg_reg_set[reg_set - 1]); +} + +#ifdef CONFIG_DEBUG_FS +static int pm_dbg_show_regs(struct seq_file *s, void *unused) +{ +	int i, j; +	unsigned long val; +	int reg_set = (int)s->private; +	u32 *ptr; +	void *store = NULL; +	int regs; +	int linefeed; + +	if (!cpu_is_omap34xx()) +		return -EINVAL; + +	if (reg_set == 0) { +		store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL); +		if (store == NULL) +			return -ENOMEM; +		ptr = store; +		pm_dbg_regset_store(ptr); +	} else { +		ptr = pm_dbg_reg_set[reg_set - 1]; +	} + +	i = 0; + +	while (pm_dbg_reg_modules[i].name[0] != 0) { +		regs = 0; +		linefeed = 0; +		if (pm_dbg_reg_modules[i].type == MOD_CM) +			seq_printf(s, "MOD: CM_%s (%08x)\n", +				pm_dbg_reg_modules[i].name, +				(u32)(OMAP3430_CM_BASE + +				pm_dbg_reg_modules[i].offset)); +		else +			seq_printf(s, "MOD: PRM_%s (%08x)\n", +				pm_dbg_reg_modules[i].name, +				(u32)(OMAP3430_PRM_BASE + +				pm_dbg_reg_modules[i].offset)); + +		for (j = pm_dbg_reg_modules[i].low; +			j <= pm_dbg_reg_modules[i].high; j += 4) { +			val = *(ptr++); +			if (val != 0) { +				regs++; +				if (linefeed) { +					seq_printf(s, "\n"); +					linefeed = 0; +				} +				seq_printf(s, "  %02x => %08lx", j, val); +				if (regs % 4 == 0) +					linefeed = 1; +			} +		} +		seq_printf(s, "\n"); +		i++; +	} + +	if (store != NULL) +		kfree(store); + +	return 0; +} + +#define WAKEUP_SOURCE_LEN 512 +void pm_dbg_show_wakeup_source(void) +{ +	u32 val = 0; +	int len = 0; +	static char buf[WAKEUP_SOURCE_LEN]; +	char *pbuf; +	u32 gpio_bit = 0; + +	/* print the real wkup sources */ +	memset(buf, 0, WAKEUP_SOURCE_LEN); +	pbuf = buf; +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (len > 16) +		pbuf += snprintf(pbuf, len, "WAKEDUP BY: "); + +	val = omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST); +	val &= omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "WKUP_MOD(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(CORE_MOD, PM_WKST1); +	val &= omap2_prm_read_mod_reg(CORE_MOD, PM_WKEN1); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "CORE_MOD(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3); +	val &= omap2_prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKEN3); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "CORE3_MOD(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST); +	val &= omap2_prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKEN); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "PER_MOD(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST); +	val &= omap2_prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKEN); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "USBHOST(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(OMAP3430_DSS_MOD, PM_WKST); +	val &= omap2_prm_read_mod_reg(OMAP3430_DSS_MOD, PM_WKEN); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "DSS(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); +	val &= omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "MPU_IRQSTATUS(0x%x), ", val); + +	val = omap2_prm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_PRM_IRQSTATUS_IVA2); +	val &= omap2_prm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_PRM_IRQENABLE_IVA2); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "IVA2_IRQSTATUS(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x009C))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_FIQ0(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00bC))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_FIQ1(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00dC))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_FIQ2(0x%x), ", val); + + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x0098))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_IRQ0(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00B8))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_IRQ1(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00D8))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_IRQ2(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x009C))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_FIQ0(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00BC))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_FIQ1(0x%x), ", val); + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00DC))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_FIQ2(0x%x), ", val); + +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if ((val & (1<<29)) && len > 20) { +		gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x48310018)) & +			__raw_readl(OMAP2_L4_IO_ADDRESS(0x4831001C)); +		pbuf += snprintf(pbuf, len, "GPIO1(0x%x), ", gpio_bit); +	} +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if ((val & (1<<30)) && len > 20) { +		gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49050018)) & +			__raw_readl(OMAP2_L4_IO_ADDRESS(0x4905001C)); +		pbuf += snprintf(pbuf, len, "GPIO2(0x%x), ", gpio_bit); +	} +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if ((val & (1<<31)) && len > 20) { +		gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49052018)) & +			__raw_readl(OMAP2_L4_IO_ADDRESS(0x4905201C)); +		pbuf += snprintf(pbuf, len, "GPIO3(0x%x), ", gpio_bit); +	} + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00b8))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_IRQ1(0x%x), ", val); + +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if ((val & (1<<0)) && len > 20) { +		gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49054018)) & +			__raw_readl(OMAP2_L4_IO_ADDRESS(0x4905401C)); +		pbuf += snprintf(pbuf, len, "GPIO4(0x%x), ", gpio_bit); +	} +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if ((val & (1<<1)) && len > 20) { +		gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49056018)) & +			__raw_readl(OMAP2_L4_IO_ADDRESS(0x4905601C)); +		pbuf += snprintf(pbuf, len, "GPIO5(0x%x), ", gpio_bit); +	} +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if ((val & (1<<2)) && len > 20) { +		gpio_bit = __raw_readl(OMAP2_L4_IO_ADDRESS(0x49058018)) & +			__raw_readl(OMAP2_L4_IO_ADDRESS(0x4905801C)); +		pbuf += snprintf(pbuf, len, "GPIO6(0x%x), ", gpio_bit); +	} + +	val = __raw_readl(OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + (0x00d8))); +	len = WAKEUP_SOURCE_LEN - (pbuf - buf); +	if (val && len > 30) +		pbuf += snprintf(pbuf, len, "INTC_IRQ2(0x%x)", val); + +	pr_debug("%s\n", buf); + +} +EXPORT_SYMBOL(pm_dbg_show_wakeup_source); + +static int pm_dbg_reg_open(struct inode *inode, struct file *file) +{ +	return single_open(file, pm_dbg_show_regs, inode->i_private); +} + +static const struct file_operations debug_reg_fops = { +	.open           = pm_dbg_reg_open, +	.read           = seq_read, +	.llseek         = seq_lseek, +	.release        = single_release, +}; +#endif + +int pm_dbg_regs_dump(int reg_set) +{ +	int i, j; +	unsigned long val; +	u32 *ptr; +	int regs; + +	if ((reg_set <= 0) || (reg_set > PM_DBG_MAX_REG_SETS)) +		return -EINVAL; + +	ptr = pm_dbg_reg_set[reg_set - 1]; + +	i = 0; + +	while (pm_dbg_reg_modules[i].name[0] != 0) { +		regs = 0; +		if (pm_dbg_reg_modules[i].type == MOD_CM) +			pr_debug("MOD: CM_%s (%08x)\n", +				pm_dbg_reg_modules[i].name, +				(u32)(OMAP3430_CM_BASE + +				pm_dbg_reg_modules[i].offset)); +		else +			pr_debug("MOD: PRM_%s (%08x)\n", +				pm_dbg_reg_modules[i].name, +				(u32)(OMAP3430_PRM_BASE + +				pm_dbg_reg_modules[i].offset)); + +		for (j = pm_dbg_reg_modules[i].low; +			j <= pm_dbg_reg_modules[i].high; j += 4) { +			val = *(ptr++); +			if (val != 0) { +				regs++; +				pr_debug("  %02x => %08lx\n", j, val); +			} +		} +		i++; +	} + +	return 0; +} +EXPORT_SYMBOL(pm_dbg_regs_dump); + +int pm_dbg_regs_dump_delta(int cur, int ref) +{ +	int i, j; +	unsigned long val_cur; +	u32 *ptr_cur; +	unsigned long val_ref; +	u32 *ptr_ref; + +	if ((cur <= 0) || (cur > PM_DBG_MAX_REG_SETS) || +	    (ref <= 0) || (ref > PM_DBG_MAX_REG_SETS)) { +		return -EINVAL; +	} + +	ptr_cur = pm_dbg_reg_set[cur - 1]; +	ptr_ref = pm_dbg_reg_set[ref - 1]; + +	i = 0; + +	pr_debug("   module      ( address) reg        %d          %d\n", +			cur, ref); +	while (pm_dbg_reg_modules[i].name[0] != 0) { +		bool cm = pm_dbg_reg_modules[i].type == MOD_CM; +		u32 base_addr = cm ?  OMAP3430_CM_BASE : OMAP3430_PRM_BASE; + +		for (j = pm_dbg_reg_modules[i].low; +		     j <= pm_dbg_reg_modules[i].high; j += 4) { +			u32 addr = (u32)(base_addr + pm_dbg_reg_modules[i].offset); + +			val_cur = *(ptr_cur++); +			val_ref = *(ptr_ref++); +			if (val_cur != val_ref) { +				pr_debug("MOD: %s_%-4s %s(%08x) " +					"%02x => 0x%08lx 0x%08lx\n", +					cm ? "CM" : "PRM", +					pm_dbg_reg_modules[i].name, +					cm ? " " : "", +					addr, j, val_cur, val_ref); +			} +		} +		i++; +	} +	return 0; +} +EXPORT_SYMBOL(pm_dbg_regs_dump_delta); + +static void __init pm_dbg_regset_init(void) +{ +	int i; + +	for (i = 0; i < PM_DBG_MAX_REG_SETS; i++) { +		pm_dbg_reg_set[i] = +			kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL); +	} +} + +#ifdef CONFIG_DEBUG_FS +int __init pm_dbg_regs_init(struct dentry *d) +{ +	int i; +	char name[2]; + +	if (!cpu_is_omap34xx()) { +		pr_err("%s: only OMAP3 supported\n", __func__); +		return -ENODEV; +	} + +	pm_dbg_dir = debugfs_create_dir("registers", d); +	if (IS_ERR(pm_dbg_dir)) +		return PTR_ERR(pm_dbg_dir); + +	(void) debugfs_create_file("current", S_IRUGO, +		pm_dbg_dir, (void *)0, &debug_reg_fops); + +	pm_dbg_regset_init(); +	for (i = 0; i < PM_DBG_MAX_REG_SETS; i++) { +		if (pm_dbg_reg_set[i] != NULL) { +			sprintf(name, "%d", i + 1); +			(void) debugfs_create_file(name, S_IRUGO, +				pm_dbg_dir, (void *)(i+1), &debug_reg_fops); +		} +	} + +	return 0; +} +#else +static int __init pm_dbg_regs_init(void) +{ +	if (!cpu_is_omap34xx()) { +		pr_err("%s: only OMAP3 supported\n", __func__); +		return -ENODEV; +	} + +	pm_dbg_regset_init(); +	return 0; +} +omap_arch_initcall(pm_dbg_regs_init); +#endif diff --git a/arch/arm/mach-omap2/pm-debug-regs.h b/arch/arm/mach-omap2/pm-debug-regs.h new file mode 100644 index 00000000000..f70bbe5c6a8 --- /dev/null +++ b/arch/arm/mach-omap2/pm-debug-regs.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Motorola Mobility LLC + * + * 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. + */ + +#ifndef __PM_DEBUG_REGS_H__ +#define __PM_DEBUG_REGS_H__ + +#include <linux/dcache.h> + +#ifdef CONFIG_PM_DEBUG +extern int  pm_dbg_regs_init(struct dentry *d); +extern void pm_dbg_regs_save(int reg_set); +extern void pm_dbg_regs_dump(int reg_set); +extern void pm_dbg_regs_dump_delta(int cur, int rfr); +extern void pm_dbg_show_wakeup_source(void); +extern void pm_dbg_regs_copy(int tgt, int src); +#else +static inline int pm_dbg_regs_init(struct dentry *d) { return 0; } +static inline void pm_dbg_regs_save(int reg_set) {}; +static inline void pm_dbg_regs_dump(int reg_set) {}; +static inline void pm_dbg_regs_dump_delta(int current, int ref) {} +static inline void pm_dbg_show_wakeup_source(void) {}; +static inline void pm_dbg_regs_copy(int tgt, int src) {}; +#endif + +#endif diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index 0b339861d75..b5dff42606c 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c @@ -36,6 +36,7 @@  #include "cm2xxx_3xxx.h"  #include "prm2xxx_3xxx.h"  #include "pm.h" +#include "pm-debug-regs.h"  u32 enable_off_mode; @@ -226,9 +227,7 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir)  static int option_get(void *data, u64 *val)  { -	u32 *option = data; - -	*val = *option; +	*val = omap_pm_get_off_mode();  	return 0;  } @@ -273,6 +272,9 @@ static int __init pm_dbg_init(void)  	(void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d,  				   &enable_off_mode, &pm_dbg_option_fops); + +	pm_dbg_regs_init(d); +  	pm_dbg_init_done = 1;  	return 0; diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index e742118fcfd..b06b22c3f64 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -17,6 +17,7 @@  #include <linux/export.h>  #include <linux/suspend.h>  #include <linux/cpu.h> +#include <linux/of_platform.h>  #include <asm/system_misc.h> @@ -227,6 +228,9 @@ static int omap_pm_begin(suspend_state_t state)  static void omap_pm_end(void)  {  	cpu_idle_poll_ctrl(false); + +	if (cpu_is_omap34xx()) +		omap_prcm_irq_complete();  }  static void omap_pm_finish(void) @@ -266,7 +270,12 @@ static void __init omap4_init_voltages(void)  static inline void omap_init_cpufreq(void)  { -	struct platform_device_info devinfo = { .name = "omap-cpufreq", }; +	struct platform_device_info devinfo = { }; + +	if (!of_have_populated_dt()) +		devinfo.name = "omap-cpufreq"; +	else +		devinfo.name = "cpufreq-cpu0";  	platform_device_register_full(&devinfo);  } @@ -300,10 +309,18 @@ int __init omap2_common_pm_late_init(void)  		/* Smartreflex device init */  		omap_devinit_smartreflex(); -		/* cpufreq dummy device instantiation */ -		omap_init_cpufreq(); +	} else { +		struct device_node *np; +		np = of_find_node_by_name(NULL, "omap_pimic"); +		if (np) { +			of_platform_populate(np, NULL, NULL, NULL); +			of_node_put(np); +		}  	} +	/* cpufreq dummy device instantiation */ +	omap_init_cpufreq(); +  #ifdef CONFIG_SUSPEND  	suspend_set_ops(&omap_pm_ops);  #endif diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 7bdd22afce6..4f6f52b5215 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -32,7 +32,7 @@ static inline int omap4_idle_init(void)  extern void *omap3_secure_ram_storage;  extern void omap3_pm_off_mode_enable(int); -extern void omap_sram_idle(void); +extern void omap_sram_idle(bool in_suspend);  extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused);  extern int (*omap_pm_suspend)(void); diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 5a2d8034c8d..6f67bfa0b03 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -30,15 +30,18 @@  #include <linux/slab.h>  #include <linux/omap-dma.h>  #include <linux/platform_data/gpio-omap.h> +#include <linux/pm_runtime.h>  #include <trace/events/power.h>  #include <asm/fncpy.h> +#include <asm/setup.h>  #include <asm/suspend.h>  #include <asm/system_misc.h>  #include "clockdomain.h"  #include "powerdomain.h" +#include "omap-pm.h"  #include "soc.h"  #include "common.h"  #include "cm3xxx.h" @@ -50,9 +53,17 @@  #include "sdrc.h"  #include "sram.h"  #include "control.h" +#include "pm-debug-regs.h" +#include "iomap.h" +#include "omap_device.h" + +#include "pad_wkup.h"  /* pm34xx errata defined in pm.h */  u16 pm34xx_errata; +bool suspend_debug; +bool suspend_offmode_ref_saved; +static struct timespec suspend_time_before;  struct power_state {  	struct powerdomain *pwrdm; @@ -70,6 +81,8 @@ void (*omap3_do_wfi_sram)(void);  static struct powerdomain *mpu_pwrdm, *neon_pwrdm;  static struct powerdomain *core_pwrdm, *per_pwrdm; +static struct powerdomain *dss_pwrdm; +static struct omap_hwmod *wd_hwmod;  static void omap3_core_save_context(void)  { @@ -144,6 +157,7 @@ static void omap3_save_secure_ram_context(void)   */  static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits)  { +	static struct clk *per_dpll_clk;  	u32 wkst, fclk, iclk, clken;  	u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1;  	u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1; @@ -156,6 +170,16 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits)  	wkst &= omap2_prm_read_mod_reg(module, grpsel_off);  	wkst &= ~ignore_bits;  	if (wkst) { +		if (module != WKUP_MOD) { +			if (per_dpll_clk == NULL) { +				per_dpll_clk = clk_get(NULL, "dpll4_ck"); +				if (per_dpll_clk == NULL) { +					pr_emerg("Unable to get dpll4_ck\n"); +					BUG(); +				} +			} +			clk_enable(per_dpll_clk); +		}  		iclk = omap2_cm_read_mod_reg(module, iclk_off);  		fclk = omap2_cm_read_mod_reg(module, fclk_off);  		while (wkst) { @@ -172,9 +196,20 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits)  			wkst = omap2_prm_read_mod_reg(module, wkst_off);  			wkst &= ~ignore_bits;  			c++; + +			/* Better to panic than to fade away */ +			if (c > 1000) { +				pr_emerg("FATAL: Unable to clear wkst " +					"wkst = 0x%08x module = 0x%04x " +					"regs = 0x%02x\n", +					wkst, module, regs); +				BUG(); +			}  		}  		omap2_cm_write_mod_reg(iclk, module, iclk_off);  		omap2_cm_write_mod_reg(fclk, module, fclk_off); +		if (module != WKUP_MOD) +			clk_disable(per_dpll_clk);  	}  	return c; @@ -184,6 +219,7 @@ static irqreturn_t _prcm_int_handle_io(int irq, void *unused)  {  	int c; +	_reconfigure_io_chain();  	c = prcm_clear_mod_irqs(WKUP_MOD, 1,  		~(OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK)); @@ -228,11 +264,21 @@ static void omap34xx_save_context(u32 *save)  static int omap34xx_do_sram_idle(unsigned long save_state)  { +	if (suspend_debug) { +		pr_debug("OMAP3_PRM_IRQENABLE_MPU_OFFSET 0x%08x\n", +			 omap2_prm_read_mod_reg(OCP_MOD, +				OMAP3_PRM_IRQENABLE_MPU_OFFSET)); +		omap_prcm_irq_restore(); +		pr_debug("OMAP3_PRM_IRQENABLE_MPU_OFFSET 0x%08x\n", +			 omap2_prm_read_mod_reg(OCP_MOD, +				OMAP3_PRM_IRQENABLE_MPU_OFFSET)); +	} +  	omap34xx_cpu_suspend(save_state);  	return 0;  } -void omap_sram_idle(void) +void omap_sram_idle(bool in_suspend)  {  	/* Variable to tell what needs to be saved and restored  	 * in omap_sram_idle*/ @@ -276,20 +322,29 @@ void omap_sram_idle(void)  	/* PER */  	if (per_next_state < PWRDM_POWER_ON) { -		per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0; +		per_going_off = (per_next_state == PWRDM_POWER_OFF) ? +			OFF_MODE : 0;  		omap2_gpio_prepare_for_idle(per_going_off);  	}  	/* CORE */  	if (core_next_state < PWRDM_POWER_ON) { +		_reconfigure_io_chain(); +		if (!in_suspend && wd_hwmod != NULL) +			platform_pm_suspend(&wd_hwmod->od->pdev->dev); +  		if (core_next_state == PWRDM_POWER_OFF) {  			omap3_core_save_context();  			omap3_cm_save_context(); -		} + +			omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, +						   WKUP_MOD, PM_WKEN); +			omap_prm_configure(true); +		} else +			omap_prm_configure(false);  	}  	omap3_intc_prepare_idle(); -  	/*  	 * On EMU/HS devices ROM code restores a SRDC value  	 * from scratchpad which has automatic self refresh on timeout @@ -302,6 +357,9 @@ void omap_sram_idle(void)  	    core_next_state == PWRDM_POWER_OFF)  		sdrc_pwr = sdrc_read_reg(SDRC_POWER); +	if (suspend_debug) +		pm_dbg_regs_save(1); +  	/*  	 * omap3_arm_context is the location where some ARM context  	 * get saved. The rest is placed on the stack, and restored @@ -314,6 +372,9 @@ void omap_sram_idle(void)  	else  		omap34xx_do_sram_idle(save_state); +	if (suspend_debug) +		pm_dbg_regs_save(2); +  	/* Restore normal SDRC POWER settings */  	if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 &&  	    (omap_type() == OMAP2_DEVICE_TYPE_EMU || @@ -323,17 +384,26 @@ void omap_sram_idle(void)  	/* CORE */  	if (core_next_state < PWRDM_POWER_ON) { +		if (!in_suspend && wd_hwmod != NULL) +			platform_pm_resume(&wd_hwmod->od->pdev->dev); +  		core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);  		if (core_prev_state == PWRDM_POWER_OFF) {  			omap3_core_restore_context();  			omap3_cm_restore_context();  			omap3_sram_restore_context();  			omap2_sms_restore_context(); + +			if (unlikely(!suspend_offmode_ref_saved)) { +				suspend_offmode_ref_saved = true; +				pm_dbg_regs_copy(3, 1); +				pm_dbg_regs_copy(4, 2); +			} +		} +		if (core_next_state == PWRDM_POWER_OFF) { +			omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, +						     WKUP_MOD, PM_WKEN);  		} -		if (core_next_state == PWRDM_POWER_OFF) -			omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK, -					       OMAP3430_GR_MOD, -					       OMAP3_PRM_VOLTCTRL_OFFSET);  	}  	omap3_intc_resume_idle(); @@ -341,7 +411,8 @@ void omap_sram_idle(void)  	/* PER */  	if (per_next_state < PWRDM_POWER_ON) -		omap2_gpio_resume_after_idle(); +		omap2_gpio_resume_after_idle(in_suspend); +  }  static void omap3_pm_idle(void) @@ -351,7 +422,7 @@ static void omap3_pm_idle(void)  	trace_cpu_idle(1, smp_processor_id()); -	omap_sram_idle(); +	omap_sram_idle(false);  	trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id());  } @@ -361,6 +432,9 @@ static int omap3_pm_suspend(void)  {  	struct power_state *pwrst;  	int state, ret = 0; +	struct timespec after; + +	suspend_debug = true;  	/* Read current next_pwrsts */  	list_for_each_entry(pwrst, &pwrst_list, node) @@ -373,11 +447,18 @@ static int omap3_pm_suspend(void)  			goto restore;  	} +	read_persistent_clock(&suspend_time_before); +  	omap3_intc_suspend(); -	omap_sram_idle(); +	omap_sram_idle(true); + +	prcm_handle_pad_wkup();  restore: +	read_persistent_clock(&after); +	after = timespec_sub(after, suspend_time_before); +  	/* Restore next_pwrsts */  	list_for_each_entry(pwrst, &pwrst_list, node) {  		state = pwrdm_read_prev_pwrst(pwrst->pwrdm); @@ -388,11 +469,26 @@ restore:  		}  		omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);  	} -	if (ret) -		pr_err("Could not enter target state in pm_suspend\n"); -	else -		pr_info("Successfully put all powerdomains to target state\n"); +	if (ret) { +		pr_err("Could not enter target state in pm_suspend " +			"for %lu.%03lu seconds\n", after.tv_sec, +			after.tv_nsec / NSEC_PER_MSEC); +		if (suspend_offmode_ref_saved) { +			pm_dbg_regs_dump_delta(1, 3); +			pm_dbg_regs_dump_delta(2, 4); +		} else { +			pm_dbg_regs_dump(1); +			pm_dbg_regs_dump(2); +		} +	} else +		pr_info("Successfully put all powerdomains to target state " +			"for %lu.%03lu seconds\n", after.tv_sec, +			after.tv_nsec / NSEC_PER_MSEC); + +	pm_dbg_show_wakeup_source(); + +	suspend_debug = false;  	return ret;  } @@ -644,9 +740,15 @@ static void __init pm_errata_configure(void)  		pm34xx_errata |= PM_RTA_ERRATUM_i608;  		/* Enable the l2 cache toggling in sleep logic */  		enable_omap3630_toggle_l2_on_restore(); +  		if (omap_rev() < OMAP3630_REV_ES1_2) -			pm34xx_errata |= (PM_SDRC_WAKEUP_ERRATUM_i583 | -					  PM_PER_MEMORIES_ERRATUM_i582); +			pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; +#ifndef CONFIG_DISABLE_OMAP_ERRATA_i583 +			pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; +#endif +		if (meminfo.bank[0].size > 256 * (1024 * 1024)) +			pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; +  	} else if (cpu_is_omap34xx()) {  		pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582;  	} @@ -700,10 +802,12 @@ int __init omap3_pm_init(void)  		ret = -EINVAL;  		goto err3;  	} +	wd_hwmod = omap_hwmod_lookup("wd_timer2");  	neon_pwrdm = pwrdm_lookup("neon_pwrdm");  	per_pwrdm = pwrdm_lookup("per_pwrdm");  	core_pwrdm = pwrdm_lookup("core_pwrdm"); +	dss_pwrdm = pwrdm_lookup("dss_pwrdm");  	neon_clkdm = clkdm_lookup("neon_clkdm");  	mpu_clkdm = clkdm_lookup("mpu_clkdm"); @@ -714,6 +818,9 @@ int __init omap3_pm_init(void)  	omap_pm_suspend = omap3_pm_suspend;  #endif +	if (omap_pm_get_off_mode()) +		omap3_pm_off_mode_enable(true); +  	arm_pm_idle = omap3_pm_idle;  	omap3_idle_init(); diff --git a/arch/arm/mach-omap2/prcm-common.h b/arch/arm/mach-omap2/prcm-common.h index c7d355fafd2..bc83da73d18 100644 --- a/arch/arm/mach-omap2/prcm-common.h +++ b/arch/arm/mach-omap2/prcm-common.h @@ -487,6 +487,7 @@ extern int omap_prcm_register_chain_handler(  extern int omap_prcm_event_to_irq(const char *event);  extern void omap_prcm_irq_prepare(void);  extern void omap_prcm_irq_complete(void); +extern void omap_prcm_irq_restore(void);  # endif diff --git a/arch/arm/mach-omap2/prm2xxx.h b/arch/arm/mach-omap2/prm2xxx.h index 3194dd87e0e..8f16aecf92f 100644 --- a/arch/arm/mach-omap2/prm2xxx.h +++ b/arch/arm/mach-omap2/prm2xxx.h @@ -94,6 +94,9 @@   */  /* Register offsets appearing on both OMAP2 and OMAP3 */ +#define INTC_PENDING_IRQ0				0x0098 +#define INTC_PENDING_IRQ1				0x00b8 +#define INTC_PENDING_IRQ2				0x00d8  #define OMAP2_RM_RSTCTRL				0x0050  #define OMAP2_RM_RSTTIME				0x0054 diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 228b850e632..c82aea6f358 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -216,6 +216,14 @@ void omap_prcm_irq_prepare(void)  	prcm_irq_setup->suspended = true;  } +void omap_prcm_irq_restore(void) +{ +	if (prcm_irq_setup->suspend_save_flag) { +		prcm_irq_setup->restore_irqen(prcm_irq_setup->saved_mask); +		prcm_irq_setup->ocp_barrier(); +	} +} +  void omap_prcm_irq_complete(void)  {  	prcm_irq_setup->suspended = false; diff --git a/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h b/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h new file mode 100644 index 00000000000..c7acdb8312d --- /dev/null +++ b/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h @@ -0,0 +1,66 @@ +/* + * SDRC register values for the Toshiba, Hynxi and Numonyx. + * These are common SDRC parameters for all vendors since we + * use the JEDEC JESD209A parameters. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ARCH_ARM_MACH_OMAP2_SDRAM_TOSHIBA_HYNIX_NUMONYX + +#define ARCH_ARM_MACH_OMAP2_SDRAM_TOSHIBA_HYNIX_NUMONYX + +#include "sdrc.h" + +static struct omap_sdrc_params JEDEC_JESD209A_sdrc_params[] = { +	[0] = { +		.rate        = 200000000, +		.actim_ctrla = 0xE2E1B4C6, +		.actim_ctrlb = 0x00022228, +		.rfr_ctrl    = 0x0005E602, +		.mr          = 0x00000032, +	}, +	[1] = { +		.rate        = 100000000, +		.actim_ctrla = 0x7211B485, +		.actim_ctrlb = 0x00022214, +		.rfr_ctrl    = 0x0002DA02, +		.mr          = 0x00000032, +	}, +	[2] = { +		.rate        = 166000000, +		.actim_ctrla = 0xE2E1B4C6, +		.actim_ctrlb = 0x00022228, +		.rfr_ctrl    = 0x0004DD02, +		.mr          = 0x00000032, +	}, +	[3] = { +		.rate        = 83000000, +		.actim_ctrla = 0x7215B485, +		.actim_ctrlb = 0x00022214, +		.rfr_ctrl    = 0x00025602, +		.mr          = 0x00000032, +	}, +	[4] = { +		.rate        = 160000000, +		.actim_ctrla = 0xBA9DB4C6, +		.actim_ctrlb = 0x00022220, +		.rfr_ctrl    = 0x0004AE02, +		.mr	     = 0x00000032, +	}, +	[5] = { +		.rate        = 80000000, +		.actim_ctrla = 0x49512284, +		.actim_ctrlb = 0x0001120C, +		.rfr_ctrl    = 0x23E02, +		.mr	     = 0x00000032, +	}, +	[6] = { +		.rate        = 0 +	}, +}; + +#endif + diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index f6601563aa6..b570d93cbb7 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -22,6 +22,7 @@  #include <linux/clk.h>  #include <linux/io.h>  #include <linux/delay.h> +#include <linux/spinlock.h>  #include <linux/platform_device.h>  #include <linux/slab.h>  #include <linux/pm_runtime.h> @@ -81,7 +82,7 @@ static struct omap_uart_port_info omap_serial_default_info[] __initdata = {  };  #ifdef CONFIG_PM -static void omap_uart_enable_wakeup(struct device *dev, bool enable) +void omap_uart_enable_wakeup(struct device *dev, bool enable)  {  	struct platform_device *pdev = to_platform_device(dev);  	struct omap_device *od = to_omap_device(pdev); @@ -94,10 +95,35 @@ static void omap_uart_enable_wakeup(struct device *dev, bool enable)  	else  		omap_hwmod_disable_wakeup(od->hwmods[0]);  } +void omap_uart_remove_wakeup(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct omap_device *od = to_omap_device(pdev); +	struct omap_hwmod *oh = od->hwmods[0]; +	u16 offs; +	unsigned long flags; + +	spin_lock_irqsave(&oh->_lock, flags); +	omap_hwmod_disable_wakeup(oh); +	if (oh->class->sysc) +		oh->class->sysc->sysc_flags &= ~SYSC_HAS_ENAWAKEUP; +	offs = (oh->prcm.omap2.prcm_reg_id == 3) ? +					OMAP3430ES2_PM_WKEN3 : PM_WKEN1; +	omap2_prm_clear_mod_reg_bits((1<<oh->prcm.omap2.module_bit), +				     oh->prcm.omap2.module_offs, offs); +	offs = (oh->prcm.omap2.prcm_reg_id == 3) ? +					OMAP3430ES2_PM_WKST3 : PM_WKST1; +	omap2_prm_set_mod_reg_bits((1<<oh->prcm.omap2.module_bit), +				   oh->prcm.omap2.module_offs, offs); +	spin_unlock_irqrestore(&oh->_lock, flags); +}  #else -static void omap_uart_enable_wakeup(struct device *dev, bool enable) +void omap_uart_enable_wakeup(struct device *dev, bool enable)  {} +void omap_uart_remove_wakeup(struct device *dev) +{ +}  #endif /* CONFIG_PM */  #ifdef CONFIG_OMAP_MUX  |