Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* Minimal serial functions needed to send messages out the serial |
| 2 | * port on SMC1. |
| 3 | */ |
| 4 | #include <linux/types.h> |
| 5 | #include <asm/mpc8260.h> |
| 6 | #include <asm/cpm2.h> |
| 7 | #include <asm/immap_cpm2.h> |
| 8 | |
| 9 | uint no_print; |
| 10 | extern char *params[]; |
| 11 | extern int nparams; |
| 12 | static u_char cons_hold[128], *sgptr; |
| 13 | static int cons_hold_cnt; |
| 14 | |
| 15 | /* If defined, enables serial console. The value (1 through 4) |
| 16 | * should designate which SCC is used, but this isn't complete. Only |
| 17 | * SCC1 is known to work at this time. |
| 18 | * We're only linked if SERIAL_CPM_CONSOLE=y, so we only need to test |
| 19 | * SERIAL_CPM_SCC1. |
| 20 | */ |
| 21 | #ifdef CONFIG_SERIAL_CPM_SCC1 |
| 22 | #define SCC_CONSOLE 1 |
| 23 | #endif |
| 24 | |
| 25 | unsigned long |
| 26 | serial_init(int ignored, bd_t *bd) |
| 27 | { |
| 28 | #ifdef SCC_CONSOLE |
| 29 | volatile scc_t *sccp; |
| 30 | volatile scc_uart_t *sup; |
| 31 | #else |
| 32 | volatile smc_t *sp; |
| 33 | volatile smc_uart_t *up; |
| 34 | #endif |
| 35 | volatile cbd_t *tbdf, *rbdf; |
| 36 | volatile cpm2_map_t *ip; |
| 37 | volatile iop_cpm2_t *io; |
| 38 | volatile cpm_cpm2_t *cp; |
| 39 | uint dpaddr, memaddr; |
| 40 | |
| 41 | ip = (cpm2_map_t *)CPM_MAP_ADDR; |
| 42 | cp = &ip->im_cpm; |
| 43 | io = &ip->im_ioport; |
| 44 | |
| 45 | /* Perform a reset. |
| 46 | */ |
| 47 | cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); |
| 48 | |
| 49 | /* Wait for it. |
| 50 | */ |
| 51 | while (cp->cp_cpcr & CPM_CR_FLG); |
| 52 | |
| 53 | #ifdef CONFIG_ADS8260 |
| 54 | /* Enable the RS-232 transceivers. |
| 55 | */ |
| 56 | *(volatile uint *)(BCSR_ADDR + 4) &= |
| 57 | ~(BCSR1_RS232_EN1 | BCSR1_RS232_EN2); |
| 58 | #endif |
| 59 | |
| 60 | #ifdef SCC_CONSOLE |
| 61 | sccp = (scc_t *)&(ip->im_scc[SCC_CONSOLE-1]); |
| 62 | sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; |
| 63 | sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); |
| 64 | sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); |
| 65 | |
| 66 | /* Use Port D for SCC1 instead of other functions. |
| 67 | */ |
| 68 | io->iop_ppard |= 0x00000003; |
| 69 | io->iop_psord &= ~0x00000001; /* Rx */ |
| 70 | io->iop_psord |= 0x00000002; /* Tx */ |
| 71 | io->iop_pdird &= ~0x00000001; /* Rx */ |
| 72 | io->iop_pdird |= 0x00000002; /* Tx */ |
| 73 | |
| 74 | #else |
| 75 | sp = (smc_t*)&(ip->im_smc[0]); |
| 76 | *(ushort *)(&ip->im_dprambase[PROFF_SMC1_BASE]) = PROFF_SMC1; |
| 77 | up = (smc_uart_t *)&ip->im_dprambase[PROFF_SMC1]; |
| 78 | |
| 79 | /* Disable transmitter/receiver. |
| 80 | */ |
| 81 | sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); |
| 82 | |
| 83 | /* Use Port D for SMC1 instead of other functions. |
| 84 | */ |
| 85 | io->iop_ppard |= 0x00c00000; |
| 86 | io->iop_pdird |= 0x00400000; |
| 87 | io->iop_pdird &= ~0x00800000; |
| 88 | io->iop_psord &= ~0x00c00000; |
| 89 | #endif |
| 90 | |
| 91 | /* Allocate space for two buffer descriptors in the DP ram. |
| 92 | * For now, this address seems OK, but it may have to |
| 93 | * change with newer versions of the firmware. |
| 94 | */ |
| 95 | dpaddr = 0x0800; |
| 96 | |
| 97 | /* Grab a few bytes from the top of memory. |
| 98 | */ |
| 99 | memaddr = (bd->bi_memsize - 256) & ~15; |
| 100 | |
| 101 | /* Set the physical address of the host memory buffers in |
| 102 | * the buffer descriptors. |
| 103 | */ |
| 104 | rbdf = (cbd_t *)&ip->im_dprambase[dpaddr]; |
| 105 | rbdf->cbd_bufaddr = memaddr; |
| 106 | rbdf->cbd_sc = 0; |
| 107 | tbdf = rbdf + 1; |
| 108 | tbdf->cbd_bufaddr = memaddr+128; |
| 109 | tbdf->cbd_sc = 0; |
| 110 | |
| 111 | /* Set up the uart parameters in the parameter ram. |
| 112 | */ |
| 113 | #ifdef SCC_CONSOLE |
| 114 | sup->scc_genscc.scc_rbase = dpaddr; |
| 115 | sup->scc_genscc.scc_tbase = dpaddr + sizeof(cbd_t); |
| 116 | |
| 117 | /* Set up the uart parameters in the |
| 118 | * parameter ram. |
| 119 | */ |
| 120 | sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; |
| 121 | sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; |
| 122 | |
| 123 | sup->scc_genscc.scc_mrblr = 128; |
| 124 | sup->scc_maxidl = 8; |
| 125 | sup->scc_brkcr = 1; |
| 126 | sup->scc_parec = 0; |
| 127 | sup->scc_frmec = 0; |
| 128 | sup->scc_nosec = 0; |
| 129 | sup->scc_brkec = 0; |
| 130 | sup->scc_uaddr1 = 0; |
| 131 | sup->scc_uaddr2 = 0; |
| 132 | sup->scc_toseq = 0; |
| 133 | sup->scc_char1 = 0x8000; |
| 134 | sup->scc_char2 = 0x8000; |
| 135 | sup->scc_char3 = 0x8000; |
| 136 | sup->scc_char4 = 0x8000; |
| 137 | sup->scc_char5 = 0x8000; |
| 138 | sup->scc_char6 = 0x8000; |
| 139 | sup->scc_char7 = 0x8000; |
| 140 | sup->scc_char8 = 0x8000; |
| 141 | sup->scc_rccm = 0xc0ff; |
| 142 | |
| 143 | /* Send the CPM an initialize command. |
| 144 | */ |
| 145 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0, |
| 146 | CPM_CR_INIT_TRX) | CPM_CR_FLG; |
| 147 | while (cp->cp_cpcr & CPM_CR_FLG); |
| 148 | |
| 149 | /* Set UART mode, 8 bit, no parity, one stop. |
| 150 | * Enable receive and transmit. |
| 151 | */ |
| 152 | sccp->scc_gsmrh = 0; |
| 153 | sccp->scc_gsmrl = |
| 154 | (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); |
| 155 | |
| 156 | /* Disable all interrupts and clear all pending |
| 157 | * events. |
| 158 | */ |
| 159 | sccp->scc_sccm = 0; |
| 160 | sccp->scc_scce = 0xffff; |
| 161 | sccp->scc_dsr = 0x7e7e; |
| 162 | sccp->scc_psmr = 0x3000; |
| 163 | |
| 164 | /* Wire BRG1 to SCC1. The console driver will take care of |
| 165 | * others. |
| 166 | */ |
| 167 | ip->im_cpmux.cmx_scr = 0; |
| 168 | #else |
| 169 | up->smc_rbase = dpaddr; |
| 170 | up->smc_tbase = dpaddr+sizeof(cbd_t); |
| 171 | up->smc_rfcr = CPMFCR_EB; |
| 172 | up->smc_tfcr = CPMFCR_EB; |
| 173 | up->smc_brklen = 0; |
| 174 | up->smc_brkec = 0; |
| 175 | up->smc_brkcr = 0; |
| 176 | up->smc_mrblr = 128; |
| 177 | up->smc_maxidl = 8; |
| 178 | |
| 179 | /* Set UART mode, 8 bit, no parity, one stop. |
| 180 | * Enable receive and transmit. |
| 181 | */ |
| 182 | sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; |
| 183 | |
| 184 | /* Mask all interrupts and remove anything pending. |
| 185 | */ |
| 186 | sp->smc_smcm = 0; |
| 187 | sp->smc_smce = 0xff; |
| 188 | |
| 189 | /* Set up the baud rate generator. |
| 190 | */ |
| 191 | ip->im_cpmux.cmx_smr = 0; |
| 192 | #endif |
| 193 | |
| 194 | /* The baud rate divisor needs to be coordinated with clk_8260(). |
| 195 | */ |
| 196 | ip->im_brgc1 = |
| 197 | (((bd->bi_brgfreq/16) / bd->bi_baudrate) << 1) | |
| 198 | CPM_BRG_EN; |
| 199 | |
| 200 | /* Make the first buffer the only buffer. |
| 201 | */ |
| 202 | tbdf->cbd_sc |= BD_SC_WRAP; |
| 203 | rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; |
| 204 | |
| 205 | /* Initialize Tx/Rx parameters. |
| 206 | */ |
| 207 | #ifdef SCC_CONSOLE |
| 208 | sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); |
| 209 | #else |
| 210 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; |
| 211 | while (cp->cp_cpcr & CPM_CR_FLG); |
| 212 | |
| 213 | /* Enable transmitter/receiver. |
| 214 | */ |
| 215 | sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; |
| 216 | #endif |
| 217 | |
| 218 | /* This is ignored. |
| 219 | */ |
| 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | int |
| 224 | serial_readbuf(u_char *cbuf) |
| 225 | { |
| 226 | volatile cbd_t *rbdf; |
| 227 | volatile char *buf; |
| 228 | #ifdef SCC_CONSOLE |
| 229 | volatile scc_uart_t *sup; |
| 230 | #else |
| 231 | volatile smc_uart_t *up; |
| 232 | #endif |
| 233 | volatile cpm2_map_t *ip; |
| 234 | int i, nc; |
| 235 | |
| 236 | ip = (cpm2_map_t *)CPM_MAP_ADDR; |
| 237 | |
| 238 | #ifdef SCC_CONSOLE |
| 239 | sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; |
| 240 | rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; |
| 241 | #else |
| 242 | up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); |
| 243 | rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; |
| 244 | #endif |
| 245 | |
| 246 | /* Wait for character to show up. |
| 247 | */ |
| 248 | buf = (char *)rbdf->cbd_bufaddr; |
| 249 | while (rbdf->cbd_sc & BD_SC_EMPTY); |
| 250 | nc = rbdf->cbd_datlen; |
| 251 | for (i=0; i<nc; i++) |
| 252 | *cbuf++ = *buf++; |
| 253 | rbdf->cbd_sc |= BD_SC_EMPTY; |
| 254 | |
| 255 | return(nc); |
| 256 | } |
| 257 | |
| 258 | void |
| 259 | serial_putc(void *ignored, const char c) |
| 260 | { |
| 261 | volatile cbd_t *tbdf; |
| 262 | volatile char *buf; |
| 263 | #ifdef SCC_CONSOLE |
| 264 | volatile scc_uart_t *sup; |
| 265 | #else |
| 266 | volatile smc_uart_t *up; |
| 267 | #endif |
| 268 | volatile cpm2_map_t *ip; |
| 269 | |
| 270 | ip = (cpm2_map_t *)CPM_MAP_ADDR; |
| 271 | #ifdef SCC_CONSOLE |
| 272 | sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; |
| 273 | tbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_tbase]; |
| 274 | #else |
| 275 | up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); |
| 276 | tbdf = (cbd_t *)&ip->im_dprambase[up->smc_tbase]; |
| 277 | #endif |
| 278 | |
| 279 | /* Wait for last character to go. |
| 280 | */ |
| 281 | buf = (char *)tbdf->cbd_bufaddr; |
| 282 | while (tbdf->cbd_sc & BD_SC_READY); |
| 283 | |
| 284 | *buf = c; |
| 285 | tbdf->cbd_datlen = 1; |
| 286 | tbdf->cbd_sc |= BD_SC_READY; |
| 287 | } |
| 288 | |
| 289 | char |
| 290 | serial_getc(void *ignored) |
| 291 | { |
| 292 | char c; |
| 293 | |
| 294 | if (cons_hold_cnt <= 0) { |
| 295 | cons_hold_cnt = serial_readbuf(cons_hold); |
| 296 | sgptr = cons_hold; |
| 297 | } |
| 298 | c = *sgptr++; |
| 299 | cons_hold_cnt--; |
| 300 | |
| 301 | return(c); |
| 302 | } |
| 303 | |
| 304 | int |
| 305 | serial_tstc(void *ignored) |
| 306 | { |
| 307 | volatile cbd_t *rbdf; |
| 308 | #ifdef SCC_CONSOLE |
| 309 | volatile scc_uart_t *sup; |
| 310 | #else |
| 311 | volatile smc_uart_t *up; |
| 312 | #endif |
| 313 | volatile cpm2_map_t *ip; |
| 314 | |
| 315 | ip = (cpm2_map_t *)CPM_MAP_ADDR; |
| 316 | #ifdef SCC_CONSOLE |
| 317 | sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; |
| 318 | rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; |
| 319 | #else |
| 320 | up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); |
| 321 | rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; |
| 322 | #endif |
| 323 | |
| 324 | return(!(rbdf->cbd_sc & BD_SC_EMPTY)); |
| 325 | } |