Commit | Line | Data |
---|---|---|
9fbbe689 | 1 | /* |
263475d4 | 2 | * Copyright 2011-2013 Freescale Semiconductor, Inc. |
9fbbe689 SG |
3 | * Copyright 2011 Linaro Ltd. |
4 | * | |
5 | * The code contained herein is licensed under the GNU General Public | |
6 | * License. You may obtain a copy of the GNU General Public License | |
7 | * Version 2 or later at the following locations: | |
8 | * | |
9 | * http://www.opensource.org/licenses/gpl-license.html | |
10 | * http://www.gnu.org/copyleft/gpl.html | |
11 | */ | |
12 | ||
13 | #include <linux/io.h> | |
14 | #include <linux/irq.h> | |
15 | #include <linux/of.h> | |
16 | #include <linux/of_address.h> | |
17 | #include <linux/of_irq.h> | |
520f7bd7 | 18 | #include <linux/irqchip/arm-gic.h> |
9a67a6fd | 19 | #include "common.h" |
9fbbe689 SG |
20 | |
21 | #define GPC_IMR1 0x008 | |
22 | #define GPC_PGC_CPU_PDN 0x2a0 | |
05136f08 AH |
23 | #define GPC_PGC_CPU_PUPSCR 0x2a4 |
24 | #define GPC_PGC_CPU_PDNSCR 0x2a8 | |
25 | #define GPC_PGC_SW2ISO_SHIFT 0x8 | |
26 | #define GPC_PGC_SW_SHIFT 0x0 | |
9fbbe689 SG |
27 | |
28 | #define IMR_NUM 4 | |
29 | ||
30 | static void __iomem *gpc_base; | |
31 | static u32 gpc_wake_irqs[IMR_NUM]; | |
32 | static u32 gpc_saved_imrs[IMR_NUM]; | |
33 | ||
05136f08 AH |
34 | void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw) |
35 | { | |
36 | writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) | | |
37 | (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PUPSCR); | |
38 | } | |
39 | ||
40 | void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw) | |
41 | { | |
42 | writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) | | |
43 | (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PDNSCR); | |
44 | } | |
45 | ||
46 | void imx_gpc_set_arm_power_in_lpm(bool power_off) | |
47 | { | |
48 | writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN); | |
49 | } | |
50 | ||
80c0ecdc | 51 | void imx_gpc_pre_suspend(bool arm_power_off) |
9fbbe689 SG |
52 | { |
53 | void __iomem *reg_imr1 = gpc_base + GPC_IMR1; | |
54 | int i; | |
55 | ||
56 | /* Tell GPC to power off ARM core when suspend */ | |
80c0ecdc | 57 | if (arm_power_off) |
05136f08 | 58 | imx_gpc_set_arm_power_in_lpm(arm_power_off); |
9fbbe689 SG |
59 | |
60 | for (i = 0; i < IMR_NUM; i++) { | |
61 | gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); | |
62 | writel_relaxed(~gpc_wake_irqs[i], reg_imr1 + i * 4); | |
63 | } | |
64 | } | |
65 | ||
66 | void imx_gpc_post_resume(void) | |
67 | { | |
68 | void __iomem *reg_imr1 = gpc_base + GPC_IMR1; | |
69 | int i; | |
70 | ||
71 | /* Keep ARM core powered on for other low-power modes */ | |
05136f08 | 72 | imx_gpc_set_arm_power_in_lpm(false); |
9fbbe689 SG |
73 | |
74 | for (i = 0; i < IMR_NUM; i++) | |
75 | writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); | |
76 | } | |
77 | ||
78 | static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on) | |
79 | { | |
e2fd06f6 | 80 | unsigned int idx = d->hwirq / 32 - 1; |
9fbbe689 SG |
81 | u32 mask; |
82 | ||
83 | /* Sanity check for SPI irq */ | |
e2fd06f6 | 84 | if (d->hwirq < 32) |
9fbbe689 SG |
85 | return -EINVAL; |
86 | ||
e2fd06f6 | 87 | mask = 1 << d->hwirq % 32; |
9fbbe689 SG |
88 | gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask : |
89 | gpc_wake_irqs[idx] & ~mask; | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
263475d4 AH |
94 | void imx_gpc_mask_all(void) |
95 | { | |
96 | void __iomem *reg_imr1 = gpc_base + GPC_IMR1; | |
97 | int i; | |
98 | ||
99 | for (i = 0; i < IMR_NUM; i++) { | |
100 | gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); | |
101 | writel_relaxed(~0, reg_imr1 + i * 4); | |
102 | } | |
103 | ||
104 | } | |
105 | ||
106 | void imx_gpc_restore_all(void) | |
107 | { | |
108 | void __iomem *reg_imr1 = gpc_base + GPC_IMR1; | |
109 | int i; | |
110 | ||
111 | for (i = 0; i < IMR_NUM; i++) | |
112 | writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); | |
113 | } | |
114 | ||
65bb688a | 115 | void imx_gpc_hwirq_unmask(unsigned int hwirq) |
9fbbe689 SG |
116 | { |
117 | void __iomem *reg; | |
118 | u32 val; | |
119 | ||
65bb688a | 120 | reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4; |
9fbbe689 | 121 | val = readl_relaxed(reg); |
65bb688a | 122 | val &= ~(1 << hwirq % 32); |
9fbbe689 SG |
123 | writel_relaxed(val, reg); |
124 | } | |
125 | ||
65bb688a | 126 | void imx_gpc_hwirq_mask(unsigned int hwirq) |
9fbbe689 SG |
127 | { |
128 | void __iomem *reg; | |
129 | u32 val; | |
130 | ||
65bb688a MZ |
131 | reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4; |
132 | val = readl_relaxed(reg); | |
133 | val |= 1 << (hwirq % 32); | |
134 | writel_relaxed(val, reg); | |
135 | } | |
136 | ||
137 | static void imx_gpc_irq_unmask(struct irq_data *d) | |
138 | { | |
139 | /* Sanity check for SPI irq */ | |
140 | if (d->hwirq < 32) | |
141 | return; | |
142 | ||
143 | imx_gpc_hwirq_unmask(d->hwirq); | |
144 | } | |
145 | ||
146 | static void imx_gpc_irq_mask(struct irq_data *d) | |
147 | { | |
9fbbe689 | 148 | /* Sanity check for SPI irq */ |
e2fd06f6 | 149 | if (d->hwirq < 32) |
9fbbe689 SG |
150 | return; |
151 | ||
65bb688a | 152 | imx_gpc_hwirq_mask(d->hwirq); |
9fbbe689 SG |
153 | } |
154 | ||
155 | void __init imx_gpc_init(void) | |
156 | { | |
157 | struct device_node *np; | |
485863b8 | 158 | int i; |
9fbbe689 SG |
159 | |
160 | np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); | |
161 | gpc_base = of_iomap(np, 0); | |
162 | WARN_ON(!gpc_base); | |
163 | ||
485863b8 SG |
164 | /* Initially mask all interrupts */ |
165 | for (i = 0; i < IMR_NUM; i++) | |
166 | writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4); | |
167 | ||
9fbbe689 SG |
168 | /* Register GPC as the secondary interrupt controller behind GIC */ |
169 | gic_arch_extn.irq_mask = imx_gpc_irq_mask; | |
170 | gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; | |
171 | gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; | |
172 | } |