266 lines
5.6 KiB
C
266 lines
5.6 KiB
C
#include <linux/version.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/list.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/regs-s3c6400-clock.h>
|
|
#include <asm/arch/pd.h>
|
|
#include "pd-s3c6410.h"
|
|
|
|
struct pdfw_md pd_md;
|
|
|
|
static unsigned long pd_get_gate_reg(struct pm_pdtype *pd)
|
|
{
|
|
return (unsigned long)S3C_NORMAL_CFG;
|
|
}
|
|
|
|
static unsigned long pd_get_blk_pwr_stat_reg(struct pm_pdtype *pd)
|
|
{
|
|
return (unsigned long)S3C_BLK_PWR_STAT;
|
|
}
|
|
|
|
int s3c_pd_enable(struct pm_pdtype *pd)
|
|
{
|
|
unsigned long gate_reg;
|
|
unsigned long stable_reg;
|
|
unsigned long flags;
|
|
unsigned long reg;
|
|
unsigned int tmp;
|
|
|
|
gate_reg = pd_get_gate_reg(pd);
|
|
stable_reg = pd_get_blk_pwr_stat_reg(pd);
|
|
|
|
local_irq_save(flags);
|
|
|
|
reg = __raw_readl(gate_reg);
|
|
reg |= pd->ctrlbit;
|
|
__raw_writel(reg, gate_reg);
|
|
|
|
/* here we have to wait until power stabilizes */
|
|
while (1) {
|
|
tmp = __raw_readl(stable_reg);
|
|
if (tmp && pd->stblregbit) break;
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
pd->state = PDSTATE_ON;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s3c_pd_disable(struct pm_pdtype *pd)
|
|
{
|
|
unsigned long gate_reg;
|
|
unsigned long flags;
|
|
unsigned long reg;
|
|
|
|
gate_reg = pd_get_gate_reg(pd);
|
|
|
|
local_irq_save(flags);
|
|
|
|
reg = __raw_readl(gate_reg);
|
|
reg &= ~(pd->ctrlbit);
|
|
__raw_writel(reg, gate_reg);
|
|
|
|
local_irq_restore(flags);
|
|
pd->state = PDSTATE_OFF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s3c_pd_state(struct pm_pdtype *pd)
|
|
{
|
|
unsigned long gate_reg;
|
|
unsigned long reg;
|
|
|
|
gate_reg = pd_get_gate_reg(pd);
|
|
|
|
reg = __raw_readl(gate_reg);
|
|
|
|
if (reg & pd->ctrlbit)
|
|
return PDSTATE_ON;
|
|
else
|
|
return PDSTATE_OFF;
|
|
}
|
|
|
|
struct pm_pdtype domain_etm = {
|
|
name:"domain_etm",
|
|
on: s3c_pd_enable,
|
|
off: s3c_pd_disable,
|
|
get_hwstate: s3c_pd_state,
|
|
ctrlbit: S3C_PWRGATE_DOMAIN_ETM,
|
|
stblregbit: S3C_BLK_ETM,
|
|
};
|
|
|
|
struct pm_pdtype domain_v = {
|
|
name:"domain_v",
|
|
on: s3c_pd_enable,
|
|
off: s3c_pd_disable,
|
|
get_hwstate: s3c_pd_state,
|
|
ctrlbit: S3C_PWRGATE_DOMAIN_V,
|
|
stblregbit: S3C_BLK_V,
|
|
};
|
|
|
|
struct pm_pdtype domain_f = {
|
|
name:"domain_f",
|
|
on: s3c_pd_enable,
|
|
off: s3c_pd_disable,
|
|
get_hwstate: s3c_pd_state,
|
|
ctrlbit: S3C_PWRGATE_DOMAIN_F,
|
|
stblregbit: S3C_BLK_F,
|
|
};
|
|
|
|
struct pm_pdtype domain_p = {
|
|
name:"domain_p",
|
|
on: s3c_pd_enable,
|
|
off: s3c_pd_disable,
|
|
get_hwstate: s3c_pd_state,
|
|
ctrlbit: S3C_PWRGATE_DOMAIN_P,
|
|
stblregbit: S3C_BLK_P,
|
|
};
|
|
|
|
struct pm_pdtype domain_i = {
|
|
name:"domain_i",
|
|
on: s3c_pd_enable,
|
|
off: s3c_pd_disable,
|
|
get_hwstate: s3c_pd_state,
|
|
ctrlbit: S3C_PWRGATE_DOMAIN_I,
|
|
stblregbit: S3C_BLK_I,
|
|
};
|
|
|
|
struct pm_pdtype domain_s = {
|
|
name:"domain_s",
|
|
on: s3c_pd_enable,
|
|
off: s3c_pd_disable,
|
|
get_hwstate: s3c_pd_state,
|
|
ctrlbit: S3C_PWRGATE_DOMAIN_S,
|
|
stblregbit: S3C_BLK_S,
|
|
};
|
|
|
|
void init_pd(struct pm_pdtype *pd)
|
|
{
|
|
/* Set initial power state to ON:
|
|
power is required until driver probe() are done */
|
|
pd->state = PDSTATE_ON;
|
|
INIT_LIST_HEAD(&(pd->entry));
|
|
INIT_LIST_HEAD(&(pd->devhead));
|
|
}
|
|
|
|
/*
|
|
* Initialize power domain structs
|
|
*/
|
|
void s3c6400_init_powerdomain(void)
|
|
{
|
|
init_pd(&domain_etm);
|
|
init_pd(&domain_v);
|
|
init_pd(&domain_f);
|
|
init_pd(&domain_p);
|
|
init_pd(&domain_i);
|
|
init_pd(&domain_s);
|
|
|
|
list_add_tail(&domain_etm.entry, &pd_head);
|
|
list_add_tail(&domain_v.entry, &pd_head);
|
|
list_add_tail(&domain_f.entry, &pd_head);
|
|
list_add_tail(&domain_p.entry, &pd_head);
|
|
list_add_tail(&domain_i.entry, &pd_head);
|
|
list_add_tail(&domain_s.entry, &pd_head);
|
|
}
|
|
|
|
void s3c6400_cleanup_powerdomain(void)
|
|
{
|
|
}
|
|
|
|
/* solve discrepancies between actual system state
|
|
and what is recorded in the pdfw data structures */
|
|
void s3c6400_sync_powerdomain(void)
|
|
{
|
|
struct list_head *t1, *t2;
|
|
struct pm_pdtype *pd;
|
|
struct pm_devtype *pdev;
|
|
|
|
list_for_each(t1, &pd_head) {
|
|
pd = list_entry(t1, struct pm_pdtype, entry);
|
|
if (pd->get_hwstate && (pd->state != pd->get_hwstate(pd))) {
|
|
printk(KERN_WARNING "%s:PDSTATE not in sync\n",pd->name);
|
|
pd->state = pd->get_hwstate(pd);
|
|
if (pd->state == PDSTATE_ON) {
|
|
list_for_each(t2, &pd->devhead) {
|
|
pdev = list_entry(t2, struct pm_devtype, entry);
|
|
if (pdev->after_pdon)
|
|
pdev->after_pdon();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_S3C6400_PDFW_PROC
|
|
#include <linux/proc_fs.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
static struct proc_dir_entry* s3c6400_pd_proc = NULL;
|
|
|
|
int s3c6400_pd_write_proc(struct file* file, const char* buffer,
|
|
unsigned long count, void* data)
|
|
{
|
|
char buf[100];
|
|
unsigned int tmp;
|
|
int size = count;
|
|
|
|
if (size >= sizeof(buf))
|
|
size = sizeof(buf) - 1;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
copy_from_user(buf, buffer, size);
|
|
buf[size] = '\0';
|
|
tmp = simple_strtol(buf, NULL, 16);
|
|
|
|
__raw_writel(tmp, S3C_NORMAL_CFG);
|
|
|
|
s3c6400_sync_powerdomain();
|
|
|
|
return count;
|
|
}
|
|
|
|
int s3c6400_pd_read_proc(char* page, char** start, off_t off, int count,
|
|
int *eof, void *data)
|
|
{
|
|
char *i = page;
|
|
unsigned int reg;
|
|
|
|
reg = __raw_readl(S3C_NORMAL_CFG);
|
|
i += sprintf(i, "0x%X\n", reg);
|
|
*start = NULL;
|
|
*eof = 1;
|
|
|
|
return (i - page);
|
|
}
|
|
#endif /* CONFIG_S3C6400_PDFW_PROC */
|
|
|
|
int __init s3c6400_pdfw_init(void)
|
|
{
|
|
#ifdef CONFIG_S3C6400_PDFW_PROC
|
|
struct proc_dir_entry *tmp;
|
|
|
|
s3c6400_pd_proc = proc_mkdir("pd", NULL);
|
|
if (s3c6400_pd_proc != NULL) {
|
|
tmp = create_proc_entry("gatereg", 0, s3c6400_pd_proc);
|
|
if (tmp) {
|
|
tmp->write_proc = s3c6400_pd_write_proc;
|
|
tmp->read_proc = s3c6400_pd_read_proc;
|
|
}
|
|
}
|
|
#endif
|
|
pd_md.init = s3c6400_init_powerdomain;
|
|
pd_md.cleanup = s3c6400_cleanup_powerdomain;
|
|
pd_md.sync = s3c6400_sync_powerdomain;
|
|
|
|
printk(KERN_INFO "s3c6400_pdfw_init done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
arch_initcall(s3c6400_pdfw_init);
|
|
|