519 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the serial port on the 21285 StrongArm-110 core logic chip.4 *5 * Based on drivers/char/serial.c6 */7#include <linux/module.h>8#include <linux/tty.h>9#include <linux/ioport.h>10#include <linux/init.h>11#include <linux/console.h>12#include <linux/device.h>13#include <linux/tty_flip.h>14#include <linux/serial_core.h>15#include <linux/serial.h>16#include <linux/io.h>17 18#include <asm/irq.h>19#include <asm/mach-types.h>20#include <asm/system_info.h>21#include <asm/hardware/dec21285.h>22#include <mach/hardware.h>23 24#define BAUD_BASE (mem_fclk_21285/64)25 26#define SERIAL_21285_NAME "ttyFB"27#define SERIAL_21285_MAJOR 20428#define SERIAL_21285_MINOR 429 30#define RXSTAT_DUMMY_READ 0x8000000031#define RXSTAT_FRAME (1 << 0)32#define RXSTAT_PARITY (1 << 1)33#define RXSTAT_OVERRUN (1 << 2)34#define RXSTAT_ANYERR (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN)35 36#define H_UBRLCR_BREAK (1 << 0)37#define H_UBRLCR_PARENB (1 << 1)38#define H_UBRLCR_PAREVN (1 << 2)39#define H_UBRLCR_STOPB (1 << 3)40#define H_UBRLCR_FIFO (1 << 4)41 42static const char serial21285_name[] = "Footbridge UART";43 44/*45 * We only need 2 bits of data, so instead of creating a whole structure for46 * this, use bits of the private_data pointer of the uart port structure.47 */48#define tx_enabled_bit 049#define rx_enabled_bit 150 51static bool is_enabled(struct uart_port *port, int bit)52{53 unsigned long *private_data = (unsigned long *)&port->private_data;54 55 if (test_bit(bit, private_data))56 return true;57 return false;58}59 60static void enable(struct uart_port *port, int bit)61{62 unsigned long *private_data = (unsigned long *)&port->private_data;63 64 set_bit(bit, private_data);65}66 67static void disable(struct uart_port *port, int bit)68{69 unsigned long *private_data = (unsigned long *)&port->private_data;70 71 clear_bit(bit, private_data);72}73 74#define is_tx_enabled(port) is_enabled(port, tx_enabled_bit)75#define tx_enable(port) enable(port, tx_enabled_bit)76#define tx_disable(port) disable(port, tx_enabled_bit)77 78#define is_rx_enabled(port) is_enabled(port, rx_enabled_bit)79#define rx_enable(port) enable(port, rx_enabled_bit)80#define rx_disable(port) disable(port, rx_enabled_bit)81 82/*83 * The documented expression for selecting the divisor is:84 * BAUD_BASE / baud - 185 * However, typically BAUD_BASE is not divisible by baud, so86 * we want to select the divisor that gives us the minimum87 * error. Therefore, we want:88 * int(BAUD_BASE / baud - 0.5) ->89 * int(BAUD_BASE / baud - (baud >> 1) / baud) ->90 * int((BAUD_BASE - (baud >> 1)) / baud)91 */92 93static void serial21285_stop_tx(struct uart_port *port)94{95 if (is_tx_enabled(port)) {96 disable_irq_nosync(IRQ_CONTX);97 tx_disable(port);98 }99}100 101static void serial21285_start_tx(struct uart_port *port)102{103 if (!is_tx_enabled(port)) {104 enable_irq(IRQ_CONTX);105 tx_enable(port);106 }107}108 109static void serial21285_stop_rx(struct uart_port *port)110{111 if (is_rx_enabled(port)) {112 disable_irq_nosync(IRQ_CONRX);113 rx_disable(port);114 }115}116 117static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)118{119 struct uart_port *port = dev_id;120 unsigned int status, rxs, max_count = 256;121 u8 ch, flag;122 123 status = *CSR_UARTFLG;124 while (!(status & 0x10) && max_count--) {125 ch = *CSR_UARTDR;126 flag = TTY_NORMAL;127 port->icount.rx++;128 129 rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;130 if (unlikely(rxs & RXSTAT_ANYERR)) {131 if (rxs & RXSTAT_PARITY)132 port->icount.parity++;133 else if (rxs & RXSTAT_FRAME)134 port->icount.frame++;135 if (rxs & RXSTAT_OVERRUN)136 port->icount.overrun++;137 138 rxs &= port->read_status_mask;139 140 if (rxs & RXSTAT_PARITY)141 flag = TTY_PARITY;142 else if (rxs & RXSTAT_FRAME)143 flag = TTY_FRAME;144 }145 146 uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag);147 148 status = *CSR_UARTFLG;149 }150 tty_flip_buffer_push(&port->state->port);151 152 return IRQ_HANDLED;153}154 155static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)156{157 struct uart_port *port = dev_id;158 u8 ch;159 160 uart_port_tx_limited(port, ch, 256,161 !(*CSR_UARTFLG & 0x20),162 *CSR_UARTDR = ch,163 ({}));164 165 return IRQ_HANDLED;166}167 168static unsigned int serial21285_tx_empty(struct uart_port *port)169{170 return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT;171}172 173/* no modem control lines */174static unsigned int serial21285_get_mctrl(struct uart_port *port)175{176 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;177}178 179static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl)180{181}182 183static void serial21285_break_ctl(struct uart_port *port, int break_state)184{185 unsigned long flags;186 unsigned int h_lcr;187 188 uart_port_lock_irqsave(port, &flags);189 h_lcr = *CSR_H_UBRLCR;190 if (break_state)191 h_lcr |= H_UBRLCR_BREAK;192 else193 h_lcr &= ~H_UBRLCR_BREAK;194 *CSR_H_UBRLCR = h_lcr;195 uart_port_unlock_irqrestore(port, flags);196}197 198static int serial21285_startup(struct uart_port *port)199{200 int ret;201 202 tx_enable(port);203 rx_enable(port);204 205 ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0,206 serial21285_name, port);207 if (ret == 0) {208 ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0,209 serial21285_name, port);210 if (ret)211 free_irq(IRQ_CONRX, port);212 }213 214 return ret;215}216 217static void serial21285_shutdown(struct uart_port *port)218{219 free_irq(IRQ_CONTX, port);220 free_irq(IRQ_CONRX, port);221}222 223static void224serial21285_set_termios(struct uart_port *port, struct ktermios *termios,225 const struct ktermios *old)226{227 unsigned long flags;228 unsigned int baud, quot, h_lcr, b;229 230 /*231 * We don't support modem control lines.232 */233 termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);234 termios->c_cflag |= CLOCAL;235 236 /*237 * We don't support BREAK character recognition.238 */239 termios->c_iflag &= ~(IGNBRK | BRKINT);240 241 /*242 * Ask the core to calculate the divisor for us.243 */244 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 245 quot = uart_get_divisor(port, baud);246 b = port->uartclk / (16 * quot);247 tty_termios_encode_baud_rate(termios, b, b);248 249 switch (termios->c_cflag & CSIZE) {250 case CS5:251 h_lcr = 0x00;252 break;253 case CS6:254 h_lcr = 0x20;255 break;256 case CS7:257 h_lcr = 0x40;258 break;259 default: /* CS8 */260 h_lcr = 0x60;261 break;262 }263 264 if (termios->c_cflag & CSTOPB)265 h_lcr |= H_UBRLCR_STOPB;266 if (termios->c_cflag & PARENB) {267 h_lcr |= H_UBRLCR_PARENB;268 if (!(termios->c_cflag & PARODD))269 h_lcr |= H_UBRLCR_PAREVN;270 }271 272 if (port->fifosize)273 h_lcr |= H_UBRLCR_FIFO;274 275 uart_port_lock_irqsave(port, &flags);276 277 /*278 * Update the per-port timeout.279 */280 uart_update_timeout(port, termios->c_cflag, baud);281 282 /*283 * Which character status flags are we interested in?284 */285 port->read_status_mask = RXSTAT_OVERRUN;286 if (termios->c_iflag & INPCK)287 port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;288 289 /*290 * Which character status flags should we ignore?291 */292 port->ignore_status_mask = 0;293 if (termios->c_iflag & IGNPAR)294 port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;295 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)296 port->ignore_status_mask |= RXSTAT_OVERRUN;297 298 /*299 * Ignore all characters if CREAD is not set.300 */301 if ((termios->c_cflag & CREAD) == 0)302 port->ignore_status_mask |= RXSTAT_DUMMY_READ;303 304 quot -= 1;305 306 *CSR_UARTCON = 0;307 *CSR_L_UBRLCR = quot & 0xff;308 *CSR_M_UBRLCR = (quot >> 8) & 0x0f;309 *CSR_H_UBRLCR = h_lcr;310 *CSR_UARTCON = 1;311 312 uart_port_unlock_irqrestore(port, flags);313}314 315static const char *serial21285_type(struct uart_port *port)316{317 return port->type == PORT_21285 ? "DC21285" : NULL;318}319 320static void serial21285_release_port(struct uart_port *port)321{322 release_mem_region(port->mapbase, 32);323}324 325static int serial21285_request_port(struct uart_port *port)326{327 return request_mem_region(port->mapbase, 32, serial21285_name)328 != NULL ? 0 : -EBUSY;329}330 331static void serial21285_config_port(struct uart_port *port, int flags)332{333 if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0)334 port->type = PORT_21285;335}336 337/*338 * verify the new serial_struct (for TIOCSSERIAL).339 */340static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser)341{342 int ret = 0;343 if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)344 ret = -EINVAL;345 if (ser->irq <= 0)346 ret = -EINVAL;347 if (ser->baud_base != port->uartclk / 16)348 ret = -EINVAL;349 return ret;350}351 352static const struct uart_ops serial21285_ops = {353 .tx_empty = serial21285_tx_empty,354 .get_mctrl = serial21285_get_mctrl,355 .set_mctrl = serial21285_set_mctrl,356 .stop_tx = serial21285_stop_tx,357 .start_tx = serial21285_start_tx,358 .stop_rx = serial21285_stop_rx,359 .break_ctl = serial21285_break_ctl,360 .startup = serial21285_startup,361 .shutdown = serial21285_shutdown,362 .set_termios = serial21285_set_termios,363 .type = serial21285_type,364 .release_port = serial21285_release_port,365 .request_port = serial21285_request_port,366 .config_port = serial21285_config_port,367 .verify_port = serial21285_verify_port,368};369 370static struct uart_port serial21285_port = {371 .mapbase = 0x42000160,372 .iotype = UPIO_MEM,373 .irq = 0,374 .fifosize = 16,375 .ops = &serial21285_ops,376 .flags = UPF_BOOT_AUTOCONF,377};378 379static void serial21285_setup_ports(void)380{381 serial21285_port.uartclk = mem_fclk_21285 / 4;382}383 384#ifdef CONFIG_SERIAL_21285_CONSOLE385static void serial21285_console_putchar(struct uart_port *port, unsigned char ch)386{387 while (*CSR_UARTFLG & 0x20)388 barrier();389 *CSR_UARTDR = ch;390}391 392static void393serial21285_console_write(struct console *co, const char *s,394 unsigned int count)395{396 uart_console_write(&serial21285_port, s, count, serial21285_console_putchar);397}398 399static void __init400serial21285_get_options(struct uart_port *port, int *baud,401 int *parity, int *bits)402{403 if (*CSR_UARTCON == 1) {404 unsigned int tmp;405 406 tmp = *CSR_H_UBRLCR;407 switch (tmp & 0x60) {408 case 0x00:409 *bits = 5;410 break;411 case 0x20:412 *bits = 6;413 break;414 case 0x40:415 *bits = 7;416 break;417 default:418 case 0x60:419 *bits = 8;420 break;421 }422 423 if (tmp & H_UBRLCR_PARENB) {424 *parity = 'o';425 if (tmp & H_UBRLCR_PAREVN)426 *parity = 'e';427 }428 429 tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8);430 431 *baud = port->uartclk / (16 * (tmp + 1));432 }433}434 435static int __init serial21285_console_setup(struct console *co, char *options)436{437 struct uart_port *port = &serial21285_port;438 int baud = 9600;439 int bits = 8;440 int parity = 'n';441 int flow = 'n';442 443 /*444 * Check whether an invalid uart number has been specified, and445 * if so, search for the first available port that does have446 * console support.447 */448 if (options)449 uart_parse_options(options, &baud, &parity, &bits, &flow);450 else451 serial21285_get_options(port, &baud, &parity, &bits);452 453 return uart_set_options(port, co, baud, parity, bits, flow);454}455 456static struct uart_driver serial21285_reg;457 458static struct console serial21285_console =459{460 .name = SERIAL_21285_NAME,461 .write = serial21285_console_write,462 .device = uart_console_device,463 .setup = serial21285_console_setup,464 .flags = CON_PRINTBUFFER,465 .index = -1,466 .data = &serial21285_reg,467};468 469static int __init rs285_console_init(void)470{471 serial21285_setup_ports();472 register_console(&serial21285_console);473 return 0;474}475console_initcall(rs285_console_init);476 477#define SERIAL_21285_CONSOLE &serial21285_console478#else479#define SERIAL_21285_CONSOLE NULL480#endif481 482static struct uart_driver serial21285_reg = {483 .owner = THIS_MODULE,484 .driver_name = "ttyFB",485 .dev_name = "ttyFB",486 .major = SERIAL_21285_MAJOR,487 .minor = SERIAL_21285_MINOR,488 .nr = 1,489 .cons = SERIAL_21285_CONSOLE,490};491 492static int __init serial21285_init(void)493{494 int ret;495 496 printk(KERN_INFO "Serial: 21285 driver\n");497 498 serial21285_setup_ports();499 500 ret = uart_register_driver(&serial21285_reg);501 if (ret == 0)502 uart_add_one_port(&serial21285_reg, &serial21285_port);503 504 return ret;505}506 507static void __exit serial21285_exit(void)508{509 uart_remove_one_port(&serial21285_reg, &serial21285_port);510 uart_unregister_driver(&serial21285_reg);511}512 513module_init(serial21285_init);514module_exit(serial21285_exit);515 516MODULE_LICENSE("GPL");517MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver");518MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);519