MIPS: Alchemy: add sysdev for IRQ PM.
Use a sysdev to implement PM methods for the Au1000 interrupt controllers. Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com> To: Linux-MIPS <linux-mips@linux-mips.org> Patchwork: http://patchwork.linux-mips.org/patch/1114/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
committed by
Ralf Baechle
parent
7b5fcd694d
commit
0f0d85bcc3
@@ -29,6 +29,8 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/sysdev.h>
|
||||||
|
|
||||||
#include <asm/irq_cpu.h>
|
#include <asm/irq_cpu.h>
|
||||||
#include <asm/mipsregs.h>
|
#include <asm/mipsregs.h>
|
||||||
@@ -216,90 +218,6 @@ struct au1xxx_irqmap au1200_irqmap[] __initdata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save/restore the interrupt controller state.
|
|
||||||
* Called from the save/restore core registers as part of the
|
|
||||||
* au_sleep function in power.c.....maybe I should just pm_register()
|
|
||||||
* them instead?
|
|
||||||
*/
|
|
||||||
static unsigned int sleep_intctl_config0[2];
|
|
||||||
static unsigned int sleep_intctl_config1[2];
|
|
||||||
static unsigned int sleep_intctl_config2[2];
|
|
||||||
static unsigned int sleep_intctl_src[2];
|
|
||||||
static unsigned int sleep_intctl_assign[2];
|
|
||||||
static unsigned int sleep_intctl_wake[2];
|
|
||||||
static unsigned int sleep_intctl_mask[2];
|
|
||||||
|
|
||||||
void save_au1xxx_intctl(void)
|
|
||||||
{
|
|
||||||
sleep_intctl_config0[0] = au_readl(IC0_CFG0RD);
|
|
||||||
sleep_intctl_config1[0] = au_readl(IC0_CFG1RD);
|
|
||||||
sleep_intctl_config2[0] = au_readl(IC0_CFG2RD);
|
|
||||||
sleep_intctl_src[0] = au_readl(IC0_SRCRD);
|
|
||||||
sleep_intctl_assign[0] = au_readl(IC0_ASSIGNRD);
|
|
||||||
sleep_intctl_wake[0] = au_readl(IC0_WAKERD);
|
|
||||||
sleep_intctl_mask[0] = au_readl(IC0_MASKRD);
|
|
||||||
|
|
||||||
sleep_intctl_config0[1] = au_readl(IC1_CFG0RD);
|
|
||||||
sleep_intctl_config1[1] = au_readl(IC1_CFG1RD);
|
|
||||||
sleep_intctl_config2[1] = au_readl(IC1_CFG2RD);
|
|
||||||
sleep_intctl_src[1] = au_readl(IC1_SRCRD);
|
|
||||||
sleep_intctl_assign[1] = au_readl(IC1_ASSIGNRD);
|
|
||||||
sleep_intctl_wake[1] = au_readl(IC1_WAKERD);
|
|
||||||
sleep_intctl_mask[1] = au_readl(IC1_MASKRD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For most restore operations, we clear the entire register and
|
|
||||||
* then set the bits we found during the save.
|
|
||||||
*/
|
|
||||||
void restore_au1xxx_intctl(void)
|
|
||||||
{
|
|
||||||
au_writel(0xffffffff, IC0_MASKCLR); au_sync();
|
|
||||||
|
|
||||||
au_writel(0xffffffff, IC0_CFG0CLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_config0[0], IC0_CFG0SET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_CFG1CLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_config1[0], IC0_CFG1SET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_CFG2CLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_config2[0], IC0_CFG2SET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_SRCCLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_src[0], IC0_SRCSET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_ASSIGNCLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_assign[0], IC0_ASSIGNSET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_WAKECLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_wake[0], IC0_WAKESET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_RISINGCLR); au_sync();
|
|
||||||
au_writel(0xffffffff, IC0_FALLINGCLR); au_sync();
|
|
||||||
au_writel(0x00000000, IC0_TESTBIT); au_sync();
|
|
||||||
|
|
||||||
au_writel(0xffffffff, IC1_MASKCLR); au_sync();
|
|
||||||
|
|
||||||
au_writel(0xffffffff, IC1_CFG0CLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_config0[1], IC1_CFG0SET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_CFG1CLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_config1[1], IC1_CFG1SET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_CFG2CLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_config2[1], IC1_CFG2SET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_SRCCLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_src[1], IC1_SRCSET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_ASSIGNCLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_assign[1], IC1_ASSIGNSET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_WAKECLR); au_sync();
|
|
||||||
au_writel(sleep_intctl_wake[1], IC1_WAKESET); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_RISINGCLR); au_sync();
|
|
||||||
au_writel(0xffffffff, IC1_FALLINGCLR); au_sync();
|
|
||||||
au_writel(0x00000000, IC1_TESTBIT); au_sync();
|
|
||||||
|
|
||||||
au_writel(sleep_intctl_mask[1], IC1_MASKSET); au_sync();
|
|
||||||
|
|
||||||
au_writel(sleep_intctl_mask[0], IC0_MASKSET); au_sync();
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_PM */
|
|
||||||
|
|
||||||
|
|
||||||
static void au1x_ic0_unmask(unsigned int irq_nr)
|
static void au1x_ic0_unmask(unsigned int irq_nr)
|
||||||
{
|
{
|
||||||
unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
|
unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
|
||||||
@@ -635,3 +553,91 @@ void __init arch_init_irq(void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct alchemy_ic_sysdev {
|
||||||
|
struct sys_device sysdev;
|
||||||
|
void __iomem *base;
|
||||||
|
unsigned long pmdata[7];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int alchemy_ic_suspend(struct sys_device *dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct alchemy_ic_sysdev *icdev =
|
||||||
|
container_of(dev, struct alchemy_ic_sysdev, sysdev);
|
||||||
|
|
||||||
|
icdev->pmdata[0] = __raw_readl(icdev->base + IC_CFG0RD);
|
||||||
|
icdev->pmdata[1] = __raw_readl(icdev->base + IC_CFG1RD);
|
||||||
|
icdev->pmdata[2] = __raw_readl(icdev->base + IC_CFG2RD);
|
||||||
|
icdev->pmdata[3] = __raw_readl(icdev->base + IC_SRCRD);
|
||||||
|
icdev->pmdata[4] = __raw_readl(icdev->base + IC_ASSIGNRD);
|
||||||
|
icdev->pmdata[5] = __raw_readl(icdev->base + IC_WAKERD);
|
||||||
|
icdev->pmdata[6] = __raw_readl(icdev->base + IC_MASKRD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alchemy_ic_resume(struct sys_device *dev)
|
||||||
|
{
|
||||||
|
struct alchemy_ic_sysdev *icdev =
|
||||||
|
container_of(dev, struct alchemy_ic_sysdev, sysdev);
|
||||||
|
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_MASKCLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_CFG0CLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_CFG1CLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_CFG2CLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_SRCCLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_ASSIGNCLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_WAKECLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_RISINGCLR);
|
||||||
|
__raw_writel(0xffffffff, icdev->base + IC_FALLINGCLR);
|
||||||
|
__raw_writel(0x00000000, icdev->base + IC_TESTBIT);
|
||||||
|
wmb();
|
||||||
|
__raw_writel(icdev->pmdata[0], icdev->base + IC_CFG0SET);
|
||||||
|
__raw_writel(icdev->pmdata[1], icdev->base + IC_CFG1SET);
|
||||||
|
__raw_writel(icdev->pmdata[2], icdev->base + IC_CFG2SET);
|
||||||
|
__raw_writel(icdev->pmdata[3], icdev->base + IC_SRCSET);
|
||||||
|
__raw_writel(icdev->pmdata[4], icdev->base + IC_ASSIGNSET);
|
||||||
|
__raw_writel(icdev->pmdata[5], icdev->base + IC_WAKESET);
|
||||||
|
wmb();
|
||||||
|
|
||||||
|
__raw_writel(icdev->pmdata[6], icdev->base + IC_MASKSET);
|
||||||
|
wmb();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sysdev_class alchemy_ic_sysdev_class = {
|
||||||
|
.name = "ic",
|
||||||
|
.suspend = alchemy_ic_suspend,
|
||||||
|
.resume = alchemy_ic_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init alchemy_ic_sysdev_init(void)
|
||||||
|
{
|
||||||
|
struct alchemy_ic_sysdev *icdev;
|
||||||
|
unsigned long icbase[2] = { IC0_PHYS_ADDR, IC1_PHYS_ADDR };
|
||||||
|
int err, i;
|
||||||
|
|
||||||
|
err = sysdev_class_register(&alchemy_ic_sysdev_class);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
icdev = kzalloc(sizeof(struct alchemy_ic_sysdev), GFP_KERNEL);
|
||||||
|
if (!icdev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
icdev->base = ioremap(icbase[i], 0x1000);
|
||||||
|
|
||||||
|
icdev->sysdev.id = i;
|
||||||
|
icdev->sysdev.cls = &alchemy_ic_sysdev_class;
|
||||||
|
err = sysdev_register(&icdev->sysdev);
|
||||||
|
if (err) {
|
||||||
|
kfree(icdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
device_initcall(alchemy_ic_sysdev_init);
|
||||||
|
@@ -106,9 +106,6 @@ static void save_core_regs(void)
|
|||||||
sleep_usb[1] = au_readl(0xb4020024); /* OTG_MUX */
|
sleep_usb[1] = au_readl(0xb4020024); /* OTG_MUX */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Save interrupt controller state. */
|
|
||||||
save_au1xxx_intctl();
|
|
||||||
|
|
||||||
/* Clocks and PLLs. */
|
/* Clocks and PLLs. */
|
||||||
sleep_sys_clocks[0] = au_readl(SYS_FREQCTRL0);
|
sleep_sys_clocks[0] = au_readl(SYS_FREQCTRL0);
|
||||||
sleep_sys_clocks[1] = au_readl(SYS_FREQCTRL1);
|
sleep_sys_clocks[1] = au_readl(SYS_FREQCTRL1);
|
||||||
@@ -200,8 +197,6 @@ static void restore_core_regs(void)
|
|||||||
au_writel(sleep_uart0_clkdiv, UART0_ADDR + UART_CLK); au_sync();
|
au_writel(sleep_uart0_clkdiv, UART0_ADDR + UART_CLK); au_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_au1xxx_intctl();
|
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
|
#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
|
||||||
au1xxx_dbdma_resume();
|
au1xxx_dbdma_resume();
|
||||||
#endif
|
#endif
|
||||||
|
@@ -190,8 +190,6 @@ extern unsigned long au1xxx_calc_clock(void);
|
|||||||
/* PM: arch/mips/alchemy/common/sleeper.S, power.c, irq.c */
|
/* PM: arch/mips/alchemy/common/sleeper.S, power.c, irq.c */
|
||||||
void au1xxx_save_and_sleep(void);
|
void au1xxx_save_and_sleep(void);
|
||||||
void au_sleep(void);
|
void au_sleep(void);
|
||||||
void save_au1xxx_intctl(void);
|
|
||||||
void restore_au1xxx_intctl(void);
|
|
||||||
|
|
||||||
|
|
||||||
/* SOC Interrupt numbers */
|
/* SOC Interrupt numbers */
|
||||||
@@ -835,6 +833,38 @@ enum soc_au1200_ints {
|
|||||||
#define MEM_STNAND_DATA 0x20
|
#define MEM_STNAND_DATA 0x20
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Interrupt Controller register offsets */
|
||||||
|
#define IC_CFG0RD 0x40
|
||||||
|
#define IC_CFG0SET 0x40
|
||||||
|
#define IC_CFG0CLR 0x44
|
||||||
|
#define IC_CFG1RD 0x48
|
||||||
|
#define IC_CFG1SET 0x48
|
||||||
|
#define IC_CFG1CLR 0x4C
|
||||||
|
#define IC_CFG2RD 0x50
|
||||||
|
#define IC_CFG2SET 0x50
|
||||||
|
#define IC_CFG2CLR 0x54
|
||||||
|
#define IC_REQ0INT 0x54
|
||||||
|
#define IC_SRCRD 0x58
|
||||||
|
#define IC_SRCSET 0x58
|
||||||
|
#define IC_SRCCLR 0x5C
|
||||||
|
#define IC_REQ1INT 0x5C
|
||||||
|
#define IC_ASSIGNRD 0x60
|
||||||
|
#define IC_ASSIGNSET 0x60
|
||||||
|
#define IC_ASSIGNCLR 0x64
|
||||||
|
#define IC_WAKERD 0x68
|
||||||
|
#define IC_WAKESET 0x68
|
||||||
|
#define IC_WAKECLR 0x6C
|
||||||
|
#define IC_MASKRD 0x70
|
||||||
|
#define IC_MASKSET 0x70
|
||||||
|
#define IC_MASKCLR 0x74
|
||||||
|
#define IC_RISINGRD 0x78
|
||||||
|
#define IC_RISINGCLR 0x78
|
||||||
|
#define IC_FALLINGRD 0x7C
|
||||||
|
#define IC_FALLINGCLR 0x7C
|
||||||
|
#define IC_TESTBIT 0x80
|
||||||
|
|
||||||
|
|
||||||
/* Interrupt Controller 0 */
|
/* Interrupt Controller 0 */
|
||||||
#define IC0_CFG0RD 0xB0400040
|
#define IC0_CFG0RD 0xB0400040
|
||||||
#define IC0_CFG0SET 0xB0400040
|
#define IC0_CFG0SET 0xB0400040
|
||||||
|
Reference in New Issue
Block a user