Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/serial/cpm_uart_cpm2.c | |
3 | * | |
4 | * Driver for CPM (SCC/SMC) serial ports; CPM2 definitions | |
5 | * | |
4c8d3d99 | 6 | * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) |
1da177e4 LT |
7 | * Pantelis Antoniou (panto@intracom.gr) (CPM1) |
8 | * | |
9 | * Copyright (C) 2004 Freescale Semiconductor, Inc. | |
10 | * (C) 2004 Intracom, S.A. | |
6e197696 VB |
11 | * (C) 2006 MontaVista Software, Inc. |
12 | * Vitaly Bordug <vbordug@ru.mvista.com> | |
1da177e4 LT |
13 | * |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2 of the License, or | |
17 | * (at your option) any later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this program; if not, write to the Free Software | |
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
27 | * | |
28 | */ | |
29 | ||
1da177e4 LT |
30 | #include <linux/module.h> |
31 | #include <linux/tty.h> | |
32 | #include <linux/ioport.h> | |
33 | #include <linux/init.h> | |
34 | #include <linux/serial.h> | |
35 | #include <linux/console.h> | |
36 | #include <linux/sysrq.h> | |
37 | #include <linux/device.h> | |
38 | #include <linux/bootmem.h> | |
39 | #include <linux/dma-mapping.h> | |
40 | ||
41 | #include <asm/io.h> | |
42 | #include <asm/irq.h> | |
3dd0dcbe | 43 | #include <asm/fs_pd.h> |
1da177e4 LT |
44 | |
45 | #include <linux/serial_core.h> | |
46 | #include <linux/kernel.h> | |
47 | ||
48 | #include "cpm_uart.h" | |
49 | ||
50 | /**************************************************************/ | |
51 | ||
7ae87036 SW |
52 | #ifdef CONFIG_PPC_CPM_NEW_BINDING |
53 | void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd) | |
54 | { | |
55 | cpm_cpm2_t __iomem *cp = cpm2_map(im_cpm); | |
56 | ||
57 | out_be32(&cp->cp_cpcr, port->command | cmd | CPM_CR_FLG); | |
58 | while (in_be32(&cp->cp_cpcr) & CPM_CR_FLG) | |
59 | ; | |
60 | ||
61 | cpm2_unmap(cp); | |
62 | } | |
63 | #else | |
64 | void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd) | |
1da177e4 | 65 | { |
1da177e4 | 66 | ulong val; |
7ae87036 | 67 | int line = port - cpm_uart_ports; |
fc8e50e3 VB |
68 | volatile cpm_cpm2_t *cp = cpm2_map(im_cpm); |
69 | ||
1da177e4 LT |
70 | |
71 | switch (line) { | |
72 | case UART_SMC1: | |
73 | val = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0, | |
74 | cmd) | CPM_CR_FLG; | |
75 | break; | |
76 | case UART_SMC2: | |
77 | val = mk_cr_cmd(CPM_CR_SMC2_PAGE, CPM_CR_SMC2_SBLOCK, 0, | |
78 | cmd) | CPM_CR_FLG; | |
79 | break; | |
80 | case UART_SCC1: | |
81 | val = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0, | |
82 | cmd) | CPM_CR_FLG; | |
83 | break; | |
84 | case UART_SCC2: | |
85 | val = mk_cr_cmd(CPM_CR_SCC2_PAGE, CPM_CR_SCC2_SBLOCK, 0, | |
86 | cmd) | CPM_CR_FLG; | |
87 | break; | |
88 | case UART_SCC3: | |
89 | val = mk_cr_cmd(CPM_CR_SCC3_PAGE, CPM_CR_SCC3_SBLOCK, 0, | |
90 | cmd) | CPM_CR_FLG; | |
91 | break; | |
92 | case UART_SCC4: | |
93 | val = mk_cr_cmd(CPM_CR_SCC4_PAGE, CPM_CR_SCC4_SBLOCK, 0, | |
94 | cmd) | CPM_CR_FLG; | |
95 | break; | |
96 | default: | |
97 | return; | |
98 | ||
99 | } | |
100 | cp->cp_cpcr = val; | |
101 | while (cp->cp_cpcr & CPM_CR_FLG) ; | |
fc8e50e3 VB |
102 | |
103 | cpm2_unmap(cp); | |
1da177e4 LT |
104 | } |
105 | ||
106 | void smc1_lineif(struct uart_cpm_port *pinfo) | |
107 | { | |
fc8e50e3 VB |
108 | volatile iop_cpm2_t *io = cpm2_map(im_ioport); |
109 | volatile cpmux_t *cpmux = cpm2_map(im_cpmux); | |
1da177e4 LT |
110 | |
111 | /* SMC1 is only on port D */ | |
112 | io->iop_ppard |= 0x00c00000; | |
113 | io->iop_pdird |= 0x00400000; | |
114 | io->iop_pdird &= ~0x00800000; | |
115 | io->iop_psord &= ~0x00c00000; | |
116 | ||
117 | /* Wire BRG1 to SMC1 */ | |
fc8e50e3 | 118 | cpmux->cmx_smr &= 0x0f; |
1da177e4 | 119 | pinfo->brg = 1; |
fc8e50e3 VB |
120 | |
121 | cpm2_unmap(cpmux); | |
122 | cpm2_unmap(io); | |
1da177e4 LT |
123 | } |
124 | ||
125 | void smc2_lineif(struct uart_cpm_port *pinfo) | |
126 | { | |
fc8e50e3 VB |
127 | volatile iop_cpm2_t *io = cpm2_map(im_ioport); |
128 | volatile cpmux_t *cpmux = cpm2_map(im_cpmux); | |
1da177e4 LT |
129 | |
130 | /* SMC2 is only on port A */ | |
131 | io->iop_ppara |= 0x00c00000; | |
132 | io->iop_pdira |= 0x00400000; | |
133 | io->iop_pdira &= ~0x00800000; | |
134 | io->iop_psora &= ~0x00c00000; | |
135 | ||
136 | /* Wire BRG2 to SMC2 */ | |
fc8e50e3 | 137 | cpmux->cmx_smr &= 0xf0; |
1da177e4 | 138 | pinfo->brg = 2; |
fc8e50e3 VB |
139 | |
140 | cpm2_unmap(cpmux); | |
141 | cpm2_unmap(io); | |
1da177e4 LT |
142 | } |
143 | ||
144 | void scc1_lineif(struct uart_cpm_port *pinfo) | |
145 | { | |
fc8e50e3 VB |
146 | volatile iop_cpm2_t *io = cpm2_map(im_ioport); |
147 | volatile cpmux_t *cpmux = cpm2_map(im_cpmux); | |
1da177e4 LT |
148 | |
149 | /* Use Port D for SCC1 instead of other functions. */ | |
150 | io->iop_ppard |= 0x00000003; | |
151 | io->iop_psord &= ~0x00000001; /* Rx */ | |
152 | io->iop_psord |= 0x00000002; /* Tx */ | |
153 | io->iop_pdird &= ~0x00000001; /* Rx */ | |
154 | io->iop_pdird |= 0x00000002; /* Tx */ | |
155 | ||
156 | /* Wire BRG1 to SCC1 */ | |
fc8e50e3 VB |
157 | cpmux->cmx_scr &= 0x00ffffff; |
158 | cpmux->cmx_scr |= 0x00000000; | |
1da177e4 | 159 | pinfo->brg = 1; |
fc8e50e3 VB |
160 | |
161 | cpm2_unmap(cpmux); | |
162 | cpm2_unmap(io); | |
1da177e4 LT |
163 | } |
164 | ||
165 | void scc2_lineif(struct uart_cpm_port *pinfo) | |
166 | { | |
a1604f91 MP |
167 | /* |
168 | * STx GP3 uses the SCC2 secondary option pin assignment | |
169 | * which this driver doesn't account for in the static | |
170 | * pin assignments. This kind of board specific info | |
171 | * really has to get out of the driver so boards can | |
172 | * be supported in a sane fashion. | |
173 | */ | |
174 | #ifndef CONFIG_STX_GP3 | |
fc8e50e3 VB |
175 | volatile iop_cpm2_t *io = cpm2_map(im_ioport); |
176 | volatile cpmux_t *cpmux = cpm2_map(im_cpmux); | |
177 | ||
1da177e4 LT |
178 | io->iop_pparb |= 0x008b0000; |
179 | io->iop_pdirb |= 0x00880000; | |
180 | io->iop_psorb |= 0x00880000; | |
181 | io->iop_pdirb &= ~0x00030000; | |
182 | io->iop_psorb &= ~0x00030000; | |
a1604f91 | 183 | #endif |
fc8e50e3 VB |
184 | cpmux->cmx_scr &= 0xff00ffff; |
185 | cpmux->cmx_scr |= 0x00090000; | |
1da177e4 | 186 | pinfo->brg = 2; |
fc8e50e3 VB |
187 | |
188 | cpm2_unmap(cpmux); | |
189 | cpm2_unmap(io); | |
1da177e4 LT |
190 | } |
191 | ||
192 | void scc3_lineif(struct uart_cpm_port *pinfo) | |
193 | { | |
fc8e50e3 VB |
194 | volatile iop_cpm2_t *io = cpm2_map(im_ioport); |
195 | volatile cpmux_t *cpmux = cpm2_map(im_cpmux); | |
196 | ||
1da177e4 LT |
197 | io->iop_pparb |= 0x008b0000; |
198 | io->iop_pdirb |= 0x00880000; | |
199 | io->iop_psorb |= 0x00880000; | |
200 | io->iop_pdirb &= ~0x00030000; | |
201 | io->iop_psorb &= ~0x00030000; | |
fc8e50e3 VB |
202 | cpmux->cmx_scr &= 0xffff00ff; |
203 | cpmux->cmx_scr |= 0x00001200; | |
1da177e4 | 204 | pinfo->brg = 3; |
fc8e50e3 VB |
205 | |
206 | cpm2_unmap(cpmux); | |
207 | cpm2_unmap(io); | |
1da177e4 LT |
208 | } |
209 | ||
210 | void scc4_lineif(struct uart_cpm_port *pinfo) | |
211 | { | |
fc8e50e3 VB |
212 | volatile iop_cpm2_t *io = cpm2_map(im_ioport); |
213 | volatile cpmux_t *cpmux = cpm2_map(im_cpmux); | |
1da177e4 LT |
214 | |
215 | io->iop_ppard |= 0x00000600; | |
216 | io->iop_psord &= ~0x00000600; /* Tx/Rx */ | |
217 | io->iop_pdird &= ~0x00000200; /* Rx */ | |
218 | io->iop_pdird |= 0x00000400; /* Tx */ | |
219 | ||
fc8e50e3 VB |
220 | cpmux->cmx_scr &= 0xffffff00; |
221 | cpmux->cmx_scr |= 0x0000001b; | |
1da177e4 | 222 | pinfo->brg = 4; |
fc8e50e3 VB |
223 | |
224 | cpm2_unmap(cpmux); | |
225 | cpm2_unmap(io); | |
1da177e4 | 226 | } |
7ae87036 | 227 | #endif |
1da177e4 LT |
228 | |
229 | /* | |
230 | * Allocate DP-Ram and memory buffers. We need to allocate a transmit and | |
231 | * receive buffer descriptors from dual port ram, and a character | |
232 | * buffer area from host mem. If we are allocating for the console we need | |
233 | * to do it from bootmem | |
234 | */ | |
235 | int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) | |
236 | { | |
237 | int dpmemsz, memsz; | |
15f8c604 | 238 | u8 __iomem *dp_mem; |
4c35630c | 239 | unsigned long dp_offset; |
1da177e4 LT |
240 | u8 *mem_addr; |
241 | dma_addr_t dma_addr = 0; | |
242 | ||
243 | pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line); | |
244 | ||
245 | dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); | |
246 | dp_offset = cpm_dpalloc(dpmemsz, 8); | |
4c35630c | 247 | if (IS_ERR_VALUE(dp_offset)) { |
1da177e4 LT |
248 | printk(KERN_ERR |
249 | "cpm_uart_cpm.c: could not allocate buffer descriptors\n"); | |
250 | return -ENOMEM; | |
251 | } | |
252 | ||
253 | dp_mem = cpm_dpram_addr(dp_offset); | |
254 | ||
255 | memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + | |
256 | L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); | |
09b03b6c | 257 | if (is_con) { |
1da177e4 | 258 | mem_addr = alloc_bootmem(memsz); |
8e30a9a2 | 259 | dma_addr = virt_to_bus(mem_addr); |
09b03b6c | 260 | } |
1da177e4 LT |
261 | else |
262 | mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr, | |
263 | GFP_KERNEL); | |
264 | ||
265 | if (mem_addr == NULL) { | |
266 | cpm_dpfree(dp_offset); | |
267 | printk(KERN_ERR | |
268 | "cpm_uart_cpm.c: could not allocate coherent memory\n"); | |
269 | return -ENOMEM; | |
270 | } | |
271 | ||
272 | pinfo->dp_addr = dp_offset; | |
273 | pinfo->mem_addr = mem_addr; | |
274 | pinfo->dma_addr = dma_addr; | |
09b03b6c | 275 | pinfo->mem_size = memsz; |
1da177e4 LT |
276 | |
277 | pinfo->rx_buf = mem_addr; | |
278 | pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos | |
279 | * pinfo->rx_fifosize); | |
280 | ||
15f8c604 | 281 | pinfo->rx_bd_base = (cbd_t __iomem *)dp_mem; |
1da177e4 LT |
282 | pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos; |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | void cpm_uart_freebuf(struct uart_cpm_port *pinfo) | |
288 | { | |
289 | dma_free_coherent(NULL, L1_CACHE_ALIGN(pinfo->rx_nrfifos * | |
290 | pinfo->rx_fifosize) + | |
291 | L1_CACHE_ALIGN(pinfo->tx_nrfifos * | |
c1dcfd9d | 292 | pinfo->tx_fifosize), (void __force *)pinfo->mem_addr, |
1da177e4 LT |
293 | pinfo->dma_addr); |
294 | ||
295 | cpm_dpfree(pinfo->dp_addr); | |
296 | } | |
297 | ||
7ae87036 | 298 | #ifndef CONFIG_PPC_CPM_NEW_BINDING |
1da177e4 | 299 | /* Setup any dynamic params in the uart desc */ |
32a56ebb | 300 | int cpm_uart_init_portdesc(void) |
1da177e4 | 301 | { |
fc8e50e3 | 302 | #if defined(CONFIG_SERIAL_CPM_SMC1) || defined(CONFIG_SERIAL_CPM_SMC2) |
0f9ec0a8 | 303 | u16 *addr; |
fc8e50e3 | 304 | #endif |
1da177e4 LT |
305 | pr_debug("CPM uart[-]:init portdesc\n"); |
306 | ||
307 | cpm_uart_nr = 0; | |
308 | #ifdef CONFIG_SERIAL_CPM_SMC1 | |
fc8e50e3 | 309 | cpm_uart_ports[UART_SMC1].smcp = (smc_t *) cpm2_map(im_smc[0]); |
1da177e4 | 310 | cpm_uart_ports[UART_SMC1].port.mapbase = |
fc8e50e3 VB |
311 | (unsigned long)cpm_uart_ports[UART_SMC1].smcp; |
312 | ||
313 | cpm_uart_ports[UART_SMC1].smcup = | |
314 | (smc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SMC1], PROFF_SMC_SIZE); | |
315 | addr = (u16 *)cpm2_map_size(im_dprambase[PROFF_SMC1_BASE], 2); | |
316 | *addr = PROFF_SMC1; | |
317 | cpm2_unmap(addr); | |
318 | ||
1da177e4 LT |
319 | cpm_uart_ports[UART_SMC1].smcp->smc_smcm |= (SMCM_RX | SMCM_TX); |
320 | cpm_uart_ports[UART_SMC1].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); | |
3dd0dcbe | 321 | cpm_uart_ports[UART_SMC1].port.uartclk = uart_clock(); |
1da177e4 LT |
322 | cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1; |
323 | #endif | |
324 | ||
325 | #ifdef CONFIG_SERIAL_CPM_SMC2 | |
fc8e50e3 | 326 | cpm_uart_ports[UART_SMC2].smcp = (smc_t *) cpm2_map(im_smc[1]); |
1da177e4 | 327 | cpm_uart_ports[UART_SMC2].port.mapbase = |
fc8e50e3 VB |
328 | (unsigned long)cpm_uart_ports[UART_SMC2].smcp; |
329 | ||
330 | cpm_uart_ports[UART_SMC2].smcup = | |
331 | (smc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SMC2], PROFF_SMC_SIZE); | |
332 | addr = (u16 *)cpm2_map_size(im_dprambase[PROFF_SMC2_BASE], 2); | |
333 | *addr = PROFF_SMC2; | |
334 | cpm2_unmap(addr); | |
335 | ||
1da177e4 LT |
336 | cpm_uart_ports[UART_SMC2].smcp->smc_smcm |= (SMCM_RX | SMCM_TX); |
337 | cpm_uart_ports[UART_SMC2].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); | |
3dd0dcbe | 338 | cpm_uart_ports[UART_SMC2].port.uartclk = uart_clock(); |
1da177e4 LT |
339 | cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2; |
340 | #endif | |
341 | ||
342 | #ifdef CONFIG_SERIAL_CPM_SCC1 | |
fc8e50e3 | 343 | cpm_uart_ports[UART_SCC1].sccp = (scc_t *) cpm2_map(im_scc[0]); |
1da177e4 | 344 | cpm_uart_ports[UART_SCC1].port.mapbase = |
fc8e50e3 VB |
345 | (unsigned long)cpm_uart_ports[UART_SCC1].sccp; |
346 | cpm_uart_ports[UART_SCC1].sccup = | |
347 | (scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC1], PROFF_SCC_SIZE); | |
348 | ||
1da177e4 LT |
349 | cpm_uart_ports[UART_SCC1].sccp->scc_sccm &= |
350 | ~(UART_SCCM_TX | UART_SCCM_RX); | |
351 | cpm_uart_ports[UART_SCC1].sccp->scc_gsmrl &= | |
352 | ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); | |
3dd0dcbe | 353 | cpm_uart_ports[UART_SCC1].port.uartclk = uart_clock(); |
1da177e4 LT |
354 | cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1; |
355 | #endif | |
356 | ||
357 | #ifdef CONFIG_SERIAL_CPM_SCC2 | |
fc8e50e3 | 358 | cpm_uart_ports[UART_SCC2].sccp = (scc_t *) cpm2_map(im_scc[1]); |
1da177e4 | 359 | cpm_uart_ports[UART_SCC2].port.mapbase = |
fc8e50e3 VB |
360 | (unsigned long)cpm_uart_ports[UART_SCC2].sccp; |
361 | cpm_uart_ports[UART_SCC2].sccup = | |
362 | (scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC2], PROFF_SCC_SIZE); | |
363 | ||
1da177e4 LT |
364 | cpm_uart_ports[UART_SCC2].sccp->scc_sccm &= |
365 | ~(UART_SCCM_TX | UART_SCCM_RX); | |
366 | cpm_uart_ports[UART_SCC2].sccp->scc_gsmrl &= | |
367 | ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); | |
3dd0dcbe | 368 | cpm_uart_ports[UART_SCC2].port.uartclk = uart_clock(); |
1da177e4 LT |
369 | cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2; |
370 | #endif | |
371 | ||
372 | #ifdef CONFIG_SERIAL_CPM_SCC3 | |
fc8e50e3 | 373 | cpm_uart_ports[UART_SCC3].sccp = (scc_t *) cpm2_map(im_scc[2]); |
1da177e4 | 374 | cpm_uart_ports[UART_SCC3].port.mapbase = |
fc8e50e3 VB |
375 | (unsigned long)cpm_uart_ports[UART_SCC3].sccp; |
376 | cpm_uart_ports[UART_SCC3].sccup = | |
377 | (scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC3], PROFF_SCC_SIZE); | |
378 | ||
1da177e4 LT |
379 | cpm_uart_ports[UART_SCC3].sccp->scc_sccm &= |
380 | ~(UART_SCCM_TX | UART_SCCM_RX); | |
381 | cpm_uart_ports[UART_SCC3].sccp->scc_gsmrl &= | |
382 | ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); | |
3dd0dcbe | 383 | cpm_uart_ports[UART_SCC3].port.uartclk = uart_clock(); |
1da177e4 LT |
384 | cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3; |
385 | #endif | |
386 | ||
387 | #ifdef CONFIG_SERIAL_CPM_SCC4 | |
fc8e50e3 | 388 | cpm_uart_ports[UART_SCC4].sccp = (scc_t *) cpm2_map(im_scc[3]); |
1da177e4 | 389 | cpm_uart_ports[UART_SCC4].port.mapbase = |
fc8e50e3 VB |
390 | (unsigned long)cpm_uart_ports[UART_SCC4].sccp; |
391 | cpm_uart_ports[UART_SCC4].sccup = | |
392 | (scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC4], PROFF_SCC_SIZE); | |
393 | ||
1da177e4 LT |
394 | cpm_uart_ports[UART_SCC4].sccp->scc_sccm &= |
395 | ~(UART_SCCM_TX | UART_SCCM_RX); | |
396 | cpm_uart_ports[UART_SCC4].sccp->scc_gsmrl &= | |
397 | ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); | |
3dd0dcbe | 398 | cpm_uart_ports[UART_SCC4].port.uartclk = uart_clock(); |
1da177e4 LT |
399 | cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4; |
400 | #endif | |
401 | ||
402 | return 0; | |
403 | } | |
7ae87036 | 404 | #endif |