ARM: shmobile: R-Car: Improve documentation
[linux-2.6/btrfs-unstable.git] / arch / arm / mach-shmobile / pm-rcar.c
blob56ea82a851cb96f72e83a99aef1c82bd3bdc907e
1 /*
2 * R-Car SYSC Power management support
4 * Copyright (C) 2014 Magnus Damm
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file "COPYING" in the main directory of this archive
8 * for more details.
9 */
11 #include <linux/delay.h>
12 #include <linux/err.h>
13 #include <linux/mm.h>
14 #include <linux/spinlock.h>
15 #include <asm/io.h>
16 #include "pm-rcar.h"
18 /* SYSC Common */
19 #define SYSCSR 0x00 /* SYSC Status Register */
20 #define SYSCISR 0x04 /* Interrupt Status Register */
21 #define SYSCISCR 0x08 /* Interrupt Status Clear Register */
22 #define SYSCIER 0x0c /* Interrupt Enable Register */
23 #define SYSCIMR 0x10 /* Interrupt Mask Register */
25 /* SYSC Status Register */
26 #define SYSCSR_PONENB 1 /* Ready for power resume requests */
27 #define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */
30 * Power Control Register Offsets inside the register block for each domain
31 * Note: The "CR" registers for ARM cores exist on H1 only
32 * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2
34 #define PWRSR_OFFS 0x00 /* Power Status Register */
35 #define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */
36 #define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */
37 #define PWRONCR_OFFS 0x0c /* Power Resume Control Register */
38 #define PWRONSR_OFFS 0x10 /* Power Resume Status Register */
39 #define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */
42 #define SYSCSR_RETRIES 100
43 #define SYSCSR_DELAY_US 1
45 #define SYSCISR_RETRIES 1000
46 #define SYSCISR_DELAY_US 1
48 static void __iomem *rcar_sysc_base;
49 static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */
51 static int rcar_sysc_pwr_on_off(struct rcar_sysc_ch *sysc_ch,
52 int sr_bit, int reg_offs)
54 int k;
56 /* Wait until SYSC is ready to accept a power request */
57 for (k = 0; k < SYSCSR_RETRIES; k++) {
58 if (ioread32(rcar_sysc_base + SYSCSR) & (1 << sr_bit))
59 break;
60 udelay(SYSCSR_DELAY_US);
63 if (k == SYSCSR_RETRIES)
64 return -EAGAIN;
66 /* Submit power shutoff or power resume request */
67 iowrite32(1 << sysc_ch->chan_bit,
68 rcar_sysc_base + sysc_ch->chan_offs + reg_offs);
70 return 0;
73 static int rcar_sysc_pwr_off(struct rcar_sysc_ch *sysc_ch)
75 return rcar_sysc_pwr_on_off(sysc_ch, SYSCSR_POFFENB, PWROFFCR_OFFS);
78 static int rcar_sysc_pwr_on(struct rcar_sysc_ch *sysc_ch)
80 return rcar_sysc_pwr_on_off(sysc_ch, SYSCSR_PONENB, PWRONCR_OFFS);
83 static int rcar_sysc_update(struct rcar_sysc_ch *sysc_ch,
84 int (*on_off_fn)(struct rcar_sysc_ch *))
86 unsigned int isr_mask = 1 << sysc_ch->isr_bit;
87 unsigned int chan_mask = 1 << sysc_ch->chan_bit;
88 unsigned int status;
89 unsigned long flags;
90 int ret = 0;
91 int k;
93 spin_lock_irqsave(&rcar_sysc_lock, flags);
95 iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
97 /* Submit power shutoff or resume request until it was accepted */
98 do {
99 ret = on_off_fn(sysc_ch);
100 if (ret)
101 goto out;
103 status = ioread32(rcar_sysc_base +
104 sysc_ch->chan_offs + PWRER_OFFS);
105 } while (status & chan_mask);
107 /* Wait until the power shutoff or resume request has completed * */
108 for (k = 0; k < SYSCISR_RETRIES; k++) {
109 if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask)
110 break;
111 udelay(SYSCISR_DELAY_US);
114 if (k == SYSCISR_RETRIES)
115 ret = -EIO;
117 iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
119 out:
120 spin_unlock_irqrestore(&rcar_sysc_lock, flags);
122 pr_debug("sysc power domain %d: %08x -> %d\n",
123 sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret);
124 return ret;
127 int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch)
129 return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_off);
132 int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch)
134 return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_on);
137 bool rcar_sysc_power_is_off(struct rcar_sysc_ch *sysc_ch)
139 unsigned int st;
141 st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS);
142 if (st & (1 << sysc_ch->chan_bit))
143 return true;
145 return false;
148 void __iomem *rcar_sysc_init(phys_addr_t base)
150 rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
151 if (!rcar_sysc_base)
152 panic("unable to ioremap R-Car SYSC hardware block\n");
154 return rcar_sysc_base;