Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d297f5e authored by Tony Lindgren's avatar Tony Lindgren
Browse files

Merge branch 'tk_prm_chain_handler_devel_3.3' of git://git.pwsan.com/linux-2.6 into prcm

Conflicts:
	arch/arm/mach-omap2/Makefile
parents aacf0941 2f31b516
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ endif
endif

# PRCM
obj-y					+= prm_common.o
obj-$(CONFIG_ARCH_OMAP2)		+= prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o
obj-$(CONFIG_ARCH_OMAP3)		+= prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \
					   vc3xxx_data.o vp3xxx_data.o
@@ -90,7 +91,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \
obj-$(CONFIG_ARCH_OMAP4)		+= prcm.o cm2xxx_3xxx.o cminst44xx.o \
					   cm44xx.o prcm_mpu44xx.o \
					   prminst44xx.o vc44xx_data.o \
					   vp44xx_data.o
					   vp44xx_data.o prm44xx.o

# OMAP voltage domains
voltagedomain-common			:= voltage.o vc.o vp.o
+87 −2
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#include <asm/system.h>

@@ -39,6 +41,7 @@

#include "control.h"
#include "mux.h"
#include "prm.h"

#define OMAP_MUX_BASE_OFFSET		0x30	/* Offset from CTRL_BASE */
#define OMAP_MUX_BASE_SZ		0x5ca
@@ -306,7 +309,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
		pad->idle = bpad->idle;
		pad->off = bpad->off;

		if (pad->flags & OMAP_DEVICE_PAD_REMUX)
		if (pad->flags &
		    (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP))
			nr_pads_dynamic++;

		pr_debug("%s: Initialized %s\n", __func__, pad->name);
