xref: /openbmc/linux/arch/powerpc/boot/cpm-serial.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
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  
cpm1_cmd(int op)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  
cpm2_cmd(int op)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  
smc_disable_port(void)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  
scc_disable_port(void)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  
smc_enable_port(void)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  
scc_enable_port(void)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  
cpm_serial_open(void)134  static int cpm_serial_open(void)
135  {
136  	disable_port();
137  
138  	out_8(&param->rfcr, 0x10);
139  	out_8(&param->tfcr, 0x10);
140  	out_be16(&param->mrblr, 1);
141  	out_be16(&param->maxidl, 0);
142  	out_be16(&param->brkec, 0);
143  	out_be16(&param->brkln, 0);
144  	out_be16(&param->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(&param->rbase, cbd_offset);
158  	out_be16(&param->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  
cpm_serial_putc(unsigned char c)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  
cpm_serial_tstc(void)178  static unsigned char cpm_serial_tstc(void)
179  {
180  	barrier();
181  	return !(rbdf->sc & 0x8000);
182  }
183  
cpm_serial_getc(void)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  
cpm_console_init(void * devp,struct serial_console_data * scdp)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