Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: Add driver for the Cell Network Processor serial port NWP device

Add support for the nwp serial device which is connected to a DCR bus. It
uses the of_serial device driver to determine necessary properties from
the device tree. The supported device is added as serial port number 85.

NWP stands for network processor and it is part of the QPACE - Quantum
Chromodynamics Parallel Computing on the Cell Broadband Engine project.
The implementation is a lightweight uart implementation with the focus
to consume as little resources as possible and it is connected to a
DCR bus.

Signed-off-by: Benjamin Krill <ben@codiert.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Benjamin Krill and committed by
Benjamin Herrenschmidt
5886188d 2b79d696

+534 -1
+18 -1
drivers/serial/Kconfig
··· 1320 1320 config SERIAL_OF_PLATFORM 1321 1321 tristate "Serial port on Open Firmware platform bus" 1322 1322 depends on PPC_OF 1323 - depends on SERIAL_8250 1323 + depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL 1324 1324 help 1325 1325 If you have a PowerPC based system that has serial ports 1326 1326 on a platform specific bus, you should enable this option. 1327 1327 Currently, only 8250 compatible ports are supported, but 1328 1328 others can easily be added. 1329 + 1330 + config SERIAL_OF_PLATFORM_NWPSERIAL 1331 + tristate "NWP serial port driver" 1332 + depends on PPC_OF && PPC_DCR 1333 + select SERIAL_OF_PLATFORM 1334 + select SERIAL_CORE_CONSOLE 1335 + select SERIAL_CORE 1336 + help 1337 + This driver supports the cell network processor nwp serial 1338 + device. 1339 + 1340 + config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE 1341 + bool "Console on NWP serial port" 1342 + depends on SERIAL_OF_PLATFORM_NWPSERIAL=y 1343 + select SERIAL_CORE_CONSOLE 1344 + help 1345 + Support for Console on the NWP serial ports. 1329 1346 1330 1347 config SERIAL_QE 1331 1348 tristate "Freescale QUICC Engine serial port support"
+1
drivers/serial/Makefile
··· 72 72 obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o 73 73 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o 74 74 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o 75 + obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o 75 76 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o 76 77 obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o 77 78 obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
+475
drivers/serial/nwpserial.c
··· 1 + /* 2 + * Serial Port driver for a NWP uart device 3 + * 4 + * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.org> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + * 11 + */ 12 + #include <linux/init.h> 13 + #include <linux/console.h> 14 + #include <linux/serial.h> 15 + #include <linux/serial_reg.h> 16 + #include <linux/serial_core.h> 17 + #include <linux/tty.h> 18 + #include <linux/irqreturn.h> 19 + #include <linux/mutex.h> 20 + #include <linux/of_platform.h> 21 + #include <linux/of_device.h> 22 + #include <linux/nwpserial.h> 23 + #include <asm/prom.h> 24 + #include <asm/dcr.h> 25 + 26 + #define NWPSERIAL_NR 2 27 + 28 + #define NWPSERIAL_STATUS_RXVALID 0x1 29 + #define NWPSERIAL_STATUS_TXFULL 0x2 30 + 31 + struct nwpserial_port { 32 + struct uart_port port; 33 + dcr_host_t dcr_host; 34 + unsigned int ier; 35 + unsigned int mcr; 36 + }; 37 + 38 + static DEFINE_MUTEX(nwpserial_mutex); 39 + static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; 40 + 41 + static void wait_for_bits(struct nwpserial_port *up, int bits) 42 + { 43 + unsigned int status, tmout = 10000; 44 + 45 + /* Wait up to 10ms for the character(s) to be sent. */ 46 + do { 47 + status = dcr_read(up->dcr_host, UART_LSR); 48 + 49 + if (--tmout == 0) 50 + break; 51 + udelay(1); 52 + } while ((status & bits) != bits); 53 + } 54 + 55 + #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE 56 + static void nwpserial_console_putchar(struct uart_port *port, int c) 57 + { 58 + struct nwpserial_port *up; 59 + up = container_of(port, struct nwpserial_port, port); 60 + /* check if tx buffer is full */ 61 + wait_for_bits(up, UART_LSR_THRE); 62 + dcr_write(up->dcr_host, UART_TX, c); 63 + up->port.icount.tx++; 64 + } 65 + 66 + static void 67 + nwpserial_console_write(struct console *co, const char *s, unsigned int count) 68 + { 69 + struct nwpserial_port *up = &nwpserial_ports[co->index]; 70 + unsigned long flags; 71 + int locked = 1; 72 + 73 + if (oops_in_progress) 74 + locked = spin_trylock_irqsave(&up->port.lock, flags); 75 + else 76 + spin_lock_irqsave(&up->port.lock, flags); 77 + 78 + /* save and disable interrupt */ 79 + up->ier = dcr_read(up->dcr_host, UART_IER); 80 + dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); 81 + 82 + uart_console_write(&up->port, s, count, nwpserial_console_putchar); 83 + 84 + /* wait for transmitter to become emtpy */ 85 + while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) 86 + cpu_relax(); 87 + 88 + /* restore interrupt state */ 89 + dcr_write(up->dcr_host, UART_IER, up->ier); 90 + 91 + if (locked) 92 + spin_unlock_irqrestore(&up->port.lock, flags); 93 + } 94 + 95 + static struct uart_driver nwpserial_reg; 96 + static struct console nwpserial_console = { 97 + .name = "ttySQ", 98 + .write = nwpserial_console_write, 99 + .device = uart_console_device, 100 + .flags = CON_PRINTBUFFER, 101 + .index = -1, 102 + .data = &nwpserial_reg, 103 + }; 104 + #define NWPSERIAL_CONSOLE (&nwpserial_console) 105 + #else 106 + #define NWPSERIAL_CONSOLE NULL 107 + #endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ 108 + 109 + /**************************************************************************/ 110 + 111 + static int nwpserial_request_port(struct uart_port *port) 112 + { 113 + return 0; 114 + } 115 + 116 + static void nwpserial_release_port(struct uart_port *port) 117 + { 118 + /* N/A */ 119 + } 120 + 121 + static void nwpserial_config_port(struct uart_port *port, int flags) 122 + { 123 + port->type = PORT_NWPSERIAL; 124 + } 125 + 126 + static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) 127 + { 128 + struct nwpserial_port *up = dev_id; 129 + struct tty_struct *tty = up->port.info->port.tty; 130 + irqreturn_t ret; 131 + unsigned int iir; 132 + unsigned char ch; 133 + 134 + spin_lock(&up->port.lock); 135 + 136 + /* check if the uart was the interrupt source. */ 137 + iir = dcr_read(up->dcr_host, UART_IIR); 138 + if (!iir) { 139 + ret = IRQ_NONE; 140 + goto out; 141 + } 142 + 143 + do { 144 + up->port.icount.rx++; 145 + ch = dcr_read(up->dcr_host, UART_RX); 146 + if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) 147 + tty_insert_flip_char(tty, ch, TTY_NORMAL); 148 + } while (dcr_read(up->dcr_host, UART_RX) & UART_LSR_DR); 149 + 150 + tty_flip_buffer_push(tty); 151 + ret = IRQ_HANDLED; 152 + 153 + out: 154 + spin_unlock(&up->port.lock); 155 + return ret; 156 + } 157 + 158 + static int nwpserial_startup(struct uart_port *port) 159 + { 160 + struct nwpserial_port *up; 161 + int err; 162 + 163 + up = container_of(port, struct nwpserial_port, port); 164 + 165 + /* disable flow control by default */ 166 + up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; 167 + dcr_write(up->dcr_host, UART_MCR, up->mcr); 168 + 169 + /* register interrupt handler */ 170 + err = request_irq(up->port.irq, nwpserial_interrupt, 171 + IRQF_SHARED, "nwpserial", up); 172 + if (err) 173 + return err; 174 + 175 + /* enable interrupts */ 176 + up->ier = UART_IER_RDI; 177 + dcr_write(up->dcr_host, UART_IER, up->ier); 178 + 179 + /* enable receiving */ 180 + up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; 181 + 182 + return 0; 183 + } 184 + 185 + static void nwpserial_shutdown(struct uart_port *port) 186 + { 187 + struct nwpserial_port *up; 188 + up = container_of(port, struct nwpserial_port, port); 189 + 190 + /* disable receiving */ 191 + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; 192 + 193 + /* disable interrupts from this port */ 194 + up->ier = 0; 195 + dcr_write(up->dcr_host, UART_IER, up->ier); 196 + 197 + /* free irq */ 198 + free_irq(up->port.irq, port); 199 + } 200 + 201 + static int nwpserial_verify_port(struct uart_port *port, 202 + struct serial_struct *ser) 203 + { 204 + return -EINVAL; 205 + } 206 + 207 + static const char *nwpserial_type(struct uart_port *port) 208 + { 209 + return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; 210 + } 211 + 212 + static void nwpserial_set_termios(struct uart_port *port, 213 + struct ktermios *termios, struct ktermios *old) 214 + { 215 + struct nwpserial_port *up; 216 + up = container_of(port, struct nwpserial_port, port); 217 + 218 + up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID 219 + | NWPSERIAL_STATUS_TXFULL; 220 + 221 + up->port.ignore_status_mask = 0; 222 + /* ignore all characters if CREAD is not set */ 223 + if ((termios->c_cflag & CREAD) == 0) 224 + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; 225 + 226 + /* Copy back the old hardware settings */ 227 + if (old) 228 + tty_termios_copy_hw(termios, old); 229 + } 230 + 231 + static void nwpserial_break_ctl(struct uart_port *port, int ctl) 232 + { 233 + /* N/A */ 234 + } 235 + 236 + static void nwpserial_enable_ms(struct uart_port *port) 237 + { 238 + /* N/A */ 239 + } 240 + 241 + static void nwpserial_stop_rx(struct uart_port *port) 242 + { 243 + struct nwpserial_port *up; 244 + up = container_of(port, struct nwpserial_port, port); 245 + /* don't forward any more data (like !CREAD) */ 246 + up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; 247 + } 248 + 249 + static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) 250 + { 251 + /* check if tx buffer is full */ 252 + wait_for_bits(up, UART_LSR_THRE); 253 + dcr_write(up->dcr_host, UART_TX, c); 254 + up->port.icount.tx++; 255 + } 256 + 257 + static void nwpserial_start_tx(struct uart_port *port) 258 + { 259 + struct nwpserial_port *up; 260 + struct circ_buf *xmit; 261 + up = container_of(port, struct nwpserial_port, port); 262 + xmit = &up->port.info->xmit; 263 + 264 + if (port->x_char) { 265 + nwpserial_putchar(up, up->port.x_char); 266 + port->x_char = 0; 267 + } 268 + 269 + while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { 270 + nwpserial_putchar(up, xmit->buf[xmit->tail]); 271 + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); 272 + } 273 + } 274 + 275 + static unsigned int nwpserial_get_mctrl(struct uart_port *port) 276 + { 277 + return 0; 278 + } 279 + 280 + static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) 281 + { 282 + /* N/A */ 283 + } 284 + 285 + static void nwpserial_stop_tx(struct uart_port *port) 286 + { 287 + /* N/A */ 288 + } 289 + 290 + static unsigned int nwpserial_tx_empty(struct uart_port *port) 291 + { 292 + struct nwpserial_port *up; 293 + unsigned long flags; 294 + int ret; 295 + up = container_of(port, struct nwpserial_port, port); 296 + 297 + spin_lock_irqsave(&up->port.lock, flags); 298 + ret = dcr_read(up->dcr_host, UART_LSR); 299 + spin_unlock_irqrestore(&up->port.lock, flags); 300 + 301 + return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; 302 + } 303 + 304 + static struct uart_ops nwpserial_pops = { 305 + .tx_empty = nwpserial_tx_empty, 306 + .set_mctrl = nwpserial_set_mctrl, 307 + .get_mctrl = nwpserial_get_mctrl, 308 + .stop_tx = nwpserial_stop_tx, 309 + .start_tx = nwpserial_start_tx, 310 + .stop_rx = nwpserial_stop_rx, 311 + .enable_ms = nwpserial_enable_ms, 312 + .break_ctl = nwpserial_break_ctl, 313 + .startup = nwpserial_startup, 314 + .shutdown = nwpserial_shutdown, 315 + .set_termios = nwpserial_set_termios, 316 + .type = nwpserial_type, 317 + .release_port = nwpserial_release_port, 318 + .request_port = nwpserial_request_port, 319 + .config_port = nwpserial_config_port, 320 + .verify_port = nwpserial_verify_port, 321 + }; 322 + 323 + static struct uart_driver nwpserial_reg = { 324 + .owner = THIS_MODULE, 325 + .driver_name = "nwpserial", 326 + .dev_name = "ttySQ", 327 + .major = TTY_MAJOR, 328 + .minor = 68, 329 + .nr = NWPSERIAL_NR, 330 + .cons = NWPSERIAL_CONSOLE, 331 + }; 332 + 333 + int nwpserial_register_port(struct uart_port *port) 334 + { 335 + struct nwpserial_port *up = NULL; 336 + int ret = -1; 337 + int i; 338 + static int first = 1; 339 + int dcr_len; 340 + int dcr_base; 341 + struct device_node *dn; 342 + 343 + mutex_lock(&nwpserial_mutex); 344 + 345 + dn = to_of_device(port->dev)->node; 346 + if (dn == NULL) 347 + goto out; 348 + 349 + /* get dcr base. */ 350 + dcr_base = dcr_resource_start(dn, 0); 351 + 352 + /* find matching entry */ 353 + for (i = 0; i < NWPSERIAL_NR; i++) 354 + if (nwpserial_ports[i].port.iobase == dcr_base) { 355 + up = &nwpserial_ports[i]; 356 + break; 357 + } 358 + 359 + /* we didn't find a mtching entry, search for a free port */ 360 + if (up == NULL) 361 + for (i = 0; i < NWPSERIAL_NR; i++) 362 + if (nwpserial_ports[i].port.type == PORT_UNKNOWN && 363 + nwpserial_ports[i].port.iobase == 0) { 364 + up = &nwpserial_ports[i]; 365 + break; 366 + } 367 + 368 + if (up == NULL) { 369 + ret = -EBUSY; 370 + goto out; 371 + } 372 + 373 + if (first) 374 + uart_register_driver(&nwpserial_reg); 375 + first = 0; 376 + 377 + up->port.membase = port->membase; 378 + up->port.irq = port->irq; 379 + up->port.uartclk = port->uartclk; 380 + up->port.fifosize = port->fifosize; 381 + up->port.regshift = port->regshift; 382 + up->port.iotype = port->iotype; 383 + up->port.flags = port->flags; 384 + up->port.mapbase = port->mapbase; 385 + up->port.private_data = port->private_data; 386 + 387 + if (port->dev) 388 + up->port.dev = port->dev; 389 + 390 + if (up->port.iobase != dcr_base) { 391 + up->port.ops = &nwpserial_pops; 392 + up->port.fifosize = 16; 393 + 394 + spin_lock_init(&up->port.lock); 395 + 396 + up->port.iobase = dcr_base; 397 + dcr_len = dcr_resource_len(dn, 0); 398 + 399 + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); 400 + if (!DCR_MAP_OK(up->dcr_host)) { 401 + printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); 402 + goto out; 403 + } 404 + } 405 + 406 + ret = uart_add_one_port(&nwpserial_reg, &up->port); 407 + if (ret == 0) 408 + ret = up->port.line; 409 + 410 + out: 411 + mutex_unlock(&nwpserial_mutex); 412 + 413 + return ret; 414 + } 415 + EXPORT_SYMBOL(nwpserial_register_port); 416 + 417 + void nwpserial_unregister_port(int line) 418 + { 419 + struct nwpserial_port *up = &nwpserial_ports[line]; 420 + mutex_lock(&nwpserial_mutex); 421 + uart_remove_one_port(&nwpserial_reg, &up->port); 422 + 423 + up->port.type = PORT_UNKNOWN; 424 + 425 + mutex_unlock(&nwpserial_mutex); 426 + } 427 + EXPORT_SYMBOL(nwpserial_unregister_port); 428 + 429 + #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE 430 + static int __init nwpserial_console_init(void) 431 + { 432 + struct nwpserial_port *up = NULL; 433 + struct device_node *dn; 434 + const char *name; 435 + int dcr_base; 436 + int dcr_len; 437 + int i; 438 + 439 + /* search for a free port */ 440 + for (i = 0; i < NWPSERIAL_NR; i++) 441 + if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { 442 + up = &nwpserial_ports[i]; 443 + break; 444 + } 445 + 446 + if (up == NULL) 447 + return -1; 448 + 449 + name = of_get_property(of_chosen, "linux,stdout-path", NULL); 450 + if (name == NULL) 451 + return -1; 452 + 453 + dn = of_find_node_by_path(name); 454 + if (!dn) 455 + return -1; 456 + 457 + spin_lock_init(&up->port.lock); 458 + up->port.ops = &nwpserial_pops; 459 + up->port.type = PORT_NWPSERIAL; 460 + up->port.fifosize = 16; 461 + 462 + dcr_base = dcr_resource_start(dn, 0); 463 + dcr_len = dcr_resource_len(dn, 0); 464 + up->port.iobase = dcr_base; 465 + 466 + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); 467 + if (!DCR_MAP_OK(up->dcr_host)) { 468 + printk("Cannot map DCR resources for SERIAL"); 469 + return -1; 470 + } 471 + register_console(&nwpserial_console); 472 + return 0; 473 + } 474 + console_initcall(nwpserial_console_init); 475 + #endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
+19
drivers/serial/of_serial.c
··· 14 14 #include <linux/serial_core.h> 15 15 #include <linux/serial_8250.h> 16 16 #include <linux/of_platform.h> 17 + #include <linux/nwpserial.h> 17 18 18 19 #include <asm/prom.h> 19 20 ··· 100 99 goto out; 101 100 102 101 switch (port_type) { 102 + #ifdef CONFIG_SERIAL_8250 103 103 case PORT_8250 ... PORT_MAX_8250: 104 104 ret = serial8250_register_port(&port); 105 105 break; 106 + #endif 107 + #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 108 + case PORT_NWPSERIAL: 109 + ret = nwpserial_register_port(&port); 110 + break; 111 + #endif 106 112 default: 107 113 /* need to add code for these */ 108 114 case PORT_UNKNOWN: ··· 137 129 { 138 130 struct of_serial_info *info = ofdev->dev.driver_data; 139 131 switch (info->type) { 132 + #ifdef CONFIG_SERIAL_8250 140 133 case PORT_8250 ... PORT_MAX_8250: 141 134 serial8250_unregister_port(info->line); 142 135 break; 136 + #endif 137 + #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 138 + case PORT_NWPSERIAL: 139 + nwpserial_unregister_port(info->line); 140 + break; 141 + #endif 143 142 default: 144 143 /* need to add code for these */ 145 144 break; ··· 163 148 { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, 164 149 { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, 165 150 { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, 151 + #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 152 + { .type = "serial", .compatible = "ibm,qpace-nwp-serial", 153 + .data = (void *)PORT_NWPSERIAL, }, 154 + #endif 166 155 { .type = "serial", .data = (void *)PORT_UNKNOWN, }, 167 156 { /* end of list */ }, 168 157 };
+18
include/linux/nwpserial.h
··· 1 + /* 2 + * Serial Port driver for a NWP uart device 3 + * 4 + * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.org> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + * 11 + */ 12 + #ifndef _NWPSERIAL_H 13 + #define _NWPSERIAL_H 14 + 15 + int nwpserial_register_port(struct uart_port *port); 16 + void nwpserial_unregister_port(int line); 17 + 18 + #endif /* _NWPSERIAL_H */
+3
include/linux/serial_core.h
··· 161 161 162 162 #define PORT_S3C6400 84 163 163 164 + /* NWPSERIAL */ 165 + #define PORT_NWPSERIAL 85 166 + 164 167 #ifdef __KERNEL__ 165 168 166 169 #include <linux/compiler.h>