@@ -331,7 +335,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
	for (i = 0; i < hmux->nr_pads; i++) {
		struct omap_device_pad *pad = &hmux->pads[i];

		if (pad->flags & OMAP_DEVICE_PAD_REMUX) {
		if (pad->flags &
		    (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) {
			pr_debug("%s: pad %s tagged dynamic\n",
					__func__, pad->name);
			hmux->pads_dynamic[nr_pads_dynamic] = pad;
@@ -351,6 +356,78 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
	return NULL;
}

/**
 * omap_hwmod_mux_scan_wakeups - omap hwmod scan wakeup pads
 * @hmux: Pads for a hwmod
 * @mpu_irqs: MPU irq array for a hwmod
 *
 * Scans the wakeup status of pads for a single hwmod.  If an irq
 * array is defined for this mux, the parser will call the registered
 * ISRs for corresponding pads, otherwise the parser will stop at the
 * first wakeup active pad and return.  Returns true if there is a
 * pending and non-served wakeup event for the mux, otherwise false.
 */
static bool omap_hwmod_mux_scan_wakeups(struct omap_hwmod_mux_info *hmux,
		struct omap_hwmod_irq_info *mpu_irqs)
{
	int i, irq;
	unsigned int val;
	u32 handled_irqs = 0;

	for (i = 0; i < hmux->nr_pads_dynamic; i++) {
		struct omap_device_pad *pad = hmux->pads_dynamic[i];

		if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP) ||
		    !(pad->idle & OMAP_WAKEUP_EN))
			continue;

		val = omap_mux_read(pad->partition, pad->mux->reg_offset);
		if (!(val & OMAP_WAKEUP_EVENT))
			continue;

		if (!hmux->irqs)
			return true;

		irq = hmux->irqs[i];
		/* make sure we only handle each irq once */
		if (handled_irqs & 1 << irq)
			continue;

		handled_irqs |= 1 << irq;

		generic_handle_irq(mpu_irqs[irq].irq);
	}

	return false;
}

/**
 * _omap_hwmod_mux_handle_irq - Process wakeup events for a single hwmod
 *
 * Checks a single hwmod for every wakeup capable pad to see if there is an
 * active wakeup event. If this is the case, call the corresponding ISR.
 */
static int _omap_hwmod_mux_handle_irq(struct omap_hwmod *oh, void *data)
{
	if (!oh->mux || !oh->mux->enabled)
		return 0;
	if (omap_hwmod_mux_scan_wakeups(oh->mux, oh->mpu_irqs))
		generic_handle_irq(oh->mpu_irqs[0].irq);
	return 0;
}

/**
 * omap_hwmod_mux_handle_irq - Process pad wakeup irqs.
 *
 * Calls a function for each registered omap_hwmod to check
 * pad wakeup statuses.
 */
static irqreturn_t omap_hwmod_mux_handle_irq(int irq, void *unused)
{
	omap_hwmod_for_each(_omap_hwmod_mux_handle_irq, NULL);
	return IRQ_HANDLED;
}

/* Assumes the calling function takes care of locking */
void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state)
{
@@ -715,6 +792,7 @@ static void __init omap_mux_free_names(struct omap_mux *m)
static int __init omap_mux_late_init(void)
{
	struct omap_mux_partition *partition;
	int ret;

	list_for_each_entry(partition, &mux_partitions, node) {
		struct omap_mux_entry *e, *tmp;
@@ -735,6 +813,13 @@ static 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);

	if (ret)
		pr_warning("mux: Failed to setup hwmod io irq %d\n", ret);

	omap_mux_dbg_init();

	return 0;
+102 −0
Original line number Diff line number Diff line
@@ -136,6 +136,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/slab.h>

#include "common.h"
#include <plat/cpu.h>
@@ -380,6 +381,51 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle,
	return 0;
}

/**
 * _set_idle_ioring_wakeup - enable/disable IO pad wakeup on hwmod idle for mux
 * @oh: struct omap_hwmod *
 * @set_wake: bool value indicating to set (true) or clear (false) wakeup enable
 *
 * Set or clear the I/O pad wakeup flag in the mux entries for the
 * hwmod @oh.  This function changes the @oh->mux->pads_dynamic array
 * in memory.  If the hwmod is currently idled, and the new idle
 * values don't match the previous ones, this function will also
 * update the SCM PADCTRL registers.  Otherwise, if the hwmod is not
 * currently idled, this function won't touch the hardware: the new
 * mux settings are written to the SCM PADCTRL registers when the
 * hwmod is idled.  No return value.
 */
static void _set_idle_ioring_wakeup(struct omap_hwmod *oh, bool set_wake)
{
	struct omap_device_pad *pad;
	bool change = false;
	u16 prev_idle;
	int j;

	if (!oh->mux || !oh->mux->enabled)
		return;

	for (j = 0; j < oh->mux->nr_pads_dynamic; j++) {
		pad = oh->mux->pads_dynamic[j];

		if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP))
			continue;

		prev_idle = pad->idle;

		if (set_wake)
			pad->idle |= OMAP_WAKEUP_EN;
		else
			pad->idle &= ~OMAP_WAKEUP_EN;

		if (prev_idle != pad->idle)
			change = true;
	}

	if (change && oh->_state == _HWMOD_STATE_IDLE)
		omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
}

/**
 * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware
 * @oh: struct omap_hwmod *
@@ -2437,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
	v = oh->_sysc_cache;
	_enable_wakeup(oh, &v);
	_write_sysconfig(v, oh);
	_set_idle_ioring_wakeup(oh, true);
	spin_unlock_irqrestore(&oh->_lock, flags);

	return 0;
@@ -2467,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh)
	v = oh->_sysc_cache;
	_disable_wakeup(oh, &v);
	_write_sysconfig(v, oh);
	_set_idle_ioring_wakeup(oh, false);
	spin_unlock_irqrestore(&oh->_lock, flags);

	return 0;
@@ -2683,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh)

	return 0;
}

/**
 * omap_hwmod_pad_route_irq - route an I/O pad wakeup to a particular MPU IRQ
 * @oh: struct omap_hwmod * containing hwmod mux entries
 * @pad_idx: array index in oh->mux of the hwmod mux entry to route wakeup
 * @irq_idx: the hwmod mpu_irqs array index of the IRQ to trigger on wakeup
 *
 * When an I/O pad wakeup arrives for the dynamic or wakeup hwmod mux
 * entry number @pad_idx for the hwmod @oh, trigger the interrupt
 * service routine for the hwmod's mpu_irqs array index @irq_idx.  If
 * this function is not called for a given pad_idx, then the ISR
 * associated with @oh's first MPU IRQ will be triggered when an I/O
 * pad wakeup occurs on that pad.  Note that @pad_idx is the index of
 * the _dynamic or wakeup_ entry: if there are other entries not
 * marked with OMAP_DEVICE_PAD_WAKEUP or OMAP_DEVICE_PAD_REMUX, these
 * entries are NOT COUNTED in the dynamic pad index.  This function
 * must be called separately for each pad that requires its interrupt
 * to be re-routed this way.  Returns -EINVAL if there is an argument
 * problem or if @oh does not have hwmod mux entries or MPU IRQs;
 * returns -ENOMEM if memory cannot be allocated; or 0 upon success.
 *
 * XXX This function interface is fragile.  Rather than using array
 * indexes, which are subject to unpredictable change, it should be
 * using hwmod IRQ names, and some other stable key for the hwmod mux
 * pad records.
 */
