PXA910电源管理代码 pxa910_pm.c

/*
 * PXA910 Power Management Routines
 *
 * This software program is licensed subject to the GNU General Public License
 * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
 *
 * (C) Copyright 2009 Marvell International Ltd.
 * All Rights Reserved
 */

#undef DEBUG
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/kobject.h>
#include <linux/suspend.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/cputype.h>
#include <mach/mfp.h>
#include <mach/gpio.h>
#include <mach/addr-map.h>
#include <mach/dma.h>
#include <mach/gpio.h>
#include <mach/pxa910-squ.h>
#include <mach/regs-apbc.h>
#include <mach/regs-apmu.h>
#include <mach/regs-icu.h>
#include <mach/regs-mpmu.h>
#include <mach/regs-timers.h>
#include <mach/pxa168_pm.h>
#include <mach/pxa910_pm.h>
#include <mach/pxa910_dvfm.h>
#include <mach/irqs.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/mfd/88pm860x.h>
#include <mach/mfp-pxa910.h>

#define TDI 0x3
#define TRST 0x2
#define TMS 0x6
#define TCK 0xa
#define TDITCK 0xb
#define TMSTCK 0xe
#define TDITMSTCK 0xf

int enable_deepidle = IDLE_CORE_INTIDLE | IDLE_CORE_EXTIDLE |
    IDLE_APPS_IDLE | IDLE_APPS_SLEEP | IDLE_SYS_SLEEP;
static unsigned long pm_state;
static int cur_lpmode = -1;
static unsigned int pm_irqs[] = {
    //IRQ_PXA168_KEYPAD,

    IRQ_PXA168_PMIC_INT,
    IRQ_PXA168_RTC_ALARM,
    IRQ_PXA910_IPC_AP_DATAACK,
    IRQ_PXA910_IPC_AP_SET_CMD,
    IRQ_PXA910_IPC_AP_SET_MSG,
    IRQ_PXA168_GPIOX,
};
static struct pxa910_peripheral_config_ops *wakeup_ops = NULL;

extern void pxa910_cpu_disable_l2(void* param);
extern void pxa910_cpu_enable_l2(void* param);

int pxa910_power_config_register(struct pxa910_peripheral_config_ops *ops)
{
    wakeup_ops = ops;
    return 0;
}

void pxa910_power_config_unregister(void)
{
    wakeup_ops = NULL;
}

static uint32_t setup_wakeup_sources(void)
{
    uint32_t apcr, awucrm;

    awucrm = 0x0;
    //awucrm |= PMUM_GSM_WAKEUPWMX;

    //awucrm |= PMUM_WCDMA_WAKEUPX;

    //awucrm |= PMUM_GSM_WAKEUPWM;

    //awucrm |= PMUM_WCDMA_WAKEUPWM;

    awucrm |= PMUM_AP_ASYNC_INT;
    awucrm |= PMUM_AP_FULL_IDLE;
    awucrm |= PMUM_SDH1;
    //awucrm |= PMUM_SDH2;

    awucrm |= PMUM_KEYPRESS;
    //awucrm |= PMUM_TRACKBALL;

    //awucrm |= PMUM_NEWROTARY;

    awucrm |= PMUM_WDT;
    awucrm |= PMUM_RTC_ALARM;
    //awucrm |= PMUM_CP_TIMER_3;

    //awucrm |= PMUM_CP_TIMER_2;

    //awucrm |= PMUM_CP_TIMER_1;

    //awucrm |= PMUM_AP2_TIMER_3;

    //awucrm |= PMUM_AP2_TIMER_2;

    //awucrm |= PMUM_AP2_TIMER_1;

    //awucrm |= PMUM_AP1_TIMER_3;

    //awucrm |= PMUM_AP1_TIMER_2;

    awucrm |= PMUM_AP1_TIMER_1;
    awucrm |= PMUM_WAKEUP7;
    awucrm |= PMUM_WAKEUP6;
    //awucrm |= PMUM_WAKEUP5;

    awucrm |= PMUM_WAKEUP4;
    awucrm |= PMUM_WAKEUP3;
    awucrm |= PMUM_WAKEUP2;
    //awucrm |= PMUM_WAKEUP1;

    //awucrm |= PMUM_WAKEUP0;


    __raw_writel(awucrm, MPMU_AWUCRM);

    apcr = 0xff087fff;    /* enable all wake-up ports */

    return apcr;
}

