Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
9d297f5e
Commit
9d297f5e
authored
Dec 16, 2011
by
Tony Lindgren
Browse files
Options
Browse Files
Download
Plain Diff
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
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
859 additions
and
78 deletions
+859
-78
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/Makefile
+2
-1
arch/arm/mach-omap2/mux.c
arch/arm/mach-omap2/mux.c
+87
-2
arch/arm/mach-omap2/omap_hwmod.c
arch/arm/mach-omap2/omap_hwmod.c
+102
-0
arch/arm/mach-omap2/pm34xx.c
arch/arm/mach-omap2/pm34xx.c
+44
-71
arch/arm/mach-omap2/prcm-common.h
arch/arm/mach-omap2/prcm-common.h
+74
-1
arch/arm/mach-omap2/prm2xxx_3xxx.c
arch/arm/mach-omap2/prm2xxx_3xxx.c
+96
-1
arch/arm/mach-omap2/prm2xxx_3xxx.h
arch/arm/mach-omap2/prm2xxx_3xxx.h
+8
-1
arch/arm/mach-omap2/prm44xx.c
arch/arm/mach-omap2/prm44xx.c
+116
-0
arch/arm/mach-omap2/prm44xx.h
arch/arm/mach-omap2/prm44xx.h
+7
-1
arch/arm/mach-omap2/prm_common.c
arch/arm/mach-omap2/prm_common.c
+320
-0
arch/arm/plat-omap/include/plat/omap_hwmod.h
arch/arm/plat-omap/include/plat/omap_hwmod.h
+3
-0
No files found.
arch/arm/mach-omap2/Makefile
View file @
9d297f5e
...
@@ -81,6 +81,7 @@ endif
...
@@ -81,6 +81,7 @@ endif
endif
endif
# PRCM
# PRCM
obj-y
+=
prm_common.o
obj-$(CONFIG_ARCH_OMAP2)
+=
prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.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
\
obj-$(CONFIG_ARCH_OMAP3)
+=
prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o
\
vc3xxx_data.o vp3xxx_data.o
vc3xxx_data.o vp3xxx_data.o
...
@@ -90,7 +91,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.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
\
obj-$(CONFIG_ARCH_OMAP4)
+=
prcm.o cm2xxx_3xxx.o cminst44xx.o
\
cm44xx.o prcm_mpu44xx.o
\
cm44xx.o prcm_mpu44xx.o
\
prminst44xx.o vc44xx_data.o
\
prminst44xx.o vc44xx_data.o
\
vp44xx_data.o
vp44xx_data.o
prm44xx.o
# OMAP voltage domains
# OMAP voltage domains
voltagedomain-common
:=
voltage.o vc.o vp.o
voltagedomain-common
:=
voltage.o vc.o vp.o
...
...
arch/arm/mach-omap2/mux.c
View file @
9d297f5e
...
@@ -32,6 +32,8 @@
...
@@ -32,6 +32,8 @@
#include <linux/debugfs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/system.h>
...
@@ -39,6 +41,7 @@
...
@@ -39,6 +41,7 @@
#include "control.h"
#include "control.h"
#include "mux.h"
#include "mux.h"
#include "prm.h"
#define OMAP_MUX_BASE_OFFSET 0x30
/* Offset from CTRL_BASE */
#define OMAP_MUX_BASE_OFFSET 0x30
/* Offset from CTRL_BASE */
#define OMAP_MUX_BASE_SZ 0x5ca
#define OMAP_MUX_BASE_SZ 0x5ca
...
@@ -306,7 +309,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
...
@@ -306,7 +309,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
pad
->
idle
=
bpad
->
idle
;
pad
->
idle
=
bpad
->
idle
;
pad
->
off
=
bpad
->
off
;
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
++
;
nr_pads_dynamic
++
;
pr_debug
(
"%s: Initialized %s
\n
"
,
__func__
,
pad
->
name
);
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)
...
@@ -331,7 +335,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
for
(
i
=
0
;
i
<
hmux
->
nr_pads
;
i
++
)
{
for
(
i
=
0
;
i
<
hmux
->
nr_pads
;
i
++
)
{
struct
omap_device_pad
*
pad
=
&
hmux
->
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
"
,
pr_debug
(
"%s: pad %s tagged dynamic
\n
"
,
__func__
,
pad
->
name
);
__func__
,
pad
->
name
);
hmux
->
pads_dynamic
[
nr_pads_dynamic
]
=
pad
;
hmux
->
pads_dynamic
[
nr_pads_dynamic
]
=
pad
;
...
@@ -351,6 +356,78 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
...
@@ -351,6 +356,78 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads)
return
NULL
;
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 */
/* Assumes the calling function takes care of locking */
void
omap_hwmod_mux
(
struct
omap_hwmod_mux_info
*
hmux
,
u8
state
)
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)
...
@@ -715,6 +792,7 @@ static void __init omap_mux_free_names(struct omap_mux *m)
static
int
__init
omap_mux_late_init
(
void
)
static
int
__init
omap_mux_late_init
(
void
)
{
{
struct
omap_mux_partition
*
partition
;
struct
omap_mux_partition
*
partition
;
int
ret
;
list_for_each_entry
(
partition
,
&
mux_partitions
,
node
)
{
list_for_each_entry
(
partition
,
&
mux_partitions
,
node
)
{
struct
omap_mux_entry
*
e
,
*
tmp
;
struct
omap_mux_entry
*
e
,
*
tmp
;
...
@@ -735,6 +813,13 @@ static int __init omap_mux_late_init(void)
...
@@ -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
();
omap_mux_dbg_init
();
return
0
;
return
0
;
...
...
arch/arm/mach-omap2/omap_hwmod.c
View file @
9d297f5e
...
@@ -136,6 +136,7 @@
...
@@ -136,6 +136,7 @@
#include <linux/list.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include "common.h"
#include "common.h"
#include <plat/cpu.h>
#include <plat/cpu.h>
...
@@ -380,6 +381,51 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle,
...
@@ -380,6 +381,51 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle,
return
0
;
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
* _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware
* @oh: struct omap_hwmod *
* @oh: struct omap_hwmod *
...
@@ -2437,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
...
@@ -2437,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
v
=
oh
->
_sysc_cache
;
v
=
oh
->
_sysc_cache
;
_enable_wakeup
(
oh
,
&
v
);
_enable_wakeup
(
oh
,
&
v
);
_write_sysconfig
(
v
,
oh
);
_write_sysconfig
(
v
,
oh
);
_set_idle_ioring_wakeup
(
oh
,
true
);
spin_unlock_irqrestore
(
&
oh
->
_lock
,
flags
);
spin_unlock_irqrestore
(
&
oh
->
_lock
,
flags
);
return
0
;
return
0
;
...
@@ -2467,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh)
...
@@ -2467,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh)
v
=
oh
->
_sysc_cache
;
v
=
oh
->
_sysc_cache
;
_disable_wakeup
(
oh
,
&
v
);
_disable_wakeup
(
oh
,
&
v
);
_write_sysconfig
(
v
,
oh
);
_write_sysconfig
(
v
,
oh
);
_set_idle_ioring_wakeup
(
oh
,
false
);
spin_unlock_irqrestore
(
&
oh
->
_lock
,
flags
);
spin_unlock_irqrestore
(
&
oh
->
_lock
,
flags
);
return
0
;
return
0
;
...
@@ -2683,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh)
...
@@ -2683,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh)
return
0
;
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
;
}
arch/arm/mach-omap2/pm34xx.c
View file @
9d297f5e
...
@@ -195,7 +195,7 @@ static void omap3_save_secure_ram_context(void)
...
@@ -195,7 +195,7 @@ static void omap3_save_secure_ram_context(void)
* that any peripheral wake-up events occurring while attempting to
* that any peripheral wake-up events occurring while attempting to
* clear the PM_WKST_x are detected and cleared.
* 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
;
u32
wkst
,
fclk
,
iclk
,
clken
;
u16
wkst_off
=
(
regs
==
3
)
?
OMAP3430ES2_PM_WKST3
:
PM_WKST1
;
u16
wkst_off
=
(
regs
==
3
)
?
OMAP3430ES2_PM_WKST3
:
PM_WKST1
;
...
@@ -207,6 +207,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
...
@@ -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
,
wkst_off
);
wkst
&=
omap2_prm_read_mod_reg
(
module
,
grpsel_off
);
wkst
&=
omap2_prm_read_mod_reg
(
module
,
grpsel_off
);
wkst
&=
~
ignore_bits
;
if
(
wkst
)
{
if
(
wkst
)
{
iclk
=
omap2_cm_read_mod_reg
(
module
,
iclk_off
);
iclk
=
omap2_cm_read_mod_reg
(
module
,
iclk_off
);
fclk
=
omap2_cm_read_mod_reg
(
module
,
fclk_off
);
fclk
=
omap2_cm_read_mod_reg
(
module
,
fclk_off
);
...
@@ -222,6 +223,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
...
@@ -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_cm_set_mod_reg_bits
(
clken
,
module
,
fclk_off
);
omap2_prm_write_mod_reg
(
wkst
,
module
,
wkst_off
);
omap2_prm_write_mod_reg
(
wkst
,
module
,
wkst_off
);
wkst
=
omap2_prm_read_mod_reg
(
module
,
wkst_off
);
wkst
=
omap2_prm_read_mod_reg
(
module
,
wkst_off
);
wkst
&=
~
ignore_bits
;
c
++
;
c
++
;
}
}
omap2_cm_write_mod_reg
(
iclk
,
module
,
iclk_off
);
omap2_cm_write_mod_reg
(
iclk
,
module
,
iclk_off
);
...
@@ -231,76 +233,35 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
...
@@ -231,76 +233,35 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
return
c
;
return
c
;
}
}
static
i
nt
_prcm_int_handle_wakeup
(
voi
d
)
static
i
rqreturn_t
_prcm_int_handle_io
(
int
irq
,
void
*
unuse
d
)
{
{
int
c
;
int
c
;
c
=
prcm_clear_mod_irqs
(
WKUP_MOD
,
1
);
c
=
prcm_clear_mod_irqs
(
WKUP_MOD
,
1
,
c
+=
prcm_clear_mod_irqs
(
CORE_MOD
,
1
);
~
(
OMAP3430_ST_IO_MASK
|
OMAP3430_ST_IO_CHAIN_MASK
));
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
);
}
return
c
;
return
c
?
IRQ_HANDLED
:
IRQ_NONE
;
}
}
/*
static
irqreturn_t
_prcm_int_handle_wakeup
(
int
irq
,
void
*
unused
)
* 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
)
{
{
u32
irqenable_mpu
,
irqstatus_mpu
;
int
c
;
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
();
/*
/*
* Is the MPU PRCM interrupt handler racing with the
* Clear all except ST_IO and ST_IO_CHAIN for wkup module,
* IVA2 PRCM interrupt handler ?
* 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 "
c
=
prcm_clear_mod_irqs
(
WKUP_MOD
,
1
,
"but no wakeup sources are marked
\n
"
);
OMAP3430_ST_IO_MASK
|
OMAP3430_ST_IO_CHAIN_MASK
);
}
else
{
c
+=
prcm_clear_mod_irqs
(
CORE_MOD
,
1
,
0
);
/* XXX we need to expand our PRCM interrupt handler */
c
+=
prcm_clear_mod_irqs
(
OMAP3430_PER_MOD
,
1
,
0
);
WARN
(
1
,
"prcm: WARNING: PRCM interrupt received, but "
if
(
omap_rev
()
>
OMAP3430_REV_ES1_0
)
{
"no code to handle it (%08x)
\n
"
,
irqstatus_mpu
);
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
,
return
c
?
IRQ_HANDLED
:
IRQ_NONE
;
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
;
}
}
static
void
omap34xx_save_context
(
u32
*
save
)
static
void
omap34xx_save_context
(
u32
*
save
)
...
@@ -581,6 +542,7 @@ static int omap3_pm_begin(suspend_state_t state)
...
@@ -581,6 +542,7 @@ static int omap3_pm_begin(suspend_state_t state)
disable_hlt
();
disable_hlt
();
suspend_state
=
state
;
suspend_state
=
state
;
omap_uart_enable_irqs
(
0
);
omap_uart_enable_irqs
(
0
);
omap_prcm_irq_prepare
();
return
0
;
return
0
;
}
}
...
@@ -592,10 +554,16 @@ static void omap3_pm_end(void)
...
@@ -592,10 +554,16 @@ static void omap3_pm_end(void)
return
;
return
;
}
}
static
void
omap3_pm_finish
(
void
)
{
omap_prcm_irq_complete
();
}
static
const
struct
platform_suspend_ops
omap_pm_ops
=
{
static
const
struct
platform_suspend_ops
omap_pm_ops
=
{
.
begin
=
omap3_pm_begin
,
.
begin
=
omap3_pm_begin
,
.
end
=
omap3_pm_end
,
.
end
=
omap3_pm_end
,
.
enter
=
omap3_pm_enter
,
.
enter
=
omap3_pm_enter
,
.
finish
=
omap3_pm_finish
,
.
valid
=
suspend_valid_only_mem
,
.
valid
=
suspend_valid_only_mem
,
};
};
#endif
/* CONFIG_SUSPEND */
#endif
/* CONFIG_SUSPEND */
...
@@ -701,10 +669,6 @@ static void __init prcm_setup_regs(void)
...
@@ -701,10 +669,6 @@ static void __init prcm_setup_regs(void)
OMAP3430_GRPSEL_GPT1_MASK
|
OMAP3430_GRPSEL_GPT1_MASK
|
OMAP3430_GRPSEL_GPT12_MASK
,
OMAP3430_GRPSEL_GPT12_MASK
,
WKUP_MOD
,
OMAP3430_PM_MPUGRPSEL
);
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 */
/* Enable PM_WKEN to support DSS LPR */
omap2_prm_write_mod_reg
(
OMAP3430_PM_WKEN_DSS_EN_DSS_MASK
,
omap2_prm_write_mod_reg
(
OMAP3430_PM_WKEN_DSS_EN_DSS_MASK
,
...
@@ -881,12 +845,21 @@ static int __init omap3_pm_init(void)
...
@@ -881,12 +845,21 @@ static int __init omap3_pm_init(void)
* supervised mode for powerdomains */
* supervised mode for powerdomains */
prcm_setup_regs
();
prcm_setup_regs
();
ret
=
request_irq
(
INT_34XX_PRCM_MPU_IRQ
,
ret
=
request_irq
(
omap_prcm_event_to_irq
(
"wkup"
),
(
irq_handler_t
)
prcm_interrupt_handler
,
_prcm_int_handle_wakeup
,
IRQF_NO_SUSPEND
,
"pm_wkup"
,
NULL
);
IRQF_DISABLED
,
"prcm"
,
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
)
{
if
(
ret
)
{
printk
(
KERN_ERR
"request_irq failed to register for 0x%x
\n
"
,
pr_err
(
"pm: Failed to request pm_io irq
\n
"
);
INT_34XX_PRCM_MPU_IRQ
);
goto
err1
;
goto
err1
;
}
}
...
...
arch/arm/mach-omap2/prcm-common.h
View file @
9d297f5e
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
/*
/*
* OMAP2/3 PRCM base and module definitions
* 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
* Copyright (C) 2007-2009 Nokia Corporation
*
*
* Written by Paul Walmsley
* Written by Paul Walmsley
...
@@ -408,6 +408,79 @@
...
@@ -408,6 +408,79 @@
extern
void
__iomem
*
prm_base
;
extern
void
__iomem
*
prm_base
;
extern
void
__iomem
*
cm_base
;
extern
void
__iomem
*
cm_base
;
extern
void
__iomem
*
cm2_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
#endif
#endif
...
...
arch/arm/mach-omap2/prm2xxx_3xxx.c
View file @
9d297f5e
/*
/*
* OMAP2/3 PRM module functions
* OMAP2/3 PRM module functions
*
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2010
-2011
Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
* Copyright (C) 2010 Nokia Corporation
* Benoît Cousson
* Benoît Cousson
* Paul Walmsley
* Paul Walmsley
...
@@ -27,6 +27,24 @@
...
@@ -27,6 +27,24 @@
#include "prm-regbits-24xx.h"
#include "prm-regbits-24xx.h"
#include "prm-regbits-34xx.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
)
u32
omap2_prm_read_mod_reg
(
s16
module
,
u16
idx
)
{
{
return
__raw_readl
(
prm_base
+
module
+
idx
);
return
__raw_readl
(
prm_base
+
module
+
idx
);
...
@@ -212,3 +230,80 @@ u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset)
...
@@ -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
);
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
);
arch/arm/mach-omap2/prm2xxx_3xxx.h
View file @
9d297f5e
/*
/*
* OMAP2/3 Power/Reset Management (PRM) register definitions
* 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
* Copyright (C) 2008-2010 Nokia Corporation
* Paul Walmsley
* Paul Walmsley
*
*
...
@@ -314,6 +314,13 @@ void omap3_prm_vp_clear_txdone(u8 vp_id);
...
@@ -314,6 +314,13 @@ void omap3_prm_vp_clear_txdone(u8 vp_id);
extern
u32
omap3_prm_vcvp_read
(
u8
offset
);
extern
u32
omap3_prm_vcvp_read
(
u8
offset
);
extern
void
omap3_prm_vcvp_write
(
u32
val
,
u8
offset
);
extern
void
omap3_prm_vcvp_write
(
u32
val
,
u8
offset
);
extern
u32
omap3_prm_vcvp_rmw
(
u32
mask
,
u32
bits
,
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
/* CONFIG_ARCH_OMAP4 */
#endif
#endif
...
...
arch/arm/mach-omap2/prm44xx.c
View file @
9d297f5e
...
@@ -27,6 +27,24 @@
...
@@ -27,6 +27,24 @@
#include "prcm44xx.h"
#include "prcm44xx.h"
#include "prminst44xx.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 */
/* PRM low-level functions */
/* Read a register in a CM/PRM instance in the PRM module */
/* 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)
...
@@ -121,3 +139,101 @@ u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset)
OMAP4430_PRM_DEVICE_INST
,
OMAP4430_PRM_DEVICE_INST
,
offset
);
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
);
arch/arm/mach-omap2/prm44xx.h
View file @
9d297f5e
/*
/*
* OMAP44xx PRM instance offset macros
* OMAP44xx PRM instance offset macros
*
*
* Copyright (C) 2009-201
0
Texas Instruments, Inc.
* Copyright (C) 2009-201
1
Texas Instruments, Inc.
* Copyright (C) 2009-2010 Nokia Corporation
* Copyright (C) 2009-2010 Nokia Corporation
*
*
* Paul Walmsley (paul@pwsan.com)
* Paul Walmsley (paul@pwsan.com)
...
@@ -763,6 +763,12 @@ extern u32 omap4_prm_vcvp_read(u8 offset);
...
@@ -763,6 +763,12 @@ extern u32 omap4_prm_vcvp_read(u8 offset);
extern
void
omap4_prm_vcvp_write
(
u32
val
,
u8
offset
);
extern
void
omap4_prm_vcvp_write
(
u32
val
,
u8
offset
);
extern
u32
omap4_prm_vcvp_rmw
(
u32
mask
,
u32
bits
,
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
#endif
#endif
arch/arm/mach-omap2/prm_common.c
0 → 100644
View file @
9d297f5e
/*
* 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
;
}
arch/arm/plat-omap/include/plat/omap_hwmod.h
View file @
9d297f5e
...
@@ -97,6 +97,7 @@ struct omap_hwmod_mux_info {
...
@@ -97,6 +97,7 @@ struct omap_hwmod_mux_info {
struct
omap_device_pad
*
pads
;
struct
omap_device_pad
*
pads
;
int
nr_pads_dynamic
;
int
nr_pads_dynamic
;
struct
omap_device_pad
**
pads_dynamic
;
struct
omap_device_pad
**
pads_dynamic
;
int
*
irqs
;
bool
enabled
;
bool
enabled
;
};
};
...
@@ -607,6 +608,8 @@ int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
...
@@ -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_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
* Chip variant-specific hwmod init routines - XXX should be converted
* to use initcalls once the initial boot ordering is straightened out
* to use initcalls once the initial boot ordering is straightened out
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment