diff options
author | Krzysztof Halasa <khc@pm.waw.pl> | 2008-01-01 15:55:23 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-02-04 08:15:25 -0500 |
commit | 82a96f5790ac93a406be72ed8f308dd29ad7e6af (patch) | |
tree | 29e3029fa42938370b4cdfc0202593374aa366e6 /arch/arm/mach-ixp4xx/ixp4xx_qmgr.c | |
parent | c18f65816ef80b67eb4511ed8359c2dfcd69680d (diff) |
[ARM] 4713/3: Adds drivers for IXP4xx QMgr and NPE features
This patch adds drivers for IXP4xx hardware Queue Manager and for
Network Processor Engines. Requires patch #4712 (reading/writing
CPU feature (aka fuse) bits).
Posted to linux-arm-kernel on 2 Dec 2007 and revised.
Signed-off-by: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-ixp4xx/ixp4xx_qmgr.c')
-rw-r--r-- | arch/arm/mach-ixp4xx/ixp4xx_qmgr.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c new file mode 100644 index 000000000000..e83301325301 --- /dev/null +++ b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | * Intel IXP4xx Queue Manager driver for Linux | ||
3 | * | ||
4 | * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of version 2 of the GNU General Public License | ||
8 | * as published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/ioport.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <asm/arch/qmgr.h> | ||
16 | |||
17 | #define DEBUG 0 | ||
18 | |||
19 | struct qmgr_regs __iomem *qmgr_regs; | ||
20 | static struct resource *mem_res; | ||
21 | static spinlock_t qmgr_lock; | ||
22 | static u32 used_sram_bitmap[4]; /* 128 16-dword pages */ | ||
23 | static void (*irq_handlers[HALF_QUEUES])(void *pdev); | ||
24 | static void *irq_pdevs[HALF_QUEUES]; | ||
25 | |||
26 | void qmgr_set_irq(unsigned int queue, int src, | ||
27 | void (*handler)(void *pdev), void *pdev) | ||
28 | { | ||
29 | u32 __iomem *reg = &qmgr_regs->irqsrc[queue / 8]; /* 8 queues / u32 */ | ||
30 | int bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */ | ||
31 | unsigned long flags; | ||
32 | |||
33 | src &= 7; | ||
34 | spin_lock_irqsave(&qmgr_lock, flags); | ||
35 | __raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg); | ||
36 | irq_handlers[queue] = handler; | ||
37 | irq_pdevs[queue] = pdev; | ||
38 | spin_unlock_irqrestore(&qmgr_lock, flags); | ||
39 | } | ||
40 | |||
41 | |||
42 | static irqreturn_t qmgr_irq1(int irq, void *pdev) | ||
43 | { | ||
44 | int i; | ||
45 | u32 val = __raw_readl(&qmgr_regs->irqstat[0]); | ||
46 | __raw_writel(val, &qmgr_regs->irqstat[0]); /* ACK */ | ||
47 | |||
48 | for (i = 0; i < HALF_QUEUES; i++) | ||
49 | if (val & (1 << i)) | ||
50 | irq_handlers[i](irq_pdevs[i]); | ||
51 | |||
52 | return val ? IRQ_HANDLED : 0; | ||
53 | } | ||
54 | |||
55 | |||
56 | void qmgr_enable_irq(unsigned int queue) | ||
57 | { | ||
58 | unsigned long flags; | ||
59 | |||
60 | spin_lock_irqsave(&qmgr_lock, flags); | ||
61 | __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | (1 << queue), | ||
62 | &qmgr_regs->irqen[0]); | ||
63 | spin_unlock_irqrestore(&qmgr_lock, flags); | ||
64 | } | ||
65 | |||
66 | void qmgr_disable_irq(unsigned int queue) | ||
67 | { | ||
68 | unsigned long flags; | ||
69 | |||
70 | spin_lock_irqsave(&qmgr_lock, flags); | ||
71 | __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~(1 << queue), | ||
72 | &qmgr_regs->irqen[0]); | ||
73 | spin_unlock_irqrestore(&qmgr_lock, flags); | ||
74 | } | ||
75 | |||
76 | static inline void shift_mask(u32 *mask) | ||
77 | { | ||
78 | mask[3] = mask[3] << 1 | mask[2] >> 31; | ||
79 | mask[2] = mask[2] << 1 | mask[1] >> 31; | ||
80 | mask[1] = mask[1] << 1 | mask[0] >> 31; | ||
81 | mask[0] <<= 1; | ||
82 | } | ||
83 | |||
84 | int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, | ||
85 | unsigned int nearly_empty_watermark, | ||
86 | unsigned int nearly_full_watermark) | ||
87 | { | ||
88 | u32 cfg, addr = 0, mask[4]; /* in 16-dwords */ | ||
89 | int err; | ||
90 | |||
91 | if (queue >= HALF_QUEUES) | ||
92 | return -ERANGE; | ||
93 | |||
94 | if ((nearly_empty_watermark | nearly_full_watermark) & ~7) | ||
95 | return -EINVAL; | ||
96 | |||
97 | switch (len) { | ||
98 | case 16: | ||
99 | cfg = 0 << 24; | ||
100 | mask[0] = 0x1; | ||
101 | break; | ||
102 | case 32: | ||
103 | cfg = 1 << 24; | ||
104 | mask[0] = 0x3; | ||
105 | break; | ||
106 | case 64: | ||
107 | cfg = 2 << 24; | ||
108 | mask[0] = 0xF; | ||
109 | break; | ||
110 | case 128: | ||
111 | cfg = 3 << 24; | ||
112 | mask[0] = 0xFF; | ||
113 | break; | ||
114 | default: | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | |||
118 | cfg |= nearly_empty_watermark << 26; | ||
119 | cfg |= nearly_full_watermark << 29; | ||
120 | len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */ | ||
121 | mask[1] = mask[2] = mask[3] = 0; | ||
122 | |||
123 | if (!try_module_get(THIS_MODULE)) | ||
124 | return -ENODEV; | ||
125 | |||
126 | spin_lock_irq(&qmgr_lock); | ||
127 | if (__raw_readl(&qmgr_regs->sram[queue])) { | ||
128 | err = -EBUSY; | ||
129 | goto err; | ||
130 | } | ||
131 | |||
132 | while (1) { | ||
133 | if (!(used_sram_bitmap[0] & mask[0]) && | ||
134 | !(used_sram_bitmap[1] & mask[1]) && | ||
135 | !(used_sram_bitmap[2] & mask[2]) && | ||
136 | !(used_sram_bitmap[3] & mask[3])) | ||
137 | break; /* found free space */ | ||
138 | |||
139 | addr++; | ||
140 | shift_mask(mask); | ||
141 | if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) { | ||
142 | printk(KERN_ERR "qmgr: no free SRAM space for" | ||
143 | " queue %i\n", queue); | ||
144 | err = -ENOMEM; | ||
145 | goto err; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | used_sram_bitmap[0] |= mask[0]; | ||
150 | used_sram_bitmap[1] |= mask[1]; | ||
151 | used_sram_bitmap[2] |= mask[2]; | ||
152 | used_sram_bitmap[3] |= mask[3]; | ||
153 | __raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]); | ||
154 | spin_unlock_irq(&qmgr_lock); | ||
155 | |||
156 | #if DEBUG | ||
157 | printk(KERN_DEBUG "qmgr: requested queue %i, addr = 0x%02X\n", | ||
158 | queue, addr); | ||
159 | #endif | ||
160 | return 0; | ||
161 | |||
162 | err: | ||
163 | spin_unlock_irq(&qmgr_lock); | ||
164 | module_put(THIS_MODULE); | ||
165 | return err; | ||
166 | } | ||
167 | |||
168 | void qmgr_release_queue(unsigned int queue) | ||
169 | { | ||
170 | u32 cfg, addr, mask[4]; | ||
171 | |||
172 | BUG_ON(queue >= HALF_QUEUES); /* not in valid range */ | ||
173 | |||
174 | spin_lock_irq(&qmgr_lock); | ||
175 | cfg = __raw_readl(&qmgr_regs->sram[queue]); | ||
176 | addr = (cfg >> 14) & 0xFF; | ||
177 | |||
178 | BUG_ON(!addr); /* not requested */ | ||
179 | |||
180 | switch ((cfg >> 24) & 3) { | ||
181 | case 0: mask[0] = 0x1; break; | ||
182 | case 1: mask[0] = 0x3; break; | ||
183 | case 2: mask[0] = 0xF; break; | ||
184 | case 3: mask[0] = 0xFF; break; | ||
185 | } | ||
186 | |||
187 | while (addr--) | ||
188 | shift_mask(mask); | ||
189 | |||
190 | __raw_writel(0, &qmgr_regs->sram[queue]); | ||
191 | |||
192 | used_sram_bitmap[0] &= ~mask[0]; | ||
193 | used_sram_bitmap[1] &= ~mask[1]; | ||
194 | used_sram_bitmap[2] &= ~mask[2]; | ||
195 | used_sram_bitmap[3] &= ~mask[3]; | ||
196 | irq_handlers[queue] = NULL; /* catch IRQ bugs */ | ||
197 | spin_unlock_irq(&qmgr_lock); | ||
198 | |||
199 | module_put(THIS_MODULE); | ||
200 | #if DEBUG | ||
201 | printk(KERN_DEBUG "qmgr: released queue %i\n", queue); | ||
202 | #endif | ||
203 | } | ||
204 | |||
205 | static int qmgr_init(void) | ||
206 | { | ||
207 | int i, err; | ||
208 | mem_res = request_mem_region(IXP4XX_QMGR_BASE_PHYS, | ||
209 | IXP4XX_QMGR_REGION_SIZE, | ||
210 | "IXP4xx Queue Manager"); | ||
211 | if (mem_res == NULL) | ||
212 | return -EBUSY; | ||
213 | |||
214 | qmgr_regs = ioremap(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); | ||
215 | if (qmgr_regs == NULL) { | ||
216 | err = -ENOMEM; | ||
217 | goto error_map; | ||
218 | } | ||
219 | |||
220 | /* reset qmgr registers */ | ||
221 | for (i = 0; i < 4; i++) { | ||
222 | __raw_writel(0x33333333, &qmgr_regs->stat1[i]); | ||
223 | __raw_writel(0, &qmgr_regs->irqsrc[i]); | ||
224 | } | ||
225 | for (i = 0; i < 2; i++) { | ||
226 | __raw_writel(0, &qmgr_regs->stat2[i]); | ||
227 | __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */ | ||
228 | __raw_writel(0, &qmgr_regs->irqen[i]); | ||
229 | } | ||
230 | |||
231 | for (i = 0; i < QUEUES; i++) | ||
232 | __raw_writel(0, &qmgr_regs->sram[i]); | ||
233 | |||
234 | err = request_irq(IRQ_IXP4XX_QM1, qmgr_irq1, 0, | ||
235 | "IXP4xx Queue Manager", NULL); | ||
236 | if (err) { | ||
237 | printk(KERN_ERR "qmgr: failed to request IRQ%i\n", | ||
238 | IRQ_IXP4XX_QM1); | ||
239 | goto error_irq; | ||
240 | } | ||
241 | |||
242 | used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ | ||
243 | spin_lock_init(&qmgr_lock); | ||
244 | |||
245 | printk(KERN_INFO "IXP4xx Queue Manager initialized.\n"); | ||
246 | return 0; | ||
247 | |||
248 | error_irq: | ||
249 | iounmap(qmgr_regs); | ||
250 | error_map: | ||
251 | release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); | ||
252 | return err; | ||
253 | } | ||
254 | |||
255 | static void qmgr_remove(void) | ||
256 | { | ||
257 | free_irq(IRQ_IXP4XX_QM1, NULL); | ||
258 | synchronize_irq(IRQ_IXP4XX_QM1); | ||
259 | iounmap(qmgr_regs); | ||
260 | release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); | ||
261 | } | ||
262 | |||
263 | module_init(qmgr_init); | ||
264 | module_exit(qmgr_remove); | ||
265 | |||
266 | MODULE_LICENSE("GPL v2"); | ||
267 | MODULE_AUTHOR("Krzysztof Halasa"); | ||
268 | |||
269 | EXPORT_SYMBOL(qmgr_regs); | ||
270 | EXPORT_SYMBOL(qmgr_set_irq); | ||
271 | EXPORT_SYMBOL(qmgr_enable_irq); | ||
272 | EXPORT_SYMBOL(qmgr_disable_irq); | ||
273 | EXPORT_SYMBOL(qmgr_request_queue); | ||
274 | EXPORT_SYMBOL(qmgr_release_queue); | ||