/* * Based on drivers/serial/pxa.c by Nicolas Pitre * * Author: Troy Kisky * Copyright: (C) 2011 Boundary Devices * */ #if defined(CONFIG_SERIAL_SC16IS7XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sc16is7xx.h" static struct uart_sc16is7xx_sc *serial_sc16is7xx_sc; static const unsigned short reg_map[] = { /* always accessible */ [SC_LCR] = 3, /* LCR != 0xBF, DLAB = 0 */ [SC_RHR] = (0 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_DLAB, 0) + _RO + _V), [SC_THR] = (0 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_DLAB, 0) + _WO + _V), [SC_IER] = (1 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_DLAB, 0)), /* LCR != 0xBF, DLAB = 1 */ [SC_DLL] = (0 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_DLAB, 1)), [SC_DLH] = (1 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_DLAB, 1)), /* LCR != 0xBF */ [SC_IIR] = (2 + A_TYPE(SP_0xBF, 0) + _RO + _V), [SC_FCR] = (2 + A_TYPE(SP_0xBF, 0) + _WO + _V), [SC_MCR] = (4 + A_TYPE(SP_0xBF, 0)), [SC_LSR] = (5 + A_TYPE(SP_0xBF, 0) + _RO + _V), [SC_TXLVL] = (8 + A_TYPE(SP_0xBF, 0) + _RO + _V), [SC_RXLVL] = (9 + A_TYPE(SP_0xBF, 0) + _RO + _V), [SC_IODIR] = (0x0A + A_TYPE(SP_0xBF, 0)), [SC_IOSTATE_R] = (0x0B + A_TYPE(SP_0xBF, 0) + _RO + _V), [SC_IOSTATE_W] = (0x0B + A_TYPE(SP_0xBF, 0) + _WO), [SC_IOINTENA] = (0x0C + A_TYPE(SP_0xBF, 0)), [SC_IOCONTROL] = (0x0E + A_TYPE(SP_0xBF, 0)), [SC_EFCR] = (0x0F + A_TYPE(SP_0xBF, 0)), /* LCR != 0xBF, EFR[4]=0 || MCR[2]=0 */ [SC_MSR] = (6 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_MCR2, 0) + _RO + _V), [SC_SPR] = (7 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_MCR2, 0)), /* LCR != 0xBF, EFR[4]=1 && MCR[2]=1 */ [SC_TCR] = (6 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_MCR2, 1)), [SC_TLR] = (7 + A_TYPE(SP_0xBF, 0) + A_TYPE(SP_MCR2, 1)), /* LCR == 0xBF */ [SC_EFR] = (2 + A_TYPE(SP_0xBF, 1)), [SC_XON1] = (4 + A_TYPE(SP_0xBF, 1)), [SC_XON2] = (5 + A_TYPE(SP_0xBF, 1)), [SC_XOFF1] = (6 + A_TYPE(SP_0xBF, 1)), [SC_XOFF2] = (7 + A_TYPE(SP_0xBF, 1)), }; static int serial_in(struct uart_sc16is7xx_chan *ch, enum sc_reg reg) { unsigned mreg; if (reg > ARRAY_SIZE(reg_map)) return -EINVAL; mreg = reg_map[reg]; if (mreg & _V) if (reg != SC_MSR) pr_err("%s: error reading volatile register %i\n", __func__, reg); if (reg < SC_CHAN_REG_CNT) return ch->reg_cache[reg]; else return ch->sc->dev_cache[reg - SC_CHAN_REG_CNT]; } struct i2c_work* get_work_entry(struct uart_sc16is7xx_sc *sc) { struct i2c_work* w; spin_lock(&sc->work_free_lock); w = sc->work_free; if (w) { sc->work_free = w->next; w->next = NULL; } spin_unlock(&sc->work_free_lock); if (!w) { pr_err("%s: empty!!\n", __func__); sc->work_ready = 1; wake_up(&sc->work_waitq); } return w; } void free_work_entry(struct uart_sc16is7xx_sc *sc, struct i2c_work* w) { if (w) { spin_lock(&sc->work_free_lock); w->next = sc->work_free; sc->work_free = w; spin_unlock(&sc->work_free_lock); } } void sc_queue_work(struct uart_sc16is7xx_chan *ch, struct i2c_work* w) { int wake = 0; unsigned reg; if (!w) return; reg = w->reg; spin_lock(&ch->work_pending_lock); if ((w->mode == SCM_WRITE) && !(reg_map[reg] & _V)) { /* * The reg_cache changes when queued for non-volatiles, * and when actually written for volatiles. */ if (reg < SC_CHAN_REG_CNT) { if ((reg == SC_LCR) && (w->val == 0xbf)) { pr_err("%s: should not set LCR to 0xbf directly\n", __func__); w->val = UART_LCR_WLEN8; } ch->reg_cache[reg] = w->val; } else ch->sc->dev_cache[reg - SC_CHAN_REG_CNT] = w->val; } if (!ch->pending_tail) { ch->pending_head = w; wake = 1; } else { ch->pending_tail->next = w; } ch->pending_tail = w; spin_unlock(&ch->work_pending_lock); if (wake) { ch->sc->work_ready = 1; wake_up(&ch->sc->work_waitq); } } static int serial_out(struct uart_sc16is7xx_chan *ch, enum sc_reg reg, unsigned value) { struct i2c_work* w; unsigned mreg; if (reg > ARRAY_SIZE(reg_map)) return -EINVAL; mreg = reg_map[reg]; if (!(mreg & _V)) { unsigned v; if (reg < SC_CHAN_REG_CNT) v = ch->reg_cache[reg]; else v = ch->sc->dev_cache[reg - SC_CHAN_REG_CNT]; if (v == value) return 0; /* The reg_cache changes when queued for non-volatiles */ } w = get_work_entry(ch->sc); if (!w) return -ENOMEM; w->mode = SCM_WRITE; w->reg = reg; w->state = F_PENDING; w->cnt = 1; w->channel_no = ch->channel_no; w->val = (unsigned char)value; sc_queue_work(ch, w); return 1; } static int serial_modify(struct uart_sc16is7xx_chan *ch, enum sc_reg reg, unsigned char clear_mask, unsigned char set_mask) { int val = serial_in(ch, reg); if (val < 0) return val; val &= ~clear_mask; val |= set_mask; return serial_out(ch, reg, val); } static int serial_modify_ier(struct uart_sc16is7xx_chan *ch, unsigned char clear_mask, unsigned char set_mask) { int val; int wake = 0; spin_lock(&ch->work_pending_lock); val = ch->new_ier; val &= ~clear_mask; val |= set_mask; if (ch->new_ier != val) { unsigned change = (ch->new_ier ^ val); if (change & 0xf0) { serial_modify(ch, SC_EFR, 0, 1 << 4); //needed to allow writes } ch->new_ier = val; wake = 1; } spin_unlock(&ch->work_pending_lock); if (wake) { ch->sc->work_ready = 1; wake_up(&ch->sc->work_waitq); } return wake; } static void serial_sc16is7xx_enable_ms(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; serial_modify_ier(ch, 0, UART_IER_MSI); } static void serial_sc16is7xx_start_tx(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; serial_modify_ier(ch, 0, UART_IER_THRI); } static void serial_sc16is7xx_stop_tx(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; #ifdef CONFIG_SERIAL_SC16IS7XX_CONSOLE if (ch->console_insert != ch->console_remove) return; #endif serial_modify_ier(ch, UART_IER_THRI, 0); } static void serial_sc16is7xx_stop_rx(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; ch->port.read_status_mask &= ~UART_LSR_DR; serial_modify_ier(ch, UART_IER_RLSI, 0); } static void serial_sc16is7xx_break_ctl(struct uart_port *port, int break_state) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; serial_modify(ch, SC_LCR, UART_LCR_SBC, (break_state == -1) ? UART_LCR_SBC : 0); } static unsigned int serial_sc16is7xx_tx_empty(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; if (ch->tx_empty) return TIOCSER_TEMT; ch->check_tx_empty = 1; ch->sc->work_ready = 1; wake_up(&ch->sc->work_waitq); return 0; } static unsigned int serial_sc16is7xx_get_mctrl(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; unsigned char status; unsigned int ret; /* hopefully, modem change interrupts are keeping msr up to date */ status = serial_in(ch, SC_MSR); ret = 0; if (status & UART_MSR_DCD) ret |= TIOCM_CAR; if (status & UART_MSR_RI) ret |= TIOCM_RNG; if (status & UART_MSR_DSR) ret |= TIOCM_DSR; if (status & UART_MSR_CTS) ret |= TIOCM_CTS; return ret; } static void serial_sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; unsigned char mcr = 0; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; if (mctrl & TIOCM_DTR) mcr |= UART_MCR_DTR; if (mctrl & TIOCM_OUT1) mcr |= UART_MCR_OUT1; if (mctrl & TIOCM_OUT2) mcr |= UART_MCR_OUT2; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; serial_out(ch, SC_MCR, mcr); } static int serial_sc16is7xx_startup(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; unsigned long flags; unsigned ier; /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) */ serial_out(ch, SC_FCR, UART_FCR_ENABLE_FIFO); serial_out(ch, SC_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_out(ch, SC_FCR, 0); /* * Now, initialize the UART */ serial_out(ch, SC_LCR, UART_LCR_WLEN8); spin_lock_irqsave(&ch->port.lock, flags); ch->port.mctrl |= TIOCM_OUT2; serial_sc16is7xx_set_mctrl(&ch->port, ch->port.mctrl); spin_unlock_irqrestore(&ch->port.lock, flags); /* * Finally, enable interrupts. Note: Modem status interrupts * are set via set_termios(), which will be occurring imminently * anyway, so we don't enable them here. */ ier = UART_IER_RLSI | UART_IER_RDI; serial_modify_ier(ch, ~ier, ier); return 0; } static void serial_sc16is7xx_shutdown(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; unsigned long flags; /* * Disable interrupts from this port */ serial_modify_ier(ch, 0xff, 0); spin_lock_irqsave(&ch->port.lock, flags); ch->port.mctrl &= ~TIOCM_OUT2; serial_sc16is7xx_set_mctrl(&ch->port, ch->port.mctrl); spin_unlock_irqrestore(&ch->port.lock, flags); /* * Disable break condition and FIFOs */ serial_modify(ch, SC_LCR, UART_LCR_SBC, 0); serial_out(ch, SC_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_out(ch, SC_FCR, 0); } static void serial_sc16is7xx_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; unsigned char cval, rxlvl; unsigned char efr = 0x10; unsigned long flags; unsigned int baud, quot; switch (termios->c_cflag & CSIZE) { case CS5: cval = UART_LCR_WLEN5; break; case CS6: cval = UART_LCR_WLEN6; break; case CS7: cval = UART_LCR_WLEN7; break; default: case CS8: cval = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) cval |= UART_LCR_STOP; if (termios->c_cflag & PARENB) cval |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) cval |= UART_LCR_EPAR; baud = tty_termios_baud_rate(termios); /* * Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); if ((ch->port.uartclk / quot) <= (9600 * 16)) rxlvl = 1; else if ((ch->port.uartclk / quot) <= (57600 * 16)) rxlvl = 8; else rxlvl = 32; /* * Ok, we're now changing the port state. Do it with * interrupts disabled. */ spin_lock_irqsave(&ch->port.lock, flags); /* * Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); ch->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) ch->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (termios->c_iflag & (BRKINT | PARMRK)) ch->port.read_status_mask |= UART_LSR_BI; /* * Characters to ignore */ ch->port.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) ch->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (termios->c_iflag & IGNBRK) { ch->port.ignore_status_mask |= UART_LSR_BI; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) ch->port.ignore_status_mask |= UART_LSR_OE; } /* * ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) ch->port.ignore_status_mask |= UART_LSR_DR; /* * CTS flow control flag and modem status interrupts */ if (UART_ENABLE_MS(&ch->port, termios->c_cflag)) serial_modify_ier(ch, 0, UART_IER_MSI); else serial_modify_ier(ch, UART_IER_MSI, 0); serial_out(ch, SC_DLL, quot & 0xff); /* LS of divisor */ serial_out(ch, SC_DLH, quot >> 8); /* MS of divisor */ serial_sc16is7xx_set_mctrl(&ch->port, ch->port.mctrl); serial_out(ch, SC_EFR, efr); serial_out(ch, SC_FCR, UART_FCR_ENABLE_FIFO); #define HALT_RESUME(halt, resume) (((halt)/4) | (((resume)/4) << 4)) #define TRIG_LVL_TX_RX(txlvl, rxlvl) (((txlvl)/4) | (((rxlvl)/4) << 4)) serial_out(ch, SC_TCR,HALT_RESUME(60, 4)); serial_out(ch, SC_TLR,TRIG_LVL_TX_RX(32, rxlvl)); if (termios->c_cflag & CRTSCTS) { efr |= 0x80; serial_out(ch, SC_EFR, efr); } spin_unlock_irqrestore(&ch->port.lock, flags); } static void serial_sc16is7xx_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { } static void serial_sc16is7xx_release_port(struct uart_port *port) { } static int serial_sc16is7xx_request_port(struct uart_port *port) { return 0; } #define PORT_SC16IS7XX PORT_PXA static void serial_sc16is7xx_config_port(struct uart_port *port, int flags) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; ch->port.type = PORT_SC16IS7XX; } static int serial_sc16is7xx_verify_port(struct uart_port *port, struct serial_struct *ser) { /* we don't want the core code to modify any port params */ return -EINVAL; } static const char * serial_sc16is7xx_type(struct uart_port *port) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; return ch->name; } static struct uart_driver serial_sc16is7xx_reg; #ifdef CONFIG_SERIAL_SC16IS7XX_CONSOLE static void serial_sc16is7xx_console_putchar(struct uart_port *port, int char1) { struct uart_sc16is7xx_chan *ch = (struct uart_sc16is7xx_chan *)port; short insert = ch->console_insert; ch->console_buffer[insert++] = char1; if (insert >= ARRAY_SIZE(ch->console_buffer)) insert = 0; if (insert != ch->console_remove) ch->console_insert = insert; } /* * Print a string to the serial port trying not to disturb * any possible real use of the port... * * The console_lock must be held when we get here. */ static void serial_sc16is7xx_console_write(struct console *co, const char *s, unsigned int count) { struct uart_sc16is7xx_chan *ch = &serial_sc16is7xx_sc->chan[co->index]; uart_console_write(&ch->port, s, count, serial_sc16is7xx_console_putchar); // serial_modify_ier(ch, 0, UART_IER_THRI); } static int __init serial_sc16is7xx_console_setup(struct console *co, char *options) { struct uart_sc16is7xx_sc *sc; struct uart_sc16is7xx_chan *ch; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; if (co->index == -1 || co->index >= serial_sc16is7xx_reg.nr) co->index = 0; sc = serial_sc16is7xx_sc; if (!sc) return -ENODEV; ch = &sc->chan[co->index]; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); serial_sc16is7xx_startup(&ch->port); return uart_set_options(&ch->port, co, baud, parity, bits, flow); } static struct console serial_sc16is7xx_console = { .name = "ttySC", .write = serial_sc16is7xx_console_write, .device = uart_console_device, .setup = serial_sc16is7xx_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &serial_sc16is7xx_reg, }; #define SC16IS7XX_CONSOLE &serial_sc16is7xx_console #else #define SC16IS7XX_CONSOLE NULL #endif struct uart_ops serial_sc16is7xx_pops = { .tx_empty = serial_sc16is7xx_tx_empty, .set_mctrl = serial_sc16is7xx_set_mctrl, .get_mctrl = serial_sc16is7xx_get_mctrl, .stop_tx = serial_sc16is7xx_stop_tx, .start_tx = serial_sc16is7xx_start_tx, .stop_rx = serial_sc16is7xx_stop_rx, .enable_ms = serial_sc16is7xx_enable_ms, .break_ctl = serial_sc16is7xx_break_ctl, .startup = serial_sc16is7xx_startup, .shutdown = serial_sc16is7xx_shutdown, .set_termios = serial_sc16is7xx_set_termios, .pm = serial_sc16is7xx_pm, .type = serial_sc16is7xx_type, .release_port = serial_sc16is7xx_release_port, .request_port = serial_sc16is7xx_request_port, .config_port = serial_sc16is7xx_config_port, .verify_port = serial_sc16is7xx_verify_port, }; static struct uart_driver serial_sc16is7xx_reg = { .owner = THIS_MODULE, .driver_name = "sc16is7xx serial", .dev_name = "ttySC", .major = TTY_MAJOR, .minor = 68, .nr = 2, .cons = SC16IS7XX_CONSOLE, }; /* * ------------------------------------------------------------------ */ static int sc_select(struct uart_sc16is7xx_chan *ch, enum sc_reg reg) { int ret = 0; unsigned mreg; if (reg > ARRAY_SIZE(reg_map)) return -EINVAL; mreg = reg_map[reg]; if (mreg & A_TYPE(SP_0xBF, 1)) { /* LCR == or != 0xbf matters */ unsigned char lcr; unsigned char mcr; unsigned char efr; if (mreg & A_TYPEV(SP_MCR2)) { /* efr[4] should be 1 as well */ efr = ch->reg_cache[SC_EFR] | 0x10; if (ch->actual_efr != efr) { ret = sc_select(ch, SC_EFR); if (ret) return ret; ret = i2c_smbus_write_byte_data(ch->sc->client, MAKE_SUBADDRESS(ch->channel_no, UART_EFR), efr); if (ret) return ret; ch->actual_efr = efr; } } if (mreg & A_TYPE(SP_MCR2, 1)) { /* mcr[2] matters */ mcr = ch->reg_cache[SC_MCR] & ~0x4; if (mreg & A_TYPEV(SP_MCR2)) mcr |= 4; if (ch->actual_mcr != mcr) { ret = sc_select(ch, SC_MCR); if (ret) return ret; ret = i2c_smbus_write_byte_data(ch->sc->client, MAKE_SUBADDRESS(ch->channel_no, UART_MCR), mcr); if (ret) return ret; ch->actual_mcr = mcr; } } lcr = (mreg & A_TYPEV(SP_0xBF)) ? 0xbf : ch->reg_cache[SC_LCR]; if (mreg & A_TYPE(SP_DLAB, 1)) { if (mreg & A_TYPEV(SP_DLAB)) lcr |= 0x80; else lcr &= ~0x80; } if (ch->actual_lcr != lcr) { ret = i2c_smbus_write_byte_data(ch->sc->client, MAKE_SUBADDRESS(ch->channel_no, UART_LCR), lcr); if (ret) return ret; ch->actual_lcr = lcr; } } return ret; } static int sc_serial_out_cnt(struct uart_sc16is7xx_chan *ch, enum sc_reg reg, unsigned char* buf, int count) { int ret; unsigned sub_address; unsigned char value = buf[count - 1]; unsigned mreg; int retry = 0; if (reg > ARRAY_SIZE(reg_map)) return -EINVAL; mreg = reg_map[reg]; sub_address = MAKE_SUBADDRESS(ch->channel_no, mreg); do { ret = sc_select(ch, reg); if (!ret) { ret = i2c_smbus_write_i2c_block_data(ch->sc->client, sub_address, count, buf); if (!ret) break; } } while (retry++ < 3); if (ret) { pr_info("%s: error %i\n", __func__, ret); return ret; } if (reg < SC_CHAN_REG_CNT) buf = &ch->reg_cache[reg]; else buf = &ch->sc->dev_cache[reg - SC_CHAN_REG_CNT]; if (reg == SC_LCR) ch->actual_lcr = value; if (reg == SC_MCR) ch->actual_mcr = value; if (reg == SC_EFR) ch->actual_efr = value; if (mreg & _V) *buf = value; return ret; } static int sc_serial_out(struct uart_sc16is7xx_chan *ch, enum sc_reg reg, unsigned char value) { return sc_serial_out_cnt(ch, reg, &value, 1); } static int sc_serial_in_cnt(struct uart_sc16is7xx_chan *ch, enum sc_reg reg, unsigned char * buf, int count) { unsigned sub_address; int val = 0; int ret = 0; int retry = 0; if (reg > ARRAY_SIZE(reg_map)) return -EINVAL; sub_address = MAKE_SUBADDRESS(ch->channel_no, reg_map[reg]); do { ret = sc_select(ch, reg); if (!ret) { ret = i2c_smbus_read_i2c_block_data(ch->sc->client, sub_address, count, buf); if (ret == count) break; if (ret > 0) { pr_info("%s: error %i of %i bytes read\n", __func__, ret, count); if (ret > count) ret = count; break; } } } while (retry++ < 3); if (ret <= 0) { pr_info("%s: error %i, count=%i\n", __func__, ret, count); return ret; } val = buf[ret - 1]; if (reg == SC_LCR) { ch->actual_lcr = val; if (val == 0xbf) return ret; /* don't update reg_cache */ } else if (reg == SC_MCR) ch->actual_mcr = val; else if (reg == SC_EFR) ch->actual_efr = val; else if (reg == SC_LSR) ch->tx_empty = val & UART_LSR_TEMT; if (reg < SC_CHAN_REG_CNT) buf = &ch->reg_cache[reg]; else buf = &ch->sc->dev_cache[reg - SC_CHAN_REG_CNT]; *buf = val; return ret; } static int sc_serial_in_always(struct uart_sc16is7xx_chan *ch, enum sc_reg reg) { unsigned char buf[4]; int ret = sc_serial_in_cnt(ch, reg, buf, 1); if (ret <= 0) return ret; return buf[0]; } static int sc_serial_in(struct uart_sc16is7xx_chan *ch, enum sc_reg reg) { if (reg > ARRAY_SIZE(reg_map)) return -EINVAL; if (reg_map[reg] & _V) return sc_serial_in_always(ch, reg); if (reg < SC_CHAN_REG_CNT) return ch->reg_cache[reg]; return ch->sc->dev_cache[reg - SC_CHAN_REG_CNT]; } static int receive_chars(struct uart_sc16is7xx_chan *ch) { int rxlvl; unsigned flag; int max_count = 256; int cnt; int total = 0; int i; unsigned char buf[64]; rxlvl = sc_serial_in(ch, SC_RXLVL); if ((rxlvl < 0) || (rxlvl > 64)) rxlvl = 64; while (rxlvl) { int lsr = sc_serial_in(ch, SC_LSR); if (lsr < 0) lsr = 0; if (!(lsr & UART_LSR_DR)) break; if (lsr & (0x80 | UART_LSR_BI)) { int char1; char1 = sc_serial_in(ch, SC_RHR); if (char1 < 0) char1 = 0; buf[0] = char1; cnt = 1; } else { cnt = rxlvl; if (cnt > max_count) cnt = max_count; /* * This chip doesn't like to read more than 32 bytes * from the fifo */ if (cnt > 32) cnt = 32; cnt = sc_serial_in_cnt(ch, SC_RHR, buf, cnt); } flag = TTY_NORMAL; ch->port.icount.rx++; if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE))) { /* * For statistics only */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); ch->port.icount.brk++; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ if (uart_handle_break(&ch->port)) goto break_done; } else if (lsr & UART_LSR_PE) ch->port.icount.parity++; else if (lsr & UART_LSR_FE) ch->port.icount.frame++; if (lsr & UART_LSR_OE) ch->port.icount.overrun++; /* * Mask off conditions which should be ignored. */ lsr &= ch->port.read_status_mask; if (lsr & UART_LSR_BI) { flag = TTY_BREAK; } else if (lsr & UART_LSR_PE) flag = TTY_PARITY; else if (lsr & UART_LSR_FE) flag = TTY_FRAME; } if (ch->port.state->port.tty) { for (i = 0; i < cnt; i++) { if (!uart_handle_sysrq_char(&ch->port, buf[i])) uart_insert_char(&ch->port, lsr, UART_LSR_OE, buf[i], flag); lsr = 0; /* overrun only valid once */ } } else { pr_err("%s: tty undefined %i\n", __func__, ch->channel_no); } break_done: rxlvl -= cnt; max_count -= cnt; total += cnt; if (max_count <= 0) break; } if (ch->port.state->port.tty) tty_flip_buffer_push(&ch->port.state->port); else { pr_err("%s: tty undefined %i\n", __func__, ch->channel_no); } return total; } static int transmit_chars(struct uart_sc16is7xx_chan *ch) { struct circ_buf *xmit = &ch->port.state->xmit; int count = 0; int i = 0; unsigned char buf[32]; count = ch->port.fifosize / 2; /* * This chip doesn't like to write more than 32 bytes * to the fifo at a time. * Tx Threshold is also fixed at 32 bytes */ if (count > 32) count = 32; #ifdef CONFIG_SERIAL_SC16IS7XX_CONSOLE while (ch->console_insert != ch->console_remove) { unsigned remove = ch->console_remove; buf[i++] = ch->console_buffer[remove++]; if (remove >= ARRAY_SIZE(ch->console_buffer)) remove = 0; ch->console_remove = remove; count--; if (!count) break; } #endif if (count) { if (ch->port.x_char) { buf[i++] = ch->port.x_char; ch->port.x_char = 0; } if (uart_tx_stopped(&ch->port)) { count = 0; } } while (count) { if (uart_circ_empty(xmit)) break; buf[i++] = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); count--; } if (i) { sc_serial_out_cnt(ch, SC_THR, buf, i); ch->port.icount.tx += i; ch->tx_empty = 0; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&ch->port); if (uart_circ_empty(xmit) || !i) { #ifdef CONFIG_SERIAL_SC16IS7XX_CONSOLE if (ch->console_insert != ch->console_remove) return i; #endif serial_modify_ier(ch, UART_IER_THRI, 0); } return i; } static void check_modem_status(struct uart_sc16is7xx_chan *ch) { int status; status = sc_serial_in(ch, SC_MSR); if ((status & UART_MSR_ANY_DELTA) == 0) return; if (status & UART_MSR_TERI) ch->port.icount.rng++; if (status & UART_MSR_DDSR) ch->port.icount.dsr++; if (status & UART_MSR_DDCD) uart_handle_dcd_change(&ch->port, status & UART_MSR_DCD); if (status & UART_MSR_DCTS) uart_handle_cts_change(&ch->port, status & UART_MSR_CTS); wake_up_interruptible(&ch->port.state->port.delta_msr_wait); } static int check_for_tx_empty(struct uart_sc16is7xx_chan *ch) { if (!ch->check_tx_empty) return 0; if (ch->tx_empty) { ch->check_tx_empty = 0; return 0; } sc_serial_in_always(ch, SC_LSR); if (ch->tx_empty) { ch->check_tx_empty = 0; return 0; } return 1; } /* * This handles the interrupt from one port. */ static int serial_sc16is7xx_irq(struct uart_sc16is7xx_chan *ch) { unsigned iir; unsigned state; int count; iir = sc_serial_in(ch, SC_IIR); if (iir & UART_IIR_NO_INT) { return 0; /* gp is checked first, so should not happen */ pr_err("%s: no interupt, ier=%x\n", __func__, ch->actual_ier); return 0; } switch (iir & 0x3f) { /* highest priority case to lowest */ case UART_IIR_RLSI: /* 6 */ /* some errors in rx fifo */ case 0x0c: /* Receiver time-out interrupt */ case UART_IIR_RDI: /* 4 */ /* Rx fifo above trigger level */ count = receive_chars(ch); if (0) { if (count) pr_debug("%s: %i rx chars\n", __func__, count); else pr_info("%s: 0 rx chars\n", __func__); } break; case UART_IIR_THRI: /* 2 */ /* Tx fifo empty slots above trigger level */ count = transmit_chars(ch); pr_debug("%s: %i tx chars\n", __func__, count); break; case UART_IIR_MSI: /* 0 */ pr_debug("modem interrupt\n"); check_modem_status(ch); break; case 0x10: /* Xoff interupt, lower prority than I/O change (0x30) */ pr_debug("Xoff interupt, ier=%x\n", ch->actual_ier); // if (ch->actual_ier & 0x20) // break; // break; //Chip bug alert, sometime Xoff Interrupt is given for I/O pin change case 0x30: state = sc_serial_in(ch, SC_IOSTATE_R); if (ch->sc->gpio_callback) ch->sc->gpio_callback(ch->sc->gpio_sg, state); /* I/O pins change of state */ pr_debug("I/O pins change of state, ier=%x\n", ch->actual_ier); break; case 0x20: /* RTS/CTS now inactive */ pr_debug("RTS/CTS now inactive interupt, ier=%x\n", ch->actual_ier); check_modem_status(ch); break; default: pr_err("unknown interupt 0x%x, ier=%x\n", iir, ch->actual_ier); break; } return 1; } static void process_work_queue(struct uart_sc16is7xx_chan *ch) { struct i2c_work *w; spin_lock(&ch->work_pending_lock); w = ch->pending_head; if (w) { ch->pending_head = w->next; if (!ch->pending_head) ch->pending_tail = NULL; } spin_unlock(&ch->work_pending_lock); if (w) { if (w->mode == SCM_READ) { sc_serial_in_always(ch, w->reg); } else { sc_serial_out(ch, w->reg, w->val); } free_work_entry(ch->sc, w); } } static int sc16is7xx_thread(void *_sc) { int ret; int channel_no; enum sc_reg reg; unsigned gp_int; struct uart_sc16is7xx_sc *sc = _sc; struct uart_sc16is7xx_chan *ch; int last_active = 0; struct sched_param param = { .sched_priority = 1 }; sched_setscheduler(current, SCHED_FIFO, ¶m); for (channel_no = 0; channel_no < 2; channel_no++) { ch = &sc->chan[channel_no]; ret = sc_serial_out(ch, SC_LCR, 0); if (!ret) ret = sc_serial_out(ch, SC_MCR, 0); if (!ret) ret = sc_serial_out(ch, SC_EFR, 0); if (ret) goto out1; for (reg = 0; reg < SC_CHAN_REG_CNT; reg++) { unsigned mreg = reg_map[reg]; if (!(mreg & _WO)) { ret = sc_serial_in_always(ch, reg); if (ret < 0) goto out1; } } } ch = &sc->chan[0]; for (reg = SC_CHAN_REG_CNT; reg < SC_REG_CNT; reg++) { unsigned mreg = reg_map[reg]; if (!(mreg & _WO)) { ret = sc_serial_in_always(ch, reg); if (ret < 0) goto out1; } } if (ret < 0) goto out1; /* default gpios to modem control */ ret = sc_serial_out(ch, SC_IOCONTROL, 7); if (ret < 0) goto out1; complete(&sc->init_exit); while (!kthread_should_stop()) { sc->work_ready = 0; for (channel_no = 0; channel_no < 2; channel_no++) { ch = &sc->chan[channel_no]; while (ch->pending_head) { if (kthread_should_stop()) goto out1; #ifdef CONFIG_SERIAL_SC16IS7XX_CONSOLE if (!ch->tx_empty) { /* don't process entry unless tx empty */ ch->check_tx_empty = 1; if (check_for_tx_empty(&sc->chan[0])) break; } #endif process_work_queue(ch); } if (ch->actual_ier != ch->new_ier) { unsigned val = ch->new_ier; ch->actual_ier = val; sc_serial_out(ch, SC_IER, val); } } while (sc->write_pending) { unsigned reg; unsigned val; spin_lock(&sc->pending_lock); reg = __ffs(sc->write_pending); sc->write_pending &= ~(1 << reg); val = sc->dev_cache[reg]; /* * due to chip bug, always rewrite output state * when direction register changes */ if (reg == (SC_IODIR - SC_CHAN_REG_CNT)) sc->write_pending |= 1 << (SC_IOSTATE_W - SC_CHAN_REG_CNT); spin_unlock(&sc->pending_lock); reg += SC_CHAN_REG_CNT; sc_serial_out(&sc->chan[0], reg, val); } gp_int = gpio_get_value(sc->gp); pr_debug("sc16is7xx gp_int=%x\n", gp_int); if (gp_int) { /* high level means no interrupt pending */ ret = check_for_tx_empty(&sc->chan[0]); ret |= check_for_tx_empty(&sc->chan[1]); if (ret) wait_event_interruptible_timeout(sc->work_waitq, sc->work_ready, 200); else wait_event_interruptible(sc->work_waitq, sc->work_ready); continue; } ch = &sc->chan[last_active]; if (serial_sc16is7xx_irq(ch)) continue; ch = &sc->chan[last_active ^ 1]; if (serial_sc16is7xx_irq(ch)) { last_active ^= 1; continue; } } out1: sc->startup_code = ret; complete(&sc->init_exit); return ret; } static irqreturn_t sc16is7xx_interrupt(int irq, void *id) { struct uart_sc16is7xx_sc *sc = id; sc->work_ready = 1; wake_up(&sc->work_waitq); return IRQ_HANDLED; } static const char *client_name = "sc16is7xx-uart"; int access_read(struct sc16is7xx_access *access, enum sc_reg reg) { struct uart_sc16is7xx_sc *sc = container_of(access, struct uart_sc16is7xx_sc, sc_access); if ((reg < SC_CHAN_REG_CNT) || (reg >= SC_REG_CNT)) return -EINVAL; if (reg_map[reg] & _V) return sc_serial_in_always(&sc->chan[0], reg); return sc->dev_cache[reg - SC_CHAN_REG_CNT]; } int access_write(struct sc16is7xx_access *access, enum sc_reg reg, unsigned value) { struct uart_sc16is7xx_sc *sc = container_of(access, struct uart_sc16is7xx_sc, sc_access); int wake = 0; if ((reg < SC_CHAN_REG_CNT) || (reg >= SC_REG_CNT)) return -EINVAL; spin_lock(&sc->pending_lock); if (sc->dev_cache[reg - SC_CHAN_REG_CNT] != value) { sc->dev_cache[reg - SC_CHAN_REG_CNT] = value; sc->write_pending |= 1 << (reg - SC_CHAN_REG_CNT); wake = 1; } spin_unlock(&sc->pending_lock); if (wake) { sc->work_ready = 1; wake_up(&sc->work_waitq); } return wake; } int access_modify(struct sc16is7xx_access *access, enum sc_reg reg, unsigned clear_mask, unsigned set_mask) { unsigned val, new_val; struct uart_sc16is7xx_sc *sc = container_of(access, struct uart_sc16is7xx_sc, sc_access); int wake = 0; if ((reg < SC_CHAN_REG_CNT) || (reg >= SC_REG_CNT)) return -EINVAL; spin_lock(&sc->pending_lock); val = sc->dev_cache[reg - SC_CHAN_REG_CNT]; new_val = (val & ~clear_mask) | set_mask; if (val != new_val) { sc->dev_cache[reg - SC_CHAN_REG_CNT] = new_val; sc->write_pending |= 1 << (reg - SC_CHAN_REG_CNT); wake = 1; } spin_unlock(&sc->pending_lock); if (wake) { sc->work_ready = 1; wake_up(&sc->work_waitq); } return wake; } int access_register_gpio_interrupt(struct sc16is7xx_access *access, void (*call_back)(struct sc16is7xx_gpio *sg, unsigned), struct sc16is7xx_gpio *sg) { struct uart_sc16is7xx_sc *sc = container_of(access, struct uart_sc16is7xx_sc, sc_access); sc->gpio_callback = call_back; sc->gpio_sg = sg; return 0; } #if 0 static int sc16is7xx_add_gpio_device(struct uart_sc16is7xx_sc *sc, struct device *parent, const char *name, void *pdata, size_t pdata_size) { struct mfd_cell cell = { .name = name, .platform_data = pdata, .data_size = pdata_size, }; sc->sc_access.read = access_read; sc->sc_access.write = access_write; sc->sc_access.modify = access_modify; sc->sc_access.register_gpio_interrupt = &access_register_gpio_interrupt; return mfd_add_devices(parent, -1, &cell, 1, NULL, 0); } #endif static const char *reg_names[] = { "LCR", "RHR", "THR", "IER", "DLL", "DLH", "IIR", "FCR", "MCR", "LSR", "TXLVL", "RXLVL", "EFCR", "MSR", "SPR", "TCR", "TLR", "EFR", "XON1", "XON2", "XOFF1", "XOFF2", "IOSTATE_R", "IOSTATE_W", "IODIR", "IOINTENA", "IOCONTROL", }; static ssize_t sc16is7xx_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct uart_sc16is7xx_sc *sc = serial_sc16is7xx_sc; struct uart_sc16is7xx_chan *ch; int total = 0; int cnt; int reg; int i; if (!sc) return -ENODEV; for (i = 0; i < 2; i++) { ch = &sc->chan[i]; cnt = sprintf(buf, "Channel %i: ", i); total += cnt; buf += cnt; for (reg = 0; reg < SC_CHAN_REG_CNT; reg++) { cnt = sprintf(buf, "%s=%02x ", reg_names[reg], ch->reg_cache[reg]); total += cnt; buf += cnt; } cnt = sprintf(buf, "\n"); total += cnt; buf += cnt; } cnt = sprintf(buf, "IO_Regs: "); total += cnt; buf += cnt; for (reg = SC_CHAN_REG_CNT; reg < SC_REG_CNT; reg++) { cnt = sprintf(buf, "%s=%02x ", reg_names[reg], sc->dev_cache[reg - SC_CHAN_REG_CNT]); total += cnt; buf += cnt; } cnt = sprintf(buf, "\n"); total += cnt; return total; } static DEVICE_ATTR(sc16is7xx_reg, 0444, sc16is7xx_reg_show, NULL); struct sc16is7xx_plat { unsigned freq; }; static int sc16is7xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; int i; struct uart_sc16is7xx_sc *sc; struct uart_sc16is7xx_chan *ch; struct device *dev = &client->dev; struct sc16is7xx_plat *plat = client->dev.platform_data; struct device_node *np = client->dev.of_node; printk(KERN_ERR "%s: %s version 1.0\n", __func__, client_name); sc = kzalloc(sizeof(struct uart_sc16is7xx_sc), GFP_KERNEL); if (!sc) return -ENOMEM; init_waitqueue_head(&sc->work_waitq); sc->client = client; sc->irq = client->irq ; sc->gp = of_get_named_gpio(np, "interrupts-extended", 0); if (!gpio_is_valid(sc->gp)) return -ENODEV; ret = gpio_request(sc->gp, "sc16is7xx_irq"); if (ret < 0) { dev_err(&client->dev, "request gpio failed, cannot wake up controller: %d\n", ret); return ret; } init_completion(&sc->init_exit); spin_lock_init(&sc->work_free_lock); spin_lock_init(&sc->pending_lock); for (i = 0; i < MAX_WORK - 1; i++) { sc->work_entries[i].next = &sc->work_entries[i + 1]; } sc->work_free = &sc->work_entries[0]; for (i = 0; i < 2; i++) { ch = &sc->chan[i]; spin_lock_init(&ch->work_pending_lock); ch->port.type = PORT_SC16IS7XX; ch->port.iotype = UPIO_MEM; /* needed to pass serial_core check for existence */ ch->port.mapbase = 0xfff8 + i; ch->port.fifosize = 64; ch->port.ops = &serial_sc16is7xx_pops; ch->port.line = i; ch->port.dev = dev; ch->port.flags = UPF_BOOT_AUTOCONF; ch->port.uartclk = plat ? plat->freq : 3686400; ch->sc = sc; ch->channel_no = i; ch->tx_empty = 1; ch->name = (!i) ? "UARTA" : "UARTB"; } serial_sc16is7xx_sc = sc; ret = request_irq(sc->irq, &sc16is7xx_interrupt, IRQF_TRIGGER_FALLING, client_name, sc); if (ret) { pr_err("%s: request_irq failed, irq:%i\n", client_name,sc->irq); goto out; } i2c_set_clientdata(client, sc); init_completion(&sc->init_exit); sc->sc_thread = kthread_run(sc16is7xx_thread, sc, "sc16is7xxd"); if (ret < 0) goto out2; wait_for_completion(&sc->init_exit); /* wait for thread to Start */ ret = sc->startup_code; if (ret < 0) goto out2; for (i = 0; i < 2; i++) { ch = &sc->chan[i]; uart_add_one_port(&serial_sc16is7xx_reg, &ch->port); } #if 0 struct sc16is7xx_platform_data *plat = client->dev.platform_data; sc16is7xx_add_gpio_device(sc, dev, "sc16is7xx-gpio", plat->gpio_data, sizeof(struct sc16is7xx_gpio_platform_data)); #endif ret = device_create_file(&client->dev, &dev_attr_sc16is7xx_reg); if (ret < 0) printk(KERN_WARNING "failed to add mma7660 sysfs files\n"); pr_err("%s: succeeded\n", __func__); return 0; out2: free_irq(sc->irq, sc); out: kfree(sc); pr_err("%s: failed %i\n", __func__, ret); return ret; } static int sc16is7xx_remove(struct i2c_client *client) { struct uart_sc16is7xx_sc *sc = i2c_get_clientdata(client); kthread_stop(sc->sc_thread); uart_remove_one_port(&serial_sc16is7xx_reg, &sc->chan[1].port); uart_remove_one_port(&serial_sc16is7xx_reg, &sc->chan[0].port); free_irq(sc->irq, sc); kfree(sc); return 0; } /* Return 0 if detection is successful, -ENODEV otherwise */ static int sc16is7xx_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -ENODEV; strlcpy(info->type, client_name, I2C_NAME_SIZE); return 0; } /*-----------------------------------------------------------------------*/ static const struct i2c_device_id sc16is7xx_idtable[] = { { "sc16is7xx-uart", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, sc16is7xx_idtable); static struct i2c_driver sc16is7xx_driver = { .driver = { .owner = THIS_MODULE, .name = "sc16is7xx-uart", }, .id_table = sc16is7xx_idtable, .probe = sc16is7xx_probe, .remove = sc16is7xx_remove, .detect = sc16is7xx_detect, }; static int __init sc16is7xx_init(void) { int ret = uart_register_driver(&serial_sc16is7xx_reg); if (ret != 0) return ret; if ((ret = i2c_add_driver(&sc16is7xx_driver))) { uart_unregister_driver(&serial_sc16is7xx_reg); pr_warning("%s: i2c_add_driver failed\n",client_name); return ret; } pr_err("%s: version 1.0\n",client_name); return 0; } static void __exit sc16is7xx_exit(void) { i2c_del_driver(&sc16is7xx_driver); uart_unregister_driver(&serial_sc16is7xx_reg); } module_init(sc16is7xx_init); module_exit(sc16is7xx_exit); MODULE_AUTHOR("Troy Kisky "); MODULE_DESCRIPTION("I2C dual serial ports"); MODULE_LICENSE("GPL");