Blame view
drivers/serial/atmel_serial.c
25.8 KB
1e6c9c287
|
1 |
/* |
c2f5ccfbd
|
2 |
* linux/drivers/char/atmel_serial.c |
1e6c9c287
|
3 |
* |
7192f92c7
|
4 |
* Driver for Atmel AT91 / AT32 Serial ports |
1e6c9c287
|
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
* Copyright (C) 2003 Rick Bronson * * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ |
1e6c9c287
|
25 26 27 28 29 30 |
#include <linux/module.h> #include <linux/tty.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/serial.h> |
afefc4158
|
31 |
#include <linux/clk.h> |
1e6c9c287
|
32 33 34 |
#include <linux/console.h> #include <linux/sysrq.h> #include <linux/tty_flip.h> |
afefc4158
|
35 |
#include <linux/platform_device.h> |
93a3ddc20
|
36 |
#include <linux/atmel_pdc.h> |
1e6c9c287
|
37 38 |
#include <asm/io.h> |
afefc4158
|
39 |
#include <asm/mach/serial_at91.h> |
1e6c9c287
|
40 |
#include <asm/arch/board.h> |
93a3ddc20
|
41 |
|
acca9b83a
|
42 |
#ifdef CONFIG_ARM |
c2f5ccfbd
|
43 |
#include <asm/arch/cpu.h> |
20e652761
|
44 |
#include <asm/arch/gpio.h> |
acca9b83a
|
45 |
#endif |
1e6c9c287
|
46 |
|
5b34821a6
|
47 |
#include "atmel_serial.h" |
749c4e603
|
48 |
#if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) |
1e6c9c287
|
49 50 51 52 |
#define SUPPORT_SYSRQ #endif #include <linux/serial_core.h> |
749c4e603
|
53 |
#ifdef CONFIG_SERIAL_ATMEL_TTYAT |
1e6c9c287
|
54 55 56 57 |
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we * should coexist with the 8250 driver, such as if we have an external 16C550 * UART. */ |
7192f92c7
|
58 |
#define SERIAL_ATMEL_MAJOR 204 |
1e6c9c287
|
59 |
#define MINOR_START 154 |
7192f92c7
|
60 |
#define ATMEL_DEVICENAME "ttyAT" |
1e6c9c287
|
61 62 63 64 65 |
#else /* Use device name ttyS, major 4, minor 64-68. This is the usual serial port * name, but it is legally reserved for the 8250 driver. */ |
7192f92c7
|
66 |
#define SERIAL_ATMEL_MAJOR TTY_MAJOR |
1e6c9c287
|
67 |
#define MINOR_START 64 |
7192f92c7
|
68 |
#define ATMEL_DEVICENAME "ttyS" |
1e6c9c287
|
69 70 |
#endif |
7192f92c7
|
71 |
#define ATMEL_ISR_PASS_LIMIT 256 |
1e6c9c287
|
72 |
|
544fc7283
|
73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
#define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR) #define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR) #define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR) #define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER) #define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR) #define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR) #define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR) #define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR) #define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR) #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) // #define UART_GET_CR(port) __raw_readl((port)->membase + ATMEL_US_CR) // is write-only |
1e6c9c287
|
87 88 |
/* PDC registers */ |
544fc7283
|
89 90 91 92 93 94 95 96 97 98 99 100 101 |
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) #define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR) #define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR) #define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR) #define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR) #define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR) #define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR) #define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR) #define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR) //#define UART_PUT_TNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TNPR) //#define UART_PUT_TNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TNCR) |
1e6c9c287
|
102 |
|
71f2e2b87
|
103 104 |
static int (*atmel_open_hook)(struct uart_port *); static void (*atmel_close_hook)(struct uart_port *); |
1e6c9c287
|
105 |
|
afefc4158
|
106 107 108 |
/* * We wrap our port structure around the generic uart_port. */ |
7192f92c7
|
109 |
struct atmel_uart_port { |
afefc4158
|
110 111 112 |
struct uart_port uart; /* uart */ struct clk *clk; /* uart clock */ unsigned short suspended; /* is port suspended? */ |
9e6077bd8
|
113 |
int break_active; /* break being received */ |
afefc4158
|
114 |
}; |
7192f92c7
|
115 |
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; |
afefc4158
|
116 |
|
1e6c9c287
|
117 |
#ifdef SUPPORT_SYSRQ |
7192f92c7
|
118 |
static struct console atmel_console; |
1e6c9c287
|
119 120 121 122 123 |
#endif /* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ |
7192f92c7
|
124 |
static u_int atmel_tx_empty(struct uart_port *port) |
1e6c9c287
|
125 |
{ |
7192f92c7
|
126 |
return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0; |
1e6c9c287
|
127 128 129 130 131 |
} /* * Set state of the modem control output lines */ |
7192f92c7
|
132 |
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) |
1e6c9c287
|
133 134 |
{ unsigned int control = 0; |
afefc4158
|
135 |
unsigned int mode; |
1e6c9c287
|
136 |
|
c2f5ccfbd
|
137 |
#ifdef CONFIG_ARCH_AT91RM9200 |
79da7a610
|
138 |
if (cpu_is_at91rm9200()) { |
afefc4158
|
139 140 141 142 |
/* * AT91RM9200 Errata #39: RTS0 is not internally connected to PA21. * We need to drive the pin manually. */ |
72729910c
|
143 |
if (port->mapbase == AT91RM9200_BASE_US0) { |
afefc4158
|
144 |
if (mctrl & TIOCM_RTS) |
20e652761
|
145 |
at91_set_gpio_value(AT91_PIN_PA21, 0); |
afefc4158
|
146 |
else |
20e652761
|
147 |
at91_set_gpio_value(AT91_PIN_PA21, 1); |
afefc4158
|
148 |
} |
1e6c9c287
|
149 |
} |
acca9b83a
|
150 |
#endif |
1e6c9c287
|
151 152 |
if (mctrl & TIOCM_RTS) |
7192f92c7
|
153 |
control |= ATMEL_US_RTSEN; |
1e6c9c287
|
154 |
else |
7192f92c7
|
155 |
control |= ATMEL_US_RTSDIS; |
1e6c9c287
|
156 157 |
if (mctrl & TIOCM_DTR) |
7192f92c7
|
158 |
control |= ATMEL_US_DTREN; |
1e6c9c287
|
159 |
else |
7192f92c7
|
160 |
control |= ATMEL_US_DTRDIS; |
1e6c9c287
|
161 |
|
afefc4158
|
162 163 164 |
UART_PUT_CR(port, control); /* Local loopback mode? */ |
7192f92c7
|
165 |
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE; |
afefc4158
|
166 |
if (mctrl & TIOCM_LOOP) |
7192f92c7
|
167 |
mode |= ATMEL_US_CHMODE_LOC_LOOP; |
afefc4158
|
168 |
else |
7192f92c7
|
169 |
mode |= ATMEL_US_CHMODE_NORMAL; |
afefc4158
|
170 |
UART_PUT_MR(port, mode); |
1e6c9c287
|
171 172 173 174 175 |
} /* * Get state of the modem control input lines */ |
7192f92c7
|
176 |
static u_int atmel_get_mctrl(struct uart_port *port) |
1e6c9c287
|
177 178 179 180 181 182 183 184 |
{ unsigned int status, ret = 0; status = UART_GET_CSR(port); /* * The control signals are active low. */ |
7192f92c7
|
185 |
if (!(status & ATMEL_US_DCD)) |
1e6c9c287
|
186 |
ret |= TIOCM_CD; |
7192f92c7
|
187 |
if (!(status & ATMEL_US_CTS)) |
1e6c9c287
|
188 |
ret |= TIOCM_CTS; |
7192f92c7
|
189 |
if (!(status & ATMEL_US_DSR)) |
1e6c9c287
|
190 |
ret |= TIOCM_DSR; |
7192f92c7
|
191 |
if (!(status & ATMEL_US_RI)) |
1e6c9c287
|
192 193 194 195 196 197 198 199 |
ret |= TIOCM_RI; return ret; } /* * Stop transmitting. */ |
7192f92c7
|
200 |
static void atmel_stop_tx(struct uart_port *port) |
1e6c9c287
|
201 |
{ |
7192f92c7
|
202 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
203 |
|
7192f92c7
|
204 |
UART_PUT_IDR(port, ATMEL_US_TXRDY); |
1e6c9c287
|
205 206 207 208 209 |
} /* * Start transmitting. */ |
7192f92c7
|
210 |
static void atmel_start_tx(struct uart_port *port) |
1e6c9c287
|
211 |
{ |
7192f92c7
|
212 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
213 |
|
7192f92c7
|
214 |
UART_PUT_IER(port, ATMEL_US_TXRDY); |
1e6c9c287
|
215 216 217 218 219 |
} /* * Stop receiving - port is in process of being closed. */ |
7192f92c7
|
220 |
static void atmel_stop_rx(struct uart_port *port) |
1e6c9c287
|
221 |
{ |
7192f92c7
|
222 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
223 |
|
7192f92c7
|
224 |
UART_PUT_IDR(port, ATMEL_US_RXRDY); |
1e6c9c287
|
225 226 227 228 229 |
} /* * Enable modem status interrupts */ |
7192f92c7
|
230 |
static void atmel_enable_ms(struct uart_port *port) |
1e6c9c287
|
231 |
{ |
7192f92c7
|
232 |
UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC); |
1e6c9c287
|
233 234 235 236 237 |
} /* * Control the transmission of a break signal */ |
7192f92c7
|
238 |
static void atmel_break_ctl(struct uart_port *port, int break_state) |
1e6c9c287
|
239 240 |
{ if (break_state != 0) |
7192f92c7
|
241 |
UART_PUT_CR(port, ATMEL_US_STTBRK); /* start break */ |
1e6c9c287
|
242 |
else |
7192f92c7
|
243 |
UART_PUT_CR(port, ATMEL_US_STPBRK); /* stop break */ |
1e6c9c287
|
244 245 246 247 248 |
} /* * Characters received (called from interrupt handler) */ |
7d12e780e
|
249 |
static void atmel_rx_chars(struct uart_port *port) |
1e6c9c287
|
250 |
{ |
9e6077bd8
|
251 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
1e6c9c287
|
252 253 |
struct tty_struct *tty = port->info->tty; unsigned int status, ch, flg; |
afefc4158
|
254 |
status = UART_GET_CSR(port); |
7192f92c7
|
255 |
while (status & ATMEL_US_RXRDY) { |
1e6c9c287
|
256 |
ch = UART_GET_CHAR(port); |
1e6c9c287
|
257 258 259 260 261 262 263 264 |
port->icount.rx++; flg = TTY_NORMAL; /* * note that the error handling code is * out of the main execution path */ |
9e6077bd8
|
265 266 267 |
if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME | ATMEL_US_OVRE | ATMEL_US_RXBRK) || atmel_port->break_active)) { |
7192f92c7
|
268 |
UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */ |
9e6077bd8
|
269 270 |
if (status & ATMEL_US_RXBRK && !atmel_port->break_active) { |
7192f92c7
|
271 |
status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */ |
afefc4158
|
272 |
port->icount.brk++; |
9e6077bd8
|
273 274 |
atmel_port->break_active = 1; UART_PUT_IER(port, ATMEL_US_RXBRK); |
afefc4158
|
275 276 |
if (uart_handle_break(port)) goto ignore_char; |
9e6077bd8
|
277 278 279 280 281 282 283 284 285 286 287 |
} else { /* * This is either the end-of-break * condition or we've received at * least one character without RXBRK * being set. In both cases, the next * RXBRK will indicate start-of-break. */ UART_PUT_IDR(port, ATMEL_US_RXBRK); status &= ~ATMEL_US_RXBRK; atmel_port->break_active = 0; |
afefc4158
|
288 |
} |
7192f92c7
|
289 |
if (status & ATMEL_US_PARE) |
1e6c9c287
|
290 |
port->icount.parity++; |
7192f92c7
|
291 |
if (status & ATMEL_US_FRAME) |
1e6c9c287
|
292 |
port->icount.frame++; |
7192f92c7
|
293 |
if (status & ATMEL_US_OVRE) |
1e6c9c287
|
294 |
port->icount.overrun++; |
afefc4158
|
295 |
status &= port->read_status_mask; |
7192f92c7
|
296 |
if (status & ATMEL_US_RXBRK) |
afefc4158
|
297 |
flg = TTY_BREAK; |
7192f92c7
|
298 |
else if (status & ATMEL_US_PARE) |
1e6c9c287
|
299 |
flg = TTY_PARITY; |
7192f92c7
|
300 |
else if (status & ATMEL_US_FRAME) |
1e6c9c287
|
301 |
flg = TTY_FRAME; |
1e6c9c287
|
302 |
} |
7d12e780e
|
303 |
if (uart_handle_sysrq_char(port, ch)) |
1e6c9c287
|
304 |
goto ignore_char; |
7192f92c7
|
305 |
uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg); |
1e6c9c287
|
306 307 |
ignore_char: |
afefc4158
|
308 |
status = UART_GET_CSR(port); |
1e6c9c287
|
309 310 311 312 313 314 315 316 |
} tty_flip_buffer_push(tty); } /* * Transmit characters (called from interrupt handler) */ |
7192f92c7
|
317 |
static void atmel_tx_chars(struct uart_port *port) |
1e6c9c287
|
318 319 320 321 322 323 324 325 326 327 |
{ struct circ_buf *xmit = &port->info->xmit; if (port->x_char) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { |
7192f92c7
|
328 |
atmel_stop_tx(port); |
1e6c9c287
|
329 330 |
return; } |
7192f92c7
|
331 |
while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { |
1e6c9c287
|
332 333 334 335 336 337 338 339 340 341 342 |
UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) |
7192f92c7
|
343 |
atmel_stop_tx(port); |
1e6c9c287
|
344 345 346 347 348 |
} /* * Interrupt handler */ |
7d12e780e
|
349 |
static irqreturn_t atmel_interrupt(int irq, void *dev_id) |
1e6c9c287
|
350 351 |
{ struct uart_port *port = dev_id; |
7192f92c7
|
352 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
1e6c9c287
|
353 354 355 |
unsigned int status, pending, pass_counter = 0; status = UART_GET_CSR(port); |
afefc4158
|
356 357 358 |
pending = status & UART_GET_IMR(port); while (pending) { /* Interrupt receive */ |
7192f92c7
|
359 |
if (pending & ATMEL_US_RXRDY) |
7d12e780e
|
360 |
atmel_rx_chars(port); |
9e6077bd8
|
361 362 363 364 365 366 367 368 369 370 |
else if (pending & ATMEL_US_RXBRK) { /* * End of break detected. If it came along * with a character, atmel_rx_chars will * handle it. */ UART_PUT_CR(port, ATMEL_US_RSTSTA); UART_PUT_IDR(port, ATMEL_US_RXBRK); atmel_port->break_active = 0; } |
afefc4158
|
371 372 |
// TODO: All reads to CSR will clear these interrupts! |
7192f92c7
|
373 374 375 376 377 378 379 |
if (pending & ATMEL_US_RIIC) port->icount.rng++; if (pending & ATMEL_US_DSRIC) port->icount.dsr++; if (pending & ATMEL_US_DCDIC) uart_handle_dcd_change(port, !(status & ATMEL_US_DCD)); if (pending & ATMEL_US_CTSIC) uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC)) |
afefc4158
|
380 381 382 |
wake_up_interruptible(&port->info->delta_msr_wait); /* Interrupt transmit */ |
7192f92c7
|
383 384 |
if (pending & ATMEL_US_TXRDY) atmel_tx_chars(port); |
afefc4158
|
385 |
|
7192f92c7
|
386 |
if (pass_counter++ > ATMEL_ISR_PASS_LIMIT) |
afefc4158
|
387 |
break; |
1e6c9c287
|
388 |
|
afefc4158
|
389 390 |
status = UART_GET_CSR(port); pending = status & UART_GET_IMR(port); |
1e6c9c287
|
391 392 393 394 395 396 397 |
} return IRQ_HANDLED; } /* * Perform initialization and enable port for reception */ |
7192f92c7
|
398 |
static int atmel_startup(struct uart_port *port) |
1e6c9c287
|
399 |
{ |
7192f92c7
|
400 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
1e6c9c287
|
401 402 403 404 405 406 407 408 409 410 411 412 |
int retval; /* * Ensure that no interrupts are enabled otherwise when * request_irq() is called we could get stuck trying to * handle an unexpected interrupt */ UART_PUT_IDR(port, -1); /* * Allocate the IRQ */ |
7192f92c7
|
413 |
retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, "atmel_serial", port); |
1e6c9c287
|
414 |
if (retval) { |
7192f92c7
|
415 416 |
printk("atmel_serial: atmel_startup - Can't get irq "); |
1e6c9c287
|
417 418 419 420 421 422 423 |
return retval; } /* * If there is a specific "open" function (to register * control line interrupts) */ |
71f2e2b87
|
424 425 |
if (atmel_open_hook) { retval = atmel_open_hook(port); |
1e6c9c287
|
426 427 428 429 430 |
if (retval) { free_irq(port->irq, port); return retval; } } |
1e6c9c287
|
431 432 433 |
/* * Finally, enable the serial port */ |
7192f92c7
|
434 435 |
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); /* enable xmit & rcvr */ |
afefc4158
|
436 |
|
7192f92c7
|
437 |
UART_PUT_IER(port, ATMEL_US_RXRDY); /* enable receive only */ |
afefc4158
|
438 |
|
1e6c9c287
|
439 440 441 442 443 444 |
return 0; } /* * Disable the port */ |
7192f92c7
|
445 |
static void atmel_shutdown(struct uart_port *port) |
1e6c9c287
|
446 |
{ |
7192f92c7
|
447 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
448 |
|
1e6c9c287
|
449 450 451 |
/* * Disable all interrupts, port and break condition. */ |
7192f92c7
|
452 |
UART_PUT_CR(port, ATMEL_US_RSTSTA); |
1e6c9c287
|
453 454 455 456 457 458 459 460 461 462 463 |
UART_PUT_IDR(port, -1); /* * Free the interrupt */ free_irq(port->irq, port); /* * If there is a specific "close" function (to unregister * control line interrupts) */ |
71f2e2b87
|
464 465 |
if (atmel_close_hook) atmel_close_hook(port); |
1e6c9c287
|
466 467 468 469 470 |
} /* * Power / Clock management. */ |
7192f92c7
|
471 |
static void atmel_serial_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) |
1e6c9c287
|
472 |
{ |
7192f92c7
|
473 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
474 |
|
1e6c9c287
|
475 476 477 478 479 480 |
switch (state) { case 0: /* * Enable the peripheral clock for this serial port. * This is called on uart_open() or a resume event. */ |
7192f92c7
|
481 |
clk_enable(atmel_port->clk); |
1e6c9c287
|
482 483 484 485 486 487 |
break; case 3: /* * Disable the peripheral clock for this serial port. * This is called on uart_close() or a suspend event. */ |
7192f92c7
|
488 |
clk_disable(atmel_port->clk); |
1e6c9c287
|
489 490 |
break; default: |
7192f92c7
|
491 492 |
printk(KERN_ERR "atmel_serial: unknown pm %d ", state); |
1e6c9c287
|
493 494 495 496 497 498 |
} } /* * Change the port parameters */ |
606d099cd
|
499 |
static void atmel_set_termios(struct uart_port *port, struct ktermios * termios, struct ktermios * old) |
1e6c9c287
|
500 501 502 |
{ unsigned long flags; unsigned int mode, imr, quot, baud; |
03abeac0a
|
503 504 |
/* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP | ATMEL_US_PAR); |
1e6c9c287
|
505 506 |
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); |
03abeac0a
|
507 508 509 510 |
if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */ quot /= 8; mode |= ATMEL_US_USCLKS_MCK_DIV8; } |
1e6c9c287
|
511 512 513 514 |
/* byte size */ switch (termios->c_cflag & CSIZE) { case CS5: |
7192f92c7
|
515 |
mode |= ATMEL_US_CHRL_5; |
1e6c9c287
|
516 517 |
break; case CS6: |
7192f92c7
|
518 |
mode |= ATMEL_US_CHRL_6; |
1e6c9c287
|
519 520 |
break; case CS7: |
7192f92c7
|
521 |
mode |= ATMEL_US_CHRL_7; |
1e6c9c287
|
522 523 |
break; default: |
7192f92c7
|
524 |
mode |= ATMEL_US_CHRL_8; |
1e6c9c287
|
525 526 527 528 529 |
break; } /* stop bits */ if (termios->c_cflag & CSTOPB) |
7192f92c7
|
530 |
mode |= ATMEL_US_NBSTOP_2; |
1e6c9c287
|
531 532 533 534 535 |
/* parity */ if (termios->c_cflag & PARENB) { if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */ if (termios->c_cflag & PARODD) |
7192f92c7
|
536 |
mode |= ATMEL_US_PAR_MARK; |
1e6c9c287
|
537 |
else |
7192f92c7
|
538 |
mode |= ATMEL_US_PAR_SPACE; |
1e6c9c287
|
539 540 |
} else if (termios->c_cflag & PARODD) |
7192f92c7
|
541 |
mode |= ATMEL_US_PAR_ODD; |
1e6c9c287
|
542 |
else |
7192f92c7
|
543 |
mode |= ATMEL_US_PAR_EVEN; |
1e6c9c287
|
544 545 |
} else |
7192f92c7
|
546 |
mode |= ATMEL_US_PAR_NONE; |
1e6c9c287
|
547 548 |
spin_lock_irqsave(&port->lock, flags); |
7192f92c7
|
549 |
port->read_status_mask = ATMEL_US_OVRE; |
1e6c9c287
|
550 |
if (termios->c_iflag & INPCK) |
7192f92c7
|
551 |
port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); |
1e6c9c287
|
552 |
if (termios->c_iflag & (BRKINT | PARMRK)) |
7192f92c7
|
553 |
port->read_status_mask |= ATMEL_US_RXBRK; |
1e6c9c287
|
554 555 556 557 558 559 |
/* * Characters to ignore */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) |
7192f92c7
|
560 |
port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); |
1e6c9c287
|
561 |
if (termios->c_iflag & IGNBRK) { |
7192f92c7
|
562 |
port->ignore_status_mask |= ATMEL_US_RXBRK; |
1e6c9c287
|
563 564 565 566 567 |
/* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) |
7192f92c7
|
568 |
port->ignore_status_mask |= ATMEL_US_OVRE; |
1e6c9c287
|
569 570 571 572 573 574 575 576 577 578 |
} // TODO: Ignore all characters if CREAD is set. /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); /* disable interrupts and drain transmitter */ imr = UART_GET_IMR(port); /* get interrupt mask */ UART_PUT_IDR(port, -1); /* disable all interrupts */ |
7192f92c7
|
579 |
while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) { barrier(); } |
1e6c9c287
|
580 581 |
/* disable receiver and transmitter */ |
7192f92c7
|
582 |
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); |
1e6c9c287
|
583 584 585 586 587 588 |
/* set the parity, stop bits and data size */ UART_PUT_MR(port, mode); /* set the baud rate */ UART_PUT_BRGR(port, quot); |
7192f92c7
|
589 590 |
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); |
1e6c9c287
|
591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
/* restore interrupts */ UART_PUT_IER(port, imr); /* CTS flow-control and modem-status interrupts */ if (UART_ENABLE_MS(port, termios->c_cflag)) port->ops->enable_ms(port); spin_unlock_irqrestore(&port->lock, flags); } /* * Return string describing the specified port */ |
7192f92c7
|
605 |
static const char *atmel_type(struct uart_port *port) |
1e6c9c287
|
606 |
{ |
9ab4f88b7
|
607 |
return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL; |
1e6c9c287
|
608 609 610 611 612 |
} /* * Release the memory region(s) being used by 'port'. */ |
7192f92c7
|
613 |
static void atmel_release_port(struct uart_port *port) |
1e6c9c287
|
614 |
{ |
afefc4158
|
615 616 617 618 619 620 621 622 623 |
struct platform_device *pdev = to_platform_device(port->dev); int size = pdev->resource[0].end - pdev->resource[0].start + 1; release_mem_region(port->mapbase, size); if (port->flags & UPF_IOREMAP) { iounmap(port->membase); port->membase = NULL; } |
1e6c9c287
|
624 625 626 627 628 |
} /* * Request the memory region(s) being used by 'port'. */ |
7192f92c7
|
629 |
static int atmel_request_port(struct uart_port *port) |
1e6c9c287
|
630 |
{ |
afefc4158
|
631 632 |
struct platform_device *pdev = to_platform_device(port->dev); int size = pdev->resource[0].end - pdev->resource[0].start + 1; |
7192f92c7
|
633 |
if (!request_mem_region(port->mapbase, size, "atmel_serial")) |
afefc4158
|
634 635 636 637 638 639 640 641 642 |
return -EBUSY; if (port->flags & UPF_IOREMAP) { port->membase = ioremap(port->mapbase, size); if (port->membase == NULL) { release_mem_region(port->mapbase, size); return -ENOMEM; } } |
1e6c9c287
|
643 |
|
afefc4158
|
644 |
return 0; |
1e6c9c287
|
645 646 647 648 649 |
} /* * Configure/autoconfigure the port. */ |
7192f92c7
|
650 |
static void atmel_config_port(struct uart_port *port, int flags) |
1e6c9c287
|
651 652 |
{ if (flags & UART_CONFIG_TYPE) { |
9ab4f88b7
|
653 |
port->type = PORT_ATMEL; |
7192f92c7
|
654 |
atmel_request_port(port); |
1e6c9c287
|
655 656 657 658 659 660 |
} } /* * Verify the new serial_struct (for TIOCSSERIAL). */ |
7192f92c7
|
661 |
static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) |
1e6c9c287
|
662 663 |
{ int ret = 0; |
9ab4f88b7
|
664 |
if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL) |
1e6c9c287
|
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
ret = -EINVAL; if (port->irq != ser->irq) ret = -EINVAL; if (ser->io_type != SERIAL_IO_MEM) ret = -EINVAL; if (port->uartclk / 16 != ser->baud_base) ret = -EINVAL; if ((void *)port->mapbase != ser->iomem_base) ret = -EINVAL; if (port->iobase != ser->port) ret = -EINVAL; if (ser->hub6 != 0) ret = -EINVAL; return ret; } |
7192f92c7
|
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 |
static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, .get_mctrl = atmel_get_mctrl, .stop_tx = atmel_stop_tx, .start_tx = atmel_start_tx, .stop_rx = atmel_stop_rx, .enable_ms = atmel_enable_ms, .break_ctl = atmel_break_ctl, .startup = atmel_startup, .shutdown = atmel_shutdown, .set_termios = atmel_set_termios, .type = atmel_type, .release_port = atmel_release_port, .request_port = atmel_request_port, .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, |
1e6c9c287
|
698 |
}; |
afefc4158
|
699 700 701 |
/* * Configure the port from the platform device resource info. */ |
7192f92c7
|
702 |
static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev) |
1e6c9c287
|
703 |
{ |
7192f92c7
|
704 |
struct uart_port *port = &atmel_port->uart; |
73e2798b0
|
705 |
struct atmel_uart_data *data = pdev->dev.platform_data; |
afefc4158
|
706 707 |
port->iotype = UPIO_MEM; |
a14d52730
|
708 |
port->flags = UPF_BOOT_AUTOCONF; |
7192f92c7
|
709 |
port->ops = &atmel_pops; |
a14d52730
|
710 |
port->fifosize = 1; |
afefc4158
|
711 712 713 714 715 |
port->line = pdev->id; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; |
75d352137
|
716 717 718 |
if (data->regs) /* Already mapped by setup code */ port->membase = data->regs; |
afefc4158
|
719 720 721 722 |
else { port->flags |= UPF_IOREMAP; port->membase = NULL; } |
1e6c9c287
|
723 |
|
7192f92c7
|
724 725 726 727 |
if (!atmel_port->clk) { /* for console, the clock could already be configured */ atmel_port->clk = clk_get(&pdev->dev, "usart"); clk_enable(atmel_port->clk); port->uartclk = clk_get_rate(atmel_port->clk); |
afefc4158
|
728 |
} |
1e6c9c287
|
729 |
} |
afefc4158
|
730 731 732 |
/* * Register board-specific modem-control line handlers. */ |
71f2e2b87
|
733 |
void __init atmel_register_uart_fns(struct atmel_port_fns *fns) |
1e6c9c287
|
734 735 |
{ if (fns->enable_ms) |
7192f92c7
|
736 |
atmel_pops.enable_ms = fns->enable_ms; |
1e6c9c287
|
737 |
if (fns->get_mctrl) |
7192f92c7
|
738 |
atmel_pops.get_mctrl = fns->get_mctrl; |
1e6c9c287
|
739 |
if (fns->set_mctrl) |
7192f92c7
|
740 |
atmel_pops.set_mctrl = fns->set_mctrl; |
71f2e2b87
|
741 742 |
atmel_open_hook = fns->open; atmel_close_hook = fns->close; |
7192f92c7
|
743 744 |
atmel_pops.pm = fns->pm; atmel_pops.set_wake = fns->set_wake; |
1e6c9c287
|
745 |
} |
1e6c9c287
|
746 |
|
749c4e603
|
747 |
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE |
7192f92c7
|
748 |
static void atmel_console_putchar(struct uart_port *port, int ch) |
d358788f3
|
749 |
{ |
7192f92c7
|
750 |
while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) |
d358788f3
|
751 752 753 |
barrier(); UART_PUT_CHAR(port, ch); } |
1e6c9c287
|
754 755 756 757 |
/* * Interrupts are disabled on entering */ |
7192f92c7
|
758 |
static void atmel_console_write(struct console *co, const char *s, u_int count) |
1e6c9c287
|
759 |
{ |
7192f92c7
|
760 |
struct uart_port *port = &atmel_ports[co->index].uart; |
d358788f3
|
761 |
unsigned int status, imr; |
1e6c9c287
|
762 763 764 765 766 |
/* * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); /* get interrupt mask */ |
7192f92c7
|
767 |
UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY); |
1e6c9c287
|
768 |
|
7192f92c7
|
769 |
uart_console_write(port, s, count, atmel_console_putchar); |
1e6c9c287
|
770 771 772 773 774 775 776 |
/* * Finally, wait for transmitter to become empty * and restore IMR */ do { status = UART_GET_CSR(port); |
7192f92c7
|
777 |
} while (!(status & ATMEL_US_TXRDY)); |
1e6c9c287
|
778 779 780 781 782 783 784 |
UART_PUT_IER(port, imr); /* set interrupts back the way they were */ } /* * If the port was already initialised (eg, by a boot loader), try to determine * the current setup. */ |
7192f92c7
|
785 |
static void __init atmel_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) |
1e6c9c287
|
786 787 788 789 790 791 |
{ unsigned int mr, quot; // TODO: CR is a write-only register // unsigned int cr; // |
7192f92c7
|
792 793 |
// cr = UART_GET_CR(port) & (ATMEL_US_RXEN | ATMEL_US_TXEN); // if (cr == (ATMEL_US_RXEN | ATMEL_US_TXEN)) { |
1e6c9c287
|
794 795 |
// /* ok, the port was enabled */ // } |
7192f92c7
|
796 797 |
mr = UART_GET_MR(port) & ATMEL_US_CHRL; if (mr == ATMEL_US_CHRL_8) |
1e6c9c287
|
798 799 800 |
*bits = 8; else *bits = 7; |
7192f92c7
|
801 802 |
mr = UART_GET_MR(port) & ATMEL_US_PAR; if (mr == ATMEL_US_PAR_EVEN) |
1e6c9c287
|
803 |
*parity = 'e'; |
7192f92c7
|
804 |
else if (mr == ATMEL_US_PAR_ODD) |
1e6c9c287
|
805 |
*parity = 'o'; |
4d5e392c3
|
806 807 808 809 810 811 |
/* * The serial core only rounds down when matching this to a * supported baud rate. Make sure we don't end up slightly * lower than one of those, as it would make us fall through * to a much lower baud rate than we really want. */ |
1e6c9c287
|
812 |
quot = UART_GET_BRGR(port); |
4d5e392c3
|
813 |
*baud = port->uartclk / (16 * (quot - 1)); |
1e6c9c287
|
814 |
} |
7192f92c7
|
815 |
static int __init atmel_console_setup(struct console *co, char *options) |
1e6c9c287
|
816 |
{ |
7192f92c7
|
817 |
struct uart_port *port = &atmel_ports[co->index].uart; |
1e6c9c287
|
818 819 820 821 |
int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n'; |
afefc4158
|
822 823 |
if (port->membase == 0) /* Port not initialized yet - delay setup */ return -ENODEV; |
1e6c9c287
|
824 |
|
1e6c9c287
|
825 |
UART_PUT_IDR(port, -1); /* disable interrupts */ |
7192f92c7
|
826 827 |
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); |
1e6c9c287
|
828 829 830 831 |
if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else |
7192f92c7
|
832 |
atmel_console_get_options(port, &baud, &parity, &bits); |
1e6c9c287
|
833 834 835 |
return uart_set_options(port, co, baud, parity, bits, flow); } |
7192f92c7
|
836 |
static struct uart_driver atmel_uart; |
1e6c9c287
|
837 |
|
7192f92c7
|
838 839 840 |
static struct console atmel_console = { .name = ATMEL_DEVICENAME, .write = atmel_console_write, |
1e6c9c287
|
841 |
.device = uart_console_device, |
7192f92c7
|
842 |
.setup = atmel_console_setup, |
1e6c9c287
|
843 844 |
.flags = CON_PRINTBUFFER, .index = -1, |
7192f92c7
|
845 |
.data = &atmel_uart, |
1e6c9c287
|
846 |
}; |
7192f92c7
|
847 |
#define ATMEL_CONSOLE_DEVICE &atmel_console |
1e6c9c287
|
848 |
|
afefc4158
|
849 850 851 |
/* * Early console initialization (before VM subsystem initialized). */ |
7192f92c7
|
852 |
static int __init atmel_console_init(void) |
1e6c9c287
|
853 |
{ |
73e2798b0
|
854 |
if (atmel_default_console_device) { |
7192f92c7
|
855 856 857 |
add_preferred_console(ATMEL_DEVICENAME, atmel_default_console_device->id, NULL); atmel_init_port(&(atmel_ports[atmel_default_console_device->id]), atmel_default_console_device); register_console(&atmel_console); |
afefc4158
|
858 |
} |
1e6c9c287
|
859 |
|
1e6c9c287
|
860 861 |
return 0; } |
7192f92c7
|
862 |
console_initcall(atmel_console_init); |
1e6c9c287
|
863 |
|
afefc4158
|
864 865 866 |
/* * Late console initialization. */ |
7192f92c7
|
867 |
static int __init atmel_late_console_init(void) |
afefc4158
|
868 |
{ |
7192f92c7
|
869 870 |
if (atmel_default_console_device && !(atmel_console.flags & CON_ENABLED)) register_console(&atmel_console); |
afefc4158
|
871 872 873 |
return 0; } |
7192f92c7
|
874 |
core_initcall(atmel_late_console_init); |
afefc4158
|
875 |
|
1e6c9c287
|
876 |
#else |
7192f92c7
|
877 |
#define ATMEL_CONSOLE_DEVICE NULL |
1e6c9c287
|
878 |
#endif |
7192f92c7
|
879 |
static struct uart_driver atmel_uart = { |
1e6c9c287
|
880 |
.owner = THIS_MODULE, |
7192f92c7
|
881 882 883 |
.driver_name = "atmel_serial", .dev_name = ATMEL_DEVICENAME, .major = SERIAL_ATMEL_MAJOR, |
1e6c9c287
|
884 |
.minor = MINOR_START, |
73e2798b0
|
885 |
.nr = ATMEL_MAX_UART, |
7192f92c7
|
886 |
.cons = ATMEL_CONSOLE_DEVICE, |
1e6c9c287
|
887 |
}; |
afefc4158
|
888 |
#ifdef CONFIG_PM |
7192f92c7
|
889 |
static int atmel_serial_suspend(struct platform_device *pdev, pm_message_t state) |
1e6c9c287
|
890 |
{ |
afefc4158
|
891 |
struct uart_port *port = platform_get_drvdata(pdev); |
7192f92c7
|
892 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
893 894 895 896 |
if (device_may_wakeup(&pdev->dev) && !at91_suspend_entering_slow_clock()) enable_irq_wake(port->irq); else { |
7192f92c7
|
897 898 |
uart_suspend_port(&atmel_uart, port); atmel_port->suspended = 1; |
afefc4158
|
899 |
} |
1e6c9c287
|
900 |
|
afefc4158
|
901 902 |
return 0; } |
1e6c9c287
|
903 |
|
7192f92c7
|
904 |
static int atmel_serial_resume(struct platform_device *pdev) |
afefc4158
|
905 906 |
{ struct uart_port *port = platform_get_drvdata(pdev); |
7192f92c7
|
907 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
1e6c9c287
|
908 |
|
7192f92c7
|
909 910 911 |
if (atmel_port->suspended) { uart_resume_port(&atmel_uart, port); atmel_port->suspended = 0; |
1e6c9c287
|
912 |
} |
9b9381669
|
913 914 |
else disable_irq_wake(port->irq); |
1e6c9c287
|
915 916 917 |
return 0; } |
afefc4158
|
918 |
#else |
7192f92c7
|
919 920 |
#define atmel_serial_suspend NULL #define atmel_serial_resume NULL |
afefc4158
|
921 |
#endif |
1e6c9c287
|
922 |
|
7192f92c7
|
923 |
static int __devinit atmel_serial_probe(struct platform_device *pdev) |
1e6c9c287
|
924 |
{ |
7192f92c7
|
925 |
struct atmel_uart_port *port; |
afefc4158
|
926 |
int ret; |
1e6c9c287
|
927 |
|
7192f92c7
|
928 929 |
port = &atmel_ports[pdev->id]; atmel_init_port(port, pdev); |
1e6c9c287
|
930 |
|
7192f92c7
|
931 |
ret = uart_add_one_port(&atmel_uart, &port->uart); |
afefc4158
|
932 933 934 935 936 937 938 |
if (!ret) { device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); } return ret; } |
7192f92c7
|
939 |
static int __devexit atmel_serial_remove(struct platform_device *pdev) |
afefc4158
|
940 941 |
{ struct uart_port *port = platform_get_drvdata(pdev); |
7192f92c7
|
942 |
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; |
afefc4158
|
943 |
int ret = 0; |
7192f92c7
|
944 945 |
clk_disable(atmel_port->clk); clk_put(atmel_port->clk); |
afefc4158
|
946 947 948 949 950 |
device_init_wakeup(&pdev->dev, 0); platform_set_drvdata(pdev, NULL); if (port) { |
7192f92c7
|
951 |
ret = uart_remove_one_port(&atmel_uart, port); |
afefc4158
|
952 953 954 955 956 |
kfree(port); } return ret; } |
7192f92c7
|
957 958 959 960 961 |
static struct platform_driver atmel_serial_driver = { .probe = atmel_serial_probe, .remove = __devexit_p(atmel_serial_remove), .suspend = atmel_serial_suspend, .resume = atmel_serial_resume, |
afefc4158
|
962 |
.driver = { |
1e8ea8021
|
963 |
.name = "atmel_usart", |
afefc4158
|
964 965 966 |
.owner = THIS_MODULE, }, }; |
7192f92c7
|
967 |
static int __init atmel_serial_init(void) |
afefc4158
|
968 969 |
{ int ret; |
7192f92c7
|
970 |
ret = uart_register_driver(&atmel_uart); |
afefc4158
|
971 972 |
if (ret) return ret; |
7192f92c7
|
973 |
ret = platform_driver_register(&atmel_serial_driver); |
afefc4158
|
974 |
if (ret) |
7192f92c7
|
975 |
uart_unregister_driver(&atmel_uart); |
afefc4158
|
976 977 978 |
return ret; } |
7192f92c7
|
979 |
static void __exit atmel_serial_exit(void) |
afefc4158
|
980 |
{ |
7192f92c7
|
981 982 |
platform_driver_unregister(&atmel_serial_driver); uart_unregister_driver(&atmel_uart); |
1e6c9c287
|
983 |
} |
7192f92c7
|
984 985 |
module_init(atmel_serial_init); module_exit(atmel_serial_exit); |
1e6c9c287
|
986 987 |
MODULE_AUTHOR("Rick Bronson"); |
7192f92c7
|
988 |
MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver"); |
1e6c9c287
|
989 |
MODULE_LICENSE("GPL"); |