summaryrefslogtreecommitdiff
path: root/cleopatre/linux-2.6.25.10-spc300/arch/arm/mach-spc300/spc300-pm.c
blob: fe9a6bc3b614b8d6e2b70204157d0aee37d6a959 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
 * arch/arm/mach-spc300/spc300-pm.c
 *
 * (C) Copyright 2009 SPiDCOM Technologies
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include <linux/suspend.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#ifdef CONFIG_CHIP_FEATURE_SRAM
#include <asm/arch/ips/sram.h>
#include <asm/io.h>
#endif
#include <asm/arch/wdt.h>
#include <asm/arch/spc300-pm.h>

#include "spc300.h"

#ifdef CONFIG_CHIP_FEATURE_SRAM
/* SRAM shared data. */
sram_data_t sram_data;

static void (* pm_process) (void);
static void map_extra_registers (void);
static void unmap_extra_registers (void);
static void system_time_update (void);

/* Map hardware registers that are not used by the kernel, but that must be
 * accessed during the SRAM procedure.
 */
static void map_extra_registers (void)
{
    sram_data.spll_addr = (unsigned int)ioremap(EXT_REG_BANK_BASE + MSEPLL_SPLL_OFFSET + MSEPLL_SPPLL_CTRL_OFFSET, SPLL_BANK_SIZE);
    sram_data.dsppll_addr = (unsigned int)ioremap(EXT_REG_BANK_BASE + MSEAFE_OFFSET + MSEAFE_DPLL_CTRL_OFFSET, DSPPLL_BANK_SIZE);
}

/* Unmap the previously mapped registers. */
static void unmap_extra_registers (void)
{
    iounmap((void *)sram_data.spll_addr);
    iounmap((void *)sram_data.dsppll_addr);
    sram_data.spll_addr = 0;
    sram_data.dsppll_addr = 0;
}
/* Update the system time according to the number of timer ticks recorded
 * during the SRAM procedure and the duration of the timer.
 */
void system_time_update ()
{
    struct timeval tv;

    do_gettimeofday(&tv);
    /* Tick timer is set to 10ms */
    tv.tv_sec += (sram_data.tick_counter / 100);
    tv.tv_usec += (sram_data.tick_counter % 100) * 10000;
    if (tv.tv_usec >= 1000000)
    {
        tv.tv_sec++;
        tv.tv_usec -= 1000000;
    }

    do_settimeofday(&tv);
    sram_data.tick_counter = 0;
}
#endif

static bool spc300_pm_suspend_cmd;

void spc300_pm_request_suspend (void)
{
    spc300_pm_suspend_cmd = true;
}

void spc300_pm_clear_suspend (void)
{
    spc300_pm_suspend_cmd = false;
}

static int spc300_pm_enter(suspend_state_t state)
{
    BUG_ON(state != PM_SUSPEND_MEM);

#ifdef CONFIG_CHIP_FEATURE_SRAM
    /* Map the required registers. */
    map_extra_registers();
    /* Copy data structure in SRAM. */
    memcpy ((void *)SRAM_DATA_BASE_VA_PTR, &sram_data, sizeof(sram_data));
#endif

    spc300_irq_suspend();
    local_irq_disable();
    spc300_wdt_set_irq_mode();
#ifdef CONFIG_CHIP_FEATURE_SRAM
    pm_process();
    /* Copy data structure from SRAM. */
    memcpy (&sram_data, (void *)SRAM_DATA_BASE_VA_PTR, sizeof(sram_data));

    /* Unmap the registers. */
    unmap_extra_registers();
    /* Update system time. */
    system_time_update();
#endif
    /* Clear suspend request command */
    spc300_pm_suspend_cmd = false;
    spc300_wdt_unset_irq_mode();
    local_irq_enable();
    spc300_irq_resume();
    return 0;
}

static struct platform_suspend_ops spc300_pm_ops ={
    .valid  = suspend_valid_only_mem,
    .enter  = spc300_pm_enter,
};

/**
 * Read the PM data structure by /proc.
 *
 * \param  file  file structure.
 * \param  buffer  string pointer given by user.
 * \param  start  string pointer begin.
 * \param  offset  offset value.
 * \param  count  count parameter.
 * \param  eof  end of file.
 * \param  data  PM data structure.
 * \return  new pointer position.
 */
static int spc300_readproc_pm(char *buf, char **start, off_t offset,
                              int count, int *eof, void *data)
{
    bool suspend_cmd = *(bool *)data;
    char *p;

    p = buf;

    p += sprintf(p, "%d\n", suspend_cmd);

    *eof = 1;

    return p-buf+1;
}

static int __init spc300_pm_init(void)
{
    struct proc_dir_entry *pm_proc_dir;
    struct proc_dir_entry *entry;

    printk("SPC300: Power Management\n");

    /* Initialize PM standby command. */
    spc300_pm_suspend_cmd = false;

#ifdef CONFIG_CHIP_FEATURE_SRAM
    /* Link SRAM part of the suspend process. */
    pm_process = (void *) SRAM_BASE_VA_PTR;
#endif

    suspend_set_ops(&spc300_pm_ops);

    /* Allow WDT IRQ to wake us up in low power mode. */
    enable_irq_wake(INT_WDT);
    /* Keep timer IRQ ON in low power mode. */
    enable_irq_wake(INT_TIMER_1);

    /* Create proc entry */
    pm_proc_dir = proc_mkdir("pm", &proc_root);

    /* This entry is read only, so there is no write function and permissions
     * are set to 0444. */
    entry = create_proc_entry("suspend_cmd", 0, pm_proc_dir);
    entry->read_proc  = spc300_readproc_pm;
    entry->mode       = S_IRUGO;
    entry->data       = (void *)(&spc300_pm_suspend_cmd);

    return 0;
}
arch_initcall(spc300_pm_init);