void pxa910_pm_enter_lowpower_mode(int state)
{
    uint32_t idle_cfg, apcr;

    if (state == cur_lpmode)
        return;

    cur_lpmode = state;
    idle_cfg = __raw_readl(APMU_IDLE_CFG);
    apcr = __raw_readl(MPMU_APCR);

    apcr &= ~PMUM_DDRCORSD & ~PMUM_APBSD & ~PMUM_AXISD;
    apcr &= ~PMUM_VCTCXOSD;
    idle_cfg &= ~PMUA_MOH_IDLE;
    idle_cfg &= ~PMUA_MOH_PWRDWN;

    switch (state) {
    case POWER_MODE_UDR:
        apcr |= PMUM_STBYEN;
    case POWER_MODE_SYS_SLEEP:
        apcr |= PMUM_SLPEN;            /* set the SLPEN bit */
        apcr |= PMUM_VCTCXOSD;            /* set VCTCXOSD */
        apcr &= setup_wakeup_sources();        /* set up wakeup sources */
        /* fall through */
    case POWER_MODE_APPS_SLEEP:
        apcr |= PMUM_DDRCORSD | PMUM_APBSD;    /* set DDRCORSD and APBSD */
        /* fall through */
    case POWER_MODE_APPS_IDLE:
        apcr |= PMUM_AXISD;            /* set AXISDD bit */
        /* fall through */
    case POWER_MODE_CORE_EXTIDLE:
        idle_cfg |= PMUA_MOH_IDLE;        /* set the IDLE bit */
        idle_cfg |= PMUA_MOH_PWRDWN;        /* set the PWRDWN bit */
        idle_cfg |= 0x000f0000;            /* number of power switch to 4 */
        /* fall through */
    case POWER_MODE_CORE_INTIDLE:
        break;
    }

    /* program the memory controller hardware sleep type and auto wakeup */
    idle_cfg |= PMUA_MOH_DIS_MC_SW_REQ;
    idle_cfg |= PMUA_MOH_MC_WAKE_EN;
    __raw_writel(0x0, APMU_MC_HW_SLP_TYPE);        /* auto refresh */

    /* set DSPSD, DTCMSD, BBSD, MSASLPEN */
    apcr |= PMUM_DSPSD | PMUM_DTCMSD | PMUM_BBSD | PMUM_MSASLPEN;

    /*always set SLEPEN bit mainly for MSA*/
    apcr |= PMUM_SLPEN;

    /* finally write the registers back */
    __raw_writel(idle_cfg, APMU_IDLE_CFG);
    __raw_writel(apcr, MPMU_APCR);

    //__raw_writel(0x20807, APMU_PWR_STBL_TIMER);

    //__raw_writel(0x1, APMU_SRAM_PWR_DWN);


    //__raw_writel(0x0, APMU_MC_SW_SLP_TYPE);

    //__raw_writel(0x1, APMU_MC_SLP_REQ);


    //__raw_writel(0x0, MPMU_VRCR);

}

#define MAXTOKENS 80

static int tokenizer(char **tbuf, const char *userbuf, ssize_t n,
            char **tokptrs, int maxtoks)
{
    char *cp, *tok;
    char *whitespace = " \t\r\n";
    int ntoks = 0;
    cp = kmalloc(n + 1, GFP_KERNEL);
    if (!cp)
        return -ENOMEM;

    *tbuf = cp;
    memcpy(cp, userbuf, n);
    cp[n] = '\0';

    do {
        cp = cp + strspn(cp, whitespace);
        tok = strsep(&cp, whitespace);
        if ((*tok == '\0') || (ntoks == maxtoks))
            break;
        tokptrs[ntoks++] = tok;
    } while (cp);

    return ntoks;
}

