diff mbox

tty/serial : Add I2C support to Max310x driver (linux-3.8.13)

Message ID CDD76EB921100341B75E76FF5FC7731F1F14067F@SIMTCSMB06.napa.ad.etn.com
State New
Headers show

Commit Message

Meghan Saitwal Sept. 7, 2016, 9:05 a.m. UTC
MAX3107/8 chip supports both SPI and I2C protocol. Currently, max310x
driver support only SPI protocol. This patch adds I2C support to the
driver. With I2C support, we have added bulk read/write functionality
which can be enabled using BULK_RW_ENABLE variable.

Signed-off-by: Meghan Saitwal MeghanSaitwal@Eaton.com<mailto:MeghanSaitwal@Eaton.com>
Tested-by: Devidas Kalane DevidasKalane@Eaton.com<mailto:DevidasKalane@Eaton.com>
Suggested-by: Ashwin Patwekar AshwinPatwekar@Eaton.com<mailto:AshwinPatwekar@Eaton.com>
drivers/tty/serial/Kconfig   |   38 +++-
drivers/tty/serial/Makefile  |    2
drivers/tty/serial/max310x.c |  268 +++++++++++++++++++++++++++------
3 files changed, 254 insertions(+), 54 deletions(-)
diff mbox

Patch

diff -purN a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
--- a/drivers/tty/serial/Kconfig  2016-09-01 12:05:32.092474214 +0530
+++ b/drivers/tty/serial/Kconfig               2016-09-01 12:11:12.938507208 +0530
@@ -276,19 +276,45 @@  config SERIAL_MAX3100
               help
                 MAX3100 chip support

+config SERIAL_MAX310X_CORE
+             tristate
+
config SERIAL_MAX310X
-              bool "MAX310X support"
-              depends on SPI
+             tristate "MAX310X support"
+             depends on (SPI && !I2C) || I2C
               select SERIAL_CORE
-              select REGMAP_SPI if SPI
-              default n
               help
                 This selects support for an advanced UART from Maxim (Dallas).
                 Supported ICs are MAX3107, MAX3108.
                 Each IC contains 128 words each of receive and transmit FIFO
-                that can be controlled through I2C or high-speed SPI.
+               that can be controlled through I2C or high-speed SPI.
+               Select SPI or I2C bus using options below.
+
+config SERIAL_MAX310X_SPI
+             bool "MAX310x for spi interface"
+             depends on SERIAL_MAX310X
+             depends on SPI
+             select SERIAL_MAX310X_CORE if SERIAL_MAX310X
+             select REGMAP_SPI if SPI
+             default y
+             help
+                Enable MAX310x driver on SPI bus,
+                If required say y, and say n to spi if not required,
+                Enabled by default to support oldconfig.
+                You must select at least one bus for the driver to be built.
+
+config SERIAL_MAX310X_I2C
+             bool "MAX310x for I2C interface"
+             depends on SERIAL_MAX310X
+             depends on I2C
+             select SERIAL_MAX310XX_CORE if SERIAL_MAX310X
+             select REGMAP_I2C if I2C
+             help
+                Enable MAX310x driver on I2C bus,
+                If required say y, and say n to i2c if not required,
+                This is additional support to existing driver.
+                You must select at least one bus for the driver to be built.

-                Say Y here if you want to support this ICs.

 config SERIAL_DZ
               bool "DECstation DZ serial driver"
diff -purN a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
--- a/drivers/tty/serial/Makefile               2016-09-01 12:07:31.815485804 +0530
+++ b/drivers/tty/serial/Makefile            2016-09-01 12:11:40.576509884 +0530
@@ -28,7 +28,7 @@  obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
-obj-$(CONFIG_SERIAL_MAX310X) += max310x.o
+obj-$(CONFIG_SERIAL_MAX310X_CORE) += max310x.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
diff -purN a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
--- a/drivers/tty/serial/max310x.c            2013-05-12 02:27:46.000000000 +0530
+++ b/drivers/tty/serial/max310x.c         2016-08-12 10:22:40.000000000 +0530
@@ -25,8 +25,11 @@ 
#include <linux/regmap.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
+#include <linux/i2c.h>
#include <linux/platform_data/max310x.h>

