| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * General Purpose functions for the global management of the | 
 | 3 |  * 8260 Communication Processor Module. | 
 | 4 |  * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) | 
 | 5 |  * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) | 
 | 6 |  *	2.3.99 Updates | 
 | 7 |  * | 
 | 8 |  * In addition to the individual control of the communication | 
 | 9 |  * channels, there are a few functions that globally affect the | 
 | 10 |  * communication processor. | 
 | 11 |  * | 
 | 12 |  * Buffer descriptors must be allocated from the dual ported memory | 
 | 13 |  * space.  The allocator for that is here.  When the communication | 
 | 14 |  * process is reset, we reclaim the memory available.  There is | 
 | 15 |  * currently no deallocator for this memory. | 
 | 16 |  */ | 
 | 17 | #include <linux/errno.h> | 
 | 18 | #include <linux/sched.h> | 
 | 19 | #include <linux/kernel.h> | 
 | 20 | #include <linux/param.h> | 
 | 21 | #include <linux/string.h> | 
 | 22 | #include <linux/mm.h> | 
 | 23 | #include <linux/interrupt.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 24 | #include <linux/module.h> | 
| Kumar Gala | 2ec19fa | 2005-06-25 14:54:41 -0700 | [diff] [blame] | 25 | #include <asm/io.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 26 | #include <asm/irq.h> | 
 | 27 | #include <asm/mpc8260.h> | 
 | 28 | #include <asm/page.h> | 
 | 29 | #include <asm/pgtable.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | #include <asm/cpm2.h> | 
 | 31 | #include <asm/rheap.h> | 
 | 32 |  | 
 | 33 | static void cpm2_dpinit(void); | 
 | 34 | cpm_cpm2_t	*cpmp;		/* Pointer to comm processor space */ | 
 | 35 |  | 
 | 36 | /* We allocate this here because it is used almost exclusively for | 
 | 37 |  * the communication processor devices. | 
 | 38 |  */ | 
 | 39 | cpm2_map_t *cpm2_immr; | 
 | 40 |  | 
 | 41 | #define CPM_MAP_SIZE	(0x40000)	/* 256k - the PQ3 reserve this amount | 
 | 42 | 					   of space for CPM as it is larger | 
 | 43 | 					   than on PQ2 */ | 
 | 44 |  | 
 | 45 | void | 
 | 46 | cpm2_reset(void) | 
 | 47 | { | 
 | 48 | 	cpm2_immr = (cpm2_map_t *)ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE); | 
 | 49 |  | 
 | 50 | 	/* Reclaim the DP memory for our use. | 
 | 51 | 	 */ | 
 | 52 | 	cpm2_dpinit(); | 
 | 53 |  | 
 | 54 | 	/* Tell everyone where the comm processor resides. | 
 | 55 | 	 */ | 
 | 56 | 	cpmp = &cpm2_immr->im_cpm; | 
 | 57 | } | 
 | 58 |  | 
 | 59 | /* Set a baud rate generator.  This needs lots of work.  There are | 
 | 60 |  * eight BRGs, which can be connected to the CPM channels or output | 
 | 61 |  * as clocks.  The BRGs are in two different block of internal | 
 | 62 |  * memory mapped space. | 
 | 63 |  * The baud rate clock is the system clock divided by something. | 
 | 64 |  * It was set up long ago during the initial boot phase and is | 
 | 65 |  * is given to us. | 
 | 66 |  * Baud rate clocks are zero-based in the driver code (as that maps | 
 | 67 |  * to port numbers).  Documentation uses 1-based numbering. | 
 | 68 |  */ | 
 | 69 | #define BRG_INT_CLK	(((bd_t *)__res)->bi_brgfreq) | 
 | 70 | #define BRG_UART_CLK	(BRG_INT_CLK/16) | 
 | 71 |  | 
 | 72 | /* This function is used by UARTS, or anything else that uses a 16x | 
 | 73 |  * oversampled clock. | 
 | 74 |  */ | 
 | 75 | void | 
 | 76 | cpm_setbrg(uint brg, uint rate) | 
 | 77 | { | 
 | 78 | 	volatile uint	*bp; | 
 | 79 |  | 
 | 80 | 	/* This is good enough to get SMCs running..... | 
 | 81 | 	*/ | 
 | 82 | 	if (brg < 4) { | 
 | 83 | 		bp = (uint *)&cpm2_immr->im_brgc1; | 
 | 84 | 	} | 
 | 85 | 	else { | 
 | 86 | 		bp = (uint *)&cpm2_immr->im_brgc5; | 
 | 87 | 		brg -= 4; | 
 | 88 | 	} | 
 | 89 | 	bp += brg; | 
 | 90 | 	*bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN; | 
 | 91 | } | 
 | 92 |  | 
 | 93 | /* This function is used to set high speed synchronous baud rate | 
 | 94 |  * clocks. | 
 | 95 |  */ | 
 | 96 | void | 
 | 97 | cpm2_fastbrg(uint brg, uint rate, int div16) | 
 | 98 | { | 
 | 99 | 	volatile uint	*bp; | 
 | 100 |  | 
 | 101 | 	if (brg < 4) { | 
 | 102 | 		bp = (uint *)&cpm2_immr->im_brgc1; | 
 | 103 | 	} | 
 | 104 | 	else { | 
 | 105 | 		bp = (uint *)&cpm2_immr->im_brgc5; | 
 | 106 | 		brg -= 4; | 
 | 107 | 	} | 
 | 108 | 	bp += brg; | 
 | 109 | 	*bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; | 
 | 110 | 	if (div16) | 
 | 111 | 		*bp |= CPM_BRG_DIV16; | 
 | 112 | } | 
 | 113 |  | 
 | 114 | /* | 
 | 115 |  * dpalloc / dpfree bits. | 
 | 116 |  */ | 
 | 117 | static spinlock_t cpm_dpmem_lock; | 
 | 118 | /* 16 blocks should be enough to satisfy all requests | 
 | 119 |  * until the memory subsystem goes up... */ | 
 | 120 | static rh_block_t cpm_boot_dpmem_rh_block[16]; | 
 | 121 | static rh_info_t cpm_dpmem_info; | 
 | 122 |  | 
 | 123 | static void cpm2_dpinit(void) | 
 | 124 | { | 
 | 125 | 	spin_lock_init(&cpm_dpmem_lock); | 
 | 126 |  | 
 | 127 | 	/* initialize the info header */ | 
 | 128 | 	rh_init(&cpm_dpmem_info, 1, | 
 | 129 | 			sizeof(cpm_boot_dpmem_rh_block) / | 
 | 130 | 			sizeof(cpm_boot_dpmem_rh_block[0]), | 
 | 131 | 			cpm_boot_dpmem_rh_block); | 
 | 132 |  | 
 | 133 | 	/* Attach the usable dpmem area */ | 
 | 134 | 	/* XXX: This is actually crap. CPM_DATAONLY_BASE and | 
 | 135 | 	 * CPM_DATAONLY_SIZE is only a subset of the available dpram. It | 
 | 136 | 	 * varies with the processor and the microcode patches activated. | 
 | 137 | 	 * But the following should be at least safe. | 
 | 138 | 	 */ | 
 | 139 | 	rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE, | 
 | 140 | 			CPM_DATAONLY_SIZE); | 
 | 141 | } | 
 | 142 |  | 
 | 143 | /* This function returns an index into the DPRAM area. | 
 | 144 |  */ | 
 | 145 | uint cpm_dpalloc(uint size, uint align) | 
 | 146 | { | 
 | 147 | 	void *start; | 
 | 148 | 	unsigned long flags; | 
 | 149 |  | 
 | 150 | 	spin_lock_irqsave(&cpm_dpmem_lock, flags); | 
 | 151 | 	cpm_dpmem_info.alignment = align; | 
 | 152 | 	start = rh_alloc(&cpm_dpmem_info, size, "commproc"); | 
 | 153 | 	spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | 
 | 154 |  | 
 | 155 | 	return (uint)start; | 
 | 156 | } | 
 | 157 | EXPORT_SYMBOL(cpm_dpalloc); | 
 | 158 |  | 
 | 159 | int cpm_dpfree(uint offset) | 
 | 160 | { | 
 | 161 | 	int ret; | 
 | 162 | 	unsigned long flags; | 
 | 163 |  | 
 | 164 | 	spin_lock_irqsave(&cpm_dpmem_lock, flags); | 
 | 165 | 	ret = rh_free(&cpm_dpmem_info, (void *)offset); | 
 | 166 | 	spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | 
 | 167 |  | 
 | 168 | 	return ret; | 
 | 169 | } | 
 | 170 | EXPORT_SYMBOL(cpm_dpfree); | 
 | 171 |  | 
 | 172 | /* not sure if this is ever needed */ | 
 | 173 | uint cpm_dpalloc_fixed(uint offset, uint size, uint align) | 
 | 174 | { | 
 | 175 | 	void *start; | 
 | 176 | 	unsigned long flags; | 
 | 177 |  | 
 | 178 | 	spin_lock_irqsave(&cpm_dpmem_lock, flags); | 
 | 179 | 	cpm_dpmem_info.alignment = align; | 
 | 180 | 	start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc"); | 
 | 181 | 	spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | 
 | 182 |  | 
 | 183 | 	return (uint)start; | 
 | 184 | } | 
 | 185 | EXPORT_SYMBOL(cpm_dpalloc_fixed); | 
 | 186 |  | 
 | 187 | void cpm_dpdump(void) | 
 | 188 | { | 
 | 189 | 	rh_dump(&cpm_dpmem_info); | 
 | 190 | } | 
 | 191 | EXPORT_SYMBOL(cpm_dpdump); | 
 | 192 |  | 
 | 193 | void *cpm_dpram_addr(uint offset) | 
 | 194 | { | 
 | 195 | 	return (void *)&cpm2_immr->im_dprambase[offset]; | 
 | 196 | } | 
 | 197 | EXPORT_SYMBOL(cpm_dpram_addr); |