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