static ssize_t deepidle_show(struct kobject *kobj, struct kobj_attribute *attr,
        char *buf)
{
    int len = 0;

    if (enable_deepidle & IDLE_CORE_INTIDLE)
        len += sprintf(buf + len, "core_intidle ");
    if (enable_deepidle & IDLE_CORE_EXTIDLE)
        len += sprintf(buf + len, "core_extidle ");
    if (enable_deepidle & IDLE_APPS_IDLE)
        len += sprintf(buf + len, "apps_idle ");
    if (enable_deepidle & IDLE_APPS_SLEEP)
        len += sprintf(buf + len, "apps_sleep ");
    if (enable_deepidle & IDLE_SYS_SLEEP)
        len += sprintf(buf + len, "sys_sleep");
    len += sprintf(buf + len, "\n");
    len += sprintf(buf + len, "Usage: echo [set|unset] "
        "[core_intidle|core_extidle|apps_idle|apps_sleep|sys_sleep] "
        "> deepidle\n");
    return len;
}

static ssize_t deepidle_store(struct kobject *kobj, struct kobj_attribute *attr,
        const char *buf, size_t len)
{
    int error = 0;
    char *tbuf = NULL;
    char *token[MAXTOKENS];
    int ntoks = tokenizer(&tbuf, buf, len, (char **)&token, MAXTOKENS);

    if (ntoks <= 0) {
        error = ntoks;
        goto out;
    }

    if (strcmp(token[0], "set") == 0) {
        if (strcmp(token[1], "core_intidle") == 0)
            enable_deepidle |= IDLE_CORE_INTIDLE;
        else if (strcmp(token[1], "core_extidle") == 0)
            enable_deepidle |= IDLE_CORE_EXTIDLE;
        else if (strcmp(token[1], "apps_idle") == 0)
            enable_deepidle |= IDLE_APPS_IDLE;
        else if (strcmp(token[1], "apps_sleep") == 0)
            enable_deepidle |= IDLE_APPS_SLEEP;
        else if (strcmp(token[1], "sys_sleep") == 0)
            enable_deepidle |= IDLE_SYS_SLEEP;
        else
            error = -EINVAL;
    } else if (strcmp(token[0], "unset") == 0) {
        if (strcmp(token[1], "core_intidle") == 0)
            enable_deepidle &= ~IDLE_CORE_INTIDLE;
        else if (strcmp(token[1], "core_extidle") == 0)
            enable_deepidle &= ~IDLE_CORE_EXTIDLE;
        else if (strcmp(token[1], "apps_idle") == 0)
            enable_deepidle &= ~IDLE_APPS_IDLE;
        else if (strcmp(token[1], "apps_sleep") == 0)
            enable_deepidle &= ~IDLE_APPS_SLEEP;
        else if (strcmp(token[1], "sys_sleep") == 0)
            enable_deepidle &= ~IDLE_SYS_SLEEP;
        else
            error = -EINVAL;
    } else {
        if (strcmp(token[0], "0") == 0)
            enable_deepidle = IDLE_ACTIVE;
        else
            error = -EINVAL;
    }
out:
    kfree(tbuf);
    return error ? error : len;
}

static struct kobj_attribute deepidle_attr = {
    .attr    = {
        .name = __stringify(deepidle),
        .mode = 0644,
    },
    .show    = deepidle_show,
    .store    = deepidle_store,
};

static ssize_t regdump_show(struct kobject *kobj, struct kobj_attribute *attr,
        char *buf)
{
    int len = 0;
    unsigned int addr;
    uint32_t ccr = __raw_readl(TIMERS1_VIRT_BASE + TMR_CCR);
    uint32_t cr0 = __raw_readl(TIMERS1_VIRT_BASE + TMR_CR(0));

    /*len += sprintf(buf + len, "apcr 0x%x, cpcr: 0x%x, "
        "apidle 0x%x, cpidle 0x%x, "
        "apwake 0x%x, cpwake 0x%x\n",
        __raw_readl(MPMU_APCR), __raw_readl(MPMU_CPCR),
        __raw_readl(APMU_IDLE_CFG), __raw_readl(APMU_CP_IDLE_CFG),
        __raw_readl(MPMU_AWUCRM), __raw_readl(MPMU_CWUCRM));*/
    for (addr = MPMU_CPCR; addr <= (MPMU_CPCR+0x4c); addr += 4)
        len += sprintf(buf + len, "0x%x: 0x%x\n", addr, __raw_readl(addr));
    for (addr = MPMU_APCR; addr <= (MPMU_APCR+0x4c); addr += 4)
        len += sprintf(buf + len, "0x%x: 0x%x\n", addr, __raw_readl(addr));
    for (addr = (APMU_CCR-4); addr <= APMU_SMC_CLK_RES_CTRL; addr += 4)
        len += sprintf(buf + len, "0x%x: 0x%x\n", addr, __raw_readl(addr));
    __raw_writel(__raw_readl(APMU_CP_CCR) | 0x80000000, APMU_CP_CCR);
    __raw_writel(__raw_readl(APMU_CP_CCR) & ~0x80000000, APMU_CP_CCR);

    len += sprintf(buf + len, "ccr 0x%x, cr0: 0x%x\n", ccr, cr0);
    return len;
}

