/** * drivers/serial/sc16is7x2.c * * Copyright (C) 2010 kathy chang changgx@163.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The SC16IS7x2 device is a SPI driven dual UART with GPIOs. * * The driver exports two uarts . */ #define DEBUG #include #include #include #include #include #include #include #include #include #include #include "sc16is7x2.h" #define H_SPI_SPEED (2*1000*1000) #define SC16IS7X2_MAJOR 204 #define SC16IS7X2_MINOR 209 #define MAX_SC16IS7X2 2 #define FIFO_SIZE 64 #define PMC_PCER 0x10 #define DRIVER_NAME "sc16is7x2" #define TYPE_NAME "SC16IS7x2" #define REG_READ 0x80 #define REG_WRITE 0x00 /* Special registers */ #define REG_TXLVL 0x08 /* Transmitter FIFO Level register */ #define REG_RXLVL 0x09 /* Receiver FIFO Level register */ #define REG_IOD 0x0A /* IO Direction register */ #define REG_IOS 0x0B /* IO State register */ #define REG_IOI 0x0C /* IO Interrupt Enable register */ #define REG_IOC 0x0E /* IO Control register */ #define IOC_SRESET 0x08 /* Software reset */ #define IOC_GPIO30 0x04 /* GPIO 3:0 unset: as IO, set: as modem pins */ #define IOC_GPIO74 0x02 /* GPIO 7:4 unset: as IO, set: as modem pins */ #define IOC_IOLATCH 0x01 /* Unset: input unlatched, set: input latched */ /* Redefine some MCR bits */ #ifdef UART_MCR_TCRTLR #undef UART_MCR_TCRTLR #endif #define UART_MCR_TCRTLR 0x04 #define UART_MCR_IRDA 0x40 #define WRITE_CMD(reg, ch) (REG_WRITE | (reg & 0xf) << 3 | (ch & 0x1) << 1) #define READ_CMD(reg, ch) (REG_READ | (reg & 0xf) << 3 | (ch & 0x1) << 1) static struct sc16is7x2_platform_data sc16is7x2_spi_data = { .uartclk = 18432000, .uart_base = 0, }; /* 16bit SPI command to read or write a register */ struct sc16is7x2_spi_reg { u8 cmd; u8 value; } __attribute__ ((packed)); struct sc16is7x2_chip; /* * Some registers must be read back to modify. * To save time we cache them here in memory */ struct sc16is7x2_channel { struct sc16is7x2_chip *chip; /* back link */ struct mutex lock; struct uart_port uart; struct spi_transfer fifo_rx; struct spi_transfer fifo_tx[3]; u8 iir; u8 lsr; u8 msr; u8 ier; /* cache for IER register */ u8 fcr; /* cache for FCR register */ u8 lcr; /* cache for LCR register */ u8 mcr; /* cache for MCR register */ u8 rxbuf[FIFO_SIZE+1]; u8 write_fifo_cmd; u8 read_fifo_cmd; bool active; }; struct sc16is7x2_chip { struct spi_device *spi; struct mutex lock; struct sc16is7x2_channel channel[2]; /* for handling irqs: need workqueue since we do spi_sync */ struct workqueue_struct *workqueue; struct work_struct work; /* set to 1 to make the workhandler exit as soon as possible */ int force_end_work; /* need to know we are suspending to avoid deadlock on workqueue */ int suspending; struct spi_message fifo_message; #define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */ #define UART_BUG_NOMSR BIT(2) /* UART has buggy MSR status bits (Au1x00) */ #define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */ u16 bugs; /* port bugs */ #define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS u8 lsr_saved_flags; #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA u8 msr_saved_flags; }; /* ******************************** SPI ********************************* */ /* * Reserve memory for command sequence * @param cnt number of commands */ static inline struct sc16is7x2_spi_reg * sc16is7x2_alloc_spi_cmds(unsigned cnt) { return kzalloc(sizeof(struct sc16is7x2_spi_reg)*cnt, GFP_KERNEL); } /* * sc16is7x2_add_write_cmd - Add write command to sequence */ static inline void sc16is7x2_add_write_cmd(struct sc16is7x2_spi_reg *cmd, u8 reg, u8 ch, u8 value) { cmd->cmd = WRITE_CMD(reg, ch); cmd->value = value; } /* * sc16is7x2_add_read_cmd - Add read command to sequence */ static inline void sc16is7x2_add_read_cmd(struct sc16is7x2_spi_reg *cmd, u8 reg, u8 ch) { cmd->cmd = READ_CMD(reg, ch); cmd->value = 0; } /* * sc16is7x2_complete - Completion handler for async SPI transfers */ static void sc16is7x2_complete(void *context) { struct spi_message *m = context; u8 *tx_chain = m->state; kfree(tx_chain); kfree(m); } /* * sc16is7x2_spi_async - Send command sequence */ static int sc16is7x2_spi_async(struct spi_device *spi, struct sc16is7x2_spi_reg *cmds, unsigned len) { struct spi_transfer *t; struct spi_message *m; m = spi_message_alloc(len, GFP_KERNEL); if (!m) return -ENOMEM; m->complete = sc16is7x2_complete; m->context = m; m->state = cmds; list_for_each_entry(t, &m->transfers, transfer_list) { t->tx_buf = (u8 *)cmds; t->len = 2; t->cs_change = false; //t->cs_change = true; cmds++; } return spi_async(spi, m); } /* * sc16is7x2_write_async - Write a new register content (async) */ static inline int sc16is7x2_write_async(struct spi_device *spi, u8 reg, u8 ch, u8 value) { struct sc16is7x2_spi_reg *cmd = sc16is7x2_alloc_spi_cmds(1); if (!cmd) return -ENOMEM; sc16is7x2_add_write_cmd(cmd, reg, ch, value); return sc16is7x2_spi_async(spi, cmd, 1); } /* * sc16is7x2_write - Write a new register content (sync) */ static int sc16is7x2_write(struct spi_device *spi, u8 reg, u8 ch, u8 val) { u16 word = REG_WRITE | (reg & 0xf) << 3 | (ch & 0x3) << 1 | val << 8; return spi_write(spi, (const u8 *)&word, sizeof(word)); } /** * sc16is7x2_read - Read back register content * @spi: The SPI device * @reg: Register offset * * Returns positive 8 bit value from device if successful or a * negative value on error */ static int sc16is7x2_read(struct spi_device *spi, unsigned reg, unsigned ch) { u8 cmd = REG_READ | (reg & 0xf) << 3 | (ch & 0x3) << 1; return spi_w8r8(spi, cmd); } /* ******************************** UART ********************************* */ /* Uart divisor latch write */ static inline void sc16is7x2_add_dl_write_cmd(struct sc16is7x2_spi_reg *cmd, u8 ch, int value) { sc16is7x2_add_write_cmd(&cmd[0], UART_DLL, ch, value & 0xff); sc16is7x2_add_write_cmd(&cmd[1], UART_DLM, ch, (value >> 8) & 0xff); } static unsigned int sc16is7x2_tx_empty(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned lsr; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); mutex_lock(&chan->lock); lsr = chan->lsr; mutex_unlock(&chan->lock); return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0; } static unsigned int sc16is7x2_get_mctrl(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned int status; unsigned int ret; dev_dbg(&ts->spi->dev, "%s\n", __func__); printk(KERN_DEBUG "---------> function call %s\n", __func__); status = chan->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 unsigned int __set_mctrl(unsigned int mctrl) { unsigned char mcr = 0; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; if (mctrl & TIOCM_DTR) mcr |= UART_MCR_DTR; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; return mcr; } static void sc16is7x2_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); sc16is7x2_write_async(ts->spi, UART_MCR, ch, __set_mctrl(mctrl)); } static inline void __stop_tx(struct sc16is7x2_channel *chan) { struct sc16is7x2_chip *ts = chan->chip; unsigned ch = chan->uart.line & 0x01; if (chan->ier & UART_IER_THRI) { chan->ier &= ~UART_IER_THRI; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } } static void sc16is7x2_stop_tx(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); __stop_tx(chan); } /* open */ static void sc16is7x2_start_tx(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); if (!(chan->ier & UART_IER_THRI)) { chan->ier |= UART_IER_THRI; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } } static void sc16is7x2_stop_rx(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); chan->ier &= ~UART_IER_RLSI; chan->uart.read_status_mask &= ~UART_LSR_DR; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } static void sc16is7x2_enable_ms(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; dev_dbg(&ts->spi->dev, "%s\n", __func__); printk(KERN_DEBUG "---------> function call %s\n", __func__); chan->ier |= UART_IER_MSI; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } static void sc16is7x2_break_ctl(struct uart_port *port, int break_state) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; unsigned long flags; dev_dbg(&ts->spi->dev, "%s\n", __func__); printk(KERN_DEBUG "---------> function call %s\n", __func__); spin_lock_irqsave(&chan->uart.lock, flags); if (break_state == -1) chan->lcr |= UART_LCR_SBC; else chan->lcr &= ~UART_LCR_SBC; spin_unlock_irqrestore(&chan->uart.lock, flags); sc16is7x2_write_async(ts->spi, UART_LCR, ch, chan->lcr); } static int sc16is7x2_startup(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; unsigned long flags; dev_dbg(&ts->spi->dev, "%s (line %d)\n", __func__, port->line); spin_lock_irqsave(&chan->uart.lock, flags); chan->lcr = UART_LCR_WLEN8; chan->mcr = __set_mctrl(chan->uart.mctrl); chan->fcr = 0; chan->ier = UART_IER_RLSI | UART_IER_RDI; spin_unlock_irqrestore(&chan->uart.lock, flags); { u8 tmp; sc16is7x2_write_async(ts->spi, UART_IER, ch,0); tmp = sc16is7x2_read(ts->spi, UART_IIR, ch); tmp = sc16is7x2_read(ts->spi, UART_LSR, ch); tmp = sc16is7x2_read(ts->spi, UART_MSR, ch); sc16is7x2_write_async(ts->spi, UART_FCR, ch,UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); sc16is7x2_write_async(ts->spi, UART_FCR, ch,chan->fcr); sc16is7x2_write_async(ts->spi, UART_LCR, ch,chan->lcr); sc16is7x2_write_async(ts->spi, UART_MCR, ch,chan->mcr); } chan->active = true; return 0; } static void sc16is7x2_shutdown(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned long flags; unsigned ch = port->line & 0x01; dev_dbg(&ts->spi->dev, "%s\n", __func__); BUG_ON(!chan); BUG_ON(!ts); if (ts->suspending) return; /* Disable interrupts from this port */ chan->ier = 0; chan->active = false; sc16is7x2_write(ts->spi, UART_IER, ch, chan->ier); /* Wait for worker of this channel to finish */ mutex_lock(&chan->lock); spin_lock_irqsave(&chan->uart.lock, flags); chan->mcr = __set_mctrl(chan->uart.mctrl); spin_unlock_irqrestore(&chan->uart.lock, flags); /* Disable break condition and FIFOs */ chan->lcr &= ~UART_LCR_SBC; sc16is7x2_write(ts->spi, UART_MCR, ch, chan->mcr); sc16is7x2_write(ts->spi, UART_LCR, ch, chan->lcr); mutex_unlock(&chan->lock); } static void sc16is7x2_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; unsigned long flags; unsigned int baud, quot; u8 ier, mcr, lcr, fcr = 0; u8 efr = UART_EFR_ECB; printk(KERN_DEBUG "---------> function call %s\n", __func__); /* set word length */ switch (termios->c_cflag & CSIZE) { case CS5: lcr = UART_LCR_WLEN5; break; case CS6: lcr = UART_LCR_WLEN6; break; case CS7: lcr = UART_LCR_WLEN7; break; default: case CS8: lcr = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) lcr |= UART_LCR_STOP; if (termios->c_cflag & PARENB) lcr |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) lcr |= UART_LCR_EPAR; #ifdef CMSPAR if (termios->c_cflag & CMSPAR) lcr |= UART_LCR_SPAR; #endif /* Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, port->uartclk / 16); quot = uart_get_divisor(port, baud); dev_dbg(&ts->spi->dev, "%s (baud %u)\n", __func__, baud); /* configure the fifo */ if (baud < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00; else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01; /* * MCR-based auto flow control. When AFE is enabled, RTS will be * deasserted when the receive FIFO contains more characters than * the trigger, or the MCR RTS bit is cleared. In the case where * the remote UART is not using CTS auto flow control, we must * have sufficient FIFO entries for the latency of the remote * UART to respond. IOW, at least 32 bytes of FIFO. */ chan->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) chan->mcr |= UART_MCR_AFE; /* * Ok, we're now changing the port state. Do it with * interrupts disabled. */ spin_lock_irqsave(&chan->uart.lock, flags); /* we are sending char from a workqueue so enable */ // chan->uart.state->port.tty->low_latency = 1; chan->uart.info->port.tty->low_latency = 1; /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); chan->uart.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) chan->uart.read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (termios->c_iflag & (BRKINT | PARMRK)) chan->uart.read_status_mask |= UART_LSR_BI; /* Characters to ignore */ chan->uart.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) chan->uart.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (termios->c_iflag & IGNBRK) { chan->uart.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) chan->uart.ignore_status_mask |= UART_LSR_OE; } /* ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) chan->uart.ignore_status_mask |= UART_LSR_DR; /* CTS flow control flag and modem status interrupts */ chan->ier &= ~UART_IER_MSI; if (UART_ENABLE_MS(&chan->uart, termios->c_cflag)) chan->ier |= UART_IER_MSI; if (termios->c_cflag & CRTSCTS) efr |= UART_EFR_CTS | UART_EFR_RTS; mcr = __set_mctrl(chan->uart.mctrl); ier = chan->ier; chan->lcr = lcr; /* Save LCR */ chan->fcr = fcr; /* Save FCR */ chan->mcr = mcr; /* Save MCR */ fcr |= UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; spin_unlock_irqrestore(&chan->uart.lock, flags); sc16is7x2_write_async(ts->spi, UART_LCR, ch,UART_LCR_DLAB); printk(KERN_DEBUG "+++++++++++++++++++> %s :%d after write to read UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch)); sc16is7x2_write_async(ts->spi, UART_DLL, ch,quot & 0xff); sc16is7x2_write_async(ts->spi, UART_DLM, ch,(quot >> 8) & 0xff); sc16is7x2_write_async(ts->spi, UART_LCR, ch,0xBF); sc16is7x2_write_async(ts->spi, UART_EFR, ch, efr); sc16is7x2_write_async(ts->spi, UART_LCR, ch, lcr); sc16is7x2_write_async(ts->spi, UART_FCR, ch, fcr); sc16is7x2_write_async(ts->spi, UART_MCR, ch, mcr); sc16is7x2_write_async(ts->spi, UART_IER, ch, ier); #if 0 //for test { printk(KERN_DEBUG "+++++++++++++++++++> %s :%d termios UART_LCR = %x lcr = %x\n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch),lcr); printk(KERN_DEBUG "+++++++++++++++++++> %s :%d termios UART_LCR = %x lcr = %x\n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch),lcr); sc16is7x2_write(ts->spi, UART_LCR, ch,UART_LCR_DLAB); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_DLL = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_DLL,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_DLM = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_DLM,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_MCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_MCR,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_IER = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_IER,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_IIR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_IIR,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d UART_LSR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LSR,ch)); sc16is7x2_write(ts->spi, UART_LCR, ch,lcr); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d after restore UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR,ch)); } #endif /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); } static const char * sc16is7x2_type(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); return TYPE_NAME; } static void sc16is7x2_release_port(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); ts->force_end_work = 1; } static int sc16is7x2_request_port(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); return 0; } static void sc16is7x2_config_port(struct uart_port *port, int flags) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); if (flags & UART_CONFIG_TYPE) chan->uart.type = PORT_SC16IS7X2; } static int sc16is7x2_verify_port(struct uart_port *port, struct serial_struct *ser) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); if (ser->irq < 0 || ser->baud_base < 9600 || ser->type != PORT_SC16IS7X2) return -EINVAL; return 0; } static struct uart_ops sc16is7x2_uart_ops = { .tx_empty = sc16is7x2_tx_empty, .set_mctrl = sc16is7x2_set_mctrl, //open会调用????? .get_mctrl = sc16is7x2_get_mctrl, .stop_tx = sc16is7x2_stop_tx, .start_tx = sc16is7x2_start_tx, .stop_rx = sc16is7x2_stop_rx, .enable_ms = sc16is7x2_enable_ms, .break_ctl = sc16is7x2_break_ctl, .startup = sc16is7x2_startup, //open 的入口 .shutdown = sc16is7x2_shutdown, .set_termios = sc16is7x2_set_termios, //open会调用 tcgetattr .type = sc16is7x2_type, .release_port = sc16is7x2_release_port, .request_port = sc16is7x2_request_port, .config_port = sc16is7x2_config_port, .verify_port = sc16is7x2_verify_port, }; #define MIN(a, b) ((a < b) ? (a) : (b)) /* ******************************** IRQ ********************************* */ static void sc16is7x2_handle_fifo_rx(struct sc16is7x2_channel *chan) { struct uart_port *uart = &chan->uart; //struct tty_struct *tty = uart->state->port.tty; struct tty_struct *tty = uart->info->port.tty; u8 *rxbuf = chan->rxbuf; u8 lsr = chan->lsr; unsigned i, count = chan->fifo_rx.len; unsigned long flags; char flag = TTY_NORMAL; printk(KERN_DEBUG "---------> function call %s\n", __func__); spin_lock_irqsave(&uart->lock, flags); if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { /* * For statistics only */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); chan->uart.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(&chan->uart)) goto ignore_char; } else if (lsr & UART_LSR_PE) chan->uart.icount.parity++; else if (lsr & UART_LSR_FE) chan->uart.icount.frame++; if (lsr & UART_LSR_OE) chan->uart.icount.overrun++; /* * Mask off conditions which should be ignored. */ lsr &= chan->uart.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; } for (i = 1; i < count; i++) { uart->icount.rx++; if (!uart_handle_sysrq_char(uart, rxbuf[i])) uart_insert_char(uart, lsr, UART_LSR_OE, rxbuf[i], flag); } ignore_char: spin_unlock_irqrestore(&uart->lock, flags); if (count > 1) tty_flip_buffer_push(tty); } static void sc16is7x2_handle_fifo_tx(struct sc16is7x2_channel *chan) { struct uart_port *uart = &chan->uart; //struct circ_buf *xmit = &uart->state->xmit; struct circ_buf *xmit = &uart->info->xmit; unsigned count = chan->fifo_tx[1].len + chan->fifo_tx[2].len; unsigned long flags; BUG_ON(!uart); BUG_ON(!xmit); spin_lock_irqsave(&uart->lock, flags); uart->icount.tx += count; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(uart); if (uart_circ_empty(xmit)) { __stop_tx(chan); } spin_unlock_irqrestore(&uart->lock, flags); } static bool sc16is7x2_msg_add_fifo_rx(struct sc16is7x2_chip *ts, unsigned ch) { struct spi_message *m = &(ts->fifo_message); struct spi_transfer *t = &(ts->channel[ch].fifo_rx); int rxlvl = sc16is7x2_read(ts->spi, REG_RXLVL, ch); if (rxlvl > 0) { t->len = rxlvl + 1; spi_message_add_tail(t, m); return true; } return false; } static bool sc16is7x2_msg_add_fifo_tx(struct sc16is7x2_chip *ts, unsigned ch) { struct sc16is7x2_channel * const chan = &(ts->channel[ch]); struct uart_port *uart = &chan->uart; //struct circ_buf *xmit = &uart->state->xmit; struct circ_buf *xmit = &uart->info->xmit; unsigned count; bool split_transfer; u8 txlvl; printk(KERN_DEBUG "---------> function call %s\n", __func__); if (chan->uart.x_char && chan->lsr & UART_LSR_THRE) { dev_dbg(&ts->spi->dev, "tx: x-char\n"); sc16is7x2_write(ts->spi, UART_TX, ch, uart->x_char); uart->icount.tx++; uart->x_char = 0; return false; } if (uart_tx_stopped(&chan->uart)) { dev_dbg(&ts->spi->dev, "tx: stopped!\n"); sc16is7x2_stop_tx(uart); return false; } if (uart_circ_empty(xmit)) { __stop_tx(chan); return false; } txlvl = sc16is7x2_read(ts->spi, REG_TXLVL, ch); if (txlvl <= 0) { dev_dbg(&ts->spi->dev, " fifo full\n"); return false; } /* number of bytes to transfer to the fifo */ count = MIN(txlvl, uart_circ_chars_pending(xmit)); //uart_circ_chars_pending = ((head) - (tail)) & ((UART_XMIT_SIZE)-1) printk(KERN_DEBUG "+++++++++>kathy test%s:%d channel = %d bytes to transfer to the fifo = %d \n",__func__,__LINE__,ch,count); split_transfer = (UART_XMIT_SIZE - xmit->tail) <= count; /* add command transfer */ spi_message_add_tail(&(chan->fifo_tx[0]), &(ts->fifo_message)); /* add first fifo transfer */ spi_message_add_tail(&(chan->fifo_tx[1]), &(ts->fifo_message)); chan->fifo_tx[1].tx_buf = xmit->buf + xmit->tail; if (!split_transfer) { chan->fifo_tx[1].len = count; //chan->fifo_tx[1].cs_change = true; chan->fifo_tx[1].cs_change = false; chan->fifo_tx[2].len = 0; } else { chan->fifo_tx[1].len = (UART_XMIT_SIZE - 1) - xmit->tail; // chan->fifo_tx[1].cs_change = false; chan->fifo_tx[1].cs_change = true; chan->fifo_tx[2].tx_buf = xmit->buf; chan->fifo_tx[2].cs_change = true; // chan->fifo_tx[2].cs_change = false; chan->fifo_tx[2].len = count - chan->fifo_tx[1].len; /* add second fifo transfer */ spi_message_add_tail(&(chan->fifo_tx[2]), &(ts->fifo_message)); } xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); return true; } #if 0 //kathy chang delet 2010-11-22 10:14 static void sc16is7x2_handle_modem(struct sc16is7x2_chip *ts, unsigned ch) { struct sc16is7x2_channel *chan = &(ts->channel[ch]); struct uart_port *uart = &chan->uart; if (chan->msr & UART_MSR_ANY_DELTA && chan->ier & UART_IER_MSI && uart->state != NULL) { if (chan->msr & UART_MSR_TERI) uart->icount.rng++; if (chan->msr & UART_MSR_DDSR) uart->icount.dsr++; if (chan->msr & UART_MSR_DDCD) uart_handle_dcd_change(uart, chan->msr & UART_MSR_DCD); if (chan->msr & UART_MSR_DCTS) uart_handle_cts_change(uart, chan->msr & UART_MSR_CTS); wake_up_interruptible(&uart->state->port.delta_msr_wait); } } #endif static bool sc16is7x2_handle_channel(struct sc16is7x2_chip *ts, unsigned ch) { struct sc16is7x2_channel *chan = &(ts->channel[ch]); struct spi_message *m = &(ts->fifo_message); bool rx, tx; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s (%i)\n", __func__, ch); chan->iir = sc16is7x2_read(ts->spi, UART_IIR, ch); chan->msr = sc16is7x2_read(ts->spi, UART_MSR, ch); chan->lsr = sc16is7x2_read(ts->spi, UART_LSR, ch); #if 0 //kathy chang delete 2010-11-22 10:13 sc16is7x2_handle_modem(ts, ch); #endif spi_message_init(m); rx = sc16is7x2_msg_add_fifo_rx(ts, ch); tx = sc16is7x2_msg_add_fifo_tx(ts, ch); printk(KERN_DEBUG "+++++++++>kathy test%s:%d rx %d tx =%d\n",__func__,__LINE__,rx,tx); if (rx || tx) spi_sync(ts->spi, m); if (rx) sc16is7x2_handle_fifo_rx(chan); if (tx) sc16is7x2_handle_fifo_tx(chan); dev_dbg(&ts->spi->dev, "%s finished (iir = 0x%02x)\n", __func__, chan->iir); return (chan->iir & UART_IIR_NO_INT) == 0x00; // } static void sc16is7x2_work(struct work_struct *w) { struct sc16is7x2_chip *ts = container_of(w, struct sc16is7x2_chip, work); unsigned pending = 0; unsigned ch = 0; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); BUG_ON(!w); BUG_ON(!ts); if (ts->force_end_work) { dev_dbg(&ts->spi->dev, "%s: force end!\n", __func__); return; } if (ts->channel[0].active) pending |= BIT(0); if (ts->channel[1].active) pending |= BIT(1); do { mutex_lock(&(ts->channel[ch].lock)); if (pending & BIT(ch) && ts->channel[ch].active) { if (!sc16is7x2_handle_channel(ts, ch)) pending &= ~BIT(ch); } mutex_unlock(&(ts->channel[ch].lock)); ch ^= 1; /* switch channel */ } while (!ts->force_end_work && !freezing(current) && pending); dev_dbg(&ts->spi->dev, "%s finished\n", __func__); } static irqreturn_t sc16is7x2_interrupt(int irq, void *dev_id) { struct sc16is7x2_chip *ts = dev_id; printk(KERN_DEBUG "---------> sc16is7x2_interrupt function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); if (!ts->force_end_work && !work_pending(&ts->work) && !freezing(current) && !ts->suspending) { queue_work(ts->workqueue, &ts->work); } return IRQ_HANDLED; } /* ******************************** INIT ********************************* */ static struct uart_driver sc16is7x2_uart_driver; static int sc16is7x2_register_uart_port(struct sc16is7x2_chip *ts, struct sc16is7x2_platform_data *pdata, unsigned ch) { struct sc16is7x2_channel *chan = &(ts->channel[ch]); struct uart_port *uart = &chan->uart; mutex_init(&chan->lock); chan->active = false; /* will be set in startup */ chan->chip = ts; chan->read_fifo_cmd = READ_CMD(UART_RX, ch); chan->fifo_rx.tx_buf = &(chan->read_fifo_cmd); chan->fifo_rx.rx_buf = chan->rxbuf; // chan->fifo_rx.cs_change = true; chan->fifo_rx.cs_change = false; chan->write_fifo_cmd = WRITE_CMD(UART_TX, ch); chan->fifo_tx[0].tx_buf = &(chan->write_fifo_cmd); chan->fifo_tx[0].rx_buf = NULL; chan->fifo_tx[0].len = 1; chan->fifo_tx[0].cs_change = false; chan->fifo_tx[1].rx_buf = NULL; chan->fifo_tx[2].rx_buf = NULL; uart->irq = ts->spi->irq; uart->uartclk = pdata->uartclk; uart->fifosize = FIFO_SIZE; uart->ops = &sc16is7x2_uart_ops; uart->flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; uart->line = pdata->uart_base + ch; uart->type = PORT_SC16IS7X2; uart->dev = &ts->spi->dev; return uart_add_one_port(&sc16is7x2_uart_driver, uart); } static int __devinit sc16is7x2_probe(struct spi_device *spi) { struct sc16is7x2_chip *ts; struct sc16is7x2_platform_data *pdata; int ret; printk(KERN_DEBUG "---------> function call %s\n", __func__); spi->max_speed_hz = H_SPI_SPEED; //can modify pdata = &sc16is7x2_spi_data; ret = spi_setup(spi); if (ret < 0) return ret; ts = kzalloc(sizeof(struct sc16is7x2_chip), GFP_KERNEL); if (!ts) return -ENOMEM; mutex_init(&ts->lock); dev_set_drvdata(&spi->dev, ts); ts->spi = spi; ts->force_end_work = 1; /* Reset the chip TODO: and disable IRQ output */ sc16is7x2_write(spi, REG_IOC, 0, IOC_SRESET); at91_sys_write(AT91_PMC+PMC_PCER,1<irq); at91_set_gpio_input(AT91_PIN_PC12, 1); at91_set_A_periph(AT91_PIN_PC12, 1); at91_sys_write(AT91_AIC_SMR(spi->irq),(1<<5)|7); at91_sys_write(AT91_AIC_ICCR,1<irq); printk(KERN_DEBUG "+++++++++++++++++++++> %s :%d AT91_AIC_SMR(29) %x \n",__func__,__LINE__,at91_sys_read(AT91_AIC_SMR(spi->irq))); ret = request_irq(spi->irq, sc16is7x2_interrupt,0, "sc16is7x2", ts); if (ret) { printk(KERN_INFO "ERROR: can't get assigned irq %d \n\r", spi->irq); } if (ret) { dev_warn(&ts->spi->dev, "cannot register interrupt\n"); goto exit_destroy; } ret = sc16is7x2_register_uart_port(ts, pdata, 0); if (ret) goto exit_irq; ret = sc16is7x2_register_uart_port(ts, pdata, 1); if (ret) goto exit_uart0; ts->workqueue = create_freezeable_workqueue(DRIVER_NAME); if (!ts->workqueue) { dev_warn(&ts->spi->dev, "cannot create workqueue\n"); ret = -EBUSY; goto exit_uart1; } INIT_WORK(&ts->work, sc16is7x2_work); ts->force_end_work = 0; printk(KERN_INFO DRIVER_NAME " at CS%d (irq %d), 2 UARTs ttySC%d, ttySC%d is successful!!!!\n", spi->chip_select, spi->irq,pdata->uart_base, pdata->uart_base + 1); return ret; exit_uart1: uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart); exit_uart0: uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart); exit_irq: free_irq(spi->irq, ts); exit_destroy: dev_set_drvdata(&spi->dev, NULL); mutex_destroy(&ts->lock); kfree(ts); return ret; } static int __devexit sc16is7x2_remove(struct spi_device *spi) { struct sc16is7x2_chip *ts; int ret; ts = dev_get_drvdata(&spi->dev); if (ts == NULL) return -ENODEV; free_irq(spi->irq, ts); ts->force_end_work = 1; if (ts->workqueue) { flush_workqueue(ts->workqueue); destroy_workqueue(ts->workqueue); ts->workqueue = NULL; } dev_set_drvdata(&spi->dev, NULL); ret = uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart); if (ret) { dev_err(&spi->dev, "Failed to remove the UART port 0: %d\n", ret); goto exit_error; } ret = uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart); if (ret) { dev_err(&spi->dev, "Failed to remove the UART port 1: %d\n", ret); goto exit_error; } mutex_destroy(&ts->lock); kfree(ts); exit_error: return ret; } static struct uart_driver sc16is7x2_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = "ttySC", .major = SC16IS7X2_MAJOR, .minor = SC16IS7X2_MINOR, .nr = MAX_SC16IS7X2, }; static struct spi_driver sc16is7x2_spi_driver = { .driver = { .name = DRIVER_NAME, .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = sc16is7x2_probe, .remove = __devexit_p(sc16is7x2_remove), }; static int __init sc16is7x2_init(void) { int ret; ret = uart_register_driver(&sc16is7x2_uart_driver); if (ret) { printk(KERN_ERR "Couldn't register sc16is7x2 uart driver\n"); return ret; } return spi_register_driver(&sc16is7x2_spi_driver); } module_init(sc16is7x2_init); static void __exit sc16is7x2_exit(void) { spi_unregister_driver(&sc16is7x2_spi_driver); uart_unregister_driver(&sc16is7x2_uart_driver); } module_exit(sc16is7x2_exit); MODULE_AUTHOR("kathy chang"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SC16IS7x2 SPI based UART chip"); MODULE_ALIAS("spi:" DRIVER_NAME);