int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx)
{
	int nr_irqs;

	might_sleep();

	if (!oh || !oh->mux || !oh->mpu_irqs || pad_idx < 0 ||
	    pad_idx >= oh->mux->nr_pads_dynamic)
		return -EINVAL;

	/* Check the number of available mpu_irqs */
	for (nr_irqs = 0; oh->mpu_irqs[nr_irqs].irq >= 0; nr_irqs++)
		;

	if (irq_idx >= nr_irqs)
		return -EINVAL;

	if (!oh->mux->irqs) {
		/* XXX What frees this? */
		oh->mux->irqs = kzalloc(sizeof(int) * oh->mux->nr_pads_dynamic,
			GFP_KERNEL);
		if (!oh->mux->irqs)
			return -ENOMEM;
	}
	oh->mux->irqs[pad_idx] = irq_idx;

	return 0;
}
+44 −71
Original line number Diff line number Diff line
@@ -195,7 +195,7 @@ static void omap3_save_secure_ram_context(void)
 * that any peripheral wake-up events occurring while attempting to
 * clear the PM_WKST_x are detected and cleared.
 */
static int prcm_clear_mod_irqs(s16 module, u8 regs)
static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits)
{
	u32 wkst, fclk, iclk, clken;
	u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1;
@@ -207,6 +207,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)

	wkst = omap2_prm_read_mod_reg(module, wkst_off);
	wkst &= omap2_prm_read_mod_reg(module, grpsel_off);
	wkst &= ~ignore_bits;
	if (wkst) {
		iclk = omap2_cm_read_mod_reg(module, iclk_off);
		fclk = omap2_cm_read_mod_reg(module, fclk_off);
@@ -222,6 +223,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
			omap2_cm_set_mod_reg_bits(clken, module, fclk_off);
			omap2_prm_write_mod_reg(wkst, module, wkst_off);
			wkst = omap2_prm_read_mod_reg(module, wkst_off);
			wkst &= ~ignore_bits;
			c++;
		}
		omap2_cm_write_mod_reg(iclk, module, iclk_off);
@@ -231,76 +233,35 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
	return c;
}

static int _prcm_int_handle_wakeup(void)
static irqreturn_t _prcm_int_handle_io(int irq, void *unused)
{
	int c;

	c = prcm_clear_mod_irqs(WKUP_MOD, 1);
	c += prcm_clear_mod_irqs(CORE_MOD, 1);
	c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
	if (omap_rev() > OMAP3430_REV_ES1_0) {
		c += prcm_clear_mod_irqs(CORE_MOD, 3);
		c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
	}
	c = prcm_clear_mod_irqs(WKUP_MOD, 1,
		~(OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK));

	return c;
	return c ? IRQ_HANDLED : IRQ_NONE;
}

/*
 * PRCM Interrupt Handler
 *
 * The PRM_IRQSTATUS_MPU register indicates if there are any pending
 * interrupts from the PRCM for the MPU. These bits must be cleared in
 * order to clear the PRCM interrupt. The PRCM interrupt handler is
 * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear
 * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU
 * register indicates that a wake-up event is pending for the MPU and
 * this bit can only be cleared if the all the wake-up events latched
 * in the various PM_WKST_x registers have been cleared. The interrupt
 * handler is implemented using a do-while loop so that if a wake-up
 * event occurred during the processing of the prcm interrupt handler
 * (setting a bit in the corresponding PM_WKST_x register and thus
 * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register)
 * this would be handled.
 */
