cpm-serial.c (5312B)
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 17struct 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 32struct 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 42struct 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 65struct 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 71static void *cpcr; 72static struct cpm_param *param; 73static struct cpm_smc *smc; 74static struct cpm_scc *scc; 75static struct cpm_bd *tbdf, *rbdf; 76static u32 cpm_cmd; 77static void *cbd_addr; 78static u32 cbd_offset; 79 80static void (*do_cmd)(int op); 81static void (*enable_port)(void); 82static 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 88static 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 99static 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 110static 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 116static 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 122static 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 128static 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 134static 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 166static 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 178static unsigned char cpm_serial_tstc(void) 179{ 180 barrier(); 181 return !(rbdf->sc & 0x8000); 182} 183 184static 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 199int 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}