1 | /* |
2 | * Common INTC2 register accessors |
3 | * |
4 | * Copyright (C) 2007, 2008 Magnus Damm |
5 | * Copyright (C) 2009, 2010 Paul Mundt |
6 | * |
7 | * This file is subject to the terms and conditions of the GNU General Public |
8 | * License. See the file "COPYING" in the main directory of this archive |
9 | * for more details. |
10 | */ |
11 | #include <linux/io.h> |
12 | #include "internals.h" |
13 | |
14 | unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address) |
15 | { |
16 | struct intc_window *window; |
17 | int k; |
18 | |
19 | /* scan through physical windows and convert address */ |
20 | for (k = 0; k < d->nr_windows; k++) { |
21 | window = d->window + k; |
22 | |
23 | if (address < window->phys) |
24 | continue; |
25 | |
26 | if (address >= (window->phys + window->size)) |
27 | continue; |
28 | |
29 | address -= window->phys; |
30 | address += (unsigned long)window->virt; |
31 | |
32 | return address; |
33 | } |
34 | |
35 | /* no windows defined, register must be 1:1 mapped virt:phys */ |
36 | return address; |
37 | } |
38 | |
39 | unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) |
40 | { |
41 | unsigned int k; |
42 | |
43 | address = intc_phys_to_virt(d, address); |
44 | |
45 | for (k = 0; k < d->nr_reg; k++) { |
46 | if (d->reg[k] == address) |
47 | return k; |
48 | } |
49 | |
50 | BUG(); |
51 | return 0; |
52 | } |
53 | |
54 | unsigned int intc_set_field_from_handle(unsigned int value, |
55 | unsigned int field_value, |
56 | unsigned int handle) |
57 | { |
58 | unsigned int width = _INTC_WIDTH(handle); |
59 | unsigned int shift = _INTC_SHIFT(handle); |
60 | |
61 | value &= ~(((1 << width) - 1) << shift); |
62 | value |= field_value << shift; |
63 | return value; |
64 | } |
65 | |
66 | unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle) |
67 | { |
68 | unsigned int width = _INTC_WIDTH(handle); |
69 | unsigned int shift = _INTC_SHIFT(handle); |
70 | unsigned int mask = ((1 << width) - 1) << shift; |
71 | |
72 | return (value & mask) >> shift; |
73 | } |
74 | |
75 | static unsigned long test_8(unsigned long addr, unsigned long h, |
76 | unsigned long ignore) |
77 | { |
78 | void __iomem *ptr = (void __iomem *)addr; |
79 | return intc_get_field_from_handle(__raw_readb(addr: ptr), handle: h); |
80 | } |
81 | |
82 | static unsigned long test_16(unsigned long addr, unsigned long h, |
83 | unsigned long ignore) |
84 | { |
85 | void __iomem *ptr = (void __iomem *)addr; |
86 | return intc_get_field_from_handle(__raw_readw(addr: ptr), handle: h); |
87 | } |
88 | |
89 | static unsigned long test_32(unsigned long addr, unsigned long h, |
90 | unsigned long ignore) |
91 | { |
92 | void __iomem *ptr = (void __iomem *)addr; |
93 | return intc_get_field_from_handle(__raw_readl(addr: ptr), handle: h); |
94 | } |
95 | |
96 | static unsigned long write_8(unsigned long addr, unsigned long h, |
97 | unsigned long data) |
98 | { |
99 | void __iomem *ptr = (void __iomem *)addr; |
100 | __raw_writeb(val: intc_set_field_from_handle(value: 0, field_value: data, handle: h), addr: ptr); |
101 | (void)__raw_readb(addr: ptr); /* Defeat write posting */ |
102 | return 0; |
103 | } |
104 | |
105 | static unsigned long write_16(unsigned long addr, unsigned long h, |
106 | unsigned long data) |
107 | { |
108 | void __iomem *ptr = (void __iomem *)addr; |
109 | __raw_writew(val: intc_set_field_from_handle(value: 0, field_value: data, handle: h), addr: ptr); |
110 | (void)__raw_readw(addr: ptr); /* Defeat write posting */ |
111 | return 0; |
112 | } |
113 | |
114 | static unsigned long write_32(unsigned long addr, unsigned long h, |
115 | unsigned long data) |
116 | { |
117 | void __iomem *ptr = (void __iomem *)addr; |
118 | __raw_writel(val: intc_set_field_from_handle(value: 0, field_value: data, handle: h), addr: ptr); |
119 | (void)__raw_readl(addr: ptr); /* Defeat write posting */ |
120 | return 0; |
121 | } |
122 | |
123 | static unsigned long modify_8(unsigned long addr, unsigned long h, |
124 | unsigned long data) |
125 | { |
126 | void __iomem *ptr = (void __iomem *)addr; |
127 | unsigned long flags; |
128 | unsigned int value; |
129 | local_irq_save(flags); |
130 | value = intc_set_field_from_handle(__raw_readb(addr: ptr), field_value: data, handle: h); |
131 | __raw_writeb(val: value, addr: ptr); |
132 | (void)__raw_readb(addr: ptr); /* Defeat write posting */ |
133 | local_irq_restore(flags); |
134 | return 0; |
135 | } |
136 | |
137 | static unsigned long modify_16(unsigned long addr, unsigned long h, |
138 | unsigned long data) |
139 | { |
140 | void __iomem *ptr = (void __iomem *)addr; |
141 | unsigned long flags; |
142 | unsigned int value; |
143 | local_irq_save(flags); |
144 | value = intc_set_field_from_handle(__raw_readw(addr: ptr), field_value: data, handle: h); |
145 | __raw_writew(val: value, addr: ptr); |
146 | (void)__raw_readw(addr: ptr); /* Defeat write posting */ |
147 | local_irq_restore(flags); |
148 | return 0; |
149 | } |
150 | |
151 | static unsigned long modify_32(unsigned long addr, unsigned long h, |
152 | unsigned long data) |
153 | { |
154 | void __iomem *ptr = (void __iomem *)addr; |
155 | unsigned long flags; |
156 | unsigned int value; |
157 | local_irq_save(flags); |
158 | value = intc_set_field_from_handle(__raw_readl(addr: ptr), field_value: data, handle: h); |
159 | __raw_writel(val: value, addr: ptr); |
160 | (void)__raw_readl(addr: ptr); /* Defeat write posting */ |
161 | local_irq_restore(flags); |
162 | return 0; |
163 | } |
164 | |
165 | static unsigned long intc_mode_field(unsigned long addr, |
166 | unsigned long handle, |
167 | unsigned long (*fn)(unsigned long, |
168 | unsigned long, |
169 | unsigned long), |
170 | unsigned int irq) |
171 | { |
172 | return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); |
173 | } |
174 | |
175 | static unsigned long intc_mode_zero(unsigned long addr, |
176 | unsigned long handle, |
177 | unsigned long (*fn)(unsigned long, |
178 | unsigned long, |
179 | unsigned long), |
180 | unsigned int irq) |
181 | { |
182 | return fn(addr, handle, 0); |
183 | } |
184 | |
185 | static unsigned long intc_mode_prio(unsigned long addr, |
186 | unsigned long handle, |
187 | unsigned long (*fn)(unsigned long, |
188 | unsigned long, |
189 | unsigned long), |
190 | unsigned int irq) |
191 | { |
192 | return fn(addr, handle, intc_get_prio_level(irq)); |
193 | } |
194 | |
195 | unsigned long (*intc_reg_fns[])(unsigned long addr, |
196 | unsigned long h, |
197 | unsigned long data) = { |
198 | [REG_FN_TEST_BASE + 0] = test_8, |
199 | [REG_FN_TEST_BASE + 1] = test_16, |
200 | [REG_FN_TEST_BASE + 3] = test_32, |
201 | [REG_FN_WRITE_BASE + 0] = write_8, |
202 | [REG_FN_WRITE_BASE + 1] = write_16, |
203 | [REG_FN_WRITE_BASE + 3] = write_32, |
204 | [REG_FN_MODIFY_BASE + 0] = modify_8, |
205 | [REG_FN_MODIFY_BASE + 1] = modify_16, |
206 | [REG_FN_MODIFY_BASE + 3] = modify_32, |
207 | }; |
208 | |
209 | unsigned long (*intc_enable_fns[])(unsigned long addr, |
210 | unsigned long handle, |
211 | unsigned long (*fn)(unsigned long, |
212 | unsigned long, |
213 | unsigned long), |
214 | unsigned int irq) = { |
215 | [MODE_ENABLE_REG] = intc_mode_field, |
216 | [MODE_MASK_REG] = intc_mode_zero, |
217 | [MODE_DUAL_REG] = intc_mode_field, |
218 | [MODE_PRIO_REG] = intc_mode_prio, |
219 | [MODE_PCLR_REG] = intc_mode_prio, |
220 | }; |
221 | |
222 | unsigned long (*intc_disable_fns[])(unsigned long addr, |
223 | unsigned long handle, |
224 | unsigned long (*fn)(unsigned long, |
225 | unsigned long, |
226 | unsigned long), |
227 | unsigned int irq) = { |
228 | [MODE_ENABLE_REG] = intc_mode_zero, |
229 | [MODE_MASK_REG] = intc_mode_field, |
230 | [MODE_DUAL_REG] = intc_mode_field, |
231 | [MODE_PRIO_REG] = intc_mode_zero, |
232 | [MODE_PCLR_REG] = intc_mode_field, |
233 | }; |
234 | |
235 | unsigned long (*intc_enable_noprio_fns[])(unsigned long addr, |
236 | unsigned long handle, |
237 | unsigned long (*fn)(unsigned long, |
238 | unsigned long, |
239 | unsigned long), |
240 | unsigned int irq) = { |
241 | [MODE_ENABLE_REG] = intc_mode_field, |
242 | [MODE_MASK_REG] = intc_mode_zero, |
243 | [MODE_DUAL_REG] = intc_mode_field, |
244 | [MODE_PRIO_REG] = intc_mode_field, |
245 | [MODE_PCLR_REG] = intc_mode_field, |
246 | }; |
247 | |