Skip to content

Commit

Permalink
Merge branch 'prcm' of git://git.kernel.org/pub/scm/linux/kernel/git/…
Browse files Browse the repository at this point in the history
…tmlind/linux-omap into omap/prcm
  • Loading branch information
Olof Johansson committed Dec 20, 2011
2 parents eb3e1d9 + 9d297f5 commit fe5d3e8
Show file tree
Hide file tree
Showing 11 changed files with 884 additions and 79 deletions.
3 changes: 2 additions & 1 deletion arch/arm/mach-omap2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
89 changes: 87 additions & 2 deletions arch/arm/mach-omap2/mux.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@
#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>

#include <plat/omap_hwmod.h>

#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
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
125 changes: 124 additions & 1 deletion arch/arm/mach-omap2/omap_hwmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 *
Expand Down Expand Up @@ -1441,6 +1487,25 @@ static int _enable(struct omap_hwmod *oh)

pr_debug("omap_hwmod: %s: enabling\n", oh->name);

/*
* hwmods with HWMOD_INIT_NO_IDLE flag set are left
* in enabled state at init.
* Now that someone is really trying to enable them,
* just ensure that the hwmod mux is set.
*/
if (oh->_int_flags & _HWMOD_SKIP_ENABLE) {
/*
* If the caller has mux data populated, do the mux'ing
* which wouldn't have been done as part of the _enable()
* done during setup.
*/
if (oh->mux)
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);

oh->_int_flags &= ~_HWMOD_SKIP_ENABLE;
return 0;
}

if (oh->_state != _HWMOD_STATE_INITIALIZED &&
oh->_state != _HWMOD_STATE_IDLE &&
oh->_state != _HWMOD_STATE_DISABLED) {
Expand Down Expand Up @@ -1744,8 +1809,10 @@ static int _setup(struct omap_hwmod *oh, void *data)
* it should be set by the core code as a runtime flag during startup
*/
if ((oh->flags & HWMOD_INIT_NO_IDLE) &&
(postsetup_state == _HWMOD_STATE_IDLE))
(postsetup_state == _HWMOD_STATE_IDLE)) {
oh->_int_flags |= _HWMOD_SKIP_ENABLE;
postsetup_state = _HWMOD_STATE_ENABLED;
}

if (postsetup_state == _HWMOD_STATE_IDLE)
_idle(oh);
Expand Down Expand Up @@ -2416,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;
Expand Down Expand Up @@ -2446,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;
Expand Down Expand Up @@ -2662,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;
}
Loading

0 comments on commit fe5d3e8

Please sign in to comment.