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
This commit is contained in:
Коммит
9d297f5ee1
|
@ -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
|
||||
|
|
|
@ -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 @@ err1:
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
int c;
|
||||
|
||||
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;
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
do {
|
||||
if (irqstatus_mpu & (OMAP3430_WKUP_ST_MASK |
|
||||
OMAP3430_IO_ST_MASK)) {
|
||||
c = _prcm_int_handle_wakeup();
|
||||
|
||||
/*
|
||||
* Is the MPU PRCM interrupt handler racing with the
|
||||
* IVA2 PRCM interrupt handler ?
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
printk(KERN_ERR "request_irq failed to register for 0x%x\n",
|
||||
INT_34XX_PRCM_MPU_IRQ);
|
||||
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) {
|
||||
pr_err("pm: Failed to request pm_io irq\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OMAP2/3 PRM module functions
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments, Inc.
|
||||
* Copyright (C) 2010-2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
* Benoît Cousson
|
||||
* Paul Walmsley
|
||||
|
@ -27,6 +27,24 @@
|
|||
#include "prm-regbits-24xx.h"
|
||||
#include "prm-regbits-34xx.h"
|
||||
|
||||
static const struct omap_prcm_irq omap3_prcm_irqs[] = {
|
||||
OMAP_PRCM_IRQ("wkup", 0, 0),
|
||||
OMAP_PRCM_IRQ("io", 9, 1),
|
||||
};
|
||||
|
||||
static struct omap_prcm_irq_setup omap3_prcm_irq_setup = {
|
||||
.ack = OMAP3_PRM_IRQSTATUS_MPU_OFFSET,
|
||||
.mask = OMAP3_PRM_IRQENABLE_MPU_OFFSET,
|
||||
.nr_regs = 1,
|
||||
.irqs = omap3_prcm_irqs,
|
||||
.nr_irqs = ARRAY_SIZE(omap3_prcm_irqs),
|
||||
.irq = INT_34XX_PRCM_MPU_IRQ,
|
||||
.read_pending_irqs = &omap3xxx_prm_read_pending_irqs,
|
||||
.ocp_barrier = &omap3xxx_prm_ocp_barrier,
|
||||
.save_and_clear_irqen = &omap3xxx_prm_save_and_clear_irqen,
|
||||
.restore_irqen = &omap3xxx_prm_restore_irqen,
|
||||
};
|
||||
|
||||
u32 omap2_prm_read_mod_reg(s16 module, u16 idx)
|
||||
{
|
||||
return __raw_readl(prm_base + module + idx);
|
||||
|
@ -212,3 +230,80 @@ u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset)
|
|||
{
|
||||
return omap2_prm_rmw_mod_reg_bits(mask, bits, OMAP3430_GR_MOD, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3xxx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events
|
||||
* @events: ptr to a u32, preallocated by caller
|
||||
*
|
||||
* Read PRM_IRQSTATUS_MPU bits, AND'ed with the currently-enabled PRM
|
||||
* MPU IRQs, and store the result into the u32 pointed to by @events.
|
||||
* No return value.
|
||||
*/
|
||||
void omap3xxx_prm_read_pending_irqs(unsigned long *events)
|
||||
{
|
||||
u32 mask, st;
|
||||
|
||||
/* XXX Can the mask read be avoided (e.g., can it come from RAM?) */
|
||||
mask = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
|
||||
st = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
||||
|
||||
events[0] = mask & st;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3xxx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete
|
||||
*
|
||||
* Force any buffered writes to the PRM IP block to complete. Needed
|
||||
* by the PRM IRQ handler, which reads and writes directly to the IP
|
||||
* block, to avoid race conditions after acknowledging or clearing IRQ
|
||||
* bits. No return value.
|
||||
*/
|
||||
void omap3xxx_prm_ocp_barrier(void)
|
||||
{
|
||||
omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3xxx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU reg
|
||||
* @saved_mask: ptr to a u32 array to save IRQENABLE bits
|
||||
*
|
||||
* Save the PRM_IRQENABLE_MPU register to @saved_mask. @saved_mask
|
||||
* must be allocated by the caller. Intended to be used in the PRM
|
||||
* interrupt handler suspend callback. The OCP barrier is needed to
|
||||
* ensure the write to disable PRM interrupts reaches the PRM before
|
||||
* returning; otherwise, spurious interrupts might occur. No return
|
||||
* value.
|
||||
*/
|
||||
void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask)
|
||||
{
|
||||
saved_mask[0] = omap2_prm_read_mod_reg(OCP_MOD,
|
||||
OMAP3_PRM_IRQENABLE_MPU_OFFSET);
|
||||
omap2_prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
|
||||
|
||||
/* OCP barrier */
|
||||
omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3xxx_prm_restore_irqen - set PRM_IRQENABLE_MPU register from args
|
||||
* @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously
|
||||
*
|
||||
* Restore the PRM_IRQENABLE_MPU register from @saved_mask. Intended
|
||||
* to be used in the PRM interrupt handler resume callback to restore
|
||||
* values saved by omap3xxx_prm_save_and_clear_irqen(). No OCP
|
||||
* barrier should be needed here; any pending PRM interrupts will fire
|
||||
* once the writes reach the PRM. No return value.
|
||||
*/
|
||||
void omap3xxx_prm_restore_irqen(u32 *saved_mask)
|
||||
{
|
||||
omap2_prm_write_mod_reg(saved_mask[0], OCP_MOD,
|
||||
OMAP3_PRM_IRQENABLE_MPU_OFFSET);
|
||||
}
|
||||
|
||||
static int __init omap3xxx_prcm_init(void)
|
||||
{
|
||||
if (cpu_is_omap34xx())
|
||||
return omap_prcm_register_chain_handler(&omap3_prcm_irq_setup);
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(omap3xxx_prcm_init);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OMAP2/3 Power/Reset Management (PRM) register definitions
|
||||
*
|
||||
* Copyright (C) 2007-2009 Texas Instruments, Inc.
|
||||
* Copyright (C) 2007-2009, 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2008-2010 Nokia Corporation
|
||||
* Paul Walmsley
|
||||
*
|
||||
|
@ -314,6 +314,13 @@ void omap3_prm_vp_clear_txdone(u8 vp_id);
|
|||
extern u32 omap3_prm_vcvp_read(u8 offset);
|
||||
extern void omap3_prm_vcvp_write(u32 val, u8 offset);
|
||||
extern u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset);
|
||||
|
||||
/* PRM interrupt-related functions */
|
||||
extern void omap3xxx_prm_read_pending_irqs(unsigned long *events);
|
||||
extern void omap3xxx_prm_ocp_barrier(void);
|
||||
extern void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask);
|
||||
extern void omap3xxx_prm_restore_irqen(u32 *saved_mask);
|
||||
|
||||
#endif /* CONFIG_ARCH_OMAP4 */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,6 +27,24 @@
|
|||
#include "prcm44xx.h"
|
||||
#include "prminst44xx.h"
|
||||
|
||||
static const struct omap_prcm_irq omap4_prcm_irqs[] = {
|
||||
OMAP_PRCM_IRQ("wkup", 0, 0),
|
||||
OMAP_PRCM_IRQ("io", 9, 1),
|
||||
};
|
||||
|
||||
static struct omap_prcm_irq_setup omap4_prcm_irq_setup = {
|
||||
.ack = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
|
||||
.mask = OMAP4_PRM_IRQENABLE_MPU_OFFSET,
|
||||
.nr_regs = 2,
|
||||
.irqs = omap4_prcm_irqs,
|
||||
.nr_irqs = ARRAY_SIZE(omap4_prcm_irqs),
|
||||
.irq = OMAP44XX_IRQ_PRCM,
|
||||
.read_pending_irqs = &omap44xx_prm_read_pending_irqs,
|
||||
.ocp_barrier = &omap44xx_prm_ocp_barrier,
|
||||
.save_and_clear_irqen = &omap44xx_prm_save_and_clear_irqen,
|
||||
.restore_irqen = &omap44xx_prm_restore_irqen,
|
||||
};
|
||||
|
||||
/* PRM low-level functions */
|
||||
|
||||
/* Read a register in a CM/PRM instance in the PRM module */
|
||||
|
@ -121,3 +139,101 @@ u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset)
|
|||
OMAP4430_PRM_DEVICE_INST,
|
||||
offset);
|
||||
}
|
||||
|
||||
static inline u32 _read_pending_irq_reg(u16 irqen_offs, u16 irqst_offs)
|
||||
{
|
||||
u32 mask, st;
|
||||
|
||||
/* XXX read mask from RAM? */
|
||||
mask = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, irqen_offs);
|
||||
st = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, irqst_offs);
|
||||
|
||||
return mask & st;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap44xx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events
|
||||
* @events: ptr to two consecutive u32s, preallocated by caller
|
||||
*
|
||||
* Read PRM_IRQSTATUS_MPU* bits, AND'ed with the currently-enabled PRM
|
||||
* MPU IRQs, and store the result into the two u32s pointed to by @events.
|
||||
* No return value.
|
||||
*/
|
||||
void omap44xx_prm_read_pending_irqs(unsigned long *events)
|
||||
{
|
||||
events[0] = _read_pending_irq_reg(OMAP4_PRM_IRQENABLE_MPU_OFFSET,
|
||||
OMAP4_PRM_IRQSTATUS_MPU_OFFSET);
|
||||
|
||||
events[1] = _read_pending_irq_reg(OMAP4_PRM_IRQENABLE_MPU_2_OFFSET,
|
||||
OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap44xx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete
|
||||
*
|
||||
* Force any buffered writes to the PRM IP block to complete. Needed
|
||||
* by the PRM IRQ handler, which reads and writes directly to the IP
|
||||
* block, to avoid race conditions after acknowledging or clearing IRQ
|
||||
* bits. No return value.
|
||||
*/
|
||||
void omap44xx_prm_ocp_barrier(void)
|
||||
{
|
||||
omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_REVISION_PRM_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap44xx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU* regs
|
||||
* @saved_mask: ptr to a u32 array to save IRQENABLE bits
|
||||
*
|
||||
* Save the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers to
|
||||
* @saved_mask. @saved_mask must be allocated by the caller.
|
||||
* Intended to be used in the PRM interrupt handler suspend callback.
|
||||
* The OCP barrier is needed to ensure the write to disable PRM
|
||||
* interrupts reaches the PRM before returning; otherwise, spurious
|
||||
* interrupts might occur. No return value.
|
||||
*/
|
||||
void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask)
|
||||
{
|
||||
saved_mask[0] =
|
||||
omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_IRQSTATUS_MPU_OFFSET);
|
||||
saved_mask[1] =
|
||||
omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET);
|
||||
|
||||
omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_IRQENABLE_MPU_OFFSET);
|
||||
omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_IRQENABLE_MPU_2_OFFSET);
|
||||
|
||||
/* OCP barrier */
|
||||
omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_REVISION_PRM_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap44xx_prm_restore_irqen - set PRM_IRQENABLE_MPU* registers from args
|
||||
* @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously
|
||||
*
|
||||
* Restore the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers from
|
||||
* @saved_mask. Intended to be used in the PRM interrupt handler resume
|
||||
* callback to restore values saved by omap44xx_prm_save_and_clear_irqen().
|
||||
* No OCP barrier should be needed here; any pending PRM interrupts will fire
|
||||
* once the writes reach the PRM. No return value.
|
||||
*/
|
||||
void omap44xx_prm_restore_irqen(u32 *saved_mask)
|
||||
{
|
||||
omap4_prm_write_inst_reg(saved_mask[0], OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_IRQENABLE_MPU_OFFSET);
|
||||
omap4_prm_write_inst_reg(saved_mask[1], OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_IRQENABLE_MPU_2_OFFSET);
|
||||
}
|
||||
|
||||
static int __init omap4xxx_prcm_init(void)
|
||||
{
|
||||
if (cpu_is_omap44xx())
|
||||
return omap_prcm_register_chain_handler(&omap4_prcm_irq_setup);
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(omap4xxx_prcm_init);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OMAP44xx PRM instance offset macros
|
||||
*
|
||||
* Copyright (C) 2009-2010 Texas Instruments, Inc.
|
||||
* Copyright (C) 2009-2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2009-2010 Nokia Corporation
|
||||
*
|
||||
* Paul Walmsley (paul@pwsan.com)
|
||||
|
@ -763,6 +763,12 @@ extern u32 omap4_prm_vcvp_read(u8 offset);
|
|||
extern void omap4_prm_vcvp_write(u32 val, u8 offset);
|
||||
extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset);
|
||||
|
||||
/* PRM interrupt-related functions */
|
||||
extern void omap44xx_prm_read_pending_irqs(unsigned long *events);
|
||||
extern void omap44xx_prm_ocp_barrier(void);
|
||||
extern void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask);
|
||||
extern void omap44xx_prm_restore_irqen(u32 *saved_mask);
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* OMAP2+ common Power & Reset Management (PRM) IP block functions
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Tero Kristo <t-kristo@ti.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* For historical purposes, the API used to configure the PRM
|
||||
* interrupt handler refers to it as the "PRCM interrupt." The
|
||||
* underlying registers are located in the PRM on OMAP3/4.
|
||||
*
|
||||
* XXX This code should eventually be moved to a PRM driver.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <mach/system.h>
|
||||
#include <plat/common.h>
|
||||
#include <plat/prcm.h>
|
||||
#include <plat/irqs.h>
|
||||
|
||||
#include "prm2xxx_3xxx.h"
|
||||
#include "prm44xx.h"
|
||||
|
||||
/*
|
||||
* OMAP_PRCM_MAX_NR_PENDING_REG: maximum number of PRM_IRQ*_MPU regs
|
||||
* XXX this is technically not needed, since
|
||||
* omap_prcm_register_chain_handler() could allocate this based on the
|
||||
* actual amount of memory needed for the SoC
|
||||
*/
|
||||
#define OMAP_PRCM_MAX_NR_PENDING_REG 2
|
||||
|
||||
/*
|
||||
* prcm_irq_chips: an array of all of the "generic IRQ chips" in use
|
||||
* by the PRCM interrupt handler code. There will be one 'chip' per
|
||||
* PRM_{IRQSTATUS,IRQENABLE}_MPU register pair. (So OMAP3 will have
|
||||
* one "chip" and OMAP4 will have two.)
|
||||
*/
|
||||
static struct irq_chip_generic **prcm_irq_chips;
|
||||
|
||||
/*
|
||||
* prcm_irq_setup: the PRCM IRQ parameters for the hardware the code
|
||||
* is currently running on. Defined and passed by initialization code
|
||||
* that calls omap_prcm_register_chain_handler().
|
||||
*/
|
||||
static struct omap_prcm_irq_setup *prcm_irq_setup;
|
||||
|
||||
/* Private functions */
|
||||
|
||||
/*
|
||||
* Move priority events from events to priority_events array
|
||||
*/
|
||||
static void omap_prcm_events_filter_priority(unsigned long *events,
|
||||
unsigned long *priority_events)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < prcm_irq_setup->nr_regs; i++) {
|
||||
priority_events[i] =
|
||||
events[i] & prcm_irq_setup->priority_mask[i];
|
||||
events[i] ^= priority_events[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PRCM Interrupt Handler
|
||||
*
|
||||
* This is a common handler for the OMAP PRCM interrupts. Pending
|
||||
* interrupts are detected by a call to prcm_pending_events and
|
||||
* dispatched accordingly. Clearing of the wakeup events should be
|
||||
* done by the SoC specific individual handlers.
|
||||
*/
|
||||
static void omap_prcm_irq_handler(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
unsigned long pending[OMAP_PRCM_MAX_NR_PENDING_REG];
|
||||
unsigned long priority_pending[OMAP_PRCM_MAX_NR_PENDING_REG];
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
unsigned int virtirq;
|
||||
int nr_irqs = prcm_irq_setup->nr_regs * 32;
|
||||
|
||||
/*
|
||||
* If we are suspended, mask all interrupts from PRCM level,
|
||||
* this does not ack them, and they will be pending until we
|
||||
* re-enable the interrupts, at which point the
|
||||
* omap_prcm_irq_handler will be executed again. The
|
||||
* _save_and_clear_irqen() function must ensure that the PRM
|
||||
* write to disable all IRQs has reached the PRM before
|
||||
* returning, or spurious PRCM interrupts may occur during
|
||||
* suspend.
|
||||
*/
|
||||
if (prcm_irq_setup->suspended) {
|
||||
prcm_irq_setup->save_and_clear_irqen(prcm_irq_setup->saved_mask);
|
||||
prcm_irq_setup->suspend_save_flag = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop until all pending irqs are handled, since
|
||||
* generic_handle_irq() can cause new irqs to come
|
||||
*/
|
||||
while (!prcm_irq_setup->suspended) {
|
||||
prcm_irq_setup->read_pending_irqs(pending);
|
||||
|
||||
/* No bit set, then all IRQs are handled */
|
||||
if (find_first_bit(pending, nr_irqs) >= nr_irqs)
|
||||
break;
|
||||
|
||||
omap_prcm_events_filter_priority(pending, priority_pending);
|
||||
|
||||
/*
|
||||
* Loop on all currently pending irqs so that new irqs
|
||||
* cannot starve previously pending irqs
|
||||
*/
|
||||
|
||||
/* Serve priority events first */
|
||||
for_each_set_bit(virtirq, priority_pending, nr_irqs)
|
||||
generic_handle_irq(prcm_irq_setup->base_irq + virtirq);
|
||||
|
||||
/* Serve normal events next */
|
||||
for_each_set_bit(virtirq, pending, nr_irqs)
|
||||
generic_handle_irq(prcm_irq_setup->base_irq + virtirq);
|
||||
}
|
||||
if (chip->irq_ack)
|
||||
chip->irq_ack(&desc->irq_data);
|
||||
if (chip->irq_eoi)
|
||||
chip->irq_eoi(&desc->irq_data);
|
||||
chip->irq_unmask(&desc->irq_data);
|
||||
|
||||
prcm_irq_setup->ocp_barrier(); /* avoid spurious IRQs */
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
|
||||
/**
|
||||
* omap_prcm_event_to_irq - given a PRCM event name, returns the
|
||||
* corresponding IRQ on which the handler should be registered
|
||||
* @name: name of the PRCM interrupt bit to look up - see struct omap_prcm_irq
|
||||
*
|
||||
* Returns the Linux internal IRQ ID corresponding to @name upon success,
|
||||
* or -ENOENT upon failure.
|
||||
*/
|
||||
int omap_prcm_event_to_irq(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!prcm_irq_setup || !name)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < prcm_irq_setup->nr_irqs; i++)
|
||||
if (!strcmp(prcm_irq_setup->irqs[i].name, name))
|
||||
return prcm_irq_setup->base_irq +
|
||||
prcm_irq_setup->irqs[i].offset;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_prcm_irq_cleanup - reverses memory allocated and other steps
|
||||
* done by omap_prcm_register_chain_handler()
|
||||
*
|
||||
* No return value.
|
||||
*/
|
||||
void omap_prcm_irq_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!prcm_irq_setup) {
|
||||
pr_err("PRCM: IRQ handler not initialized; cannot cleanup\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (prcm_irq_chips) {
|
||||
for (i = 0; i < prcm_irq_setup->nr_regs; i++) {
|
||||
if (prcm_irq_chips[i])
|
||||
irq_remove_generic_chip(prcm_irq_chips[i],
|
||||
0xffffffff, 0, 0);
|
||||
prcm_irq_chips[i] = NULL;
|
||||
}
|
||||
kfree(prcm_irq_chips);
|
||||
prcm_irq_chips = NULL;
|
||||
}
|
||||
|
||||
kfree(prcm_irq_setup->saved_mask);
|
||||
prcm_irq_setup->saved_mask = NULL;
|
||||
|
||||
kfree(prcm_irq_setup->priority_mask);
|
||||
prcm_irq_setup->priority_mask = NULL;
|
||||
|
||||
irq_set_chained_handler(prcm_irq_setup->irq, NULL);
|
||||
|
||||
if (prcm_irq_setup->base_irq > 0)
|
||||
irq_free_descs(prcm_irq_setup->base_irq,
|
||||
prcm_irq_setup->nr_regs * 32);
|
||||
prcm_irq_setup->base_irq = 0;
|
||||
}
|
||||
|
||||
void omap_prcm_irq_prepare(void)
|
||||
{
|
||||
prcm_irq_setup->suspended = true;
|
||||
}
|
||||
|
||||
void omap_prcm_irq_complete(void)
|
||||
{
|
||||
prcm_irq_setup->suspended = false;
|
||||
|
||||
/* If we have not saved the masks, do not attempt to restore */
|
||||
if (!prcm_irq_setup->suspend_save_flag)
|
||||
return;
|
||||
|
||||
prcm_irq_setup->suspend_save_flag = false;
|
||||
|
||||
/*
|
||||
* Re-enable all masked PRCM irq sources, this causes the PRCM
|
||||
* interrupt to fire immediately if the events were masked
|
||||
* previously in the chain handler
|
||||
*/
|
||||
prcm_irq_setup->restore_irqen(prcm_irq_setup->saved_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_prcm_register_chain_handler - initializes the prcm chained interrupt
|
||||
* handler based on provided parameters
|
||||
* @irq_setup: hardware data about the underlying PRM/PRCM
|
||||
*
|
||||
* Set up the PRCM chained interrupt handler on the PRCM IRQ. Sets up
|
||||
* one generic IRQ chip per PRM interrupt status/enable register pair.
|
||||
* Returns 0 upon success, -EINVAL if called twice or if invalid
|
||||
* arguments are passed, or -ENOMEM on any other error.
|
||||
*/
|
||||
int omap_prcm_register_chain_handler(struct omap_prcm_irq_setup *irq_setup)
|
||||
{
|
||||
int nr_regs = irq_setup->nr_regs;
|
||||
u32 mask[OMAP_PRCM_MAX_NR_PENDING_REG];
|
||||
int offset, i;
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_chip_type *ct;
|
||||
|
||||
if (!irq_setup)
|
||||
return -EINVAL;
|
||||
|
||||
if (prcm_irq_setup) {
|
||||
pr_err("PRCM: already initialized; won't reinitialize\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nr_regs > OMAP_PRCM_MAX_NR_PENDING_REG) {
|
||||
pr_err("PRCM: nr_regs too large\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prcm_irq_setup = irq_setup;
|
||||
|
||||
prcm_irq_chips = kzalloc(sizeof(void *) * nr_regs, GFP_KERNEL);
|
||||
prcm_irq_setup->saved_mask = kzalloc(sizeof(u32) * nr_regs, GFP_KERNEL);
|
||||
prcm_irq_setup->priority_mask = kzalloc(sizeof(u32) * nr_regs,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!prcm_irq_chips || !prcm_irq_setup->saved_mask ||
|
||||
!prcm_irq_setup->priority_mask) {
|
||||
pr_err("PRCM: kzalloc failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(mask, 0, sizeof(mask));
|
||||
|
||||
for (i = 0; i < irq_setup->nr_irqs; i++) {
|
||||
offset = irq_setup->irqs[i].offset;
|
||||
mask[offset >> 5] |= 1 << (offset & 0x1f);
|
||||
if (irq_setup->irqs[i].priority)
|
||||
irq_setup->priority_mask[offset >> 5] |=
|
||||
1 << (offset & 0x1f);
|
||||
}
|
||||
|
||||
irq_set_chained_handler(irq_setup->irq, omap_prcm_irq_handler);
|
||||
|
||||
irq_setup->base_irq = irq_alloc_descs(-1, 0, irq_setup->nr_regs * 32,
|
||||
0);
|
||||
|
||||
if (irq_setup->base_irq < 0) {
|
||||
pr_err("PRCM: failed to allocate irq descs: %d\n",
|
||||
irq_setup->base_irq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i <= irq_setup->nr_regs; i++) {
|
||||
gc = irq_alloc_generic_chip("PRCM", 1,
|
||||
irq_setup->base_irq + i * 32, prm_base,
|
||||
handle_level_irq);
|
||||
|
||||
if (!gc) {
|
||||
pr_err("PRCM: failed to allocate generic chip\n");
|
||||
goto err;
|
||||
}
|
||||
ct = gc->chip_types;
|
||||
ct->chip.irq_ack = irq_gc_ack_set_bit;
|
||||
ct->chip.irq_mask = irq_gc_mask_clr_bit;
|
||||
ct->chip.irq_unmask = irq_gc_mask_set_bit;
|
||||
|
||||
ct->regs.ack = irq_setup->ack + i * 4;
|
||||
ct->regs.mask = irq_setup->mask + i * 4;
|
||||
|
||||
irq_setup_generic_chip(gc, mask[i], 0, IRQ_NOREQUEST, 0);
|
||||
prcm_irq_chips[i] = gc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
omap_prcm_irq_cleanup();
|
||||
return -ENOMEM;
|
||||
}
|
|
@ -97,6 +97,7 @@ struct omap_hwmod_mux_info {
|
|||
struct omap_device_pad *pads;
|
||||
int nr_pads_dynamic;
|
||||
struct omap_device_pad **pads_dynamic;
|
||||
int *irqs;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
|
@ -607,6 +608,8 @@ int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
|
|||
|
||||
int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
|
||||
|
||||
int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx);
|
||||
|
||||
/*
|
||||
* Chip variant-specific hwmod init routines - XXX should be converted
|
||||
* to use initcalls once the initial boot ordering is straightened out
|
||||
|
|
Загрузка…
Ссылка в новой задаче