162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CPM serial console support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * Author: Scott Wood <scottwood@freescale.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * It is assumed that the firmware (or the platform file) has already set 962306a36Sopenharmony_ci * up the port. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "types.h" 1362306a36Sopenharmony_ci#include "io.h" 1462306a36Sopenharmony_ci#include "ops.h" 1562306a36Sopenharmony_ci#include "page.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct cpm_scc { 1862306a36Sopenharmony_ci u32 gsmrl; 1962306a36Sopenharmony_ci u32 gsmrh; 2062306a36Sopenharmony_ci u16 psmr; 2162306a36Sopenharmony_ci u8 res1[2]; 2262306a36Sopenharmony_ci u16 todr; 2362306a36Sopenharmony_ci u16 dsr; 2462306a36Sopenharmony_ci u16 scce; 2562306a36Sopenharmony_ci u8 res2[2]; 2662306a36Sopenharmony_ci u16 sccm; 2762306a36Sopenharmony_ci u8 res3; 2862306a36Sopenharmony_ci u8 sccs; 2962306a36Sopenharmony_ci u8 res4[8]; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct cpm_smc { 3362306a36Sopenharmony_ci u8 res1[2]; 3462306a36Sopenharmony_ci u16 smcmr; 3562306a36Sopenharmony_ci u8 res2[2]; 3662306a36Sopenharmony_ci u8 smce; 3762306a36Sopenharmony_ci u8 res3[3]; 3862306a36Sopenharmony_ci u8 smcm; 3962306a36Sopenharmony_ci u8 res4[5]; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct cpm_param { 4362306a36Sopenharmony_ci u16 rbase; 4462306a36Sopenharmony_ci u16 tbase; 4562306a36Sopenharmony_ci u8 rfcr; 4662306a36Sopenharmony_ci u8 tfcr; 4762306a36Sopenharmony_ci u16 mrblr; 4862306a36Sopenharmony_ci u32 rstate; 4962306a36Sopenharmony_ci u8 res1[4]; 5062306a36Sopenharmony_ci u16 rbptr; 5162306a36Sopenharmony_ci u8 res2[6]; 5262306a36Sopenharmony_ci u32 tstate; 5362306a36Sopenharmony_ci u8 res3[4]; 5462306a36Sopenharmony_ci u16 tbptr; 5562306a36Sopenharmony_ci u8 res4[6]; 5662306a36Sopenharmony_ci u16 maxidl; 5762306a36Sopenharmony_ci u16 idlc; 5862306a36Sopenharmony_ci u16 brkln; 5962306a36Sopenharmony_ci u16 brkec; 6062306a36Sopenharmony_ci u16 brkcr; 6162306a36Sopenharmony_ci u16 rmask; 6262306a36Sopenharmony_ci u8 res5[4]; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct cpm_bd { 6662306a36Sopenharmony_ci u16 sc; /* Status and Control */ 6762306a36Sopenharmony_ci u16 len; /* Data length in buffer */ 6862306a36Sopenharmony_ci u8 *addr; /* Buffer address in host memory */ 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void *cpcr; 7262306a36Sopenharmony_cistatic struct cpm_param *param; 7362306a36Sopenharmony_cistatic struct cpm_smc *smc; 7462306a36Sopenharmony_cistatic struct cpm_scc *scc; 7562306a36Sopenharmony_cistatic struct cpm_bd *tbdf, *rbdf; 7662306a36Sopenharmony_cistatic u32 cpm_cmd; 7762306a36Sopenharmony_cistatic void *cbd_addr; 7862306a36Sopenharmony_cistatic u32 cbd_offset; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void (*do_cmd)(int op); 8162306a36Sopenharmony_cistatic void (*enable_port)(void); 8262306a36Sopenharmony_cistatic void (*disable_port)(void); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define CPM_CMD_STOP_TX 4 8562306a36Sopenharmony_ci#define CPM_CMD_RESTART_TX 6 8662306a36Sopenharmony_ci#define CPM_CMD_INIT_RX_TX 0 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void cpm1_cmd(int op) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci while (in_be16(cpcr) & 1) 9162306a36Sopenharmony_ci ; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci out_be16(cpcr, (op << 8) | cpm_cmd | 1); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci while (in_be16(cpcr) & 1) 9662306a36Sopenharmony_ci ; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void cpm2_cmd(int op) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci while (in_be32(cpcr) & 0x10000) 10262306a36Sopenharmony_ci ; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci out_be32(cpcr, op | cpm_cmd | 0x10000); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci while (in_be32(cpcr) & 0x10000) 10762306a36Sopenharmony_ci ; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void smc_disable_port(void) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci do_cmd(CPM_CMD_STOP_TX); 11362306a36Sopenharmony_ci out_be16(&smc->smcmr, in_be16(&smc->smcmr) & ~3); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void scc_disable_port(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci do_cmd(CPM_CMD_STOP_TX); 11962306a36Sopenharmony_ci out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) & ~0x30); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void smc_enable_port(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci out_be16(&smc->smcmr, in_be16(&smc->smcmr) | 3); 12562306a36Sopenharmony_ci do_cmd(CPM_CMD_RESTART_TX); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void scc_enable_port(void) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) | 0x30); 13162306a36Sopenharmony_ci do_cmd(CPM_CMD_RESTART_TX); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int cpm_serial_open(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci disable_port(); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci out_8(¶m->rfcr, 0x10); 13962306a36Sopenharmony_ci out_8(¶m->tfcr, 0x10); 14062306a36Sopenharmony_ci out_be16(¶m->mrblr, 1); 14162306a36Sopenharmony_ci out_be16(¶m->maxidl, 0); 14262306a36Sopenharmony_ci out_be16(¶m->brkec, 0); 14362306a36Sopenharmony_ci out_be16(¶m->brkln, 0); 14462306a36Sopenharmony_ci out_be16(¶m->brkcr, 0); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci rbdf = cbd_addr; 14762306a36Sopenharmony_ci rbdf->addr = (u8 *)rbdf - 1; 14862306a36Sopenharmony_ci rbdf->sc = 0xa000; 14962306a36Sopenharmony_ci rbdf->len = 1; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci tbdf = rbdf + 1; 15262306a36Sopenharmony_ci tbdf->addr = (u8 *)rbdf - 2; 15362306a36Sopenharmony_ci tbdf->sc = 0x2000; 15462306a36Sopenharmony_ci tbdf->len = 1; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci sync(); 15762306a36Sopenharmony_ci out_be16(¶m->rbase, cbd_offset); 15862306a36Sopenharmony_ci out_be16(¶m->tbase, cbd_offset + sizeof(struct cpm_bd)); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci do_cmd(CPM_CMD_INIT_RX_TX); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci enable_port(); 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void cpm_serial_putc(unsigned char c) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci while (tbdf->sc & 0x8000) 16962306a36Sopenharmony_ci barrier(); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci sync(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci tbdf->addr[0] = c; 17462306a36Sopenharmony_ci eieio(); 17562306a36Sopenharmony_ci tbdf->sc |= 0x8000; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic unsigned char cpm_serial_tstc(void) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci barrier(); 18162306a36Sopenharmony_ci return !(rbdf->sc & 0x8000); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic unsigned char cpm_serial_getc(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci unsigned char c; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci while (!cpm_serial_tstc()) 18962306a36Sopenharmony_ci ; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci sync(); 19262306a36Sopenharmony_ci c = rbdf->addr[0]; 19362306a36Sopenharmony_ci eieio(); 19462306a36Sopenharmony_ci rbdf->sc |= 0x8000; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return c; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint cpm_console_init(void *devp, struct serial_console_data *scdp) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci void *vreg[2]; 20262306a36Sopenharmony_ci u32 reg[2]; 20362306a36Sopenharmony_ci int is_smc = 0, is_cpm2 = 0; 20462306a36Sopenharmony_ci void *parent, *muram; 20562306a36Sopenharmony_ci void *muram_addr; 20662306a36Sopenharmony_ci unsigned long muram_offset, muram_size; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (dt_is_compatible(devp, "fsl,cpm1-smc-uart")) { 20962306a36Sopenharmony_ci is_smc = 1; 21062306a36Sopenharmony_ci } else if (dt_is_compatible(devp, "fsl,cpm2-scc-uart")) { 21162306a36Sopenharmony_ci is_cpm2 = 1; 21262306a36Sopenharmony_ci } else if (dt_is_compatible(devp, "fsl,cpm2-smc-uart")) { 21362306a36Sopenharmony_ci is_cpm2 = 1; 21462306a36Sopenharmony_ci is_smc = 1; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (is_smc) { 21862306a36Sopenharmony_ci enable_port = smc_enable_port; 21962306a36Sopenharmony_ci disable_port = smc_disable_port; 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci enable_port = scc_enable_port; 22262306a36Sopenharmony_ci disable_port = scc_disable_port; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (is_cpm2) 22662306a36Sopenharmony_ci do_cmd = cpm2_cmd; 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci do_cmd = cpm1_cmd; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (getprop(devp, "fsl,cpm-command", &cpm_cmd, 4) < 4) 23162306a36Sopenharmony_ci return -1; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (dt_get_virtual_reg(devp, vreg, 2) < 2) 23462306a36Sopenharmony_ci return -1; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (is_smc) 23762306a36Sopenharmony_ci smc = vreg[0]; 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci scc = vreg[0]; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci param = vreg[1]; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci parent = get_parent(devp); 24462306a36Sopenharmony_ci if (!parent) 24562306a36Sopenharmony_ci return -1; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (dt_get_virtual_reg(parent, &cpcr, 1) < 1) 24862306a36Sopenharmony_ci return -1; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci muram = finddevice("/soc/cpm/muram/data"); 25162306a36Sopenharmony_ci if (!muram) 25262306a36Sopenharmony_ci return -1; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* For bootwrapper-compatible device trees, we assume that the first 25562306a36Sopenharmony_ci * entry has at least 128 bytes, and that #address-cells/#data-cells 25662306a36Sopenharmony_ci * is one for both parent and child. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (dt_get_virtual_reg(muram, &muram_addr, 1) < 1) 26062306a36Sopenharmony_ci return -1; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (getprop(muram, "reg", reg, 8) < 8) 26362306a36Sopenharmony_ci return -1; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci muram_offset = reg[0]; 26662306a36Sopenharmony_ci muram_size = reg[1]; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Store the buffer descriptors at the end of the first muram chunk. 26962306a36Sopenharmony_ci * For SMC ports on CPM2-based platforms, relocate the parameter RAM 27062306a36Sopenharmony_ci * just before the buffer descriptors. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci cbd_offset = muram_offset + muram_size - 2 * sizeof(struct cpm_bd); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (is_cpm2 && is_smc) { 27662306a36Sopenharmony_ci u16 *smc_base = (u16 *)param; 27762306a36Sopenharmony_ci u16 pram_offset; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci pram_offset = cbd_offset - 64; 28062306a36Sopenharmony_ci pram_offset = _ALIGN_DOWN(pram_offset, 64); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci disable_port(); 28362306a36Sopenharmony_ci out_be16(smc_base, pram_offset); 28462306a36Sopenharmony_ci param = muram_addr - muram_offset + pram_offset; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci cbd_addr = muram_addr - muram_offset + cbd_offset; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci scdp->open = cpm_serial_open; 29062306a36Sopenharmony_ci scdp->putc = cpm_serial_putc; 29162306a36Sopenharmony_ci scdp->getc = cpm_serial_getc; 29262306a36Sopenharmony_ci scdp->tstc = cpm_serial_tstc; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 296