+#define BULK_RW_ENABLE      0
+
#define MAX310X_MAJOR                                         204
#define MAX310X_MINOR                                         209

@@ -272,6 +275,8 @@  struct max310x_port {
               const char                           *name;
               int                                           uartclk;

+             unsigned char                    buf[MAX310X_FIFO_SIZE];
+
               unsigned int                       nr_gpio;
#ifdef CONFIG_GPIOLIB
               struct gpio_chip                gpio;
@@ -459,7 +464,7 @@  static int max310x_set_ref_clk(struct ma

 static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
{
-              unsigned int sts = 0, ch = 0, flag;
+             unsigned int sts = 0, ch = 0, flag, bytes_read, i;
               struct tty_struct *tty = tty_port_tty_get(&s->port.state->port);

                if (!tty)
@@ -473,7 +478,64 @@  static void max310x_handle_rx(struct max

                dev_dbg(s->port.dev, "RX Len = %u\n", rxlen);

-              while (rxlen--) {
+             #if BULK_RW_ENABLE
+
+             while (rxlen) {
+                             regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts);
+                             sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
+                                    MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
+                             if(sts)
+                             {
+                                             regmap_read(s->regmap,MAX310X_RHR_REG, s->buf);
+                                             bytes_read = 1;
+                             } else {
+                                             regcache_cache_bypass(s->regmap, true);
+                             regmap_raw_read(s->regmap, MAX310X_RHR_REG, s->buf, rxlen);
+                             regcache_cache_bypass(s->regmap, false);
+                                             bytes_read = rxlen;
+                             }
+
+                             s->port.icount.rx++;
+                             flag = TTY_NORMAL;
+
+                             if (unlikely(sts)) {
+                                             if (sts & MAX310X_LSR_RXBRK_BIT) {
+                                                             s->port.icount.brk++;
+                                                             if (uart_handle_break(&s->port))
+                                                                             continue;
+                                             } else if (sts & MAX310X_LSR_RXPAR_BIT)
+                                                             s->port.icount.parity++;
+                                             else if (sts & MAX310X_LSR_FRERR_BIT)
+                                                             s->port.icount.frame++;
+                                             else if (sts & MAX310X_LSR_RXOVR_BIT)
+                                                             s->port.icount.overrun++;
+
+                                             sts &= s->port.read_status_mask;
+                                             if (sts & MAX310X_LSR_RXBRK_BIT)
+                                                             flag = TTY_BREAK;
+                                             else if (sts & MAX310X_LSR_RXPAR_BIT)
+                                                             flag = TTY_PARITY;
+                                             else if (sts & MAX310X_LSR_FRERR_BIT)
+                                                             flag = TTY_FRAME;
+                                             else if (sts & MAX310X_LSR_RXOVR_BIT)
+                                                             flag = TTY_OVERRUN;
+                             }
+
+                             for(i=0; i< bytes_read; i++)
+                             {
+                                             if (uart_handle_sysrq_char(s->port, s->buf[i]))
+                                                             continue;
+
+                                             if (sts & s->port.ignore_status_mask)
+                                                             continue;
+
+                                             uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT,
+                                                             s->buf[i], flag);
+                             }
+                             rxlen -= bytes_read;
+             }
+             #else
+                             while (rxlen--) {
                               regmap_read(s->regmap, MAX310X_RHR_REG, &ch);
                               regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts);

@@ -514,8 +576,8 @@  static void max310x_handle_rx(struct max

                                uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT,
                                                                ch, flag);
-              }
-
+                             }
+             #endif
               tty_flip_buffer_push(tty);

                tty_kref_put(tty);
@@ -524,8 +586,7 @@  static void max310x_handle_rx(struct max
static void max310x_handle_tx(struct max310x_port *s)
{
               struct circ_buf *xmit = &s->port.state->xmit;
-              unsigned int txlen = 0, to_send;
-
+             unsigned int txlen = 0, to_send = 0, i;
               if (unlikely(s->port.x_char)) {
                               regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char);
                               s->port.icount.tx++;
@@ -545,7 +606,18 @@  static void max310x_handle_tx(struct max
                               to_send = (to_send > txlen) ? txlen : to_send;

                                dev_dbg(s->port.dev, "TX Len = %u\n", to_send);
-
+#if BULK_RW_ENABLE
+                             /* Add data to send */
+                             s->port.icount.tx += to_send;
+                             for(i=0; i < to_send; ++i)
+                             {
+                                             s->buf[i] = xmit->buf[xmit->tail];
+                                             xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+                             }
+                             regcache_cache_bypass(s->regmap, true);
+                             regmap_raw_write(s->regmap, MAX310X_THR_REG, s->buf, to_send);
+                             regcache_cache_bypass(s->regmap, false);
+#else
                                /* Add data to send */
                               s->port.icount.tx += to_send;
                               while (to_send--) {
@@ -553,6 +625,7 @@  static void max310x_handle_tx(struct max
                                                                    xmit->buf[xmit->tail]);
                                               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
                               };
+#endif
               }

                if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -593,7 +666,7 @@  static irqreturn_t max310x_ist(int irq,
static void max310x_wq_proc(struct work_struct *ws)
{
               struct max310x_port *s = container_of(ws, struct max310x_port, tx_work);
-
+
               mutex_lock(&s->max310x_mutex);
               max310x_handle_tx(s);
               mutex_unlock(&s->max310x_mutex);
@@ -887,12 +960,12 @@  static struct uart_ops max310x_ops = {
               .verify_port        = max310x_verify_port,
};

-static int max310x_suspend(struct spi_device *spi, pm_message_t state)
+static int max310x_suspend(struct device *dev)
{
               int ret;
-              struct max310x_port *s = dev_get_drvdata(&spi->dev);
+             struct max310x_port *s = dev_get_drvdata(dev);

-              dev_dbg(&spi->dev, "Suspend\n");
+             dev_dbg(dev, "Suspend\n");

                ret = uart_suspend_port(&s->uart, &s->port);

@@ -911,11 +984,11 @@  static int max310x_suspend(struct spi_de
               return ret;
}

-static int max310x_resume(struct spi_device *spi)
+static int max310x_resume(struct device *dev)
{
-              struct max310x_port *s = dev_get_drvdata(&spi->dev);
+             struct max310x_port *s = dev_get_drvdata(dev);

-              dev_dbg(&spi->dev, "Resume\n");
+             dev_dbg(dev, "Resume\n");

                if (s->pdata->suspend)
                               s->pdata->suspend(0);
@@ -995,17 +1068,15 @@  static struct max310x_pdata generic_plat
               .frequency          = 26000000,
};

-static int max310x_probe(struct spi_device *spi)
+static int max310x_probe(struct device *dev, int chiptype, struct regmap *regmap, int irq)
{
               struct max310x_port *s;
-              struct device *dev = &spi->dev;
-              int chiptype = spi_get_device_id(spi)->driver_data;
               struct max310x_pdata *pdata = dev->platform_data;
               unsigned int val = 0;
               int ret;

                /* Check for IRQ */
-              if (spi->irq <= 0) {
+             if (irq <= 0) {
                               dev_err(dev, "No IRQ specified\n");
                               return -ENOTSUPP;
               }
@@ -1023,6 +1094,7 @@  static int max310x_probe(struct spi_devi
                               pdata = &generic_plat_data;
               }
               s->pdata = pdata;
+    s->regmap = regmap;

                /* Individual chip settings */
               switch (chiptype) {
@@ -1030,13 +1102,11 @@  static int max310x_probe(struct spi_devi
                               s->name = "MAX3107";
                               s->nr_gpio = 4;
                               s->uart.nr = 1;
-                              s->regcfg.max_register = 0x1f;
                               break;
               case MAX310X_TYPE_MAX3108:
                               s->name = "MAX3108";
                               s->nr_gpio = 4;
                               s->uart.nr = 1;
-                              s->regcfg.max_register = 0x1e;
                               break;
               default:
                               dev_err(dev, "Unsupported chip type %i\n", chiptype);
@@ -1054,32 +1124,16 @@  static int max310x_probe(struct spi_devi

                mutex_init(&s->max310x_mutex);

-              /* Setup SPI bus */
-              spi->mode                          = SPI_MODE_0;
-              spi->bits_per_word        = 8;
-              spi->max_speed_hz       = 26000000;
-              spi_setup(spi);
-
-              /* Setup regmap */
-              s->regcfg.reg_bits                           = 8;
-              s->regcfg.val_bits                            = 8;
-              s->regcfg.read_flag_mask           = 0x00;
-              s->regcfg.write_flag_mask          = 0x80;
-              s->regcfg.cache_type                    = REGCACHE_RBTREE;
-              s->regcfg.writeable_reg                               = max3107_8_reg_writeable;
-              s->regcfg.volatile_reg                    = max310x_reg_volatile;
-              s->regcfg.precious_reg                 = max310x_reg_precious;
-              s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
               if (IS_ERR(s->regmap)) {
                               ret = PTR_ERR(s->regmap);
                               dev_err(dev, "Failed to initialize register map\n");
                               goto err_out;
               }

-              /* Reset chip & check SPI function */
+             /* Reset chip & check SPI/I2C function */
               ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT);
               if (ret) {
-                              dev_err(dev, "SPI transfer failed\n");
+                             dev_err(dev, "SPI/I2C transfer failed\n");
                               goto err_out;
               }
               /* Clear chip reset */
@@ -1129,11 +1183,12 @@  static int max310x_probe(struct spi_devi
               regmap_write(s->regmap, MAX310X_MODE1_REG, val);

                /* Setup interrupt */
-              ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist,
+             /* Used IRQF_TRIGGER_LOW in case of I2C */
+             ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist,
                                                                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                                                               dev_name(dev), s);
               if (ret) {
-                              dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq);
+                             dev_err(dev, "Unable to reguest IRQ %i\n", irq);
                               goto err_out;
               }

@@ -1156,7 +1211,7 @@  static int max310x_probe(struct spi_devi
               /* Initialize UART port data */
               s->port.line                        = 0;
               s->port.dev                        = dev;
-              s->port.irq                          = spi->irq;
+             s->port.irq                          = irq;
               s->port.type                      = PORT_MAX310X;
               s->port.fifosize = MAX310X_FIFO_SIZE;
               s->port.flags                      = UPF_SKIP_TEST | UPF_FIXED_TYPE;
@@ -1203,9 +1258,8 @@  err_out:
               return ret;
}

-static int max310x_remove(struct spi_device *spi)
+static int max310x_remove(struct device *dev)
{
-              struct device *dev = &spi->dev;
               struct max310x_port *s = dev_get_drvdata(dev);
               int ret = 0;

@@ -1237,6 +1291,63 @@  static int max310x_remove(struct spi_dev
               return ret;
}

+static struct regmap_config regcfg = {
+    .reg_bits = 8,
+    .val_bits = 8,
+    .read_flag_mask = 0x00,
+    .write_flag_mask = 0x80,      // may need to remove this mask
+    .cache_type = REGCACHE_RBTREE,
+    .writeable_reg = max3107_8_reg_writeable,
+    .volatile_reg = max310x_reg_volatile,
+    .precious_reg = max310x_reg_precious,
+};
+
+#ifdef CONFIG_SERIAL_MAX310X_SPI
+
+static int max310x_spi_probe(struct spi_device *spi)
+{
+             struct regmap *regmap;
+    int chiptype = spi_get_device_id(spi)->driver_data;
+
+             /* Setup SPI bus */
+             spi->mode                          = SPI_MODE_0;
+             spi->bits_per_word        = 8;
+             spi->max_speed_hz       = 26000000;
+             spi_setup(spi);
+
+             switch (chiptype) {
+             case MAX310X_TYPE_MAX3107:
+                             regcfg.max_register = 0x1f;
+                             break;
+             case MAX310X_TYPE_MAX3108:
+                             regcfg.max_register = 0x1e;
+                             break;
+             default:
+                             dev_err(&spi->dev, "Unsupported chip type %i\n", chiptype);
+                             return -ENOTSUPP;
+             }
+
+
+             regmap = devm_regmap_init_spi(spi, &regcfg);
+
+    return max310x_probe(&spi->dev, chiptype, regmap, spi->irq);
+}
+
+static int max310x_spi_remove(struct spi_device *spi)
+{
+             return max310x_remove(&spi->dev);
+}
+
+static int max310x_suspend(struct spi_device *spi, pm_message_t state)
+{
+    return max310x_suspend(&spi->dev);
+}
+
+static int max310x_resume(struct spi_device *spi)
+{
+    return max310x_resume(&spi->dev);
+}
+
static const struct spi_device_id max310x_id_table[] = {
               { "max3107",      MAX310X_TYPE_MAX3107 },
               { "max3108",      MAX310X_TYPE_MAX3108 },
@@ -1249,13 +1360,76 @@  static struct spi_driver max310x_driver
                               .name   = "max310x",
                               .owner = THIS_MODULE,
               },
-              .probe                  = max310x_probe,
-              .remove                               = max310x_remove,
-              .suspend             = max310x_suspend,
-              .resume                               = max310x_resume,
+             .probe                  = max310x_spi_probe,
+             .remove                               = max310x_spi_remove,
+             .suspend             = max310x_spi_suspend,
+             .resume                               = max310x_spi_resume,
               .id_table              = max310x_id_table,
};
module_spi_driver(max310x_driver);
+#endif
+
+#ifdef CONFIG_SERIAL_MAX310X_I2C
+
+static int max310x_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id)
+{
+    struct regmap *regmap;
+             int chiptype = id->driver_data;
+    printk("chiptype = %d \n",chiptype);
+             switch (chiptype) {
+             case MAX310X_TYPE_MAX3107:
+                             regcfg.max_register = 0x1f;
+                             break;
+             case MAX310X_TYPE_MAX3108:
+                             regcfg.max_register = 0x1e;
+                             break;
+             default:
+                             dev_err(&i2c->dev, "Unsupported chip type %i\n", chiptype);
+                             return -ENOTSUPP;
+             }
+
+
+    regmap = devm_regmap_init_i2c(i2c, &regcfg);
+
+    return max310x_probe(&i2c->dev, chiptype, regmap,  i2c->irq);
+}
+
+static int max310x_i2c_remove(struct i2c_client *i2c)
+{
+             return max310x_remove(&i2c->dev);
+}
+
+static int max310x_i2c_suspend(struct i2c_client *i2c, pm_message_t state)
+{
+    return max310x_suspend(&i2c->dev);
+}
+
+static int max310x_i2c_resume(struct i2c_client *i2c)
+{
+    return max310x_resume(&i2c->dev);
+}
+
+
+static const struct i2c_device_id max310x_id_table[] = {
+             { "max3107",      MAX310X_TYPE_MAX3107 },
+             { "max3108",      MAX310X_TYPE_MAX3108 },
+             { }
+};
+MODULE_DEVICE_TABLE(i2c, max310x_id_table);
+
+static struct i2c_driver max310x_driver = {
+             .driver = {
+                             .name   = "max310x",
+                             .owner = THIS_MODULE,
+             },
+             .probe                  = max310x_i2c_probe,
+             .remove                               = max310x_i2c_remove,
+             .suspend             = max310x_i2c_suspend,
+             .resume                               = max310x_i2c_resume,
+             .id_table              = max310x_id_table,
+};
+module_i2c_driver(max310x_driver);
+#endif

 MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru<mailto:shc_work@mail.ru>>");