iwlwifi: mvm: handle FRAME_RELEASE in MQ code
[sfrench/cifs-2.6.git] / arch / arm / mach-shmobile / pm-rcar.c
1 /*
2  * R-Car SYSC Power management support
3  *
4  * Copyright (C) 2014  Magnus Damm
5  *
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  */
10
11 #include <linux/delay.h>
12 #include <linux/err.h>
13 #include <linux/mm.h>
14 #include <linux/spinlock.h>
15 #include <linux/io.h>
16 #include "pm-rcar.h"
17
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 */
24
25 /* SYSC Status Register */
26 #define SYSCSR_PONENB           1       /* Ready for power resume requests */
27 #define SYSCSR_POFFENB          0       /* Ready for power shutoff requests */
28
29 /*
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
33  */
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 */
40
41
42 #define SYSCSR_RETRIES          100
43 #define SYSCSR_DELAY_US         1
44
45 #define PWRER_RETRIES           100
46 #define PWRER_DELAY_US          1
47
48 #define SYSCISR_RETRIES         1000
49 #define SYSCISR_DELAY_US        1
50
51 static void __iomem *rcar_sysc_base;
52 static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */
53
54 static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on)
55 {
56         unsigned int sr_bit, reg_offs;
57         int k;
58
59         if (on) {
60                 sr_bit = SYSCSR_PONENB;
61                 reg_offs = PWRONCR_OFFS;
62         } else {
63                 sr_bit = SYSCSR_POFFENB;
64                 reg_offs = PWROFFCR_OFFS;
65         }
66
67         /* Wait until SYSC is ready to accept a power request */
68         for (k = 0; k < SYSCSR_RETRIES; k++) {
69                 if (ioread32(rcar_sysc_base + SYSCSR) & BIT(sr_bit))
70                         break;
71                 udelay(SYSCSR_DELAY_US);
72         }
73
74         if (k == SYSCSR_RETRIES)
75                 return -EAGAIN;
76
77         /* Submit power shutoff or power resume request */
78         iowrite32(BIT(sysc_ch->chan_bit),
79                   rcar_sysc_base + sysc_ch->chan_offs + reg_offs);
80
81         return 0;
82 }
83
84 static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on)
85 {
86         unsigned int isr_mask = BIT(sysc_ch->isr_bit);
87         unsigned int chan_mask = BIT(sysc_ch->chan_bit);
88         unsigned int status;
89         unsigned long flags;
90         int ret = 0;
91         int k;
92
93         spin_lock_irqsave(&rcar_sysc_lock, flags);
94
95         iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
96
97         /* Submit power shutoff or resume request until it was accepted */
98         for (k = 0; k < PWRER_RETRIES; k++) {
99                 ret = rcar_sysc_pwr_on_off(sysc_ch, on);
100                 if (ret)
101                         goto out;
102
103                 status = ioread32(rcar_sysc_base +
104                                   sysc_ch->chan_offs + PWRER_OFFS);
105                 if (!(status & chan_mask))
106                         break;
107
108                 udelay(PWRER_DELAY_US);
109         }
110
111         if (k == PWRER_RETRIES) {
112                 ret = -EIO;
113                 goto out;
114         }
115
116         /* Wait until the power shutoff or resume request has completed * */
117         for (k = 0; k < SYSCISR_RETRIES; k++) {
118                 if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask)
119                         break;
120                 udelay(SYSCISR_DELAY_US);
121         }
122
123         if (k == SYSCISR_RETRIES)
124                 ret = -EIO;
125
126         iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
127
128  out:
129         spin_unlock_irqrestore(&rcar_sysc_lock, flags);
130
131         pr_debug("sysc power domain %d: %08x -> %d\n",
132                  sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret);
133         return ret;
134 }
135
136 int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch)
137 {
138         return rcar_sysc_power(sysc_ch, false);
139 }
140
141 int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch)
142 {
143         return rcar_sysc_power(sysc_ch, true);
144 }
145
146 bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch)
147 {
148         unsigned int st;
149
150         st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS);
151         if (st & BIT(sysc_ch->chan_bit))
152                 return true;
153
154         return false;
155 }
156
157 void __iomem *rcar_sysc_init(phys_addr_t base)
158 {
159         rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
160         if (!rcar_sysc_base)
161                 panic("unable to ioremap R-Car SYSC hardware block\n");
162
163         return rcar_sysc_base;
164 }