static ssize_t regdump_store(struct kobject *kobj, struct kobj_attribute *attr,
        const char *buf, size_t len)
{
    unsigned int addr, value;
    int error = 0;
    char *tbuf = NULL;
    char *token[MAXTOKENS];
    int ntoks = tokenizer(&tbuf, buf, len, (char **)&token, MAXTOKENS);

    if (ntoks <= 0) {
        error = ntoks;
        goto out;
    }

    addr = simple_strtoul(token[0], NULL, 0);
    if (addr < 0xd4000000 || addr > 0xd4400000) {
        error = -EINVAL;
        goto out;
    }
    value = simple_strtoul(token[1], NULL, 0);

    __raw_writel(value, addr+0x2a000000);
out:
    kfree(tbuf);
    return error ? error : len;
}

static struct kobj_attribute regdump_attr = {
    .attr    = {
        .name = __stringify(regdump),
        .mode = 0644,
    },
    .show    = regdump_show,
    .store    = regdump_store,
};

static struct attribute * g[] = {
    &deepidle_attr.attr,
    ®dump_attr.attr,
    NULL,
};

static struct attribute_group attr_group = {
    .attrs = g,
};

static void setbit1(void) {
    __raw_writel(TDI, JTAGSW_CTRL);
    __raw_writel(TDITCK, JTAGSW_CTRL);
}

static void setbit0(void) {
    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL);
}

//leave GC and C&M power on to enter chip sleep

static void pxa910_A0_Workaround(void) {
    int i;
    printk("-----------------WORKAROUND\n");
    __raw_writel(0x3, APBC_PXA168_SW_JTAG);
    __raw_writel(0x1, JTAGSW_EN);
    for (i=0; i<30; i++) {
        __raw_writel(TMS, JTAGSW_CTRL);
        __raw_writel(TMSTCK, JTAGSW_CTRL);
    }// force TAP to reset state

    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL); // TAP goto RUTI


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // TAP goto SDRS


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // TAP goto SIRS


    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL); // TAP goto CAIR


    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL); // goto SHIR state, data will be latched in next negedge


    // target IR address: 9'h09f

    setbit1();
    setbit1();
    setbit1();
    setbit1();
    setbit1();
    setbit0();
    setbit0();
    setbit1();
    // shift IR done, goto UPIR


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // posedge latch bit[9], and TAP goto Exit1_IR


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // TAP goto update IR


    // IR done, goto DR

    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL); // TAP goto RUTI


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // TAP goto SDRS


    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL); // TAP goto capture DR


    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TCK, JTAGSW_CTRL); // TAP goto shift DR state, data will be latched in next neg edge


    // target data = 130'h

    //bit[0] = 1, all_island_off

    setbit1(); // GC&DX8 can be on during sleep

    //bit[128:1] = 0

    for (i=1; i<129; i++)
        setbit0(); // posedge latch data


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // posedge latch bit[129], and TAP goto Exit1_DR


    __raw_writel(TMS, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL); // TAP goto update DR


    __raw_writel(TRST, JTAGSW_CTRL);
    __raw_writel(TMSTCK, JTAGSW_CTRL);

    __raw_writel(0x0, JTAGSW_EN);    // disable JTAG SW mode, hardware JTAG will be valid now

}

static int peripheral_suspend_config(void)
{
    if (wakeup_ops->pin_lpm_config)
        wakeup_ops->pin_lpm_config();

    return 0;
}

