VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / arch / ppc / 8xx_io / commproc.c
1 /*
2  * General Purpose functions for the global management of the
3  * Communication Processor Module.
4  * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
5  *
6  * In addition to the individual control of the communication
7  * channels, there are a few functions that globally affect the
8  * communication processor.
9  *
10  * Buffer descriptors must be allocated from the dual ported memory
11  * space.  The allocator for that is here.  When the communication
12  * process is reset, we reclaim the memory available.  There is
13  * currently no deallocator for this memory.
14  * The amount of space available is platform dependent.  On the
15  * MBX, the EPPC software loads additional microcode into the
16  * communication processor, and uses some of the DP ram for this
17  * purpose.  Current, the first 512 bytes and the last 256 bytes of
18  * memory are used.  Right now I am conservative and only use the
19  * memory that can never be used for microcode.  If there are
20  * applications that require more DP ram, we can expand the boundaries
21  * but then we have to be careful of any downloaded microcode.
22  */
23 #include <linux/errno.h>
24 #include <linux/sched.h>
25 #include <linux/kernel.h>
26 #include <linux/param.h>
27 #include <linux/string.h>
28 #include <linux/mm.h>
29 #include <linux/interrupt.h>
30 #include <linux/module.h>
31 #include <asm/irq.h>
32 #include <asm/mpc8xx.h>
33 #include <asm/page.h>
34 #include <asm/pgtable.h>
35 #include <asm/8xx_immap.h>
36 #include <asm/commproc.h>
37 #include <asm/io.h>
38 #include <asm/rheap.h>
39
40 extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep);
41
42 static void m8xx_cpm_dpinit(void);
43 static  uint    host_buffer;    /* One page of host buffer */
44 static  uint    host_end;       /* end + 1 */
45 cpm8xx_t        *cpmp;          /* Pointer to comm processor space */
46
47 /* CPM interrupt vector functions.
48 */
49 struct  cpm_action {
50         void    (*handler)(void *, struct pt_regs * regs);
51         void    *dev_id;
52 };
53 static  struct  cpm_action cpm_vecs[CPMVEC_NR];
54 static  void    cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
55 static  void    cpm_error_interrupt(void *, struct pt_regs * regs);
56 static  void    alloc_host_memory(void);
57
58 #if 1
59 void
60 m8xx_cpm_reset(void)
61 {
62         volatile immap_t         *imp;
63         volatile cpm8xx_t       *commproc;
64
65         imp = (immap_t *)IMAP_ADDR;
66         commproc = (cpm8xx_t *)&imp->im_cpm;
67
68 #ifdef CONFIG_UCODE_PATCH
69         /* Perform a reset.
70         */
71         commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
72
73         /* Wait for it.
74         */
75         while (commproc->cp_cpcr & CPM_CR_FLG);
76
77         cpm_load_patch(imp);
78 #endif
79
80         /* Set SDMA Bus Request priority 5.
81          * On 860T, this also enables FEC priority 6.  I am not sure
82          * this is what we realy want for some applications, but the
83          * manual recommends it.
84          * Bit 25, FAM can also be set to use FEC aggressive mode (860T).
85          */
86         imp->im_siu_conf.sc_sdcr = 1;
87
88         /* Reclaim the DP memory for our use. */
89         m8xx_cpm_dpinit();
90
91         /* Tell everyone where the comm processor resides.
92         */
93         cpmp = (cpm8xx_t *)commproc;
94 }
95
96 /* We used to do this earlier, but have to postpone as long as possible
97  * to ensure the kernel VM is now running.
98  */
99 static void
100 alloc_host_memory()
101 {
102         uint    physaddr;
103
104         /* Set the host page for allocation.
105         */
106         host_buffer = (uint)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &physaddr);
107         host_end = host_buffer + PAGE_SIZE;
108 }
109 #else
110 void
111 m8xx_cpm_reset(uint host_page_addr)
112 {
113         volatile immap_t         *imp;
114         volatile cpm8xx_t       *commproc;
115         pte_t                   *pte;
116
117         imp = (immap_t *)IMAP_ADDR;
118         commproc = (cpm8xx_t *)&imp->im_cpm;
119
120 #ifdef CONFIG_UCODE_PATCH
121         /* Perform a reset.
122         */
123         commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
124
125         /* Wait for it.
126         */
127         while (commproc->cp_cpcr & CPM_CR_FLG);
128
129         cpm_load_patch(imp);
130 #endif
131
132         /* Set SDMA Bus Request priority 5.
133          * On 860T, this also enables FEC priority 6.  I am not sure
134          * this is what we realy want for some applications, but the
135          * manual recommends it.
136          * Bit 25, FAM can also be set to use FEC aggressive mode (860T).
137         */
138         imp->im_siu_conf.sc_sdcr = 1;
139
140         /* Reclaim the DP memory for our use. */
141         m8xx_cpm_dpinit();
142
143         /* Set the host page for allocation.
144         */
145         host_buffer = host_page_addr;   /* Host virtual page address */
146         host_end = host_page_addr + PAGE_SIZE;
147
148         /* We need to get this page early, so I have to do it the
149          * hard way.
150          */
151         if (get_pteptr(&init_mm, host_page_addr, &pte)) {
152                 pte_val(*pte) |= _PAGE_NO_CACHE;
153                 flush_tlb_page(init_mm.mmap, host_buffer);
154         }
155         else {
156                 panic("Huh?  No CPM host page?");
157         }
158
159         /* Tell everyone where the comm processor resides.
160         */
161         cpmp = (cpm8xx_t *)commproc;
162 }
163 #endif
164
165 /* This is called during init_IRQ.  We used to do it above, but this
166  * was too early since init_IRQ was not yet called.
167  */
168 void
169 cpm_interrupt_init(void)
170 {
171         /* Initialize the CPM interrupt controller.
172         */
173         ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr =
174             (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
175                 ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK;
176         ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr = 0;
177
178         /* Set our interrupt handler with the core CPU.
179         */
180         if (request_8xxirq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0)
181                 panic("Could not allocate CPM IRQ!");
182
183         /* Install our own error handler.
184         */
185         cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL);
186         ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN;
187 }
188
189 /* CPM interrupt controller interrupt.
190 */
191 static  void
192 cpm_interrupt(int irq, void * dev, struct pt_regs * regs)
193 {
194         uint    vec;
195
196         /* Get the vector by setting the ACK bit and then reading
197          * the register.
198          */
199         ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr = 1;
200         vec = ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr;
201         vec >>= 11;
202
203         if (cpm_vecs[vec].handler != 0)
204                 (*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id, regs);
205         else
206                 ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec);
207
208         /* After servicing the interrupt, we have to remove the status
209          * indicator.
210          */
211         ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr = (1 << vec);
212         
213 }
214
215 /* The CPM can generate the error interrupt when there is a race condition
216  * between generating and masking interrupts.  All we have to do is ACK it
217  * and return.  This is a no-op function so we don't need any special
218  * tests in the interrupt handler.
219  */
220 static  void
221 cpm_error_interrupt(void *dev, struct pt_regs *regs)
222 {
223 }
224
225 /* Install a CPM interrupt handler.
226 */
227 void
228 cpm_install_handler(int vec, void (*handler)(void *, struct pt_regs *regs),
229                     void *dev_id)
230 {
231
232         /* If null handler, assume we are trying to free the IRQ.
233         */
234         if (!handler) {
235                 cpm_free_handler(vec);
236                 return;
237         }
238
239         if (cpm_vecs[vec].handler != 0)
240                 printk("CPM interrupt %x replacing %x\n",
241                         (uint)handler, (uint)cpm_vecs[vec].handler);
242         cpm_vecs[vec].handler = handler;
243         cpm_vecs[vec].dev_id = dev_id;
244         ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec);
245 }
246
247 /* Free a CPM interrupt handler.
248 */
249 void
250 cpm_free_handler(int vec)
251 {
252         cpm_vecs[vec].handler = NULL;
253         cpm_vecs[vec].dev_id = NULL;
254         ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec);
255 }
256
257 /* We also own one page of host buffer space for the allocation of
258  * UART "fifos" and the like.
259  */
260 uint
261 m8xx_cpm_hostalloc(uint size)
262 {
263         uint    retloc;
264
265 #if 1
266         if (host_buffer == 0)
267                 alloc_host_memory();
268 #endif
269
270         if ((host_buffer + size) >= host_end)
271                 return(0);
272
273         retloc = host_buffer;
274         host_buffer += size;
275
276         return(retloc);
277 }
278
279 /* Set a baud rate generator.  This needs lots of work.  There are
280  * four BRGs, any of which can be wired to any channel.
281  * The internal baud rate clock is the system clock divided by 16.
282  * This assumes the baudrate is 16x oversampled by the uart.
283  */
284 #define BRG_INT_CLK             (((bd_t *)__res)->bi_intfreq)
285 #define BRG_UART_CLK            (BRG_INT_CLK/16)
286 #define BRG_UART_CLK_DIV16      (BRG_UART_CLK/16)
287
288 void
289 cpm_setbrg(uint brg, uint rate)
290 {
291         volatile uint   *bp;
292
293         /* This is good enough to get SMCs running.....
294         */
295         bp = (uint *)&cpmp->cp_brgc1;
296         bp += brg;
297         /* The BRG has a 12-bit counter.  For really slow baud rates (or
298          * really fast processors), we may have to further divide by 16.
299          */
300         if (((BRG_UART_CLK / rate) - 1) < 4096)
301                 *bp = (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN;
302         else
303                 *bp = (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) |
304                                                 CPM_BRG_EN | CPM_BRG_DIV16;
305 }
306
307 /*
308  * dpalloc / dpfree bits.
309  */
310 static spinlock_t cpm_dpmem_lock;
311 /*
312  * 16 blocks should be enough to satisfy all requests
313  * until the memory subsystem goes up...
314  */
315 static rh_block_t cpm_boot_dpmem_rh_block[16];
316 static rh_info_t cpm_dpmem_info;
317
318 #define CPM_DPMEM_ALIGNMENT     8
319
320 void m8xx_cpm_dpinit(void)
321 {
322         cpm8xx_t *cp = &((immap_t *)IMAP_ADDR)->im_cpm;
323
324         spin_lock_init(&cpm_dpmem_lock);
325
326         /* Initialize the info header */
327         rh_init(&cpm_dpmem_info, CPM_DPMEM_ALIGNMENT,
328                         sizeof(cpm_boot_dpmem_rh_block) /
329                         sizeof(cpm_boot_dpmem_rh_block[0]),
330                         cpm_boot_dpmem_rh_block);
331
332         /*
333          * Attach the usable dpmem area.
334          * XXX: This is actually crap.  CPM_DATAONLY_BASE and
335          * CPM_DATAONLY_SIZE are a subset of the available dparm.  It varies
336          * with the processor and the microcode patches applied / activated.
337          * But the following should be at least safe.
338          */
339         rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE);
340 }
341
342 /*
343  * Allocate the requested size worth of DP memory.
344  * This function used to return an index into the DPRAM area.
345  * Now it returns the actuall physical address of that area.
346  * use m8xx_cpm_dpram_offset() to get the index
347  */
348 uint cpm_dpalloc(uint size, uint align)
349 {
350         void *start;
351         unsigned long flags;
352
353         spin_lock_irqsave(&cpm_dpmem_lock, flags);
354         cpm_dpmem_info.alignment = align;
355         start = rh_alloc(&cpm_dpmem_info, size, "commproc");
356         spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
357
358         return (uint)start;
359 }
360 EXPORT_SYMBOL(cpm_dpalloc);
361
362 int cpm_dpfree(uint offset)
363 {
364         int ret;
365         unsigned long flags;
366
367         spin_lock_irqsave(&cpm_dpmem_lock, flags);
368         ret = rh_free(&cpm_dpmem_info, (void *)offset);
369         spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
370
371         return ret;
372 }
373 EXPORT_SYMBOL(cpm_dpfree);
374
375 uint cpm_dpalloc_fixed(uint offset, uint size, uint align)
376 {
377         void *start;
378         unsigned long flags;
379
380         spin_lock_irqsave(&cpm_dpmem_lock, flags);
381         cpm_dpmem_info.alignment = align;
382         start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc");
383         spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
384
385         return (uint)start;
386 }
387 EXPORT_SYMBOL(cpm_dpalloc_fixed);
388
389 void cpm_dpdump(void)
390 {
391         rh_dump(&cpm_dpmem_info);
392 }
393 EXPORT_SYMBOL(cpm_dpdump);
394
395 void *cpm_dpram_addr(uint offset)
396 {
397         return ((immap_t *)IMAP_ADDR)->im_cpm.cp_dpmem + offset;
398 }
399 EXPORT_SYMBOL(cpm_dpram_addr);