static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused)
{
	u32 irqenable_mpu, irqstatus_mpu;
	int c = 0;

	irqenable_mpu = omap2_prm_read_mod_reg(OCP_MOD,
					 OMAP3_PRM_IRQENABLE_MPU_OFFSET);
	irqstatus_mpu = omap2_prm_read_mod_reg(OCP_MOD,
					 OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
	irqstatus_mpu &= irqenable_mpu;

	do {
		if (irqstatus_mpu & (OMAP3430_WKUP_ST_MASK |
				     OMAP3430_IO_ST_MASK)) {
			c = _prcm_int_handle_wakeup();
	int c;

	/*
			 * Is the MPU PRCM interrupt handler racing with the
			 * IVA2 PRCM interrupt handler ?
	 * Clear all except ST_IO and ST_IO_CHAIN for wkup module,
	 * these are handled in a separate handler to avoid acking
	 * IO events before parsing in mux code
	 */
			WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
			     "but no wakeup sources are marked\n");
		} else {
			/* XXX we need to expand our PRCM interrupt handler */
			WARN(1, "prcm: WARNING: PRCM interrupt received, but "
			     "no code to handle it (%08x)\n", irqstatus_mpu);
	c = prcm_clear_mod_irqs(WKUP_MOD, 1,
		OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK);
	c += prcm_clear_mod_irqs(CORE_MOD, 1, 0);
	c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1, 0);
	if (omap_rev() > OMAP3430_REV_ES1_0) {
		c += prcm_clear_mod_irqs(CORE_MOD, 3, 0);
		c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1, 0);
	}

		omap2_prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
					OMAP3_PRM_IRQSTATUS_MPU_OFFSET);

		irqstatus_mpu = omap2_prm_read_mod_reg(OCP_MOD,
					OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
		irqstatus_mpu &= irqenable_mpu;

	} while (irqstatus_mpu);

	return IRQ_HANDLED;
	return c ? IRQ_HANDLED : IRQ_NONE;
}

static void omap34xx_save_context(u32 *save)
@@ -581,6 +542,7 @@ static int omap3_pm_begin(suspend_state_t state)
	disable_hlt();
	suspend_state = state;
	omap_uart_enable_irqs(0);
	omap_prcm_irq_prepare();
	return 0;
}

@@ -592,10 +554,16 @@ static void omap3_pm_end(void)
	return;
}

static void omap3_pm_finish(void)
{
	omap_prcm_irq_complete();
}

