18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CPM serial console support. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci * Author: Scott Wood <scottwood@freescale.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * It is assumed that the firmware (or the platform file) has already set 98c2ecf20Sopenharmony_ci * up the port. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "types.h" 138c2ecf20Sopenharmony_ci#include "io.h" 148c2ecf20Sopenharmony_ci#include "ops.h" 158c2ecf20Sopenharmony_ci#include "page.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct cpm_scc { 188c2ecf20Sopenharmony_ci u32 gsmrl; 198c2ecf20Sopenharmony_ci u32 gsmrh; 208c2ecf20Sopenharmony_ci u16 psmr; 218c2ecf20Sopenharmony_ci u8 res1[2]; 228c2ecf20Sopenharmony_ci u16 todr; 238c2ecf20Sopenharmony_ci u16 dsr; 248c2ecf20Sopenharmony_ci u16 scce; 258c2ecf20Sopenharmony_ci u8 res2[2]; 268c2ecf20Sopenharmony_ci u16 sccm; 278c2ecf20Sopenharmony_ci u8 res3; 288c2ecf20Sopenharmony_ci u8 sccs; 298c2ecf20Sopenharmony_ci u8 res4[8]; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct cpm_smc { 338c2ecf20Sopenharmony_ci u8 res1[2]; 348c2ecf20Sopenharmony_ci u16 smcmr; 358c2ecf20Sopenharmony_ci u8 res2[2]; 368c2ecf20Sopenharmony_ci u8 smce; 378c2ecf20Sopenharmony_ci u8 res3[3]; 388c2ecf20Sopenharmony_ci u8 smcm; 398c2ecf20Sopenharmony_ci u8 res4[5]; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct cpm_param { 438c2ecf20Sopenharmony_ci u16 rbase; 448c2ecf20Sopenharmony_ci u16 tbase; 458c2ecf20Sopenharmony_ci u8 rfcr; 468c2ecf20Sopenharmony_ci u8 tfcr; 478c2ecf20Sopenharmony_ci u16 mrblr; 488c2ecf20Sopenharmony_ci u32 rstate; 498c2ecf20Sopenharmony_ci u8 res1[4]; 508c2ecf20Sopenharmony_ci u16 rbptr; 518c2ecf20Sopenharmony_ci u8 res2[6]; 528c2ecf20Sopenharmony_ci u32 tstate; 538c2ecf20Sopenharmony_ci u8 res3[4]; 548c2ecf20Sopenharmony_ci u16 tbptr; 558c2ecf20Sopenharmony_ci u8 res4[6]; 568c2ecf20Sopenharmony_ci u16 maxidl; 578c2ecf20Sopenharmony_ci u16 idlc; 588c2ecf20Sopenharmony_ci u16 brkln; 598c2ecf20Sopenharmony_ci u16 brkec; 608c2ecf20Sopenharmony_ci u16 brkcr; 618c2ecf20Sopenharmony_ci u16 rmask; 628c2ecf20Sopenharmony_ci u8 res5[4]; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct cpm_bd { 668c2ecf20Sopenharmony_ci u16 sc; /* Status and Control */ 678c2ecf20Sopenharmony_ci u16 len; /* Data length in buffer */ 688c2ecf20Sopenharmony_ci u8 *addr; /* Buffer address in host memory */ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void *cpcr; 728c2ecf20Sopenharmony_cistatic struct cpm_param *param; 738c2ecf20Sopenharmony_cistatic struct cpm_smc *smc; 748c2ecf20Sopenharmony_cistatic struct cpm_scc *scc; 758c2ecf20Sopenharmony_cistatic struct cpm_bd *tbdf, *rbdf; 768c2ecf20Sopenharmony_cistatic u32 cpm_cmd; 778c2ecf20Sopenharmony_cistatic void *cbd_addr; 788c2ecf20Sopenharmony_cistatic u32 cbd_offset; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void (*do_cmd)(int op); 818c2ecf20Sopenharmony_cistatic void (*enable_port)(void); 828c2ecf20Sopenharmony_cistatic void (*disable_port)(void); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define CPM_CMD_STOP_TX 4 858c2ecf20Sopenharmony_ci#define CPM_CMD_RESTART_TX 6 868c2ecf20Sopenharmony_ci#define CPM_CMD_INIT_RX_TX 0 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void cpm1_cmd(int op) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci while (in_be16(cpcr) & 1) 918c2ecf20Sopenharmony_ci ; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci out_be16(cpcr, (op << 8) | cpm_cmd | 1); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci while (in_be16(cpcr) & 1) 968c2ecf20Sopenharmony_ci ; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void cpm2_cmd(int op) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci while (in_be32(cpcr) & 0x10000) 1028c2ecf20Sopenharmony_ci ; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci out_be32(cpcr, op | cpm_cmd | 0x10000); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci while (in_be32(cpcr) & 0x10000) 1078c2ecf20Sopenharmony_ci ; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void smc_disable_port(void) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci do_cmd(CPM_CMD_STOP_TX); 1138c2ecf20Sopenharmony_ci out_be16(&smc->smcmr, in_be16(&smc->smcmr) & ~3); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void scc_disable_port(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci do_cmd(CPM_CMD_STOP_TX); 1198c2ecf20Sopenharmony_ci out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) & ~0x30); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void smc_enable_port(void) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci out_be16(&smc->smcmr, in_be16(&smc->smcmr) | 3); 1258c2ecf20Sopenharmony_ci do_cmd(CPM_CMD_RESTART_TX); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void scc_enable_port(void) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci out_be32(&scc->gsmrl, in_be32(&scc->gsmrl) | 0x30); 1318c2ecf20Sopenharmony_ci do_cmd(CPM_CMD_RESTART_TX); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int cpm_serial_open(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci disable_port(); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci out_8(¶m->rfcr, 0x10); 1398c2ecf20Sopenharmony_ci out_8(¶m->tfcr, 0x10); 1408c2ecf20Sopenharmony_ci out_be16(¶m->mrblr, 1); 1418c2ecf20Sopenharmony_ci out_be16(¶m->maxidl, 0); 1428c2ecf20Sopenharmony_ci out_be16(¶m->brkec, 0); 1438c2ecf20Sopenharmony_ci out_be16(¶m->brkln, 0); 1448c2ecf20Sopenharmony_ci out_be16(¶m->brkcr, 0); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci rbdf = cbd_addr; 1478c2ecf20Sopenharmony_ci rbdf->addr = (u8 *)rbdf - 1; 1488c2ecf20Sopenharmony_ci rbdf->sc = 0xa000; 1498c2ecf20Sopenharmony_ci rbdf->len = 1; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci tbdf = rbdf + 1; 1528c2ecf20Sopenharmony_ci tbdf->addr = (u8 *)rbdf - 2; 1538c2ecf20Sopenharmony_ci tbdf->sc = 0x2000; 1548c2ecf20Sopenharmony_ci tbdf->len = 1; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci sync(); 1578c2ecf20Sopenharmony_ci out_be16(¶m->rbase, cbd_offset); 1588c2ecf20Sopenharmony_ci out_be16(¶m->tbase, cbd_offset + sizeof(struct cpm_bd)); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci do_cmd(CPM_CMD_INIT_RX_TX); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci enable_port(); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void cpm_serial_putc(unsigned char c) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci while (tbdf->sc & 0x8000) 1698c2ecf20Sopenharmony_ci barrier(); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sync(); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci tbdf->addr[0] = c; 1748c2ecf20Sopenharmony_ci eieio(); 1758c2ecf20Sopenharmony_ci tbdf->sc |= 0x8000; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic unsigned char cpm_serial_tstc(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci barrier(); 1818c2ecf20Sopenharmony_ci return !(rbdf->sc & 0x8000); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic unsigned char cpm_serial_getc(void) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci unsigned char c; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci while (!cpm_serial_tstc()) 1898c2ecf20Sopenharmony_ci ; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci sync(); 1928c2ecf20Sopenharmony_ci c = rbdf->addr[0]; 1938c2ecf20Sopenharmony_ci eieio(); 1948c2ecf20Sopenharmony_ci rbdf->sc |= 0x8000; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return c; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciint cpm_console_init(void *devp, struct serial_console_data *scdp) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci void *vreg[2]; 2028c2ecf20Sopenharmony_ci u32 reg[2]; 2038c2ecf20Sopenharmony_ci int is_smc = 0, is_cpm2 = 0; 2048c2ecf20Sopenharmony_ci void *parent, *muram; 2058c2ecf20Sopenharmony_ci void *muram_addr; 2068c2ecf20Sopenharmony_ci unsigned long muram_offset, muram_size; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (dt_is_compatible(devp, "fsl,cpm1-smc-uart")) { 2098c2ecf20Sopenharmony_ci is_smc = 1; 2108c2ecf20Sopenharmony_ci } else if (dt_is_compatible(devp, "fsl,cpm2-scc-uart")) { 2118c2ecf20Sopenharmony_ci is_cpm2 = 1; 2128c2ecf20Sopenharmony_ci } else if (dt_is_compatible(devp, "fsl,cpm2-smc-uart")) { 2138c2ecf20Sopenharmony_ci is_cpm2 = 1; 2148c2ecf20Sopenharmony_ci is_smc = 1; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (is_smc) { 2188c2ecf20Sopenharmony_ci enable_port = smc_enable_port; 2198c2ecf20Sopenharmony_ci disable_port = smc_disable_port; 2208c2ecf20Sopenharmony_ci } else { 2218c2ecf20Sopenharmony_ci enable_port = scc_enable_port; 2228c2ecf20Sopenharmony_ci disable_port = scc_disable_port; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (is_cpm2) 2268c2ecf20Sopenharmony_ci do_cmd = cpm2_cmd; 2278c2ecf20Sopenharmony_ci else 2288c2ecf20Sopenharmony_ci do_cmd = cpm1_cmd; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (getprop(devp, "fsl,cpm-command", &cpm_cmd, 4) < 4) 2318c2ecf20Sopenharmony_ci return -1; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (dt_get_virtual_reg(devp, vreg, 2) < 2) 2348c2ecf20Sopenharmony_ci return -1; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (is_smc) 2378c2ecf20Sopenharmony_ci smc = vreg[0]; 2388c2ecf20Sopenharmony_ci else 2398c2ecf20Sopenharmony_ci scc = vreg[0]; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci param = vreg[1]; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci parent = get_parent(devp); 2448c2ecf20Sopenharmony_ci if (!parent) 2458c2ecf20Sopenharmony_ci return -1; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (dt_get_virtual_reg(parent, &cpcr, 1) < 1) 2488c2ecf20Sopenharmony_ci return -1; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci muram = finddevice("/soc/cpm/muram/data"); 2518c2ecf20Sopenharmony_ci if (!muram) 2528c2ecf20Sopenharmony_ci return -1; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* For bootwrapper-compatible device trees, we assume that the first 2558c2ecf20Sopenharmony_ci * entry has at least 128 bytes, and that #address-cells/#data-cells 2568c2ecf20Sopenharmony_ci * is one for both parent and child. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (dt_get_virtual_reg(muram, &muram_addr, 1) < 1) 2608c2ecf20Sopenharmony_ci return -1; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (getprop(muram, "reg", reg, 8) < 8) 2638c2ecf20Sopenharmony_ci return -1; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci muram_offset = reg[0]; 2668c2ecf20Sopenharmony_ci muram_size = reg[1]; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Store the buffer descriptors at the end of the first muram chunk. 2698c2ecf20Sopenharmony_ci * For SMC ports on CPM2-based platforms, relocate the parameter RAM 2708c2ecf20Sopenharmony_ci * just before the buffer descriptors. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci cbd_offset = muram_offset + muram_size - 2 * sizeof(struct cpm_bd); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (is_cpm2 && is_smc) { 2768c2ecf20Sopenharmony_ci u16 *smc_base = (u16 *)param; 2778c2ecf20Sopenharmony_ci u16 pram_offset; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci pram_offset = cbd_offset - 64; 2808c2ecf20Sopenharmony_ci pram_offset = _ALIGN_DOWN(pram_offset, 64); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci disable_port(); 2838c2ecf20Sopenharmony_ci out_be16(smc_base, pram_offset); 2848c2ecf20Sopenharmony_ci param = muram_addr - muram_offset + pram_offset; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci cbd_addr = muram_addr - muram_offset + cbd_offset; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci scdp->open = cpm_serial_open; 2908c2ecf20Sopenharmony_ci scdp->putc = cpm_serial_putc; 2918c2ecf20Sopenharmony_ci scdp->getc = cpm_serial_getc; 2928c2ecf20Sopenharmony_ci scdp->tstc = cpm_serial_tstc; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 296