1 /* 2 * CPM serial console support. 3 * 4 * Copyright 2007 Freescale Semiconductor, Inc. 5 * Author: Scott Wood <scottwood@freescale.com> 6 * 7 * It is assumed that the firmware (or the platform file) has already set 8 * up the port. 9 */ 10 11 #include "types.h" 12 #include "io.h" 13 #include "ops.h" 14 #include "page.h" 15 16 struct cpm_scc { 17 u32 gsmrl; 18 u32 gsmrh; 19 u16 psmr; 20 u8 res1[2]; 21 u16 todr; 22 u16 dsr; 23 u16 scce; 24 u8 res2[2]; 25 u16 sccm; 26 u8 res3; 27 u8 sccs; 28 u8 res4[8]; 29 }; 30 31 struct cpm_smc { 32 u8 res1[2]; 33 u16 smcmr; 34 u8 res2[2]; 35 u8 smce; 36 u8 res3[3]; 37 u8 smcm; 38 u8 res4[5]; 39 }; 40 41 struct cpm_param { 42 u16 rbase; 43 u16 tbase; 44 u8 rfcr; 45 u8 tfcr; 46 u16 mrblr; 47 u32 rstate; 48 u8 res1[4]; 49 u16 rbptr; 50 u8 res2[6]; 51 u32 tstate; 52 u8 res3[4]; 53 u16 tbptr; 54 u8 res4[6]; 55 u16 maxidl; 56 u16 idlc; 57 u16 brkln; 58 u16 brkec; 59 u16 brkcr; 60 u16 rmask; 61 u8 res5[4]; 62 }; 63 64 struct cpm_bd { 65 u16 sc; /* Status and Control */ 66 u16 len; /* Data length in buffer */ 67 u8 *addr; /* Buffer address in host memory */ 68 }; 69 70 static void *cpcr; 71 static struct cpm_param *param; 72 static struct cpm_smc *smc; 73 static struct cpm_scc *scc; 74 static struct cpm_bd *tbdf, *rbdf; 75 static u32 cpm_cmd; 76 static void *cbd_addr; 77 static u32 cbd_offset; 78 79 static void (*do_cmd)(int op); 80 static void (*enable_port)(void); 81 static void (*disable_port)(void); 82 83 #define CPM_CMD_STOP_TX 4 84 #define CPM_CMD_RESTART_TX 6 85 #define CPM_CMD_INIT_RX_TX 0 86 87 static void cpm1_cmd(int op) 88 { 89 while (in_be16(cpcr) & 1) 90 ; 91 92 out_be16(cpcr, (op << 8) | cpm_cmd | 1); 93 94 while (in_be16(cpcr) & 1) 95 ; 96 } 97 98 static void cpm2_cmd(int op) 99 { 100 while (in_be32(cpcr) & 0x10000) 101 ; 102 103 out_be32(cpcr, op | cpm_cmd | 0x10000); 104 105 while (in_be32(cpcr) & 0x10000) 106 ; 107 } 108 109 static void smc_disable_port(void) 110 { 111 do_cmd(CPM_CMD_STOP_TX); 112 out_be16(&smc->smcmr, in_be16(&smc->smcmr) & ~3); 113 } 114 115 static void scc_disable_port(void) 116 { 117 do_cmd(CPM_CMD_STOP_TX); 118 out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) & ~0x30); 119 } 120 121 static void smc_enable_port(void) 122 { 123 out_be16(&smc->smcmr, in_be16(&smc->smcmr) | 3); 124 do_cmd(CPM_CMD_RESTART_TX); 125 } 126 127 static void scc_enable_port(void) 128 { 129 out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) | 0x30); 130 do_cmd(CPM_CMD_RESTART_TX); 131 } 132 133 static int cpm_serial_open(void) 134 { 135 disable_port(); 136 137 out_8(¶m->rfcr, 0x10); 138 out_8(¶m->tfcr, 0x10); 139 out_be16(¶m->mrblr, 1); 140 out_be16(¶m->maxidl, 0); 141 out_be16(¶m->brkec, 0); 142 out_be16(¶m->brkln, 0); 143 out_be16(¶m->brkcr, 0); 144 145 rbdf = cbd_addr; 146 rbdf->addr = (u8 *)rbdf - 1; 147 rbdf->sc = 0xa000; 148 rbdf->len = 1; 149 150 tbdf = rbdf + 1; 151 tbdf->addr = (u8 *)rbdf - 2; 152 tbdf->sc = 0x2000; 153 tbdf->len = 1; 154 155 sync(); 156 out_be16(¶m->rbase, cbd_offset); 157 out_be16(¶m->tbase, cbd_offset + sizeof(struct cpm_bd)); 158 159 do_cmd(CPM_CMD_INIT_RX_TX); 160 161 enable_port(); 162 return 0; 163 } 164 165 static void cpm_serial_putc(unsigned char c) 166 { 167 while (tbdf->sc & 0x8000) 168 barrier(); 169 170 sync(); 171 172 tbdf->addr[0] = c; 173 eieio(); 174 tbdf->sc |= 0x8000; 175 } 176 177 static unsigned char cpm_serial_tstc(void) 178 { 179 barrier(); 180 return !(rbdf->sc & 0x8000); 181 } 182 183 static unsigned char cpm_serial_getc(void) 184 { 185 unsigned char c; 186 187 while (!cpm_serial_tstc()) 188 ; 189 190 sync(); 191 c = rbdf->addr[0]; 192 eieio(); 193 rbdf->sc |= 0x8000; 194 195 return c; 196 } 197 198 int cpm_console_init(void *devp, struct serial_console_data *scdp) 199 { 200 void *vreg[2]; 201 u32 reg[2]; 202 int is_smc = 0, is_cpm2 = 0; 203 void *parent, *muram; 204 void *muram_addr; 205 unsigned long muram_offset, muram_size; 206 207 if (dt_is_compatible(devp, "fsl,cpm1-smc-uart")) { 208 is_smc = 1; 209 } else if (dt_is_compatible(devp, "fsl,cpm2-scc-uart")) { 210 is_cpm2 = 1; 211 } else if (dt_is_compatible(devp, "fsl,cpm2-smc-uart")) { 212 is_cpm2 = 1; 213 is_smc = 1; 214 } 215 216 if (is_smc) { 217 enable_port = smc_enable_port; 218 disable_port = smc_disable_port; 219 } else { 220 enable_port = scc_enable_port; 221 disable_port = scc_disable_port; 222 } 223 224 if (is_cpm2) 225 do_cmd = cpm2_cmd; 226 else 227 do_cmd = cpm1_cmd; 228 229 if (getprop(devp, "fsl,cpm-command", &cpm_cmd, 4) < 4) 230 return -1; 231 232 if (dt_get_virtual_reg(devp, vreg, 2) < 2) 233 return -1; 234 235 if (is_smc) 236 smc = vreg[0]; 237 else 238 scc = vreg[0]; 239 240 param = vreg[1]; 241 242 parent = get_parent(devp); 243 if (!parent) 244 return -1; 245 246 if (dt_get_virtual_reg(parent, &cpcr, 1) < 1) 247 return -1; 248 249 muram = finddevice("/soc/cpm/muram/data"); 250 if (!muram) 251 return -1; 252 253 /* For bootwrapper-compatible device trees, we assume that the first 254 * entry has at least 128 bytes, and that #address-cells/#data-cells 255 * is one for both parent and child. 256 */ 257 258 if (dt_get_virtual_reg(muram, &muram_addr, 1) < 1) 259 return -1; 260 261 if (getprop(muram, "reg", reg, 8) < 8) 262 return -1; 263 264 muram_offset = reg[0]; 265 muram_size = reg[1]; 266 267 /* Store the buffer descriptors at the end of the first muram chunk. 268 * For SMC ports on CPM2-based platforms, relocate the parameter RAM 269 * just before the buffer descriptors. 270 */ 271 272 cbd_offset = muram_offset + muram_size - 2 * sizeof(struct cpm_bd); 273 274 if (is_cpm2 && is_smc) { 275 u16 *smc_base = (u16 *)param; 276 u16 pram_offset; 277 278 pram_offset = cbd_offset - 64; 279 pram_offset = _ALIGN_DOWN(pram_offset, 64); 280 281 disable_port(); 282 out_be16(smc_base, pram_offset); 283 param = muram_addr - muram_offset + pram_offset; 284 } 285 286 cbd_addr = muram_addr - muram_offset + cbd_offset; 287 288 scdp->open = cpm_serial_open; 289 scdp->putc = cpm_serial_putc; 290 scdp->getc = cpm_serial_getc; 291 scdp->tstc = cpm_serial_tstc; 292 293 return 0; 294 } 295