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 15 struct cpm_scc { 16 u32 gsmrl; 17 u32 gsmrh; 18 u16 psmr; 19 u8 res1[2]; 20 u16 todr; 21 u16 dsr; 22 u16 scce; 23 u8 res2[2]; 24 u16 sccm; 25 u8 res3; 26 u8 sccs; 27 u8 res4[8]; 28 }; 29 30 struct cpm_smc { 31 u8 res1[2]; 32 u16 smcmr; 33 u8 res2[2]; 34 u8 smce; 35 u8 res3[3]; 36 u8 smcm; 37 u8 res4[5]; 38 }; 39 40 struct cpm_param { 41 u16 rbase; 42 u16 tbase; 43 u8 rfcr; 44 u8 tfcr; 45 }; 46 47 struct cpm_bd { 48 u16 sc; /* Status and Control */ 49 u16 len; /* Data length in buffer */ 50 u8 *addr; /* Buffer address in host memory */ 51 }; 52 53 static void *cpcr; 54 static struct cpm_param *param; 55 static struct cpm_smc *smc; 56 static struct cpm_scc *scc; 57 struct cpm_bd *tbdf, *rbdf; 58 static u32 cpm_cmd; 59 static u8 *muram_start; 60 static u32 muram_offset; 61 62 static void (*do_cmd)(int op); 63 static void (*enable_port)(void); 64 static void (*disable_port)(void); 65 66 #define CPM_CMD_STOP_TX 4 67 #define CPM_CMD_RESTART_TX 6 68 #define CPM_CMD_INIT_RX_TX 0 69 70 static void cpm1_cmd(int op) 71 { 72 while (in_be16(cpcr) & 1) 73 ; 74 75 out_be16(cpcr, (op << 8) | cpm_cmd | 1); 76 77 while (in_be16(cpcr) & 1) 78 ; 79 } 80 81 static void cpm2_cmd(int op) 82 { 83 while (in_be32(cpcr) & 0x10000) 84 ; 85 86 out_be32(cpcr, op | cpm_cmd | 0x10000); 87 88 while (in_be32(cpcr) & 0x10000) 89 ; 90 } 91 92 static void smc_disable_port(void) 93 { 94 do_cmd(CPM_CMD_STOP_TX); 95 out_be16(&smc->smcmr, in_be16(&smc->smcmr) & ~3); 96 } 97 98 static void scc_disable_port(void) 99 { 100 do_cmd(CPM_CMD_STOP_TX); 101 out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) & ~0x30); 102 } 103 104 static void smc_enable_port(void) 105 { 106 out_be16(&smc->smcmr, in_be16(&smc->smcmr) | 3); 107 do_cmd(CPM_CMD_RESTART_TX); 108 } 109 110 static void scc_enable_port(void) 111 { 112 out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) | 0x30); 113 do_cmd(CPM_CMD_RESTART_TX); 114 } 115 116 static int cpm_serial_open(void) 117 { 118 disable_port(); 119 120 out_8(¶m->rfcr, 0x10); 121 out_8(¶m->tfcr, 0x10); 122 123 rbdf = (struct cpm_bd *)muram_start; 124 rbdf->addr = (u8 *)(rbdf + 2); 125 rbdf->sc = 0xa000; 126 rbdf->len = 1; 127 128 tbdf = rbdf + 1; 129 tbdf->addr = (u8 *)(rbdf + 2) + 1; 130 tbdf->sc = 0x2000; 131 tbdf->len = 1; 132 133 sync(); 134 out_be16(¶m->rbase, muram_offset); 135 out_be16(¶m->tbase, muram_offset + sizeof(struct cpm_bd)); 136 137 do_cmd(CPM_CMD_INIT_RX_TX); 138 139 enable_port(); 140 return 0; 141 } 142 143 static void cpm_serial_putc(unsigned char c) 144 { 145 while (tbdf->sc & 0x8000) 146 barrier(); 147 148 sync(); 149 150 tbdf->addr[0] = c; 151 eieio(); 152 tbdf->sc |= 0x8000; 153 } 154 155 static unsigned char cpm_serial_tstc(void) 156 { 157 barrier(); 158 return !(rbdf->sc & 0x8000); 159 } 160 161 static unsigned char cpm_serial_getc(void) 162 { 163 unsigned char c; 164 165 while (!cpm_serial_tstc()) 166 ; 167 168 sync(); 169 c = rbdf->addr[0]; 170 eieio(); 171 rbdf->sc |= 0x8000; 172 173 return c; 174 } 175 176 int cpm_console_init(void *devp, struct serial_console_data *scdp) 177 { 178 void *reg_virt[2]; 179 int is_smc = 0, is_cpm2 = 0, n; 180 unsigned long reg_phys; 181 void *parent, *muram; 182 183 if (dt_is_compatible(devp, "fsl,cpm1-smc-uart")) { 184 is_smc = 1; 185 } else if (dt_is_compatible(devp, "fsl,cpm2-scc-uart")) { 186 is_cpm2 = 1; 187 } else if (dt_is_compatible(devp, "fsl,cpm2-smc-uart")) { 188 is_cpm2 = 1; 189 is_smc = 1; 190 } 191 192 if (is_smc) { 193 enable_port = smc_enable_port; 194 disable_port = smc_disable_port; 195 } else { 196 enable_port = scc_enable_port; 197 disable_port = scc_disable_port; 198 } 199 200 if (is_cpm2) 201 do_cmd = cpm2_cmd; 202 else 203 do_cmd = cpm1_cmd; 204 205 n = getprop(devp, "fsl,cpm-command", &cpm_cmd, 4); 206 if (n < 4) 207 return -1; 208 209 n = getprop(devp, "virtual-reg", reg_virt, sizeof(reg_virt)); 210 if (n < (int)sizeof(reg_virt)) { 211 for (n = 0; n < 2; n++) { 212 if (!dt_xlate_reg(devp, n, ®_phys, NULL)) 213 return -1; 214 215 reg_virt[n] = (void *)reg_phys; 216 } 217 } 218 219 if (is_smc) 220 smc = reg_virt[0]; 221 else 222 scc = reg_virt[0]; 223 224 param = reg_virt[1]; 225 226 parent = get_parent(devp); 227 if (!parent) 228 return -1; 229 230 n = getprop(parent, "virtual-reg", reg_virt, sizeof(reg_virt)); 231 if (n < (int)sizeof(reg_virt)) { 232 if (!dt_xlate_reg(parent, 0, ®_phys, NULL)) 233 return -1; 234 235 reg_virt[0] = (void *)reg_phys; 236 } 237 238 cpcr = reg_virt[0]; 239 240 muram = finddevice("/soc/cpm/muram/data"); 241 if (!muram) 242 return -1; 243 244 /* For bootwrapper-compatible device trees, we assume that the first 245 * entry has at least 18 bytes, and that #address-cells/#data-cells 246 * is one for both parent and child. 247 */ 248 249 n = getprop(muram, "virtual-reg", reg_virt, sizeof(reg_virt)); 250 if (n < (int)sizeof(reg_virt)) { 251 if (!dt_xlate_reg(muram, 0, ®_phys, NULL)) 252 return -1; 253 254 reg_virt[0] = (void *)reg_phys; 255 } 256 257 muram_start = reg_virt[0]; 258 259 n = getprop(muram, "reg", &muram_offset, 4); 260 if (n < 4) 261 return -1; 262 263 scdp->open = cpm_serial_open; 264 scdp->putc = cpm_serial_putc; 265 scdp->getc = cpm_serial_getc; 266 scdp->tstc = cpm_serial_tstc; 267 268 return 0; 269 } 270