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 
intc_phys_to_virt(struct intc_desc_int * d,unsigned long address)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 
intc_get_reg(struct intc_desc_int * d,unsigned long address)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 
intc_set_field_from_handle(unsigned int value,unsigned int field_value,unsigned int handle)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 
intc_get_field_from_handle(unsigned int value,unsigned int handle)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 
test_8(unsigned long addr,unsigned long h,unsigned long ignore)75 static unsigned long test_8(unsigned long addr, unsigned long h,
76 			    unsigned long ignore)
77 {
78 	return intc_get_field_from_handle(__raw_readb(addr), h);
79 }
80 
test_16(unsigned long addr,unsigned long h,unsigned long ignore)81 static unsigned long test_16(unsigned long addr, unsigned long h,
82 			     unsigned long ignore)
83 {
84 	return intc_get_field_from_handle(__raw_readw(addr), h);
85 }
86 
test_32(unsigned long addr,unsigned long h,unsigned long ignore)87 static unsigned long test_32(unsigned long addr, unsigned long h,
88 			     unsigned long ignore)
89 {
90 	return intc_get_field_from_handle(__raw_readl(addr), h);
91 }
92 
write_8(unsigned long addr,unsigned long h,unsigned long data)93 static unsigned long write_8(unsigned long addr, unsigned long h,
94 			     unsigned long data)
95 {
96 	__raw_writeb(intc_set_field_from_handle(0, data, h), addr);
97 	(void)__raw_readb(addr);	/* Defeat write posting */
98 	return 0;
99 }
100 
write_16(unsigned long addr,unsigned long h,unsigned long data)101 static unsigned long write_16(unsigned long addr, unsigned long h,
102 			      unsigned long data)
103 {
104 	__raw_writew(intc_set_field_from_handle(0, data, h), addr);
105 	(void)__raw_readw(addr);	/* Defeat write posting */
106 	return 0;
107 }
108 
write_32(unsigned long addr,unsigned long h,unsigned long data)109 static unsigned long write_32(unsigned long addr, unsigned long h,
110 			      unsigned long data)
111 {
112 	__raw_writel(intc_set_field_from_handle(0, data, h), addr);
113 	(void)__raw_readl(addr);	/* Defeat write posting */
114 	return 0;
115 }
116 
modify_8(unsigned long addr,unsigned long h,unsigned long data)117 static unsigned long modify_8(unsigned long addr, unsigned long h,
118 			      unsigned long data)
119 {
120 	unsigned long flags;
121 	unsigned int value;
122 	local_irq_save(flags);
123 	value = intc_set_field_from_handle(__raw_readb(addr), data, h);
124 	__raw_writeb(value, addr);
125 	(void)__raw_readb(addr);	/* Defeat write posting */
126 	local_irq_restore(flags);
127 	return 0;
128 }
129 
modify_16(unsigned long addr,unsigned long h,unsigned long data)130 static unsigned long modify_16(unsigned long addr, unsigned long h,
131 			       unsigned long data)
132 {
133 	unsigned long flags;
134 	unsigned int value;
135 	local_irq_save(flags);
136 	value = intc_set_field_from_handle(__raw_readw(addr), data, h);
137 	__raw_writew(value, addr);
138 	(void)__raw_readw(addr);	/* Defeat write posting */
139 	local_irq_restore(flags);
140 	return 0;
141 }
142 
modify_32(unsigned long addr,unsigned long h,unsigned long data)143 static unsigned long modify_32(unsigned long addr, unsigned long h,
144 			       unsigned long data)
145 {
146 	unsigned long flags;
147 	unsigned int value;
148 	local_irq_save(flags);
149 	value = intc_set_field_from_handle(__raw_readl(addr), data, h);
150 	__raw_writel(value, addr);
151 	(void)__raw_readl(addr);	/* Defeat write posting */
152 	local_irq_restore(flags);
153 	return 0;
154 }
155 
intc_mode_field(unsigned long addr,unsigned long handle,unsigned long (* fn)(unsigned long,unsigned long,unsigned long),unsigned int irq)156 static unsigned long intc_mode_field(unsigned long addr,
157 				     unsigned long handle,
158 				     unsigned long (*fn)(unsigned long,
159 						unsigned long,
160 						unsigned long),
161 				     unsigned int irq)
162 {
163 	return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
164 }
165 
intc_mode_zero(unsigned long addr,unsigned long handle,unsigned long (* fn)(unsigned long,unsigned long,unsigned long),unsigned int irq)166 static unsigned long intc_mode_zero(unsigned long addr,
167 				    unsigned long handle,
168 				    unsigned long (*fn)(unsigned long,
169 					       unsigned long,
170 					       unsigned long),
171 				    unsigned int irq)
172 {
173 	return fn(addr, handle, 0);
174 }
175 
intc_mode_prio(unsigned long addr,unsigned long handle,unsigned long (* fn)(unsigned long,unsigned long,unsigned long),unsigned int irq)176 static unsigned long intc_mode_prio(unsigned long addr,
177 				    unsigned long handle,
178 				    unsigned long (*fn)(unsigned long,
179 					       unsigned long,
180 					       unsigned long),
181 				    unsigned int irq)
182 {
183 	return fn(addr, handle, intc_get_prio_level(irq));
184 }
185 
186 unsigned long (*intc_reg_fns[])(unsigned long addr,
187 				unsigned long h,
188 				unsigned long data) = {
189 	[REG_FN_TEST_BASE + 0] = test_8,
190 	[REG_FN_TEST_BASE + 1] = test_16,
191 	[REG_FN_TEST_BASE + 3] = test_32,
192 	[REG_FN_WRITE_BASE + 0] = write_8,
193 	[REG_FN_WRITE_BASE + 1] = write_16,
194 	[REG_FN_WRITE_BASE + 3] = write_32,
195 	[REG_FN_MODIFY_BASE + 0] = modify_8,
196 	[REG_FN_MODIFY_BASE + 1] = modify_16,
197 	[REG_FN_MODIFY_BASE + 3] = modify_32,
198 };
199 
200 unsigned long (*intc_enable_fns[])(unsigned long addr,
201 				   unsigned long handle,
202 				   unsigned long (*fn)(unsigned long,
203 					    unsigned long,
204 					    unsigned long),
205 				   unsigned int irq) = {
206 	[MODE_ENABLE_REG] = intc_mode_field,
207 	[MODE_MASK_REG] = intc_mode_zero,
208 	[MODE_DUAL_REG] = intc_mode_field,
209 	[MODE_PRIO_REG] = intc_mode_prio,
210 	[MODE_PCLR_REG] = intc_mode_prio,
211 };
212 
213 unsigned long (*intc_disable_fns[])(unsigned long addr,
214 				    unsigned long handle,
215 				    unsigned long (*fn)(unsigned long,
216 					     unsigned long,
217 					     unsigned long),
218 				    unsigned int irq) = {
219 	[MODE_ENABLE_REG] = intc_mode_zero,
220 	[MODE_MASK_REG] = intc_mode_field,
221 	[MODE_DUAL_REG] = intc_mode_field,
222 	[MODE_PRIO_REG] = intc_mode_zero,
223 	[MODE_PCLR_REG] = intc_mode_field,
224 };
225 
226 unsigned long (*intc_enable_noprio_fns[])(unsigned long addr,
227 					  unsigned long handle,
228 					  unsigned long (*fn)(unsigned long,
229 						unsigned long,
230 						unsigned long),
231 					  unsigned int irq) = {
232 	[MODE_ENABLE_REG] = intc_mode_field,
233 	[MODE_MASK_REG] = intc_mode_zero,
234 	[MODE_DUAL_REG] = intc_mode_field,
235 	[MODE_PRIO_REG] = intc_mode_field,
236 	[MODE_PCLR_REG] = intc_mode_field,
237 };
238