static int peripheral_resume_config(void)
{
    if (wakeup_ops->pin_restore)
        wakeup_ops->pin_restore();

    return 0;
}

/* This function is used to set unit clock before system enters sleep.
 */
static void pxa910_pm_set_cken(void)
{
    /*
     * turn off SMC periphral clock to save power in udr mode.
     */
    __raw_writel(0x3, APMU_SMC_CLK_RES_CTRL);
}

/* This function is used to restore unit clock after system resumes.
 */
static void pxa910_pm_restore_cken(void)
{
    __raw_writel(0x1b, APMU_SMC_CLK_RES_CTRL);
}

static int pxa910_pm_enter(suspend_state_t state)
{
    int idle_cfg;
    pxa910_pm_enter_lowpower_mode(POWER_MODE_UDR);
    __raw_writel(0x0, MPMU_VRCR);
    if(cpu_is_pxa910_Y0())
        pxa910_A0_Workaround();
    peripheral_suspend_config();
    pxa910_pm_set_cken();

    idle_cfg = __raw_readl(APMU_IDLE_CFG); // add it

    idle_cfg |= 0x60;
    __raw_writel(idle_cfg, APMU_IDLE_CFG);

    pxa910_cpu_disable_l2(0);

    cpu_do_idle();

    pxa910_cpu_enable_l2(0);

    idle_cfg = __raw_readl(APMU_IDLE_CFG);
    idle_cfg &= (~0x60);
    __raw_writel(idle_cfg, APMU_IDLE_CFG);

    pxa910_pm_restore_cken();
    peripheral_resume_config();

    pxa910_pm_enter_lowpower_mode(POWER_MODE_CORE_INTIDLE);
    return 0;
}

static void pxa910_pm_irq(unsigned int enable)
{
    struct irq_desc *desc = irq_to_desc(pm_irqs[0]);
    unsigned int i, j, flag=0;
    int reg;

    for (i = 0; i < 64; i++) {
        flag = 0;
        for (j = 0; j < ARRAY_SIZE(pm_irqs); j++)
            if (i == pm_irqs[j]) {
                flag = 1;
                break;
            }
        if(flag)
            continue;
        reg = __raw_readl(ICU_INT_CONF(i));
        if(!(reg & ICU_INT_CONF_CP_INT)) {
            if(enable)
                desc->chip->unmask(i);
            else
                desc->chip->mask(i);
        }
    }
}

/*
 * Called after processes are frozen, but before we shut down devices.
 */
static int pxa910_pm_prepare(void)
{
    pxa910_pm_irq(0);
    return 0;
}

/*
 * Called after devices are re-setup, but before processes are thawed.
 */
static void pxa910_pm_finish(void)
{
    pm_state = PM_SUSPEND_ON;
    pxa910_pm_irq(1);
    __raw_writel(0x1, MPMU_VRCR);
    pm860x_codec_reg_write(PM8607_AUDIO_REG_BASE + PM8607_AUDIO_SHORTS, 0xaa);
}

static int pxa910_pm_valid(suspend_state_t state)
{
    int ret = 1;

    if (state == PM_SUSPEND_STANDBY) {
        pm_state = PM_SUSPEND_STANDBY;
    } else if (state == PM_SUSPEND_MEM) {
        pm_state = PM_SUSPEND_MEM;
    } else {
        ret = 0;
    }
    return ret;
}

/*
 * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
 */
static struct platform_suspend_ops pxa910_pm_ops = {
    .valid        = pxa910_pm_valid,
    .prepare    = pxa910_pm_prepare,
    .enter        = pxa910_pm_enter,
    .finish        = pxa910_pm_finish,
};

static int __init pxa910_pm_init(void)
{
    if (!cpu_is_pxa910())
        return -EIO;
    if (sysfs_create_group(power_kobj, &attr_group))
        return -1;
    suspend_set_ops(&pxa910_pm_ops);

    if(cpu_is_pxa910_Ax()) {
        __raw_writel(__raw_readl(APMU_SQU_CLK_GATE_CTRL) | BIT_30, APMU_SQU_CLK_GATE_CTRL);
        __raw_writel(__raw_readl(MPMU_FCCR) | BIT_28, MPMU_FCCR);
    }

    return 0;
}

late_initcall(pxa910_pm_init);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值