This is NOT a patch. Only for initial review and comment. So, do NOT apply. Since the new version is very different from the old one I'm posting it as a new driver to simplify the code view. New features: - Using the regmap. This makes it easy to add support for the driver via I2C. Register cache removes unnecessary reading that improves performance. I tested the driver on a slow processor with a very slow SPI bus, so the speed was an important factor when writing code. - Using devm_XXX-related functions. - The use of threaded IRQ with IRQF_ONESHOT flag allows the driver to the hardware that supports only level IRQ. - Proper serial error handling. - Proper treatment of FIFO queues. - Advanced flags allows turn on RS-485 mode (Auto direction control). - Ability to load multiple drivers MAX3107. - Header moved to include/linux/platform_data. - Cleanup, cleanup, cleanup... So waiting for comments. Thanks! --- drivers/tty/serial/Kconfig | 7 +- drivers/tty/serial/max3107.c | 1087 +++++++++++++++++++++++++++++++++ include/linux/platform_data/max3107.h | 56 ++ 3 files changed, 1149 insertions(+), 1 deletions(-) create mode 100644 drivers/tty/serial/max3107.c create mode 100644 include/linux/platform_data/max3107.h diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 070b442..d2bbfd11 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -254,8 +254,13 @@ config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI select SERIAL_CORE + select REGMAP_SPI if SPI help - MAX3100 chip support + The MAX3107 is an advanced universal asynchronous receiver-transmitter + with 128 words each of receive and transmit first-in/first-out (FIFO) + that can be controlled through I2C or high-speed SPI. + + Say Y here if you want to support this chip. config SERIAL_MAX3107 tristate "MAX3107 support" diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c new file mode 100644 index 0000000..c7ea7d0 --- /dev/null +++ b/drivers/tty/serial/max3107.c @@ -0,0 +1,1087 @@ +/* + * SPI UART protocol driver for Maxim (Dallas) MAX3107 + * + * Based on max3100.c + * by Christian Pellegrin <chripell@xxxxxxxxxxxx> + * and max3110.c + * by Feng Tang <feng.tang@xxxxxxxxx> + * + * Rewritten by Alexander Shiyan <shc_work@xxxxxxx> + * + * Copyright (C) Aavamobile 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* TODO: GPIO handling */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include <linux/platform_data/max3107.h> + +#define MAX3107_MAJOR 204 +#define MAX3107_MINOR 209 + +/* Chip wakeup delay */ +#define MAX3107_WAKEUP_DELAY 50 + +/* Sleep mode definitions */ +#define MAX3107_DISABLE_FORCED_SLEEP 0 +#define MAX3107_ENABLE_FORCED_SLEEP 1 +#define MAX3107_DISABLE_AUTOSLEEP 2 +#define MAX3107_ENABLE_AUTOSLEEP 3 + +/* MAX3107 register definitions */ +#define MAX3107_RHR_REG (0x00) /* RX FIFO */ +#define MAX3107_THR_REG (0x00) /* TX FIFO */ +#define MAX3107_IRQEN_REG (0x01) /* IRQ enable */ +#define MAX3107_IRQSTS_REG (0x02) /* IRQ status */ +#define MAX3107_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */ +#define MAX3107_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */ +#define MAX3107_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */ +#define MAX3107_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */ +#define MAX3107_STS_IRQEN_REG (0x07) /* Status IRQ enable */ +#define MAX3107_STS_IRQSTS_REG (0x08) /* Status IRQ status */ +#define MAX3107_MODE1_REG (0x09) /* MODE1 */ +#define MAX3107_MODE2_REG (0x0a) /* MODE2 */ +#define MAX3107_LCR_REG (0x0b) /* LCR */ +#define MAX3107_RXTO_REG (0x0c) /* RX timeout */ +#define MAX3107_HDPIXDELAY_REG (0x0d) /* Auto transceiver delays */ +#define MAX3107_IRDA_REG (0x0e) /* IRDA settings */ +#define MAX3107_FLOWLVL_REG (0x0f) /* Flow control levels */ +#define MAX3107_FIFOTRIGLVL_REG (0x10) /* FIFO IRQ trigger levels */ +#define MAX3107_TXFIFOLVL_REG (0x11) /* TX FIFO level */ +#define MAX3107_RXFIFOLVL_REG (0x12) /* RX FIFO level */ +#define MAX3107_FLOWCTRL_REG (0x13) /* Flow control */ +#define MAX3107_XON1_REG (0x14) /* XON1 character */ +#define MAX3107_XON2_REG (0x15) /* XON2 character */ +#define MAX3107_XOFF1_REG (0x16) /* XOFF1 character */ +#define MAX3107_XOFF2_REG (0x17) /* XOFF2 character */ +#define MAX3107_GPIOCFG_REG (0x18) /* GPIO config */ +#define MAX3107_GPIODATA_REG (0x19) /* GPIO data */ +#define MAX3107_PLLCFG_REG (0x1a) /* PLL config */ +#define MAX3107_BRGCFG_REG (0x1b) /* Baud rate generator conf */ +#define MAX3107_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */ +#define MAX3107_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */ +#define MAX3107_CLKSRC_REG (0x1e) /* Clock source */ +#define MAX3107_REVID_REG (0x1f) /* Revision identification */ + +/* IRQ register bits */ +#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ +#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ +#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ +#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ +#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ +#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ +#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ +#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ + +/* LSR register bits */ +#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ +#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ +#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ +#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ +#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ +#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ +#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ + +/* Special character register bits */ +#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ +#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ +#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ +#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ +#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ +#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ + +/* Status register bits */ +#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ +#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ +#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ +#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ +#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ +#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ + +/* MODE1 register bits */ +#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ +#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ +#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ +#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ +#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ +#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ +#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ +#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ + +/* MODE2 register bits */ +#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ +#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ +#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ +#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ +#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ +#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ +#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ +#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ + +/* LCR register bits */ +#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ +#define MAX3107_LCR_WORD_LEN_5 (0x00) +#define MAX3107_LCR_WORD_LEN_6 (0x01) +#define MAX3107_LCR_WORD_LEN_7 (0x02) +#define MAX3107_LCR_WORD_LEN_8 (0x03) + +/* IRDA register bits */ +#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ +#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ +#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ +#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ +#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ + +/* Flow control trigger level register masks */ +#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ +#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX3107_FLOWLVL_HALT(words) ((words / 8) & 0x0f) +#define MAX3107_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4) + +/* FIFO interrupt trigger level register masks */ +#define MAX3107_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f) +#define MAX3107_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4) + +/* Flow control register bits */ +#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs + * are used in conjunction with + * XOFF2 for definition of + * special character */ +#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ +#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ +#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 + * + * SWFLOW bits 1 & 0 table: + * 00 -> no transmitter flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * and controls + * transmitter + * 10 -> receiver compares + * XON1 and XOFF1 + * and controls + * transmitter + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 and controls + * transmitter + */ +#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ +#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* GPIO configuration register bits */ +#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ +#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ +#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ +#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ +#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ +#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ +#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ +#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ + +/* GPIO DATA register bits */ +#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ +#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ +#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ +#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ +#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ +#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ +#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ +#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ + +/* PLL configuration register masks */ +#define MAX3107_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */ +#define MAX3107_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */ + +/* Baud rate generator configuration register masks and bits */ +#define MAX3107_BRGCFG_FRACT_MASK (0x0f) /* Fractional portion of + * Baud rate generator divisor + */ +#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ +#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ + +/* Clock source register bits */ +#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ +#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ +#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ +#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ +#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ + +/* Misc definitions */ +#define MAX3107_FIFO_SIZE (128) +#define MAX3107_REV_ID (0xa0) +#define MAX3107_REV_MASK (0xfe) + +/* Error status definitions */ +#define MAX3107_ERR_PARITY MAX3107_LSR_RXPAR_BIT +#define MAX3107_ERR_FRAME MAX3107_LSR_FRERR_BIT +#define MAX3107_ERR_OVERRUN MAX3107_LSR_RXOVR_BIT +#define MAX3107_ERR_BREAK MAX3107_LSR_RXBRK_BIT + +/* IRQ status bits definitions */ +#define MAX3107_IRQ_TX (MAX3107_IRQ_TXFIFO_BIT | MAX3107_IRQ_TXEMPTY_BIT) +#define MAX3107_IRQ_RX (MAX3107_IRQ_RXFIFO_BIT | MAX3107_IRQ_RXEMPTY_BIT) + +struct max3107_port { + struct uart_port port; + + struct regmap *regmap; + + struct workqueue_struct *wq; + struct work_struct tx_work; + + struct mutex max3107_mutex; + + int suspended; + + struct max3107_plat *pdata; +}; + +/* Baudrate table for 13 MHz external clock */ +const struct baud_table brg_table_13m[] = { + { 300, 0x0a9400 | 0x05 }, + { 600, 0x054a00 | 0x03 }, + { 1200, 0x02a500 | 0x01 }, + { 2400, 0x015200 | 0x09 }, + { 4800, 0x00a900 | 0x04 }, + { 9600, 0x005400 | 0x0a }, + { 19200, 0x002a00 | 0x05 }, + { 38400, 0x001500 | 0x03 }, + { 57600, 0x000e00 | 0x02 }, + { 115200, 0x000700 | 0x01 }, + { 230400, 0x000300 | 0x08 }, + { 460800, 0x000100 | 0x0c }, + { 921600, 0x000100 | 0x1c }, + { 0, 0 } +}; +EXPORT_SYMBOL_GPL(brg_table_13m); + +/* Baudrate table for 26 MHz external clock */ +const struct baud_table brg_table_26m[] = { + { 300, 0x152800 | 0x0a }, + { 600, 0x0a9400 | 0x05 }, + { 1200, 0x054a00 | 0x03 }, + { 2400, 0x02a500 | 0x01 }, + { 4800, 0x015200 | 0x09 }, + { 9600, 0x00a900 | 0x04 }, + { 19200, 0x005400 | 0x0a }, + { 38400, 0x002a00 | 0x05 }, + { 57600, 0x001c00 | 0x03 }, + { 115200, 0x000e00 | 0x02 }, + { 230400, 0x000700 | 0x01 }, + { 460800, 0x000300 | 0x08 }, + { 921600, 0x000100 | 0x0c }, + { 0, 0 } +}; +EXPORT_SYMBOL_GPL(brg_table_26m); + +/* Baudrate table for 3.6864 MHz external clock */ +const struct baud_table brg_table_3_6864m[] = { + { 300, 0x030000 | 0x00 }, + { 600, 0x018000 | 0x00 }, + { 1200, 0x00c000 | 0x00 }, + { 2400, 0x006000 | 0x00 }, + { 4800, 0x003000 | 0x00 }, + { 9600, 0x001800 | 0x00 }, + { 19200, 0x000c00 | 0x00 }, + { 38400, 0x000600 | 0x00 }, + { 57600, 0x000400 | 0x00 }, + { 115200, 0x000200 | 0x00 }, + { 230400, 0x000100 | 0x00 }, + { 460800, 0x000100 | 0x10 }, + { 921600, 0x000100 | 0x20 }, + { 0, 0 } +}; +EXPORT_SYMBOL_GPL(brg_table_3_6864m); + +static bool max3107_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX3107_IRQSTS_REG: + case MAX3107_LSR_IRQSTS_REG: + case MAX3107_SPCHR_IRQSTS_REG: + case MAX3107_STS_IRQSTS_REG: + case MAX3107_TXFIFOLVL_REG: + case MAX3107_RXFIFOLVL_REG: + case MAX3107_REVID_REG: + return false; + default: + break; + } + + return true; +} + +static bool max3107_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX3107_RHR_REG: + case MAX3107_IRQSTS_REG: + case MAX3107_LSR_IRQSTS_REG: + case MAX3107_SPCHR_IRQSTS_REG: + case MAX3107_STS_IRQSTS_REG: + case MAX3107_TXFIFOLVL_REG: + case MAX3107_RXFIFOLVL_REG: + case MAX3107_GPIODATA_REG: + return true; + default: + break; + } + + return false; +} + +static bool max3107_reg_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX3107_RHR_REG: + case MAX3107_IRQSTS_REG: + case MAX3107_SPCHR_IRQSTS_REG: + case MAX3107_STS_IRQSTS_REG: + return true; + default: + break; + } + + return false; +} + +static struct regmap_config max3107_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x1f, + .read_flag_mask = 0x00, + .write_flag_mask= 0x80, + .cache_type = REGCACHE_RBTREE, + .writeable_reg = max3107_reg_writeable, + .volatile_reg = max3107_reg_volatile, + .precious_reg = max3107_reg_precious, +}; + +static u32 max3107_get_brg(int baud, struct max3107_port *s) +{ + int i; + const struct baud_table *baud_tbl = s->pdata->baud_tbl; + + for (i = 0; baud_tbl[i].baud; i++) { + if (baud == baud_tbl[i].baud) + return baud_tbl[i].brg; + } + + return 0; +} + +static void max3107_handle_rx(struct max3107_port *s, unsigned int rxlen) +{ + unsigned int status, ch, flag; + struct tty_struct *tty = s->port.state->port.tty; + + if (rxlen >= MAX3107_FIFO_SIZE) { + dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen); + /* Ensure sanity of RX level */ + rxlen = MAX3107_FIFO_SIZE; + } + + dev_dbg(s->port.dev, "RX Len = %u\n", rxlen); + + while (rxlen--) { + regmap_read(s->regmap, MAX3107_RHR_REG, &ch); + regmap_read(s->regmap, MAX3107_LSR_IRQSTS_REG, &status); + + status &= MAX3107_ERR_PARITY | MAX3107_ERR_FRAME | + MAX3107_ERR_OVERRUN | MAX3107_ERR_BREAK; + + s->port.icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(status)) { + if (status & MAX3107_ERR_BREAK) { + s->port.icount.brk++; + if (uart_handle_break(&s->port)) + continue; + } else if (status & MAX3107_ERR_PARITY) + s->port.icount.parity++; + else if (status & MAX3107_ERR_FRAME) + s->port.icount.frame++; + else if (status & MAX3107_ERR_OVERRUN) + s->port.icount.overrun++; + + status &= s->port.read_status_mask; + if (status & MAX3107_ERR_BREAK) + flag = TTY_BREAK; + else if (status & MAX3107_ERR_PARITY) + flag = TTY_PARITY; + else if (status & MAX3107_ERR_FRAME) + flag = TTY_FRAME; + else if (status & MAX3107_ERR_OVERRUN) + flag = TTY_OVERRUN; + } + + if (uart_handle_sysrq_char(s->port, ch)) + continue; + + if (status & s->port.ignore_status_mask) + continue; + + uart_insert_char(&s->port, status, MAX3107_ERR_OVERRUN, ch, flag); + } + + tty_flip_buffer_push(tty); +} + +static void max3107_handle_tx(struct max3107_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + unsigned int txlen, to_send; + + if (s->port.x_char) { + regmap_write(s->regmap, MAX3107_THR_REG, s->port.x_char); + s->port.icount.tx++; + s->port.x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + return; + + /* Get length of data pending in circular buffer */ + to_send = uart_circ_chars_pending(xmit); + if (to_send) { + /* Limit to size of TX FIFO */ + regmap_read(s->regmap, MAX3107_TXFIFOLVL_REG, &txlen); + txlen = MAX3107_FIFO_SIZE - txlen; + to_send = (to_send > txlen) ? txlen : to_send; + + dev_dbg(s->port.dev, "TX Len = %u\n", to_send); + + /* Add data to send */ + s->port.icount.tx += to_send; + do { + regmap_write(s->regmap, MAX3107_THR_REG, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } while (--to_send > 0); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); +} + +static irqreturn_t max3107_ist(int irq, void *dev_id) +{ + struct max3107_port *s = (struct max3107_port*)dev_id; + unsigned int ists, lsr, rxlen; + + mutex_lock(&s->max3107_mutex); + + for (;;) { + /* Read IRQ status & RX FIFO level */ + regmap_read(s->regmap, MAX3107_IRQSTS_REG, &ists); + regmap_read(s->regmap, MAX3107_RXFIFOLVL_REG, &rxlen); + if (!ists && !(lsr & MAX3107_LSR_RXTO_BIT) && !rxlen) + break; + + dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists); + + if (rxlen) + max3107_handle_rx(s, rxlen); + if (ists & MAX3107_IRQ_TX) + max3107_handle_tx(s); + if (ists & MAX3107_IRQ_CTS_BIT) { + regmap_read(s->regmap, MAX3107_LSR_IRQSTS_REG, &lsr); + uart_handle_cts_change(&s->port, + !!(lsr & MAX3107_LSR_CTS_BIT)); + } + } + + mutex_unlock(&s->max3107_mutex); + + return IRQ_HANDLED; +} + +static void max3107_wq_proc(struct work_struct *ws) +{ + struct max3107_port *s = container_of(ws, struct max3107_port, tx_work); + + mutex_lock(&s->max3107_mutex); + + max3107_handle_tx(s); + + mutex_unlock(&s->max3107_mutex); +} + +static void max3107_enable_ms(struct uart_port *port) +{ + /* Modem status not supported */ +} + +static void max3107_start_tx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + queue_work(s->wq, &s->tx_work); +} + +static void max3107_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +static void max3107_stop_rx(struct uart_port *port) +{ + /* Do nothing */ +} + +static unsigned int max3107_tx_empty(struct uart_port *port) +{ + unsigned int val; + struct max3107_port *s = container_of(port, struct max3107_port, port); + + mutex_lock(&s->max3107_mutex); + regmap_read(s->regmap, MAX3107_TXFIFOLVL_REG, &val); + mutex_unlock(&s->max3107_mutex); + + return val ? 0: TIOCSER_TEMT; +} + +static unsigned int max3107_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* DCD and DSR are not wired and CTS/RTS is hadnled automatically + * so do nothing + */ +} + +static void max3107_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + struct tty_struct *tty; + int baud; + u16 lcr; + u32 brg = 0; + + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + /* Word size */ + if ((termios->c_cflag & CSIZE) == CS7) + lcr = MAX3107_LCR_WORD_LEN_7; + else + lcr = MAX3107_LCR_WORD_LEN_8; + + /* Parity */ + if (termios->c_cflag & PARENB) { + lcr |= MAX3107_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + lcr |= MAX3107_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= MAX3107_LCR_STOPLEN_BIT; /* 2 stops */ + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Set read status mask */ + s->port.read_status_mask = MAX3107_ERR_OVERRUN; + if (termios->c_iflag & INPCK) + s->port.read_status_mask |= MAX3107_ERR_PARITY | MAX3107_ERR_FRAME; + if (termios->c_iflag & (BRKINT | PARMRK)) + s->port.read_status_mask |= MAX3107_ERR_BREAK; + + /* Set status ignore mask */ + s->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + s->port.ignore_status_mask |= MAX3107_ERR_BREAK; + if (!(termios->c_cflag & CREAD)) + s->port.ignore_status_mask |= MAX3107_ERR_PARITY | + MAX3107_ERR_OVERRUN | + MAX3107_ERR_FRAME | + MAX3107_ERR_BREAK; + + /* Get new baud rate generator configuration */ + baud = tty_get_baud_rate(tty); + + brg = max3107_get_brg(baud, s); + tty_termios_encode_baud_rate(termios, baud, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + mutex_lock(&s->max3107_mutex); + + regmap_write(s->regmap, MAX3107_LCR_REG, lcr); + regmap_write(s->regmap, MAX3107_BRGDIVMSB_REG, brg >> 16); + regmap_write(s->regmap, MAX3107_BRGDIVLSB_REG, brg >> 8); + regmap_write(s->regmap, MAX3107_BRGCFG_REG, brg); + + mutex_unlock(&s->max3107_mutex); +} + +static void max3107_register_init(struct max3107_port *s) +{ + unsigned int val; + + /* Configure baud rate, 9600 as default */ + val = max3107_get_brg(9600, s); + + regmap_write(s->regmap, MAX3107_BRGDIVMSB_REG, val >> 16); + regmap_write(s->regmap, MAX3107_BRGDIVLSB_REG, val >> 8); + regmap_write(s->regmap, MAX3107_BRGCFG_REG, val); + + /* Configure LCR register, 8N1 mode by default */ + val = MAX3107_LCR_WORD_LEN_8; + regmap_write(s->regmap, MAX3107_LCR_REG, val); + + /* Configure MODE1 register */ + val = MAX3107_MODE1_IRQSEL_BIT; /* Enable IRQ pin */ + if (s->pdata->flags & MAX3107_AUTO_DIR_CTRL) + val |= MAX3107_MODE1_TRNSCVCTRL_BIT; + regmap_write(s->regmap, MAX3107_MODE1_REG, val); + + /* Configure MODE2 register */ + val = MAX3107_MODE2_RXEMPTINV_BIT; + if (s->pdata->flags & MAX3107_LOOPBACK) + val |= MAX3107_MODE2_LOOPBACK_BIT; + if (s->pdata->flags & MAX3107_ECHO_SUPRESS) + val |= MAX3107_MODE2_ECHOSUPR_BIT; + + /* Reset FIFOs */ + val |= MAX3107_MODE2_FIFORST_BIT; + regmap_write(s->regmap, MAX3107_MODE2_REG, val); + + /* Configure FIFO trigger level register */ + /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */ + val = MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(64); + regmap_write(s->regmap, MAX3107_FIFOTRIGLVL_REG, val); + + /* Configure flow control levels */ + /* Flow control halt level 96, resume level 48 */ + val = MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96); + regmap_write(s->regmap, MAX3107_FLOWLVL_REG, val); + + /* Configure flow control */ + val = (s->pdata->flags & MAX3107_AUTOCTS) ? MAX3107_FLOWCTRL_AUTOCTS_BIT : 0; + val |= (s->pdata->flags & MAX3107_AUTORTS) ? MAX3107_FLOWCTRL_AUTORTS_BIT : 0; + regmap_write(s->regmap, MAX3107_FLOWCTRL_REG, val); + + /* Clear timeout register */ + regmap_write(s->regmap, MAX3107_RXTO_REG, 0); + + /* Configure LSR interrupt enable register */ + /* Enable RX timeout interrupt */ + val = MAX3107_LSR_RXTO_BIT; + regmap_write(s->regmap, MAX3107_LSR_IRQEN_REG, val); + + /* Clear FIFO reset */ + regmap_update_bits(s->regmap, MAX3107_MODE2_REG, + MAX3107_MODE2_FIFORST_BIT, 0); + + /* Clear IRQ status register by reading it */ + regmap_read(s->regmap, MAX3107_IRQSTS_REG, &val); + + /* Configure interrupt enable register */ + /* Enable CTS change interrupt */ + val = MAX3107_IRQ_CTS_BIT; + /* Enable RX, TX interrupts */ + val |= MAX3107_IRQ_RX | MAX3107_IRQ_TX; + regmap_write(s->regmap, MAX3107_IRQEN_REG, val); +} + +static int max3107_startup(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + if (s->pdata->suspend) + s->pdata->suspend(s, 0); + + /* Init registers */ + max3107_register_init(s); + + /* Setup interrupt */ + if (devm_request_threaded_irq(port->dev, port->irq, NULL, max3107_ist, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max3107", s)) { + dev_err(port->dev, "Unable to reguest IRQ %i\n", port->irq); + return -EBUSY; + } + + return 0; +} + +static void max3107_shutdown(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + if (s->suspended && s->pdata->suspend) + s->pdata->suspend(s, 0); + + /* Disable all interrupts */ + mutex_lock(&s->max3107_mutex); + regmap_write(s->regmap, MAX3107_IRQEN_REG, 0); + mutex_unlock(&s->max3107_mutex); + + /* Free the interrupt */ + devm_free_irq(port->dev, port->irq, s); + + if (s->pdata->suspend) + s->pdata->suspend(s, 1); +} + +static const char *max3107_type(struct uart_port *port) +{ + return (port->type == PORT_MAX3107) ? "MAX3107" : NULL; +} + +static void max3107_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +static int max3107_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void max3107_config_port(struct uart_port *port, int flags) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + s->port.type = PORT_MAX3107; +} + +static int max3107_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX3107)) + return 0; + + return -EINVAL; +} + +static void max3107_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support break control, do nothing */ +} + +static struct uart_ops max3107_ops = { + .tx_empty = max3107_tx_empty, + .set_mctrl = max3107_set_mctrl, + .get_mctrl = max3107_get_mctrl, + .stop_tx = max3107_stop_tx, + .start_tx = max3107_start_tx, + .stop_rx = max3107_stop_rx, + .enable_ms = max3107_enable_ms, + .break_ctl = max3107_break_ctl, + .startup = max3107_startup, + .shutdown = max3107_shutdown, + .set_termios = max3107_set_termios, + .type = max3107_type, + .request_port = max3107_request_port, + .release_port = max3107_release_port, + .config_port = max3107_config_port, + .verify_port = max3107_verify_port, +}; + +static struct uart_driver max3107_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyMAX", + .dev_name = "ttyMAX", + .major = MAX3107_MAJOR, + .minor = MAX3107_MINOR, + .nr = 1, + .cons = NULL, +}; + +static volatile int driver_registered = 0; + +static void max3107_set_sleep(struct max3107_port *s, int mode) +{ + unsigned int val; + + mutex_lock(&s->max3107_mutex); + + regmap_read(s->regmap, MAX3107_MODE1_REG, &val); + + switch (mode) { + case MAX3107_DISABLE_FORCED_SLEEP: + val &= ~MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_ENABLE_FORCED_SLEEP: + val |= MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_DISABLE_AUTOSLEEP: + val &= ~MAX3107_MODE1_AUTOSLEEP_BIT; + break; + case MAX3107_ENABLE_AUTOSLEEP: + val |= MAX3107_MODE1_AUTOSLEEP_BIT; + break; + default: + dev_warn(s->port.dev, "Invalid sleep mode %i\n", mode); + mutex_unlock(&s->max3107_mutex); + return; + } + + regmap_write(s->regmap, MAX3107_MODE1_REG, val); + + if ((mode == MAX3107_DISABLE_AUTOSLEEP) || + (mode == MAX3107_DISABLE_FORCED_SLEEP)) + msleep(MAX3107_WAKEUP_DELAY); + + mutex_unlock(&s->max3107_mutex); +} + +static int max3107_suspend(struct spi_device *spi, pm_message_t state) +{ + int ret; + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Suspend\n"); + + ret = uart_suspend_port(&max3107_uart_driver, &s->port); + + if (s->pdata->suspend) + s->pdata->suspend(s, 1); + + return ret; +} + +static int max3107_resume(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Resume\n"); + + if (s->pdata->suspend) + s->pdata->suspend(s, 0); + + return uart_resume_port(&max3107_uart_driver, &s->port); +} + +void max3107_default_suspend(struct max3107_port *s, int do_suspend) +{ + dev_dbg(s->port.dev, "Change suspend state to %i\n", do_suspend); + + s->suspended = do_suspend; + max3107_set_sleep(s, do_suspend ? MAX3107_ENABLE_AUTOSLEEP : + MAX3107_DISABLE_AUTOSLEEP); +} +EXPORT_SYMBOL_GPL(max3107_default_suspend); + +/* Generic platform data */ +static struct max3107_plat generic_plat_data = { + .flags = MAX3107_EXT_CLK | MAX3107_AUTOCTS | MAX3107_AUTORTS, + .baud_tbl = brg_table_26m, + .suspend = &max3107_default_suspend, +}; + +static int __devinit max3107_probe(struct spi_device *spi) +{ + struct max3107_port *s; + struct max3107_plat *pdata = spi->dev.platform_data; + int ret; + unsigned int val; + + /* Check for IRQ */ + if (spi->irq <= 0) { + dev_err(&spi->dev, "No IRQ specified\n"); + return -ENOTSUPP; + } + + /* Alloc port structure */ + s = devm_kzalloc(&spi->dev, sizeof(struct max3107_port), GFP_KERNEL); + if (!s) { + dev_err(&spi->dev, "Error allocating port structure\n"); + return -ENOMEM; + } + + if (!pdata) { + dev_warn(&spi->dev, "No platform data supplied, using defaults\n"); + pdata = &generic_plat_data; + } + s->pdata = pdata; + dev_set_drvdata(&spi->dev, s); + + /* Board specific configure */ + if (pdata->configure) + pdata->configure(); + + mutex_init(&s->max3107_mutex); + + /* Setup SPI bus */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + spi->max_speed_hz = 26000000; + spi_setup(spi); + + s->regmap = devm_regmap_init_spi(spi, &max3107_regmap_config); + if (IS_ERR(s->regmap)) { + ret = PTR_ERR(s->regmap); + dev_err(&spi->dev, "Failed to initialize register map: %d\n", ret); + goto err_out; + } + + /* Check REV ID to ensure we are talking to what we expect */ + if (regmap_read(s->regmap, MAX3107_REVID_REG, &val)) { + dev_err(&spi->dev, "SPI transfer failed\n"); + ret = -EIO; + goto err_out; + } + + if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) { + dev_err(&spi->dev, "Device ID 0x%02x does not match\n", val); + ret = -ENODEV; + goto err_out; + } + + /* Disable all interrupts */ + regmap_write(s->regmap, MAX3107_IRQEN_REG, 0); + + /* Configure clock source */ + val = MAX3107_CLKSRC_PLLBYP_BIT; /* PLL bypass ON */ + if (pdata->flags & MAX3107_EXT_CLK) + val |= MAX3107_CLKSRC_EXTCLK_BIT; + else + val |= MAX3107_CLKSRC_CRYST_BIT; + regmap_write(s->regmap, MAX3107_CLKSRC_REG, val); + + /* Register UART driver */ + if (!driver_registered) { + ret = uart_register_driver(&max3107_uart_driver); + if (ret) { + dev_err(&spi->dev, "Registering UART driver failed\n"); + goto err_out; + } + } + driver_registered++; + + /* Initialize UART port data */ + s->port.fifosize = MAX3107_FIFO_SIZE; + s->port.ops = &max3107_ops; + s->port.dev = &spi->dev; + s->port.uartclk = 9600; /* Bogus value */ + s->port.flags = UPF_SKIP_TEST; + s->port.irq = spi->irq; + s->port.type = PORT_MAX3107; + + uart_add_one_port(&max3107_uart_driver, &s->port); + + /* Initialize workqueue for start TX */ + s->wq = create_freezable_workqueue("max3107_wq"); + INIT_WORK(&s->tx_work, max3107_wq_proc); + + /* Go to suspend mode */ + if (pdata->suspend) + pdata->suspend(s, 1); + + dev_info(&spi->dev, "MAX3107 UART driver registered\n"); + + return 0; + +err_out: + devm_kfree(&spi->dev, s); + + return ret; +} + +static int __devexit max3107_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(s->port.dev, "Removing port\n"); + + destroy_workqueue(s->wq); + + uart_remove_one_port(&max3107_uart_driver, &s->port); + dev_set_drvdata(&spi->dev, NULL); + + if (driver_registered == 1) + uart_unregister_driver(&max3107_uart_driver); + driver_registered--; + + devm_kfree(&spi->dev, s); + + return 0; +} + +static struct spi_driver max3107_driver = { + .driver = { + .name = "max3107", + .owner = THIS_MODULE, + }, + .probe = max3107_probe, + .remove = __devexit_p(max3107_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; +module_spi_driver(max3107_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Aavamobile"); +MODULE_DESCRIPTION("MAX3107 UART driver"); diff --git a/include/linux/platform_data/max3107.h b/include/linux/platform_data/max3107.h new file mode 100644 index 0000000..6e13701 --- /dev/null +++ b/include/linux/platform_data/max3107.h @@ -0,0 +1,56 @@ +/* + * SPI UART protocol driver for Maxim (Dallas) MAX3107 + * + * Based on max3100.c + * by Christian Pellegrin <chripell@xxxxxxxxxxxx> + * and max3110.c + * by Feng Tang <feng.tang@xxxxxxxxx> + * + * Rewritten by Alexander Shiyan <shc_work@xxxxxxx> + * + * Copyright (C) Aavamobile 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef _MAX3107_H_ +#define _MAX3107_H_ + +struct baud_table { + int baud; + u32 brg; +}; + +struct max3107_port; + +/* MAX3107 platform data structure */ +struct max3107_plat { + /* Platform data flags definitions */ + const unsigned int flags; +#define MAX3107_LOOPBACK (0x00000001) /* Loopback mode enable */ +#define MAX3107_EXT_CLK (0x00000002) /* External clock enable */ +#define MAX3107_AUTOCTS (0x00000004) /* Enable auto CTS flow control */ +#define MAX3107_AUTORTS (0x00000008) /* Enable auto RTS flow control */ +#define MAX3107_ECHO_SUPRESS (0x00000010) /* Enable echo supress */ +#define MAX3107_AUTO_DIR_CTRL (0x00000020) /* Enable Auto direction control (RS-485) */ + /* Baudrate table */ + const struct baud_table *baud_tbl; + /* GPIO base number */ + const unsigned int gpio_base; /* Not implemented yet */ + /* Called before initialization */ + void (*configure)(void); + /* HW suspend function */ + void (*suspend)(struct max3107_port *s, int do_suspend); +}; + +/* Functions that can be reused in board support code */ +void max3107_default_suspend(struct max3107_port *s, int do_suspend); + +/* Tables that can be reused in board support code */ +extern const struct baud_table brg_table_13m[]; +extern const struct baud_table brg_table_26m[]; +extern const struct baud_table brg_table_3_6864m[]; + +#endif -- 1.7.3.4 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html