static const struct platform_suspend_ops omap_pm_ops = {
	.begin		= omap3_pm_begin,
	.end		= omap3_pm_end,
	.enter		= omap3_pm_enter,
	.finish		= omap3_pm_finish,
	.valid		= suspend_valid_only_mem,
};
#endif /* CONFIG_SUSPEND */
@@ -701,10 +669,6 @@ static void __init prcm_setup_regs(void)
			  OMAP3430_GRPSEL_GPT1_MASK |
			  OMAP3430_GRPSEL_GPT12_MASK,
			  WKUP_MOD, OMAP3430_PM_MPUGRPSEL);
	/* For some reason IO doesn't generate wakeup event even if
	 * it is selected to mpu wakeup goup */
	omap2_prm_write_mod_reg(OMAP3430_IO_EN_MASK | OMAP3430_WKUP_EN_MASK,
			  OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);

	/* Enable PM_WKEN to support DSS LPR */
	omap2_prm_write_mod_reg(OMAP3430_PM_WKEN_DSS_EN_DSS_MASK,
@@ -881,12 +845,21 @@ static int __init omap3_pm_init(void)
	 * supervised mode for powerdomains */
	prcm_setup_regs();

	ret = request_irq(INT_34XX_PRCM_MPU_IRQ,
			  (irq_handler_t)prcm_interrupt_handler,
			  IRQF_DISABLED, "prcm", NULL);
	ret = request_irq(omap_prcm_event_to_irq("wkup"),
		_prcm_int_handle_wakeup, IRQF_NO_SUSPEND, "pm_wkup", NULL);

	if (ret) {
		pr_err("pm: Failed to request pm_wkup irq\n");
		goto err1;
	}

	/* IO interrupt is shared with mux code */
	ret = request_irq(omap_prcm_event_to_irq("io"),
		_prcm_int_handle_io, IRQF_SHARED | IRQF_NO_SUSPEND, "pm_io",
		omap3_pm_init);

	if (ret) {
		printk(KERN_ERR "request_irq failed to register for 0x%x\n",
		       INT_34XX_PRCM_MPU_IRQ);
		pr_err("pm: Failed to request pm_io irq\n");
		goto err1;
	}

+74 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
/*
 * OMAP2/3 PRCM base and module definitions
 *
 * Copyright (C) 2007-2009 Texas Instruments, Inc.
 * Copyright (C) 2007-2009, 2011 Texas Instruments, Inc.
 * Copyright (C) 2007-2009 Nokia Corporation
 *
 * Written by Paul Walmsley
@@ -408,6 +408,79 @@
extern void __iomem *prm_base;
extern void __iomem *cm_base;
extern void __iomem *cm2_base;

/**
 * struct omap_prcm_irq - describes a PRCM interrupt bit
 * @name: a short name describing the interrupt type, e.g. "wkup" or "io"
 * @offset: the bit shift of the interrupt inside the IRQ{ENABLE,STATUS} regs
 * @priority: should this interrupt be handled before @priority=false IRQs?
 *
 * Describes interrupt bits inside the PRM_IRQ{ENABLE,STATUS}_MPU* registers.
 * On systems with multiple PRM MPU IRQ registers, the bitfields read from
 * the registers are concatenated, so @offset could be > 31 on these systems -
 * see omap_prm_irq_handler() for more details.  I/O ring interrupts should
 * have @priority set to true.
 */
struct omap_prcm_irq {
	const char *name;
	unsigned int offset;
	bool priority;
};

/**
 * struct omap_prcm_irq_setup - PRCM interrupt controller details
 * @ack: PRM register offset for the first PRM_IRQSTATUS_MPU register
 * @mask: PRM register offset for the first PRM_IRQENABLE_MPU register
 * @nr_regs: number of PRM_IRQ{STATUS,ENABLE}_MPU* registers
 * @nr_irqs: number of entries in the @irqs array
 * @irqs: ptr to an array of PRCM interrupt bits (see @nr_irqs)
 * @irq: MPU IRQ asserted when a PRCM interrupt arrives
 * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending
 * @ocp_barrier: fn ptr to force buffered PRM writes to complete
 * @save_and_clear_irqen: fn ptr to save and clear IRQENABLE regs
 * @restore_irqen: fn ptr to save and clear IRQENABLE regs
 * @saved_mask: IRQENABLE regs are saved here during suspend
 * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true
 * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init
 * @suspended: set to true after Linux suspend code has called our ->prepare()
 * @suspend_save_flag: set to true after IRQ masks have been saved and disabled
 *
 * @saved_mask, @priority_mask, @base_irq, @suspended, and
 * @suspend_save_flag are populated dynamically, and are not to be
 * specified in static initializers.
 */
struct omap_prcm_irq_setup {
	u16 ack;
	u16 mask;
	u8 nr_regs;
	u8 nr_irqs;
	const struct omap_prcm_irq *irqs;
	int irq;
	void (*read_pending_irqs)(unsigned long *events);
	void (*ocp_barrier)(void);
	void (*save_and_clear_irqen)(u32 *saved_mask);
	void (*restore_irqen)(u32 *saved_mask);
	u32 *saved_mask;
	u32 *priority_mask;
	int base_irq;
	bool suspended;
	bool suspend_save_flag;
};

/* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */
#define OMAP_PRCM_IRQ(_name, _offset, _priority) {	\
	.name = _name,					\
	.offset = _offset,				\
	.priority = _priority				\
	}

extern void omap_prcm_irq_cleanup(void);
extern int omap_prcm_register_chain_handler(
	struct omap_prcm_irq_setup *irq_setup);
extern int omap_prcm_event_to_irq(const char *event);
extern void omap_prcm_irq_prepare(void);
extern void omap_prcm_irq_complete(void);

# endif

#endif
Loading